summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdrivers/serial/tegra_hsuart.c150
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);