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