summaryrefslogtreecommitdiff
path: root/drivers/serial
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-06-04 10:56:50 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:35:21 -0800
commit6cbaf4773daa8cdda026dbe4fa95e2cd0dbdb9fe (patch)
treedea27038a9d733b1014944229ade2e4e163b45dc /drivers/serial
parent1395b4bbb19c45c235063096a664d471557f1dbc (diff)
serial: tegra_hsuart: Cleanups and bug fixes
tegra_start_tx was called directly by the serial core, as well as from dma and serial interrupts to queue the next block of data. Separate out the "queue next data" functionality into tegra_start_next_tx. Also fixes TX PIO by adjusting FIFO sizes and prevents last characters from getting lost by spinning on TEMT before disabling clocks. Change-Id: If8ce15490f77dcbde48f1e64959d5c3f0ec35120 Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/tegra_hsuart.c79
1 files changed, 49 insertions, 30 deletions
diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c
index 2f585058dbb6..ff028e28fc25 100644
--- a/drivers/serial/tegra_hsuart.c
+++ b/drivers/serial/tegra_hsuart.c
@@ -71,7 +71,7 @@ const int dma_req_sel[] = {
#define TEGRA_TX_DMA 2
#define TEGRA_UART_MIN_DMA 16
-#define TEGRA_UART_FIFO_SIZE 16
+#define TEGRA_UART_FIFO_SIZE 8
struct tegra_uart_port {
struct uart_port uport;
@@ -158,7 +158,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_00;
+ t->fcr_shadow |= UART_FCR_T_TRIG_10;
uart_writeb(t, t->fcr_shadow, UART_FCR);
t->tx_in_progress = TEGRA_TX_PIO;
t->tx_bytes = bytes;
@@ -166,12 +166,10 @@ static void tegra_start_pio_tx(struct tegra_uart_port *t, unsigned int bytes)
uart_writeb(t, t->ier_shadow, UART_IER);
}
-static void tegra_start_dma_tx(struct tegra_uart_port *t)
+static void tegra_start_dma_tx(struct tegra_uart_port *t, unsigned long bytes)
{
- unsigned int count;
struct circ_buf *xmit;
xmit = &t->uport.state->xmit;
- count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
dma_sync_single_for_device(t->uport.dev, t->xmit_dma_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
@@ -180,7 +178,7 @@ static void tegra_start_dma_tx(struct tegra_uart_port *t)
t->fcr_shadow |= UART_FCR_T_TRIG_01;
uart_writeb(t, t->fcr_shadow, UART_FCR);
- t->tx_bytes = count & ~(sizeof(u32)-1);
+ t->tx_bytes = bytes & ~(sizeof(u32)-1);
t->tx_dma_req.source_addr = t->xmit_dma_addr + xmit->tail;
t->tx_dma_req.size = t->tx_bytes;
@@ -189,31 +187,47 @@ static void tegra_start_dma_tx(struct tegra_uart_port *t)
tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req);
}
-/* Called by serial core driver, or in interrupt context when a PIO or DMA
- * tranmission is complete. Called with u->lock taken.
- */
+/* Called with u->lock taken */
+static void tegra_start_next_tx(struct tegra_uart_port *t)
+{
+ unsigned long tail;
+ unsigned long count;
+
+ struct circ_buf *xmit;
+
+ xmit = &t->uport.state->xmit;
+ tail = (unsigned long)&xmit->buf[xmit->tail];
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+
+ dev_vdbg(t->uport.dev, "+%s %lu %d\n", __func__, count,
+ t->tx_in_progress);
+
+ if (count == 0)
+ goto out;
+
+ if (TX_FORCE_PIO || count < TEGRA_UART_MIN_DMA)
+ tegra_start_pio_tx(t, count);
+ else if (BYTES_TO_ALIGN(tail) > 0)
+ tegra_start_pio_tx(t, BYTES_TO_ALIGN(tail));
+ else
+ tegra_start_dma_tx(t, count);
+
+out:
+ dev_vdbg(t->uport.dev, "-%s", __func__);
+}
+
+/* Called by serial core driver with u->lock taken. */
static void tegra_start_tx(struct uart_port *u)
{
struct tegra_uart_port *t;
struct circ_buf *xmit;
- unsigned long tail;
- int pending;
t = container_of(u, struct tegra_uart_port, uport);
xmit = &u->state->xmit;
- dev_vdbg(t->uport.dev, "+tegra_start_tx\n");
- while (!uart_circ_empty(xmit) && !t->tx_in_progress) {
- tail = (unsigned long)&xmit->buf[xmit->tail];
- pending = uart_circ_chars_pending(xmit);
- if (TX_FORCE_PIO || pending < TEGRA_UART_MIN_DMA)
- tegra_start_pio_tx(t, pending);
- else if (BYTES_TO_ALIGN(tail) > 0)
- tegra_start_pio_tx(t, BYTES_TO_ALIGN(tail));
- else
- tegra_start_dma_tx(t);
- }
- dev_vdbg(t->uport.dev, "-tegra_start_tx\n");
+ if (!uart_circ_empty(xmit) && !t->tx_in_progress)
+ tegra_start_next_tx(t);
}
static int tegra_start_dma_rx(struct tegra_uart_port *t)
@@ -382,7 +396,7 @@ static void do_handle_tx_pio(struct tegra_uart_port *t)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&t->uport);
- tegra_start_tx(&t->uport);
+ tegra_start_next_tx(t);
return;
}
@@ -393,16 +407,14 @@ static void tegra_tx_dma_complete_callback(struct tegra_dma_req *req)
int count = req->bytes_transferred;
unsigned long flags;
int timeout = 20;
- if (req->status == -TEGRA_DMA_REQ_ERROR_ABORTED)
- BUG();
dev_vdbg(t->uport.dev, "%s: %d\n", __func__, count);
while ((uart_readb(t, UART_LSR) & TX_EMPTY_STATUS) != TX_EMPTY_STATUS) {
timeout--;
- if (timeout-- == 0) {
+ if (timeout == 0) {
dev_err(t->uport.dev,
- "timed out waiting for TX FIFO to empty");
+ "timed out waiting for TX FIFO to empty\n");
return;
}
msleep(1);
@@ -415,7 +427,9 @@ static void tegra_tx_dma_complete_callback(struct tegra_dma_req *req)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&t->uport);
- tegra_start_tx(&t->uport);
+ if (req->status != -TEGRA_DMA_REQ_ERROR_ABORTED)
+ tegra_start_next_tx(t);
+
spin_unlock_irqrestore(&t->uport.lock, flags);
}
@@ -502,11 +516,16 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
/* Disable interrupts */
uart_writeb(t, 0, UART_IER);
+ while ((uart_readb(t, UART_LSR) & UART_LSR_TEMT) != UART_LSR_TEMT);
+ udelay(2000);
+
/* 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(2000);
+
clk_disable(t->clk);
t->baud = 0;
}
@@ -563,7 +582,7 @@ 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_01;
+ t->fcr_shadow |= UART_FCR_T_TRIG_10;
uart_writeb(t, t->fcr_shadow, UART_FCR);
if (t->use_rx_dma)