summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2018-05-17 13:37:14 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-05-24 18:37:56 +0200
commitd8db840dcb9e6d9d7f3d2527a3f96c6bb07414f7 (patch)
tree12f59aecff0e041d5fffc26507b12892477c170a /drivers/tty
parenttty: Fix data race in tty_insert_flip_string_fixed_flag (diff)
downloadlinux-d8db840dcb9e6d9d7f3d2527a3f96c6bb07414f7.tar.xz
linux-d8db840dcb9e6d9d7f3d2527a3f96c6bb07414f7.zip
serial: samsung: check DMA engine capabilities before using DMA mode
DMA engine driver might not always provide all the features needed by serial driver to properly operate in DMA mode, so check that before selecting DMA mode. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/samsung.c33
1 files changed, 29 insertions, 4 deletions
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 64e96926f1ad..2f8fa184aafa 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -856,6 +856,8 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
{
struct s3c24xx_uart_dma *dma = p->dma;
+ struct dma_slave_caps dma_caps;
+ const char *reason = NULL;
int ret;
/* Default slave configuration parameters */
@@ -871,17 +873,37 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
dma->rx_chan = dma_request_chan(p->port.dev, "rx");
- if (IS_ERR(dma->rx_chan))
- return PTR_ERR(dma->rx_chan);
+ if (IS_ERR(dma->rx_chan)) {
+ reason = "DMA RX channel request failed";
+ ret = PTR_ERR(dma->rx_chan);
+ goto err_warn;
+ }
+
+ ret = dma_get_slave_caps(dma->rx_chan, &dma_caps);
+ if (ret < 0 ||
+ dma_caps.residue_granularity < DMA_RESIDUE_GRANULARITY_BURST) {
+ reason = "insufficient DMA RX engine capabilities";
+ ret = -EOPNOTSUPP;
+ goto err_release_rx;
+ }
dmaengine_slave_config(dma->rx_chan, &dma->rx_conf);
dma->tx_chan = dma_request_chan(p->port.dev, "tx");
if (IS_ERR(dma->tx_chan)) {
+ reason = "DMA TX channel request failed";
ret = PTR_ERR(dma->tx_chan);
goto err_release_rx;
}
+ ret = dma_get_slave_caps(dma->tx_chan, &dma_caps);
+ if (ret < 0 ||
+ dma_caps.residue_granularity < DMA_RESIDUE_GRANULARITY_BURST) {
+ reason = "insufficient DMA TX engine capabilities";
+ ret = -EOPNOTSUPP;
+ goto err_release_tx;
+ }
+
dmaengine_slave_config(dma->tx_chan, &dma->tx_conf);
/* RX buffer */
@@ -896,6 +918,7 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
dma->rx_addr = dma_map_single(p->port.dev, dma->rx_buf,
dma->rx_size, DMA_FROM_DEVICE);
if (dma_mapping_error(p->port.dev, dma->rx_addr)) {
+ reason = "DMA mapping error for RX buffer";
ret = -EIO;
goto err_free_rx;
}
@@ -904,6 +927,7 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf,
UART_XMIT_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(p->port.dev, dma->tx_addr)) {
+ reason = "DMA mapping error for TX buffer";
ret = -EIO;
goto err_unmap_rx;
}
@@ -919,6 +943,9 @@ err_release_tx:
dma_release_channel(dma->tx_chan);
err_release_rx:
dma_release_channel(dma->rx_chan);
+err_warn:
+ if (reason)
+ dev_warn(p->port.dev, "%s, DMA will not be used\n", reason);
return ret;
}
@@ -1037,8 +1064,6 @@ static int s3c64xx_serial_startup(struct uart_port *port)
if (ourport->dma) {
ret = s3c24xx_serial_request_dma(ourport);
if (ret < 0) {
- dev_warn(port->dev,
- "DMA request failed, DMA will not be used\n");
devm_kfree(port->dev, ourport->dma);
ourport->dma = NULL;
}