summaryrefslogtreecommitdiff
path: root/drivers/serial/tegra_hsuart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/serial/tegra_hsuart.c')
-rw-r--r--drivers/serial/tegra_hsuart.c717
1 files changed, 431 insertions, 286 deletions
diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c
index fac5e5eff24f..e58473a88fa5 100644
--- a/drivers/serial/tegra_hsuart.c
+++ b/drivers/serial/tegra_hsuart.c
@@ -47,9 +47,15 @@
#include "nvrm_interrupt.h"
#include "nvrm_power.h"
-#define UART_RX_DMA_PING_BUFFER_SIZE 0x800
-static int use_dma = 1;
+#define TX_EMPTY_STATUS (NV_DRF_DEF(UART, LSR, TMTY, EMPTY) | \
+ NV_DRF_DEF(UART, LSR, THRE, EMPTY))
+
+#define UART_RX_DMA_BUFFER_SIZE (4 * 1024 * 16)
+
+static int tx_force_pio = 0;
+static int rx_force_pio = 0;
+
struct tegra_uart_port {
struct uart_port uport;
@@ -62,6 +68,7 @@ struct tegra_uart_port {
NvOsPhysAddr phys;
NvU32 size;
struct clk *clk;
+ unsigned int baud;
/* Register shadow */
unsigned char fcr_shadow;
@@ -85,7 +92,7 @@ struct tegra_uart_port {
int tx_dma;
/* DMA requests */
- struct tegra_dma_req rx_dma_req[2];
+ struct tegra_dma_req rx_dma_req;
int rx_dma;
struct tegra_dma_req tx_dma_req;
@@ -95,12 +102,45 @@ struct tegra_uart_port {
int rx_pio_buffer_size;
bool use_rx_dma;
- bool dma_for_tx;
+ bool use_tx_dma;
+
+ bool tx_pio_inflight;
+
+ struct work_struct work;
+ struct workqueue_struct *work_queue;
- struct tasklet_struct tasklet;
};
static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud);
+static void tegra_set_mctrl(struct uart_port *u, unsigned int mctrl);
+static void do_handle_rx_pio(struct uart_port *u);
+static inline bool tegra_wait_for_tx_fifo_empty(struct uart_port *u);
+
+/*
+ * Attempts to wait for some time before to make sure that the data is drained.
+ * Assumes that the caller has taken the u->lock.
+ *
+ * */
+static inline bool tegra_wait_for_tx_fifo_empty(struct uart_port *u)
+{
+ /* FIXME compute the correct value based on the baud rate */
+ struct tegra_uart_port *t;
+ int timeout = 10;
+ unsigned char lsr;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ do {
+ lsr = readb(t->regs + UART_LSR_0);
+ if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
+ return true;
+ spin_unlock(&u->lock);
+ msleep(1);
+ spin_lock(&u->lock);
+ timeout--;
+ } while (timeout);
+
+ return false;
+}
static inline int tegra_uart_isbreak(struct uart_port *u)
{
@@ -122,22 +162,120 @@ static inline int tegra_uart_isbreak(struct uart_port *u)
return 0;
}
-static inline void tegra_uart_set_rts(struct tegra_uart_port *t,
- int logical_value)
+void tegra_rx_dma_threshold_callback(struct tegra_dma_req *req, int err)
{
- unsigned char mcr;
+ struct uart_port *u = req->data;
+ struct tegra_uart_port *t;
+ unsigned long flags;
- /* To set to logical value 0, set the bit to 1 and vice versa */
- mcr = t->mcr_shadow;
- if (logical_value)
- mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_LOW, mcr);
- else
- mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_HI, mcr);
+ t = container_of(u, struct tegra_uart_port, uport);
- if (mcr != t->mcr_shadow) {
- writeb(mcr, t->regs + UART_MCR_0);
- t->mcr_shadow = mcr;
+ spin_lock_irqsave(&u->lock, flags);
+
+ u->mctrl &= ~TIOCM_RTS;
+ u->mctrl &= ~TIOCM_DTR;
+ tegra_set_mctrl(u, u->mctrl);
+
+ tegra_dma_dequeue(t->rx_dma);
+
+ u->mctrl |= TIOCM_RTS;
+ u->mctrl |= TIOCM_DTR;
+ tegra_set_mctrl(u, u->mctrl);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+}
+
+/* It is expected that the callers take the UART lock when this API is called.
+ *
+ * There are 2 contexts when this function is called:
+ *
+ * 1. DMA ISR - DMA ISR triggers the threshold complete calback, which calls the
+ * dequue API which in-turn calls this callback. UART lock is taken during
+ * the call to the threshold callback.
+ *
+ * 2. UART ISR - UART calls the dequue API which in-turn will call this API.
+ * In this case, UART ISR takes the UART lock.
+ * */
+void tegra_rx_dma_complete_callback(struct tegra_dma_req *req, int err)
+{
+ struct uart_port *u = req->data;
+ struct tegra_uart_port *t;
+ struct tty_struct *tty = u->info->port.tty;
+
+ /* If we are here, DMA is stopped */
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ if (req->bytes_transferred) {
+ t->uport.icount.rx += req->bytes_transferred;
+ tty_insert_flip_string(tty,
+ ((unsigned char *)(req->virt_addr)),
+ req->bytes_transferred);
+ dev_dbg(u->dev, "Received %d bytes\n", req->bytes_transferred);
+ }
+
+ if (req->status == -TEGRA_DMA_REQ_ERROR_ABOTRED) {
+ do_handle_rx_pio(u);
}
+
+ spin_unlock(&u->lock);
+ tty_flip_buffer_push(u->info->port.tty);
+ spin_lock(&u->lock);
+
+ /* Enqueue the request again */
+ tegra_dma_enqueue_req(t->rx_dma, req);
+}
+
+/* Lock already taken */
+static void do_handle_rx_dma(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ u->mctrl &= ~TIOCM_RTS;
+ u->mctrl &= ~TIOCM_DTR;
+ tegra_set_mctrl(u, u->mctrl);
+
+ tegra_dma_dequeue(t->rx_dma);
+
+ u->mctrl |= TIOCM_RTS;
+ u->mctrl |= TIOCM_DTR;
+ tegra_set_mctrl(u, u->mctrl);
+
+}
+
+static char do_decode_rx_error(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+ char flag = TTY_NORMAL;
+ unsigned char lsr;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ lsr = readb(t->regs + UART_LSR_0);
+ if (lsr & NV_DRF_DEF(UART, LSR, OVRF, OVERRUN_ERROR)) {
+ /* Overrrun error */
+ flag |= TTY_OVERRUN;
+ t->uport.icount.overrun++;
+ dev_err(u->dev, "Got overrun errors\n");
+ } else if (lsr & NV_DRF_DEF(UART, LSR, PERR, PARITY_ERR)) {
+ /* Parity error */
+ flag |= TTY_PARITY;
+ t->uport.icount.parity++;
+ dev_err(u->dev, "Got Parity errors\n");
+ } else if (lsr & NV_DRF_DEF(UART, LSR, FERR, FRAME_ERR)) {
+ flag |= TTY_FRAME;
+ dev_err(u->dev, "Got frame errors\n");
+ } else if (lsr & NV_DRF_DEF(UART, LSR, BRK, BREAK)) {
+ dev_err(u->dev, "Got Break \n");
+ /* If FIFO read error without any data, reset Rx FIFO */
+ if (!(lsr & 0x1) && (lsr & 0x80)) {
+ unsigned char fcr = t->fcr_shadow;
+ fcr |= 0x2;
+ writeb(fcr, t->regs + UART_IIR_FCR_0);
+ }
+ }
+ return flag;
}
static void do_handle_rx_pio(struct uart_port *u)
@@ -152,115 +290,116 @@ static void do_handle_rx_pio(struct uart_port *u)
unsigned char lsr;
unsigned char ch;
- lsr = readb(t->regs + UART_LSR_0);
- if (lsr & NV_DRF_DEF(UART, LSR, OVRF, OVERRUN_ERROR)) {
- /* Overrrun error */
- flag |= TTY_OVERRUN;
- t->uport.icount.overrun++;
- dev_err(u->dev, "Got overrun errors\n");
- } else if (lsr & NV_DRF_DEF(UART, LSR, PERR, PARITY_ERR)) {
- /* Parity error */
- flag |= TTY_PARITY;
- t->uport.icount.parity++;
- dev_err(u->dev, "Got Parity errors\n");
- } else if (lsr & NV_DRF_DEF(UART, LSR, FERR, FRAME_ERR)) {
- flag |= TTY_FRAME;
- dev_err(u->dev, "Got frame errors\n");
- } else if (lsr & NV_DRF_DEF(UART, LSR, BRK, BREAK)) {
- dev_err(u->dev, "Got Break \n");
- /* If FIFO read error without any data, reset Rx FIFO */
- if (!(lsr & 0x1) && (lsr & 0x80)) {
- unsigned char fcr = t->fcr_shadow;
- fcr |= 0x2;
- writeb(fcr, t->regs + UART_IIR_FCR_0);
- }
- } else if (lsr & NV_DRF_DEF(UART, LSR, FIFOE, ERR)) {
- unsigned char fcr;
- fcr = t->fcr_shadow;
- fcr = NV_FLD_SET_DRF_DEF(UART, IIR_FCR,
- RX_CLR, CLEAR, fcr);
- writeb(fcr, t->regs + UART_IIR_FCR_0);
- dev_err(u->dev, "Got fifo errors\n");
- }
+ flag = do_decode_rx_error(u);
+ lsr = readb(t->regs + UART_LSR_0);
if (!(lsr & NV_DRF_DEF(UART, LSR, RDR, DATA_IN_FIFO)))
break;
ch = readb(t->regs + UART_THR_DLAB_0_0);
dev_vdbg(u->dev, "%c\n", ch);
- if (!uart_handle_sysrq_char(u, c))
+ if (!uart_handle_sysrq_char(u, c)) {
tty_insert_flip_char(tty, ch, flag);
+ t->uport.icount.rx ++;
+ }
} while (1);
return;
}
-static void tegra_dma_tasklet_func(unsigned long data)
+
+static void tegra_tx_dma_workqueue(struct work_struct *w)
{
- struct uart_port *u = (struct uart_port *)data;
+ struct uart_port *u;
struct tegra_uart_port *t;
- struct circ_buf *xmit = &u->info->xmit;
+ struct circ_buf *xmit;
unsigned int count;
unsigned int to_send;
unsigned long flags;
- t = container_of(u, struct tegra_uart_port, uport);
+ t = container_of(w, struct tegra_uart_port, work);
+ u = &t->uport;
+ xmit = &u->info->xmit;
+
spin_lock_irqsave(&u->lock, flags);
- if (uart_circ_chars_pending(xmit)) {
- dma_sync_single_for_device(u->dev, t->tx_dma_phys,
- t->tx_dma_size, DMA_BIDIRECTIONAL);
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ /* DMA request is already queued, just return */
+ if (tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req)) {
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+ }
- to_send = CIRC_CNT_TO_END(xmit->head, xmit->tail,
- UART_XMIT_SIZE);
+ /* DMA just finished. Wait for the FIFO to drain. */
+ if (t->tx_dma_req.size) {
+ bool empty;
+ empty = tegra_wait_for_tx_fifo_empty(u);
+ BUG_ON(empty != true);
+ }
- /* DMA can only handle 4 byte aligned trasfers. So, align the
- * size */
- count = (to_send >> 2) << 2;
- if (count && !(xmit->tail & 0x3)) {
- dev_vdbg(u->dev, "Tx DMA starting 0x%x size %d\n",
- xmit->tail, count);
- t->tx_dma_req.source_addr = t->tx_dma_phys + xmit->tail;
- t->tx_dma_req.size = count;
+ /* Update the DMA tail pointer */
+ xmit->tail += t->tx_dma_req.size;
+ xmit->tail &= UART_XMIT_SIZE - 1;
+ u->icount.tx += t->tx_dma_req.size;
+ t->tx_dma_req.size = 0;
- xmit->tail += count;
- xmit->tail &= UART_XMIT_SIZE - 1;
- u->icount.tx += count;
+ /* PIO is in flight. Just return */
+ if (t->tx_pio_inflight == true) {
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+ }
- tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req);
+ if (uart_circ_empty(xmit)) {
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+ }
- /* Just return as the rest of the chars, if any, will
- * be scheduled when the DMA is completed */
- spin_unlock_irqrestore(&u->lock, flags);
- return;
+ dma_sync_single_for_device(u->dev, t->tx_dma_phys,
+ t->tx_dma_size, DMA_TO_DEVICE);
- } else {
- /* Use PIO for cases that cannot be handled by the DMA.
- * FIXME: We need to optimize this.
- * */
- if (tegra_dma_is_empty(t->tx_dma)) {
- count = to_send & 0x3;
- while (count) {
- unsigned char lsr;
-
- lsr = readb(t->regs + UART_LSR_0);
- if (!(lsr & (NV_DRF_DEF(UART, LSR,
- TMTY, EMPTY))))
- goto end;
- writeb(xmit->buf[xmit->tail],
- t->regs + UART_THR_DLAB_0_0);
- xmit->tail = (xmit->tail + 1) &
- (UART_XMIT_SIZE - 1);
- u->icount.tx++;
- count--;
- }
- }
+ to_send = CIRC_CNT_TO_END(xmit->head, xmit->tail,
+ UART_XMIT_SIZE);
+
+ /* DMA can only handle 4 byte aligned trasfers. So, align the
+ * size */
+ count = (to_send >> 2) << 2;
+ if (count && !(xmit->tail & 0x3)) {
+ dev_dbg(u->dev, "Tx DMA starting 0x%x size %d\n",
+ xmit->tail, count);
+
+ t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG,
+ FIFO_COUNT_GREATER_4, t->fcr_shadow);
+ writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0);
+
+ t->tx_dma_req.source_addr = t->tx_dma_phys + xmit->tail;
+ t->tx_dma_req.size = count;
+ t->tx_pio_inflight = false;
+
+ tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(u);
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+
+ } else {
+ /* Trasnfer in PIO mode */
+
+ t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG,
+ FIFO_COUNT_GREATER_8, t->fcr_shadow);
+ writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0);
+
+ if (!t->tx_pio_inflight) {
+ t->tx_pio_inflight = true;
+ t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0,
+ IE_THR, ENABLE, t->ier_shadow);
+ writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0);
}
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+
}
-end:
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(u);
- spin_unlock_irqrestore(&u->lock, flags);
}
static void do_handle_modem_signal(struct uart_port *u)
@@ -299,6 +438,7 @@ static void do_handle_tx_pio(struct uart_port *u)
t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0,
IE_THR, DISABLE, t->ier_shadow);
writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0);
+ t->tx_pio_inflight = false;
break;
}
@@ -314,52 +454,13 @@ static void do_handle_tx_pio(struct uart_port *u)
void tegra_tx_dma_complete_callback(struct tegra_dma_req *req, int err)
{
- tasklet_schedule((struct tasklet_struct *)req->data);
-}
-
-void do_handle_rx_dma(struct uart_port *u, int eord_int)
-{
+ struct uart_port *u = (struct uart_port *)req->data;
struct tegra_uart_port *t;
- unsigned char ier;
-
- t = container_of(u, struct tegra_uart_port, uport);
- if (eord_int) {
- /* As per hw spec, to clear EORD interrupt, we need to disable
- * and then re-enable the interrupt.
- */
- ier = t->ier_shadow;
- ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD,
- DISABLE, ier);
- writeb(ier, t->regs + UART_IER_DLAB_0_0);
- ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD,
- ENABLE, ier);
- writeb(ier, t->regs + UART_IER_DLAB_0_0);
- }
- tegra_dma_dequeue(t->rx_dma);
-}
-
-void tegra_rx_dma_complete_callback(struct tegra_dma_req *req, int err)
-{
- struct uart_port *u = req->data;
- struct tegra_uart_port *t;
- struct tty_struct *tty = u->info->port.tty;
-
- t = container_of(u, struct tegra_uart_port, uport);
- if (req->bytes_transferred) {
- tty_insert_flip_string(tty,
- ((unsigned char *)(req->virt_addr)),
- req->bytes_transferred);
- }
-
- if (req->status == -TEGRA_DMA_REQ_ERROR_ABOTRED) {
- /* Drain the FIFO entries, only for the aborted requests */
- do_handle_rx_pio(u);
- }
-
- tty_flip_buffer_push(u->info->port.tty);
+ if (err == -TEGRA_DMA_REQ_ERROR_ABOTRED)
+ return;
- /* Enqueue the request again */
- tegra_dma_enqueue_req(t->rx_dma, req);
+ t = container_of(u, struct tegra_uart_port, uport);
+ queue_work(t->work_queue, &t->work);
}
static irqreturn_t tegra_uart_isr(int irq, void *data)
@@ -367,12 +468,15 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
struct uart_port *u = (struct uart_port *)data;
struct tegra_uart_port *t;
unsigned char iir_fcr;
+ unsigned char ier;
+ spin_lock(&u->lock);
t = container_of(u, struct tegra_uart_port, uport);
/* FIXME why do we need to loop here? */
while (1) {
iir_fcr = readb(t->regs + UART_IIR_FCR_0);
if (iir_fcr & NV_DRF_DEF(UART, IIR_FCR, IS_STA, NO_INTR_PEND)) {
+ spin_unlock(&u->lock);
return IRQ_HANDLED;
}
@@ -384,20 +488,34 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
case 1: /* Transmit interrupt only triggered when using PIO */
do_handle_tx_pio(u);
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 = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD,
+ DISABLE, ier);
+ writeb(ier, t->regs + UART_IER_DLAB_0_0);
+ ier = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, IE_EORD,
+ ENABLE, ier);
+ writeb(ier, t->regs + UART_IER_DLAB_0_0);
+ /* fallthrough */
case 2: /* Receive */
- case 3: /* Receive error */
case 6: /* Rx timeout */
- if (likely(t->use_rx_dma))
- do_handle_rx_dma(u, 0);
- else
+ if (likely(t->use_rx_dma)) {
+ do_handle_rx_dma(u);
+ } else {
do_handle_rx_pio(u);
- tty_flip_buffer_push(u->info->port.tty);
+
+ spin_unlock(&u->lock);
+ tty_flip_buffer_push(u->info->port.tty);
+ spin_lock(&u->lock);
+ }
break;
- case 4: /* End of data */
- do_handle_rx_dma(u, 1);
- tty_flip_buffer_push(u->info->port.tty);
+ case 3: /* Receive error */
+ /* FIXME how to handle this? Why do we get here */
+ do_decode_rx_error(u);
break;
-
case 5: /* break nothing to handle */
case 7: /* break nothing to handle */
break;
@@ -407,19 +525,40 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
static void tegra_stop_rx(struct uart_port *u)
{
- dev_vdbg(u->dev, "+tegra_stop_rx\n");
- dev_vdbg(u->dev, "-tegra_stop_rx\n");
return;
}
-static int tegra_uart_hwinit(struct tegra_uart_port *t)
+static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
+{
+ unsigned char fcr;
+
+ /* Disable interrupts */
+ writeb(0, t->regs + UART_IER_DLAB_0_0);
+
+ /* Reset the Rx and Tx FIFOs */
+ fcr = t->fcr_shadow;
+ fcr = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_CLR, CLEAR, fcr);
+ fcr = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, RX_CLR, CLEAR, fcr);
+ writeb(fcr, t->regs + UART_IIR_FCR_0);
+
+ NvRmModuleReset(s_hRmGlobal, t->modid);
+ clk_disable(t->clk);
+ NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_TRUE);
+}
+
+static int tegra_uart_hw_init(struct tegra_uart_port *t)
{
unsigned char fcr;
unsigned char mcr;
unsigned char ier;
NvError err;
- dev_vdbg(t->uport.dev, "+tegra_uart_hwinit\n");
+ dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n");
+
+ t->fcr_shadow = 0;
+ t->mcr_shadow = 0;
+ t->lcr_shadow = 0;
+ t->ier_shadow = 0;
err = NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_FALSE);
if (err != NvSuccess) {
@@ -428,8 +567,6 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t)
}
clk_enable(t->clk);
-
- tegra_set_baudrate(t, 9600);
NvRmModuleReset(s_hRmGlobal, t->modid);
/* Reset the FIFO twice with some delay to make sure that the FIFOs are
@@ -471,7 +608,7 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t)
t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, RX_TRIG,
FIFO_COUNT_GREATER_4, t->fcr_shadow);
- if (t->dma_for_tx) {
+ if (t->use_tx_dma) {
t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG,
FIFO_COUNT_GREATER_4, t->fcr_shadow);
} else {
@@ -495,7 +632,9 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t)
*/
mcr = t->mcr_shadow;
mcr = NV_FLD_SET_DRF_DEF(UART, MCR, CTS_EN, ENABLE, mcr);
- mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS_EN, ENABLE, mcr);
+ mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS_EN, DISABLE, mcr);
+ mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_HI, mcr);
+ mcr = NV_FLD_SET_DRF_DEF(UART, MCR, DTR, FORCE_DTR_HI, mcr);
t->mcr_shadow = mcr;
writeb(mcr, t->regs + UART_MCR_0);
@@ -529,68 +668,56 @@ static int tegra_uart_hwinit(struct tegra_uart_port *t)
t->ier_shadow = ier;
writeb(ier, t->regs + UART_IER_DLAB_0_0);
- dev_vdbg(t->uport.dev, "-tegra_uart_hwinit\n");
+ dev_vdbg(t->uport.dev, "-tegra_uart_hw_init\n");
return 0;
fail:
- dev_vdbg(t->uport.dev, "-tegra_uart_hwinit\n");
+ dev_err(t->uport.dev, "HW init failed\n");
return -ENODEV;
}
static int tegra_uart_init_rx_dma(struct tegra_uart_port *t)
{
- int i;
+ dma_addr_t rx_dma_phys;
+ void *rx_dma_virt;
- /* Rx uses 1 DMA channel and 2 chained buffers */
t->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINOUS);
if (t->rx_dma < 0)
return -ENODEV;
- memset(t->rx_dma_req, 0, sizeof(t->rx_dma_req));
- for (i=0; i<2; i++) {
- dma_addr_t rx_dma_phys;
- void *rx_dma_virt;
+ memset(&t->rx_dma_req, 0, sizeof(t->rx_dma_req));
- t->rx_dma_req[i].size = UART_RX_DMA_PING_BUFFER_SIZE * 2;
- /* Allocate receive DMA buffer
- * This buffer can hold data for 50 ms with 4.5 Mbps data rate.
- */
- rx_dma_virt = dma_alloc_coherent(t->uport.dev,
- t->rx_dma_req[i].size, &rx_dma_phys, GFP_KERNEL);
- if (!rx_dma_virt) {
- dev_err(t->uport.dev, "DMA buffers allocate failed \n");
- goto fail;
- }
- t->rx_dma_req[i].dest_addr = rx_dma_phys;
- t->rx_dma_req[i].virt_addr = rx_dma_virt;
+ t->rx_dma_req.size = UART_RX_DMA_BUFFER_SIZE;
+ rx_dma_virt = dma_alloc_coherent(t->uport.dev,
+ t->rx_dma_req.size, &rx_dma_phys, GFP_KERNEL);
+ if (!rx_dma_virt) {
+ dev_err(t->uport.dev, "DMA buffers allocate failed \n");
+ goto fail;
}
-
- for (i=0; i<2; i++) {
- t->rx_dma_req[i].source_addr = t->phys;
- t->rx_dma_req[i].source_wrap = 4;
- t->rx_dma_req[i].dest_wrap = 0;
- t->rx_dma_req[i].to_memory = 1;
- t->rx_dma_req[i].modid = NvRmModuleID_Uart;
- t->rx_dma_req[i].instance = t->uport.line;
- t->rx_dma_req[i].complete = tegra_rx_dma_complete_callback;
- t->rx_dma_req[i].size = t->rx_dma_req[i].size;
- t->rx_dma_req[i].data = &t->uport;
- INIT_LIST_HEAD(&(t->rx_dma_req[i].list));
- if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req[i])) {
- dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n");
- goto fail;
- }
+ t->rx_dma_req.dest_addr = rx_dma_phys;
+ t->rx_dma_req.virt_addr = rx_dma_virt;
+
+ t->rx_dma_req.source_addr = t->phys;
+ t->rx_dma_req.source_wrap = 4;
+ t->rx_dma_req.dest_wrap = 0;
+ t->rx_dma_req.to_memory = 1;
+ t->rx_dma_req.modid = NvRmModuleID_Uart;
+ t->rx_dma_req.instance = t->uport.line;
+ t->rx_dma_req.complete = tegra_rx_dma_complete_callback;
+ t->rx_dma_req.threshold = tegra_rx_dma_threshold_callback;
+ t->rx_dma_req.data = &t->uport;
+ INIT_LIST_HEAD(&(t->rx_dma_req.list));
+ if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req)) {
+ dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n");
+ goto fail;
}
return 0;
fail:
tegra_dma_free_channel(t->rx_dma);
- if (t->rx_dma_req[0].dest_addr)
- dma_free_coherent(t->uport.dev, t->rx_dma_req[0].size,
- t->rx_dma_req[0].virt_addr, t->rx_dma_req[0].dest_addr);
- if (t->rx_dma_req[1].dest_addr)
- dma_free_coherent(t->uport.dev, t->rx_dma_req[1].size,
- t->rx_dma_req[1].virt_addr, t->rx_dma_req[1].dest_addr);
+ 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);
return -ENODEV;
}
@@ -613,16 +740,16 @@ static int tegra_startup(struct uart_port *u)
t->irq = NvRmGetIrqForLogicalInterrupt(s_hRmGlobal, t->modid, 0);
BUG_ON(t->irq == (NvU32)(-1));
- t->dma_for_tx = false;
- if (use_dma) {
+ t->use_tx_dma = false;
+ if (!tx_force_pio) {
t->tx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
if (t->tx_dma >= 0)
- t->dma_for_tx = true;
+ t->use_tx_dma = true;
}
- if (t->dma_for_tx) {
+ if (t->use_tx_dma) {
t->tx_dma_virt = xmit->buf;
t->tx_dma_phys = dma_map_single(u->dev, xmit->buf,
- UART_XMIT_SIZE, DMA_BIDIRECTIONAL);
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
t->tx_dma_size = UART_XMIT_SIZE;
t->tx_dma_offset = 0;
@@ -636,15 +763,16 @@ static int tegra_startup(struct uart_port *u)
t->tx_dma_req.dest_addr = t->phys;
t->tx_dma_req.dest_wrap = 4;
t->tx_dma_req.source_wrap = 0;
- t->tx_dma_req.data = &t->tasklet;
+ t->tx_dma_req.data = u;
+ t->tx_dma_req.size = 0;
}
t->use_rx_dma = false;
- if (use_dma) {
+ if (!rx_force_pio) {
if (!tegra_uart_init_rx_dma(t))
t->use_rx_dma = true;
}
- ret = tegra_uart_hwinit(t);
+ ret = tegra_uart_hw_init(t);
if (ret)
goto fail;
@@ -664,57 +792,35 @@ fail:
return ret;
}
-#define TX_EMPTY_STATUS (NV_DRF_DEF(UART, LSR, TMTY, EMPTY) | \
- NV_DRF_DEF(UART, LSR, THRE, EMPTY))
-
static void tegra_shutdown(struct uart_port *u)
{
- int timeout = 10;
-
struct tegra_uart_port *t;
+ unsigned long flags;
+ spin_lock_irqsave(&u->lock, flags);
t = container_of(u, struct tegra_uart_port, uport);
dev_vdbg(u->dev, "+tegra_shutdown\n");
- if (!t->dma_for_tx) {
- /* wait for 10 msec to drain the Tx buffer, if not empty */
- unsigned char lsr;
- do {
- lsr = readb(t->regs + UART_LSR_0);
- if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
- break;
- timeout--;
- msleep(1);
- } while (timeout);
- } else {
- do {
- timeout = 10;
- if (tegra_dma_is_empty(t->tx_dma))
- break;
- timeout--;
- msleep(1);
- } while (timeout);
- if (!timeout)
- dev_info(u->dev, "DMA wait timedout\n");
+ if (t->use_tx_dma) {
+ /* FIXME: dequeue means abort. Should we need to abort
+ * or wait for the DMA to complete?
+ */
+ tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req);
+ t->tx_dma_req.size = 0;
}
+ tegra_uart_hw_deinit(t);
+ spin_unlock_irqrestore(&u->lock, flags);
if (t->use_rx_dma) {
- tegra_dma_flush(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_dma_free_channel(t->rx_dma);
- dma_free_coherent(u->dev, t->rx_dma_req[0].size,
- t->rx_dma_req[0].virt_addr, t->rx_dma_req[0].dest_addr);
- dma_free_coherent(u->dev, t->rx_dma_req[1].size,
- t->rx_dma_req[1].virt_addr, t->rx_dma_req[1].dest_addr);
}
- if (t->dma_for_tx) {
+ if (t->use_tx_dma) {
tegra_dma_free_channel(t->tx_dma);
}
- clk_disable(t->clk);
-
- NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_TRUE);
free_irq(t->irq, u);
-
dev_vdbg(u->dev, "-tegra_shutdown\n");
}
@@ -733,7 +839,27 @@ static unsigned int tegra_get_mctrl(struct uart_port *u)
static void tegra_set_mctrl(struct uart_port *u, unsigned int mctrl)
{
- dev_vdbg(u->dev, "tegra_set_mctrl\n");
+ unsigned char mcr;
+ struct tegra_uart_port *t;
+
+ dev_vdbg(u->dev, "tegra_set_mctrl called with %d\n", mctrl);
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ mcr = t->mcr_shadow;
+ if (mctrl & TIOCM_RTS)
+ mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_LOW, mcr);
+ else
+ mcr = NV_FLD_SET_DRF_DEF(UART, MCR, RTS, FORCE_RTS_HI, mcr);
+
+ if (mctrl & TIOCM_DTR)
+ mcr = NV_FLD_SET_DRF_DEF(UART, MCR, DTR, FORCE_DTR_LOW, mcr);
+ else
+ mcr = NV_FLD_SET_DRF_DEF(UART, MCR, DTR, FORCE_DTR_HI, mcr);
+
+ if (mcr != t->mcr_shadow) {
+ writeb(mcr, t->regs + UART_MCR_0);
+ t->mcr_shadow = mcr;
+ }
return;
}
@@ -759,6 +885,8 @@ static int tegra_request_port(struct uart_port *u)
static void tegra_release_port(struct uart_port *u)
{
+
+
}
static unsigned int tegra_tx_empty(struct uart_port *u)
@@ -770,20 +898,21 @@ static unsigned int tegra_tx_empty(struct uart_port *u)
t = container_of(u, struct tegra_uart_port, uport);
dev_vdbg(u->dev, "+tegra_tx_empty\n");
+ if (t->use_tx_dma) {
+ if (tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req))
+ return 0;
+ }
lsr = readb(t->regs + UART_LSR_0);
if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
ret = TIOCSER_TEMT;
else
ret = 0;
-
dev_vdbg(u->dev, "-tegra_tx_empty\n");
return 0;
}
static void tegra_stop_tx(struct uart_port *u)
{
- dev_vdbg(u->dev, "+tegra_stop_tx\n");
- dev_vdbg(u->dev, "-tegra_stop_tx\n");
return;
}
@@ -793,25 +922,20 @@ static void tegra_start_tx_locked(struct uart_port *u)
t = container_of(u, struct tegra_uart_port, uport);
- // dev_vdbg(t->uport.dev, "+tegra_start_tx_locked\n");
+ dev_vdbg(t->uport.dev, "+tegra_start_tx_locked\n");
- if (!t->dma_for_tx) {
+ if (!t->use_tx_dma) {
/* Enable interrupt on transmit FIFO empty, if it is disabled */
- if (!(t->ier_shadow & NV_DRF_DEF(UART, IER_DLAB_0, IE_THR,
- ENABLE))) {
+ if (!t->tx_pio_inflight) {
+ t->tx_pio_inflight = true;
t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0,
IE_THR, ENABLE, t->ier_shadow);
writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0);
}
} else {
- /* If DMA is empty try to scheule new requests */
- if (tegra_dma_is_empty(t->tx_dma)) {
- dev_vdbg(t->uport.dev, "Scheduling Tx\n");
- tasklet_schedule(&t->tasklet);
- }
+ queue_work(t->work_queue, &t->work);
}
-
- // dev_vdbg(t->uport.dev, "-tegra_start_tx_locked\n");
+ dev_vdbg(t->uport.dev, "-tegra_start_tx_locked\n");
}
static void tegra_enable_ms(struct uart_port *u)
@@ -830,6 +954,9 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud)
NvError err;
unsigned char lcr;
+ if (t->baud == baud)
+ return;
+
clock = (baud * 16) / 1000;
clock = clock ? clock : 1;
@@ -848,10 +975,10 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud)
return;
}
- /* FIXME is this division correct? */
- divisor = (actual_clock * 1000) / 16;
+ divisor = (actual_clock * 1000);
+ do_div(divisor, 16);
divisor += baud/2;
- divisor /= baud;
+ do_div(divisor, baud);
lcr = t->lcr_shadow;
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, DLAB, ENABLE, lcr);
@@ -863,7 +990,8 @@ static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud)
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, DLAB, DISABLE, lcr);
writeb(lcr, t->regs + UART_LCR_0);
- dev_dbg(t->uport.dev, "Baud %d clock freq %d and divisor of %d\n",
+ t->baud = baud;
+ dev_info(t->uport.dev, "Baud %d clock freq %d and divisor of %d\n",
baud, actual_clock, divisor);
}
@@ -892,18 +1020,18 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
lcr = t->lcr_shadow;
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, PAR, NO_PARITY, lcr);
if (PARENB == (c_cflag & PARENB)) {
- if (PARODD == (c_cflag & PARODD)) {
- strlcat(debug_string, "even parity ", 50);
+ if (CMSPAR == (c_cflag & CMSPAR)) {
+ strlcat(debug_string, "space parity ", 50);
+ /* FIXME What is space parity? */
+ /* data |= SPACE_PARITY; */
+ } else if (c_cflag & PARODD) {
+ strlcat(debug_string, "ODD parity ", 50);
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, PAR, PARITY, lcr);
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, EVEN, DISABLE, lcr);
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, SET_P, NO_PARITY,
lcr);
- } else if (CMSPAR == (c_cflag & CMSPAR)) {
- strlcat(debug_string, "space parity ", 50);
- /* FIXME What is space parity? */
- /* data |= SPACE_PARITY; */
} else {
- strlcat(debug_string, "odd parity ", 50);
+ strlcat(debug_string, "Even parity ", 50);
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, PAR, PARITY, lcr);
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, EVEN, ENABLE, lcr);
lcr = NV_FLD_SET_DRF_DEF(UART, LCR, SET_P, NO_PARITY,
@@ -951,36 +1079,49 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
return;
}
-static void tegra_pm(struct uart_port *u, unsigned int state,
- unsigned int oldstate)
+/*
+ * Flush any TX data submitted for DMA. Called when the TX circular
+ * buffer is reset.
+ */
+static void tegra_flush_buffer(struct uart_port *u)
{
+ struct tegra_uart_port *t;
+ dev_vdbg(u->dev, "tegra_flush_buffer called");
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ if (t->use_tx_dma) {
+ tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req);
+ t->tx_dma_req.size = 0;
+ }
+ return;
}
-static const char *tegra_type(struct uart_port *u)
-{
+
+static void tegra_pm(struct uart_port *u, unsigned int state,
+ unsigned int oldstate) {
+
+}
+
+static const char *tegra_type(struct uart_port *u) {
return 0;
}
static struct uart_ops tegra_uart_ops = {
.tx_empty = tegra_tx_empty,
-
.set_mctrl = tegra_set_mctrl,
.get_mctrl = tegra_get_mctrl,
-
.stop_tx = tegra_stop_tx,
.start_tx = tegra_start_tx_locked,
.stop_rx = tegra_stop_rx,
-
+ .flush_buffer = tegra_flush_buffer,
.enable_ms = tegra_enable_ms,
.break_ctl = tegra_break_ctl,
-
.startup = tegra_startup,
.shutdown = tegra_shutdown,
.set_termios = tegra_set_termios,
-
.pm = tegra_pm,
.type = tegra_type,
-
.request_port = tegra_request_port,
.release_port = tegra_release_port,
};
@@ -1015,6 +1156,8 @@ static int __devexit tegra_uart_remove(struct platform_device *pdev)
u = &t->uport;
uart_remove_one_port(&tegra_uart_driver, u);
+ destroy_workqueue(t->work_queue);
+
platform_set_drvdata(pdev, NULL);
NvRmSetModuleTristate(s_hRmGlobal, t->modid, NV_TRUE);
@@ -1032,7 +1175,7 @@ static int __init tegra_uart_probe(struct platform_device *pdev)
struct uart_port *u;
int ret;
char clk_name[MAX_CLK_NAME_CHARS];
-
+ char name[64];
if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr) {
printk(KERN_ERR "Invalid Uart instance (%d) \n", pdev->id);
return -ENODEV;
@@ -1058,9 +1201,6 @@ static int __init tegra_uart_probe(struct platform_device *pdev)
goto fail;
}
- tasklet_init(&t->tasklet, tegra_dma_tasklet_func,
- (unsigned long)u);
-
if (NvRmSetModuleTristate(s_hRmGlobal,
t->modid, NV_FALSE) != NvSuccess) {
dev_err(u->dev, "No Pin Mux - not registering the port\n");
@@ -1078,11 +1218,16 @@ static int __init tegra_uart_probe(struct platform_device *pdev)
return ret;
}
+ snprintf(name, sizeof(name), "tegra_hsuart_%d", u->line);
+ t->work_queue = create_singlethread_workqueue(name);
+ if (t->work_queue == NULL) {
+ dev_err(u->dev, "Failed to create work queue\n");
+ goto fail;
+ }
+ INIT_WORK(&t->work, tegra_tx_dma_workqueue);
printk(KERN_INFO "Registered UART port %s%d\n",
tegra_uart_driver.dev_name, u->line);
-
return ret;
-
fail:
kfree(t);
return -ENODEV;