diff options
author | Lothar Waßmann <LW@KARO-electronics.de> | 2011-12-08 03:15:44 -0500 |
---|---|---|
committer | Vinod Koul <vinod.koul@linux.intel.com> | 2011-12-23 10:24:55 -0500 |
commit | 7ad7a345a4f17c08a1bb9bfdbb62f7793d84aa36 (patch) | |
tree | 83839c0af2da4529d5fdec1b9c961b541d572c66 /drivers/dma | |
parent | 6d23ea4b1906f28f5d99ad6aeef7207c48be6bfd (diff) |
dma: mxs-dma: Don't use CLKGATE bits in CTRL0 to disable DMA channels
This is how the original Freescale code (unintentionally) worked,
because the code path which would have asserted the CLKGATE bit was
never actually reached in their code.
This fixes the nefarious "DMA timout" bug when multiple DMA channels
(e.g. GPMI NAND and MMC) are used at the same time.
If a better fix for this problem should be found, the clkgate handling
could be reinstated.
See http://lists.infradead.org/pipermail/linux-arm-kernel/2011-September/065228.html
Also reverse the order of mxs_dma_disable_chan() and
mxs_dma_reset_chan() in mxs_dma_control() because mxs_dma_reset_chan()
can only work when the DMA channel is enabled.
Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
Signed-off-by: Vinod Koul <vinod.koul@linux.intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/mxs-dma.c | 29 |
1 files changed, 1 insertions, 28 deletions
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 1b4c6be3aacb..6548595c26dc 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c | |||
@@ -44,7 +44,6 @@ | |||
44 | #define HW_APBHX_CTRL0 0x000 | 44 | #define HW_APBHX_CTRL0 0x000 |
45 | #define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) | 45 | #define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) |
46 | #define BM_APBH_CTRL0_APB_BURST_EN (1 << 28) | 46 | #define BM_APBH_CTRL0_APB_BURST_EN (1 << 28) |
47 | #define BP_APBH_CTRL0_CLKGATE_CHANNEL 8 | ||
48 | #define BP_APBH_CTRL0_RESET_CHANNEL 16 | 47 | #define BP_APBH_CTRL0_RESET_CHANNEL 16 |
49 | #define HW_APBHX_CTRL1 0x010 | 48 | #define HW_APBHX_CTRL1 0x010 |
50 | #define HW_APBHX_CTRL2 0x020 | 49 | #define HW_APBHX_CTRL2 0x020 |
@@ -131,23 +130,6 @@ struct mxs_dma_engine { | |||
131 | struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; | 130 | struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; |
132 | }; | 131 | }; |
133 | 132 | ||
134 | static inline void mxs_dma_clkgate(struct mxs_dma_chan *mxs_chan, int enable) | ||
135 | { | ||
136 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; | ||
137 | int chan_id = mxs_chan->chan.chan_id; | ||
138 | int set_clr = enable ? MXS_CLR_ADDR : MXS_SET_ADDR; | ||
139 | |||
140 | /* enable apbh channel clock */ | ||
141 | if (dma_is_apbh()) { | ||
142 | if (apbh_is_old()) | ||
143 | writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL), | ||
144 | mxs_dma->base + HW_APBHX_CTRL0 + set_clr); | ||
145 | else | ||
146 | writel(1 << chan_id, | ||
147 | mxs_dma->base + HW_APBHX_CTRL0 + set_clr); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) | 133 | static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) |
152 | { | 134 | { |
153 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; | 135 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; |
@@ -166,9 +148,6 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) | |||
166 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; | 148 | struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; |
167 | int chan_id = mxs_chan->chan.chan_id; | 149 | int chan_id = mxs_chan->chan.chan_id; |
168 | 150 | ||
169 | /* clkgate needs to be enabled before writing other registers */ | ||
170 | mxs_dma_clkgate(mxs_chan, 1); | ||
171 | |||
172 | /* set cmd_addr up */ | 151 | /* set cmd_addr up */ |
173 | writel(mxs_chan->ccw_phys, | 152 | writel(mxs_chan->ccw_phys, |
174 | mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id)); | 153 | mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id)); |
@@ -179,9 +158,6 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) | |||
179 | 158 | ||
180 | static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) | 159 | static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) |
181 | { | 160 | { |
182 | /* disable apbh channel clock */ | ||
183 | mxs_dma_clkgate(mxs_chan, 0); | ||
184 | |||
185 | mxs_chan->status = DMA_SUCCESS; | 161 | mxs_chan->status = DMA_SUCCESS; |
186 | } | 162 | } |
187 | 163 | ||
@@ -339,10 +315,7 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) | |||
339 | if (ret) | 315 | if (ret) |
340 | goto err_clk; | 316 | goto err_clk; |
341 | 317 | ||
342 | /* clkgate needs to be enabled for reset to finish */ | ||
343 | mxs_dma_clkgate(mxs_chan, 1); | ||
344 | mxs_dma_reset_chan(mxs_chan); | 318 | mxs_dma_reset_chan(mxs_chan); |
345 | mxs_dma_clkgate(mxs_chan, 0); | ||
346 | 319 | ||
347 | dma_async_tx_descriptor_init(&mxs_chan->desc, chan); | 320 | dma_async_tx_descriptor_init(&mxs_chan->desc, chan); |
348 | mxs_chan->desc.tx_submit = mxs_dma_tx_submit; | 321 | mxs_chan->desc.tx_submit = mxs_dma_tx_submit; |
@@ -542,8 +515,8 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
542 | 515 | ||
543 | switch (cmd) { | 516 | switch (cmd) { |
544 | case DMA_TERMINATE_ALL: | 517 | case DMA_TERMINATE_ALL: |
545 | mxs_dma_disable_chan(mxs_chan); | ||
546 | mxs_dma_reset_chan(mxs_chan); | 518 | mxs_dma_reset_chan(mxs_chan); |
519 | mxs_dma_disable_chan(mxs_chan); | ||
547 | break; | 520 | break; |
548 | case DMA_PAUSE: | 521 | case DMA_PAUSE: |
549 | mxs_dma_pause_chan(mxs_chan); | 522 | mxs_dma_pause_chan(mxs_chan); |