Commit 42b68768 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Greg Kroah-Hartman

serial: fsl_lpuart: DMA support for 32-bit variant

Add DMA support for 32-bit variant of the LPUART, such as LS1021A.
Signed-off-by: default avatarTomonori Sakita <tomonori.sakita@sord.co.jp>
Signed-off-by: default avatarAtsushi Nemoto <atsushi.nemoto@sord.co.jp>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7c0cca7c
...@@ -426,6 +426,17 @@ static void lpuart_dma_tx_complete(void *arg) ...@@ -426,6 +426,17 @@ static void lpuart_dma_tx_complete(void *arg)
spin_unlock_irqrestore(&sport->port.lock, flags); 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) static int lpuart_dma_tx_request(struct uart_port *port)
{ {
struct lpuart_port *sport = container_of(port, struct lpuart_port *sport = container_of(port,
...@@ -433,7 +444,7 @@ static int lpuart_dma_tx_request(struct uart_port *port) ...@@ -433,7 +444,7 @@ static int lpuart_dma_tx_request(struct uart_port *port)
struct dma_slave_config dma_tx_sconfig = {}; struct dma_slave_config dma_tx_sconfig = {};
int ret; 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_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_tx_sconfig.dst_maxburst = 1; dma_tx_sconfig.dst_maxburst = 1;
dma_tx_sconfig.direction = DMA_MEM_TO_DEV; dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
...@@ -636,13 +647,19 @@ static void lpuart_start_tx(struct uart_port *port) ...@@ -636,13 +647,19 @@ static void lpuart_start_tx(struct uart_port *port)
static void lpuart32_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 lpuart_port *sport = container_of(port, struct lpuart_port, port);
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long temp; unsigned long temp;
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); temp = lpuart32_read(port, UARTCTRL);
lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL); lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL);
if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE) if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)
lpuart32_transmit_buffer(sport); lpuart32_transmit_buffer(sport);
}
} }
/* return TIOCSER_TEMT when transmitter is not busy */ /* return TIOCSER_TEMT when transmitter is not busy */
...@@ -872,11 +889,10 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) ...@@ -872,11 +889,10 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
rxcount = lpuart32_read(&sport->port, UARTWATER); rxcount = lpuart32_read(&sport->port, UARTWATER);
rxcount = rxcount >> UARTWATER_RXCNT_OFF; 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); lpuart32_rxint(irq, dev_id);
if ((sts & UARTSTAT_TDRE) && if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use)
!(lpuart32_read(&sport->port, UARTBAUD) & UARTBAUD_TDMAE))
lpuart_txint(irq, dev_id); lpuart_txint(irq, dev_id);
lpuart32_write(&sport->port, sts, UARTSTAT); lpuart32_write(&sport->port, sts, UARTSTAT);
...@@ -891,9 +907,21 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) ...@@ -891,9 +907,21 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
struct circ_buf *ring = &sport->rx_ring; struct circ_buf *ring = &sport->rx_ring;
unsigned long flags; unsigned long flags;
int count = 0; 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 & (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)) { if (sr & (UARTSR1_PE | UARTSR1_FE)) {
/* Read DR to clear the error flags */ /* Read DR to clear the error flags */
...@@ -904,6 +932,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) ...@@ -904,6 +932,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
else if (sr & UARTSR1_FE) else if (sr & UARTSR1_FE)
sport->port.icount.frame++; sport->port.icount.frame++;
} }
}
async_tx_ack(sport->dma_rx_desc); async_tx_ack(sport->dma_rx_desc);
...@@ -1025,7 +1054,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) ...@@ -1025,7 +1054,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
return -EINVAL; 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_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_rx_sconfig.src_maxburst = 1; dma_rx_sconfig.src_maxburst = 1;
dma_rx_sconfig.direction = DMA_DEV_TO_MEM; dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
...@@ -1053,8 +1082,14 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) ...@@ -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); sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
dma_async_issue_pending(sport->dma_rx_chan); dma_async_issue_pending(sport->dma_rx_chan);
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, writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS,
sport->port.membase + UARTCR5); sport->port.membase + UARTCR5);
}
return 0; return 0;
} }
...@@ -1354,8 +1389,41 @@ static int lpuart32_startup(struct uart_port *port) ...@@ -1354,8 +1389,41 @@ static int lpuart32_startup(struct uart_port *port)
lpuart32_setup_watermark(sport); lpuart32_setup_watermark(sport);
temp = lpuart32_read(&sport->port, UARTCTRL); temp = lpuart32_read(&sport->port, UARTCTRL);
temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE); temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE;
temp |= 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); lpuart32_write(&sport->port, temp, UARTCTRL);
spin_unlock_irqrestore(&sport->port.lock, flags); spin_unlock_irqrestore(&sport->port.lock, flags);
...@@ -1396,6 +1464,8 @@ static void lpuart_shutdown(struct uart_port *port) ...@@ -1396,6 +1464,8 @@ static void lpuart_shutdown(struct uart_port *port)
static void lpuart32_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 temp;
unsigned long flags; unsigned long flags;
...@@ -1408,6 +1478,21 @@ static void lpuart32_shutdown(struct uart_port *port) ...@@ -1408,6 +1478,21 @@ static void lpuart32_shutdown(struct uart_port *port)
lpuart32_write(port, temp, UARTCTRL); lpuart32_write(port, temp, UARTCTRL);
spin_unlock_irqrestore(&port->lock, flags); 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 static void
...@@ -1633,7 +1718,10 @@ lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate) ...@@ -1633,7 +1718,10 @@ lpuart32_serial_setbrg(struct lpuart_port *sport, unsigned int baudrate)
tmp &= ~UARTBAUD_SBR_MASK; tmp &= ~UARTBAUD_SBR_MASK;
tmp |= sbr & 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); lpuart32_write(&sport->port, tmp, UARTBAUD);
} }
...@@ -1711,6 +1799,18 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -1711,6 +1799,18 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
/* ask the core to calculate the divisor */ /* ask the core to calculate the divisor */
baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 4); baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 4);
/*
* 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); spin_lock_irqsave(&sport->port.lock, flags);
sport->port.read_status_mask = 0; sport->port.read_status_mask = 0;
...@@ -1749,6 +1849,13 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -1749,6 +1849,13 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
lpuart32_write(&sport->port, ctrl, UARTCTRL); lpuart32_write(&sport->port, ctrl, UARTCTRL);
/* restore control register */ /* 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); spin_unlock_irqrestore(&sport->port.lock, flags);
} }
...@@ -2318,8 +2425,14 @@ static int lpuart_suspend(struct device *dev) ...@@ -2318,8 +2425,14 @@ static int lpuart_suspend(struct device *dev)
} }
/* Disable Rx DMA to use UART port as wakeup source */ /* Disable Rx DMA to use UART port as wakeup source */
writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS, if (lpuart_is_32(sport)) {
sport->port.membase + UARTCR5); 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);
}
} }
if (sport->lpuart_dma_tx_use) { if (sport->lpuart_dma_tx_use) {
...@@ -2345,8 +2458,7 @@ static int lpuart_resume(struct device *dev) ...@@ -2345,8 +2458,7 @@ static int lpuart_resume(struct device *dev)
if (lpuart_is_32(sport)) { if (lpuart_is_32(sport)) {
lpuart32_setup_watermark(sport); lpuart32_setup_watermark(sport);
temp = lpuart32_read(&sport->port, UARTCTRL); temp = lpuart32_read(&sport->port, UARTCTRL);
temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE;
UARTCTRL_TE | UARTCTRL_ILIE);
lpuart32_write(&sport->port, temp, UARTCTRL); lpuart32_write(&sport->port, temp, UARTCTRL);
} else { } else {
lpuart_setup_watermark(sport); lpuart_setup_watermark(sport);
...@@ -2367,12 +2479,34 @@ static int lpuart_resume(struct device *dev) ...@@ -2367,12 +2479,34 @@ static int lpuart_resume(struct device *dev)
if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) { if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
init_waitqueue_head(&sport->dma_wait); init_waitqueue_head(&sport->dma_wait);
sport->lpuart_dma_tx_use = true; sport->lpuart_dma_tx_use = true;
if (lpuart_is_32(sport)) {
temp = lpuart32_read(&sport->port, UARTBAUD);
lpuart32_write(&sport->port,
temp | UARTBAUD_TDMAE, UARTBAUD);
} else {
writeb(readb(sport->port.membase + UARTCR5) | writeb(readb(sport->port.membase + UARTCR5) |
UARTCR5_TDMAS, sport->port.membase + UARTCR5); UARTCR5_TDMAS, sport->port.membase + UARTCR5);
}
} else { } else {
sport->lpuart_dma_tx_use = false; sport->lpuart_dma_tx_use = false;
} }
if (lpuart_is_32(sport)) {
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);
}
uart_resume_port(&lpuart_reg, &sport->port); uart_resume_port(&lpuart_reg, &sport->port);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment