Browse Source

serial: 8250: Validate dmaengine rx chan meets requirements

8250 dma support requires the dmaengine driver support error-free
pause/terminate and better-than-descriptor residue granularity. Query
slave caps to determine if necessary commands/properties are supported;
disable dma if not.

Note this means dmaengine driver must support slave caps reporting
as well.

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Peter Hurley 9 years ago
parent
commit
ec5a11a91e
1 changed files with 21 additions and 6 deletions
  1. 21 6
      drivers/tty/serial/8250/8250_dma.c

+ 21 - 6
drivers/tty/serial/8250/8250_dma.c

@@ -158,6 +158,8 @@ int serial8250_request_dma(struct uart_8250_port *p)
 {
 	struct uart_8250_dma	*dma = p->dma;
 	dma_cap_mask_t		mask;
+	struct dma_slave_caps	caps;
+	int			ret;
 
 	/* Default slave configuration parameters */
 	dma->rxconf.direction		= DMA_DEV_TO_MEM;
@@ -178,6 +180,16 @@ int serial8250_request_dma(struct uart_8250_port *p)
 	if (!dma->rxchan)
 		return -ENODEV;
 
+	/* 8250 rx dma requires dmaengine driver to support pause/terminate */
+	ret = dma_get_slave_caps(dma->rxchan, &caps);
+	if (ret)
+		goto release_rx;
+	if (!caps.cmd_pause || !caps.cmd_terminate ||
+	    caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) {
+		ret = -EINVAL;
+		goto release_rx;
+	}
+
 	dmaengine_slave_config(dma->rxchan, &dma->rxconf);
 
 	/* Get a channel for TX */
@@ -185,8 +197,8 @@ int serial8250_request_dma(struct uart_8250_port *p)
 						       dma->fn, dma->tx_param,
 						       p->port.dev, "tx");
 	if (!dma->txchan) {
-		dma_release_channel(dma->rxchan);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto release_rx;
 	}
 
 	dmaengine_slave_config(dma->txchan, &dma->txconf);
@@ -197,8 +209,10 @@ 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)
+	if (!dma->rx_buf) {
+		ret = -ENOMEM;
 		goto err;
+	}
 
 	/* TX buffer */
 	dma->tx_addr = dma_map_single(dma->txchan->device->dev,
@@ -208,6 +222,7 @@ int serial8250_request_dma(struct uart_8250_port *p)
 	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;
 	}
 
@@ -215,10 +230,10 @@ int serial8250_request_dma(struct uart_8250_port *p)
 
 	return 0;
 err:
-	dma_release_channel(dma->rxchan);
 	dma_release_channel(dma->txchan);
-
-	return -ENOMEM;
+release_rx:
+	dma_release_channel(dma->rxchan);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(serial8250_request_dma);