162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/plat-omap/dma.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003 - 2008 Nokia Corporation 662306a36Sopenharmony_ci * Author: Juha Yrjölä <juha.yrjola@nokia.com> 762306a36Sopenharmony_ci * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> 862306a36Sopenharmony_ci * Graphics DMA and LCD DMA graphics tranformations 962306a36Sopenharmony_ci * by Imre Deak <imre.deak@nokia.com> 1062306a36Sopenharmony_ci * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc. 1162306a36Sopenharmony_ci * Merged to support both OMAP1 and OMAP2 by Tony Lindgren <tony@atomide.com> 1262306a36Sopenharmony_ci * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments 1562306a36Sopenharmony_ci * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Support functions for the OMAP internal DMA channels. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ 2062306a36Sopenharmony_ci * Converted DMA library into DMA platform driver. 2162306a36Sopenharmony_ci * - G, Manjunath Kondaiah <manjugk@ti.com> 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci#include <linux/sched.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/errno.h> 2962306a36Sopenharmony_ci#include <linux/interrupt.h> 3062306a36Sopenharmony_ci#include <linux/irq.h> 3162306a36Sopenharmony_ci#include <linux/io.h> 3262306a36Sopenharmony_ci#include <linux/slab.h> 3362306a36Sopenharmony_ci#include <linux/delay.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/omap-dma.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/soc/ti/omap1-io.h> 3862306a36Sopenharmony_ci#include <linux/soc/ti/omap1-soc.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include "tc.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * MAX_LOGICAL_DMA_CH_COUNT: the maximum number of logical DMA 4462306a36Sopenharmony_ci * channels that an instance of the SDMA IP block can support. Used 4562306a36Sopenharmony_ci * to size arrays. (The actual maximum on a particular SoC may be less 4662306a36Sopenharmony_ci * than this -- for example, OMAP1 SDMA instances only support 17 logical 4762306a36Sopenharmony_ci * DMA channels.) 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#define MAX_LOGICAL_DMA_CH_COUNT 32 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#undef DEBUG 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define OMAP_DMA_ACTIVE 0x01 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct omap_system_dma_plat_info *p; 5862306a36Sopenharmony_cistatic struct omap_dma_dev_attr *d; 5962306a36Sopenharmony_cistatic int enable_1510_mode; 6062306a36Sopenharmony_cistatic u32 errata; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct dma_link_info { 6362306a36Sopenharmony_ci int *linked_dmach_q; 6462306a36Sopenharmony_ci int no_of_lchs_linked; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci int q_count; 6762306a36Sopenharmony_ci int q_tail; 6862306a36Sopenharmony_ci int q_head; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci int chain_state; 7162306a36Sopenharmony_ci int chain_mode; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int dma_lch_count; 7662306a36Sopenharmony_cistatic int dma_chan_count; 7762306a36Sopenharmony_cistatic int omap_dma_reserve_channels; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(dma_chan_lock); 8062306a36Sopenharmony_cistatic struct omap_dma_lch *dma_chan; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic inline void omap_disable_channel_irq(int lch) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci /* disable channel interrupts */ 8562306a36Sopenharmony_ci p->dma_write(0, CICR, lch); 8662306a36Sopenharmony_ci /* Clear CSR */ 8762306a36Sopenharmony_ci p->dma_read(CSR, lch); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic inline void set_gdma_dev(int req, int dev) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4; 9362306a36Sopenharmony_ci int shift = ((req - 1) % 5) * 6; 9462306a36Sopenharmony_ci u32 l; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci l = omap_readl(reg); 9762306a36Sopenharmony_ci l &= ~(0x3f << shift); 9862306a36Sopenharmony_ci l |= (dev - 1) << shift; 9962306a36Sopenharmony_ci omap_writel(l, reg); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_FB_OMAP) 10362306a36Sopenharmony_civoid omap_set_dma_priority(int lch, int dst_port, int priority) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci unsigned long reg; 10662306a36Sopenharmony_ci u32 l; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (dma_omap1()) { 10962306a36Sopenharmony_ci switch (dst_port) { 11062306a36Sopenharmony_ci case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */ 11162306a36Sopenharmony_ci reg = OMAP_TC_OCPT1_PRIOR; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */ 11462306a36Sopenharmony_ci reg = OMAP_TC_OCPT2_PRIOR; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */ 11762306a36Sopenharmony_ci reg = OMAP_TC_EMIFF_PRIOR; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */ 12062306a36Sopenharmony_ci reg = OMAP_TC_EMIFS_PRIOR; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci default: 12362306a36Sopenharmony_ci BUG(); 12462306a36Sopenharmony_ci return; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci l = omap_readl(reg); 12762306a36Sopenharmony_ci l &= ~(0xf << 8); 12862306a36Sopenharmony_ci l |= (priority & 0xf) << 8; 12962306a36Sopenharmony_ci omap_writel(l, reg); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_priority); 13362306a36Sopenharmony_ci#endif 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_OMAP) 13662306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP15XX 13762306a36Sopenharmony_ci/* Returns 1 if the DMA module is in OMAP1510-compatible mode, 0 otherwise */ 13862306a36Sopenharmony_cistatic int omap_dma_in_1510_mode(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci return enable_1510_mode; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci#else 14362306a36Sopenharmony_ci#define omap_dma_in_1510_mode() 0 14462306a36Sopenharmony_ci#endif 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_civoid omap_set_dma_transfer_params(int lch, int data_type, int elem_count, 14762306a36Sopenharmony_ci int frame_count, int sync_mode, 14862306a36Sopenharmony_ci int dma_trigger, int src_or_dst_synch) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci u32 l; 15162306a36Sopenharmony_ci u16 ccr; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci l = p->dma_read(CSDP, lch); 15462306a36Sopenharmony_ci l &= ~0x03; 15562306a36Sopenharmony_ci l |= data_type; 15662306a36Sopenharmony_ci p->dma_write(l, CSDP, lch); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ccr = p->dma_read(CCR, lch); 15962306a36Sopenharmony_ci ccr &= ~(1 << 5); 16062306a36Sopenharmony_ci if (sync_mode == OMAP_DMA_SYNC_FRAME) 16162306a36Sopenharmony_ci ccr |= 1 << 5; 16262306a36Sopenharmony_ci p->dma_write(ccr, CCR, lch); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ccr = p->dma_read(CCR2, lch); 16562306a36Sopenharmony_ci ccr &= ~(1 << 2); 16662306a36Sopenharmony_ci if (sync_mode == OMAP_DMA_SYNC_BLOCK) 16762306a36Sopenharmony_ci ccr |= 1 << 2; 16862306a36Sopenharmony_ci p->dma_write(ccr, CCR2, lch); 16962306a36Sopenharmony_ci p->dma_write(elem_count, CEN, lch); 17062306a36Sopenharmony_ci p->dma_write(frame_count, CFN, lch); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_transfer_params); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (!dma_omap15xx()) { 17762306a36Sopenharmony_ci u32 l; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci l = p->dma_read(LCH_CTRL, lch); 18062306a36Sopenharmony_ci l &= ~0x7; 18162306a36Sopenharmony_ci l |= mode; 18262306a36Sopenharmony_ci p->dma_write(l, LCH_CTRL, lch); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_channel_mode); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* Note that src_port is only for omap1 */ 18862306a36Sopenharmony_civoid omap_set_dma_src_params(int lch, int src_port, int src_amode, 18962306a36Sopenharmony_ci unsigned long src_start, 19062306a36Sopenharmony_ci int src_ei, int src_fi) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 l; 19362306a36Sopenharmony_ci u16 w; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci w = p->dma_read(CSDP, lch); 19662306a36Sopenharmony_ci w &= ~(0x1f << 2); 19762306a36Sopenharmony_ci w |= src_port << 2; 19862306a36Sopenharmony_ci p->dma_write(w, CSDP, lch); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci l = p->dma_read(CCR, lch); 20162306a36Sopenharmony_ci l &= ~(0x03 << 12); 20262306a36Sopenharmony_ci l |= src_amode << 12; 20362306a36Sopenharmony_ci p->dma_write(l, CCR, lch); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci p->dma_write(src_start, CSSA, lch); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci p->dma_write(src_ei, CSEI, lch); 20862306a36Sopenharmony_ci p->dma_write(src_fi, CSFI, lch); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_src_params); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_civoid omap_set_dma_src_data_pack(int lch, int enable) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci u32 l; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci l = p->dma_read(CSDP, lch); 21762306a36Sopenharmony_ci l &= ~(1 << 6); 21862306a36Sopenharmony_ci if (enable) 21962306a36Sopenharmony_ci l |= (1 << 6); 22062306a36Sopenharmony_ci p->dma_write(l, CSDP, lch); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_src_data_pack); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_civoid omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci unsigned int burst = 0; 22762306a36Sopenharmony_ci u32 l; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci l = p->dma_read(CSDP, lch); 23062306a36Sopenharmony_ci l &= ~(0x03 << 7); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci switch (burst_mode) { 23362306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_DIS: 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_4: 23662306a36Sopenharmony_ci burst = 0x2; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_8: 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * not supported by current hardware on OMAP1 24162306a36Sopenharmony_ci * w |= (0x03 << 7); 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ci fallthrough; 24462306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_16: 24562306a36Sopenharmony_ci /* OMAP1 don't support burst 16 */ 24662306a36Sopenharmony_ci fallthrough; 24762306a36Sopenharmony_ci default: 24862306a36Sopenharmony_ci BUG(); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci l |= (burst << 7); 25262306a36Sopenharmony_ci p->dma_write(l, CSDP, lch); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_src_burst_mode); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* Note that dest_port is only for OMAP1 */ 25762306a36Sopenharmony_civoid omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, 25862306a36Sopenharmony_ci unsigned long dest_start, 25962306a36Sopenharmony_ci int dst_ei, int dst_fi) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci u32 l; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci l = p->dma_read(CSDP, lch); 26462306a36Sopenharmony_ci l &= ~(0x1f << 9); 26562306a36Sopenharmony_ci l |= dest_port << 9; 26662306a36Sopenharmony_ci p->dma_write(l, CSDP, lch); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci l = p->dma_read(CCR, lch); 26962306a36Sopenharmony_ci l &= ~(0x03 << 14); 27062306a36Sopenharmony_ci l |= dest_amode << 14; 27162306a36Sopenharmony_ci p->dma_write(l, CCR, lch); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci p->dma_write(dest_start, CDSA, lch); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci p->dma_write(dst_ei, CDEI, lch); 27662306a36Sopenharmony_ci p->dma_write(dst_fi, CDFI, lch); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_dest_params); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_civoid omap_set_dma_dest_data_pack(int lch, int enable) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci u32 l; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci l = p->dma_read(CSDP, lch); 28562306a36Sopenharmony_ci l &= ~(1 << 13); 28662306a36Sopenharmony_ci if (enable) 28762306a36Sopenharmony_ci l |= 1 << 13; 28862306a36Sopenharmony_ci p->dma_write(l, CSDP, lch); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_dest_data_pack); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_civoid omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci unsigned int burst = 0; 29562306a36Sopenharmony_ci u32 l; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci l = p->dma_read(CSDP, lch); 29862306a36Sopenharmony_ci l &= ~(0x03 << 14); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci switch (burst_mode) { 30162306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_DIS: 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_4: 30462306a36Sopenharmony_ci burst = 0x2; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_8: 30762306a36Sopenharmony_ci burst = 0x3; 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case OMAP_DMA_DATA_BURST_16: 31062306a36Sopenharmony_ci /* OMAP1 don't support burst 16 */ 31162306a36Sopenharmony_ci fallthrough; 31262306a36Sopenharmony_ci default: 31362306a36Sopenharmony_ci printk(KERN_ERR "Invalid DMA burst mode\n"); 31462306a36Sopenharmony_ci BUG(); 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci l |= (burst << 14); 31862306a36Sopenharmony_ci p->dma_write(l, CSDP, lch); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ciEXPORT_SYMBOL(omap_set_dma_dest_burst_mode); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic inline void omap_enable_channel_irq(int lch) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci /* Clear CSR */ 32562306a36Sopenharmony_ci p->dma_read(CSR, lch); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Enable some nice interrupts. */ 32862306a36Sopenharmony_ci p->dma_write(dma_chan[lch].enabled_irqs, CICR, lch); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_civoid omap_disable_dma_irq(int lch, u16 bits) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci dma_chan[lch].enabled_irqs &= ~bits; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ciEXPORT_SYMBOL(omap_disable_dma_irq); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic inline void enable_lnk(int lch) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci u32 l; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci l = p->dma_read(CLNK_CTRL, lch); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci l &= ~(1 << 14); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Set the ENABLE_LNK bits */ 34662306a36Sopenharmony_ci if (dma_chan[lch].next_lch != -1) 34762306a36Sopenharmony_ci l = dma_chan[lch].next_lch | (1 << 15); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci p->dma_write(l, CLNK_CTRL, lch); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic inline void disable_lnk(int lch) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci u32 l; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci l = p->dma_read(CLNK_CTRL, lch); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Disable interrupts */ 35962306a36Sopenharmony_ci omap_disable_channel_irq(lch); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Set the STOP_LNK bit */ 36262306a36Sopenharmony_ci l |= 1 << 14; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci p->dma_write(l, CLNK_CTRL, lch); 36562306a36Sopenharmony_ci dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci#endif 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint omap_request_dma(int dev_id, const char *dev_name, 37062306a36Sopenharmony_ci void (*callback)(int lch, u16 ch_status, void *data), 37162306a36Sopenharmony_ci void *data, int *dma_ch_out) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci int ch, free_ch = -1; 37462306a36Sopenharmony_ci unsigned long flags; 37562306a36Sopenharmony_ci struct omap_dma_lch *chan; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci WARN(strcmp(dev_name, "DMA engine"), "Using deprecated platform DMA API - please update to DMA engine"); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci spin_lock_irqsave(&dma_chan_lock, flags); 38062306a36Sopenharmony_ci for (ch = 0; ch < dma_chan_count; ch++) { 38162306a36Sopenharmony_ci if (free_ch == -1 && dma_chan[ch].dev_id == -1) { 38262306a36Sopenharmony_ci free_ch = ch; 38362306a36Sopenharmony_ci /* Exit after first free channel found */ 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci if (free_ch == -1) { 38862306a36Sopenharmony_ci spin_unlock_irqrestore(&dma_chan_lock, flags); 38962306a36Sopenharmony_ci return -EBUSY; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci chan = dma_chan + free_ch; 39262306a36Sopenharmony_ci chan->dev_id = dev_id; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (p->clear_lch_regs) 39562306a36Sopenharmony_ci p->clear_lch_regs(free_ch); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci spin_unlock_irqrestore(&dma_chan_lock, flags); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci chan->dev_name = dev_name; 40062306a36Sopenharmony_ci chan->callback = callback; 40162306a36Sopenharmony_ci chan->data = data; 40262306a36Sopenharmony_ci chan->flags = 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci chan->enabled_irqs = OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci chan->enabled_irqs |= OMAP1_DMA_TOUT_IRQ; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (dma_omap16xx()) { 40962306a36Sopenharmony_ci /* If the sync device is set, configure it dynamically. */ 41062306a36Sopenharmony_ci if (dev_id != 0) { 41162306a36Sopenharmony_ci set_gdma_dev(free_ch + 1, dev_id); 41262306a36Sopenharmony_ci dev_id = free_ch + 1; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * Disable the 1510 compatibility mode and set the sync device 41662306a36Sopenharmony_ci * id. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci p->dma_write(dev_id | (1 << 10), CCR, free_ch); 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci p->dma_write(dev_id, CCR, free_ch); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci *dma_ch_out = free_ch; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ciEXPORT_SYMBOL(omap_request_dma); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_civoid omap_free_dma(int lch) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci unsigned long flags; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (dma_chan[lch].dev_id == -1) { 43462306a36Sopenharmony_ci pr_err("omap_dma: trying to free unallocated DMA channel %d\n", 43562306a36Sopenharmony_ci lch); 43662306a36Sopenharmony_ci return; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Disable all DMA interrupts for the channel. */ 44062306a36Sopenharmony_ci omap_disable_channel_irq(lch); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Make sure the DMA transfer is stopped. */ 44362306a36Sopenharmony_ci p->dma_write(0, CCR, lch); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci spin_lock_irqsave(&dma_chan_lock, flags); 44662306a36Sopenharmony_ci dma_chan[lch].dev_id = -1; 44762306a36Sopenharmony_ci dma_chan[lch].next_lch = -1; 44862306a36Sopenharmony_ci dma_chan[lch].callback = NULL; 44962306a36Sopenharmony_ci spin_unlock_irqrestore(&dma_chan_lock, flags); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ciEXPORT_SYMBOL(omap_free_dma); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * Clears any DMA state so the DMA engine is ready to restart with new buffers 45562306a36Sopenharmony_ci * through omap_start_dma(). Any buffers in flight are discarded. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_cistatic void omap_clear_dma(int lch) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci unsigned long flags; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci local_irq_save(flags); 46262306a36Sopenharmony_ci p->clear_dma(lch); 46362306a36Sopenharmony_ci local_irq_restore(flags); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_OMAP) 46762306a36Sopenharmony_civoid omap_start_dma(int lch) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci u32 l; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * The CPC/CDAC register needs to be initialized to zero 47362306a36Sopenharmony_ci * before starting dma transfer. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci if (dma_omap15xx()) 47662306a36Sopenharmony_ci p->dma_write(0, CPC, lch); 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci p->dma_write(0, CDAC, lch); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { 48162306a36Sopenharmony_ci int next_lch, cur_lch; 48262306a36Sopenharmony_ci char dma_chan_link_map[MAX_LOGICAL_DMA_CH_COUNT]; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* Set the link register of the first channel */ 48562306a36Sopenharmony_ci enable_lnk(lch); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); 48862306a36Sopenharmony_ci dma_chan_link_map[lch] = 1; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci cur_lch = dma_chan[lch].next_lch; 49162306a36Sopenharmony_ci do { 49262306a36Sopenharmony_ci next_lch = dma_chan[cur_lch].next_lch; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* The loop case: we've been here already */ 49562306a36Sopenharmony_ci if (dma_chan_link_map[cur_lch]) 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci /* Mark the current channel */ 49862306a36Sopenharmony_ci dma_chan_link_map[cur_lch] = 1; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci enable_lnk(cur_lch); 50162306a36Sopenharmony_ci omap_enable_channel_irq(cur_lch); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci cur_lch = next_lch; 50462306a36Sopenharmony_ci } while (next_lch != -1); 50562306a36Sopenharmony_ci } else if (IS_DMA_ERRATA(DMA_ERRATA_PARALLEL_CHANNELS)) 50662306a36Sopenharmony_ci p->dma_write(lch, CLNK_CTRL, lch); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci omap_enable_channel_irq(lch); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci l = p->dma_read(CCR, lch); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (IS_DMA_ERRATA(DMA_ERRATA_IFRAME_BUFFERING)) 51362306a36Sopenharmony_ci l |= OMAP_DMA_CCR_BUFFERING_DISABLE; 51462306a36Sopenharmony_ci l |= OMAP_DMA_CCR_EN; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* 51762306a36Sopenharmony_ci * As dma_write() uses IO accessors which are weakly ordered, there 51862306a36Sopenharmony_ci * is no guarantee that data in coherent DMA memory will be visible 51962306a36Sopenharmony_ci * to the DMA device. Add a memory barrier here to ensure that any 52062306a36Sopenharmony_ci * such data is visible prior to enabling DMA. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci mb(); 52362306a36Sopenharmony_ci p->dma_write(l, CCR, lch); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci dma_chan[lch].flags |= OMAP_DMA_ACTIVE; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ciEXPORT_SYMBOL(omap_start_dma); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_civoid omap_stop_dma(int lch) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci u32 l; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* Disable all interrupts on the channel */ 53462306a36Sopenharmony_ci omap_disable_channel_irq(lch); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci l = p->dma_read(CCR, lch); 53762306a36Sopenharmony_ci if (IS_DMA_ERRATA(DMA_ERRATA_i541) && 53862306a36Sopenharmony_ci (l & OMAP_DMA_CCR_SEL_SRC_DST_SYNC)) { 53962306a36Sopenharmony_ci int i = 0; 54062306a36Sopenharmony_ci u32 sys_cf; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Configure No-Standby */ 54362306a36Sopenharmony_ci l = p->dma_read(OCP_SYSCONFIG, lch); 54462306a36Sopenharmony_ci sys_cf = l; 54562306a36Sopenharmony_ci l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK; 54662306a36Sopenharmony_ci l |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE); 54762306a36Sopenharmony_ci p->dma_write(l , OCP_SYSCONFIG, 0); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci l = p->dma_read(CCR, lch); 55062306a36Sopenharmony_ci l &= ~OMAP_DMA_CCR_EN; 55162306a36Sopenharmony_ci p->dma_write(l, CCR, lch); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Wait for sDMA FIFO drain */ 55462306a36Sopenharmony_ci l = p->dma_read(CCR, lch); 55562306a36Sopenharmony_ci while (i < 100 && (l & (OMAP_DMA_CCR_RD_ACTIVE | 55662306a36Sopenharmony_ci OMAP_DMA_CCR_WR_ACTIVE))) { 55762306a36Sopenharmony_ci udelay(5); 55862306a36Sopenharmony_ci i++; 55962306a36Sopenharmony_ci l = p->dma_read(CCR, lch); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci if (i >= 100) 56262306a36Sopenharmony_ci pr_err("DMA drain did not complete on lch %d\n", lch); 56362306a36Sopenharmony_ci /* Restore OCP_SYSCONFIG */ 56462306a36Sopenharmony_ci p->dma_write(sys_cf, OCP_SYSCONFIG, lch); 56562306a36Sopenharmony_ci } else { 56662306a36Sopenharmony_ci l &= ~OMAP_DMA_CCR_EN; 56762306a36Sopenharmony_ci p->dma_write(l, CCR, lch); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* 57162306a36Sopenharmony_ci * Ensure that data transferred by DMA is visible to any access 57262306a36Sopenharmony_ci * after DMA has been disabled. This is important for coherent 57362306a36Sopenharmony_ci * DMA regions. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci mb(); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) { 57862306a36Sopenharmony_ci int next_lch, cur_lch = lch; 57962306a36Sopenharmony_ci char dma_chan_link_map[MAX_LOGICAL_DMA_CH_COUNT]; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map)); 58262306a36Sopenharmony_ci do { 58362306a36Sopenharmony_ci /* The loop case: we've been here already */ 58462306a36Sopenharmony_ci if (dma_chan_link_map[cur_lch]) 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci /* Mark the current channel */ 58762306a36Sopenharmony_ci dma_chan_link_map[cur_lch] = 1; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci disable_lnk(cur_lch); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci next_lch = dma_chan[cur_lch].next_lch; 59262306a36Sopenharmony_ci cur_lch = next_lch; 59362306a36Sopenharmony_ci } while (next_lch != -1); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ciEXPORT_SYMBOL(omap_stop_dma); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* 60162306a36Sopenharmony_ci * Allows changing the DMA callback function or data. This may be needed if 60262306a36Sopenharmony_ci * the driver shares a single DMA channel for multiple dma triggers. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci/* 60562306a36Sopenharmony_ci * Returns current physical source address for the given DMA channel. 60662306a36Sopenharmony_ci * If the channel is running the caller must disable interrupts prior calling 60762306a36Sopenharmony_ci * this function and process the returned value before re-enabling interrupt to 60862306a36Sopenharmony_ci * prevent races with the interrupt handler. Note that in continuous mode there 60962306a36Sopenharmony_ci * is a chance for CSSA_L register overflow between the two reads resulting 61062306a36Sopenharmony_ci * in incorrect return value. 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_cidma_addr_t omap_get_dma_src_pos(int lch) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci dma_addr_t offset = 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (dma_omap15xx()) 61762306a36Sopenharmony_ci offset = p->dma_read(CPC, lch); 61862306a36Sopenharmony_ci else 61962306a36Sopenharmony_ci offset = p->dma_read(CSAC, lch); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (IS_DMA_ERRATA(DMA_ERRATA_3_3) && offset == 0) 62262306a36Sopenharmony_ci offset = p->dma_read(CSAC, lch); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (!dma_omap15xx()) { 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * CDAC == 0 indicates that the DMA transfer on the channel has 62762306a36Sopenharmony_ci * not been started (no data has been transferred so far). 62862306a36Sopenharmony_ci * Return the programmed source start address in this case. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci if (likely(p->dma_read(CDAC, lch))) 63162306a36Sopenharmony_ci offset = p->dma_read(CSAC, lch); 63262306a36Sopenharmony_ci else 63362306a36Sopenharmony_ci offset = p->dma_read(CSSA, lch); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci offset |= (p->dma_read(CSSA, lch) & 0xFFFF0000); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return offset; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ciEXPORT_SYMBOL(omap_get_dma_src_pos); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* 64362306a36Sopenharmony_ci * Returns current physical destination address for the given DMA channel. 64462306a36Sopenharmony_ci * If the channel is running the caller must disable interrupts prior calling 64562306a36Sopenharmony_ci * this function and process the returned value before re-enabling interrupt to 64662306a36Sopenharmony_ci * prevent races with the interrupt handler. Note that in continuous mode there 64762306a36Sopenharmony_ci * is a chance for CDSA_L register overflow between the two reads resulting 64862306a36Sopenharmony_ci * in incorrect return value. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_cidma_addr_t omap_get_dma_dst_pos(int lch) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci dma_addr_t offset = 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (dma_omap15xx()) 65562306a36Sopenharmony_ci offset = p->dma_read(CPC, lch); 65662306a36Sopenharmony_ci else 65762306a36Sopenharmony_ci offset = p->dma_read(CDAC, lch); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is 66162306a36Sopenharmony_ci * read before the DMA controller finished disabling the channel. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ci if (!dma_omap15xx() && offset == 0) { 66462306a36Sopenharmony_ci offset = p->dma_read(CDAC, lch); 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * CDAC == 0 indicates that the DMA transfer on the channel has 66762306a36Sopenharmony_ci * not been started (no data has been transferred so far). 66862306a36Sopenharmony_ci * Return the programmed destination start address in this case. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci if (unlikely(!offset)) 67162306a36Sopenharmony_ci offset = p->dma_read(CDSA, lch); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci offset |= (p->dma_read(CDSA, lch) & 0xFFFF0000); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return offset; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ciEXPORT_SYMBOL(omap_get_dma_dst_pos); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ciint omap_get_dma_active_status(int lch) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci return (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN) != 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ciEXPORT_SYMBOL(omap_get_dma_active_status); 68562306a36Sopenharmony_ci#endif 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciint omap_dma_running(void) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci int lch; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (omap_lcd_dma_running()) 69262306a36Sopenharmony_ci return 1; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci for (lch = 0; lch < dma_chan_count; lch++) 69562306a36Sopenharmony_ci if (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN) 69662306a36Sopenharmony_ci return 1; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/ 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int omap1_dma_handle_ch(int ch) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci u32 csr; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (enable_1510_mode && ch >= 6) { 70862306a36Sopenharmony_ci csr = dma_chan[ch].saved_csr; 70962306a36Sopenharmony_ci dma_chan[ch].saved_csr = 0; 71062306a36Sopenharmony_ci } else 71162306a36Sopenharmony_ci csr = p->dma_read(CSR, ch); 71262306a36Sopenharmony_ci if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) { 71362306a36Sopenharmony_ci dma_chan[ch + 6].saved_csr = csr >> 7; 71462306a36Sopenharmony_ci csr &= 0x7f; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci if ((csr & 0x3f) == 0) 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci if (unlikely(dma_chan[ch].dev_id == -1)) { 71962306a36Sopenharmony_ci pr_warn("Spurious interrupt from DMA channel %d (CSR %04x)\n", 72062306a36Sopenharmony_ci ch, csr); 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci if (unlikely(csr & OMAP1_DMA_TOUT_IRQ)) 72462306a36Sopenharmony_ci pr_warn("DMA timeout with device %d\n", dma_chan[ch].dev_id); 72562306a36Sopenharmony_ci if (unlikely(csr & OMAP_DMA_DROP_IRQ)) 72662306a36Sopenharmony_ci pr_warn("DMA synchronization event drop occurred with device %d\n", 72762306a36Sopenharmony_ci dma_chan[ch].dev_id); 72862306a36Sopenharmony_ci if (likely(csr & OMAP_DMA_BLOCK_IRQ)) 72962306a36Sopenharmony_ci dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE; 73062306a36Sopenharmony_ci if (likely(dma_chan[ch].callback != NULL)) 73162306a36Sopenharmony_ci dma_chan[ch].callback(ch, csr, dma_chan[ch].data); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 1; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic irqreturn_t omap1_dma_irq_handler(int irq, void *dev_id) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci int ch = ((int) dev_id) - 1; 73962306a36Sopenharmony_ci int handled = 0; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci for (;;) { 74262306a36Sopenharmony_ci int handled_now = 0; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci handled_now += omap1_dma_handle_ch(ch); 74562306a36Sopenharmony_ci if (enable_1510_mode && dma_chan[ch + 6].saved_csr) 74662306a36Sopenharmony_ci handled_now += omap1_dma_handle_ch(ch + 6); 74762306a36Sopenharmony_ci if (!handled_now) 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci handled += handled_now; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return handled ? IRQ_HANDLED : IRQ_NONE; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistruct omap_system_dma_plat_info *omap_get_plat_info(void) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci return p; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_get_plat_info); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int omap_system_dma_probe(struct platform_device *pdev) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci int ch, ret = 0; 76462306a36Sopenharmony_ci int dma_irq; 76562306a36Sopenharmony_ci char irq_name[4]; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci p = pdev->dev.platform_data; 76862306a36Sopenharmony_ci if (!p) { 76962306a36Sopenharmony_ci dev_err(&pdev->dev, 77062306a36Sopenharmony_ci "%s: System DMA initialized without platform data\n", 77162306a36Sopenharmony_ci __func__); 77262306a36Sopenharmony_ci return -EINVAL; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci d = p->dma_attr; 77662306a36Sopenharmony_ci errata = p->errata; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if ((d->dev_caps & RESERVE_CHANNEL) && omap_dma_reserve_channels 77962306a36Sopenharmony_ci && (omap_dma_reserve_channels < d->lch_count)) 78062306a36Sopenharmony_ci d->lch_count = omap_dma_reserve_channels; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci dma_lch_count = d->lch_count; 78362306a36Sopenharmony_ci dma_chan_count = dma_lch_count; 78462306a36Sopenharmony_ci enable_1510_mode = d->dev_caps & ENABLE_1510_MODE; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci dma_chan = devm_kcalloc(&pdev->dev, dma_lch_count, 78762306a36Sopenharmony_ci sizeof(*dma_chan), GFP_KERNEL); 78862306a36Sopenharmony_ci if (!dma_chan) 78962306a36Sopenharmony_ci return -ENOMEM; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci for (ch = 0; ch < dma_chan_count; ch++) { 79262306a36Sopenharmony_ci omap_clear_dma(ch); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci dma_chan[ch].dev_id = -1; 79562306a36Sopenharmony_ci dma_chan[ch].next_lch = -1; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (ch >= 6 && enable_1510_mode) 79862306a36Sopenharmony_ci continue; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* 80162306a36Sopenharmony_ci * request_irq() doesn't like dev_id (ie. ch) being 80262306a36Sopenharmony_ci * zero, so we have to kludge around this. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci sprintf(&irq_name[0], "%d", ch); 80562306a36Sopenharmony_ci dma_irq = platform_get_irq_byname(pdev, irq_name); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (dma_irq < 0) { 80862306a36Sopenharmony_ci ret = dma_irq; 80962306a36Sopenharmony_ci goto exit_dma_irq_fail; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* INT_DMA_LCD is handled in lcd_dma.c */ 81362306a36Sopenharmony_ci if (dma_irq == INT_DMA_LCD) 81462306a36Sopenharmony_ci continue; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = request_irq(dma_irq, 81762306a36Sopenharmony_ci omap1_dma_irq_handler, 0, "DMA", 81862306a36Sopenharmony_ci (void *) (ch + 1)); 81962306a36Sopenharmony_ci if (ret != 0) 82062306a36Sopenharmony_ci goto exit_dma_irq_fail; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* reserve dma channels 0 and 1 in high security devices on 34xx */ 82462306a36Sopenharmony_ci if (d->dev_caps & HS_CHANNELS_RESERVED) { 82562306a36Sopenharmony_ci pr_info("Reserving DMA channels 0 and 1 for HS ROM code\n"); 82662306a36Sopenharmony_ci dma_chan[0].dev_id = 0; 82762306a36Sopenharmony_ci dma_chan[1].dev_id = 1; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci p->show_dma_caps(); 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciexit_dma_irq_fail: 83362306a36Sopenharmony_ci return ret; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic void omap_system_dma_remove(struct platform_device *pdev) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci int dma_irq, irq_rel = 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for ( ; irq_rel < dma_chan_count; irq_rel++) { 84162306a36Sopenharmony_ci dma_irq = platform_get_irq(pdev, irq_rel); 84262306a36Sopenharmony_ci free_irq(dma_irq, (void *)(irq_rel + 1)); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic struct platform_driver omap_system_dma_driver = { 84762306a36Sopenharmony_ci .probe = omap_system_dma_probe, 84862306a36Sopenharmony_ci .remove_new = omap_system_dma_remove, 84962306a36Sopenharmony_ci .driver = { 85062306a36Sopenharmony_ci .name = "omap_dma_system" 85162306a36Sopenharmony_ci }, 85262306a36Sopenharmony_ci}; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int __init omap_system_dma_init(void) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci return platform_driver_register(&omap_system_dma_driver); 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ciarch_initcall(omap_system_dma_init); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic void __exit omap_system_dma_exit(void) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci platform_driver_unregister(&omap_system_dma_driver); 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP SYSTEM DMA DRIVER"); 86662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 86762306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc"); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/* 87062306a36Sopenharmony_ci * Reserve the omap SDMA channels using cmdline bootarg 87162306a36Sopenharmony_ci * "omap_dma_reserve_ch=". The valid range is 1 to 32 87262306a36Sopenharmony_ci */ 87362306a36Sopenharmony_cistatic int __init omap_dma_cmdline_reserve_ch(char *str) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci if (get_option(&str, &omap_dma_reserve_channels) != 1) 87662306a36Sopenharmony_ci omap_dma_reserve_channels = 0; 87762306a36Sopenharmony_ci return 1; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci__setup("omap_dma_reserve_ch=", omap_dma_cmdline_reserve_ch); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci 883