]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.4.177/dmaengine-at_xdmac-fix-wrongfull-report-of-a-channel.patch
Linux 4.4.177
[thirdparty/kernel/stable-queue.git] / releases / 4.4.177 / dmaengine-at_xdmac-fix-wrongfull-report-of-a-channel.patch
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