From aaf98d1380d04fa16584f41b5b4fcf184b9eac85 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Tue, 15 Feb 2011 16:18:59 -0600 Subject: [PATCH] serial: tegra_hsuart: Clean-up FIFO flush and set baudrate operations. 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 --- drivers/serial/tegra_hsuart.c | 87 ++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index 09f5f454683c..2c70ac7a7893 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -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; -- 2.34.1