On Mon, Jan 21, 2019 at 05:39:39PM +0900, Atsushi Nemoto wrote: > Add DMA support for 32-bit variant of the LPUART, such as LS1021A. > > Signed-off-by: Tomonori Sakita <tomonori.sakita@xxxxxxxxxx> > Signed-off-by: Atsushi Nemoto <atsushi.nemoto@xxxxxxxxxx> > --- > drivers/tty/serial/fsl_lpuart.c | 191 ++++++++++++++++++++++++++++++++++------ > 1 file changed, 162 insertions(+), 29 deletions(-) > > diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c > index d0a98ec..f651a0d 100644 > --- a/drivers/tty/serial/fsl_lpuart.c > +++ b/drivers/tty/serial/fsl_lpuart.c > @@ -426,6 +426,17 @@ static void lpuart_dma_tx_complete(void *arg) > spin_unlock_irqrestore(&sport->port.lock, flags); > } > > +static dma_addr_t lpuart_dma_datareg_addr(struct lpuart_port *sport) > +{ > + switch (sport->port.iotype) { > + case UPIO_MEM32: > + return sport->port.mapbase + UARTDATA; > + case UPIO_MEM32BE: > + return sport->port.mapbase + UARTDATA + sizeof(u32) - 1; > + } > + return sport->port.mapbase + UARTDR; > +} > + > static int lpuart_dma_tx_request(struct uart_port *port) > { > struct lpuart_port *sport = container_of(port, > @@ -433,7 +444,7 @@ static int lpuart_dma_tx_request(struct uart_port *port) > struct dma_slave_config dma_tx_sconfig = {}; > int ret; > > - dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR; > + dma_tx_sconfig.dst_addr = lpuart_dma_datareg_addr(sport); > 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; > @@ -636,13 +647,19 @@ static void lpuart_start_tx(struct uart_port *port) > static void lpuart32_start_tx(struct uart_port *port) > { > struct lpuart_port *sport = container_of(port, struct lpuart_port, port); > + struct circ_buf *xmit = &sport->port.state->xmit; > unsigned long temp; > > - temp = lpuart32_read(port, UARTCTRL); > - lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL); > + if (sport->lpuart_dma_tx_use) { > + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) > + lpuart_dma_tx(sport); > + } else { > + temp = lpuart32_read(port, UARTCTRL); > + lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL); > > - if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE) > - lpuart32_transmit_buffer(sport); > + if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE) > + lpuart32_transmit_buffer(sport); > + } > } > > /* return TIOCSER_TEMT when transmitter is not busy */ > @@ -872,11 +889,10 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) > rxcount = lpuart32_read(&sport->port, UARTWATER); > rxcount = rxcount >> UARTWATER_RXCNT_OFF; > > - if (sts & UARTSTAT_RDRF || rxcount > 0) > + if ((sts & UARTSTAT_RDRF || rxcount > 0) && !sport->lpuart_dma_rx_use) > lpuart32_rxint(irq, dev_id); > > - if ((sts & UARTSTAT_TDRE) && > - !(lpuart32_read(&sport->port, UARTBAUD) & UARTBAUD_TDMAE)) > + if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use) > lpuart_txint(irq, dev_id); > > lpuart32_write(&sport->port, sts, UARTSTAT); > @@ -891,18 +907,31 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) > struct circ_buf *ring = &sport->rx_ring; > unsigned long flags; > int count = 0; > - unsigned char sr; > > - sr = readb(sport->port.membase + UARTSR1); > + if (lpuart_is_32(sport)) { > + unsigned long sr = lpuart32_read(&sport->port, UARTSTAT); > > - if (sr & (UARTSR1_PE | UARTSR1_FE)) { > - /* Read DR to clear the error flags */ > - readb(sport->port.membase + UARTDR); > + if (sr & (UARTSTAT_PE | UARTSTAT_FE)) { > + /* Read DR to clear the error flags */ > + lpuart32_read(&sport->port, UARTDATA); > + > + if (sr & UARTSTAT_PE) > + sport->port.icount.parity++; > + else if (sr & UARTSTAT_FE) > + sport->port.icount.frame++; > + } > + } else { > + unsigned char 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++; > + if (sr & UARTSR1_PE) > + sport->port.icount.parity++; > + else if (sr & UARTSR1_FE) > + sport->port.icount.frame++; > + } > } > > async_tx_ack(sport->dma_rx_desc); > @@ -1025,7 +1054,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) > return -EINVAL; > } > > - dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR; > + dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport); > 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; > @@ -1053,8 +1082,14 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *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); > + if (lpuart_is_32(sport)) { > + unsigned long temp = lpuart32_read(&sport->port, UARTBAUD); > + > + lpuart32_write(&sport->port, temp | UARTBAUD_RDMAE, UARTBAUD); > + } else { > + writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS, > + sport->port.membase + UARTCR5); > + } > > return 0; > } > @@ -1354,8 +1389,41 @@ static int lpuart32_startup(struct uart_port *port) > lpuart32_setup_watermark(sport); > > temp = lpuart32_read(&sport->port, UARTCTRL); > - temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE); > - temp |= UARTCTRL_ILIE; > + temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE; > + lpuart32_write(&sport->port, temp, UARTCTRL); > + > + 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 = lpuart32_read(&sport->port, UARTBAUD); > + lpuart32_write(&sport->port, temp | UARTBAUD_TDMAE, UARTBAUD); > + } else { > + sport->lpuart_dma_tx_use = false; > + } > + > + if (sport->lpuart_dma_rx_use) { > + /* RXWATER must be 0 */ > + temp = lpuart32_read(&sport->port, UARTWATER); > + temp &= ~(UARTWATER_WATER_MASK << UARTWATER_RXWATER_OFF); > + lpuart32_write(&sport->port, temp, UARTWATER); > + } > + temp = lpuart32_read(&sport->port, UARTCTRL); > + if (!sport->lpuart_dma_rx_use) > + temp |= UARTCTRL_RIE; > + if (!sport->lpuart_dma_tx_use) > + temp |= UARTCTRL_TIE; > lpuart32_write(&sport->port, temp, UARTCTRL); > > spin_unlock_irqrestore(&sport->port.lock, flags); > @@ -1396,6 +1464,8 @@ static void lpuart_shutdown(struct uart_port *port) > > static void lpuart32_shutdown(struct uart_port *port) > { > + struct lpuart_port *sport = > + container_of(port, struct lpuart_port, port); > unsigned long temp; > unsigned long flags; > > @@ -1408,6 +1478,21 @@ static void lpuart32_shutdown(struct uart_port *port) > lpuart32_write(port, temp, UARTCTRL); > > spin_unlock_irqrestore(&port->lock, flags); > + > + if (sport->lpuart_dma_rx_use) { > + del_timer_sync(&sport->lpuart_timer); > + lpuart_dma_rx_free(&sport->port); > + } > + > + if (sport->lpuart_dma_tx_use) { > + if (wait_event_interruptible(sport->dma_wait, > + !sport->dma_tx_in_progress)) { > + sport->dma_tx_in_progress = false; > + dmaengine_terminate_all(sport->dma_tx_chan); > + } > + > + lpuart32_stop_tx(port); > + } > } > > static void > @@ -1633,7 +1718,10 @@ lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate) > tmp &= ~UARTBAUD_SBR_MASK; > tmp |= sbr & UARTBAUD_SBR_MASK; > > - tmp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); > + if (!sport->lpuart_dma_rx_use) > + tmp &= ~UARTBAUD_RDMAE; > + if (!sport->lpuart_dma_tx_use) > + tmp &= ~UARTBAUD_TDMAE; > > lpuart32_write(&sport->port, tmp, UARTBAUD); > } > @@ -1711,6 +1799,18 @@ lpuart32_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; > @@ -1749,6 +1849,13 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, > lpuart32_write(&sport->port, ctrl, UARTCTRL); > /* restore control register */ > > + 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); > } > > @@ -2318,8 +2425,13 @@ static int lpuart_suspend(struct device *dev) > } > > /* Disable Rx DMA to use UART port as wakeup source */ > - writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS, > - sport->port.membase + UARTCR5); > + if (lpuart_is_32(sport)) { > + temp = lpuart32_read(&sport->port, UARTBAUD); > + lpuart32_write(port, temp & ~UARTBAUD_RDMAE, UARTBAUD); Did you test build this code? This line breaks the build :( Please fix this up and be more careful. greg k-h