---
config/arm32.mk | 1 +
xen/drivers/char/Makefile | 1 +
xen/drivers/char/rcar2-uart.c | 366 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 368 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..7ace6ad
--- /dev/null
+++ b/xen/drivers/char/rcar2-uart.c
@@ -0,0 +1,366 @@
+/*
+ * xen/drivers/char/rcar2-uart.c
+ *
+ * Driver for R-Car Gen2 UART.
+ *
+ * Oleksandr Tyshchenko <oleksandr.tyshche...@globallogic.com>
+ * 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;
+} 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);
+
+ /* 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);
+
+ 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;
+ }
+}
+
+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);
+ 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);
+}
+
+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;
+
+ /* 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 void rcar2_uart_start_tx(struct serial_port *port)
+{
+ struct rcar2_uart *uart = port->uart;
+
+ rcar2_writew(uart, SCIF_SCSCR, rcar2_readw(uart, SCIF_SCSCR) | SCSCR_TIE);
+}
+
+static void rcar2_uart_stop_tx(struct serial_port *port)
+{
+ struct rcar2_uart *uart = port->uart;
+
+ rcar2_writew(uart, SCIF_SCSCR, rcar2_readw(uart, SCIF_SCSCR) & ~SCSCR_TIE);
+}
+
+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,
+ .start_tx = rcar2_uart_start_tx,
+ .stop_tx = rcar2_uart_stop_tx,
+ .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;
+ }
+
+ res = platform_get_irq(dev, 0);
+ if ( res < 0 )
+ {
+ printk("rcar2-uart: Unable to retrieve the IRQ\n");
+ return res;
+ }
+ uart->irq = res;
+
+ uart->regs = ioremap_nocache(addr, size);
+ if ( !uart->regs )
+ {
+ printk("rcar2-uart: Unable to map the UART memory\n");
+ return -ENOMEM;
+ }
+
+ 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:
+ */