[xen master] xen/arm: Add MVEBU UART driver for Marvell Armada 3700 SoC

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[xen master] xen/arm: Add MVEBU UART driver for Marvell Armada 3700 SoC

patchbot
commit 33fcfac4ee76c8cfa31534e1d3eee960799b8554
Author:     Amit Singh Tomar <[hidden email]>
AuthorDate: Fri Apr 6 21:30:35 2018 +0530
Commit:     Julien Grall <[hidden email]>
CommitDate: Fri Apr 6 17:17:35 2018 +0100

    xen/arm: Add MVEBU UART driver for Marvell Armada 3700 SoC
   
    This patch adds driver for UART controller found on Armada 3700 SoC.
   
    There is no reference manuals available for 3700 SoC in public and it
    is derived by looking at Linux driver[1].
   
    [1]https://github.com/torvalds/linux/blob/master/drivers/tty/serial/mvebu-uart.c
    commit-id: c685af1108d7c303f0b901413405d68eaeac4477
   
    Signed-off-by: Amit Singh Tomar <[hidden email]>
    Reviewed-by: Andre Przywara <[hidden email]>
    Tested-by: Andre Przywara <[hidden email]>
    Acked-by: Julien Grall <[hidden email]>
---
 MAINTAINERS                   |   1 +
 xen/drivers/char/Kconfig      |   8 ++
 xen/drivers/char/Makefile     |   1 +
 xen/drivers/char/mvebu-uart.c | 294 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 304 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index bb049c8664..bbda4b9f43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -169,6 +169,7 @@ F: xen/arch/arm/
 F: xen/drivers/char/arm-uart.c
 F: xen/drivers/char/cadence-uart.c
 F: xen/drivers/char/exynos4210-uart.c
+F: xen/drivers/char/mvebu-uart.c
 F: xen/drivers/char/omap-uart.c
 F: xen/drivers/char/pl011.c
 F: xen/drivers/char/scif-uart.c
diff --git a/xen/drivers/char/Kconfig b/xen/drivers/char/Kconfig
index fb53dd804f..cc78ec3e3c 100644
--- a/xen/drivers/char/Kconfig
+++ b/xen/drivers/char/Kconfig
@@ -12,6 +12,14 @@ config HAS_CADENCE_UART
   This selects the Xilinx Zynq Cadence UART. If you have a Xilinx Zynq
   based board, say Y.
 
+config HAS_MVEBU
+ bool
+ default y
+ depends on ARM_64
+ help
+  This selects the Marvell MVEBU UART. If you have a ARMADA 3700
+  based board, say Y.
+
 config HAS_PL011
  bool
  default y
diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
index 0d48b16e8d..b68c330191 100644
--- a/xen/drivers/char/Makefile
+++ b/xen/drivers/char/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_HAS_NS16550) += ns16550.o
 obj-$(CONFIG_HAS_CADENCE_UART) += cadence-uart.o
 obj-$(CONFIG_HAS_PL011) += pl011.o
 obj-$(CONFIG_HAS_EXYNOS4210) += exynos4210-uart.o
+obj-$(CONFIG_HAS_MVEBU) += mvebu-uart.o
 obj-$(CONFIG_HAS_OMAP) += omap-uart.o
 obj-$(CONFIG_HAS_SCIF) += scif-uart.o
 obj-$(CONFIG_HAS_EHCI) += ehci-dbgp.o
diff --git a/xen/drivers/char/mvebu-uart.c b/xen/drivers/char/mvebu-uart.c
new file mode 100644
index 0000000000..b72db9542e
--- /dev/null
+++ b/xen/drivers/char/mvebu-uart.c
@@ -0,0 +1,294 @@
+/*
+ * xen/drivers/char/mvebu3700-uart.c
+ *
+ * Driver for Marvell MVEBU UART.
+ *
+ * Copyright (c) 2018, Amit Singh Tomar <[hidden email]>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms and conditions of the GNU General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/irq.h>
+#include <xen/serial.h>
+#include <xen/vmap.h>
+#include <asm/io.h>
+
+/* Register offsets */
+#define UART_RX_REG             0x00
+
+#define UART_TX_REG             0x04
+
+#define UART_CTRL_REG           0x08
+#define CTRL_TXFIFO_RST         BIT(15)
+#define CTRL_RXFIFO_RST         BIT(14)
+#define CTRL_TX_RDY_INT         BIT(5)
+#define CTRL_RX_RDY_INT         BIT(4)
+#define CTRL_BRK_DET_INT        BIT(3)
+#define CTRL_FRM_ERR_INT        BIT(2)
+#define CTRL_PAR_ERR_INT        BIT(1)
+#define CTRL_OVR_ERR_INT        BIT(0)
+#define CTRL_ERR_INT            (CTRL_BRK_DET_INT | CTRL_FRM_ERR_INT | \
+                                 CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT)
+
+#define UART_STATUS_REG         0x0c
+#define STATUS_TXFIFO_EMP       BIT(13)
+#define STATUS_TXFIFO_FUL       BIT(11)
+#define STATUS_TXFIFO_HFL       BIT(10)
+#define STATUS_TX_RDY           BIT(5)
+#define STATUS_RX_RDY           BIT(4)
+#define STATUS_BRK_DET          BIT(3)
+#define STATUS_FRM_ERR          BIT(2)
+#define STATUS_PAR_ERR          BIT(1)
+#define STATUS_OVR_ERR          BIT(0)
+#define STATUS_BRK_ERR          (STATUS_BRK_DET | STATUS_FRM_ERR | \
+                                 STATUS_PAR_ERR | STATUS_OVR_ERR)
+
+#define TX_FIFO_SIZE            32
+
+static struct mvebu3700_uart {
+    unsigned int irq;
+    void __iomem *regs;
+    struct irqaction irqaction;
+    struct vuart_info vuart;
+} mvebu3700_com = {0};
+
+#define mvebu3700_read(uart, off)           readl((uart)->regs + off)
+#define mvebu3700_write(uart, off, val)     writel(val, (uart->regs) + off)
+
+static void mvebu3700_uart_interrupt(int irq, void *data,
+                                     struct cpu_user_regs *regs)
+{
+    struct serial_port *port = data;
+    struct mvebu3700_uart *uart = port->uart;
+    uint32_t st = mvebu3700_read(uart, UART_STATUS_REG);
+
+    if ( st & (STATUS_RX_RDY | STATUS_OVR_ERR | STATUS_FRM_ERR |
+               STATUS_BRK_DET) )
+        serial_rx_interrupt(port, regs);
+
+    if ( st & STATUS_TX_RDY )
+        serial_tx_interrupt(port, regs);
+}
+
+static void __init mvebu3700_uart_init_preirq(struct serial_port *port)
+{
+    struct mvebu3700_uart *uart = port->uart;
+    uint32_t reg;
+
+    reg = mvebu3700_read(uart, UART_CTRL_REG);
+    reg |= (CTRL_TXFIFO_RST | CTRL_RXFIFO_RST);
+    mvebu3700_write(uart, UART_CTRL_REG, reg);
+
+    /* Before we make IRQ request, clear the error bits of state register. */
+    reg = mvebu3700_read(uart, UART_STATUS_REG);
+    reg |= STATUS_BRK_ERR;
+    mvebu3700_write(uart, UART_STATUS_REG, reg);
+
+    /* Clear error interrupts. */
+    mvebu3700_write(uart, UART_CTRL_REG, CTRL_ERR_INT);
+
+    /* Disable Rx/Tx interrupts. */
+    reg = mvebu3700_read(uart, UART_CTRL_REG);
+    reg &= ~(CTRL_RX_RDY_INT | CTRL_TX_RDY_INT);
+    mvebu3700_write(uart, UART_CTRL_REG, reg);
+}
+
+static void __init mvebu3700_uart_init_postirq(struct serial_port *port)
+{
+    struct mvebu3700_uart *uart = port->uart;
+    uint32_t reg;
+
+    uart->irqaction.handler = mvebu3700_uart_interrupt;
+    uart->irqaction.name    = "mvebu3700_uart";
+    uart->irqaction.dev_id  = port;
+
+    if ( setup_irq(uart->irq, 0, &uart->irqaction) != 0 )
+    {
+        printk("Failed to allocated mvebu3700_uart IRQ %d\n", uart->irq);
+        return;
+    }
+
+    /* Make sure Rx/Tx interrupts are enabled now */
+    reg = mvebu3700_read(uart, UART_CTRL_REG);
+    reg |= (CTRL_RX_RDY_INT | CTRL_TX_RDY_INT);
+    mvebu3700_write(uart, UART_CTRL_REG, reg);
+}
+
+static void mvebu3700_uart_suspend(struct serial_port *port)
+{
+    BUG();
+}
+
+static void mvebu3700_uart_resume(struct serial_port *port)
+{
+    BUG();
+}
+
+static void mvebu3700_uart_putc(struct serial_port *port, char c)
+{
+    struct mvebu3700_uart *uart = port->uart;
+
+    mvebu3700_write(uart, UART_TX_REG, c);
+}
+
+static int mvebu3700_uart_getc(struct serial_port *port, char *c)
+{
+    struct mvebu3700_uart *uart = port->uart;
+
+    if ( !(mvebu3700_read(uart, UART_STATUS_REG) & STATUS_RX_RDY) )
+        return 0;
+
+    *c = mvebu3700_read(uart, UART_RX_REG) & 0xff;
+
+    return 1;
+}
+
+static int __init mvebu3700_irq(struct serial_port *port)
+{
+    struct mvebu3700_uart *uart = port->uart;
+
+    return uart->irq;
+}
+
+static const struct vuart_info *mvebu3700_vuart_info(struct serial_port *port)
+{
+    struct mvebu3700_uart *uart = port->uart;
+
+    return &uart->vuart;
+}
+
+static void mvebu3700_uart_stop_tx(struct serial_port *port)
+{
+    struct mvebu3700_uart *uart = port->uart;
+    uint32_t reg;
+
+    reg = mvebu3700_read(uart, UART_CTRL_REG);
+    reg &= ~CTRL_TX_RDY_INT;
+    mvebu3700_write(uart, UART_CTRL_REG, reg);
+}
+
+static void mvebu3700_uart_start_tx(struct serial_port *port)
+{
+    struct mvebu3700_uart *uart = port->uart;
+    uint32_t reg;
+
+    reg = mvebu3700_read(uart, UART_CTRL_REG);
+    reg |= CTRL_TX_RDY_INT;
+    mvebu3700_write(uart, UART_CTRL_REG, reg);
+}
+
+static int mvebu3700_uart_tx_ready(struct serial_port *port)
+{
+    struct mvebu3700_uart *uart = port->uart;
+    uint32_t reg;
+
+    reg = mvebu3700_read(uart, UART_STATUS_REG);
+
+    if ( reg & STATUS_TXFIFO_EMP )
+        return TX_FIFO_SIZE;
+    if ( reg & STATUS_TXFIFO_FUL )
+        return 0;
+    if ( reg & STATUS_TXFIFO_HFL )
+        return TX_FIFO_SIZE / 2;
+
+    /*
+     * If we reach here, we don't know the number of free char in FIFO
+     * but we are sure that neither the FIFO is full nor empty.
+     * So, let's just return at least 1.
+     */
+    return 1;
+}
+
+static struct uart_driver __read_mostly mvebu3700_uart_driver = {
+    .init_preirq  = mvebu3700_uart_init_preirq,
+    .init_postirq = mvebu3700_uart_init_postirq,
+    .endboot      = NULL,
+    .suspend      = mvebu3700_uart_suspend,
+    .resume       = mvebu3700_uart_resume,
+    .putc         = mvebu3700_uart_putc,
+    .getc         = mvebu3700_uart_getc,
+    .tx_ready     = mvebu3700_uart_tx_ready,
+    .stop_tx      = mvebu3700_uart_stop_tx,
+    .start_tx     = mvebu3700_uart_start_tx,
+    .irq          = mvebu3700_irq,
+    .vuart_info   = mvebu3700_vuart_info,
+};
+
+static int __init mvebu_uart_init(struct dt_device_node *dev, const void *data)
+{
+    const char *config = data;
+    struct mvebu3700_uart *uart;
+    int res;
+    u64 addr, size;
+
+    if ( strcmp(config, "") )
+        printk("WARNING: UART configuration is not supported\n");
+
+    uart = &mvebu3700_com;
+
+    res = dt_device_get_address(dev, 0, &addr, &size);
+    if ( res )
+    {
+        printk("mvebu3700: Unable to retrieve the base address of the UART\n");
+        return res;
+    }
+
+    res = platform_get_irq(dev, 0);
+    if ( res < 0 )
+    {
+        printk("mvebu3700: Unable to retrieve the IRQ\n");
+        return -EINVAL;
+    }
+
+    uart->irq  = res;
+
+    uart->regs = ioremap_nocache(addr, size);
+    if ( !uart->regs )
+    {
+        printk("mvebu3700: Unable to map the UART memory\n");
+        return -ENOMEM;
+    }
+
+    uart->vuart.base_addr = addr;
+    uart->vuart.size = size;
+    uart->vuart.data_off = UART_CTRL_REG;
+    uart->vuart.status_off = UART_STATUS_REG;
+    uart->vuart.status = STATUS_TX_RDY | STATUS_RX_RDY;
+
+    /* Register with generic serial driver. */
+    serial_register_uart(SERHND_DTUART, &mvebu3700_uart_driver, uart);
+
+    dt_device_set_used_by(dev, DOMID_XEN);
+
+    return 0;
+}
+
+static const struct dt_device_match mvebu_dt_match[] __initconst =
+{
+    DT_MATCH_COMPATIBLE("marvell,armada-3700-uart"),
+    { /* sentinel */ },
+};
+
+DT_DEVICE_START(mvebu, "Marvell Armada-3700 UART", DEVICE_SERIAL)
+    .dt_match = mvebu_dt_match,
+    .init = mvebu_uart_init,
+DT_DEVICE_END
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
generated by git-patchbot for /home/xen/git/xen.git#master

_______________________________________________
Xen-changelog mailing list
[hidden email]
https://lists.xenproject.org/xen-changelog