diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index 2433a8555270..33f4592cf6ac 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -73,6 +73,13 @@ const int dma_req_sel[] = { #define TEGRA_UART_MIN_DMA 16 #define TEGRA_UART_FIFO_SIZE 8 +/* Tx fifo trigger level setting in tegra uart is in + * reverse way then conventional uart */ +#define TEGRA_UART_TX_TRIG_16B 0x00 +#define TEGRA_UART_TX_TRIG_8B 0x10 +#define TEGRA_UART_TX_TRIG_4B 0x20 +#define TEGRA_UART_TX_TRIG_1B 0x30 + struct tegra_uart_port { struct uart_port uport; char port_name[32]; @@ -158,7 +165,7 @@ static void tegra_start_pio_tx(struct tegra_uart_port *t, unsigned int bytes) bytes = TEGRA_UART_FIFO_SIZE; t->fcr_shadow &= ~UART_FCR_T_TRIG_11; - t->fcr_shadow |= UART_FCR_T_TRIG_10; + t->fcr_shadow |= TEGRA_UART_TX_TRIG_8B; uart_writeb(t, t->fcr_shadow, UART_FCR); t->tx_in_progress = TEGRA_TX_PIO; t->tx_bytes = bytes; @@ -175,7 +182,7 @@ static void tegra_start_dma_tx(struct tegra_uart_port *t, unsigned long bytes) UART_XMIT_SIZE, DMA_TO_DEVICE); t->fcr_shadow &= ~UART_FCR_T_TRIG_11; - t->fcr_shadow |= UART_FCR_T_TRIG_01; + t->fcr_shadow |= TEGRA_UART_TX_TRIG_4B; uart_writeb(t, t->fcr_shadow, UART_FCR); t->tx_bytes = bytes & ~(sizeof(u32)-1); @@ -251,6 +258,10 @@ static void tegra_rx_dma_threshold_callback(struct tegra_dma_req *req) if (t->rts_active) set_rts(t, false); tegra_dma_dequeue(t->rx_dma); + + /* enqueue the request again */ + tegra_start_dma_rx(t); + if (t->rts_active) set_rts(t, true); @@ -293,8 +304,6 @@ static void tegra_rx_dma_complete_callback(struct tegra_dma_req *req) spin_unlock(&u->lock); tty_flip_buffer_push(u->state->port.tty); spin_lock(&u->lock); - - tegra_start_dma_rx(t); } /* Lock already taken */ @@ -303,6 +312,8 @@ static void do_handle_rx_dma(struct tegra_uart_port *t) if (t->rts_active) set_rts(t, false); tegra_dma_dequeue(t->rx_dma); + /* enqueue the request again */ + tegra_start_dma_rx(t); if (t->rts_active) set_rts(t, true); } @@ -530,6 +541,20 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *t) t->baud = 0; } +static void tegra_uart_free_rx_dma(struct tegra_uart_port *t) +{ + if (!t->use_rx_dma) + return; + + tegra_dma_free_channel(t->rx_dma); + + if (likely(t->rx_dma_req.dest_addr)) + dma_free_coherent(t->uport.dev, t->rx_dma_req.size, + t->rx_dma_req.virt_addr, t->rx_dma_req.dest_addr); + + t->use_rx_dma = false; +} + static int tegra_uart_hw_init(struct tegra_uart_port *t) { unsigned char fcr; @@ -557,7 +582,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) uart_writeb(t, fcr, UART_FCR); udelay(100); - uart_writeb(t, fcr, UART_FCR); + uart_writeb(t, t->fcr_shadow, UART_FCR); udelay(100); /* Set the trigger level @@ -582,12 +607,25 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) * programmed in the DMA registers. * */ t->fcr_shadow |= UART_FCR_R_TRIG_01; - t->fcr_shadow |= UART_FCR_T_TRIG_10; + t->fcr_shadow |= TEGRA_UART_TX_TRIG_8B; uart_writeb(t, t->fcr_shadow, UART_FCR); - if (t->use_rx_dma) - t->fcr_shadow |= UART_FCR_DMA_SELECT; - uart_writeb(t, t->fcr_shadow, UART_FCR); + if (t->use_rx_dma) { + /* 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); + t->fcr_shadow |= UART_FCR_DMA_SELECT; + uart_writeb(t, t->fcr_shadow, UART_FCR); + if (tegra_start_dma_rx(t)) { + dev_err(t->uport.dev, "Rx DMA enqueue failed\n"); + tegra_uart_free_rx_dma(t); + t->fcr_shadow &= ~UART_FCR_DMA_SELECT; + uart_writeb(t, t->fcr_shadow, UART_FCR); + } + } + else + uart_writeb(t, t->fcr_shadow, UART_FCR); /* * Enable IE_RXS for the receive status interrupts like line errros. @@ -656,10 +694,7 @@ static int tegra_uart_init_rx_dma(struct tegra_uart_port *t) return 0; fail: - tegra_dma_free_channel(t->rx_dma); - if (t->rx_dma_req.dest_addr) - dma_free_coherent(t->uport.dev, t->rx_dma_req.size, - t->rx_dma_req.virt_addr, t->rx_dma_req.dest_addr); + tegra_uart_free_rx_dma(t); return -ENODEV; } @@ -707,9 +742,6 @@ static int tegra_startup(struct uart_port *u) if (ret) goto fail; - if (t->use_rx_dma) - tegra_start_dma_rx(t); - dev_dbg(u->dev, "Requesting IRQ %d\n", u->irq); msleep(1); @@ -738,12 +770,7 @@ static void tegra_shutdown(struct uart_port *u) tegra_uart_hw_deinit(t); spin_unlock_irqrestore(&u->lock, flags); - if (t->use_rx_dma) { - tegra_dma_free_channel(t->rx_dma); - dma_free_coherent(u->dev, t->rx_dma_req.size, - t->rx_dma_req.virt_addr, t->rx_dma_req.dest_addr); - } - + tegra_uart_free_rx_dma(t); if (t->use_tx_dma) tegra_dma_free_channel(t->tx_dma); @@ -788,7 +815,7 @@ static void set_dtr(struct tegra_uart_port *t, bool active) if (active) mcr |= UART_MCR_DTR; else - mcr &= UART_MCR_DTR; + mcr &= ~UART_MCR_DTR; if (mcr != t->mcr_shadow) { uart_writeb(t, mcr, UART_MCR); t->mcr_shadow = mcr; @@ -926,6 +953,10 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, spin_lock_irqsave(&u->lock, flags); + /* Changing configuration, it is safe to stop any rx now */ + 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); @@ -981,6 +1012,9 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, t->mcr_shadow = mcr; uart_writeb(t, mcr, UART_MCR); t->use_cts_control = true; + /* if top layer has asked to set rts active then do so here */ + if (t->rts_active) + set_rts(t, true); } else { mcr = t->mcr_shadow; mcr &= ~UART_MCR_CTS_EN; @@ -990,6 +1024,9 @@ static void tegra_set_termios(struct uart_port *u, struct ktermios *termios, t->use_cts_control = false; } + /* update the port timeout based on new settings */ + uart_update_timeout(u, termios->c_cflag, baud); + spin_unlock_irqrestore(&u->lock, flags); dev_vdbg(t->uport.dev, "-tegra_set_termios\n"); return; @@ -1141,6 +1178,7 @@ static int tegra_uart_probe(struct platform_device *pdev) u->line = pdev->id; u->ops = &tegra_uart_ops; u->type = ~PORT_UNKNOWN; + u->fifosize = 32; u->mapbase = pdata->mapbase; u->membase = pdata->membase; u->irq = pdata->irq;