[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v2 2/3] xen/arm: Add new driver for R-Car Gen2 UART



From: Oleksandr Tyshchenko <oleksandr.tyshchenko@xxxxxxxxxxxxxxx>

Signed-off-by: Oleksandr Tyshchenko <oleksandr.tyshchenko@xxxxxxxxxxxxxxx>
Signed-off-by: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx>
---
 config/arm32.mk               |   1 +
 xen/drivers/char/Makefile     |   1 +
 xen/drivers/char/rcar2-uart.c | 376 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 378 insertions(+)
 create mode 100644 xen/drivers/char/rcar2-uart.c

diff --git a/config/arm32.mk b/config/arm32.mk
index 4f83a63..6ee5173 100644
--- a/config/arm32.mk
+++ b/config/arm32.mk
@@ -12,6 +12,7 @@ CFLAGS += -marm
 HAS_PL011 := y
 HAS_EXYNOS4210 := y
 HAS_OMAP := y
+HAS_RCAR2 := y
 HAS_NS16550 := y
 
 # Use only if calling $(LD) directly.
diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
index 911b788..64428b7 100644
--- a/xen/drivers/char/Makefile
+++ b/xen/drivers/char/Makefile
@@ -3,6 +3,7 @@ obj-$(HAS_NS16550) += ns16550.o
 obj-$(HAS_PL011) += pl011.o
 obj-$(HAS_EXYNOS4210) += exynos4210-uart.o
 obj-$(HAS_OMAP) += omap-uart.o
+obj-$(HAS_RCAR2) += rcar2-uart.o
 obj-$(HAS_EHCI) += ehci-dbgp.o
 obj-$(CONFIG_ARM) += dt-uart.o
 obj-y += serial.o
diff --git a/xen/drivers/char/rcar2-uart.c b/xen/drivers/char/rcar2-uart.c
new file mode 100644
index 0000000..0929db7
--- /dev/null
+++ b/xen/drivers/char/rcar2-uart.c
@@ -0,0 +1,376 @@
+/*
+ * xen/drivers/char/rcar2-uart.c
+ *
+ * Driver for R-Car Gen2 UART.
+ *
+ * Oleksandr Tyshchenko <oleksandr.tyshchenko@xxxxxxxxxxxxxxx>
+ * Copyright (C) 2014, Globallogic.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <xen/config.h>
+#include <xen/console.h>
+#include <xen/errno.h>
+#include <xen/serial.h>
+#include <xen/init.h>
+#include <xen/irq.h>
+#include <xen/mm.h>
+#include <xen/delay.h>
+#include <asm/device.h>
+#include <asm/rcar2-uart.h>
+#include <asm/io.h>
+
+#define PARITY_NONE    0
+#define PARITY_EVEN    1
+#define PARITY_ODD     2
+
+#define rcar2_readb(uart, off)          readb((uart)->regs + (off))
+#define rcar2_writeb(uart, off, val)    writeb((val), (uart)->regs + (off))
+
+#define rcar2_readw(uart, off)          readw((uart)->regs + (off))
+#define rcar2_writew(uart, off, val)    writew((val), (uart)->regs + (off))
+
+static struct rcar2_uart {
+    unsigned int baud, clock_hz, data_bits, parity, stop_bits;
+    unsigned int irq;
+    char __iomem *regs;
+    struct irqaction irqaction;
+    struct vuart_info vuart;
+    bool_t irq_en;
+} rcar2_com = {0};
+
+static void rcar2_uart_interrupt(int irq, void *data, struct cpu_user_regs 
*regs)
+{
+    struct serial_port *port = data;
+    struct rcar2_uart *uart = port->uart;
+    uint16_t status, ctrl;
+
+    ctrl = rcar2_readw(uart, SCIF_SCSCR);
+    status = rcar2_readw(uart, SCIF_SCFSR) & ~SCFSR_TEND;
+    /* Ignore next flag if TX Interrupt is disabled */
+    if ( !(ctrl & SCSCR_TIE) )
+        status &= ~SCFSR_TDFE;
+
+    while ( status != 0 )
+    {
+        /* TX Interrupt */
+        if ( status & SCFSR_TDFE )
+        {
+            serial_tx_interrupt(port, regs);
+
+            if ( port->txbufc == port->txbufp )
+            {
+                /*
+                 * There is no data bytes to send. We have to disable
+                 * TX Interrupt to prevent us from getting stuck in the
+                 * interrupt handler
+                 */
+                ctrl = rcar2_readw(uart, SCIF_SCSCR);
+                ctrl &= ~SCSCR_TIE;
+                rcar2_writew(uart, SCIF_SCSCR, ctrl);
+            }
+        }
+
+        /* RX Interrupt */
+        if ( status & (SCFSR_RDF | SCFSR_DR) )
+            serial_rx_interrupt(port, regs);
+
+        /* Error Interrupt */
+        if ( status & SCIF_ERRORS )
+            rcar2_writew(uart, SCIF_SCFSR, ~SCIF_ERRORS);
+        if ( rcar2_readw(uart, SCIF_SCLSR) & SCLSR_ORER )
+            rcar2_writew(uart, SCIF_SCLSR, 0);
+
+        status = rcar2_readw(uart, SCIF_SCFSR) & ~SCFSR_TEND;
+        if ( !(ctrl & SCSCR_TIE) )
+            status &= ~SCFSR_TDFE;
+    }
+}
+
+static void __init rcar2_uart_init_preirq(struct serial_port *port)
+{
+    struct rcar2_uart *uart = port->uart;
+    unsigned int divisor;
+    uint16_t val;
+
+    /*
+     * Wait until last bit has been transmitted. This is needed for a smooth
+     * transition when we come from early printk
+     */
+    while ( !(rcar2_readw(uart, SCIF_SCFSR) & SCFSR_TEND) );
+
+    /* Disable TX/RX parts and all interrupts */
+    rcar2_writew(uart, SCIF_SCSCR, 0);
+
+    /* Reset TX/RX FIFOs */
+    rcar2_writew(uart, SCIF_SCFCR, SCFCR_RFRST | SCFCR_TFRST);
+
+    /* Clear all errors and flags */
+    rcar2_readw(uart, SCIF_SCFSR);
+    rcar2_writew(uart, SCIF_SCFSR, 0);
+    rcar2_readw(uart, SCIF_SCLSR);
+    rcar2_writew(uart, SCIF_SCLSR, 0);
+
+    /* Select Baud rate generator output as a clock source */
+    rcar2_writew(uart, SCIF_SCSCR, SCSCR_CKE10);
+
+    /* Setup protocol format and Baud rate, select Asynchronous mode */
+    val = 0;
+    ASSERT( uart->data_bits >= 7 && uart->data_bits <= 8 );
+    if ( uart->data_bits == 7 )
+        val |= SCSMR_CHR;
+    else
+        val &= ~SCSMR_CHR;
+
+    ASSERT( uart->stop_bits >= 1 && uart->stop_bits <= 2 );
+    if ( uart->stop_bits == 2 )
+        val |= SCSMR_STOP;
+    else
+        val &= ~SCSMR_STOP;
+
+    ASSERT( uart->parity >= PARITY_NONE && uart->parity <= PARITY_ODD );
+    switch ( uart->parity )
+    {
+    case PARITY_NONE:
+        val &= ~SCSMR_PE;
+        break;
+
+    case PARITY_EVEN:
+        val |= SCSMR_PE;
+        break;
+
+    case PARITY_ODD:
+        val |= SCSMR_PE | SCSMR_ODD;
+        break;
+    }
+    rcar2_writew(uart, SCIF_SCSMR, val);
+
+    ASSERT( uart->clock_hz > 0 );
+    if ( uart->baud != BAUD_AUTO )
+    {
+        /* Setup desired Baud rate */
+        divisor = uart->clock_hz / (uart->baud << 4);
+        ASSERT( divisor >= 1 && divisor <= (uint16_t)UINT_MAX );
+        rcar2_writew(uart, SCIF_DL, (uint16_t)divisor);
+        /* Selects the frequency divided clock (SC_CLK external input) */
+        rcar2_writew(uart, SCIF_CKS, 0);
+        /*
+         * TODO: should be uncommented
+         * udelay(1000000 / uart->baud + 1);
+         */
+    }
+    else
+    {
+        /* Read current Baud rate */
+        divisor = rcar2_readw(uart, SCIF_DL);
+        ASSERT( divisor >= 1 && divisor <= (uint16_t)UINT_MAX );
+        uart->baud = uart->clock_hz / (divisor << 4);
+    }
+
+    /* Setup trigger level for TX/RX FIFOs */
+    rcar2_writew(uart, SCIF_SCFCR, SCFCR_RTRG11 | SCFCR_TTRG11);
+
+    /* Enable TX/RX parts */
+    rcar2_writew(uart, SCIF_SCSCR, rcar2_readw(uart, SCIF_SCSCR) |
+                 SCSCR_TE | SCSCR_RE);
+}
+
+static void __init rcar2_uart_init_postirq(struct serial_port *port)
+{
+    struct rcar2_uart *uart = port->uart;
+    int rc;
+
+    uart->irqaction.handler = rcar2_uart_interrupt;
+    uart->irqaction.name    = "rcar2_uart";
+    uart->irqaction.dev_id  = port;
+
+    if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 )
+        dprintk(XENLOG_ERR, "Failed to allocated rcar2_uart IRQ %d\n",
+                uart->irq);
+
+    /* Clear all errors */
+    if ( rcar2_readw(uart, SCIF_SCFSR) & SCIF_ERRORS )
+        rcar2_writew(uart, SCIF_SCFSR, ~SCIF_ERRORS);
+    if ( rcar2_readw(uart, SCIF_SCLSR) & SCLSR_ORER )
+        rcar2_writew(uart, SCIF_SCLSR, 0);
+
+    /* Enable TX/RX and Error Interrupts  */
+    rcar2_writew(uart, SCIF_SCSCR, rcar2_readw(uart, SCIF_SCSCR) |
+                 SCSCR_TIE | SCSCR_RIE | SCSCR_REIE);
+
+    uart->irq_en = 1;
+}
+
+static void rcar2_uart_suspend(struct serial_port *port)
+{
+    BUG();
+}
+
+static void rcar2_uart_resume(struct serial_port *port)
+{
+    BUG();
+}
+
+static int rcar2_uart_tx_ready(struct serial_port *port)
+{
+    struct rcar2_uart *uart = port->uart;
+    uint16_t cnt;
+
+    /* Check for empty space in TX FIFO */
+    if ( !(rcar2_readw(uart, SCIF_SCFSR) & SCFSR_TDFE) )
+        return 0;
+
+    /*
+     * It seems that the Framework has a data bytes to send.
+     * Enable TX Interrupt disabled from interrupt handler before
+     */
+    if ( uart->irq_en )
+        rcar2_writew(uart, SCIF_SCSCR, rcar2_readw(uart, SCIF_SCSCR) |
+                     SCSCR_TIE);
+
+     /* Check number of data bytes stored in TX FIFO */
+    cnt = rcar2_readw(uart, SCIF_SCFDR) >> 8;
+    ASSERT( cnt >= 0 && cnt <= SCIF_FIFO_MAX_SIZE );
+
+    return (SCIF_FIFO_MAX_SIZE - cnt);
+}
+
+static void rcar2_uart_putc(struct serial_port *port, char c)
+{
+    struct rcar2_uart *uart = port->uart;
+
+    rcar2_writeb(uart, SCIF_SCFTDR, c);
+    /* Clear required TX flags */
+    rcar2_writew(uart, SCIF_SCFSR, rcar2_readw(uart, SCIF_SCFSR) &
+                 ~(SCFSR_TEND | SCFSR_TDFE));
+}
+
+static int rcar2_uart_getc(struct serial_port *port, char *pc)
+{
+    struct rcar2_uart *uart = port->uart;
+
+    /* Check for available data bytes in RX FIFO */
+    if ( !(rcar2_readw(uart, SCIF_SCFSR) & (SCFSR_RDF | SCFSR_DR)) )
+        return 0;
+
+    *pc = rcar2_readb(uart, SCIF_SCFRDR);
+
+    /* dummy read */
+    rcar2_readw(uart, SCIF_SCFSR);
+    /* Clear required RX flags */
+    rcar2_writew(uart, SCIF_SCFSR, ~(SCFSR_RDF | SCFSR_DR));
+
+    return 1;
+}
+
+static int __init rcar2_uart_irq(struct serial_port *port)
+{
+    struct rcar2_uart *uart = port->uart;
+
+    return ((uart->irq > 0) ? uart->irq : -1);
+}
+
+static const struct vuart_info *rcar2_vuart_info(struct serial_port *port)
+{
+    struct rcar2_uart *uart = port->uart;
+
+    return &uart->vuart;
+}
+
+static struct uart_driver __read_mostly rcar2_uart_driver = {
+    .init_preirq  = rcar2_uart_init_preirq,
+    .init_postirq = rcar2_uart_init_postirq,
+    .endboot      = NULL,
+    .suspend      = rcar2_uart_suspend,
+    .resume       = rcar2_uart_resume,
+    .tx_ready     = rcar2_uart_tx_ready,
+    .putc         = rcar2_uart_putc,
+    .getc         = rcar2_uart_getc,
+    .irq          = rcar2_uart_irq,
+    .vuart_info   = rcar2_vuart_info,
+};
+
+static int __init rcar2_uart_init(struct dt_device_node *dev,
+                                 const void *data)
+{
+    const char *config = data;
+    struct rcar2_uart *uart;
+    int res;
+    u64 addr, size;
+
+    if ( strcmp(config, "") )
+        printk("WARNING: UART configuration is not supported\n");
+
+    uart = &rcar2_com;
+
+    uart->clock_hz  = SCIF_CLK_FREQ;
+    uart->baud      = BAUD_AUTO;
+    uart->data_bits = 8;
+    uart->parity    = PARITY_NONE;
+    uart->stop_bits = 1;
+
+    res = dt_device_get_address(dev, 0, &addr, &size);
+    if ( res )
+    {
+        printk("rcar2-uart: Unable to retrieve the base"
+                     " address of the UART\n");
+        return res;
+    }
+
+    uart->regs = ioremap_nocache(addr, size);
+    if ( !uart->regs )
+    {
+        printk("rcar2-uart: Unable to map the UART memory\n");
+        return -ENOMEM;
+    }
+
+    res = platform_get_irq(dev, 0);
+    if ( res < 0 )
+    {
+        printk("rcar2-uart: Unable to retrieve the IRQ\n");
+        return res;
+    }
+    uart->irq = res;
+
+    uart->vuart.base_addr  = addr;
+    uart->vuart.size       = size;
+    uart->vuart.data_off   = SCIF_SCFTDR;
+    uart->vuart.status_off = SCIF_SCFSR;
+    uart->vuart.status     = SCFSR_TDFE;
+
+    /* Register with generic serial driver */
+    serial_register_uart(SERHND_DTUART, &rcar2_uart_driver, uart);
+
+    dt_device_set_used_by(dev, DOMID_XEN);
+
+    return 0;
+}
+
+static const char * const rcar2_uart_dt_compat[] __initconst =
+{
+    "renesas,scif",
+    NULL
+};
+
+DT_DEVICE_START(rcar2_uart, "R-Car Gen2 UART", DEVICE_SERIAL)
+    .compatible = rcar2_uart_dt_compat,
+    .init = rcar2_uart_init,
+DT_DEVICE_END
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.