diff mbox

[RFC,25/29] xen/arm: Add exynos 4210 UART support

Message ID dc6936fd45909059c5656ad644d0f6ce074095b0.1367188423.git.julien.grall@linaro.org
State Changes Requested, archived
Headers show

Commit Message

Julien Grall April 28, 2013, 11:02 p.m. UTC
Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 config/arm32.mk                 |    1 +
 xen/drivers/char/Makefile       |    1 +
 xen/drivers/char/exynos5-uart.c |  346 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 348 insertions(+)
 create mode 100644 xen/drivers/char/exynos5-uart.c

Comments

Ian Campbell April 29, 2013, 4:51 p.m. UTC | #1
On Mon, 2013-04-29 at 00:02 +0100, Julien Grall wrote:
> Signed-off-by: Julien Grall <julien.grall@linaro.org>

WIthout a datasheet there isn't much review to be done, 
> ---
>  config/arm32.mk                 |    1 +
>  xen/drivers/char/Makefile       |    1 +
>  xen/drivers/char/exynos5-uart.c |  346 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 348 insertions(+)
>  create mode 100644 xen/drivers/char/exynos5-uart.c
> 
> diff --git a/config/arm32.mk b/config/arm32.mk
> index 83a7767..593a1d1 100644
> --- a/config/arm32.mk
> +++ b/config/arm32.mk
> @@ -19,6 +19,7 @@ CFLAGS += -marm
>  #   - pl011: printk with PL011 UART
>  CONFIG_EARLY_PRINTK := none
>  HAS_PL011 := y
> +HAS_EXYNOS5 := y
> 
>  # Use only if calling $(LD) directly.
>  #LDFLAGS_DIRECT_OpenBSD = _obsd
> diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
> index e68a54a..12a4b49 100644
> --- a/xen/drivers/char/Makefile
> +++ b/xen/drivers/char/Makefile
> @@ -1,6 +1,7 @@
>  obj-y += console.o
>  obj-$(HAS_NS16550) += ns16550.o
>  obj-$(HAS_PL011) += pl011.o
> +obj-$(HAS_EXYNOS5) += exynos5-uart.o
>  obj-$(HAS_EHCI) += ehci-dbgp.o
>  obj-$(CONFIG_ARM) += arm-uart.o
>  obj-y += serial.o
> diff --git a/xen/drivers/char/exynos5-uart.c b/xen/drivers/char/exynos5-uart.c
> new file mode 100644
> index 0000000..1bae153
> --- /dev/null
> +++ b/xen/drivers/char/exynos5-uart.c
> @@ -0,0 +1,346 @@
> +/*
> + * xen/drivers/char/exynos5-uart.c
> + *
> + * Driver for Exynos 4210 UART.
> + *
> + * Anthony PERARD <anthony.perard@citrix.com>
> + * Copyright (c) 2012 Citrix Systems.
> + *
> + * 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 <asm/early_printk.h>
> +#include <asm/device.h>
> +
> +static struct exynos5_uart {
> +    unsigned int baud, clock_hz, data_bits, parity, stop_bits;
> +    struct dt_irq irq;
> +    volatile uint32_t *regs;
> +    struct irqaction irqaction;
> +} exynos5_com[2] = {{0}};
> +
> +/* register addresses */
> +#define ULCON     (0x00/4)
> +#define UCON      (0x04/4)
> +#define UFCON     (0x08/4)
> +#define UMCON     (0x0c/4)
> +#define UTRSTAT   (0x10/4)
> +#define UERSTAT   (0x14/4)
> +#define UFSTAT    (0x18/4)
> +#define UMSTAT    (0x1c/4)
> +#define UTXH      (0x20/4)
> +#define URXH      (0x24/4)
> +#define UBRDIV    (0x28/4)
> +#define UFRACVAL  (0x2c/4)
> +#define UINTP     (0x30/4)
> +#define UINTS     (0x34/4)
> +#define UINTM     (0x38/4)
> +
> +/* ULCON */
> +#define RXIRQ (0x1<<0)
> +#define RXDMA (0x2<<0)
> +#define TXIRQ (0x1<<2)
> +#define TXDMA (0x2<<2)
> +
> +/* UFCON */
> +#define FIFO_TX_RESET (1<<2)
> +#define FIFO_RX_RESET (1<<1)
> +#define FIFO_EN   (1<<0)
> +
> +/* UMCON */
> +#define INT_EN (1<<3)
> +
> +/* UTRSTAT */
> +#define TXE     (1<<2)
> +#define TXFE    (1<<1)
> +#define RXDR    (1<<0)
> +
> +/* Interrupt bits (UINTP, UINTS, UINTM) */
> +#define MODEM   (1<<3)
> +#define TXD     (1<<2)
> +#define ERROR   (1<<1)
> +#define RXD     (1<<0)
> +#define ALLI    (MODEM|TXD|ERROR|RXD)
> +
> +/* These parity settings can be ORed directly into the ULCON. */
> +#define PARITY_NONE  (0)
> +#define PARITY_ODD   (0x4)
> +#define PARITY_EVEN  (0x5)
> +#define FORCED_CHECKED_AS_ONE (0x6)
> +#define FORCED_CHECKED_AS_ZERO (0x7)
> +
> +static void exynos5_uart_interrupt(int irq, void *data, struct cpu_user_regs *regs)
> +{
> +    struct serial_port *port = data;
> +    struct exynos5_uart *uart = port->uart;
> +    unsigned int status = uart->regs[UINTP];
> +
> +    if ( status )
> +    {
> +        do
> +        {
> +            // clear all pending interrept
> +            // but should take care of ERROR and MODEM

Xen comments are always /* */ I think. A bunch of instance of this in
this patch.

> +
> +            if ( status & ERROR )
> +            {
> +                int error_bit = uart->regs[UERSTAT] & 0xf;
> +                if ( error_bit & (1 << 0) )
> +                    printk(XENLOG_ERR "uart: overrun error\n");
> +                if ( error_bit & (1 << 1) )
> +                    printk(XENLOG_ERR "uart: parity error\n");
> +                if ( error_bit & (1 << 2) )
> +                    printk(XENLOG_ERR "uart: frame error\n");
> +                if ( error_bit & (1 << 3) )

Can you #define these bits?

[...]
> +    // reset FIFO_TX_RESET | FIFO_RX_RESET |
> +    uart->regs[UFCON] = (0x6 << 8) | FIFO_EN;

#define
> +
> +    /* Enable the UART for RX and TX */
> +    // level tx/rx interrupt,only rx
> +    // enable rx timeout interrupt
> +    uart->regs[UCON] = (0 << 9) | (0 << 8) | RXIRQ | TXIRQ | ( 1 << 7);

More #defines, I expect there's a bunch more too ;-)

> +}
> +
> +static void __init exynos5_uart_init_postirq(struct serial_port *port)
> +{
> +    struct exynos5_uart *uart = port->uart;
> +    int rc;
> +
> +    if ( uart->irq.irq > 0 )
> +    {
> +        uart->irqaction.handler = exynos5_uart_interrupt;
> +        uart->irqaction.name    = "exynos5_uart";
> +        uart->irqaction.dev_id  = port;
> +        if ( (rc = setup_irq(uart->irq.irq, &uart->irqaction)) != 0 )
> +            printk("ERROR: Failed to allocate exynos5_uart IRQ %d\n",
> +                   uart->irq.irq);
> +
> +        /* Unmask interrupts */
> +        uart->regs[UINTM] = 0; //MODEM|TXD|ERROR; // only have rx interrupt

Left over debug?

Ian.
Anthony PERARD April 29, 2013, 6:12 p.m. UTC | #2
On 29/04/13 17:51, Ian Campbell wrote:
> On Mon, 2013-04-29 at 00:02 +0100, Julien Grall wrote:
>> Signed-off-by: Julien Grall <julien.grall@linaro.org>

Julien, could you add my signed off before yours?

> WIthout a datasheet there isn't much review to be done, 

You can find the datasheet in the "Exynos 5 Dual User Manual (Public)
REV1.00.pdf" document. Hope it's enough to have the name.
Julien Grall April 29, 2013, 6:21 p.m. UTC | #3
On 04/29/2013 07:12 PM, Anthony PERARD wrote:

> On 29/04/13 17:51, Ian Campbell wrote:
>> On Mon, 2013-04-29 at 00:02 +0100, Julien Grall wrote:
>>> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> 
> Julien, could you add my signed off before yours?


Yes. I will add to the next patch series.
Ian Campbell April 30, 2013, 9:22 a.m. UTC | #4
On Mon, 2013-04-29 at 19:21 +0100, Julien Grall wrote:
> On 04/29/2013 07:12 PM, Anthony PERARD wrote:
> 
> > On 29/04/13 17:51, Ian Campbell wrote:
> >> On Mon, 2013-04-29 at 00:02 +0100, Julien Grall wrote:
> >>> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> > 
> > Julien, could you add my signed off before yours?
> 
> 
> Yes. I will add to the next patch series.

If Anthony is the primary author of the patch then you should include a
From: psuedo-header as the first line of the mail. If you "git commit
--amend --author='Anthony PERARD <anthon...@citrix.com>' then git send
email and friends will do the right thing.

If there are other patches which need to be attributed to Anthony then
please do so as well.

Ian.
diff mbox

Patch

diff --git a/config/arm32.mk b/config/arm32.mk
index 83a7767..593a1d1 100644
--- a/config/arm32.mk
+++ b/config/arm32.mk
@@ -19,6 +19,7 @@  CFLAGS += -marm
 #   - pl011: printk with PL011 UART
 CONFIG_EARLY_PRINTK := none
 HAS_PL011 := y
+HAS_EXYNOS5 := y
 
 # Use only if calling $(LD) directly.
 #LDFLAGS_DIRECT_OpenBSD = _obsd
diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile
index e68a54a..12a4b49 100644
--- a/xen/drivers/char/Makefile
+++ b/xen/drivers/char/Makefile
@@ -1,6 +1,7 @@ 
 obj-y += console.o
 obj-$(HAS_NS16550) += ns16550.o
 obj-$(HAS_PL011) += pl011.o
+obj-$(HAS_EXYNOS5) += exynos5-uart.o
 obj-$(HAS_EHCI) += ehci-dbgp.o
 obj-$(CONFIG_ARM) += arm-uart.o
 obj-y += serial.o
diff --git a/xen/drivers/char/exynos5-uart.c b/xen/drivers/char/exynos5-uart.c
new file mode 100644
index 0000000..1bae153
--- /dev/null
+++ b/xen/drivers/char/exynos5-uart.c
@@ -0,0 +1,346 @@ 
+/*
+ * xen/drivers/char/exynos5-uart.c
+ *
+ * Driver for Exynos 4210 UART.
+ *
+ * Anthony PERARD <anthony.perard@citrix.com>
+ * Copyright (c) 2012 Citrix Systems.
+ *
+ * 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 <asm/early_printk.h>
+#include <asm/device.h>
+
+static struct exynos5_uart {
+    unsigned int baud, clock_hz, data_bits, parity, stop_bits;
+    struct dt_irq irq;
+    volatile uint32_t *regs;
+    struct irqaction irqaction;
+} exynos5_com[2] = {{0}};
+
+/* register addresses */
+#define ULCON     (0x00/4)
+#define UCON      (0x04/4)
+#define UFCON     (0x08/4)
+#define UMCON     (0x0c/4)
+#define UTRSTAT   (0x10/4)
+#define UERSTAT   (0x14/4)
+#define UFSTAT    (0x18/4)
+#define UMSTAT    (0x1c/4)
+#define UTXH      (0x20/4)
+#define URXH      (0x24/4)
+#define UBRDIV    (0x28/4)
+#define UFRACVAL  (0x2c/4)
+#define UINTP     (0x30/4)
+#define UINTS     (0x34/4)
+#define UINTM     (0x38/4)
+
+/* ULCON */
+#define RXIRQ (0x1<<0)
+#define RXDMA (0x2<<0)
+#define TXIRQ (0x1<<2)
+#define TXDMA (0x2<<2)
+
+/* UFCON */
+#define FIFO_TX_RESET (1<<2)
+#define FIFO_RX_RESET (1<<1)
+#define FIFO_EN   (1<<0)
+
+/* UMCON */
+#define INT_EN (1<<3)
+
+/* UTRSTAT */
+#define TXE     (1<<2)
+#define TXFE    (1<<1)
+#define RXDR    (1<<0)
+
+/* Interrupt bits (UINTP, UINTS, UINTM) */
+#define MODEM   (1<<3)
+#define TXD     (1<<2)
+#define ERROR   (1<<1)
+#define RXD     (1<<0)
+#define ALLI    (MODEM|TXD|ERROR|RXD)
+
+/* These parity settings can be ORed directly into the ULCON. */
+#define PARITY_NONE  (0)
+#define PARITY_ODD   (0x4)
+#define PARITY_EVEN  (0x5)
+#define FORCED_CHECKED_AS_ONE (0x6)
+#define FORCED_CHECKED_AS_ZERO (0x7)
+
+static void exynos5_uart_interrupt(int irq, void *data, struct cpu_user_regs *regs)
+{
+    struct serial_port *port = data;
+    struct exynos5_uart *uart = port->uart;
+    unsigned int status = uart->regs[UINTP];
+
+    if ( status )
+    {
+        do
+        {
+            // clear all pending interrept
+            // but should take care of ERROR and MODEM
+
+            if ( status & ERROR )
+            {
+                int error_bit = uart->regs[UERSTAT] & 0xf;
+                if ( error_bit & (1 << 0) )
+                    printk(XENLOG_ERR "uart: overrun error\n");
+                if ( error_bit & (1 << 1) )
+                    printk(XENLOG_ERR "uart: parity error\n");
+                if ( error_bit & (1 << 2) )
+                    printk(XENLOG_ERR "uart: frame error\n");
+                if ( error_bit & (1 << 3) )
+                    printk(XENLOG_ERR "uart: break detected\n");
+                uart->regs[UINTP] = ERROR;
+            }
+
+
+            if ( status & (RXD|ERROR) )
+            {
+                /* uart->regs[UINTM] |= RXD|ERROR; */
+                serial_rx_interrupt(port, regs);
+                /* uart->regs[UINTM] &= ~(RXD|ERROR); */
+                uart->regs[UINTP] = RXD|ERROR;
+            }
+
+            if ( status & (TXD|MODEM) )
+            {
+                /* uart->regs[UINTM] |= TXD|MODEM; */
+                serial_tx_interrupt(port, regs);
+                /* uart->regs[UINTM] &= ~(TXD|MODEM); */
+                uart->regs[UINTP] = TXD|MODEM;
+            }
+
+            status = uart->regs[UINTP];
+        } while ( status != 0 );
+    }
+}
+
+static void __init exynos5_uart_init_preirq(struct serial_port *port)
+{
+    struct exynos5_uart *uart = port->uart;
+    unsigned int divisor;
+
+    /* reset, TX/RX disables */
+    uart->regs[UCON] = 0x0;
+
+    /* No Interrupt, auto flow control */
+    uart->regs[UMCON] = 0x0;
+
+    /* Line control and baud-rate generator. */
+    if ( uart->baud != BAUD_AUTO )
+    {
+        /* Baud rate specified: program it into the divisor latch. */
+        // div_val = ubrdiv + ufracval/16
+        // or
+        // div_val = (clock_uart/(baud*16))-1
+        divisor = ((uart->clock_hz) / (uart->baud)) - 1;
+        // FIXME will use a hacked divisor, assuming the src clock and bauds
+        uart->regs[UFRACVAL] = 53;
+        uart->regs[UBRDIV] = 4;
+        /* uart->regs[UFRACVAL] = divisor & 0xf; */
+        /* uart->regs[UBRDIV] = divisor >> 4; */
+    }
+    else
+    {
+        // TODO, should be updated
+        /* Baud rate already set: read it out from the divisor latch. */
+        //divisor = (uart->regs[IBRD] << 6) | uart->regs[FBRD];
+        //uart->baud = (uart->clock_hz << 2) / divisor;
+    }
+    uart->regs[ULCON] = ( (uart->data_bits - 5) << 0
+                          | ((uart->stop_bits - 1) << 2)
+                          | uart->parity << 3 );
+
+    /* Mask and clear the interrupts */
+    uart->regs[UINTM] = ALLI;
+    uart->regs[UINTP] = ALLI;
+
+    /* enable FIFO */
+    uart->regs[UFCON] = FIFO_TX_RESET | FIFO_RX_RESET;
+    while ( uart->regs[UFCON] & (FIFO_TX_RESET | FIFO_RX_RESET) )
+           ;
+    // reset FIFO_TX_RESET | FIFO_RX_RESET |
+    uart->regs[UFCON] = (0x6 << 8) | FIFO_EN;
+
+    /* Enable the UART for RX and TX */
+    // level tx/rx interrupt,only rx
+    // enable rx timeout interrupt
+    uart->regs[UCON] = (0 << 9) | (0 << 8) | RXIRQ | TXIRQ | ( 1 << 7);
+}
+
+static void __init exynos5_uart_init_postirq(struct serial_port *port)
+{
+    struct exynos5_uart *uart = port->uart;
+    int rc;
+
+    if ( uart->irq.irq > 0 )
+    {
+        uart->irqaction.handler = exynos5_uart_interrupt;
+        uart->irqaction.name    = "exynos5_uart";
+        uart->irqaction.dev_id  = port;
+        if ( (rc = setup_irq(uart->irq.irq, &uart->irqaction)) != 0 )
+            printk("ERROR: Failed to allocate exynos5_uart IRQ %d\n",
+                   uart->irq.irq);
+
+        /* Unmask interrupts */
+        uart->regs[UINTM] = 0; //MODEM|TXD|ERROR; // only have rx interrupt
+
+        /* Clear pending error interrupts */
+        uart->regs[UINTP] = ALLI;
+
+        /* Enable interrupts */
+        uart->regs[UMCON] |= INT_EN;
+    }
+}
+
+static void exynos5_uart_suspend(struct serial_port *port)
+{
+    BUG(); // XXX
+}
+
+static void exynos5_uart_resume(struct serial_port *port)
+{
+    BUG(); // XXX
+}
+
+static unsigned int exynos5_uart_tx_ready(struct serial_port *port)
+{
+    struct exynos5_uart *uart = port->uart;
+
+    // Tx FIFO full
+    if ( uart->regs[UFSTAT] & (1 << 24) )
+        return 0;
+    else
+    {
+        int x = 16 - ((uart->regs[UFSTAT] >> 16) & 0xff);
+        // Tx FIFO count
+        if ( x > 0 )
+            return x;
+        else if ( x == 0 )
+            return 0;
+        else {
+            panic("unwanted value: %d\n", x);
+            return 0;
+        }
+    }
+}
+
+static void exynos5_uart_putc(struct serial_port *port, char c)
+{
+    struct exynos5_uart *uart = port->uart;
+    uart->regs[UTXH] = (uint32_t) (unsigned char) c;
+}
+
+static int exynos5_uart_getc(struct serial_port *port, char *pc)
+{
+    struct exynos5_uart *uart = port->uart;
+
+    // check if rx fifo is full or if there is something in it
+    if ( (uart->regs[UFSTAT] & (1 << 8)) || (uart->regs[UFSTAT] & 0xff) )
+    {
+        *pc = uart->regs[URXH] & 0xff;
+        return 1;
+    }
+    else
+        return 0;
+}
+
+static int __init exynos5_uart_irq(struct serial_port *port)
+{
+    struct exynos5_uart *uart = port->uart;
+    if ( uart->irq.irq > 0 )
+        return uart->irq.irq;
+    else
+        return -1;
+}
+
+static const struct dt_irq __init *exynos5_uart_dt_irq(struct serial_port *port)
+{
+    struct exynos5_uart *uart = port->uart;
+
+    return &uart->irq;
+}
+
+static struct uart_driver __read_mostly exynos5_uart_driver = {
+    .init_preirq  = exynos5_uart_init_preirq,
+    .init_postirq = exynos5_uart_init_postirq,
+    .endboot      = NULL,
+    .suspend      = exynos5_uart_suspend,
+    .resume       = exynos5_uart_resume,
+    .tx_ready     = exynos5_uart_tx_ready,
+    .putc         = exynos5_uart_putc,
+    .getc         = exynos5_uart_getc,
+    .irq          = exynos5_uart_irq,
+    .dt_irq_get   = exynos5_uart_dt_irq,
+};
+
+static int __init exynos5_uart_init(struct dt_device_node *dev,
+                                    const void *data)
+{
+    const struct serial_arm_defaults *defaults = data;
+    struct exynos5_uart *uart;
+    int res;
+
+    if ( (defaults->index < 0) || (defaults->index > 1) )
+        return -EINVAL;
+
+    uart = &exynos5_com[defaults->index];
+
+    /* uart->clock_hz  = 0x16e3600; */
+    uart->baud      = BAUD_AUTO;//115200;
+    uart->data_bits = 8;
+    uart->parity    = PARITY_NONE;
+    uart->stop_bits = 1;
+    uart->regs      = (uint32_t *) defaults->register_base_address;
+
+    res = dt_device_get_irq(dev, 0, &uart->irq);
+    if ( res )
+    {
+        early_printk("exynos5: Unable to retrieve the IRQ\n");
+        return res;
+    }
+
+    /* Register with generic serial driver. */
+    serial_register_uart(uart - exynos5_com, &exynos5_uart_driver, uart);
+
+    dt_device_set_used_by(dev, DT_USED_BY_XEN);
+
+    return 0;
+}
+
+static const char const *exynos5_dt_compat[] __initdata =
+{
+    "samsung,exynos4210-uart",
+    NULL
+};
+
+DT_DEVICE_START(exynos5, "Exynos5 UART", DEVICE_SERIAL)
+        .compatible = exynos5_dt_compat,
+        .init = exynos5_uart_init,
+DT_DEVICE_END
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */