serial: 8250: support rx dma mode only

Most SOCS have only 8 or 6 channels, but have more than 16
peripherals. If those peripherals work together, some
fails to request dma channel, because there are no enough
channels. And maybe it's unnecessary to use dma for uart
tx. It is necessary for uart rx when hardware auto flow
control is not used.

&uart0 {
	dma-names = "!tx", "rx";  // disable uart tx with dma
	status = "okay";
};

Change-Id: Ia74477514ba57300a4d19a5c2565ae7b5b8ab521
Signed-off-by: Huibin Hong <huibin.hong@rock-chips.com>
This commit is contained in:
Huibin Hong 2019-01-24 19:40:45 +08:00 committed by Tao Huang
parent e019b9df43
commit 68ae37643a
2 changed files with 70 additions and 45 deletions

View File

@ -295,35 +295,15 @@ int serial8250_request_dma(struct uart_8250_port *p)
/* 8250 rx dma requires dmaengine driver to support pause/terminate */
ret = dma_get_slave_caps(dma->rxchan, &caps);
if (ret)
goto release_rx;
goto err_rx;
if (!caps.cmd_pause || !caps.cmd_terminate ||
caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) {
ret = -EINVAL;
goto release_rx;
goto err_rx;
}
dmaengine_slave_config(dma->rxchan, &dma->rxconf);
/* Get a channel for TX */
dma->txchan = dma_request_slave_channel_compat(mask,
dma->fn, dma->tx_param,
p->port.dev, "tx");
if (!dma->txchan) {
ret = -ENODEV;
goto release_rx;
}
/* 8250 tx dma requires dmaengine driver to support terminate */
ret = dma_get_slave_caps(dma->txchan, &caps);
if (ret)
goto err;
if (!caps.cmd_terminate) {
ret = -EINVAL;
goto err;
}
dmaengine_slave_config(dma->txchan, &dma->txconf);
/* RX buffer */
#ifdef CONFIG_ARCH_ROCKCHIP
if (!dma->rx_size)
@ -335,32 +315,44 @@ int serial8250_request_dma(struct uart_8250_port *p)
dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size,
&dma->rx_addr, GFP_KERNEL);
if (!dma->rx_buf) {
ret = -ENOMEM;
goto err;
goto err_rx;
}
/* TX buffer */
dma->tx_addr = dma_map_single(dma->txchan->device->dev,
p->port.state->xmit.buf,
UART_XMIT_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
dma_free_coherent(dma->rxchan->device->dev, dma->rx_size,
dma->rx_buf, dma->rx_addr);
ret = -ENOMEM;
goto err;
/* Get a channel for TX */
dma->txchan = dma_request_slave_channel_compat(mask,
dma->fn, dma->tx_param,
p->port.dev, "tx");
if (dma->txchan) {
dmaengine_slave_config(dma->txchan, &dma->txconf);
/* TX buffer */
dma->tx_addr = dma_map_single(dma->txchan->device->dev,
p->port.state->xmit.buf,
UART_XMIT_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
dma_free_coherent(dma->rxchan->device->dev,
dma->rx_size, dma->rx_buf,
dma->rx_addr);
dma_release_channel(dma->txchan);
dma->txchan = NULL;
}
dev_info_ratelimited(p->port.dev, "got rx and tx dma channels\n");
} else {
dev_info_ratelimited(p->port.dev, "got rx dma channels only\n");
}
dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
#ifdef CONFIG_ARCH_ROCKCHIP
/* start dma for rx*/
serial8250_start_rx_dma(p);
#endif
return 0;
err:
dma_release_channel(dma->txchan);
release_rx:
err_rx:
dma_release_channel(dma->rxchan);
return ret;
}
@ -383,13 +375,14 @@ void serial8250_release_dma(struct uart_8250_port *p)
dma->rx_running = 0;
#endif
/* Release TX resources */
dmaengine_terminate_sync(dma->txchan);
dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
dma_release_channel(dma->txchan);
dma->txchan = NULL;
dma->tx_running = 0;
if (dma->txchan) {
dmaengine_terminate_all(dma->txchan);
dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
dma_release_channel(dma->txchan);
dma->txchan = NULL;
dma->tx_running = 0;
}
dev_dbg_ratelimited(p->port.dev, "dma channels released\n");
}
EXPORT_SYMBOL_GPL(serial8250_release_dma);

View File

@ -1552,8 +1552,13 @@ static inline void __start_tx(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
#ifdef CONFIG_ARCH_ROCKCHIP
if (up->dma && up->dma->txchan && !up->dma->tx_dma(up))
return;
#else
if (up->dma && !up->dma->tx_dma(up))
return;
#endif
if (!(up->ier & UART_IER_THRI)) {
up->ier |= UART_IER_THRI;
@ -1843,6 +1848,12 @@ EXPORT_SYMBOL_GPL(serial8250_modem_status);
static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
{
#ifdef CONFIG_ARCH_ROCKCHIP
if ((iir & 0xf) != UART_IIR_RX_TIMEOUT)
return 0;
else
return up->dma->rx_dma(up);
#else
switch (iir & 0x3f) {
case UART_IIR_RX_TIMEOUT:
serial8250_rx_dma_flush(up);
@ -1851,6 +1862,7 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
return true;
}
return up->dma->rx_dma(up);
#endif
}
/*
@ -1861,6 +1873,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
unsigned char status;
unsigned long flags;
struct uart_8250_port *up = up_to_u8250p(port);
int dma_err = -1;
if (iir & UART_IIR_NO_INT)
return 0;
@ -1869,14 +1882,33 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
status = serial_port_in(port, UART_LSR);
#ifdef CONFIG_ARCH_ROCKCHIP
if (status & (UART_LSR_DR | UART_LSR_BI)) {
if (up->dma && up->dma->rxchan)
dma_err = handle_rx_dma(up, iir);
if (!up->dma || dma_err)
status = serial8250_rx_chars(up, status);
}
#else
if (status & (UART_LSR_DR | UART_LSR_BI) &&
iir & UART_IIR_RDI) {
if (!up->dma || handle_rx_dma(up, iir))
status = serial8250_rx_chars(up, status);
}
#endif
serial8250_modem_status(up);
if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE))
#ifdef CONFIG_ARCH_ROCKCHIP
if ((!up->dma || (up->dma && (!up->dma->txchan || up->dma->tx_err))) &&
(status & UART_LSR_THRE))
serial8250_tx_chars(up);
#else
if ((!up->dma || (up->dma && up->dma->tx_err)) &&
(status & UART_LSR_THRE))
serial8250_tx_chars(up);
#endif
#ifdef CONFIG_ARCH_ROCKCHIP
if (status & UART_LSR_BRK_ERROR_BITS) {