diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2010-08-06 06:45:44 +0530 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-08-06 14:10:01 -0700 |
commit | 1d37d0aa2e3dc546736430479053cf12c59011e7 (patch) | |
tree | 137abdfacc373e1e0f143d01645e11fdceb3f4fd | |
parent | 785fb21c5b0de6cfa4cc9bb40e6a8717d6fffa07 (diff) |
[arm/tegrar]serial:Make sure dma status updated before dma stop.tegra-10.8.1
Sometimes, when uart ask for the dma for get the number of bytes
transferred by dma, the dma does not return the actual number of
bytes transffred, it returns the less number of byes (less by
burst size) and so uart driver client gets the data loss in
communication.
So to avoid the race condition, the driver stops the incoming data
by making RTS line to inactive and wait for some time to complete
the dma burst and then ask dma to get number of bytes transffred
by dma from fifo to memory.
Change-Id: I08de955fde77431115626bd884b68c8e42d52270
Reviewed-on: http://git-master/r/4832
Reviewed-by: Anantha Idapalapati <aidapalapati@nvidia.com>
Tested-by: Anantha Idapalapati <aidapalapati@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Tested-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
-rwxr-xr-x | drivers/serial/tegra_hsuart.c | 150 |
1 files changed, 106 insertions, 44 deletions
diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index 613d2543e9a0..7c1b1ac5bf42 100755 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -27,6 +27,7 @@ #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/platform_device.h> +#include <linux/workqueue.h> #include <linux/io.h> #include <linux/dma-mapping.h> #include <linux/dmapool.h> @@ -49,7 +50,7 @@ #define BYTES_TO_ALIGN(x) ((unsigned long)(ALIGN((x), sizeof(u32))) - \ (unsigned long)(x)) -#define UART_RX_DMA_BUFFER_SIZE 2048 +#define UART_RX_DMA_BUFFER_SIZE (2048*4) #define UART_LSR_FIFOE 0x80 #define UART_IER_EORD 0x20 @@ -87,6 +88,9 @@ const int dma_req_sel[] = { #define TEGRA_UART_TX_TRIG_4B 0x20 #define TEGRA_UART_TX_TRIG_1B 0x30 +/* Wait time in microsec to complete current burst and update status */ +#define WAITTIME_DMA_BURST_COMPLETE 20 + struct tegra_uart_port { struct uart_port uport; char port_name[32]; @@ -126,6 +130,9 @@ struct tegra_uart_port { bool rx_timeout; int uart_state; NvOdmUartHandle odm_uart_handle; + int rx_in_progress; + struct work_struct rx_work; + struct workqueue_struct *rx_work_queue; }; static inline u8 uart_readb(struct tegra_uart_port *t, unsigned long reg) @@ -256,6 +263,7 @@ static void tegra_start_tx(struct uart_port *u) static int tegra_start_dma_rx(struct tegra_uart_port *t) { wmb(); + t->rx_dma_req.bytes_transferred = 0; if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req)) { dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n"); return -EINVAL; @@ -266,22 +274,7 @@ static int tegra_start_dma_rx(struct tegra_uart_port *t) static void tegra_rx_dma_threshold_callback(struct tegra_dma_req *req) { struct tegra_uart_port *t = req->dev; - struct uart_port *u = &t->uport; - unsigned long flags; - - spin_lock_irqsave(&u->lock, flags); - - 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); - - spin_unlock_irqrestore(&u->lock, flags); + queue_work(t->rx_work_queue, &t->rx_work); } /* It is expected that the callers take the UART lock when this API is called. @@ -312,10 +305,11 @@ static void tegra_rx_dma_complete_callback(struct tegra_dma_req *req) req->bytes_transferred); } - if (t->rx_timeout) { - t->rx_timeout = 0; - do_handle_rx_pio(t); - } + do_handle_rx_pio(t); + + /* Push the read data later in caller place. */ + if (req->status == -TEGRA_DMA_REQ_ERROR_ABORTED) + return; spin_unlock(&u->lock); tty_flip_buffer_push(u->state->port.tty); @@ -325,9 +319,12 @@ static void tegra_rx_dma_complete_callback(struct tegra_dma_req *req) /* Lock already taken */ static void do_handle_rx_dma(struct tegra_uart_port *t) { + struct uart_port *u = &t->uport; if (t->rts_active) set_rts(t, false); + udelay(WAITTIME_DMA_BURST_COMPLETE); tegra_dma_dequeue(t->rx_dma); + tty_flip_buffer_push(u->state->port.tty); /* enqueue the request again */ tegra_start_dma_rx(t); if (t->rts_active) @@ -393,6 +390,33 @@ static void do_handle_rx_pio(struct tegra_uart_port *t) return; } +static void tegra_rx_dma_workqueue(struct work_struct *w) +{ + struct tegra_uart_port *t; + struct uart_port *u; + unsigned long flags; + unsigned char ier; + + t = container_of(w, struct tegra_uart_port, rx_work); + u = &t->uport; + + if (!t->rx_in_progress) { + dev_dbg(t->uport.dev, "The Rx is stopped\n"); + return; + } + + do_handle_rx_dma(t); + + spin_lock_irqsave(&u->lock, flags); + if (t->rx_in_progress) { + ier = t->ier_shadow; + ier |= (UART_IER_RLSI | UART_IER_RTOIE | UART_IER_EORD); + t->ier_shadow = ier; + uart_writeb(t, ier, UART_IER); + } + spin_unlock_irqrestore(&u->lock, flags); + return; +} static void do_handle_modem_signal(struct uart_port *u) { @@ -455,6 +479,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) struct uart_port *u = &t->uport; unsigned char iir; unsigned char ier; + bool is_rx_int = false; unsigned long flags; spin_lock_irqsave(&u->lock, flags); @@ -462,6 +487,11 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) while (1) { iir = uart_readb(t, UART_IIR); if (iir & UART_IIR_NO_INT) { + if (likely(t->use_rx_dma)) { + if (is_rx_int) { + queue_work(t->rx_work_queue, &t->rx_work); + } + } spin_unlock_irqrestore(&u->lock, flags); return IRQ_HANDLED; } @@ -478,27 +508,25 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) do_handle_tx_pio(t); break; case 4: /* End of data */ - /* As per hw spec, to clear EORD interrupt, we need - * to disable and then re-enable the interrupt. - */ - ier = t->ier_shadow; - ier &= ~UART_IER_EORD; - uart_writeb(t, ier, UART_IER); - ier |= UART_IER_EORD; - uart_writeb(t, ier, UART_IER); - /* fallthrough */ case 6: /* Rx timeout */ - t->rx_timeout = 1; - /* fallthrough */ case 2: /* Receive */ - if (likely(t->use_rx_dma)) - do_handle_rx_dma(t); - else - do_handle_rx_pio(t); - - spin_unlock_irqrestore(&u->lock, flags); - tty_flip_buffer_push(u->state->port.tty); - spin_lock_irqsave(&u->lock, flags); + if (likely(t->use_rx_dma)) { + if (!is_rx_int) { + is_rx_int = true; + /* Disable interrups */ + ier = t->ier_shadow; + ier |= UART_IER_RDI; + uart_writeb(t, ier, UART_IER); + ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | UART_IER_EORD); + t->ier_shadow = ier; + uart_writeb(t, ier, UART_IER); + } + } else { + do_handle_rx_pio(t); + spin_unlock_irqrestore(&u->lock, flags); + tty_flip_buffer_push(u->state->port.tty); + spin_lock_irqsave(&u->lock, flags); + } break; case 3: /* Receive error */ /* FIXME how to handle this? Why do we get here */ @@ -514,13 +542,29 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) static void tegra_stop_rx(struct uart_port *u) { struct tegra_uart_port *t; + unsigned char ier; t = container_of(u, struct tegra_uart_port, uport); if (t->rts_active) set_rts(t, false); - if (t->rx_dma) + + if (t->rx_in_progress) { + /* Enable the interrupts */ + ier = t->ier_shadow; + ier |= UART_IER_RDI; + uart_writeb(t, ier, UART_IER); + + ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | UART_IER_EORD); + t->ier_shadow = ier; + uart_writeb(t, ier, UART_IER); + t->rx_in_progress = 0; + } + + if (t->rx_dma) { tegra_dma_dequeue(t->rx_dma); + tty_flip_buffer_push(u->state->port.tty); + } return; } @@ -582,6 +626,8 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) clk_enable(t->clk); msleep(10); + 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. @@ -638,6 +684,8 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) else uart_writeb(t, t->fcr_shadow, UART_FCR); + t->rx_in_progress = 1; + /* * Enable IE_RXS for the receive status interrupts like line errros. * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd. @@ -745,7 +793,7 @@ static int tegra_startup(struct uart_port *u) t->tx_in_progress = 0; t->use_rx_dma = false; - if (!TX_FORCE_PIO) { + if (!RX_FORCE_PIO) { if (!tegra_uart_init_rx_dma(t)) t->use_rx_dma = true; } @@ -1192,6 +1240,7 @@ static int __devexit tegra_uart_remove(struct platform_device *pdev) uart_remove_one_port(&tegra_uart_driver, u); if (t->odm_uart_handle) NvOdmUartClose(t->odm_uart_handle); + destroy_workqueue(t->rx_work_queue); platform_set_drvdata(pdev, NULL); @@ -1248,16 +1297,31 @@ static int __init tegra_uart_probe(struct platform_device *pdev) return ret; } + /* Create the workqueue for the Rx Path */ + snprintf(name, sizeof(name), "tegra_hsuart_rx_%d", u->line); + t->rx_work_queue = create_singlethread_workqueue(name); + if (t->rx_work_queue == NULL) { + dev_err(u->dev, "Failed to create work queue\n"); + ret = -ENODEV; + goto rx_workq_fail; + } + INIT_WORK(&t->rx_work, tegra_rx_dma_workqueue); + snprintf(name, sizeof(name), "tegra_hsuart_%d", u->line); pr_info("Registered UART port %s%d\n", tegra_uart_driver.dev_name, u->line); t->uart_state = TEGRA_UART_CLOSED; return ret; + +rx_workq_fail: + uart_remove_one_port(&tegra_uart_driver, u); + fail: if (t->odm_uart_handle) NvOdmUartClose(t->odm_uart_handle); kfree(t); + platform_set_drvdata(pdev, NULL); return -ENODEV; } @@ -1299,12 +1363,10 @@ void tegra_uart_request_clock_on(struct uart_port *uport) void tegra_uart_set_mctrl(struct uart_port *uport, unsigned int mctrl) { unsigned long flags; - unsigned char mcr; struct tegra_uart_port *t; t = container_of(uport, struct tegra_uart_port, uport); spin_lock_irqsave(&uport->lock, flags); - mcr = t->mcr_shadow; if (mctrl & TIOCM_RTS) { t->rts_active = true; set_rts(t, true); |