diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-09 08:51:45 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-09 08:51:45 -0700 |
commit | 055128ee008b00fba14e3638e7e84fc2cff8d77d (patch) | |
tree | 2206549b3597594c4bb1980e3996a361eaeac144 /drivers/dma/stm32-dma.c | |
parent | ddab5337b23c99777d7cfb39c0f8efe536c17dff (diff) | |
parent | f33e7bb3eb922618612a90f0a828c790e8880773 (diff) |
Merge tag 'dmaengine-5.2-rc1' of git://git.infradead.org/users/vkoul/slave-dma
Pull dmaengine updates from Vinod Koul:
- Updates to stm32 dma residue calculations
- Interleave dma capability to axi-dmac and support for ZynqMP arch
- Rework of channel assignment for rcar dma
- Debugfs for pl330 driver
- Support for Tegra186/Tegra194, refactoring for new chips and support
for pause/resume
- Updates to axi-dmac, bcm2835, fsl-edma, idma64, imx-sdma, rcar-dmac,
stm32-dma etc
- dev_get_drvdata() updates on few drivers
* tag 'dmaengine-5.2-rc1' of git://git.infradead.org/users/vkoul/slave-dma: (34 commits)
dmaengine: tegra210-adma: restore channel status
dmaengine: tegra210-dma: free dma controller in remove()
dmaengine: tegra210-adma: add pause/resume support
dmaengine: tegra210-adma: add support for Tegra186/Tegra194
Documentation: DT: Add compatibility binding for Tegra186
dmaengine: tegra210-adma: prepare for supporting newer Tegra chips
dmaengine: at_xdmac: remove a stray bottom half unlock
dmaengine: fsl-edma: Adjust indentation
dmaengine: fsl-edma: Fix typo in Vybrid name
dmaengine: stm32-dma: fix residue calculation in stm32-dma
dmaengine: nbpfaxi: Use dev_get_drvdata()
dmaengine: bcm-sba-raid: Use dev_get_drvdata()
dmaengine: stm32-dma: Fix unsigned variable compared with zero
dmaengine: stm32-dma: use platform_get_irq()
dmaengine: rcar-dmac: Update copyright information
dmaengine: imx-sdma: Only check ratio on parts that support 1:1
dmaengine: xgene-dma: fix spelling mistake "descripto" -> "descriptor"
dmaengine: idma64: Move driver name to the header
dmaengine: bcm2835: Drop duplicate capability setting.
dmaengine: pl330: _stop: clear interrupt status
...
Diffstat (limited to 'drivers/dma/stm32-dma.c')
-rw-r--r-- | drivers/dma/stm32-dma.c | 103 |
1 files changed, 85 insertions, 18 deletions
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index ba239b529fa9..88d9c6c4389f 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -1042,33 +1042,97 @@ static u32 stm32_dma_get_remaining_bytes(struct stm32_dma_chan *chan) return ndtr << width; } +/** + * stm32_dma_is_current_sg - check that expected sg_req is currently transferred + * @chan: dma channel + * + * This function called when IRQ are disable, checks that the hardware has not + * switched on the next transfer in double buffer mode. The test is done by + * comparing the next_sg memory address with the hardware related register + * (based on CT bit value). + * + * Returns true if expected current transfer is still running or double + * buffer mode is not activated. + */ +static bool stm32_dma_is_current_sg(struct stm32_dma_chan *chan) +{ + struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); + struct stm32_dma_sg_req *sg_req; + u32 dma_scr, dma_smar, id; + + id = chan->id; + dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id)); + + if (!(dma_scr & STM32_DMA_SCR_DBM)) + return true; + + sg_req = &chan->desc->sg_req[chan->next_sg]; + + if (dma_scr & STM32_DMA_SCR_CT) { + dma_smar = stm32_dma_read(dmadev, STM32_DMA_SM0AR(id)); + return (dma_smar == sg_req->chan_reg.dma_sm0ar); + } + + dma_smar = stm32_dma_read(dmadev, STM32_DMA_SM1AR(id)); + + return (dma_smar == sg_req->chan_reg.dma_sm1ar); +} + static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, struct stm32_dma_desc *desc, u32 next_sg) { u32 modulo, burst_size; - u32 residue = 0; + u32 residue; + u32 n_sg = next_sg; + struct stm32_dma_sg_req *sg_req = &chan->desc->sg_req[chan->next_sg]; int i; /* - * In cyclic mode, for the last period, residue = remaining bytes from - * NDTR + * Calculate the residue means compute the descriptors + * information: + * - the sg_req currently transferred + * - the Hardware remaining position in this sg (NDTR bits field). + * + * A race condition may occur if DMA is running in cyclic or double + * buffer mode, since the DMA register are automatically reloaded at end + * of period transfer. The hardware may have switched to the next + * transfer (CT bit updated) just before the position (SxNDTR reg) is + * read. + * In this case the SxNDTR reg could (or not) correspond to the new + * transfer position, and not the expected one. + * The strategy implemented in the stm32 driver is to: + * - read the SxNDTR register + * - crosscheck that hardware is still in current transfer. + * In case of switch, we can assume that the DMA is at the beginning of + * the next transfer. So we approximate the residue in consequence, by + * pointing on the beginning of next transfer. + * + * This race condition doesn't apply for none cyclic mode, as double + * buffer is not used. In such situation registers are updated by the + * software. */ - if (chan->desc->cyclic && next_sg == 0) { - residue = stm32_dma_get_remaining_bytes(chan); - goto end; + + residue = stm32_dma_get_remaining_bytes(chan); + + if (!stm32_dma_is_current_sg(chan)) { + n_sg++; + if (n_sg == chan->desc->num_sgs) + n_sg = 0; + residue = sg_req->len; } /* - * For all other periods in cyclic mode, and in sg mode, - * residue = remaining bytes from NDTR + remaining periods/sg to be - * transferred + * In cyclic mode, for the last period, residue = remaining bytes + * from NDTR, + * else for all other periods in cyclic mode, and in sg mode, + * residue = remaining bytes from NDTR + remaining + * periods/sg to be transferred */ - for (i = next_sg; i < desc->num_sgs; i++) - residue += desc->sg_req[i].len; - residue += stm32_dma_get_remaining_bytes(chan); + if (!chan->desc->cyclic || n_sg != 0) + for (i = n_sg; i < desc->num_sgs; i++) + residue += desc->sg_req[i].len; -end: if (!chan->mem_burst) return residue; @@ -1302,13 +1366,16 @@ static int stm32_dma_probe(struct platform_device *pdev) for (i = 0; i < STM32_DMA_MAX_CHANNELS; i++) { chan = &dmadev->chan[i]; - res = platform_get_resource(pdev, IORESOURCE_IRQ, i); - if (!res) { - ret = -EINVAL; - dev_err(&pdev->dev, "No irq resource for chan %d\n", i); + chan->irq = platform_get_irq(pdev, i); + ret = platform_get_irq(pdev, i); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "No irq resource for chan %d\n", i); goto err_unregister; } - chan->irq = res->start; + chan->irq = ret; + ret = devm_request_irq(&pdev->dev, chan->irq, stm32_dma_chan_irq, 0, dev_name(chan2dev(chan)), chan); |