serial: tegra_hsuart: Clean-up FIFO flush and set baudrate operations.
authorAndrei Warkentin <andreiw@motorola.com>
Tue, 15 Feb 2011 22:18:59 +0000 (16:18 -0600)
committerColin Cross <ccross@android.com>
Wed, 2 Mar 2011 00:41:59 +0000 (16:41 -0800)
Do necessary waits and fifo clear according to NV. Also use 115200
as the rate set while bringing-up rx dma during init, to decrease
start-up time.

Change-Id: I5211c75607cf2880ab2b1c22ab30f2c265534a1c
Signed-off-by: Andrei Warkentin <andreiw@motorola.com>
drivers/serial/tegra_hsuart.c

index 09f5f454683cb3e5ac5525a5521872164486babe..2c70ac7a78930dd47cfb4e3002c8251c71770919 100644 (file)
@@ -318,6 +318,51 @@ static void do_handle_rx_dma(struct tegra_uart_port *t)
                set_rts(t, true);
 }
 
+/* Wait for a symbol-time. */
+static void wait_sym_time(struct tegra_uart_port *t, unsigned int syms)
+{
+
+       /* Definitely have a start bit. */
+       unsigned int bits = 1;
+       switch (t->lcr_shadow & 3) {
+       case UART_LCR_WLEN5:
+               bits += 5;
+               break;
+       case UART_LCR_WLEN6:
+               bits += 6;
+               break;
+       case UART_LCR_WLEN7:
+               bits += 7;
+               break;
+       default:
+               bits += 8;
+               break;
+       }
+
+       /* Technically 5 bits gets 1.5 bits of stop... */
+       if (t->lcr_shadow & UART_LCR_STOP) {
+               bits += 2;
+       } else {
+               bits++;
+       }
+
+       if (t->lcr_shadow & UART_LCR_PARITY)
+               bits++;
+
+       if (likely(t->baud))
+               udelay(DIV_ROUND_UP(syms * bits * 1000000, t->baud));
+}
+
+/* Flush desired FIFO. */
+static void tegra_fifo_reset(struct tegra_uart_port *t, u8 fcr_bits)
+{
+       unsigned char fcr = t->fcr_shadow;
+       fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+       uart_writeb(t, fcr, UART_FCR);
+       uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */
+       wait_sym_time(t, 1); /* Wait for the flush to propagate. */
+}
+
 static char do_decode_rx_error(struct tegra_uart_port *t, u8 lsr)
 {
        char flag = TTY_NORMAL;
@@ -341,11 +386,8 @@ static char do_decode_rx_error(struct tegra_uart_port *t, u8 lsr)
                        dev_err(t->uport.dev, "Got Break\n");
                        t->uport.icount.brk++;
                        /* If FIFO read error without any data, reset Rx FIFO */
-                       if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) {
-                               unsigned char fcr = t->fcr_shadow;
-                               fcr |= UART_FCR_CLEAR_RCVR;
-                               uart_writeb(t, fcr, UART_FCR);
-                       }
+                       if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE))
+                               tegra_fifo_reset(t, UART_FCR_CLEAR_RCVR);
                }
        }
        return flag;
@@ -558,7 +600,6 @@ static void tegra_stop_rx(struct uart_port *u)
 
 static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
 {
-       unsigned char fcr;
        unsigned long flags;
 
        flush_work(&t->tx_work);
@@ -572,11 +613,7 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
        spin_lock_irqsave(&t->uport.lock, flags);
 
        /* Reset the Rx and Tx FIFOs */
-       fcr = t->fcr_shadow;
-       fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR;
-       uart_writeb(t, fcr, UART_FCR);
-
-       udelay(200);
+       tegra_fifo_reset(t, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR);
 
        clk_disable(t->clk);
        t->baud = 0;
@@ -603,7 +640,6 @@ static void tegra_uart_free_rx_dma(struct tegra_uart_port *t)
 
 static int tegra_uart_hw_init(struct tegra_uart_port *t)
 {
-       unsigned char fcr;
        unsigned char ier;
 
        dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n");
@@ -624,20 +660,6 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t)
 
        t->rx_in_progress = 0;
 
-       /* Reset the FIFO twice with some delay to make sure that the FIFOs are
-        * really flushed. Wait is needed as the clearing needs to cross
-        * multiple clock domains.
-        * */
-       t->fcr_shadow = UART_FCR_ENABLE_FIFO;
-
-       fcr = t->fcr_shadow;
-       fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR;
-       uart_writeb(t, fcr, UART_FCR);
-
-       udelay(100);
-       uart_writeb(t, t->fcr_shadow, UART_FCR);
-       udelay(100);
-
        /* Set the trigger level
         *
         * For PIO mode:
@@ -659,6 +681,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t)
         *  Set the Tx trigger to 4. This should match the DMA burst size that
         *  programmed in the DMA registers.
         * */
+       t->fcr_shadow = UART_FCR_ENABLE_FIFO;
        t->fcr_shadow |= UART_FCR_R_TRIG_01;
        t->fcr_shadow |= TEGRA_UART_TX_TRIG_8B;
        uart_writeb(t, t->fcr_shadow, UART_FCR);
@@ -667,7 +690,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t)
                /* initialize the UART for a simple default configuration
                  * so that the receive DMA buffer may be enqueued */
                t->lcr_shadow = 3;  /* no parity, stop, 8 data bits */
-               tegra_set_baudrate(t, 9600);
+               tegra_set_baudrate(t, 115200);
                 t->fcr_shadow |= UART_FCR_DMA_SELECT;
                uart_writeb(t, t->fcr_shadow, UART_FCR);
                if (tegra_start_dma_rx(t)) {
@@ -995,8 +1018,10 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud)
 
        lcr &= ~UART_LCR_DLAB;
        uart_writeb(t, lcr, UART_LCR);
+       uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */
 
        t->baud = baud;
+       wait_sym_time(t, 2); /* wait two character intervals at new rate */
        dev_dbg(t->uport.dev, "Baud %u clock freq %lu and divisor of %u\n",
                baud, rate, divisor);
 }
@@ -1020,10 +1045,6 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
        if (t->rts_active)
                set_rts(t, false);
 
-       /* Baud rate */
-       baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000);
-       tegra_set_baudrate(t, baud);
-
        /* Parity */
        lcr = t->lcr_shadow;
        lcr &= ~UART_LCR_PARITY;
@@ -1067,6 +1088,10 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
        uart_writeb(t, lcr, UART_LCR);
        t->lcr_shadow = lcr;
 
+       /* Baud rate. */
+       baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000);
+       tegra_set_baudrate(t, baud);
+
        /* Flow control */
        if (termios->c_cflag & CRTSCTS) {
                mcr = t->mcr_shadow;