Skip to content

Commit bca700b

Browse files
zeargregkh
authored andcommitted
sh: dma: Fix DMA channel offset calculation
[ Upstream commit e82e475 ] Various SoCs of the SH3, SH4 and SH4A family, which use this driver, feature a differing number of DMA channels, which can be distributed between up to two DMAC modules. The existing implementation fails to correctly accommodate for all those variations, resulting in wrong channel offset calculations and leading to kernel panics. Rewrite dma_base_addr() in order to properly calculate channel offsets in a DMAC module. Fix dmaor_read_reg() and dmaor_write_reg(), so that the correct DMAC module base is selected for the DMAOR register. Fixes: 7f47c71 ("sh: dma: More legacy cpu dma chainsawing.") Signed-off-by: Artur Rojek <contact@artur-rojek.eu> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Link: https://lore.kernel.org/r/20230527164452.64797-2-contact@artur-rojek.eu Signed-off-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent f502922 commit bca700b

1 file changed

Lines changed: 24 additions & 13 deletions

File tree

arch/sh/drivers/dma/dma-sh.c

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@
2121
#include <cpu/dma-register.h>
2222
#include <cpu/dma.h>
2323

24+
/*
25+
* Some of the SoCs feature two DMAC modules. In such a case, the channels are
26+
* distributed equally among them.
27+
*/
28+
#ifdef SH_DMAC_BASE1
29+
#define SH_DMAC_NR_MD_CH (CONFIG_NR_ONCHIP_DMA_CHANNELS / 2)
30+
#else
31+
#define SH_DMAC_NR_MD_CH CONFIG_NR_ONCHIP_DMA_CHANNELS
32+
#endif
33+
34+
#define SH_DMAC_CH_SZ 0x10
35+
2436
/*
2537
* Define the default configuration for dual address memory-memory transfer.
2638
* The 0x400 value represents auto-request, external->external.
@@ -32,7 +44,7 @@ static unsigned long dma_find_base(unsigned int chan)
3244
unsigned long base = SH_DMAC_BASE0;
3345

3446
#ifdef SH_DMAC_BASE1
35-
if (chan >= 6)
47+
if (chan >= SH_DMAC_NR_MD_CH)
3648
base = SH_DMAC_BASE1;
3749
#endif
3850

@@ -43,13 +55,13 @@ static unsigned long dma_base_addr(unsigned int chan)
4355
{
4456
unsigned long base = dma_find_base(chan);
4557

46-
/* Normalize offset calculation */
47-
if (chan >= 9)
48-
chan -= 6;
49-
if (chan >= 4)
50-
base += 0x10;
58+
chan = (chan % SH_DMAC_NR_MD_CH) * SH_DMAC_CH_SZ;
59+
60+
/* DMAOR is placed inside the channel register space. Step over it. */
61+
if (chan >= DMAOR)
62+
base += SH_DMAC_CH_SZ;
5163

52-
return base + (chan * 0x10);
64+
return base + chan;
5365
}
5466

5567
#ifdef CONFIG_SH_DMA_IRQ_MULTI
@@ -253,12 +265,11 @@ static int sh_dmac_get_dma_residue(struct dma_channel *chan)
253265
#define NR_DMAOR 1
254266
#endif
255267

256-
/*
257-
* DMAOR bases are broken out amongst channel groups. DMAOR0 manages
258-
* channels 0 - 5, DMAOR1 6 - 11 (optional).
259-
*/
260-
#define dmaor_read_reg(n) __raw_readw(dma_find_base((n)*6))
261-
#define dmaor_write_reg(n, data) __raw_writew(data, dma_find_base(n)*6)
268+
#define dmaor_read_reg(n) __raw_readw(dma_find_base((n) * \
269+
SH_DMAC_NR_MD_CH) + DMAOR)
270+
#define dmaor_write_reg(n, data) __raw_writew(data, \
271+
dma_find_base((n) * \
272+
SH_DMAC_NR_MD_CH) + DMAOR)
262273

263274
static inline int dmaor_reset(int no)
264275
{

0 commit comments

Comments
 (0)