162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/sh/drivers/dma/dma-sh.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * SuperH On-chip DMAC Support
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2000 Takashi YOSHII
862306a36Sopenharmony_ci * Copyright (C) 2003, 2004 Paul Mundt
962306a36Sopenharmony_ci * Copyright (C) 2005 Andriy Skulysh
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <mach-dreamcast/mach/dma.h>
1662306a36Sopenharmony_ci#include <asm/dma.h>
1762306a36Sopenharmony_ci#include <asm/dma-register.h>
1862306a36Sopenharmony_ci#include <cpu/dma-register.h>
1962306a36Sopenharmony_ci#include <cpu/dma.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * Some of the SoCs feature two DMAC modules. In such a case, the channels are
2362306a36Sopenharmony_ci * distributed equally among them.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci#ifdef	SH_DMAC_BASE1
2662306a36Sopenharmony_ci#define	SH_DMAC_NR_MD_CH	(CONFIG_NR_ONCHIP_DMA_CHANNELS / 2)
2762306a36Sopenharmony_ci#else
2862306a36Sopenharmony_ci#define	SH_DMAC_NR_MD_CH	CONFIG_NR_ONCHIP_DMA_CHANNELS
2962306a36Sopenharmony_ci#endif
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define	SH_DMAC_CH_SZ		0x10
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * Define the default configuration for dual address memory-memory transfer.
3562306a36Sopenharmony_ci * The 0x400 value represents auto-request, external->external.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci#define RS_DUAL	(DM_INC | SM_INC | RS_AUTO | TS_INDEX2VAL(XMIT_SZ_32BIT))
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic unsigned long dma_find_base(unsigned int chan)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	unsigned long base = SH_DMAC_BASE0;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#ifdef SH_DMAC_BASE1
4462306a36Sopenharmony_ci	if (chan >= SH_DMAC_NR_MD_CH)
4562306a36Sopenharmony_ci		base = SH_DMAC_BASE1;
4662306a36Sopenharmony_ci#endif
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return base;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic unsigned long dma_base_addr(unsigned int chan)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	unsigned long base = dma_find_base(chan);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	chan = (chan % SH_DMAC_NR_MD_CH) * SH_DMAC_CH_SZ;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* DMAOR is placed inside the channel register space. Step over it. */
5862306a36Sopenharmony_ci	if (chan >= DMAOR)
5962306a36Sopenharmony_ci		base += SH_DMAC_CH_SZ;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return base + chan;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#ifdef CONFIG_SH_DMA_IRQ_MULTI
6562306a36Sopenharmony_cistatic inline unsigned int get_dmte_irq(unsigned int chan)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	return chan >= 6 ? DMTE6_IRQ : DMTE0_IRQ;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci#else
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic unsigned int dmte_irq_map[] = {
7262306a36Sopenharmony_ci	DMTE0_IRQ, DMTE0_IRQ + 1, DMTE0_IRQ + 2, DMTE0_IRQ + 3,
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#ifdef DMTE4_IRQ
7562306a36Sopenharmony_ci	DMTE4_IRQ, DMTE4_IRQ + 1,
7662306a36Sopenharmony_ci#endif
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#ifdef DMTE6_IRQ
7962306a36Sopenharmony_ci	DMTE6_IRQ, DMTE6_IRQ + 1,
8062306a36Sopenharmony_ci#endif
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#ifdef DMTE8_IRQ
8362306a36Sopenharmony_ci	DMTE8_IRQ, DMTE9_IRQ, DMTE10_IRQ, DMTE11_IRQ,
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline unsigned int get_dmte_irq(unsigned int chan)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	return dmte_irq_map[chan];
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci#endif
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * We determine the correct shift size based off of the CHCR transmit size
9562306a36Sopenharmony_ci * for the given channel. Since we know that it will take:
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci *	info->count >> ts_shift[transmit_size]
9862306a36Sopenharmony_ci *
9962306a36Sopenharmony_ci * iterations to complete the transfer.
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistatic unsigned int ts_shift[] = TS_SHIFT;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic inline unsigned int calc_xmit_shift(struct dma_channel *chan)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	u32 chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
10662306a36Sopenharmony_ci	int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) |
10762306a36Sopenharmony_ci		((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return ts_shift[cnt];
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * The transfer end interrupt must read the chcr register to end the
11462306a36Sopenharmony_ci * hardware interrupt active condition.
11562306a36Sopenharmony_ci * Besides that it needs to waken any waiting process, which should handle
11662306a36Sopenharmony_ci * setting up the next transfer.
11762306a36Sopenharmony_ci */
11862306a36Sopenharmony_cistatic irqreturn_t dma_tei(int irq, void *dev_id)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct dma_channel *chan = dev_id;
12162306a36Sopenharmony_ci	u32 chcr;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (!(chcr & CHCR_TE))
12662306a36Sopenharmony_ci		return IRQ_NONE;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	chcr &= ~(CHCR_IE | CHCR_DE);
12962306a36Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	wake_up(&chan->wait_queue);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return IRQ_HANDLED;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int sh_dmac_request_dma(struct dma_channel *chan)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	if (unlikely(!(chan->flags & DMA_TEI_CAPABLE)))
13962306a36Sopenharmony_ci		return 0;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return request_irq(get_dmte_irq(chan->chan), dma_tei, IRQF_SHARED,
14262306a36Sopenharmony_ci			   chan->dev_id, chan);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void sh_dmac_free_dma(struct dma_channel *chan)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	free_irq(get_dmte_irq(chan->chan), chan);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int
15162306a36Sopenharmony_cish_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	if (!chcr)
15462306a36Sopenharmony_ci		chcr = RS_DUAL | CHCR_IE;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (chcr & CHCR_IE) {
15762306a36Sopenharmony_ci		chcr &= ~CHCR_IE;
15862306a36Sopenharmony_ci		chan->flags |= DMA_TEI_CAPABLE;
15962306a36Sopenharmony_ci	} else {
16062306a36Sopenharmony_ci		chan->flags &= ~DMA_TEI_CAPABLE;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	chan->flags |= DMA_CONFIGURED;
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void sh_dmac_enable_dma(struct dma_channel *chan)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int irq;
17262306a36Sopenharmony_ci	u32 chcr;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
17562306a36Sopenharmony_ci	chcr |= CHCR_DE;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (chan->flags & DMA_TEI_CAPABLE)
17862306a36Sopenharmony_ci		chcr |= CHCR_IE;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (chan->flags & DMA_TEI_CAPABLE) {
18362306a36Sopenharmony_ci		irq = get_dmte_irq(chan->chan);
18462306a36Sopenharmony_ci		enable_irq(irq);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void sh_dmac_disable_dma(struct dma_channel *chan)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int irq;
19162306a36Sopenharmony_ci	u32 chcr;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (chan->flags & DMA_TEI_CAPABLE) {
19462306a36Sopenharmony_ci		irq = get_dmte_irq(chan->chan);
19562306a36Sopenharmony_ci		disable_irq(irq);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
19962306a36Sopenharmony_ci	chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
20062306a36Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int sh_dmac_xfer_dma(struct dma_channel *chan)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * If we haven't pre-configured the channel with special flags, use
20762306a36Sopenharmony_ci	 * the defaults.
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	if (unlikely(!(chan->flags & DMA_CONFIGURED)))
21062306a36Sopenharmony_ci		sh_dmac_configure_channel(chan, 0);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	sh_dmac_disable_dma(chan);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/*
21562306a36Sopenharmony_ci	 * Single-address mode usage note!
21662306a36Sopenharmony_ci	 *
21762306a36Sopenharmony_ci	 * It's important that we don't accidentally write any value to SAR/DAR
21862306a36Sopenharmony_ci	 * (this includes 0) that hasn't been directly specified by the user if
21962306a36Sopenharmony_ci	 * we're in single-address mode.
22062306a36Sopenharmony_ci	 *
22162306a36Sopenharmony_ci	 * In this case, only one address can be defined, anything else will
22262306a36Sopenharmony_ci	 * result in a DMA address error interrupt (at least on the SH-4),
22362306a36Sopenharmony_ci	 * which will subsequently halt the transfer.
22462306a36Sopenharmony_ci	 *
22562306a36Sopenharmony_ci	 * Channel 2 on the Dreamcast is a special case, as this is used for
22662306a36Sopenharmony_ci	 * cascading to the PVR2 DMAC. In this case, we still need to write
22762306a36Sopenharmony_ci	 * SAR and DAR, regardless of value, in order for cascading to work.
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	if (chan->sar || (mach_is_dreamcast() &&
23062306a36Sopenharmony_ci			  chan->chan == PVR2_CASCADE_CHAN))
23162306a36Sopenharmony_ci		__raw_writel(chan->sar, (dma_base_addr(chan->chan) + SAR));
23262306a36Sopenharmony_ci	if (chan->dar || (mach_is_dreamcast() &&
23362306a36Sopenharmony_ci			  chan->chan == PVR2_CASCADE_CHAN))
23462306a36Sopenharmony_ci		__raw_writel(chan->dar, (dma_base_addr(chan->chan) + DAR));
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	__raw_writel(chan->count >> calc_xmit_shift(chan),
23762306a36Sopenharmony_ci		(dma_base_addr(chan->chan) + TCR));
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	sh_dmac_enable_dma(chan);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return 0;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int sh_dmac_get_dma_residue(struct dma_channel *chan)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	if (!(__raw_readl(dma_base_addr(chan->chan) + CHCR) & CHCR_DE))
24762306a36Sopenharmony_ci		return 0;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return __raw_readl(dma_base_addr(chan->chan) + TCR)
25062306a36Sopenharmony_ci		 << calc_xmit_shift(chan);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/*
25462306a36Sopenharmony_ci * DMAOR handling
25562306a36Sopenharmony_ci */
25662306a36Sopenharmony_ci#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
25762306a36Sopenharmony_ci    defined(CONFIG_CPU_SUBTYPE_SH7724)	|| \
25862306a36Sopenharmony_ci    defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
25962306a36Sopenharmony_ci    defined(CONFIG_CPU_SUBTYPE_SH7785)
26062306a36Sopenharmony_ci#define NR_DMAOR	2
26162306a36Sopenharmony_ci#else
26262306a36Sopenharmony_ci#define NR_DMAOR	1
26362306a36Sopenharmony_ci#endif
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci#define dmaor_read_reg(n)		__raw_readw(dma_find_base((n) * \
26662306a36Sopenharmony_ci						    SH_DMAC_NR_MD_CH) + DMAOR)
26762306a36Sopenharmony_ci#define dmaor_write_reg(n, data)	__raw_writew(data, \
26862306a36Sopenharmony_ci						     dma_find_base((n) * \
26962306a36Sopenharmony_ci						     SH_DMAC_NR_MD_CH) + DMAOR)
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic inline int dmaor_reset(int no)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	unsigned long dmaor = dmaor_read_reg(no);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Try to clear the error flags first, incase they are set */
27662306a36Sopenharmony_ci	dmaor &= ~(DMAOR_NMIF | DMAOR_AE);
27762306a36Sopenharmony_ci	dmaor_write_reg(no, dmaor);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	dmaor |= DMAOR_INIT;
28062306a36Sopenharmony_ci	dmaor_write_reg(no, dmaor);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* See if we got an error again */
28362306a36Sopenharmony_ci	if ((dmaor_read_reg(no) & (DMAOR_AE | DMAOR_NMIF))) {
28462306a36Sopenharmony_ci		printk(KERN_ERR "dma-sh: Can't initialize DMAOR.\n");
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/*
29262306a36Sopenharmony_ci * DMAE handling
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_ci#ifdef CONFIG_CPU_SH4
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci#if defined(DMAE1_IRQ)
29762306a36Sopenharmony_ci#define NR_DMAE		2
29862306a36Sopenharmony_ci#else
29962306a36Sopenharmony_ci#define NR_DMAE		1
30062306a36Sopenharmony_ci#endif
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic const char *dmae_name[] = {
30362306a36Sopenharmony_ci	"DMAC Address Error0",
30462306a36Sopenharmony_ci	"DMAC Address Error1"
30562306a36Sopenharmony_ci};
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci#ifdef CONFIG_SH_DMA_IRQ_MULTI
30862306a36Sopenharmony_cistatic inline unsigned int get_dma_error_irq(int n)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	return get_dmte_irq(n * 6);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci#else
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic unsigned int dmae_irq_map[] = {
31562306a36Sopenharmony_ci	DMAE0_IRQ,
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci#ifdef DMAE1_IRQ
31862306a36Sopenharmony_ci	DMAE1_IRQ,
31962306a36Sopenharmony_ci#endif
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic inline unsigned int get_dma_error_irq(int n)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	return dmae_irq_map[n];
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci#endif
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic irqreturn_t dma_err(int irq, void *dummy)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	int i;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	for (i = 0; i < NR_DMAOR; i++)
33362306a36Sopenharmony_ci		dmaor_reset(i);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	disable_irq(irq);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return IRQ_HANDLED;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int dmae_irq_init(void)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	int n;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	for (n = 0; n < NR_DMAE; n++) {
34562306a36Sopenharmony_ci		int i = request_irq(get_dma_error_irq(n), dma_err,
34662306a36Sopenharmony_ci				    IRQF_SHARED, dmae_name[n], (void *)dmae_name[n]);
34762306a36Sopenharmony_ci		if (unlikely(i < 0)) {
34862306a36Sopenharmony_ci			printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
34962306a36Sopenharmony_ci			return i;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return 0;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic void dmae_irq_free(void)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	int n;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	for (n = 0; n < NR_DMAE; n++)
36162306a36Sopenharmony_ci		free_irq(get_dma_error_irq(n), NULL);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci#else
36462306a36Sopenharmony_cistatic inline int dmae_irq_init(void)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic void dmae_irq_free(void)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci#endif
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic struct dma_ops sh_dmac_ops = {
37562306a36Sopenharmony_ci	.request	= sh_dmac_request_dma,
37662306a36Sopenharmony_ci	.free		= sh_dmac_free_dma,
37762306a36Sopenharmony_ci	.get_residue	= sh_dmac_get_dma_residue,
37862306a36Sopenharmony_ci	.xfer		= sh_dmac_xfer_dma,
37962306a36Sopenharmony_ci	.configure	= sh_dmac_configure_channel,
38062306a36Sopenharmony_ci};
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic struct dma_info sh_dmac_info = {
38362306a36Sopenharmony_ci	.name		= "sh_dmac",
38462306a36Sopenharmony_ci	.nr_channels	= CONFIG_NR_ONCHIP_DMA_CHANNELS,
38562306a36Sopenharmony_ci	.ops		= &sh_dmac_ops,
38662306a36Sopenharmony_ci	.flags		= DMAC_CHANNELS_TEI_CAPABLE,
38762306a36Sopenharmony_ci};
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int __init sh_dmac_init(void)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct dma_info *info = &sh_dmac_info;
39262306a36Sopenharmony_ci	int i, rc;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/*
39562306a36Sopenharmony_ci	 * Initialize DMAE, for parts that support it.
39662306a36Sopenharmony_ci	 */
39762306a36Sopenharmony_ci	rc = dmae_irq_init();
39862306a36Sopenharmony_ci	if (unlikely(rc != 0))
39962306a36Sopenharmony_ci		return rc;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/*
40262306a36Sopenharmony_ci	 * Initialize DMAOR, and clean up any error flags that may have
40362306a36Sopenharmony_ci	 * been set.
40462306a36Sopenharmony_ci	 */
40562306a36Sopenharmony_ci	for (i = 0; i < NR_DMAOR; i++) {
40662306a36Sopenharmony_ci		rc = dmaor_reset(i);
40762306a36Sopenharmony_ci		if (unlikely(rc != 0))
40862306a36Sopenharmony_ci			return rc;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return register_dmac(info);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void __exit sh_dmac_exit(void)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	dmae_irq_free();
41762306a36Sopenharmony_ci	unregister_dmac(&sh_dmac_info);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cisubsys_initcall(sh_dmac_init);
42162306a36Sopenharmony_cimodule_exit(sh_dmac_exit);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ciMODULE_AUTHOR("Takashi YOSHII, Paul Mundt, Andriy Skulysh");
42462306a36Sopenharmony_ciMODULE_DESCRIPTION("SuperH On-Chip DMAC Support");
42562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
426