18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/sh/drivers/dma/dma-sh.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * SuperH On-chip DMAC Support
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2000 Takashi YOSHII
88c2ecf20Sopenharmony_ci * Copyright (C) 2003, 2004 Paul Mundt
98c2ecf20Sopenharmony_ci * Copyright (C) 2005 Andriy Skulysh
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <mach-dreamcast/mach/dma.h>
168c2ecf20Sopenharmony_ci#include <asm/dma.h>
178c2ecf20Sopenharmony_ci#include <asm/dma-register.h>
188c2ecf20Sopenharmony_ci#include <cpu/dma-register.h>
198c2ecf20Sopenharmony_ci#include <cpu/dma.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * Some of the SoCs feature two DMAC modules. In such a case, the channels are
238c2ecf20Sopenharmony_ci * distributed equally among them.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci#ifdef	SH_DMAC_BASE1
268c2ecf20Sopenharmony_ci#define	SH_DMAC_NR_MD_CH	(CONFIG_NR_ONCHIP_DMA_CHANNELS / 2)
278c2ecf20Sopenharmony_ci#else
288c2ecf20Sopenharmony_ci#define	SH_DMAC_NR_MD_CH	CONFIG_NR_ONCHIP_DMA_CHANNELS
298c2ecf20Sopenharmony_ci#endif
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define	SH_DMAC_CH_SZ		0x10
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * Define the default configuration for dual address memory-memory transfer.
358c2ecf20Sopenharmony_ci * The 0x400 value represents auto-request, external->external.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci#define RS_DUAL	(DM_INC | SM_INC | RS_AUTO | TS_INDEX2VAL(XMIT_SZ_32BIT))
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic unsigned long dma_find_base(unsigned int chan)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	unsigned long base = SH_DMAC_BASE0;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#ifdef SH_DMAC_BASE1
448c2ecf20Sopenharmony_ci	if (chan >= SH_DMAC_NR_MD_CH)
458c2ecf20Sopenharmony_ci		base = SH_DMAC_BASE1;
468c2ecf20Sopenharmony_ci#endif
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return base;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic unsigned long dma_base_addr(unsigned int chan)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	unsigned long base = dma_find_base(chan);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	chan = (chan % SH_DMAC_NR_MD_CH) * SH_DMAC_CH_SZ;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* DMAOR is placed inside the channel register space. Step over it. */
588c2ecf20Sopenharmony_ci	if (chan >= DMAOR)
598c2ecf20Sopenharmony_ci		base += SH_DMAC_CH_SZ;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return base + chan;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_DMA_IRQ_MULTI
658c2ecf20Sopenharmony_cistatic inline unsigned int get_dmte_irq(unsigned int chan)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return chan >= 6 ? DMTE6_IRQ : DMTE0_IRQ;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci#else
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic unsigned int dmte_irq_map[] = {
728c2ecf20Sopenharmony_ci	DMTE0_IRQ, DMTE0_IRQ + 1, DMTE0_IRQ + 2, DMTE0_IRQ + 3,
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#ifdef DMTE4_IRQ
758c2ecf20Sopenharmony_ci	DMTE4_IRQ, DMTE4_IRQ + 1,
768c2ecf20Sopenharmony_ci#endif
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#ifdef DMTE6_IRQ
798c2ecf20Sopenharmony_ci	DMTE6_IRQ, DMTE6_IRQ + 1,
808c2ecf20Sopenharmony_ci#endif
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#ifdef DMTE8_IRQ
838c2ecf20Sopenharmony_ci	DMTE8_IRQ, DMTE9_IRQ, DMTE10_IRQ, DMTE11_IRQ,
848c2ecf20Sopenharmony_ci#endif
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic inline unsigned int get_dmte_irq(unsigned int chan)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	return dmte_irq_map[chan];
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci#endif
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/*
948c2ecf20Sopenharmony_ci * We determine the correct shift size based off of the CHCR transmit size
958c2ecf20Sopenharmony_ci * for the given channel. Since we know that it will take:
968c2ecf20Sopenharmony_ci *
978c2ecf20Sopenharmony_ci *	info->count >> ts_shift[transmit_size]
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci * iterations to complete the transfer.
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_cistatic unsigned int ts_shift[] = TS_SHIFT;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic inline unsigned int calc_xmit_shift(struct dma_channel *chan)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	u32 chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
1068c2ecf20Sopenharmony_ci	int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) |
1078c2ecf20Sopenharmony_ci		((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return ts_shift[cnt];
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * The transfer end interrupt must read the chcr register to end the
1148c2ecf20Sopenharmony_ci * hardware interrupt active condition.
1158c2ecf20Sopenharmony_ci * Besides that it needs to waken any waiting process, which should handle
1168c2ecf20Sopenharmony_ci * setting up the next transfer.
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_cistatic irqreturn_t dma_tei(int irq, void *dev_id)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct dma_channel *chan = dev_id;
1218c2ecf20Sopenharmony_ci	u32 chcr;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (!(chcr & CHCR_TE))
1268c2ecf20Sopenharmony_ci		return IRQ_NONE;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	chcr &= ~(CHCR_IE | CHCR_DE);
1298c2ecf20Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	wake_up(&chan->wait_queue);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int sh_dmac_request_dma(struct dma_channel *chan)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	if (unlikely(!(chan->flags & DMA_TEI_CAPABLE)))
1398c2ecf20Sopenharmony_ci		return 0;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return request_irq(get_dmte_irq(chan->chan), dma_tei, IRQF_SHARED,
1428c2ecf20Sopenharmony_ci			   chan->dev_id, chan);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void sh_dmac_free_dma(struct dma_channel *chan)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	free_irq(get_dmte_irq(chan->chan), chan);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int
1518c2ecf20Sopenharmony_cish_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	if (!chcr)
1548c2ecf20Sopenharmony_ci		chcr = RS_DUAL | CHCR_IE;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (chcr & CHCR_IE) {
1578c2ecf20Sopenharmony_ci		chcr &= ~CHCR_IE;
1588c2ecf20Sopenharmony_ci		chan->flags |= DMA_TEI_CAPABLE;
1598c2ecf20Sopenharmony_ci	} else {
1608c2ecf20Sopenharmony_ci		chan->flags &= ~DMA_TEI_CAPABLE;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	chan->flags |= DMA_CONFIGURED;
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void sh_dmac_enable_dma(struct dma_channel *chan)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	int irq;
1728c2ecf20Sopenharmony_ci	u32 chcr;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
1758c2ecf20Sopenharmony_ci	chcr |= CHCR_DE;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (chan->flags & DMA_TEI_CAPABLE)
1788c2ecf20Sopenharmony_ci		chcr |= CHCR_IE;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (chan->flags & DMA_TEI_CAPABLE) {
1838c2ecf20Sopenharmony_ci		irq = get_dmte_irq(chan->chan);
1848c2ecf20Sopenharmony_ci		enable_irq(irq);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void sh_dmac_disable_dma(struct dma_channel *chan)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int irq;
1918c2ecf20Sopenharmony_ci	u32 chcr;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (chan->flags & DMA_TEI_CAPABLE) {
1948c2ecf20Sopenharmony_ci		irq = get_dmte_irq(chan->chan);
1958c2ecf20Sopenharmony_ci		disable_irq(irq);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	chcr = __raw_readl(dma_base_addr(chan->chan) + CHCR);
1998c2ecf20Sopenharmony_ci	chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
2008c2ecf20Sopenharmony_ci	__raw_writel(chcr, (dma_base_addr(chan->chan) + CHCR));
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int sh_dmac_xfer_dma(struct dma_channel *chan)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	/*
2068c2ecf20Sopenharmony_ci	 * If we haven't pre-configured the channel with special flags, use
2078c2ecf20Sopenharmony_ci	 * the defaults.
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	if (unlikely(!(chan->flags & DMA_CONFIGURED)))
2108c2ecf20Sopenharmony_ci		sh_dmac_configure_channel(chan, 0);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	sh_dmac_disable_dma(chan);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/*
2158c2ecf20Sopenharmony_ci	 * Single-address mode usage note!
2168c2ecf20Sopenharmony_ci	 *
2178c2ecf20Sopenharmony_ci	 * It's important that we don't accidentally write any value to SAR/DAR
2188c2ecf20Sopenharmony_ci	 * (this includes 0) that hasn't been directly specified by the user if
2198c2ecf20Sopenharmony_ci	 * we're in single-address mode.
2208c2ecf20Sopenharmony_ci	 *
2218c2ecf20Sopenharmony_ci	 * In this case, only one address can be defined, anything else will
2228c2ecf20Sopenharmony_ci	 * result in a DMA address error interrupt (at least on the SH-4),
2238c2ecf20Sopenharmony_ci	 * which will subsequently halt the transfer.
2248c2ecf20Sopenharmony_ci	 *
2258c2ecf20Sopenharmony_ci	 * Channel 2 on the Dreamcast is a special case, as this is used for
2268c2ecf20Sopenharmony_ci	 * cascading to the PVR2 DMAC. In this case, we still need to write
2278c2ecf20Sopenharmony_ci	 * SAR and DAR, regardless of value, in order for cascading to work.
2288c2ecf20Sopenharmony_ci	 */
2298c2ecf20Sopenharmony_ci	if (chan->sar || (mach_is_dreamcast() &&
2308c2ecf20Sopenharmony_ci			  chan->chan == PVR2_CASCADE_CHAN))
2318c2ecf20Sopenharmony_ci		__raw_writel(chan->sar, (dma_base_addr(chan->chan) + SAR));
2328c2ecf20Sopenharmony_ci	if (chan->dar || (mach_is_dreamcast() &&
2338c2ecf20Sopenharmony_ci			  chan->chan == PVR2_CASCADE_CHAN))
2348c2ecf20Sopenharmony_ci		__raw_writel(chan->dar, (dma_base_addr(chan->chan) + DAR));
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	__raw_writel(chan->count >> calc_xmit_shift(chan),
2378c2ecf20Sopenharmony_ci		(dma_base_addr(chan->chan) + TCR));
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	sh_dmac_enable_dma(chan);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int sh_dmac_get_dma_residue(struct dma_channel *chan)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	if (!(__raw_readl(dma_base_addr(chan->chan) + CHCR) & CHCR_DE))
2478c2ecf20Sopenharmony_ci		return 0;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return __raw_readl(dma_base_addr(chan->chan) + TCR)
2508c2ecf20Sopenharmony_ci		 << calc_xmit_shift(chan);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/*
2548c2ecf20Sopenharmony_ci * DMAOR handling
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_ci#if defined(CONFIG_CPU_SUBTYPE_SH7723)	|| \
2578c2ecf20Sopenharmony_ci    defined(CONFIG_CPU_SUBTYPE_SH7724)	|| \
2588c2ecf20Sopenharmony_ci    defined(CONFIG_CPU_SUBTYPE_SH7780)	|| \
2598c2ecf20Sopenharmony_ci    defined(CONFIG_CPU_SUBTYPE_SH7785)
2608c2ecf20Sopenharmony_ci#define NR_DMAOR	2
2618c2ecf20Sopenharmony_ci#else
2628c2ecf20Sopenharmony_ci#define NR_DMAOR	1
2638c2ecf20Sopenharmony_ci#endif
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci#define dmaor_read_reg(n)		__raw_readw(dma_find_base((n) * \
2668c2ecf20Sopenharmony_ci						    SH_DMAC_NR_MD_CH) + DMAOR)
2678c2ecf20Sopenharmony_ci#define dmaor_write_reg(n, data)	__raw_writew(data, \
2688c2ecf20Sopenharmony_ci						     dma_find_base((n) * \
2698c2ecf20Sopenharmony_ci						     SH_DMAC_NR_MD_CH) + DMAOR)
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic inline int dmaor_reset(int no)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	unsigned long dmaor = dmaor_read_reg(no);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Try to clear the error flags first, incase they are set */
2768c2ecf20Sopenharmony_ci	dmaor &= ~(DMAOR_NMIF | DMAOR_AE);
2778c2ecf20Sopenharmony_ci	dmaor_write_reg(no, dmaor);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	dmaor |= DMAOR_INIT;
2808c2ecf20Sopenharmony_ci	dmaor_write_reg(no, dmaor);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* See if we got an error again */
2838c2ecf20Sopenharmony_ci	if ((dmaor_read_reg(no) & (DMAOR_AE | DMAOR_NMIF))) {
2848c2ecf20Sopenharmony_ci		printk(KERN_ERR "dma-sh: Can't initialize DMAOR.\n");
2858c2ecf20Sopenharmony_ci		return -EINVAL;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/*
2928c2ecf20Sopenharmony_ci * DMAE handling
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_SH4
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci#if defined(DMAE1_IRQ)
2978c2ecf20Sopenharmony_ci#define NR_DMAE		2
2988c2ecf20Sopenharmony_ci#else
2998c2ecf20Sopenharmony_ci#define NR_DMAE		1
3008c2ecf20Sopenharmony_ci#endif
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic const char *dmae_name[] = {
3038c2ecf20Sopenharmony_ci	"DMAC Address Error0",
3048c2ecf20Sopenharmony_ci	"DMAC Address Error1"
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_DMA_IRQ_MULTI
3088c2ecf20Sopenharmony_cistatic inline unsigned int get_dma_error_irq(int n)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	return get_dmte_irq(n * 6);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci#else
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic unsigned int dmae_irq_map[] = {
3158c2ecf20Sopenharmony_ci	DMAE0_IRQ,
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci#ifdef DMAE1_IRQ
3188c2ecf20Sopenharmony_ci	DMAE1_IRQ,
3198c2ecf20Sopenharmony_ci#endif
3208c2ecf20Sopenharmony_ci};
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic inline unsigned int get_dma_error_irq(int n)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	return dmae_irq_map[n];
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci#endif
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic irqreturn_t dma_err(int irq, void *dummy)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	int i;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	for (i = 0; i < NR_DMAOR; i++)
3338c2ecf20Sopenharmony_ci		dmaor_reset(i);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	disable_irq(irq);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int dmae_irq_init(void)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	int n;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	for (n = 0; n < NR_DMAE; n++) {
3458c2ecf20Sopenharmony_ci		int i = request_irq(get_dma_error_irq(n), dma_err,
3468c2ecf20Sopenharmony_ci				    IRQF_SHARED, dmae_name[n], (void *)dmae_name[n]);
3478c2ecf20Sopenharmony_ci		if (unlikely(i < 0)) {
3488c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
3498c2ecf20Sopenharmony_ci			return i;
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return 0;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic void dmae_irq_free(void)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	int n;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	for (n = 0; n < NR_DMAE; n++)
3618c2ecf20Sopenharmony_ci		free_irq(get_dma_error_irq(n), NULL);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci#else
3648c2ecf20Sopenharmony_cistatic inline int dmae_irq_init(void)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void dmae_irq_free(void)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci#endif
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic struct dma_ops sh_dmac_ops = {
3758c2ecf20Sopenharmony_ci	.request	= sh_dmac_request_dma,
3768c2ecf20Sopenharmony_ci	.free		= sh_dmac_free_dma,
3778c2ecf20Sopenharmony_ci	.get_residue	= sh_dmac_get_dma_residue,
3788c2ecf20Sopenharmony_ci	.xfer		= sh_dmac_xfer_dma,
3798c2ecf20Sopenharmony_ci	.configure	= sh_dmac_configure_channel,
3808c2ecf20Sopenharmony_ci};
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic struct dma_info sh_dmac_info = {
3838c2ecf20Sopenharmony_ci	.name		= "sh_dmac",
3848c2ecf20Sopenharmony_ci	.nr_channels	= CONFIG_NR_ONCHIP_DMA_CHANNELS,
3858c2ecf20Sopenharmony_ci	.ops		= &sh_dmac_ops,
3868c2ecf20Sopenharmony_ci	.flags		= DMAC_CHANNELS_TEI_CAPABLE,
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int __init sh_dmac_init(void)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct dma_info *info = &sh_dmac_info;
3928c2ecf20Sopenharmony_ci	int i, rc;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/*
3958c2ecf20Sopenharmony_ci	 * Initialize DMAE, for parts that support it.
3968c2ecf20Sopenharmony_ci	 */
3978c2ecf20Sopenharmony_ci	rc = dmae_irq_init();
3988c2ecf20Sopenharmony_ci	if (unlikely(rc != 0))
3998c2ecf20Sopenharmony_ci		return rc;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/*
4028c2ecf20Sopenharmony_ci	 * Initialize DMAOR, and clean up any error flags that may have
4038c2ecf20Sopenharmony_ci	 * been set.
4048c2ecf20Sopenharmony_ci	 */
4058c2ecf20Sopenharmony_ci	for (i = 0; i < NR_DMAOR; i++) {
4068c2ecf20Sopenharmony_ci		rc = dmaor_reset(i);
4078c2ecf20Sopenharmony_ci		if (unlikely(rc != 0))
4088c2ecf20Sopenharmony_ci			return rc;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return register_dmac(info);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic void __exit sh_dmac_exit(void)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	dmae_irq_free();
4178c2ecf20Sopenharmony_ci	unregister_dmac(&sh_dmac_info);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cisubsys_initcall(sh_dmac_init);
4218c2ecf20Sopenharmony_cimodule_exit(sh_dmac_exit);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Takashi YOSHII, Paul Mundt, Andriy Skulysh");
4248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SuperH On-Chip DMAC Support");
4258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
426