summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei Warkentin <andreiw@motorola.com>2011-02-15 16:18:59 -0600
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:37:00 -0800
commitf9b5a9db5af2202f0b1a66260f166d3619a1779d (patch)
treed43bdbed4c23b82172df38078225f9e58fea7e83
parent3d24ea9f00e92a4754e4afa7e3b1e5ef9eb79254 (diff)
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 <andreiw@motorola.com>
-rw-r--r--drivers/serial/tegra_hsuart.c87
1 files 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;