summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-06-04 10:56:50 -0700
committerGary King <gking@nvidia.com>2010-06-09 09:00:58 -0700
commit53f5c9ef46b5a333d25d8f80b73ddfb8ad1a00eb (patch)
treebbae77e668d1c0957f76868238de6b6f71b5c9e7 /drivers
parent889e84ae45b84b9feb15f3fb39aa022e54aa65ae (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> Reviewed-on: http://git-master/r/2288 Reviewed-by: Gary King <gking@nvidia.com> Tested-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'drivers')
-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 4165aac264b6..8d1a58ee117d 100644
--- a/drivers/serial/tegra_hsuart.c
+++ b/drivers/serial/tegra_hsuart.c
@@ -73,7 +73,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;
@@ -163,7 +163,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;
@@ -171,12 +171,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);
@@ -185,7 +183,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;
@@ -194,31 +192,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)
@@ -391,7 +405,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;
}
@@ -402,16 +416,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);
@@ -424,7 +436,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);
}
@@ -511,11 +525,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;
}
@@ -589,7 +608,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) {