]>
Commit | Line | Data |
---|---|---|
1981af9d SL |
1 | From 6ee0b2d89a3e17e4f545ca6515f62b5f81870090 Mon Sep 17 00:00:00 2001 |
2 | From: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> | |
3 | Date: Wed, 23 Jan 2019 16:33:47 +0000 | |
4 | Subject: dmaengine: at_xdmac: Fix wrongfull report of a channel as in use | |
5 | ||
6 | [ Upstream commit dc3f595b6617ebc0307e0ce151e8f2f2b2489b95 ] | |
7 | ||
8 | atchan->status variable is used to store two different information: | |
9 | - pass channel interrupts status from interrupt handler to tasklet; | |
10 | - channel information like whether it is cyclic or paused; | |
11 | ||
12 | This causes a bug when device_terminate_all() is called, | |
13 | (AT_XDMAC_CHAN_IS_CYCLIC cleared on atchan->status) and then a late End | |
14 | of Block interrupt arrives (AT_XDMAC_CIS_BIS), which sets bit 0 of | |
15 | atchan->status. Bit 0 is also used for AT_XDMAC_CHAN_IS_CYCLIC, so when | |
16 | a new descriptor for a cyclic transfer is created, the driver reports | |
17 | the channel as in use: | |
18 | ||
19 | if (test_and_set_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status)) { | |
20 | dev_err(chan2dev(chan), "channel currently used\n"); | |
21 | return NULL; | |
22 | } | |
23 | ||
24 | This patch fixes the bug by adding a different struct member to keep | |
25 | the interrupts status separated from the channel status bits. | |
26 | ||
27 | Fixes: e1f7c9eee707 ("dmaengine: at_xdmac: creation of the atmel eXtended DMA Controller driver") | |
28 | Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> | |
29 | Acked-by: Ludovic Desroches <ludovic.desroches@microchip.com> | |
30 | Signed-off-by: Vinod Koul <vkoul@kernel.org> | |
31 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
32 | --- | |
33 | drivers/dma/at_xdmac.c | 19 ++++++++++--------- | |
34 | 1 file changed, 10 insertions(+), 9 deletions(-) | |
35 | ||
36 | diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c | |
37 | index 82a7c89caae2..af24c5bf32d6 100644 | |
38 | --- a/drivers/dma/at_xdmac.c | |
39 | +++ b/drivers/dma/at_xdmac.c | |
40 | @@ -203,6 +203,7 @@ struct at_xdmac_chan { | |
41 | u32 save_cim; | |
42 | u32 save_cnda; | |
43 | u32 save_cndc; | |
44 | + u32 irq_status; | |
45 | unsigned long status; | |
46 | struct tasklet_struct tasklet; | |
47 | struct dma_slave_config sconfig; | |
48 | @@ -1582,8 +1583,8 @@ static void at_xdmac_tasklet(unsigned long data) | |
49 | struct at_xdmac_desc *desc; | |
50 | u32 error_mask; | |
51 | ||
52 | - dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08lx\n", | |
53 | - __func__, atchan->status); | |
54 | + dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n", | |
55 | + __func__, atchan->irq_status); | |
56 | ||
57 | error_mask = AT_XDMAC_CIS_RBEIS | |
58 | | AT_XDMAC_CIS_WBEIS | |
59 | @@ -1591,15 +1592,15 @@ static void at_xdmac_tasklet(unsigned long data) | |
60 | ||
61 | if (at_xdmac_chan_is_cyclic(atchan)) { | |
62 | at_xdmac_handle_cyclic(atchan); | |
63 | - } else if ((atchan->status & AT_XDMAC_CIS_LIS) | |
64 | - || (atchan->status & error_mask)) { | |
65 | + } else if ((atchan->irq_status & AT_XDMAC_CIS_LIS) | |
66 | + || (atchan->irq_status & error_mask)) { | |
67 | struct dma_async_tx_descriptor *txd; | |
68 | ||
69 | - if (atchan->status & AT_XDMAC_CIS_RBEIS) | |
70 | + if (atchan->irq_status & AT_XDMAC_CIS_RBEIS) | |
71 | dev_err(chan2dev(&atchan->chan), "read bus error!!!"); | |
72 | - if (atchan->status & AT_XDMAC_CIS_WBEIS) | |
73 | + if (atchan->irq_status & AT_XDMAC_CIS_WBEIS) | |
74 | dev_err(chan2dev(&atchan->chan), "write bus error!!!"); | |
75 | - if (atchan->status & AT_XDMAC_CIS_ROIS) | |
76 | + if (atchan->irq_status & AT_XDMAC_CIS_ROIS) | |
77 | dev_err(chan2dev(&atchan->chan), "request overflow error!!!"); | |
78 | ||
79 | spin_lock_bh(&atchan->lock); | |
80 | @@ -1654,7 +1655,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id) | |
81 | atchan = &atxdmac->chan[i]; | |
82 | chan_imr = at_xdmac_chan_read(atchan, AT_XDMAC_CIM); | |
83 | chan_status = at_xdmac_chan_read(atchan, AT_XDMAC_CIS); | |
84 | - atchan->status = chan_status & chan_imr; | |
85 | + atchan->irq_status = chan_status & chan_imr; | |
86 | dev_vdbg(atxdmac->dma.dev, | |
87 | "%s: chan%d: imr=0x%x, status=0x%x\n", | |
88 | __func__, i, chan_imr, chan_status); | |
89 | @@ -1668,7 +1669,7 @@ static irqreturn_t at_xdmac_interrupt(int irq, void *dev_id) | |
90 | at_xdmac_chan_read(atchan, AT_XDMAC_CDA), | |
91 | at_xdmac_chan_read(atchan, AT_XDMAC_CUBC)); | |
92 | ||
93 | - if (atchan->status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS)) | |
94 | + if (atchan->irq_status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS)) | |
95 | at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); | |
96 | ||
97 | tasklet_schedule(&atchan->tasklet); | |
98 | -- | |
99 | 2.19.1 | |
100 |