linux-mips
[Top] [All Lists]

[PATCH 4/5] mips: msp71xx/serial: add workaround for DW UART

To: linux-serial@vger.kernel.org
Subject: [PATCH 4/5] mips: msp71xx/serial: add workaround for DW UART
From: Jamie Iles <jamie@jamieiles.com>
Date: Mon, 15 Aug 2011 10:17:54 +0100
Cc: arnd@arndb.de, Jamie Iles <jamie@jamieiles.com>, Ralf Baechle <ralf@linux-mips.org>, Alan Cox <alan@linux.intel.com>
In-reply-to: <1313399875-11006-1-git-send-email-jamie@jamieiles.com>
References: <1313399875-11006-1-git-send-email-jamie@jamieiles.com>
Resent-date: Mon, 15 Aug 2011 13:42:27 +0100
Resent-from: Ralf Baechle <ralf@linux-mips.org>
Resent-message-id: <20110815124227.GB25536@linux-mips.org>
Resent-to: linux-mips@linux-mips.org
Sender: linux-mips-bounce@linux-mips.org
The Synopsys DesignWare UART in pmc-sierra msp71xx has an extra feature
where the UART detects a write attempt to the LCR whilst busy and raises
an interrupt.  The driver needs to clear the interrupt and rewrite the
LCR.  Move this into platform code and out of the 8250 driver.

Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Alan Cox <alan@linux.intel.com>
Signed-off-by: Jamie Iles <jamie@jamieiles.com>
---
 arch/mips/pmc-sierra/msp71xx/msp_serial.c |   69 +++++++++++++++++++++++++++--
 1 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/arch/mips/pmc-sierra/msp71xx/msp_serial.c 
b/arch/mips/pmc-sierra/msp71xx/msp_serial.c
index c3247b5..a1c7c7d 100644
--- a/arch/mips/pmc-sierra/msp71xx/msp_serial.c
+++ b/arch/mips/pmc-sierra/msp71xx/msp_serial.c
@@ -27,6 +27,7 @@
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
+#include <linux/slab.h>
 
 #include <asm/bootinfo.h>
 #include <asm/io.h>
@@ -38,6 +39,55 @@
 #include <msp_int.h>
 #include <msp_regs.h>
 
+struct msp_uart_data {
+       int     last_lcr;
+};
+
+static void msp_serial_out(struct uart_port *p, int offset, int value)
+{
+       struct msp_uart_data *d = p->private_data;
+
+       if (offset == UART_LCR)
+               d->last_lcr = value;
+
+       offset <<= p->regshift;
+       writeb(value, p->membase + offset);
+}
+
+static unsigned int msp_serial_in(struct uart_port *p, int offset)
+{
+       offset <<= p->regshift;
+
+       return readb(p->membase + offset);
+}
+
+static int msp_serial_handle_irq(struct uart_port *p)
+{
+       struct msp_uart_data *d = p->private_data;
+       unsigned int iir = readb(p->membase + (UART_IIR << p->regshift));
+
+       if (serial8250_handle_irq(p, iir)) {
+               return 1;
+       } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
+               /*
+                * The DesignWare APB UART has an Busy Detect (0x07) interrupt
+                * meaning an LCR write attempt occurred while the UART was
+                * busy. The interrupt must be cleared by reading the UART
+                * status register (USR) and the LCR re-written.
+                *
+                * Note: MSP reserves 0x20 bytes of address space for the UART
+                * and the USR is mapped in a separate block at an offset of
+                * 0xc0 from the start of the UART.
+                */
+               (void)readb(p->membase + 0xc0);
+               writeb(d->last_lcr, p->membase + (UART_LCR << p->regshift));
+
+               return 1;
+       }
+
+       return 0;
+}
+
 void __init msp_serial_setup(void)
 {
        char    *s;
@@ -59,13 +109,22 @@ void __init msp_serial_setup(void)
        up.irq          = MSP_INT_UART0;
        up.uartclk      = uartclk;
        up.regshift     = 2;
-       up.iotype       = UPIO_DWAPB; /* UPIO_MEM like */
+       up.iotype       = UPIO_MEM;
        up.flags        = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
        up.type         = PORT_16550A;
        up.line         = 0;
-       up.private_data         = (void*)UART0_STATUS_REG;
-       if (early_serial_setup(&up))
+       up.serial_out   = msp_serial_out;
+       up.serial_in    = msp_serial_in;
+       up.handle_irq   = msp_serial_handle_irq;
+       up.private_data = kzalloc(sizeof(struct msp_uart_data), GFP_KERNEL);
+       if (!up.private_data) {
+               pr_err("failed to allocate uart private data\n");
+               return;
+       }
+       if (early_serial_setup(&up)) {
+               kfree(up.private_data);
                pr_err("Early serial init of port 0 failed\n");
+       }
 
        /* Initialize the second serial port, if one exists */
        switch (mips_machtype) {
@@ -88,6 +147,8 @@ void __init msp_serial_setup(void)
        up.irq          = MSP_INT_UART1;
        up.line         = 1;
        up.private_data         = (void*)UART1_STATUS_REG;
-       if (early_serial_setup(&up))
+       if (early_serial_setup(&up)) {
+               kfree(up.private_data);
                pr_err("Early serial init of port 1 failed\n");
+       }
 }
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

<Prev in Thread] Current Thread [Next in Thread>
  • [PATCH 4/5] mips: msp71xx/serial: add workaround for DW UART, Jamie Iles <=