diff options
Diffstat (limited to 'drivers/serial/tegra_hsuart.c')
-rw-r--r-- | drivers/serial/tegra_hsuart.c | 717 |
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; |