diff options
author | Codrin Ciubotariu <codrin.ciubotariu@microchip.com> | 2019-01-23 11:33:47 -0500 |
---|---|---|
committer | Vinod Koul <vkoul@kernel.org> | 2019-02-02 05:25:26 -0500 |
commit | dc3f595b6617ebc0307e0ce151e8f2f2b2489b95 (patch) | |
tree | 80b4634f6b1782a5ddab15ce39681c02c0f53ebc | |
parent | bfeffd155283772bbe78c6a05dec7c0128ee500c (diff) |
dmaengine: at_xdmac: Fix wrongfull report of a channel as in use
atchan->status variable is used to store two different information:
- pass channel interrupts status from interrupt handler to tasklet;
- channel information like whether it is cyclic or paused;
This causes a bug when device_terminate_all() is called,
(AT_XDMAC_CHAN_IS_CYCLIC cleared on atchan->status) and then a late End
of Block interrupt arrives (AT_XDMAC_CIS_BIS), which sets bit 0 of
atchan->status. Bit 0 is also used for AT_XDMAC_CHAN_IS_CYCLIC, so when
a new descriptor for a cyclic transfer is created, the driver reports
the channel as in use:
if (test_and_set_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status)) {
dev_err(chan2dev(chan), "channel currently used\n");
return NULL;
}
This patch fixes the bug by adding a different struct member to keep
the interrupts status separated from the channel status bits.
Fixes: e1f7c9eee707 ("dmaengine: at_xdmac: creation of the atmel eXtended DMA Controller driver")
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
Acked-by: Ludovic Desroches <ludovic.desroches@microchip.com>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
-rw-r--r-- | drivers/dma/at_xdmac.c | 19 |
1 files changed, 10 insertions, 9 deletions
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 4e557684f792..fe69dccfa0c0 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c | |||
@@ -203,6 +203,7 @@ struct at_xdmac_chan { | |||
203 | u32 save_cim; | 203 | u32 save_cim; |
204 | u32 save_cnda; | 204 | u32 save_cnda; |
205 | u32 save_cndc; | 205 | u32 save_cndc; |
206 | u32 irq_status; | ||
206 | unsigned long status; | 207 | unsigned long status; |
207 | struct tasklet_struct tasklet; | 208 | struct tasklet_struct tasklet; |
208 | struct dma_slave_config sconfig; | 209 | struct dma_slave_config sconfig; |
@@ -1580,8 +1581,8 @@ static void at_xdmac_tasklet(unsigned long data) | |||
1580 | struct at_xdmac_desc *desc; | 1581 | struct at_xdmac_desc *desc; |
1581 | u32 error_mask; | 1582 | u32 error_mask; |
1582 | 1583 | ||
1583 | dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08lx\n", | 1584 | dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n", |
1584 | __func__, atchan->status); | 1585 | __func__, atchan->irq_status); |
1585 | 1586 | ||
1586 | error_mask = AT_XDMAC_CIS_RBEIS | 1587 | error_mask = AT_XDMAC_CIS_RBEIS |
1587 | | AT_XDMAC_CIS_WBEIS | 1588 | | AT_XDMAC_CIS_WBEIS |
@@ -1589,15 +1590,15 @@ static void at_xdmac_tasklet(unsigned long data) | |||
1589 | 1590 | ||
1590 | if (at_xdmac_chan_is_cyclic(atchan)) { | 1591 | if (at_xdmac_chan_is_cyclic(atchan)) { |
1591 | at_xdmac_handle_cyclic(atchan); | 1592 | at_xdmac_handle_cyclic(atchan); |
1592 | } else if ((atchan->status & AT_XDMAC_CIS_LIS) | 1593 | } else if ((atchan->irq_status & AT_XDMAC_CIS_LIS) |
1593 | || (atchan->status & error_mask)) { | 1594 | || (atchan->irq_status & error_mask)) { |
1594 | struct dma_async_tx_descriptor *txd; | 1595 | struct dma_async_tx_descriptor *txd; |
1595 | 1596 | ||
1596 | if (atchan->status & AT_XDMAC_CIS_RBEIS) | 1597 | if (atchan->irq_status & AT_XDMAC_CIS_RBEIS) |
1597 | dev_err(chan2dev(&atchan->chan), "read bus error!!!"); | 1598 | dev_err(chan2dev(&atchan->chan), "read bus error!!!"); |
1598 | if (atchan->status & AT_XDMAC_CIS_WBEIS) | 1599 | if (atchan->irq_status & AT_XDMAC_CIS_WBEIS) |
1599 | dev_err(chan2dev(&atchan->chan), "write bus error!!!"); | 1600 | dev_err(chan2dev(&atchan->chan), "write bus error!!!"); |
1600 | if (atchan->status & AT_XDMAC_CIS_ROIS) | 1601 | if (atchan->irq_status & AT_XDMAC_CIS_ROIS) |
1601 | dev_err(chan2dev(&atchan->chan), "request overflow error!!!"); | 1602 | dev_err(chan2dev(&atchan->chan), "request overflow error!!!"); |
1602 | 1603 | ||
1603 | spin_lock(&atchan->lock); | 1604 | spin_lock(&atchan->lock); |
@@ -1652,7 +1653,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id) | |||
1652 | atchan = &atxdmac->chan[i]; | 1653 | atchan = &atxdmac->chan[i]; |
1653 | chan_imr = at_xdmac_chan_read(atchan, AT_XDMAC_CIM); | 1654 | chan_imr = at_xdmac_chan_read(atchan, AT_XDMAC_CIM); |
1654 | chan_status = at_xdmac_chan_read(atchan, AT_XDMAC_CIS); | 1655 | chan_status = at_xdmac_chan_read(atchan, AT_XDMAC_CIS); |
1655 | atchan->status = chan_status & chan_imr; | 1656 | atchan->irq_status = chan_status & chan_imr; |
1656 | dev_vdbg(atxdmac->dma.dev, | 1657 | dev_vdbg(atxdmac->dma.dev, |
1657 | "%s: chan%d: imr=0x%x, status=0x%x\n", | 1658 | "%s: chan%d: imr=0x%x, status=0x%x\n", |
1658 | __func__, i, chan_imr, chan_status); | 1659 | __func__, i, chan_imr, chan_status); |
@@ -1666,7 +1667,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id) | |||
1666 | at_xdmac_chan_read(atchan, AT_XDMAC_CDA), | 1667 | at_xdmac_chan_read(atchan, AT_XDMAC_CDA), |
1667 | at_xdmac_chan_read(atchan, AT_XDMAC_CUBC)); | 1668 | at_xdmac_chan_read(atchan, AT_XDMAC_CUBC)); |
1668 | 1669 | ||
1669 | if (atchan->status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS)) | 1670 | if (atchan->irq_status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS)) |
1670 | at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); | 1671 | at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); |
1671 | 1672 | ||
1672 | tasklet_schedule(&atchan->tasklet); | 1673 | tasklet_schedule(&atchan->tasklet); |