2016-04-12 11:01 GMT+09:00 Mark Brown <broonie@xxxxxxxxxx>: > The patch > > spi: omap2-mcspi: Undo broken fix for dma transfer of vmalloced buffer > > has been applied to the spi tree at > > git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git I realized that this undo patch has not been merged to Linus' tree yet. I have a fix for this issue (attached patch). But the change is not small and also v4.6 release is soon. So I think this undo patch should go into 4.6 release.
From 5abc589ab08e239e884fb2e8937d74f81fb0b888 Mon Sep 17 00:00:00 2001 From: Akinobu Mita <akinobu.mita@xxxxxxxxx> Date: Mon, 21 Mar 2016 01:16:06 +0900 Subject: [PATCH] spi: omap2-mcspi: fix dma transfer This fixes the problem introduced by the commit 3525e0aac91c ("spi: omap2-mcspi: fix dma transfer for vmalloced buffer") The actual DMA transfer length by dmaengine can be smaller than SPI transfer length in the specific condition. In that case, the last word needs to be filled after DMA transfer completion. This fixes it by detecting that case and remap the scatterlist with correct DMA transfer length. Signed-off-by: Akinobu Mita <akinobu.mita@xxxxxxxxx> Cc: Mark Brown <broonie@xxxxxxxxxx> --- drivers/spi/spi-omap2-mcspi.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 43a02e3..a7fafd0 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -439,7 +439,41 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, } dma_async_issue_pending(mcspi_dma->dma_tx); omap2_mcspi_set_dma_req(spi, 0, 1); +} + +static int +omap2_mcspi_rx_dma_remap(struct spi_device *spi, struct spi_transfer *xfer, + unsigned int dma_count) +{ + int i; + struct scatterlist *sg; + struct omap2_mcspi *mcspi; + unsigned int nents = 0; + unsigned int count = 0; + int ret; + + mcspi = spi_master_get_devdata(spi->master); + + dma_unmap_sg(mcspi->dev, xfer->rx_sg.sgl, xfer->rx_sg.nents, + DMA_FROM_DEVICE); + + for_each_sg(xfer->rx_sg.sgl, sg, xfer->rx_sg.nents, i) { + nents++; + if (count + sg->length < dma_count) { + count += sg->length; + continue; + } + sg->length = dma_count - count; + break; + } + ret = dma_map_sg(mcspi->dev, xfer->rx_sg.sgl, nents, DMA_FROM_DEVICE); + if (!ret) + return -ENOMEM; + + xfer->rx_sg.nents = ret; + + return 0; } static unsigned @@ -480,6 +514,16 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0) dma_count -= es; + if (xfer->len != dma_count) { + int ret; + + ret = omap2_mcspi_rx_dma_remap(spi, xfer, dma_count); + if (ret) { + dev_err(&spi->dev, "failed to map rx buf\n"); + return 0; + } + } + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, xfer->rx_sg.sgl, xfer->rx_sg.nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -496,6 +540,9 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, omap2_mcspi_set_dma_req(spi, 1, 1); wait_for_completion(&mcspi_dma->dma_rx_completion); + dma_unmap_sg(mcspi->dev, xfer->rx_sg.sgl, xfer->rx_sg.nents, + DMA_FROM_DEVICE); + sg_free_table(&xfer->rx_sg); if (mcspi->fifo_depth > 0) return count; -- 2.7.4