ARM: 6157/2: PL011 TX/RX split of LCR for ST-Ericssons derivative
authorLinus Walleij <linus.walleij@stericsson.com>
Wed, 2 Jun 2010 07:13:52 +0000 (08:13 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Tue, 27 Jul 2010 09:43:47 +0000 (10:43 +0100)
In the ST-Ericsson version of the PL011 the TX and RX have different
control registers.

Cc: Alessandro Rubini <rubini@unipv.it>
Signed-off-by: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
drivers/serial/amba-pl011.c
include/linux/amba/serial.h

index eb4cb480b93e9be344f9f84bbd574c68fe3ec8e6..5644cf2385bbad2f60cbb5dd251395b8d6d2be8e 100644 (file)
 struct uart_amba_port {
        struct uart_port        port;
        struct clk              *clk;
-       unsigned int            im;     /* interrupt mask */
+       unsigned int            im;             /* interrupt mask */
        unsigned int            old_status;
-       unsigned int            ifls;   /* vendor-specific */
+       unsigned int            ifls;           /* vendor-specific */
+       unsigned int            lcrh_tx;        /* vendor-specific */
+       unsigned int            lcrh_rx;        /* vendor-specific */
        bool                    autorts;
 };
 
@@ -79,16 +81,22 @@ struct uart_amba_port {
 struct vendor_data {
        unsigned int            ifls;
        unsigned int            fifosize;
+       unsigned int            lcrh_tx;
+       unsigned int            lcrh_rx;
 };
 
 static struct vendor_data vendor_arm = {
        .ifls                   = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
        .fifosize               = 16,
+       .lcrh_tx                = UART011_LCRH,
+       .lcrh_rx                = UART011_LCRH,
 };
 
 static struct vendor_data vendor_st = {
        .ifls                   = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
        .fifosize               = 64,
+       .lcrh_tx                = ST_UART011_LCRH_TX,
+       .lcrh_rx                = ST_UART011_LCRH_RX,
 };
 
 static void pl011_stop_tx(struct uart_port *port)
@@ -327,12 +335,12 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
        unsigned int lcr_h;
 
        spin_lock_irqsave(&uap->port.lock, flags);
-       lcr_h = readw(uap->port.membase + UART011_LCRH);
+       lcr_h = readw(uap->port.membase + uap->lcrh_tx);
        if (break_state == -1)
                lcr_h |= UART01x_LCRH_BRK;
        else
                lcr_h &= ~UART01x_LCRH_BRK;
-       writew(lcr_h, uap->port.membase + UART011_LCRH);
+       writew(lcr_h, uap->port.membase + uap->lcrh_tx);
        spin_unlock_irqrestore(&uap->port.lock, flags);
 }
 
@@ -393,7 +401,17 @@ static int pl011_startup(struct uart_port *port)
        writew(cr, uap->port.membase + UART011_CR);
        writew(0, uap->port.membase + UART011_FBRD);
        writew(1, uap->port.membase + UART011_IBRD);
-       writew(0, uap->port.membase + UART011_LCRH);
+       writew(0, uap->port.membase + uap->lcrh_rx);
+       if (uap->lcrh_tx != uap->lcrh_rx) {
+               int i;
+               /*
+                * Wait 10 PCLKs before writing LCRH_TX register,
+                * to get this delay write read only register 10 times
+                */
+               for (i = 0; i < 10; ++i)
+                       writew(0xff, uap->port.membase + UART011_MIS);
+               writew(0, uap->port.membase + uap->lcrh_tx);
+       }
        writew(0, uap->port.membase + UART01x_DR);
        while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
                barrier();
@@ -422,10 +440,19 @@ static int pl011_startup(struct uart_port *port)
        return retval;
 }
 
+static void pl011_shutdown_channel(struct uart_amba_port *uap,
+                                       unsigned int lcrh)
+{
+      unsigned long val;
+
+      val = readw(uap->port.membase + lcrh);
+      val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
+      writew(val, uap->port.membase + lcrh);
+}
+
 static void pl011_shutdown(struct uart_port *port)
 {
        struct uart_amba_port *uap = (struct uart_amba_port *)port;
-       unsigned long val;
 
        /*
         * disable all interrupts
@@ -450,9 +477,9 @@ static void pl011_shutdown(struct uart_port *port)
        /*
         * disable break condition and fifos
         */
-       val = readw(uap->port.membase + UART011_LCRH);
-       val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
-       writew(val, uap->port.membase + UART011_LCRH);
+       pl011_shutdown_channel(uap, uap->lcrh_rx);
+       if (uap->lcrh_rx != uap->lcrh_tx)
+               pl011_shutdown_channel(uap, uap->lcrh_tx);
 
        /*
         * Shut down the clock producer
@@ -561,7 +588,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
         * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
         * ----------^----------^----------^----------^-----
         */
-       writew(lcr_h, port->membase + UART011_LCRH);
+       writew(lcr_h, port->membase + uap->lcrh_rx);
+       if (uap->lcrh_rx != uap->lcrh_tx) {
+               int i;
+               /*
+                * Wait 10 PCLKs before writing LCRH_TX register,
+                * to get this delay write read only register 10 times
+                */
+               for (i = 0; i < 10; ++i)
+                       writew(0xff, uap->port.membase + UART011_MIS);
+               writew(lcr_h, port->membase + uap->lcrh_tx);
+       }
        writew(old_cr, port->membase + UART011_CR);
 
        spin_unlock_irqrestore(&port->lock, flags);
@@ -688,7 +725,7 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud,
        if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) {
                unsigned int lcr_h, ibrd, fbrd;
 
-               lcr_h = readw(uap->port.membase + UART011_LCRH);
+               lcr_h = readw(uap->port.membase + uap->lcrh_tx);
 
                *parity = 'n';
                if (lcr_h & UART01x_LCRH_PEN) {
@@ -800,6 +837,8 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id)
        }
 
        uap->ifls = vendor->ifls;
+       uap->lcrh_rx = vendor->lcrh_rx;
+       uap->lcrh_tx = vendor->lcrh_tx;
        uap->port.dev = &dev->dev;
        uap->port.mapbase = dev->res.start;
        uap->port.membase = base;
index 5a5a7fd62490ca34fe27baa952f48de8831d8197..93c96a66c5180a845236630db2560644536df23c 100644 (file)
 #define UART01x_FR             0x18    /* Flag register (Read only). */
 #define UART010_IIR            0x1C    /* Interrupt indentification register (Read). */
 #define UART010_ICR            0x1C    /* Interrupt clear register (Write). */
+#define ST_UART011_LCRH_RX     0x1C    /* Rx line control register. */
 #define UART01x_ILPR           0x20    /* IrDA low power counter register. */
 #define UART011_IBRD           0x24    /* Integer baud rate divisor register. */
 #define UART011_FBRD           0x28    /* Fractional baud rate divisor register. */
 #define UART011_LCRH           0x2c    /* Line control register. */
+#define ST_UART011_LCRH_TX     0x2c    /* Tx Line control register. */
 #define UART011_CR             0x30    /* Control register. */
 #define UART011_IFLS           0x34    /* Interrupt fifo level select. */
 #define UART011_IMSC           0x38    /* Interrupt mask. */