diff options
Diffstat (limited to 'drivers/spi/spi-rspi.c')
| -rw-r--r-- | drivers/spi/spi-rspi.c | 125 | 
1 files changed, 80 insertions, 45 deletions
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 10112745bb17..ad87a98f8f68 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -472,25 +472,52 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,  	dma_cookie_t cookie;  	int ret; -	if (tx) { -		desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx, -					tx->sgl, tx->nents, DMA_TO_DEVICE, -					DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -		if (!desc_tx) -			return -EIO; - -		irq_mask |= SPCR_SPTIE; -	} +	/* First prepare and submit the DMA request(s), as this may fail */  	if (rx) {  		desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx,  					rx->sgl, rx->nents, DMA_FROM_DEVICE,  					DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -		if (!desc_rx) -			return -EIO; +		if (!desc_rx) { +			ret = -EAGAIN; +			goto no_dma_rx; +		} + +		desc_rx->callback = rspi_dma_complete; +		desc_rx->callback_param = rspi; +		cookie = dmaengine_submit(desc_rx); +		if (dma_submit_error(cookie)) { +			ret = cookie; +			goto no_dma_rx; +		}  		irq_mask |= SPCR_SPRIE;  	} +	if (tx) { +		desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx, +					tx->sgl, tx->nents, DMA_TO_DEVICE, +					DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +		if (!desc_tx) { +			ret = -EAGAIN; +			goto no_dma_tx; +		} + +		if (rx) { +			/* No callback */ +			desc_tx->callback = NULL; +		} else { +			desc_tx->callback = rspi_dma_complete; +			desc_tx->callback_param = rspi; +		} +		cookie = dmaengine_submit(desc_tx); +		if (dma_submit_error(cookie)) { +			ret = cookie; +			goto no_dma_tx; +		} + +		irq_mask |= SPCR_SPTIE; +	} +  	/*  	 * DMAC needs SPxIE, but if SPxIE is set, the IRQ routine will be  	 * called. So, this driver disables the IRQ while DMA transfer. @@ -503,34 +530,24 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,  	rspi_enable_irq(rspi, irq_mask);  	rspi->dma_callbacked = 0; -	if (rx) { -		desc_rx->callback = rspi_dma_complete; -		desc_rx->callback_param = rspi; -		cookie = dmaengine_submit(desc_rx); -		if (dma_submit_error(cookie)) -			return cookie; +	/* Now start DMA */ +	if (rx)  		dma_async_issue_pending(rspi->master->dma_rx); -	} -	if (tx) { -		if (rx) { -			/* No callback */ -			desc_tx->callback = NULL; -		} else { -			desc_tx->callback = rspi_dma_complete; -			desc_tx->callback_param = rspi; -		} -		cookie = dmaengine_submit(desc_tx); -		if (dma_submit_error(cookie)) -			return cookie; +	if (tx)  		dma_async_issue_pending(rspi->master->dma_tx); -	}  	ret = wait_event_interruptible_timeout(rspi->wait,  					       rspi->dma_callbacked, HZ);  	if (ret > 0 && rspi->dma_callbacked)  		ret = 0; -	else if (!ret) +	else if (!ret) { +		dev_err(&rspi->master->dev, "DMA timeout\n");  		ret = -ETIMEDOUT; +		if (tx) +			dmaengine_terminate_all(rspi->master->dma_tx); +		if (rx) +			dmaengine_terminate_all(rspi->master->dma_rx); +	}  	rspi_disable_irq(rspi, irq_mask); @@ -540,6 +557,17 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,  		enable_irq(rspi->rx_irq);  	return ret; + +no_dma_tx: +	if (rx) +		dmaengine_terminate_all(rspi->master->dma_rx); +no_dma_rx: +	if (ret == -EAGAIN) { +		pr_warn_once("%s %s: DMA not available, falling back to PIO\n", +			     dev_driver_string(&rspi->master->dev), +			     dev_name(&rspi->master->dev)); +	} +	return ret;  }  static void rspi_receive_init(const struct rspi_data *rspi) @@ -593,8 +621,10 @@ static int rspi_common_transfer(struct rspi_data *rspi,  	if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {  		/* rx_buf can be NULL on RSPI on SH in TX-only Mode */ -		return rspi_dma_transfer(rspi, &xfer->tx_sg, -					 xfer->rx_buf ? &xfer->rx_sg : NULL); +		ret = rspi_dma_transfer(rspi, &xfer->tx_sg, +					xfer->rx_buf ? &xfer->rx_sg : NULL); +		if (ret != -EAGAIN) +			return ret;  	}  	ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); @@ -630,7 +660,6 @@ static int rspi_rz_transfer_one(struct spi_master *master,  				struct spi_transfer *xfer)  {  	struct rspi_data *rspi = spi_master_get_devdata(master); -	int ret;  	rspi_rz_receive_init(rspi); @@ -649,8 +678,11 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)  {  	int ret; -	if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) -		return rspi_dma_transfer(rspi, &xfer->tx_sg, NULL); +	if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { +		ret = rspi_dma_transfer(rspi, &xfer->tx_sg, NULL); +		if (ret != -EAGAIN) +			return ret; +	}  	ret = rspi_pio_transfer(rspi, xfer->tx_buf, NULL, xfer->len);  	if (ret < 0) @@ -664,8 +696,11 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)  static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer)  { -	if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) -		return rspi_dma_transfer(rspi, NULL, &xfer->rx_sg); +	if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { +		int ret = rspi_dma_transfer(rspi, NULL, &xfer->rx_sg); +		if (ret != -EAGAIN) +			return ret; +	}  	return rspi_pio_transfer(rspi, NULL, xfer->rx_buf, xfer->len);  } @@ -927,19 +962,19 @@ static int rspi_request_dma(struct device *dev, struct spi_master *master,  	return 0;  } -static void rspi_release_dma(struct rspi_data *rspi) +static void rspi_release_dma(struct spi_master *master)  { -	if (rspi->master->dma_tx) -		dma_release_channel(rspi->master->dma_tx); -	if (rspi->master->dma_rx) -		dma_release_channel(rspi->master->dma_rx); +	if (master->dma_tx) +		dma_release_channel(master->dma_tx); +	if (master->dma_rx) +		dma_release_channel(master->dma_rx);  }  static int rspi_remove(struct platform_device *pdev)  {  	struct rspi_data *rspi = platform_get_drvdata(pdev); -	rspi_release_dma(rspi); +	rspi_release_dma(rspi->master);  	pm_runtime_disable(&pdev->dev);  	return 0; @@ -1141,7 +1176,7 @@ static int rspi_probe(struct platform_device *pdev)  	return 0;  error3: -	rspi_release_dma(rspi); +	rspi_release_dma(master);  error2:  	pm_runtime_disable(&pdev->dev);  error1:  |