diff options
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 899 |
1 files changed, 572 insertions, 327 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 3d790033744e..b94a4d7b4849 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -224,12 +224,16 @@ #define UARTWATER_TXWATER_OFF 0 #define UARTWATER_RXWATER_OFF 16 -#define FSL_UART_RX_DMA_BUFFER_SIZE 64 +/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */ +#define DMA_RX_TIMEOUT (10) #define DRIVER_NAME "fsl-lpuart" #define DEV_NAME "ttyLP" #define UART_NR 6 +static bool nodma = false; +module_param(nodma, bool, S_IRUGO); + struct lpuart_port { struct uart_port port; struct clk *clk; @@ -243,18 +247,18 @@ struct lpuart_port { struct dma_chan *dma_rx_chan; struct dma_async_tx_descriptor *dma_tx_desc; struct dma_async_tx_descriptor *dma_rx_desc; - dma_addr_t dma_tx_buf_bus; - dma_addr_t dma_rx_buf_bus; dma_cookie_t dma_tx_cookie; dma_cookie_t dma_rx_cookie; - unsigned char *dma_tx_buf_virt; - unsigned char *dma_rx_buf_virt; unsigned int dma_tx_bytes; unsigned int dma_rx_bytes; - int dma_tx_in_progress; - int dma_rx_in_progress; + bool dma_tx_in_progress; unsigned int dma_rx_timeout; struct timer_list lpuart_timer; + struct scatterlist rx_sgl, tx_sgl[2]; + struct circ_buf rx_ring; + int rx_dma_rng_buf_len; + unsigned int dma_tx_nents; + wait_queue_head_t dma_wait; }; static const struct of_device_id lpuart_dt_ids[] = { @@ -270,7 +274,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids); /* Forward declare this for the dma callbacks*/ static void lpuart_dma_tx_complete(void *arg); -static void lpuart_dma_rx_complete(void *arg); static u32 lpuart32_read(void __iomem *addr) { @@ -316,141 +319,102 @@ static void lpuart32_stop_rx(struct uart_port *port) lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL); } -static void lpuart_copy_rx_to_tty(struct lpuart_port *sport, - struct tty_port *tty, int count) +static void lpuart_dma_tx(struct lpuart_port *sport) { - int copied; - - sport->port.icount.rx += count; + struct circ_buf *xmit = &sport->port.state->xmit; + struct scatterlist *sgl = sport->tx_sgl; + struct device *dev = sport->port.dev; + int ret; - if (!tty) { - dev_err(sport->port.dev, "No tty port\n"); + if (sport->dma_tx_in_progress) return; - } - dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - copied = tty_insert_flip_string(tty, - ((unsigned char *)(sport->dma_rx_buf_virt)), count); + sport->dma_tx_bytes = uart_circ_chars_pending(xmit); - if (copied != count) { - WARN_ON(1); - dev_err(sport->port.dev, "RxData copy to tty layer failed\n"); + if (xmit->tail < xmit->head || xmit->head == 0) { + sport->dma_tx_nents = 1; + sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes); + } else { + sport->dma_tx_nents = 2; + sg_init_table(sgl, 2); + sg_set_buf(sgl, xmit->buf + xmit->tail, + UART_XMIT_SIZE - xmit->tail); + sg_set_buf(sgl + 1, xmit->buf, xmit->head); } - dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); -} - -static void lpuart_pio_tx(struct lpuart_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long flags; - - spin_lock_irqsave(&sport->port.lock, flags); - - while (!uart_circ_empty(xmit) && - readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) { - writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; + ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + if (!ret) { + dev_err(dev, "DMA mapping error for TX.\n"); + return; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - - if (uart_circ_empty(xmit)) - writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - dma_addr_t tx_bus_addr; - - dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus, - UART_XMIT_SIZE, DMA_TO_DEVICE); - sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1); - tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail; - sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan, - tx_bus_addr, sport->dma_tx_bytes, + sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl, + sport->dma_tx_nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); - if (!sport->dma_tx_desc) { - dev_err(sport->port.dev, "Not able to get desc for tx\n"); - return -EIO; + dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + dev_err(dev, "Cannot prepare TX slave DMA!\n"); + return; } sport->dma_tx_desc->callback = lpuart_dma_tx_complete; sport->dma_tx_desc->callback_param = sport; - sport->dma_tx_in_progress = 1; + sport->dma_tx_in_progress = true; sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc); dma_async_issue_pending(sport->dma_tx_chan); - - return 0; -} - -static void lpuart_prepare_tx(struct lpuart_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long count = CIRC_CNT_TO_END(xmit->head, - xmit->tail, UART_XMIT_SIZE); - - if (!count) - return; - - if (count < sport->txfifo_size) - writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - else { - writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, - sport->port.membase + UARTCR5); - lpuart_dma_tx(sport, count); - } } static void lpuart_dma_tx_complete(void *arg) { struct lpuart_port *sport = arg; + struct scatterlist *sgl = &sport->tx_sgl[0]; struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; - async_tx_ack(sport->dma_tx_desc); - spin_lock_irqsave(&sport->port.lock, flags); + dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1); - sport->dma_tx_in_progress = 0; + + sport->port.icount.tx += sport->dma_tx_bytes; + sport->dma_tx_in_progress = false; + spin_unlock_irqrestore(&sport->port.lock, flags); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - lpuart_prepare_tx(sport); + if (waitqueue_active(&sport->dma_wait)) { + wake_up(&sport->dma_wait); + return; + } + + spin_lock_irqsave(&sport->port.lock, flags); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port)) + lpuart_dma_tx(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } -static int lpuart_dma_rx(struct lpuart_port *sport) +static int lpuart_dma_tx_request(struct uart_port *port) { - dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); - sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan, - sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + struct dma_slave_config dma_tx_sconfig = {}; + int ret; - if (!sport->dma_rx_desc) { - dev_err(sport->port.dev, "Not able to get desc for rx\n"); - return -EIO; - } + dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR; + dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_tx_sconfig.dst_maxburst = 1; + dma_tx_sconfig.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig); - sport->dma_rx_desc->callback = lpuart_dma_rx_complete; - sport->dma_rx_desc->callback_param = sport; - sport->dma_rx_in_progress = 1; - sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc); - dma_async_issue_pending(sport->dma_rx_chan); + if (ret) { + dev_err(sport->port.dev, + "DMA slave config failed, err = %d\n", ret); + return ret; + } return 0; } @@ -458,75 +422,76 @@ static int lpuart_dma_rx(struct lpuart_port *sport) static void lpuart_flush_buffer(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + if (sport->lpuart_dma_tx_use) { + if (sport->dma_tx_in_progress) { + dma_unmap_sg(sport->port.dev, &sport->tx_sgl[0], + sport->dma_tx_nents, DMA_TO_DEVICE); + sport->dma_tx_in_progress = false; + } dmaengine_terminate_all(sport->dma_tx_chan); - sport->dma_tx_in_progress = 0; } } -static void lpuart_dma_rx_complete(void *arg) +#if defined(CONFIG_CONSOLE_POLL) + +static int lpuart_poll_init(struct uart_port *port) { - struct lpuart_port *sport = arg; - struct tty_port *port = &sport->port.state->port; + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); unsigned long flags; + unsigned char temp; - async_tx_ack(sport->dma_rx_desc); - mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout); + sport->port.fifosize = 0; spin_lock_irqsave(&sport->port.lock, flags); + /* Disable Rx & Tx */ + writeb(0, sport->port.membase + UARTCR2); - sport->dma_rx_in_progress = 0; - lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE); - tty_flip_buffer_push(port); - lpuart_dma_rx(sport); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static void lpuart_timer_func(unsigned long data) -{ - struct lpuart_port *sport = (struct lpuart_port *)data; - struct tty_port *port = &sport->port.state->port; - struct dma_tx_state state; - unsigned long flags; - unsigned char temp; - int count; + temp = readb(sport->port.membase + UARTPFIFO); + /* Enable Rx and Tx FIFO */ + writeb(temp | UARTPFIFO_RXFE | UARTPFIFO_TXFE, + sport->port.membase + UARTPFIFO); - del_timer(&sport->lpuart_timer); - dmaengine_pause(sport->dma_rx_chan); - dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state); - dmaengine_terminate_all(sport->dma_rx_chan); - count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue; - async_tx_ack(sport->dma_rx_desc); + /* flush Tx and Rx FIFO */ + writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, + sport->port.membase + UARTCFIFO); - spin_lock_irqsave(&sport->port.lock, flags); + /* explicitly clear RDRF */ + if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { + readb(sport->port.membase + UARTDR); + writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); + } - sport->dma_rx_in_progress = 0; - lpuart_copy_rx_to_tty(sport, port, count); - tty_flip_buffer_push(port); - temp = readb(sport->port.membase + UARTCR5); - writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5); + writeb(0, sport->port.membase + UARTTWFIFO); + writeb(1, sport->port.membase + UARTRWFIFO); + /* Enable Rx and Tx */ + writeb(UARTCR2_RE | UARTCR2_TE, sport->port.membase + UARTCR2); spin_unlock_irqrestore(&sport->port.lock, flags); + + return 0; } -static inline void lpuart_prepare_rx(struct lpuart_port *sport) +static void lpuart_poll_put_char(struct uart_port *port, unsigned char c) { - unsigned long flags; - unsigned char temp; - - spin_lock_irqsave(&sport->port.lock, flags); + /* drain */ + while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) + barrier(); - sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout; - add_timer(&sport->lpuart_timer); + writeb(c, port->membase + UARTDR); +} - lpuart_dma_rx(sport); - temp = readb(sport->port.membase + UARTCR5); - writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5); +static int lpuart_poll_get_char(struct uart_port *port) +{ + if (!(readb(port->membase + UARTSR1) & UARTSR1_RDRF)) + return NO_POLL_CHAR; - spin_unlock_irqrestore(&sport->port.lock, flags); + return readb(port->membase + UARTDR); } +#endif + static inline void lpuart_transmit_buffer(struct lpuart_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; @@ -580,8 +545,8 @@ static void lpuart_start_tx(struct uart_port *port) writeb(temp | UARTCR2_TIE, port->membase + UARTCR2); if (sport->lpuart_dma_tx_use) { - if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress) - lpuart_prepare_tx(sport); + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) + lpuart_dma_tx(sport); } else { if (readb(port->membase + UARTSR1) & UARTSR1_TDRE) lpuart_transmit_buffer(sport); @@ -600,6 +565,29 @@ static void lpuart32_start_tx(struct uart_port *port) lpuart32_transmit_buffer(sport); } +/* return TIOCSER_TEMT when transmitter is not busy */ +static unsigned int lpuart_tx_empty(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + unsigned char sr1 = readb(port->membase + UARTSR1); + unsigned char sfifo = readb(port->membase + UARTSFIFO); + + if (sport->dma_tx_in_progress) + return 0; + + if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT) + return TIOCSER_TEMT; + + return 0; +} + +static unsigned int lpuart32_tx_empty(struct uart_port *port) +{ + return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? + TIOCSER_TEMT : 0; +} + static irqreturn_t lpuart_txint(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -644,6 +632,9 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id) unsigned long flags; unsigned char rx, sr; + if (sport->port.irq_wake) + pm_wakeup_event(port->tty->dev, 0); + spin_lock_irqsave(&sport->port.lock, flags); while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) { @@ -766,23 +757,15 @@ out: static irqreturn_t lpuart_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; - unsigned char sts, crdma; + unsigned char sts; sts = readb(sport->port.membase + UARTSR1); - crdma = readb(sport->port.membase + UARTCR5); - if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) { - if (sport->lpuart_dma_rx_use) - lpuart_prepare_rx(sport); - else - lpuart_rxint(irq, dev_id); - } - if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) { - if (sport->lpuart_dma_tx_use) - lpuart_pio_tx(sport); - else - lpuart_txint(irq, dev_id); - } + if (sts & UARTSR1_RDRF) + lpuart_rxint(irq, dev_id); + + if (sts & UARTSR1_TDRE) + lpuart_txint(irq, dev_id); return IRQ_HANDLED; } @@ -807,17 +790,241 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) return IRQ_HANDLED; } -/* return TIOCSER_TEMT when transmitter is not busy */ -static unsigned int lpuart_tx_empty(struct uart_port *port) +static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) { - return (readb(port->membase + UARTSR1) & UARTSR1_TC) ? - TIOCSER_TEMT : 0; + struct tty_port *port = &sport->port.state->port; + struct dma_tx_state state; + enum dma_status dmastat; + struct circ_buf *ring = &sport->rx_ring; + unsigned long flags; + int count = 0; + unsigned char sr; + + sr = readb(sport->port.membase + UARTSR1); + + if (sr & (UARTSR1_PE | UARTSR1_FE)) { + /* Read DR to clear the error flags */ + readb(sport->port.membase + UARTDR); + + if (sr & UARTSR1_PE) + sport->port.icount.parity++; + else if (sr & UARTSR1_FE) + sport->port.icount.frame++; + } + + async_tx_ack(sport->dma_rx_desc); + + spin_lock_irqsave(&sport->port.lock, flags); + + dmastat = dmaengine_tx_status(sport->dma_rx_chan, + sport->dma_rx_cookie, + &state); + + if (dmastat == DMA_ERROR) { + dev_err(sport->port.dev, "Rx DMA transfer failed!\n"); + spin_unlock_irqrestore(&sport->port.lock, flags); + return; + } + + /* CPU claims ownership of RX DMA buffer */ + dma_sync_sg_for_cpu(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); + + /* + * ring->head points to the end of data already written by the DMA. + * ring->tail points to the beginning of data to be read by the + * framework. + * The current transfer size should not be larger than the dma buffer + * length. + */ + ring->head = sport->rx_sgl.length - state.residue; + BUG_ON(ring->head > sport->rx_sgl.length); + /* + * At this point ring->head may point to the first byte right after the + * last byte of the dma buffer: + * 0 <= ring->head <= sport->rx_sgl.length + * + * However ring->tail must always points inside the dma buffer: + * 0 <= ring->tail <= sport->rx_sgl.length - 1 + * + * Since we use a ring buffer, we have to handle the case + * where head is lower than tail. In such a case, we first read from + * tail to the end of the buffer then reset tail. + */ + if (ring->head < ring->tail) { + count = sport->rx_sgl.length - ring->tail; + + tty_insert_flip_string(port, ring->buf + ring->tail, count); + ring->tail = 0; + sport->port.icount.rx += count; + } + + /* Finally we read data from tail to head */ + if (ring->tail < ring->head) { + count = ring->head - ring->tail; + tty_insert_flip_string(port, ring->buf + ring->tail, count); + /* Wrap ring->head if needed */ + if (ring->head >= sport->rx_sgl.length) + ring->head = 0; + ring->tail = ring->head; + sport->port.icount.rx += count; + } + + dma_sync_sg_for_device(sport->port.dev, &sport->rx_sgl, 1, + DMA_FROM_DEVICE); + + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout); } -static unsigned int lpuart32_tx_empty(struct uart_port *port) +static void lpuart_dma_rx_complete(void *arg) { - return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? - TIOCSER_TEMT : 0; + struct lpuart_port *sport = arg; + + lpuart_copy_rx_to_tty(sport); +} + +static void lpuart_timer_func(unsigned long data) +{ + struct lpuart_port *sport = (struct lpuart_port *)data; + + lpuart_copy_rx_to_tty(sport); +} + +static inline int lpuart_start_rx_dma(struct lpuart_port *sport) +{ + struct dma_slave_config dma_rx_sconfig = {}; + struct circ_buf *ring = &sport->rx_ring; + int ret, nent; + int bits, baud; + struct tty_struct *tty = tty_port_tty_get(&sport->port.state->port); + struct ktermios *termios = &tty->termios; + + baud = tty_get_baud_rate(tty); + + bits = (termios->c_cflag & CSIZE) == CS7 ? 9 : 10; + if (termios->c_cflag & PARENB) + bits++; + + /* + * Calculate length of one DMA buffer size to keep latency below + * 10ms at any baud rate. + */ + sport->rx_dma_rng_buf_len = (DMA_RX_TIMEOUT * baud / bits / 1000) * 2; + sport->rx_dma_rng_buf_len = (1 << (fls(sport->rx_dma_rng_buf_len) - 1)); + if (sport->rx_dma_rng_buf_len < 16) + sport->rx_dma_rng_buf_len = 16; + + ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC); + if (!ring->buf) { + dev_err(sport->port.dev, "Ring buf alloc failed\n"); + return -ENOMEM; + } + + sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len); + sg_set_buf(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len); + nent = dma_map_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); + + if (!nent) { + dev_err(sport->port.dev, "DMA Rx mapping error\n"); + return -EINVAL; + } + + dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR; + dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_rx_sconfig.src_maxburst = 1; + dma_rx_sconfig.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig); + + if (ret < 0) { + dev_err(sport->port.dev, + "DMA Rx slave config failed, err = %d\n", ret); + return ret; + } + + sport->dma_rx_desc = dmaengine_prep_dma_cyclic(sport->dma_rx_chan, + sg_dma_address(&sport->rx_sgl), + sport->rx_sgl.length, + sport->rx_sgl.length / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!sport->dma_rx_desc) { + dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n"); + return -EFAULT; + } + + sport->dma_rx_desc->callback = lpuart_dma_rx_complete; + sport->dma_rx_desc->callback_param = sport; + sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc); + dma_async_issue_pending(sport->dma_rx_chan); + + writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS, + sport->port.membase + UARTCR5); + + return 0; +} + +static void lpuart_dma_rx_free(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + + if (sport->dma_rx_chan) + dmaengine_terminate_all(sport->dma_rx_chan); + + dma_unmap_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); + kfree(sport->rx_ring.buf); + sport->rx_ring.tail = 0; + sport->rx_ring.head = 0; + sport->dma_rx_desc = NULL; + sport->dma_rx_cookie = -EINVAL; +} + +static int lpuart_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + + u8 modem = readb(sport->port.membase + UARTMODEM) & + ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE); + writeb(modem, sport->port.membase + UARTMODEM); + + if (rs485->flags & SER_RS485_ENABLED) { + /* Enable auto RS-485 RTS mode */ + modem |= UARTMODEM_TXRTSE; + + /* + * RTS needs to be logic HIGH either during transer _or_ after + * transfer, other variants are not supported by the hardware. + */ + + if (!(rs485->flags & (SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND))) + rs485->flags |= SER_RS485_RTS_ON_SEND; + + if (rs485->flags & SER_RS485_RTS_ON_SEND && + rs485->flags & SER_RS485_RTS_AFTER_SEND) + rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; + + /* + * The hardware defaults to RTS logic HIGH while transfer. + * Switch polarity in case RTS shall be logic HIGH + * after transfer. + * Note: UART is assumed to be active high. + */ + if (rs485->flags & SER_RS485_RTS_ON_SEND) + modem &= ~UARTMODEM_TXRTSPOL; + else if (rs485->flags & SER_RS485_RTS_AFTER_SEND) + modem |= UARTMODEM_TXRTSPOL; + } + + /* Store the new configuration */ + sport->port.rs485 = *rs485; + + writeb(modem, sport->port.membase + UARTMODEM); + return 0; } static unsigned int lpuart_get_mctrl(struct uart_port *port) @@ -853,17 +1060,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port) static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned char temp; + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); - temp = readb(port->membase + UARTMODEM) & + /* Make sure RXRTSE bit is not set when RS485 is enabled */ + if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) { + temp = readb(sport->port.membase + UARTMODEM) & ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); - if (mctrl & TIOCM_RTS) - temp |= UARTMODEM_RXRTSE; + if (mctrl & TIOCM_RTS) + temp |= UARTMODEM_RXRTSE; - if (mctrl & TIOCM_CTS) - temp |= UARTMODEM_TXCTSE; + if (mctrl & TIOCM_CTS) + temp |= UARTMODEM_TXCTSE; - writeb(temp, port->membase + UARTMODEM); + writeb(temp, port->membase + UARTMODEM); + } } static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -921,13 +1133,16 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, sport->port.membase + UARTPFIFO); - /* explicitly clear RDRF */ - readb(sport->port.membase + UARTSR1); - /* flush Tx and Rx FIFO */ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO); + /* explicitly clear RDRF */ + if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { + readb(sport->port.membase + UARTDR); + writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); + } + writeb(0, sport->port.membase + UARTTWFIFO); writeb(1, sport->port.membase + UARTRWFIFO); @@ -960,110 +1175,12 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL); } -static int lpuart_dma_tx_request(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - struct dma_slave_config dma_tx_sconfig; - dma_addr_t dma_bus; - unsigned char *dma_buf; - int ret; - - dma_bus = dma_map_single(sport->dma_tx_chan->device->dev, - sport->port.state->xmit.buf, - UART_XMIT_SIZE, DMA_TO_DEVICE); - - if (dma_mapping_error(sport->dma_tx_chan->device->dev, dma_bus)) { - dev_err(sport->port.dev, "dma_map_single tx failed\n"); - return -ENOMEM; - } - - dma_buf = sport->port.state->xmit.buf; - dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR; - dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma_tx_sconfig.dst_maxburst = sport->txfifo_size; - dma_tx_sconfig.direction = DMA_MEM_TO_DEV; - ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig); - - if (ret < 0) { - dev_err(sport->port.dev, - "Dma slave config failed, err = %d\n", ret); - return ret; - } - - sport->dma_tx_buf_virt = dma_buf; - sport->dma_tx_buf_bus = dma_bus; - sport->dma_tx_in_progress = 0; - - return 0; -} - -static int lpuart_dma_rx_request(struct uart_port *port) +static void rx_dma_timer_init(struct lpuart_port *sport) { - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - struct dma_slave_config dma_rx_sconfig; - dma_addr_t dma_bus; - unsigned char *dma_buf; - int ret; - - dma_buf = devm_kzalloc(sport->port.dev, - FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL); - - if (!dma_buf) { - dev_err(sport->port.dev, "Dma rx alloc failed\n"); - return -ENOMEM; - } - - dma_bus = dma_map_single(sport->dma_rx_chan->device->dev, dma_buf, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - - if (dma_mapping_error(sport->dma_rx_chan->device->dev, dma_bus)) { - dev_err(sport->port.dev, "dma_map_single rx failed\n"); - return -ENOMEM; - } - - dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR; - dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - dma_rx_sconfig.src_maxburst = 1; - dma_rx_sconfig.direction = DMA_DEV_TO_MEM; - ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig); - - if (ret < 0) { - dev_err(sport->port.dev, - "Dma slave config failed, err = %d\n", ret); - return ret; - } - - sport->dma_rx_buf_virt = dma_buf; - sport->dma_rx_buf_bus = dma_bus; - sport->dma_rx_in_progress = 0; - - return 0; -} - -static void lpuart_dma_tx_free(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - - dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus, - UART_XMIT_SIZE, DMA_TO_DEVICE); - - sport->dma_tx_buf_bus = 0; - sport->dma_tx_buf_virt = NULL; -} - -static void lpuart_dma_rx_free(struct uart_port *port) -{ - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - - dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus, - FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); - - sport->dma_rx_buf_bus = 0; - sport->dma_rx_buf_virt = NULL; + setup_timer(&sport->lpuart_timer, lpuart_timer_func, + (unsigned long)sport); + sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout; + add_timer(&sport->lpuart_timer); } static int lpuart_startup(struct uart_port *port) @@ -1084,22 +1201,6 @@ static int lpuart_startup(struct uart_port *port) sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK) + 1); - if (sport->dma_rx_chan && !lpuart_dma_rx_request(port)) { - sport->lpuart_dma_rx_use = true; - setup_timer(&sport->lpuart_timer, lpuart_timer_func, - (unsigned long)sport); - } else - sport->lpuart_dma_rx_use = false; - - - if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) { - sport->lpuart_dma_tx_use = true; - temp = readb(port->membase + UARTCR5); - temp &= ~UARTCR5_RDMAS; - writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); - } else - sport->lpuart_dma_tx_use = false; - ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0, DRIVER_NAME, sport); if (ret) @@ -1113,7 +1214,29 @@ static int lpuart_startup(struct uart_port *port) temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); writeb(temp, sport->port.membase + UARTCR2); + if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) { + /* set Rx DMA timeout */ + sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT); + if (!sport->dma_rx_timeout) + sport->dma_rx_timeout = 1; + + sport->lpuart_dma_rx_use = true; + rx_dma_timer_init(sport); + } else { + sport->lpuart_dma_rx_use = false; + } + + if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) { + init_waitqueue_head(&sport->dma_wait); + sport->lpuart_dma_tx_use = true; + temp = readb(port->membase + UARTCR5); + writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); + } else { + sport->lpuart_dma_tx_use = false; + } + spin_unlock_irqrestore(&sport->port.lock, flags); + return 0; } @@ -1170,12 +1293,19 @@ static void lpuart_shutdown(struct uart_port *port) devm_free_irq(port->dev, port->irq, sport); if (sport->lpuart_dma_rx_use) { - lpuart_dma_rx_free(&sport->port); del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); } - if (sport->lpuart_dma_tx_use) - lpuart_dma_tx_free(&sport->port); + if (sport->lpuart_dma_tx_use) { + if (wait_event_interruptible(sport->dma_wait, + !sport->dma_tx_in_progress) != false) { + sport->dma_tx_in_progress = false; + dmaengine_terminate_all(sport->dma_tx_chan); + } + + lpuart_stop_tx(port); + } } static void lpuart32_shutdown(struct uart_port *port) @@ -1203,13 +1333,14 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); unsigned long flags; - unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; + unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem; unsigned int baud; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; unsigned int sbr, brfa; cr1 = old_cr1 = readb(sport->port.membase + UARTCR1); old_cr2 = readb(sport->port.membase + UARTCR2); + cr3 = readb(sport->port.membase + UARTCR3); cr4 = readb(sport->port.membase + UARTCR4); bdh = readb(sport->port.membase + UARTBDH); modem = readb(sport->port.membase + UARTMODEM); @@ -1240,6 +1371,13 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, cr1 |= UARTCR1_M; } + /* + * When auto RS-485 RTS mode is enabled, + * hardware flow control need to be disabled. + */ + if (sport->port.rs485.flags & SER_RS485_ENABLED) + termios->c_cflag &= ~CRTSCTS; + if (termios->c_cflag & CRTSCTS) { modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); } else { @@ -1257,7 +1395,10 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & PARENB)) { if (termios->c_cflag & CMSPAR) { cr1 &= ~UARTCR1_PE; - cr1 |= UARTCR1_M; + if (termios->c_cflag & PARODD) + cr3 |= UARTCR3_T8; + else + cr3 &= ~UARTCR3_T8; } else { cr1 |= UARTCR1_PE; if ((termios->c_cflag & CSIZE) == CS8) @@ -1272,6 +1413,18 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* ask the core to calculate the divisor */ baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + /* + * Need to update the Ring buffer length according to the selected + * baud rate and restart Rx DMA path. + * + * Since timer function acqures sport->port.lock, need to stop before + * acquring same lock because otherwise del_timer_sync() can deadlock. + */ + if (old && sport->lpuart_dma_rx_use) { + del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + } + spin_lock_irqsave(&sport->port.lock, flags); sport->port.read_status_mask = 0; @@ -1297,17 +1450,6 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); - if (sport->lpuart_dma_rx_use) { - /* Calculate delay for 1.5 DMA buffers */ - sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) * - FSL_UART_RX_DMA_BUFFER_SIZE * 3 / - sport->rxfifo_size / 2; - dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n", - sport->dma_rx_timeout * 1000 / HZ, sport->port.timeout); - if (sport->dma_rx_timeout < msecs_to_jiffies(20)) - sport->dma_rx_timeout = msecs_to_jiffies(20); - } - /* wait transmit engin complete */ while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) barrier(); @@ -1325,12 +1467,20 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, writeb(cr4 | brfa, sport->port.membase + UARTCR4); writeb(bdh, sport->port.membase + UARTBDH); writeb(sbr & 0xFF, sport->port.membase + UARTBDL); + writeb(cr3, sport->port.membase + UARTCR3); writeb(cr1, sport->port.membase + UARTCR1); writeb(modem, sport->port.membase + UARTMODEM); /* restore control register */ writeb(old_cr2, sport->port.membase + UARTCR2); + if (old && sport->lpuart_dma_rx_use) { + if (!lpuart_start_rx_dma(sport)) + rx_dma_timer_init(sport); + else + sport->lpuart_dma_rx_use = false; + } + spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -1402,6 +1552,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, else ctrl &= ~UARTCTRL_PT; } + } else { + ctrl &= ~UARTCTRL_PE; } /* ask the core to calculate the divisor */ @@ -1494,7 +1646,7 @@ static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } -static struct uart_ops lpuart_pops = { +static const struct uart_ops lpuart_pops = { .tx_empty = lpuart_tx_empty, .set_mctrl = lpuart_set_mctrl, .get_mctrl = lpuart_get_mctrl, @@ -1511,9 +1663,14 @@ static struct uart_ops lpuart_pops = { .config_port = lpuart_config_port, .verify_port = lpuart_verify_port, .flush_buffer = lpuart_flush_buffer, +#if defined(CONFIG_CONSOLE_POLL) + .poll_init = lpuart_poll_init, + .poll_get_char = lpuart_poll_get_char, + .poll_put_char = lpuart_poll_put_char, +#endif }; -static struct uart_ops lpuart32_pops = { +static const struct uart_ops lpuart32_pops = { .tx_empty = lpuart32_tx_empty, .set_mctrl = lpuart32_set_mctrl, .get_mctrl = lpuart32_get_mctrl, @@ -1556,6 +1713,13 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) { struct lpuart_port *sport = lpuart_ports[co->index]; unsigned char old_cr2, cr2; + unsigned long flags; + int locked = 1; + + if (sport->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&sport->port.lock, flags); + else + spin_lock_irqsave(&sport->port.lock, flags); /* first save CR2 and then disable interrupts */ cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); @@ -1570,6 +1734,9 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) barrier(); writeb(old_cr2, sport->port.membase + UARTCR2); + + if (locked) + spin_unlock_irqrestore(&sport->port.lock, flags); } static void @@ -1577,6 +1744,13 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) { struct lpuart_port *sport = lpuart_ports[co->index]; unsigned long old_cr, cr; + unsigned long flags; + int locked = 1; + + if (sport->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&sport->port.lock, flags); + else + spin_lock_irqsave(&sport->port.lock, flags); /* first save CR2 and then disable interrupts */ cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL); @@ -1591,6 +1765,9 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) barrier(); lpuart32_write(old_cr, sport->port.membase + UARTCTRL); + + if (locked) + spin_unlock_irqrestore(&sport->port.lock, flags); } /* @@ -1800,6 +1977,18 @@ static struct uart_driver lpuart_reg = { .cons = LPUART_CONSOLE, }; +static struct dma_chan *lpuart_request_dma_chan(struct lpuart_port *sport, + const char *name) +{ + struct dma_chan *chan; + + chan = dma_request_slave_channel(sport->port.dev, name); + if (!chan) + dev_info(sport->port.dev, "DMA %s channel request failed, " + "operating without %s DMA\n", name, name); + return chan; +} + static int lpuart_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1830,13 +2019,21 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; sport->port.iotype = UPIO_MEM; - sport->port.irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(&pdev->dev, "cannot obtain irq\n"); + return ret; + } + sport->port.irq = ret; + if (sport->lpuart32) sport->port.ops = &lpuart32_pops; else sport->port.ops = &lpuart_pops; sport->port.flags = UPF_BOOT_AUTOCONF; + sport->port.rs485_config = lpuart_config_rs485; + sport->clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->clk)) { ret = PTR_ERR(sport->clk); @@ -1867,15 +2064,16 @@ static int lpuart_probe(struct platform_device *pdev) return ret; } - sport->dma_tx_chan = dma_request_slave_channel(sport->port.dev, "tx"); - if (!sport->dma_tx_chan) - dev_info(sport->port.dev, "DMA tx channel request failed, " - "operating without tx DMA\n"); + if (!nodma) { + sport->dma_tx_chan = lpuart_request_dma_chan(sport, "tx"); + sport->dma_rx_chan = lpuart_request_dma_chan(sport, "rx"); + } - sport->dma_rx_chan = dma_request_slave_channel(sport->port.dev, "rx"); - if (!sport->dma_rx_chan) - dev_info(sport->port.dev, "DMA rx channel request failed, " - "operating without rx DMA\n"); + if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) { + sport->port.rs485.flags |= SER_RS485_ENABLED; + sport->port.rs485.flags |= SER_RS485_RTS_ON_SEND; + writeb(UARTMODEM_TXRTSE, sport->port.membase + UARTMODEM); + } return 0; } @@ -1917,6 +2115,32 @@ static int lpuart_suspend(struct device *dev) uart_suspend_port(&lpuart_reg, &sport->port); + if (sport->lpuart_dma_rx_use) { + /* + * EDMA driver during suspend will forcefully release any + * non-idle DMA channels. If port wakeup is enabled or if port + * is console port or 'no_console_suspend' is set the Rx DMA + * cannot resume as as expected, hence gracefully release the + * Rx DMA path before suspend and start Rx DMA path on resume. + */ + if (sport->port.irq_wake) { + del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + } + + /* Disable Rx DMA to use UART port as wakeup source */ + writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS, + sport->port.membase + UARTCR5); + } + + if (sport->lpuart_dma_tx_use) { + sport->dma_tx_in_progress = false; + dmaengine_terminate_all(sport->dma_tx_chan); + } + + if (sport->port.suspended && !sport->port.irq_wake) + clk_disable_unprepare(sport->clk); + return 0; } @@ -1925,6 +2149,9 @@ static int lpuart_resume(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; + if (sport->port.suspended && !sport->port.irq_wake) + clk_prepare_enable(sport->clk); + if (sport->lpuart32) { lpuart32_setup_watermark(sport); temp = lpuart32_read(sport->port.membase + UARTCTRL); @@ -1938,6 +2165,24 @@ static int lpuart_resume(struct device *dev) writeb(temp, sport->port.membase + UARTCR2); } + if (sport->lpuart_dma_rx_use) { + if (sport->port.irq_wake) { + if (!lpuart_start_rx_dma(sport)) + rx_dma_timer_init(sport); + else + sport->lpuart_dma_rx_use = false; + } + } + + if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) { + init_waitqueue_head(&sport->dma_wait); + sport->lpuart_dma_tx_use = true; + writeb(readb(sport->port.membase + UARTCR5) | + UARTCR5_TDMAS, sport->port.membase + UARTCR5); + } else { + sport->lpuart_dma_tx_use = false; + } + uart_resume_port(&lpuart_reg, &sport->port); return 0; |