18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * TI EDMA DMA engine driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2012 Texas Instruments 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 78c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 88c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 118c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 128c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138c2ecf20Sopenharmony_ci * GNU General Public License for more details. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/list.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/of_dma.h> 298c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 308c2ecf20Sopenharmony_ci#include <linux/of_address.h> 318c2ecf20Sopenharmony_ci#include <linux/of_device.h> 328c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/platform_data/edma.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "../dmaengine.h" 378c2ecf20Sopenharmony_ci#include "../virt-dma.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Offsets matching "struct edmacc_param" */ 408c2ecf20Sopenharmony_ci#define PARM_OPT 0x00 418c2ecf20Sopenharmony_ci#define PARM_SRC 0x04 428c2ecf20Sopenharmony_ci#define PARM_A_B_CNT 0x08 438c2ecf20Sopenharmony_ci#define PARM_DST 0x0c 448c2ecf20Sopenharmony_ci#define PARM_SRC_DST_BIDX 0x10 458c2ecf20Sopenharmony_ci#define PARM_LINK_BCNTRLD 0x14 468c2ecf20Sopenharmony_ci#define PARM_SRC_DST_CIDX 0x18 478c2ecf20Sopenharmony_ci#define PARM_CCNT 0x1c 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define PARM_SIZE 0x20 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Offsets for EDMA CC global channel registers and their shadows */ 528c2ecf20Sopenharmony_ci#define SH_ER 0x00 /* 64 bits */ 538c2ecf20Sopenharmony_ci#define SH_ECR 0x08 /* 64 bits */ 548c2ecf20Sopenharmony_ci#define SH_ESR 0x10 /* 64 bits */ 558c2ecf20Sopenharmony_ci#define SH_CER 0x18 /* 64 bits */ 568c2ecf20Sopenharmony_ci#define SH_EER 0x20 /* 64 bits */ 578c2ecf20Sopenharmony_ci#define SH_EECR 0x28 /* 64 bits */ 588c2ecf20Sopenharmony_ci#define SH_EESR 0x30 /* 64 bits */ 598c2ecf20Sopenharmony_ci#define SH_SER 0x38 /* 64 bits */ 608c2ecf20Sopenharmony_ci#define SH_SECR 0x40 /* 64 bits */ 618c2ecf20Sopenharmony_ci#define SH_IER 0x50 /* 64 bits */ 628c2ecf20Sopenharmony_ci#define SH_IECR 0x58 /* 64 bits */ 638c2ecf20Sopenharmony_ci#define SH_IESR 0x60 /* 64 bits */ 648c2ecf20Sopenharmony_ci#define SH_IPR 0x68 /* 64 bits */ 658c2ecf20Sopenharmony_ci#define SH_ICR 0x70 /* 64 bits */ 668c2ecf20Sopenharmony_ci#define SH_IEVAL 0x78 678c2ecf20Sopenharmony_ci#define SH_QER 0x80 688c2ecf20Sopenharmony_ci#define SH_QEER 0x84 698c2ecf20Sopenharmony_ci#define SH_QEECR 0x88 708c2ecf20Sopenharmony_ci#define SH_QEESR 0x8c 718c2ecf20Sopenharmony_ci#define SH_QSER 0x90 728c2ecf20Sopenharmony_ci#define SH_QSECR 0x94 738c2ecf20Sopenharmony_ci#define SH_SIZE 0x200 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Offsets for EDMA CC global registers */ 768c2ecf20Sopenharmony_ci#define EDMA_REV 0x0000 778c2ecf20Sopenharmony_ci#define EDMA_CCCFG 0x0004 788c2ecf20Sopenharmony_ci#define EDMA_QCHMAP 0x0200 /* 8 registers */ 798c2ecf20Sopenharmony_ci#define EDMA_DMAQNUM 0x0240 /* 8 registers (4 on OMAP-L1xx) */ 808c2ecf20Sopenharmony_ci#define EDMA_QDMAQNUM 0x0260 818c2ecf20Sopenharmony_ci#define EDMA_QUETCMAP 0x0280 828c2ecf20Sopenharmony_ci#define EDMA_QUEPRI 0x0284 838c2ecf20Sopenharmony_ci#define EDMA_EMR 0x0300 /* 64 bits */ 848c2ecf20Sopenharmony_ci#define EDMA_EMCR 0x0308 /* 64 bits */ 858c2ecf20Sopenharmony_ci#define EDMA_QEMR 0x0310 868c2ecf20Sopenharmony_ci#define EDMA_QEMCR 0x0314 878c2ecf20Sopenharmony_ci#define EDMA_CCERR 0x0318 888c2ecf20Sopenharmony_ci#define EDMA_CCERRCLR 0x031c 898c2ecf20Sopenharmony_ci#define EDMA_EEVAL 0x0320 908c2ecf20Sopenharmony_ci#define EDMA_DRAE 0x0340 /* 4 x 64 bits*/ 918c2ecf20Sopenharmony_ci#define EDMA_QRAE 0x0380 /* 4 registers */ 928c2ecf20Sopenharmony_ci#define EDMA_QUEEVTENTRY 0x0400 /* 2 x 16 registers */ 938c2ecf20Sopenharmony_ci#define EDMA_QSTAT 0x0600 /* 2 registers */ 948c2ecf20Sopenharmony_ci#define EDMA_QWMTHRA 0x0620 958c2ecf20Sopenharmony_ci#define EDMA_QWMTHRB 0x0624 968c2ecf20Sopenharmony_ci#define EDMA_CCSTAT 0x0640 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define EDMA_M 0x1000 /* global channel registers */ 998c2ecf20Sopenharmony_ci#define EDMA_ECR 0x1008 1008c2ecf20Sopenharmony_ci#define EDMA_ECRH 0x100C 1018c2ecf20Sopenharmony_ci#define EDMA_SHADOW0 0x2000 /* 4 shadow regions */ 1028c2ecf20Sopenharmony_ci#define EDMA_PARM 0x4000 /* PaRAM entries */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define EDMA_DCHMAP 0x0100 /* 64 registers */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* CCCFG register */ 1098c2ecf20Sopenharmony_ci#define GET_NUM_DMACH(x) (x & 0x7) /* bits 0-2 */ 1108c2ecf20Sopenharmony_ci#define GET_NUM_QDMACH(x) ((x & 0x70) >> 4) /* bits 4-6 */ 1118c2ecf20Sopenharmony_ci#define GET_NUM_PAENTRY(x) ((x & 0x7000) >> 12) /* bits 12-14 */ 1128c2ecf20Sopenharmony_ci#define GET_NUM_EVQUE(x) ((x & 0x70000) >> 16) /* bits 16-18 */ 1138c2ecf20Sopenharmony_ci#define GET_NUM_REGN(x) ((x & 0x300000) >> 20) /* bits 20-21 */ 1148c2ecf20Sopenharmony_ci#define CHMAP_EXIST BIT(24) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* CCSTAT register */ 1178c2ecf20Sopenharmony_ci#define EDMA_CCSTAT_ACTV BIT(4) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * Max of 20 segments per channel to conserve PaRAM slots 1218c2ecf20Sopenharmony_ci * Also note that MAX_NR_SG should be atleast the no.of periods 1228c2ecf20Sopenharmony_ci * that are required for ASoC, otherwise DMA prep calls will 1238c2ecf20Sopenharmony_ci * fail. Today davinci-pcm is the only user of this driver and 1248c2ecf20Sopenharmony_ci * requires atleast 17 slots, so we setup the default to 20. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci#define MAX_NR_SG 20 1278c2ecf20Sopenharmony_ci#define EDMA_MAX_SLOTS MAX_NR_SG 1288c2ecf20Sopenharmony_ci#define EDMA_DESCRIPTORS 16 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define EDMA_CHANNEL_ANY -1 /* for edma_alloc_channel() */ 1318c2ecf20Sopenharmony_ci#define EDMA_SLOT_ANY -1 /* for edma_alloc_slot() */ 1328c2ecf20Sopenharmony_ci#define EDMA_CONT_PARAMS_ANY 1001 1338c2ecf20Sopenharmony_ci#define EDMA_CONT_PARAMS_FIXED_EXACT 1002 1348c2ecf20Sopenharmony_ci#define EDMA_CONT_PARAMS_FIXED_NOT_EXACT 1003 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * 64bit array registers are split into two 32bit registers: 1388c2ecf20Sopenharmony_ci * reg0: channel/event 0-31 1398c2ecf20Sopenharmony_ci * reg1: channel/event 32-63 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * bit 5 in the channel number tells the array index (0/1) 1428c2ecf20Sopenharmony_ci * bit 0-4 (0x1f) is the bit offset within the register 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci#define EDMA_REG_ARRAY_INDEX(channel) ((channel) >> 5) 1458c2ecf20Sopenharmony_ci#define EDMA_CHANNEL_BIT(channel) (BIT((channel) & 0x1f)) 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* PaRAM slots are laid out like this */ 1488c2ecf20Sopenharmony_cistruct edmacc_param { 1498c2ecf20Sopenharmony_ci u32 opt; 1508c2ecf20Sopenharmony_ci u32 src; 1518c2ecf20Sopenharmony_ci u32 a_b_cnt; 1528c2ecf20Sopenharmony_ci u32 dst; 1538c2ecf20Sopenharmony_ci u32 src_dst_bidx; 1548c2ecf20Sopenharmony_ci u32 link_bcntrld; 1558c2ecf20Sopenharmony_ci u32 src_dst_cidx; 1568c2ecf20Sopenharmony_ci u32 ccnt; 1578c2ecf20Sopenharmony_ci} __packed; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* fields in edmacc_param.opt */ 1608c2ecf20Sopenharmony_ci#define SAM BIT(0) 1618c2ecf20Sopenharmony_ci#define DAM BIT(1) 1628c2ecf20Sopenharmony_ci#define SYNCDIM BIT(2) 1638c2ecf20Sopenharmony_ci#define STATIC BIT(3) 1648c2ecf20Sopenharmony_ci#define EDMA_FWID (0x07 << 8) 1658c2ecf20Sopenharmony_ci#define TCCMODE BIT(11) 1668c2ecf20Sopenharmony_ci#define EDMA_TCC(t) ((t) << 12) 1678c2ecf20Sopenharmony_ci#define TCINTEN BIT(20) 1688c2ecf20Sopenharmony_ci#define ITCINTEN BIT(21) 1698c2ecf20Sopenharmony_ci#define TCCHEN BIT(22) 1708c2ecf20Sopenharmony_ci#define ITCCHEN BIT(23) 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistruct edma_pset { 1738c2ecf20Sopenharmony_ci u32 len; 1748c2ecf20Sopenharmony_ci dma_addr_t addr; 1758c2ecf20Sopenharmony_ci struct edmacc_param param; 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistruct edma_desc { 1798c2ecf20Sopenharmony_ci struct virt_dma_desc vdesc; 1808c2ecf20Sopenharmony_ci struct list_head node; 1818c2ecf20Sopenharmony_ci enum dma_transfer_direction direction; 1828c2ecf20Sopenharmony_ci int cyclic; 1838c2ecf20Sopenharmony_ci bool polled; 1848c2ecf20Sopenharmony_ci int absync; 1858c2ecf20Sopenharmony_ci int pset_nr; 1868c2ecf20Sopenharmony_ci struct edma_chan *echan; 1878c2ecf20Sopenharmony_ci int processed; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * The following 4 elements are used for residue accounting. 1918c2ecf20Sopenharmony_ci * 1928c2ecf20Sopenharmony_ci * - processed_stat: the number of SG elements we have traversed 1938c2ecf20Sopenharmony_ci * so far to cover accounting. This is updated directly to processed 1948c2ecf20Sopenharmony_ci * during edma_callback and is always <= processed, because processed 1958c2ecf20Sopenharmony_ci * refers to the number of pending transfer (programmed to EDMA 1968c2ecf20Sopenharmony_ci * controller), where as processed_stat tracks number of transfers 1978c2ecf20Sopenharmony_ci * accounted for so far. 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * - residue: The amount of bytes we have left to transfer for this desc 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * - residue_stat: The residue in bytes of data we have covered 2028c2ecf20Sopenharmony_ci * so far for accounting. This is updated directly to residue 2038c2ecf20Sopenharmony_ci * during callbacks to keep it current. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * - sg_len: Tracks the length of the current intermediate transfer, 2068c2ecf20Sopenharmony_ci * this is required to update the residue during intermediate transfer 2078c2ecf20Sopenharmony_ci * completion callback. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci int processed_stat; 2108c2ecf20Sopenharmony_ci u32 sg_len; 2118c2ecf20Sopenharmony_ci u32 residue; 2128c2ecf20Sopenharmony_ci u32 residue_stat; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci struct edma_pset pset[]; 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistruct edma_cc; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct edma_tc { 2208c2ecf20Sopenharmony_ci struct device_node *node; 2218c2ecf20Sopenharmony_ci u16 id; 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistruct edma_chan { 2258c2ecf20Sopenharmony_ci struct virt_dma_chan vchan; 2268c2ecf20Sopenharmony_ci struct list_head node; 2278c2ecf20Sopenharmony_ci struct edma_desc *edesc; 2288c2ecf20Sopenharmony_ci struct edma_cc *ecc; 2298c2ecf20Sopenharmony_ci struct edma_tc *tc; 2308c2ecf20Sopenharmony_ci int ch_num; 2318c2ecf20Sopenharmony_ci bool alloced; 2328c2ecf20Sopenharmony_ci bool hw_triggered; 2338c2ecf20Sopenharmony_ci int slot[EDMA_MAX_SLOTS]; 2348c2ecf20Sopenharmony_ci int missed; 2358c2ecf20Sopenharmony_ci struct dma_slave_config cfg; 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistruct edma_cc { 2398c2ecf20Sopenharmony_ci struct device *dev; 2408c2ecf20Sopenharmony_ci struct edma_soc_info *info; 2418c2ecf20Sopenharmony_ci void __iomem *base; 2428c2ecf20Sopenharmony_ci int id; 2438c2ecf20Sopenharmony_ci bool legacy_mode; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* eDMA3 resource information */ 2468c2ecf20Sopenharmony_ci unsigned num_channels; 2478c2ecf20Sopenharmony_ci unsigned num_qchannels; 2488c2ecf20Sopenharmony_ci unsigned num_region; 2498c2ecf20Sopenharmony_ci unsigned num_slots; 2508c2ecf20Sopenharmony_ci unsigned num_tc; 2518c2ecf20Sopenharmony_ci bool chmap_exist; 2528c2ecf20Sopenharmony_ci enum dma_event_q default_queue; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci unsigned int ccint; 2558c2ecf20Sopenharmony_ci unsigned int ccerrint; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* 2588c2ecf20Sopenharmony_ci * The slot_inuse bit for each PaRAM slot is clear unless the slot is 2598c2ecf20Sopenharmony_ci * in use by Linux or if it is allocated to be used by DSP. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci unsigned long *slot_inuse; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* 2648c2ecf20Sopenharmony_ci * For tracking reserved channels used by DSP. 2658c2ecf20Sopenharmony_ci * If the bit is cleared, the channel is allocated to be used by DSP 2668c2ecf20Sopenharmony_ci * and Linux must not touch it. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci unsigned long *channels_mask; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci struct dma_device dma_slave; 2718c2ecf20Sopenharmony_ci struct dma_device *dma_memcpy; 2728c2ecf20Sopenharmony_ci struct edma_chan *slave_chans; 2738c2ecf20Sopenharmony_ci struct edma_tc *tc_list; 2748c2ecf20Sopenharmony_ci int dummy_slot; 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* dummy param set used to (re)initialize parameter RAM slots */ 2788c2ecf20Sopenharmony_cistatic const struct edmacc_param dummy_paramset = { 2798c2ecf20Sopenharmony_ci .link_bcntrld = 0xffff, 2808c2ecf20Sopenharmony_ci .ccnt = 1, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci#define EDMA_BINDING_LEGACY 0 2848c2ecf20Sopenharmony_ci#define EDMA_BINDING_TPCC 1 2858c2ecf20Sopenharmony_cistatic const u32 edma_binding_type[] = { 2868c2ecf20Sopenharmony_ci [EDMA_BINDING_LEGACY] = EDMA_BINDING_LEGACY, 2878c2ecf20Sopenharmony_ci [EDMA_BINDING_TPCC] = EDMA_BINDING_TPCC, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic const struct of_device_id edma_of_ids[] = { 2918c2ecf20Sopenharmony_ci { 2928c2ecf20Sopenharmony_ci .compatible = "ti,edma3", 2938c2ecf20Sopenharmony_ci .data = &edma_binding_type[EDMA_BINDING_LEGACY], 2948c2ecf20Sopenharmony_ci }, 2958c2ecf20Sopenharmony_ci { 2968c2ecf20Sopenharmony_ci .compatible = "ti,edma3-tpcc", 2978c2ecf20Sopenharmony_ci .data = &edma_binding_type[EDMA_BINDING_TPCC], 2988c2ecf20Sopenharmony_ci }, 2998c2ecf20Sopenharmony_ci {} 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, edma_of_ids); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const struct of_device_id edma_tptc_of_ids[] = { 3048c2ecf20Sopenharmony_ci { .compatible = "ti,edma3-tptc", }, 3058c2ecf20Sopenharmony_ci {} 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, edma_tptc_of_ids); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic inline unsigned int edma_read(struct edma_cc *ecc, int offset) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci return (unsigned int)__raw_readl(ecc->base + offset); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic inline void edma_write(struct edma_cc *ecc, int offset, int val) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci __raw_writel(val, ecc->base + offset); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic inline void edma_modify(struct edma_cc *ecc, int offset, unsigned and, 3208c2ecf20Sopenharmony_ci unsigned or) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci unsigned val = edma_read(ecc, offset); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci val &= and; 3258c2ecf20Sopenharmony_ci val |= or; 3268c2ecf20Sopenharmony_ci edma_write(ecc, offset, val); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic inline void edma_and(struct edma_cc *ecc, int offset, unsigned and) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci unsigned val = edma_read(ecc, offset); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci val &= and; 3348c2ecf20Sopenharmony_ci edma_write(ecc, offset, val); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic inline void edma_or(struct edma_cc *ecc, int offset, unsigned or) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci unsigned val = edma_read(ecc, offset); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci val |= or; 3428c2ecf20Sopenharmony_ci edma_write(ecc, offset, val); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic inline unsigned int edma_read_array(struct edma_cc *ecc, int offset, 3468c2ecf20Sopenharmony_ci int i) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci return edma_read(ecc, offset + (i << 2)); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic inline void edma_write_array(struct edma_cc *ecc, int offset, int i, 3528c2ecf20Sopenharmony_ci unsigned val) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci edma_write(ecc, offset + (i << 2), val); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic inline void edma_modify_array(struct edma_cc *ecc, int offset, int i, 3588c2ecf20Sopenharmony_ci unsigned and, unsigned or) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci edma_modify(ecc, offset + (i << 2), and, or); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic inline void edma_or_array(struct edma_cc *ecc, int offset, int i, 3648c2ecf20Sopenharmony_ci unsigned or) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci edma_or(ecc, offset + (i << 2), or); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic inline void edma_or_array2(struct edma_cc *ecc, int offset, int i, int j, 3708c2ecf20Sopenharmony_ci unsigned or) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci edma_or(ecc, offset + ((i * 2 + j) << 2), or); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic inline void edma_write_array2(struct edma_cc *ecc, int offset, int i, 3768c2ecf20Sopenharmony_ci int j, unsigned val) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci edma_write(ecc, offset + ((i * 2 + j) << 2), val); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic inline unsigned int edma_shadow0_read(struct edma_cc *ecc, int offset) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci return edma_read(ecc, EDMA_SHADOW0 + offset); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic inline unsigned int edma_shadow0_read_array(struct edma_cc *ecc, 3878c2ecf20Sopenharmony_ci int offset, int i) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci return edma_read(ecc, EDMA_SHADOW0 + offset + (i << 2)); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic inline void edma_shadow0_write(struct edma_cc *ecc, int offset, 3938c2ecf20Sopenharmony_ci unsigned val) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_SHADOW0 + offset, val); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic inline void edma_shadow0_write_array(struct edma_cc *ecc, int offset, 3998c2ecf20Sopenharmony_ci int i, unsigned val) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_SHADOW0 + offset + (i << 2), val); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic inline unsigned int edma_param_read(struct edma_cc *ecc, int offset, 4058c2ecf20Sopenharmony_ci int param_no) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci return edma_read(ecc, EDMA_PARM + offset + (param_no << 5)); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic inline void edma_param_write(struct edma_cc *ecc, int offset, 4118c2ecf20Sopenharmony_ci int param_no, unsigned val) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_PARM + offset + (param_no << 5), val); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic inline void edma_param_modify(struct edma_cc *ecc, int offset, 4178c2ecf20Sopenharmony_ci int param_no, unsigned and, unsigned or) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci edma_modify(ecc, EDMA_PARM + offset + (param_no << 5), and, or); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic inline void edma_param_and(struct edma_cc *ecc, int offset, int param_no, 4238c2ecf20Sopenharmony_ci unsigned and) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci edma_and(ecc, EDMA_PARM + offset + (param_no << 5), and); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic inline void edma_param_or(struct edma_cc *ecc, int offset, int param_no, 4298c2ecf20Sopenharmony_ci unsigned or) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci edma_or(ecc, EDMA_PARM + offset + (param_no << 5), or); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void edma_assign_priority_to_queue(struct edma_cc *ecc, int queue_no, 4358c2ecf20Sopenharmony_ci int priority) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int bit = queue_no * 4; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci edma_modify(ecc, EDMA_QUEPRI, ~(0x7 << bit), ((priority & 0x7) << bit)); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void edma_set_chmap(struct edma_chan *echan, int slot) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 4458c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (ecc->chmap_exist) { 4488c2ecf20Sopenharmony_ci slot = EDMA_CHAN_SLOT(slot); 4498c2ecf20Sopenharmony_ci edma_write_array(ecc, EDMA_DCHMAP, channel, (slot << 5)); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void edma_setup_interrupt(struct edma_chan *echan, bool enable) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 4568c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 4578c2ecf20Sopenharmony_ci int idx = EDMA_REG_ARRAY_INDEX(channel); 4588c2ecf20Sopenharmony_ci int ch_bit = EDMA_CHANNEL_BIT(channel); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (enable) { 4618c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_ICR, idx, ch_bit); 4628c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_IESR, idx, ch_bit); 4638c2ecf20Sopenharmony_ci } else { 4648c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_IECR, idx, ch_bit); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* 4698c2ecf20Sopenharmony_ci * paRAM slot management functions 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic void edma_write_slot(struct edma_cc *ecc, unsigned slot, 4728c2ecf20Sopenharmony_ci const struct edmacc_param *param) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci slot = EDMA_CHAN_SLOT(slot); 4758c2ecf20Sopenharmony_ci if (slot >= ecc->num_slots) 4768c2ecf20Sopenharmony_ci return; 4778c2ecf20Sopenharmony_ci memcpy_toio(ecc->base + PARM_OFFSET(slot), param, PARM_SIZE); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int edma_read_slot(struct edma_cc *ecc, unsigned slot, 4818c2ecf20Sopenharmony_ci struct edmacc_param *param) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci slot = EDMA_CHAN_SLOT(slot); 4848c2ecf20Sopenharmony_ci if (slot >= ecc->num_slots) 4858c2ecf20Sopenharmony_ci return -EINVAL; 4868c2ecf20Sopenharmony_ci memcpy_fromio(param, ecc->base + PARM_OFFSET(slot), PARM_SIZE); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/** 4928c2ecf20Sopenharmony_ci * edma_alloc_slot - allocate DMA parameter RAM 4938c2ecf20Sopenharmony_ci * @ecc: pointer to edma_cc struct 4948c2ecf20Sopenharmony_ci * @slot: specific slot to allocate; negative for "any unused slot" 4958c2ecf20Sopenharmony_ci * 4968c2ecf20Sopenharmony_ci * This allocates a parameter RAM slot, initializing it to hold a 4978c2ecf20Sopenharmony_ci * dummy transfer. Slots allocated using this routine have not been 4988c2ecf20Sopenharmony_ci * mapped to a hardware DMA channel, and will normally be used by 4998c2ecf20Sopenharmony_ci * linking to them from a slot associated with a DMA channel. 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * Normal use is to pass EDMA_SLOT_ANY as the @slot, but specific 5028c2ecf20Sopenharmony_ci * slots may be allocated on behalf of DSP firmware. 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * Returns the number of the slot, else negative errno. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_cistatic int edma_alloc_slot(struct edma_cc *ecc, int slot) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci if (slot >= 0) { 5098c2ecf20Sopenharmony_ci slot = EDMA_CHAN_SLOT(slot); 5108c2ecf20Sopenharmony_ci /* Requesting entry paRAM slot for a HW triggered channel. */ 5118c2ecf20Sopenharmony_ci if (ecc->chmap_exist && slot < ecc->num_channels) 5128c2ecf20Sopenharmony_ci slot = EDMA_SLOT_ANY; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (slot < 0) { 5168c2ecf20Sopenharmony_ci if (ecc->chmap_exist) 5178c2ecf20Sopenharmony_ci slot = 0; 5188c2ecf20Sopenharmony_ci else 5198c2ecf20Sopenharmony_ci slot = ecc->num_channels; 5208c2ecf20Sopenharmony_ci for (;;) { 5218c2ecf20Sopenharmony_ci slot = find_next_zero_bit(ecc->slot_inuse, 5228c2ecf20Sopenharmony_ci ecc->num_slots, 5238c2ecf20Sopenharmony_ci slot); 5248c2ecf20Sopenharmony_ci if (slot == ecc->num_slots) 5258c2ecf20Sopenharmony_ci return -ENOMEM; 5268c2ecf20Sopenharmony_ci if (!test_and_set_bit(slot, ecc->slot_inuse)) 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci } else if (slot >= ecc->num_slots) { 5308c2ecf20Sopenharmony_ci return -EINVAL; 5318c2ecf20Sopenharmony_ci } else if (test_and_set_bit(slot, ecc->slot_inuse)) { 5328c2ecf20Sopenharmony_ci return -EBUSY; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci edma_write_slot(ecc, slot, &dummy_paramset); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return EDMA_CTLR_CHAN(ecc->id, slot); 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic void edma_free_slot(struct edma_cc *ecc, unsigned slot) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci slot = EDMA_CHAN_SLOT(slot); 5438c2ecf20Sopenharmony_ci if (slot >= ecc->num_slots) 5448c2ecf20Sopenharmony_ci return; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci edma_write_slot(ecc, slot, &dummy_paramset); 5478c2ecf20Sopenharmony_ci clear_bit(slot, ecc->slot_inuse); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/** 5518c2ecf20Sopenharmony_ci * edma_link - link one parameter RAM slot to another 5528c2ecf20Sopenharmony_ci * @ecc: pointer to edma_cc struct 5538c2ecf20Sopenharmony_ci * @from: parameter RAM slot originating the link 5548c2ecf20Sopenharmony_ci * @to: parameter RAM slot which is the link target 5558c2ecf20Sopenharmony_ci * 5568c2ecf20Sopenharmony_ci * The originating slot should not be part of any active DMA transfer. 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_cistatic void edma_link(struct edma_cc *ecc, unsigned from, unsigned to) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci if (unlikely(EDMA_CTLR(from) != EDMA_CTLR(to))) 5618c2ecf20Sopenharmony_ci dev_warn(ecc->dev, "Ignoring eDMA instance for linking\n"); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci from = EDMA_CHAN_SLOT(from); 5648c2ecf20Sopenharmony_ci to = EDMA_CHAN_SLOT(to); 5658c2ecf20Sopenharmony_ci if (from >= ecc->num_slots || to >= ecc->num_slots) 5668c2ecf20Sopenharmony_ci return; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci edma_param_modify(ecc, PARM_LINK_BCNTRLD, from, 0xffff0000, 5698c2ecf20Sopenharmony_ci PARM_OFFSET(to)); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/** 5738c2ecf20Sopenharmony_ci * edma_get_position - returns the current transfer point 5748c2ecf20Sopenharmony_ci * @ecc: pointer to edma_cc struct 5758c2ecf20Sopenharmony_ci * @slot: parameter RAM slot being examined 5768c2ecf20Sopenharmony_ci * @dst: true selects the dest position, false the source 5778c2ecf20Sopenharmony_ci * 5788c2ecf20Sopenharmony_ci * Returns the position of the current active slot 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_cistatic dma_addr_t edma_get_position(struct edma_cc *ecc, unsigned slot, 5818c2ecf20Sopenharmony_ci bool dst) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci u32 offs; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci slot = EDMA_CHAN_SLOT(slot); 5868c2ecf20Sopenharmony_ci offs = PARM_OFFSET(slot); 5878c2ecf20Sopenharmony_ci offs += dst ? PARM_DST : PARM_SRC; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return edma_read(ecc, offs); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/* 5938c2ecf20Sopenharmony_ci * Channels with event associations will be triggered by their hardware 5948c2ecf20Sopenharmony_ci * events, and channels without such associations will be triggered by 5958c2ecf20Sopenharmony_ci * software. (At this writing there is no interface for using software 5968c2ecf20Sopenharmony_ci * triggers except with channels that don't support hardware triggers.) 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_cistatic void edma_start(struct edma_chan *echan) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 6018c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 6028c2ecf20Sopenharmony_ci int idx = EDMA_REG_ARRAY_INDEX(channel); 6038c2ecf20Sopenharmony_ci int ch_bit = EDMA_CHANNEL_BIT(channel); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (!echan->hw_triggered) { 6068c2ecf20Sopenharmony_ci /* EDMA channels without event association */ 6078c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "ESR%d %08x\n", idx, 6088c2ecf20Sopenharmony_ci edma_shadow0_read_array(ecc, SH_ESR, idx)); 6098c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_ESR, idx, ch_bit); 6108c2ecf20Sopenharmony_ci } else { 6118c2ecf20Sopenharmony_ci /* EDMA channel with event association */ 6128c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "ER%d %08x\n", idx, 6138c2ecf20Sopenharmony_ci edma_shadow0_read_array(ecc, SH_ER, idx)); 6148c2ecf20Sopenharmony_ci /* Clear any pending event or error */ 6158c2ecf20Sopenharmony_ci edma_write_array(ecc, EDMA_ECR, idx, ch_bit); 6168c2ecf20Sopenharmony_ci edma_write_array(ecc, EDMA_EMCR, idx, ch_bit); 6178c2ecf20Sopenharmony_ci /* Clear any SER */ 6188c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_SECR, idx, ch_bit); 6198c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_EESR, idx, ch_bit); 6208c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "EER%d %08x\n", idx, 6218c2ecf20Sopenharmony_ci edma_shadow0_read_array(ecc, SH_EER, idx)); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic void edma_stop(struct edma_chan *echan) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 6288c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 6298c2ecf20Sopenharmony_ci int idx = EDMA_REG_ARRAY_INDEX(channel); 6308c2ecf20Sopenharmony_ci int ch_bit = EDMA_CHANNEL_BIT(channel); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_EECR, idx, ch_bit); 6338c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_ECR, idx, ch_bit); 6348c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_SECR, idx, ch_bit); 6358c2ecf20Sopenharmony_ci edma_write_array(ecc, EDMA_EMCR, idx, ch_bit); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* clear possibly pending completion interrupt */ 6388c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_ICR, idx, ch_bit); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "EER%d %08x\n", idx, 6418c2ecf20Sopenharmony_ci edma_shadow0_read_array(ecc, SH_EER, idx)); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* REVISIT: consider guarding against inappropriate event 6448c2ecf20Sopenharmony_ci * chaining by overwriting with dummy_paramset. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* 6498c2ecf20Sopenharmony_ci * Temporarily disable EDMA hardware events on the specified channel, 6508c2ecf20Sopenharmony_ci * preventing them from triggering new transfers 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic void edma_pause(struct edma_chan *echan) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci edma_shadow0_write_array(echan->ecc, SH_EECR, 6578c2ecf20Sopenharmony_ci EDMA_REG_ARRAY_INDEX(channel), 6588c2ecf20Sopenharmony_ci EDMA_CHANNEL_BIT(channel)); 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/* Re-enable EDMA hardware events on the specified channel. */ 6628c2ecf20Sopenharmony_cistatic void edma_resume(struct edma_chan *echan) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci edma_shadow0_write_array(echan->ecc, SH_EESR, 6678c2ecf20Sopenharmony_ci EDMA_REG_ARRAY_INDEX(channel), 6688c2ecf20Sopenharmony_ci EDMA_CHANNEL_BIT(channel)); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic void edma_trigger_channel(struct edma_chan *echan) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 6748c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 6758c2ecf20Sopenharmony_ci int idx = EDMA_REG_ARRAY_INDEX(channel); 6768c2ecf20Sopenharmony_ci int ch_bit = EDMA_CHANNEL_BIT(channel); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_ESR, idx, ch_bit); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "ESR%d %08x\n", idx, 6818c2ecf20Sopenharmony_ci edma_shadow0_read_array(ecc, SH_ESR, idx)); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic void edma_clean_channel(struct edma_chan *echan) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 6878c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 6888c2ecf20Sopenharmony_ci int idx = EDMA_REG_ARRAY_INDEX(channel); 6898c2ecf20Sopenharmony_ci int ch_bit = EDMA_CHANNEL_BIT(channel); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "EMR%d %08x\n", idx, 6928c2ecf20Sopenharmony_ci edma_read_array(ecc, EDMA_EMR, idx)); 6938c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_ECR, idx, ch_bit); 6948c2ecf20Sopenharmony_ci /* Clear the corresponding EMR bits */ 6958c2ecf20Sopenharmony_ci edma_write_array(ecc, EDMA_EMCR, idx, ch_bit); 6968c2ecf20Sopenharmony_ci /* Clear any SER */ 6978c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_SECR, idx, ch_bit); 6988c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_CCERRCLR, BIT(16) | BIT(1) | BIT(0)); 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci/* Move channel to a specific event queue */ 7028c2ecf20Sopenharmony_cistatic void edma_assign_channel_eventq(struct edma_chan *echan, 7038c2ecf20Sopenharmony_ci enum dma_event_q eventq_no) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 7068c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 7078c2ecf20Sopenharmony_ci int bit = (channel & 0x7) * 4; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* default to low priority queue */ 7108c2ecf20Sopenharmony_ci if (eventq_no == EVENTQ_DEFAULT) 7118c2ecf20Sopenharmony_ci eventq_no = ecc->default_queue; 7128c2ecf20Sopenharmony_ci if (eventq_no >= ecc->num_tc) 7138c2ecf20Sopenharmony_ci return; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci eventq_no &= 7; 7168c2ecf20Sopenharmony_ci edma_modify_array(ecc, EDMA_DMAQNUM, (channel >> 3), ~(0x7 << bit), 7178c2ecf20Sopenharmony_ci eventq_no << bit); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int edma_alloc_channel(struct edma_chan *echan, 7218c2ecf20Sopenharmony_ci enum dma_event_q eventq_no) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 7248c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (!test_bit(echan->ch_num, ecc->channels_mask)) { 7278c2ecf20Sopenharmony_ci dev_err(ecc->dev, "Channel%d is reserved, can not be used!\n", 7288c2ecf20Sopenharmony_ci echan->ch_num); 7298c2ecf20Sopenharmony_ci return -EINVAL; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* ensure access through shadow region 0 */ 7338c2ecf20Sopenharmony_ci edma_or_array2(ecc, EDMA_DRAE, 0, EDMA_REG_ARRAY_INDEX(channel), 7348c2ecf20Sopenharmony_ci EDMA_CHANNEL_BIT(channel)); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* ensure no events are pending */ 7378c2ecf20Sopenharmony_ci edma_stop(echan); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci edma_setup_interrupt(echan, true); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci edma_assign_channel_eventq(echan, eventq_no); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return 0; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic void edma_free_channel(struct edma_chan *echan) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci /* ensure no events are pending */ 7498c2ecf20Sopenharmony_ci edma_stop(echan); 7508c2ecf20Sopenharmony_ci /* REVISIT should probably take out of shadow region 0 */ 7518c2ecf20Sopenharmony_ci edma_setup_interrupt(echan, false); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic inline struct edma_cc *to_edma_cc(struct dma_device *d) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci return container_of(d, struct edma_cc, dma_slave); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic inline struct edma_chan *to_edma_chan(struct dma_chan *c) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci return container_of(c, struct edma_chan, vchan.chan); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic inline struct edma_desc *to_edma_desc(struct dma_async_tx_descriptor *tx) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci return container_of(tx, struct edma_desc, vdesc.tx); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic void edma_desc_free(struct virt_dma_desc *vdesc) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci kfree(container_of(vdesc, struct edma_desc, vdesc)); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/* Dispatch a queued descriptor to the controller (caller holds lock) */ 7758c2ecf20Sopenharmony_cistatic void edma_execute(struct edma_chan *echan) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 7788c2ecf20Sopenharmony_ci struct virt_dma_desc *vdesc; 7798c2ecf20Sopenharmony_ci struct edma_desc *edesc; 7808c2ecf20Sopenharmony_ci struct device *dev = echan->vchan.chan.device->dev; 7818c2ecf20Sopenharmony_ci int i, j, left, nslots; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (!echan->edesc) { 7848c2ecf20Sopenharmony_ci /* Setup is needed for the first transfer */ 7858c2ecf20Sopenharmony_ci vdesc = vchan_next_desc(&echan->vchan); 7868c2ecf20Sopenharmony_ci if (!vdesc) 7878c2ecf20Sopenharmony_ci return; 7888c2ecf20Sopenharmony_ci list_del(&vdesc->node); 7898c2ecf20Sopenharmony_ci echan->edesc = to_edma_desc(&vdesc->tx); 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci edesc = echan->edesc; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* Find out how many left */ 7958c2ecf20Sopenharmony_ci left = edesc->pset_nr - edesc->processed; 7968c2ecf20Sopenharmony_ci nslots = min(MAX_NR_SG, left); 7978c2ecf20Sopenharmony_ci edesc->sg_len = 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Write descriptor PaRAM set(s) */ 8008c2ecf20Sopenharmony_ci for (i = 0; i < nslots; i++) { 8018c2ecf20Sopenharmony_ci j = i + edesc->processed; 8028c2ecf20Sopenharmony_ci edma_write_slot(ecc, echan->slot[i], &edesc->pset[j].param); 8038c2ecf20Sopenharmony_ci edesc->sg_len += edesc->pset[j].len; 8048c2ecf20Sopenharmony_ci dev_vdbg(dev, 8058c2ecf20Sopenharmony_ci "\n pset[%d]:\n" 8068c2ecf20Sopenharmony_ci " chnum\t%d\n" 8078c2ecf20Sopenharmony_ci " slot\t%d\n" 8088c2ecf20Sopenharmony_ci " opt\t%08x\n" 8098c2ecf20Sopenharmony_ci " src\t%08x\n" 8108c2ecf20Sopenharmony_ci " dst\t%08x\n" 8118c2ecf20Sopenharmony_ci " abcnt\t%08x\n" 8128c2ecf20Sopenharmony_ci " ccnt\t%08x\n" 8138c2ecf20Sopenharmony_ci " bidx\t%08x\n" 8148c2ecf20Sopenharmony_ci " cidx\t%08x\n" 8158c2ecf20Sopenharmony_ci " lkrld\t%08x\n", 8168c2ecf20Sopenharmony_ci j, echan->ch_num, echan->slot[i], 8178c2ecf20Sopenharmony_ci edesc->pset[j].param.opt, 8188c2ecf20Sopenharmony_ci edesc->pset[j].param.src, 8198c2ecf20Sopenharmony_ci edesc->pset[j].param.dst, 8208c2ecf20Sopenharmony_ci edesc->pset[j].param.a_b_cnt, 8218c2ecf20Sopenharmony_ci edesc->pset[j].param.ccnt, 8228c2ecf20Sopenharmony_ci edesc->pset[j].param.src_dst_bidx, 8238c2ecf20Sopenharmony_ci edesc->pset[j].param.src_dst_cidx, 8248c2ecf20Sopenharmony_ci edesc->pset[j].param.link_bcntrld); 8258c2ecf20Sopenharmony_ci /* Link to the previous slot if not the last set */ 8268c2ecf20Sopenharmony_ci if (i != (nslots - 1)) 8278c2ecf20Sopenharmony_ci edma_link(ecc, echan->slot[i], echan->slot[i + 1]); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci edesc->processed += nslots; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* 8338c2ecf20Sopenharmony_ci * If this is either the last set in a set of SG-list transactions 8348c2ecf20Sopenharmony_ci * then setup a link to the dummy slot, this results in all future 8358c2ecf20Sopenharmony_ci * events being absorbed and that's OK because we're done 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci if (edesc->processed == edesc->pset_nr) { 8388c2ecf20Sopenharmony_ci if (edesc->cyclic) 8398c2ecf20Sopenharmony_ci edma_link(ecc, echan->slot[nslots - 1], echan->slot[1]); 8408c2ecf20Sopenharmony_ci else 8418c2ecf20Sopenharmony_ci edma_link(ecc, echan->slot[nslots - 1], 8428c2ecf20Sopenharmony_ci echan->ecc->dummy_slot); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (echan->missed) { 8468c2ecf20Sopenharmony_ci /* 8478c2ecf20Sopenharmony_ci * This happens due to setup times between intermediate 8488c2ecf20Sopenharmony_ci * transfers in long SG lists which have to be broken up into 8498c2ecf20Sopenharmony_ci * transfers of MAX_NR_SG 8508c2ecf20Sopenharmony_ci */ 8518c2ecf20Sopenharmony_ci dev_dbg(dev, "missed event on channel %d\n", echan->ch_num); 8528c2ecf20Sopenharmony_ci edma_clean_channel(echan); 8538c2ecf20Sopenharmony_ci edma_stop(echan); 8548c2ecf20Sopenharmony_ci edma_start(echan); 8558c2ecf20Sopenharmony_ci edma_trigger_channel(echan); 8568c2ecf20Sopenharmony_ci echan->missed = 0; 8578c2ecf20Sopenharmony_ci } else if (edesc->processed <= MAX_NR_SG) { 8588c2ecf20Sopenharmony_ci dev_dbg(dev, "first transfer starting on channel %d\n", 8598c2ecf20Sopenharmony_ci echan->ch_num); 8608c2ecf20Sopenharmony_ci edma_start(echan); 8618c2ecf20Sopenharmony_ci } else { 8628c2ecf20Sopenharmony_ci dev_dbg(dev, "chan: %d: completed %d elements, resuming\n", 8638c2ecf20Sopenharmony_ci echan->ch_num, edesc->processed); 8648c2ecf20Sopenharmony_ci edma_resume(echan); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int edma_terminate_all(struct dma_chan *chan) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 8718c2ecf20Sopenharmony_ci unsigned long flags; 8728c2ecf20Sopenharmony_ci LIST_HEAD(head); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci spin_lock_irqsave(&echan->vchan.lock, flags); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* 8778c2ecf20Sopenharmony_ci * Stop DMA activity: we assume the callback will not be called 8788c2ecf20Sopenharmony_ci * after edma_dma() returns (even if it does, it will see 8798c2ecf20Sopenharmony_ci * echan->edesc is NULL and exit.) 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_ci if (echan->edesc) { 8828c2ecf20Sopenharmony_ci edma_stop(echan); 8838c2ecf20Sopenharmony_ci /* Move the cyclic channel back to default queue */ 8848c2ecf20Sopenharmony_ci if (!echan->tc && echan->edesc->cyclic) 8858c2ecf20Sopenharmony_ci edma_assign_channel_eventq(echan, EVENTQ_DEFAULT); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci vchan_terminate_vdesc(&echan->edesc->vdesc); 8888c2ecf20Sopenharmony_ci echan->edesc = NULL; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci vchan_get_all_descriptors(&echan->vchan, &head); 8928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&echan->vchan.lock, flags); 8938c2ecf20Sopenharmony_ci vchan_dma_desc_free_list(&echan->vchan, &head); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic void edma_synchronize(struct dma_chan *chan) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci vchan_synchronize(&echan->vchan); 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int edma_slave_config(struct dma_chan *chan, 9068c2ecf20Sopenharmony_ci struct dma_slave_config *cfg) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || 9118c2ecf20Sopenharmony_ci cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) 9128c2ecf20Sopenharmony_ci return -EINVAL; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (cfg->src_maxburst > chan->device->max_burst || 9158c2ecf20Sopenharmony_ci cfg->dst_maxburst > chan->device->max_burst) 9168c2ecf20Sopenharmony_ci return -EINVAL; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci memcpy(&echan->cfg, cfg, sizeof(echan->cfg)); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci return 0; 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic int edma_dma_pause(struct dma_chan *chan) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (!echan->edesc) 9288c2ecf20Sopenharmony_ci return -EINVAL; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci edma_pause(echan); 9318c2ecf20Sopenharmony_ci return 0; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic int edma_dma_resume(struct dma_chan *chan) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci edma_resume(echan); 9398c2ecf20Sopenharmony_ci return 0; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/* 9438c2ecf20Sopenharmony_ci * A PaRAM set configuration abstraction used by other modes 9448c2ecf20Sopenharmony_ci * @chan: Channel who's PaRAM set we're configuring 9458c2ecf20Sopenharmony_ci * @pset: PaRAM set to initialize and setup. 9468c2ecf20Sopenharmony_ci * @src_addr: Source address of the DMA 9478c2ecf20Sopenharmony_ci * @dst_addr: Destination address of the DMA 9488c2ecf20Sopenharmony_ci * @burst: In units of dev_width, how much to send 9498c2ecf20Sopenharmony_ci * @dev_width: How much is the dev_width 9508c2ecf20Sopenharmony_ci * @dma_length: Total length of the DMA transfer 9518c2ecf20Sopenharmony_ci * @direction: Direction of the transfer 9528c2ecf20Sopenharmony_ci */ 9538c2ecf20Sopenharmony_cistatic int edma_config_pset(struct dma_chan *chan, struct edma_pset *epset, 9548c2ecf20Sopenharmony_ci dma_addr_t src_addr, dma_addr_t dst_addr, u32 burst, 9558c2ecf20Sopenharmony_ci unsigned int acnt, unsigned int dma_length, 9568c2ecf20Sopenharmony_ci enum dma_transfer_direction direction) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 9598c2ecf20Sopenharmony_ci struct device *dev = chan->device->dev; 9608c2ecf20Sopenharmony_ci struct edmacc_param *param = &epset->param; 9618c2ecf20Sopenharmony_ci int bcnt, ccnt, cidx; 9628c2ecf20Sopenharmony_ci int src_bidx, dst_bidx, src_cidx, dst_cidx; 9638c2ecf20Sopenharmony_ci int absync; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* src/dst_maxburst == 0 is the same case as src/dst_maxburst == 1 */ 9668c2ecf20Sopenharmony_ci if (!burst) 9678c2ecf20Sopenharmony_ci burst = 1; 9688c2ecf20Sopenharmony_ci /* 9698c2ecf20Sopenharmony_ci * If the maxburst is equal to the fifo width, use 9708c2ecf20Sopenharmony_ci * A-synced transfers. This allows for large contiguous 9718c2ecf20Sopenharmony_ci * buffer transfers using only one PaRAM set. 9728c2ecf20Sopenharmony_ci */ 9738c2ecf20Sopenharmony_ci if (burst == 1) { 9748c2ecf20Sopenharmony_ci /* 9758c2ecf20Sopenharmony_ci * For the A-sync case, bcnt and ccnt are the remainder 9768c2ecf20Sopenharmony_ci * and quotient respectively of the division of: 9778c2ecf20Sopenharmony_ci * (dma_length / acnt) by (SZ_64K -1). This is so 9788c2ecf20Sopenharmony_ci * that in case bcnt over flows, we have ccnt to use. 9798c2ecf20Sopenharmony_ci * Note: In A-sync tranfer only, bcntrld is used, but it 9808c2ecf20Sopenharmony_ci * only applies for sg_dma_len(sg) >= SZ_64K. 9818c2ecf20Sopenharmony_ci * In this case, the best way adopted is- bccnt for the 9828c2ecf20Sopenharmony_ci * first frame will be the remainder below. Then for 9838c2ecf20Sopenharmony_ci * every successive frame, bcnt will be SZ_64K-1. This 9848c2ecf20Sopenharmony_ci * is assured as bcntrld = 0xffff in end of function. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ci absync = false; 9878c2ecf20Sopenharmony_ci ccnt = dma_length / acnt / (SZ_64K - 1); 9888c2ecf20Sopenharmony_ci bcnt = dma_length / acnt - ccnt * (SZ_64K - 1); 9898c2ecf20Sopenharmony_ci /* 9908c2ecf20Sopenharmony_ci * If bcnt is non-zero, we have a remainder and hence an 9918c2ecf20Sopenharmony_ci * extra frame to transfer, so increment ccnt. 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_ci if (bcnt) 9948c2ecf20Sopenharmony_ci ccnt++; 9958c2ecf20Sopenharmony_ci else 9968c2ecf20Sopenharmony_ci bcnt = SZ_64K - 1; 9978c2ecf20Sopenharmony_ci cidx = acnt; 9988c2ecf20Sopenharmony_ci } else { 9998c2ecf20Sopenharmony_ci /* 10008c2ecf20Sopenharmony_ci * If maxburst is greater than the fifo address_width, 10018c2ecf20Sopenharmony_ci * use AB-synced transfers where A count is the fifo 10028c2ecf20Sopenharmony_ci * address_width and B count is the maxburst. In this 10038c2ecf20Sopenharmony_ci * case, we are limited to transfers of C count frames 10048c2ecf20Sopenharmony_ci * of (address_width * maxburst) where C count is limited 10058c2ecf20Sopenharmony_ci * to SZ_64K-1. This places an upper bound on the length 10068c2ecf20Sopenharmony_ci * of an SG segment that can be handled. 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci absync = true; 10098c2ecf20Sopenharmony_ci bcnt = burst; 10108c2ecf20Sopenharmony_ci ccnt = dma_length / (acnt * bcnt); 10118c2ecf20Sopenharmony_ci if (ccnt > (SZ_64K - 1)) { 10128c2ecf20Sopenharmony_ci dev_err(dev, "Exceeded max SG segment size\n"); 10138c2ecf20Sopenharmony_ci return -EINVAL; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci cidx = acnt * bcnt; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci epset->len = dma_length; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 10218c2ecf20Sopenharmony_ci src_bidx = acnt; 10228c2ecf20Sopenharmony_ci src_cidx = cidx; 10238c2ecf20Sopenharmony_ci dst_bidx = 0; 10248c2ecf20Sopenharmony_ci dst_cidx = 0; 10258c2ecf20Sopenharmony_ci epset->addr = src_addr; 10268c2ecf20Sopenharmony_ci } else if (direction == DMA_DEV_TO_MEM) { 10278c2ecf20Sopenharmony_ci src_bidx = 0; 10288c2ecf20Sopenharmony_ci src_cidx = 0; 10298c2ecf20Sopenharmony_ci dst_bidx = acnt; 10308c2ecf20Sopenharmony_ci dst_cidx = cidx; 10318c2ecf20Sopenharmony_ci epset->addr = dst_addr; 10328c2ecf20Sopenharmony_ci } else if (direction == DMA_MEM_TO_MEM) { 10338c2ecf20Sopenharmony_ci src_bidx = acnt; 10348c2ecf20Sopenharmony_ci src_cidx = cidx; 10358c2ecf20Sopenharmony_ci dst_bidx = acnt; 10368c2ecf20Sopenharmony_ci dst_cidx = cidx; 10378c2ecf20Sopenharmony_ci epset->addr = src_addr; 10388c2ecf20Sopenharmony_ci } else { 10398c2ecf20Sopenharmony_ci dev_err(dev, "%s: direction not implemented yet\n", __func__); 10408c2ecf20Sopenharmony_ci return -EINVAL; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci param->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num)); 10448c2ecf20Sopenharmony_ci /* Configure A or AB synchronized transfers */ 10458c2ecf20Sopenharmony_ci if (absync) 10468c2ecf20Sopenharmony_ci param->opt |= SYNCDIM; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci param->src = src_addr; 10498c2ecf20Sopenharmony_ci param->dst = dst_addr; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci param->src_dst_bidx = (dst_bidx << 16) | src_bidx; 10528c2ecf20Sopenharmony_ci param->src_dst_cidx = (dst_cidx << 16) | src_cidx; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci param->a_b_cnt = bcnt << 16 | acnt; 10558c2ecf20Sopenharmony_ci param->ccnt = ccnt; 10568c2ecf20Sopenharmony_ci /* 10578c2ecf20Sopenharmony_ci * Only time when (bcntrld) auto reload is required is for 10588c2ecf20Sopenharmony_ci * A-sync case, and in this case, a requirement of reload value 10598c2ecf20Sopenharmony_ci * of SZ_64K-1 only is assured. 'link' is initially set to NULL 10608c2ecf20Sopenharmony_ci * and then later will be populated by edma_execute. 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_ci param->link_bcntrld = 0xffffffff; 10638c2ecf20Sopenharmony_ci return absync; 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *edma_prep_slave_sg( 10678c2ecf20Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, 10688c2ecf20Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction direction, 10698c2ecf20Sopenharmony_ci unsigned long tx_flags, void *context) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 10728c2ecf20Sopenharmony_ci struct device *dev = chan->device->dev; 10738c2ecf20Sopenharmony_ci struct edma_desc *edesc; 10748c2ecf20Sopenharmony_ci dma_addr_t src_addr = 0, dst_addr = 0; 10758c2ecf20Sopenharmony_ci enum dma_slave_buswidth dev_width; 10768c2ecf20Sopenharmony_ci u32 burst; 10778c2ecf20Sopenharmony_ci struct scatterlist *sg; 10788c2ecf20Sopenharmony_ci int i, nslots, ret; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (unlikely(!echan || !sgl || !sg_len)) 10818c2ecf20Sopenharmony_ci return NULL; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) { 10848c2ecf20Sopenharmony_ci src_addr = echan->cfg.src_addr; 10858c2ecf20Sopenharmony_ci dev_width = echan->cfg.src_addr_width; 10868c2ecf20Sopenharmony_ci burst = echan->cfg.src_maxburst; 10878c2ecf20Sopenharmony_ci } else if (direction == DMA_MEM_TO_DEV) { 10888c2ecf20Sopenharmony_ci dst_addr = echan->cfg.dst_addr; 10898c2ecf20Sopenharmony_ci dev_width = echan->cfg.dst_addr_width; 10908c2ecf20Sopenharmony_ci burst = echan->cfg.dst_maxburst; 10918c2ecf20Sopenharmony_ci } else { 10928c2ecf20Sopenharmony_ci dev_err(dev, "%s: bad direction: %d\n", __func__, direction); 10938c2ecf20Sopenharmony_ci return NULL; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { 10978c2ecf20Sopenharmony_ci dev_err(dev, "%s: Undefined slave buswidth\n", __func__); 10988c2ecf20Sopenharmony_ci return NULL; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci edesc = kzalloc(struct_size(edesc, pset, sg_len), GFP_ATOMIC); 11028c2ecf20Sopenharmony_ci if (!edesc) 11038c2ecf20Sopenharmony_ci return NULL; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci edesc->pset_nr = sg_len; 11068c2ecf20Sopenharmony_ci edesc->residue = 0; 11078c2ecf20Sopenharmony_ci edesc->direction = direction; 11088c2ecf20Sopenharmony_ci edesc->echan = echan; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* Allocate a PaRAM slot, if needed */ 11118c2ecf20Sopenharmony_ci nslots = min_t(unsigned, MAX_NR_SG, sg_len); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci for (i = 0; i < nslots; i++) { 11148c2ecf20Sopenharmony_ci if (echan->slot[i] < 0) { 11158c2ecf20Sopenharmony_ci echan->slot[i] = 11168c2ecf20Sopenharmony_ci edma_alloc_slot(echan->ecc, EDMA_SLOT_ANY); 11178c2ecf20Sopenharmony_ci if (echan->slot[i] < 0) { 11188c2ecf20Sopenharmony_ci kfree(edesc); 11198c2ecf20Sopenharmony_ci dev_err(dev, "%s: Failed to allocate slot\n", 11208c2ecf20Sopenharmony_ci __func__); 11218c2ecf20Sopenharmony_ci return NULL; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* Configure PaRAM sets for each SG */ 11278c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 11288c2ecf20Sopenharmony_ci /* Get address for each SG */ 11298c2ecf20Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) 11308c2ecf20Sopenharmony_ci dst_addr = sg_dma_address(sg); 11318c2ecf20Sopenharmony_ci else 11328c2ecf20Sopenharmony_ci src_addr = sg_dma_address(sg); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci ret = edma_config_pset(chan, &edesc->pset[i], src_addr, 11358c2ecf20Sopenharmony_ci dst_addr, burst, dev_width, 11368c2ecf20Sopenharmony_ci sg_dma_len(sg), direction); 11378c2ecf20Sopenharmony_ci if (ret < 0) { 11388c2ecf20Sopenharmony_ci kfree(edesc); 11398c2ecf20Sopenharmony_ci return NULL; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci edesc->absync = ret; 11438c2ecf20Sopenharmony_ci edesc->residue += sg_dma_len(sg); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (i == sg_len - 1) 11468c2ecf20Sopenharmony_ci /* Enable completion interrupt */ 11478c2ecf20Sopenharmony_ci edesc->pset[i].param.opt |= TCINTEN; 11488c2ecf20Sopenharmony_ci else if (!((i+1) % MAX_NR_SG)) 11498c2ecf20Sopenharmony_ci /* 11508c2ecf20Sopenharmony_ci * Enable early completion interrupt for the 11518c2ecf20Sopenharmony_ci * intermediateset. In this case the driver will be 11528c2ecf20Sopenharmony_ci * notified when the paRAM set is submitted to TC. This 11538c2ecf20Sopenharmony_ci * will allow more time to set up the next set of slots. 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_ci edesc->pset[i].param.opt |= (TCINTEN | TCCMODE); 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci edesc->residue_stat = edesc->residue; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *edma_prep_dma_memcpy( 11638c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, 11648c2ecf20Sopenharmony_ci size_t len, unsigned long tx_flags) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci int ret, nslots; 11678c2ecf20Sopenharmony_ci struct edma_desc *edesc; 11688c2ecf20Sopenharmony_ci struct device *dev = chan->device->dev; 11698c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 11708c2ecf20Sopenharmony_ci unsigned int width, pset_len, array_size; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (unlikely(!echan || !len)) 11738c2ecf20Sopenharmony_ci return NULL; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* Align the array size (acnt block) with the transfer properties */ 11768c2ecf20Sopenharmony_ci switch (__ffs((src | dest | len))) { 11778c2ecf20Sopenharmony_ci case 0: 11788c2ecf20Sopenharmony_ci array_size = SZ_32K - 1; 11798c2ecf20Sopenharmony_ci break; 11808c2ecf20Sopenharmony_ci case 1: 11818c2ecf20Sopenharmony_ci array_size = SZ_32K - 2; 11828c2ecf20Sopenharmony_ci break; 11838c2ecf20Sopenharmony_ci default: 11848c2ecf20Sopenharmony_ci array_size = SZ_32K - 4; 11858c2ecf20Sopenharmony_ci break; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (len < SZ_64K) { 11898c2ecf20Sopenharmony_ci /* 11908c2ecf20Sopenharmony_ci * Transfer size less than 64K can be handled with one paRAM 11918c2ecf20Sopenharmony_ci * slot and with one burst. 11928c2ecf20Sopenharmony_ci * ACNT = length 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_ci width = len; 11958c2ecf20Sopenharmony_ci pset_len = len; 11968c2ecf20Sopenharmony_ci nslots = 1; 11978c2ecf20Sopenharmony_ci } else { 11988c2ecf20Sopenharmony_ci /* 11998c2ecf20Sopenharmony_ci * Transfer size bigger than 64K will be handled with maximum of 12008c2ecf20Sopenharmony_ci * two paRAM slots. 12018c2ecf20Sopenharmony_ci * slot1: (full_length / 32767) times 32767 bytes bursts. 12028c2ecf20Sopenharmony_ci * ACNT = 32767, length1: (full_length / 32767) * 32767 12038c2ecf20Sopenharmony_ci * slot2: the remaining amount of data after slot1. 12048c2ecf20Sopenharmony_ci * ACNT = full_length - length1, length2 = ACNT 12058c2ecf20Sopenharmony_ci * 12068c2ecf20Sopenharmony_ci * When the full_length is multibple of 32767 one slot can be 12078c2ecf20Sopenharmony_ci * used to complete the transfer. 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci width = array_size; 12108c2ecf20Sopenharmony_ci pset_len = rounddown(len, width); 12118c2ecf20Sopenharmony_ci /* One slot is enough for lengths multiple of (SZ_32K -1) */ 12128c2ecf20Sopenharmony_ci if (unlikely(pset_len == len)) 12138c2ecf20Sopenharmony_ci nslots = 1; 12148c2ecf20Sopenharmony_ci else 12158c2ecf20Sopenharmony_ci nslots = 2; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci edesc = kzalloc(struct_size(edesc, pset, nslots), GFP_ATOMIC); 12198c2ecf20Sopenharmony_ci if (!edesc) 12208c2ecf20Sopenharmony_ci return NULL; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci edesc->pset_nr = nslots; 12238c2ecf20Sopenharmony_ci edesc->residue = edesc->residue_stat = len; 12248c2ecf20Sopenharmony_ci edesc->direction = DMA_MEM_TO_MEM; 12258c2ecf20Sopenharmony_ci edesc->echan = echan; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci ret = edma_config_pset(chan, &edesc->pset[0], src, dest, 1, 12288c2ecf20Sopenharmony_ci width, pset_len, DMA_MEM_TO_MEM); 12298c2ecf20Sopenharmony_ci if (ret < 0) { 12308c2ecf20Sopenharmony_ci kfree(edesc); 12318c2ecf20Sopenharmony_ci return NULL; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci edesc->absync = ret; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci edesc->pset[0].param.opt |= ITCCHEN; 12378c2ecf20Sopenharmony_ci if (nslots == 1) { 12388c2ecf20Sopenharmony_ci /* Enable transfer complete interrupt if requested */ 12398c2ecf20Sopenharmony_ci if (tx_flags & DMA_PREP_INTERRUPT) 12408c2ecf20Sopenharmony_ci edesc->pset[0].param.opt |= TCINTEN; 12418c2ecf20Sopenharmony_ci } else { 12428c2ecf20Sopenharmony_ci /* Enable transfer complete chaining for the first slot */ 12438c2ecf20Sopenharmony_ci edesc->pset[0].param.opt |= TCCHEN; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (echan->slot[1] < 0) { 12468c2ecf20Sopenharmony_ci echan->slot[1] = edma_alloc_slot(echan->ecc, 12478c2ecf20Sopenharmony_ci EDMA_SLOT_ANY); 12488c2ecf20Sopenharmony_ci if (echan->slot[1] < 0) { 12498c2ecf20Sopenharmony_ci kfree(edesc); 12508c2ecf20Sopenharmony_ci dev_err(dev, "%s: Failed to allocate slot\n", 12518c2ecf20Sopenharmony_ci __func__); 12528c2ecf20Sopenharmony_ci return NULL; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci dest += pset_len; 12568c2ecf20Sopenharmony_ci src += pset_len; 12578c2ecf20Sopenharmony_ci pset_len = width = len % array_size; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci ret = edma_config_pset(chan, &edesc->pset[1], src, dest, 1, 12608c2ecf20Sopenharmony_ci width, pset_len, DMA_MEM_TO_MEM); 12618c2ecf20Sopenharmony_ci if (ret < 0) { 12628c2ecf20Sopenharmony_ci kfree(edesc); 12638c2ecf20Sopenharmony_ci return NULL; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci edesc->pset[1].param.opt |= ITCCHEN; 12678c2ecf20Sopenharmony_ci /* Enable transfer complete interrupt if requested */ 12688c2ecf20Sopenharmony_ci if (tx_flags & DMA_PREP_INTERRUPT) 12698c2ecf20Sopenharmony_ci edesc->pset[1].param.opt |= TCINTEN; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (!(tx_flags & DMA_PREP_INTERRUPT)) 12738c2ecf20Sopenharmony_ci edesc->polled = true; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor * 12798c2ecf20Sopenharmony_ciedma_prep_dma_interleaved(struct dma_chan *chan, 12808c2ecf20Sopenharmony_ci struct dma_interleaved_template *xt, 12818c2ecf20Sopenharmony_ci unsigned long tx_flags) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci struct device *dev = chan->device->dev; 12848c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 12858c2ecf20Sopenharmony_ci struct edmacc_param *param; 12868c2ecf20Sopenharmony_ci struct edma_desc *edesc; 12878c2ecf20Sopenharmony_ci size_t src_icg, dst_icg; 12888c2ecf20Sopenharmony_ci int src_bidx, dst_bidx; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* Slave mode is not supported */ 12918c2ecf20Sopenharmony_ci if (is_slave_direction(xt->dir)) 12928c2ecf20Sopenharmony_ci return NULL; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if (xt->frame_size != 1 || xt->numf == 0) 12958c2ecf20Sopenharmony_ci return NULL; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (xt->sgl[0].size > SZ_64K || xt->numf > SZ_64K) 12988c2ecf20Sopenharmony_ci return NULL; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]); 13018c2ecf20Sopenharmony_ci if (src_icg) { 13028c2ecf20Sopenharmony_ci src_bidx = src_icg + xt->sgl[0].size; 13038c2ecf20Sopenharmony_ci } else if (xt->src_inc) { 13048c2ecf20Sopenharmony_ci src_bidx = xt->sgl[0].size; 13058c2ecf20Sopenharmony_ci } else { 13068c2ecf20Sopenharmony_ci dev_err(dev, "%s: SRC constant addressing is not supported\n", 13078c2ecf20Sopenharmony_ci __func__); 13088c2ecf20Sopenharmony_ci return NULL; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]); 13128c2ecf20Sopenharmony_ci if (dst_icg) { 13138c2ecf20Sopenharmony_ci dst_bidx = dst_icg + xt->sgl[0].size; 13148c2ecf20Sopenharmony_ci } else if (xt->dst_inc) { 13158c2ecf20Sopenharmony_ci dst_bidx = xt->sgl[0].size; 13168c2ecf20Sopenharmony_ci } else { 13178c2ecf20Sopenharmony_ci dev_err(dev, "%s: DST constant addressing is not supported\n", 13188c2ecf20Sopenharmony_ci __func__); 13198c2ecf20Sopenharmony_ci return NULL; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (src_bidx > SZ_64K || dst_bidx > SZ_64K) 13238c2ecf20Sopenharmony_ci return NULL; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci edesc = kzalloc(struct_size(edesc, pset, 1), GFP_ATOMIC); 13268c2ecf20Sopenharmony_ci if (!edesc) 13278c2ecf20Sopenharmony_ci return NULL; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci edesc->direction = DMA_MEM_TO_MEM; 13308c2ecf20Sopenharmony_ci edesc->echan = echan; 13318c2ecf20Sopenharmony_ci edesc->pset_nr = 1; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci param = &edesc->pset[0].param; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci param->src = xt->src_start; 13368c2ecf20Sopenharmony_ci param->dst = xt->dst_start; 13378c2ecf20Sopenharmony_ci param->a_b_cnt = xt->numf << 16 | xt->sgl[0].size; 13388c2ecf20Sopenharmony_ci param->ccnt = 1; 13398c2ecf20Sopenharmony_ci param->src_dst_bidx = (dst_bidx << 16) | src_bidx; 13408c2ecf20Sopenharmony_ci param->src_dst_cidx = 0; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci param->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num)); 13438c2ecf20Sopenharmony_ci param->opt |= ITCCHEN; 13448c2ecf20Sopenharmony_ci /* Enable transfer complete interrupt if requested */ 13458c2ecf20Sopenharmony_ci if (tx_flags & DMA_PREP_INTERRUPT) 13468c2ecf20Sopenharmony_ci param->opt |= TCINTEN; 13478c2ecf20Sopenharmony_ci else 13488c2ecf20Sopenharmony_ci edesc->polled = true; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *edma_prep_dma_cyclic( 13548c2ecf20Sopenharmony_ci struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, 13558c2ecf20Sopenharmony_ci size_t period_len, enum dma_transfer_direction direction, 13568c2ecf20Sopenharmony_ci unsigned long tx_flags) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 13598c2ecf20Sopenharmony_ci struct device *dev = chan->device->dev; 13608c2ecf20Sopenharmony_ci struct edma_desc *edesc; 13618c2ecf20Sopenharmony_ci dma_addr_t src_addr, dst_addr; 13628c2ecf20Sopenharmony_ci enum dma_slave_buswidth dev_width; 13638c2ecf20Sopenharmony_ci bool use_intermediate = false; 13648c2ecf20Sopenharmony_ci u32 burst; 13658c2ecf20Sopenharmony_ci int i, ret, nslots; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci if (unlikely(!echan || !buf_len || !period_len)) 13688c2ecf20Sopenharmony_ci return NULL; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) { 13718c2ecf20Sopenharmony_ci src_addr = echan->cfg.src_addr; 13728c2ecf20Sopenharmony_ci dst_addr = buf_addr; 13738c2ecf20Sopenharmony_ci dev_width = echan->cfg.src_addr_width; 13748c2ecf20Sopenharmony_ci burst = echan->cfg.src_maxburst; 13758c2ecf20Sopenharmony_ci } else if (direction == DMA_MEM_TO_DEV) { 13768c2ecf20Sopenharmony_ci src_addr = buf_addr; 13778c2ecf20Sopenharmony_ci dst_addr = echan->cfg.dst_addr; 13788c2ecf20Sopenharmony_ci dev_width = echan->cfg.dst_addr_width; 13798c2ecf20Sopenharmony_ci burst = echan->cfg.dst_maxburst; 13808c2ecf20Sopenharmony_ci } else { 13818c2ecf20Sopenharmony_ci dev_err(dev, "%s: bad direction: %d\n", __func__, direction); 13828c2ecf20Sopenharmony_ci return NULL; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { 13868c2ecf20Sopenharmony_ci dev_err(dev, "%s: Undefined slave buswidth\n", __func__); 13878c2ecf20Sopenharmony_ci return NULL; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if (unlikely(buf_len % period_len)) { 13918c2ecf20Sopenharmony_ci dev_err(dev, "Period should be multiple of Buffer length\n"); 13928c2ecf20Sopenharmony_ci return NULL; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci nslots = (buf_len / period_len) + 1; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* 13988c2ecf20Sopenharmony_ci * Cyclic DMA users such as audio cannot tolerate delays introduced 13998c2ecf20Sopenharmony_ci * by cases where the number of periods is more than the maximum 14008c2ecf20Sopenharmony_ci * number of SGs the EDMA driver can handle at a time. For DMA types 14018c2ecf20Sopenharmony_ci * such as Slave SGs, such delays are tolerable and synchronized, 14028c2ecf20Sopenharmony_ci * but the synchronization is difficult to achieve with Cyclic and 14038c2ecf20Sopenharmony_ci * cannot be guaranteed, so we error out early. 14048c2ecf20Sopenharmony_ci */ 14058c2ecf20Sopenharmony_ci if (nslots > MAX_NR_SG) { 14068c2ecf20Sopenharmony_ci /* 14078c2ecf20Sopenharmony_ci * If the burst and period sizes are the same, we can put 14088c2ecf20Sopenharmony_ci * the full buffer into a single period and activate 14098c2ecf20Sopenharmony_ci * intermediate interrupts. This will produce interrupts 14108c2ecf20Sopenharmony_ci * after each burst, which is also after each desired period. 14118c2ecf20Sopenharmony_ci */ 14128c2ecf20Sopenharmony_ci if (burst == period_len) { 14138c2ecf20Sopenharmony_ci period_len = buf_len; 14148c2ecf20Sopenharmony_ci nslots = 2; 14158c2ecf20Sopenharmony_ci use_intermediate = true; 14168c2ecf20Sopenharmony_ci } else { 14178c2ecf20Sopenharmony_ci return NULL; 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci edesc = kzalloc(struct_size(edesc, pset, nslots), GFP_ATOMIC); 14228c2ecf20Sopenharmony_ci if (!edesc) 14238c2ecf20Sopenharmony_ci return NULL; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci edesc->cyclic = 1; 14268c2ecf20Sopenharmony_ci edesc->pset_nr = nslots; 14278c2ecf20Sopenharmony_ci edesc->residue = edesc->residue_stat = buf_len; 14288c2ecf20Sopenharmony_ci edesc->direction = direction; 14298c2ecf20Sopenharmony_ci edesc->echan = echan; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", 14328c2ecf20Sopenharmony_ci __func__, echan->ch_num, nslots, period_len, buf_len); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci for (i = 0; i < nslots; i++) { 14358c2ecf20Sopenharmony_ci /* Allocate a PaRAM slot, if needed */ 14368c2ecf20Sopenharmony_ci if (echan->slot[i] < 0) { 14378c2ecf20Sopenharmony_ci echan->slot[i] = 14388c2ecf20Sopenharmony_ci edma_alloc_slot(echan->ecc, EDMA_SLOT_ANY); 14398c2ecf20Sopenharmony_ci if (echan->slot[i] < 0) { 14408c2ecf20Sopenharmony_ci kfree(edesc); 14418c2ecf20Sopenharmony_ci dev_err(dev, "%s: Failed to allocate slot\n", 14428c2ecf20Sopenharmony_ci __func__); 14438c2ecf20Sopenharmony_ci return NULL; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (i == nslots - 1) { 14488c2ecf20Sopenharmony_ci memcpy(&edesc->pset[i], &edesc->pset[0], 14498c2ecf20Sopenharmony_ci sizeof(edesc->pset[0])); 14508c2ecf20Sopenharmony_ci break; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci ret = edma_config_pset(chan, &edesc->pset[i], src_addr, 14548c2ecf20Sopenharmony_ci dst_addr, burst, dev_width, period_len, 14558c2ecf20Sopenharmony_ci direction); 14568c2ecf20Sopenharmony_ci if (ret < 0) { 14578c2ecf20Sopenharmony_ci kfree(edesc); 14588c2ecf20Sopenharmony_ci return NULL; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) 14628c2ecf20Sopenharmony_ci dst_addr += period_len; 14638c2ecf20Sopenharmony_ci else 14648c2ecf20Sopenharmony_ci src_addr += period_len; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci dev_vdbg(dev, "%s: Configure period %d of buf:\n", __func__, i); 14678c2ecf20Sopenharmony_ci dev_vdbg(dev, 14688c2ecf20Sopenharmony_ci "\n pset[%d]:\n" 14698c2ecf20Sopenharmony_ci " chnum\t%d\n" 14708c2ecf20Sopenharmony_ci " slot\t%d\n" 14718c2ecf20Sopenharmony_ci " opt\t%08x\n" 14728c2ecf20Sopenharmony_ci " src\t%08x\n" 14738c2ecf20Sopenharmony_ci " dst\t%08x\n" 14748c2ecf20Sopenharmony_ci " abcnt\t%08x\n" 14758c2ecf20Sopenharmony_ci " ccnt\t%08x\n" 14768c2ecf20Sopenharmony_ci " bidx\t%08x\n" 14778c2ecf20Sopenharmony_ci " cidx\t%08x\n" 14788c2ecf20Sopenharmony_ci " lkrld\t%08x\n", 14798c2ecf20Sopenharmony_ci i, echan->ch_num, echan->slot[i], 14808c2ecf20Sopenharmony_ci edesc->pset[i].param.opt, 14818c2ecf20Sopenharmony_ci edesc->pset[i].param.src, 14828c2ecf20Sopenharmony_ci edesc->pset[i].param.dst, 14838c2ecf20Sopenharmony_ci edesc->pset[i].param.a_b_cnt, 14848c2ecf20Sopenharmony_ci edesc->pset[i].param.ccnt, 14858c2ecf20Sopenharmony_ci edesc->pset[i].param.src_dst_bidx, 14868c2ecf20Sopenharmony_ci edesc->pset[i].param.src_dst_cidx, 14878c2ecf20Sopenharmony_ci edesc->pset[i].param.link_bcntrld); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci edesc->absync = ret; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci /* 14928c2ecf20Sopenharmony_ci * Enable period interrupt only if it is requested 14938c2ecf20Sopenharmony_ci */ 14948c2ecf20Sopenharmony_ci if (tx_flags & DMA_PREP_INTERRUPT) { 14958c2ecf20Sopenharmony_ci edesc->pset[i].param.opt |= TCINTEN; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci /* Also enable intermediate interrupts if necessary */ 14988c2ecf20Sopenharmony_ci if (use_intermediate) 14998c2ecf20Sopenharmony_ci edesc->pset[i].param.opt |= ITCINTEN; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci /* Place the cyclic channel to highest priority queue */ 15048c2ecf20Sopenharmony_ci if (!echan->tc) 15058c2ecf20Sopenharmony_ci edma_assign_channel_eventq(echan, EVENTQ_0); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic void edma_completion_handler(struct edma_chan *echan) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci struct device *dev = echan->vchan.chan.device->dev; 15138c2ecf20Sopenharmony_ci struct edma_desc *edesc; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci spin_lock(&echan->vchan.lock); 15168c2ecf20Sopenharmony_ci edesc = echan->edesc; 15178c2ecf20Sopenharmony_ci if (edesc) { 15188c2ecf20Sopenharmony_ci if (edesc->cyclic) { 15198c2ecf20Sopenharmony_ci vchan_cyclic_callback(&edesc->vdesc); 15208c2ecf20Sopenharmony_ci spin_unlock(&echan->vchan.lock); 15218c2ecf20Sopenharmony_ci return; 15228c2ecf20Sopenharmony_ci } else if (edesc->processed == edesc->pset_nr) { 15238c2ecf20Sopenharmony_ci edesc->residue = 0; 15248c2ecf20Sopenharmony_ci edma_stop(echan); 15258c2ecf20Sopenharmony_ci vchan_cookie_complete(&edesc->vdesc); 15268c2ecf20Sopenharmony_ci echan->edesc = NULL; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci dev_dbg(dev, "Transfer completed on channel %d\n", 15298c2ecf20Sopenharmony_ci echan->ch_num); 15308c2ecf20Sopenharmony_ci } else { 15318c2ecf20Sopenharmony_ci dev_dbg(dev, "Sub transfer completed on channel %d\n", 15328c2ecf20Sopenharmony_ci echan->ch_num); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci edma_pause(echan); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* Update statistics for tx_status */ 15378c2ecf20Sopenharmony_ci edesc->residue -= edesc->sg_len; 15388c2ecf20Sopenharmony_ci edesc->residue_stat = edesc->residue; 15398c2ecf20Sopenharmony_ci edesc->processed_stat = edesc->processed; 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci edma_execute(echan); 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci spin_unlock(&echan->vchan.lock); 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci/* eDMA interrupt handler */ 15488c2ecf20Sopenharmony_cistatic irqreturn_t dma_irq_handler(int irq, void *data) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct edma_cc *ecc = data; 15518c2ecf20Sopenharmony_ci int ctlr; 15528c2ecf20Sopenharmony_ci u32 sh_ier; 15538c2ecf20Sopenharmony_ci u32 sh_ipr; 15548c2ecf20Sopenharmony_ci u32 bank; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci ctlr = ecc->id; 15578c2ecf20Sopenharmony_ci if (ctlr < 0) 15588c2ecf20Sopenharmony_ci return IRQ_NONE; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci dev_vdbg(ecc->dev, "dma_irq_handler\n"); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci sh_ipr = edma_shadow0_read_array(ecc, SH_IPR, 0); 15638c2ecf20Sopenharmony_ci if (!sh_ipr) { 15648c2ecf20Sopenharmony_ci sh_ipr = edma_shadow0_read_array(ecc, SH_IPR, 1); 15658c2ecf20Sopenharmony_ci if (!sh_ipr) 15668c2ecf20Sopenharmony_ci return IRQ_NONE; 15678c2ecf20Sopenharmony_ci sh_ier = edma_shadow0_read_array(ecc, SH_IER, 1); 15688c2ecf20Sopenharmony_ci bank = 1; 15698c2ecf20Sopenharmony_ci } else { 15708c2ecf20Sopenharmony_ci sh_ier = edma_shadow0_read_array(ecc, SH_IER, 0); 15718c2ecf20Sopenharmony_ci bank = 0; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci do { 15758c2ecf20Sopenharmony_ci u32 slot; 15768c2ecf20Sopenharmony_ci u32 channel; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci slot = __ffs(sh_ipr); 15798c2ecf20Sopenharmony_ci sh_ipr &= ~(BIT(slot)); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (sh_ier & BIT(slot)) { 15828c2ecf20Sopenharmony_ci channel = (bank << 5) | slot; 15838c2ecf20Sopenharmony_ci /* Clear the corresponding IPR bits */ 15848c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_ICR, bank, BIT(slot)); 15858c2ecf20Sopenharmony_ci edma_completion_handler(&ecc->slave_chans[channel]); 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci } while (sh_ipr); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci edma_shadow0_write(ecc, SH_IEVAL, 1); 15908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic void edma_error_handler(struct edma_chan *echan) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 15968c2ecf20Sopenharmony_ci struct device *dev = echan->vchan.chan.device->dev; 15978c2ecf20Sopenharmony_ci struct edmacc_param p; 15988c2ecf20Sopenharmony_ci int err; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (!echan->edesc) 16018c2ecf20Sopenharmony_ci return; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci spin_lock(&echan->vchan.lock); 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci err = edma_read_slot(ecc, echan->slot[0], &p); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci /* 16088c2ecf20Sopenharmony_ci * Issue later based on missed flag which will be sure 16098c2ecf20Sopenharmony_ci * to happen as: 16108c2ecf20Sopenharmony_ci * (1) we finished transmitting an intermediate slot and 16118c2ecf20Sopenharmony_ci * edma_execute is coming up. 16128c2ecf20Sopenharmony_ci * (2) or we finished current transfer and issue will 16138c2ecf20Sopenharmony_ci * call edma_execute. 16148c2ecf20Sopenharmony_ci * 16158c2ecf20Sopenharmony_ci * Important note: issuing can be dangerous here and 16168c2ecf20Sopenharmony_ci * lead to some nasty recursion when we are in a NULL 16178c2ecf20Sopenharmony_ci * slot. So we avoid doing so and set the missed flag. 16188c2ecf20Sopenharmony_ci */ 16198c2ecf20Sopenharmony_ci if (err || (p.a_b_cnt == 0 && p.ccnt == 0)) { 16208c2ecf20Sopenharmony_ci dev_dbg(dev, "Error on null slot, setting miss\n"); 16218c2ecf20Sopenharmony_ci echan->missed = 1; 16228c2ecf20Sopenharmony_ci } else { 16238c2ecf20Sopenharmony_ci /* 16248c2ecf20Sopenharmony_ci * The slot is already programmed but the event got 16258c2ecf20Sopenharmony_ci * missed, so its safe to issue it here. 16268c2ecf20Sopenharmony_ci */ 16278c2ecf20Sopenharmony_ci dev_dbg(dev, "Missed event, TRIGGERING\n"); 16288c2ecf20Sopenharmony_ci edma_clean_channel(echan); 16298c2ecf20Sopenharmony_ci edma_stop(echan); 16308c2ecf20Sopenharmony_ci edma_start(echan); 16318c2ecf20Sopenharmony_ci edma_trigger_channel(echan); 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci spin_unlock(&echan->vchan.lock); 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cistatic inline bool edma_error_pending(struct edma_cc *ecc) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci if (edma_read_array(ecc, EDMA_EMR, 0) || 16398c2ecf20Sopenharmony_ci edma_read_array(ecc, EDMA_EMR, 1) || 16408c2ecf20Sopenharmony_ci edma_read(ecc, EDMA_QEMR) || edma_read(ecc, EDMA_CCERR)) 16418c2ecf20Sopenharmony_ci return true; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci return false; 16448c2ecf20Sopenharmony_ci} 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci/* eDMA error interrupt handler */ 16478c2ecf20Sopenharmony_cistatic irqreturn_t dma_ccerr_handler(int irq, void *data) 16488c2ecf20Sopenharmony_ci{ 16498c2ecf20Sopenharmony_ci struct edma_cc *ecc = data; 16508c2ecf20Sopenharmony_ci int i, j; 16518c2ecf20Sopenharmony_ci int ctlr; 16528c2ecf20Sopenharmony_ci unsigned int cnt = 0; 16538c2ecf20Sopenharmony_ci unsigned int val; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci ctlr = ecc->id; 16568c2ecf20Sopenharmony_ci if (ctlr < 0) 16578c2ecf20Sopenharmony_ci return IRQ_NONE; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci dev_vdbg(ecc->dev, "dma_ccerr_handler\n"); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci if (!edma_error_pending(ecc)) { 16628c2ecf20Sopenharmony_ci /* 16638c2ecf20Sopenharmony_ci * The registers indicate no pending error event but the irq 16648c2ecf20Sopenharmony_ci * handler has been called. 16658c2ecf20Sopenharmony_ci * Ask eDMA to re-evaluate the error registers. 16668c2ecf20Sopenharmony_ci */ 16678c2ecf20Sopenharmony_ci dev_err(ecc->dev, "%s: Error interrupt without error event!\n", 16688c2ecf20Sopenharmony_ci __func__); 16698c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_EEVAL, 1); 16708c2ecf20Sopenharmony_ci return IRQ_NONE; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci while (1) { 16748c2ecf20Sopenharmony_ci /* Event missed register(s) */ 16758c2ecf20Sopenharmony_ci for (j = 0; j < 2; j++) { 16768c2ecf20Sopenharmony_ci unsigned long emr; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci val = edma_read_array(ecc, EDMA_EMR, j); 16798c2ecf20Sopenharmony_ci if (!val) 16808c2ecf20Sopenharmony_ci continue; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "EMR%d 0x%08x\n", j, val); 16838c2ecf20Sopenharmony_ci emr = val; 16848c2ecf20Sopenharmony_ci for (i = find_next_bit(&emr, 32, 0); i < 32; 16858c2ecf20Sopenharmony_ci i = find_next_bit(&emr, 32, i + 1)) { 16868c2ecf20Sopenharmony_ci int k = (j << 5) + i; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* Clear the corresponding EMR bits */ 16898c2ecf20Sopenharmony_ci edma_write_array(ecc, EDMA_EMCR, j, BIT(i)); 16908c2ecf20Sopenharmony_ci /* Clear any SER */ 16918c2ecf20Sopenharmony_ci edma_shadow0_write_array(ecc, SH_SECR, j, 16928c2ecf20Sopenharmony_ci BIT(i)); 16938c2ecf20Sopenharmony_ci edma_error_handler(&ecc->slave_chans[k]); 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci val = edma_read(ecc, EDMA_QEMR); 16988c2ecf20Sopenharmony_ci if (val) { 16998c2ecf20Sopenharmony_ci dev_dbg(ecc->dev, "QEMR 0x%02x\n", val); 17008c2ecf20Sopenharmony_ci /* Not reported, just clear the interrupt reason. */ 17018c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_QEMCR, val); 17028c2ecf20Sopenharmony_ci edma_shadow0_write(ecc, SH_QSECR, val); 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci val = edma_read(ecc, EDMA_CCERR); 17068c2ecf20Sopenharmony_ci if (val) { 17078c2ecf20Sopenharmony_ci dev_warn(ecc->dev, "CCERR 0x%08x\n", val); 17088c2ecf20Sopenharmony_ci /* Not reported, just clear the interrupt reason. */ 17098c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_CCERRCLR, val); 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci if (!edma_error_pending(ecc)) 17138c2ecf20Sopenharmony_ci break; 17148c2ecf20Sopenharmony_ci cnt++; 17158c2ecf20Sopenharmony_ci if (cnt > 10) 17168c2ecf20Sopenharmony_ci break; 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci edma_write(ecc, EDMA_EEVAL, 1); 17198c2ecf20Sopenharmony_ci return IRQ_HANDLED; 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci/* Alloc channel resources */ 17238c2ecf20Sopenharmony_cistatic int edma_alloc_chan_resources(struct dma_chan *chan) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 17268c2ecf20Sopenharmony_ci struct edma_cc *ecc = echan->ecc; 17278c2ecf20Sopenharmony_ci struct device *dev = ecc->dev; 17288c2ecf20Sopenharmony_ci enum dma_event_q eventq_no = EVENTQ_DEFAULT; 17298c2ecf20Sopenharmony_ci int ret; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (echan->tc) { 17328c2ecf20Sopenharmony_ci eventq_no = echan->tc->id; 17338c2ecf20Sopenharmony_ci } else if (ecc->tc_list) { 17348c2ecf20Sopenharmony_ci /* memcpy channel */ 17358c2ecf20Sopenharmony_ci echan->tc = &ecc->tc_list[ecc->info->default_queue]; 17368c2ecf20Sopenharmony_ci eventq_no = echan->tc->id; 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci ret = edma_alloc_channel(echan, eventq_no); 17408c2ecf20Sopenharmony_ci if (ret) 17418c2ecf20Sopenharmony_ci return ret; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci echan->slot[0] = edma_alloc_slot(ecc, echan->ch_num); 17448c2ecf20Sopenharmony_ci if (echan->slot[0] < 0) { 17458c2ecf20Sopenharmony_ci dev_err(dev, "Entry slot allocation failed for channel %u\n", 17468c2ecf20Sopenharmony_ci EDMA_CHAN_SLOT(echan->ch_num)); 17478c2ecf20Sopenharmony_ci ret = echan->slot[0]; 17488c2ecf20Sopenharmony_ci goto err_slot; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci /* Set up channel -> slot mapping for the entry slot */ 17528c2ecf20Sopenharmony_ci edma_set_chmap(echan, echan->slot[0]); 17538c2ecf20Sopenharmony_ci echan->alloced = true; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci dev_dbg(dev, "Got eDMA channel %d for virt channel %d (%s trigger)\n", 17568c2ecf20Sopenharmony_ci EDMA_CHAN_SLOT(echan->ch_num), chan->chan_id, 17578c2ecf20Sopenharmony_ci echan->hw_triggered ? "HW" : "SW"); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci return 0; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_cierr_slot: 17628c2ecf20Sopenharmony_ci edma_free_channel(echan); 17638c2ecf20Sopenharmony_ci return ret; 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci/* Free channel resources */ 17678c2ecf20Sopenharmony_cistatic void edma_free_chan_resources(struct dma_chan *chan) 17688c2ecf20Sopenharmony_ci{ 17698c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 17708c2ecf20Sopenharmony_ci struct device *dev = echan->ecc->dev; 17718c2ecf20Sopenharmony_ci int i; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci /* Terminate transfers */ 17748c2ecf20Sopenharmony_ci edma_stop(echan); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci vchan_free_chan_resources(&echan->vchan); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* Free EDMA PaRAM slots */ 17798c2ecf20Sopenharmony_ci for (i = 0; i < EDMA_MAX_SLOTS; i++) { 17808c2ecf20Sopenharmony_ci if (echan->slot[i] >= 0) { 17818c2ecf20Sopenharmony_ci edma_free_slot(echan->ecc, echan->slot[i]); 17828c2ecf20Sopenharmony_ci echan->slot[i] = -1; 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* Set entry slot to the dummy slot */ 17878c2ecf20Sopenharmony_ci edma_set_chmap(echan, echan->ecc->dummy_slot); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* Free EDMA channel */ 17908c2ecf20Sopenharmony_ci if (echan->alloced) { 17918c2ecf20Sopenharmony_ci edma_free_channel(echan); 17928c2ecf20Sopenharmony_ci echan->alloced = false; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci echan->tc = NULL; 17968c2ecf20Sopenharmony_ci echan->hw_triggered = false; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci dev_dbg(dev, "Free eDMA channel %d for virt channel %d\n", 17998c2ecf20Sopenharmony_ci EDMA_CHAN_SLOT(echan->ch_num), chan->chan_id); 18008c2ecf20Sopenharmony_ci} 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci/* Send pending descriptor to hardware */ 18038c2ecf20Sopenharmony_cistatic void edma_issue_pending(struct dma_chan *chan) 18048c2ecf20Sopenharmony_ci{ 18058c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 18068c2ecf20Sopenharmony_ci unsigned long flags; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci spin_lock_irqsave(&echan->vchan.lock, flags); 18098c2ecf20Sopenharmony_ci if (vchan_issue_pending(&echan->vchan) && !echan->edesc) 18108c2ecf20Sopenharmony_ci edma_execute(echan); 18118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&echan->vchan.lock, flags); 18128c2ecf20Sopenharmony_ci} 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci/* 18158c2ecf20Sopenharmony_ci * This limit exists to avoid a possible infinite loop when waiting for proof 18168c2ecf20Sopenharmony_ci * that a particular transfer is completed. This limit can be hit if there 18178c2ecf20Sopenharmony_ci * are large bursts to/from slow devices or the CPU is never able to catch 18188c2ecf20Sopenharmony_ci * the DMA hardware idle. On an AM335x transfering 48 bytes from the UART 18198c2ecf20Sopenharmony_ci * RX-FIFO, as many as 55 loops have been seen. 18208c2ecf20Sopenharmony_ci */ 18218c2ecf20Sopenharmony_ci#define EDMA_MAX_TR_WAIT_LOOPS 1000 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_cistatic u32 edma_residue(struct edma_desc *edesc) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci bool dst = edesc->direction == DMA_DEV_TO_MEM; 18268c2ecf20Sopenharmony_ci int loop_count = EDMA_MAX_TR_WAIT_LOOPS; 18278c2ecf20Sopenharmony_ci struct edma_chan *echan = edesc->echan; 18288c2ecf20Sopenharmony_ci struct edma_pset *pset = edesc->pset; 18298c2ecf20Sopenharmony_ci dma_addr_t done, pos, pos_old; 18308c2ecf20Sopenharmony_ci int channel = EDMA_CHAN_SLOT(echan->ch_num); 18318c2ecf20Sopenharmony_ci int idx = EDMA_REG_ARRAY_INDEX(channel); 18328c2ecf20Sopenharmony_ci int ch_bit = EDMA_CHANNEL_BIT(channel); 18338c2ecf20Sopenharmony_ci int event_reg; 18348c2ecf20Sopenharmony_ci int i; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci /* 18378c2ecf20Sopenharmony_ci * We always read the dst/src position from the first RamPar 18388c2ecf20Sopenharmony_ci * pset. That's the one which is active now. 18398c2ecf20Sopenharmony_ci */ 18408c2ecf20Sopenharmony_ci pos = edma_get_position(echan->ecc, echan->slot[0], dst); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci /* 18438c2ecf20Sopenharmony_ci * "pos" may represent a transfer request that is still being 18448c2ecf20Sopenharmony_ci * processed by the EDMACC or EDMATC. We will busy wait until 18458c2ecf20Sopenharmony_ci * any one of the situations occurs: 18468c2ecf20Sopenharmony_ci * 1. while and event is pending for the channel 18478c2ecf20Sopenharmony_ci * 2. a position updated 18488c2ecf20Sopenharmony_ci * 3. we hit the loop limit 18498c2ecf20Sopenharmony_ci */ 18508c2ecf20Sopenharmony_ci if (is_slave_direction(edesc->direction)) 18518c2ecf20Sopenharmony_ci event_reg = SH_ER; 18528c2ecf20Sopenharmony_ci else 18538c2ecf20Sopenharmony_ci event_reg = SH_ESR; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci pos_old = pos; 18568c2ecf20Sopenharmony_ci while (edma_shadow0_read_array(echan->ecc, event_reg, idx) & ch_bit) { 18578c2ecf20Sopenharmony_ci pos = edma_get_position(echan->ecc, echan->slot[0], dst); 18588c2ecf20Sopenharmony_ci if (pos != pos_old) 18598c2ecf20Sopenharmony_ci break; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci if (!--loop_count) { 18628c2ecf20Sopenharmony_ci dev_dbg_ratelimited(echan->vchan.chan.device->dev, 18638c2ecf20Sopenharmony_ci "%s: timeout waiting for PaRAM update\n", 18648c2ecf20Sopenharmony_ci __func__); 18658c2ecf20Sopenharmony_ci break; 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci cpu_relax(); 18698c2ecf20Sopenharmony_ci } 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci /* 18728c2ecf20Sopenharmony_ci * Cyclic is simple. Just subtract pset[0].addr from pos. 18738c2ecf20Sopenharmony_ci * 18748c2ecf20Sopenharmony_ci * We never update edesc->residue in the cyclic case, so we 18758c2ecf20Sopenharmony_ci * can tell the remaining room to the end of the circular 18768c2ecf20Sopenharmony_ci * buffer. 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_ci if (edesc->cyclic) { 18798c2ecf20Sopenharmony_ci done = pos - pset->addr; 18808c2ecf20Sopenharmony_ci edesc->residue_stat = edesc->residue - done; 18818c2ecf20Sopenharmony_ci return edesc->residue_stat; 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci /* 18858c2ecf20Sopenharmony_ci * If the position is 0, then EDMA loaded the closing dummy slot, the 18868c2ecf20Sopenharmony_ci * transfer is completed 18878c2ecf20Sopenharmony_ci */ 18888c2ecf20Sopenharmony_ci if (!pos) 18898c2ecf20Sopenharmony_ci return 0; 18908c2ecf20Sopenharmony_ci /* 18918c2ecf20Sopenharmony_ci * For SG operation we catch up with the last processed 18928c2ecf20Sopenharmony_ci * status. 18938c2ecf20Sopenharmony_ci */ 18948c2ecf20Sopenharmony_ci pset += edesc->processed_stat; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci for (i = edesc->processed_stat; i < edesc->processed; i++, pset++) { 18978c2ecf20Sopenharmony_ci /* 18988c2ecf20Sopenharmony_ci * If we are inside this pset address range, we know 18998c2ecf20Sopenharmony_ci * this is the active one. Get the current delta and 19008c2ecf20Sopenharmony_ci * stop walking the psets. 19018c2ecf20Sopenharmony_ci */ 19028c2ecf20Sopenharmony_ci if (pos >= pset->addr && pos < pset->addr + pset->len) 19038c2ecf20Sopenharmony_ci return edesc->residue_stat - (pos - pset->addr); 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci /* Otherwise mark it done and update residue_stat. */ 19068c2ecf20Sopenharmony_ci edesc->processed_stat++; 19078c2ecf20Sopenharmony_ci edesc->residue_stat -= pset->len; 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci return edesc->residue_stat; 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci/* Check request completion status */ 19138c2ecf20Sopenharmony_cistatic enum dma_status edma_tx_status(struct dma_chan *chan, 19148c2ecf20Sopenharmony_ci dma_cookie_t cookie, 19158c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 19188c2ecf20Sopenharmony_ci struct dma_tx_state txstate_tmp; 19198c2ecf20Sopenharmony_ci enum dma_status ret; 19208c2ecf20Sopenharmony_ci unsigned long flags; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci if (ret == DMA_COMPLETE) 19258c2ecf20Sopenharmony_ci return ret; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci /* Provide a dummy dma_tx_state for completion checking */ 19288c2ecf20Sopenharmony_ci if (!txstate) 19298c2ecf20Sopenharmony_ci txstate = &txstate_tmp; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci spin_lock_irqsave(&echan->vchan.lock, flags); 19328c2ecf20Sopenharmony_ci if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) { 19338c2ecf20Sopenharmony_ci txstate->residue = edma_residue(echan->edesc); 19348c2ecf20Sopenharmony_ci } else { 19358c2ecf20Sopenharmony_ci struct virt_dma_desc *vdesc = vchan_find_desc(&echan->vchan, 19368c2ecf20Sopenharmony_ci cookie); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci if (vdesc) 19398c2ecf20Sopenharmony_ci txstate->residue = to_edma_desc(&vdesc->tx)->residue; 19408c2ecf20Sopenharmony_ci else 19418c2ecf20Sopenharmony_ci txstate->residue = 0; 19428c2ecf20Sopenharmony_ci } 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci /* 19458c2ecf20Sopenharmony_ci * Mark the cookie completed if the residue is 0 for non cyclic 19468c2ecf20Sopenharmony_ci * transfers 19478c2ecf20Sopenharmony_ci */ 19488c2ecf20Sopenharmony_ci if (ret != DMA_COMPLETE && !txstate->residue && 19498c2ecf20Sopenharmony_ci echan->edesc && echan->edesc->polled && 19508c2ecf20Sopenharmony_ci echan->edesc->vdesc.tx.cookie == cookie) { 19518c2ecf20Sopenharmony_ci edma_stop(echan); 19528c2ecf20Sopenharmony_ci vchan_cookie_complete(&echan->edesc->vdesc); 19538c2ecf20Sopenharmony_ci echan->edesc = NULL; 19548c2ecf20Sopenharmony_ci edma_execute(echan); 19558c2ecf20Sopenharmony_ci ret = DMA_COMPLETE; 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&echan->vchan.lock, flags); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci return ret; 19618c2ecf20Sopenharmony_ci} 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_cistatic bool edma_is_memcpy_channel(int ch_num, s32 *memcpy_channels) 19648c2ecf20Sopenharmony_ci{ 19658c2ecf20Sopenharmony_ci if (!memcpy_channels) 19668c2ecf20Sopenharmony_ci return false; 19678c2ecf20Sopenharmony_ci while (*memcpy_channels != -1) { 19688c2ecf20Sopenharmony_ci if (*memcpy_channels == ch_num) 19698c2ecf20Sopenharmony_ci return true; 19708c2ecf20Sopenharmony_ci memcpy_channels++; 19718c2ecf20Sopenharmony_ci } 19728c2ecf20Sopenharmony_ci return false; 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci#define EDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 19768c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 19778c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \ 19788c2ecf20Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_cistatic void edma_dma_init(struct edma_cc *ecc, bool legacy_mode) 19818c2ecf20Sopenharmony_ci{ 19828c2ecf20Sopenharmony_ci struct dma_device *s_ddev = &ecc->dma_slave; 19838c2ecf20Sopenharmony_ci struct dma_device *m_ddev = NULL; 19848c2ecf20Sopenharmony_ci s32 *memcpy_channels = ecc->info->memcpy_channels; 19858c2ecf20Sopenharmony_ci int i, j; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci dma_cap_zero(s_ddev->cap_mask); 19888c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, s_ddev->cap_mask); 19898c2ecf20Sopenharmony_ci dma_cap_set(DMA_CYCLIC, s_ddev->cap_mask); 19908c2ecf20Sopenharmony_ci if (ecc->legacy_mode && !memcpy_channels) { 19918c2ecf20Sopenharmony_ci dev_warn(ecc->dev, 19928c2ecf20Sopenharmony_ci "Legacy memcpy is enabled, things might not work\n"); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, s_ddev->cap_mask); 19958c2ecf20Sopenharmony_ci dma_cap_set(DMA_INTERLEAVE, s_ddev->cap_mask); 19968c2ecf20Sopenharmony_ci s_ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy; 19978c2ecf20Sopenharmony_ci s_ddev->device_prep_interleaved_dma = edma_prep_dma_interleaved; 19988c2ecf20Sopenharmony_ci s_ddev->directions = BIT(DMA_MEM_TO_MEM); 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci s_ddev->device_prep_slave_sg = edma_prep_slave_sg; 20028c2ecf20Sopenharmony_ci s_ddev->device_prep_dma_cyclic = edma_prep_dma_cyclic; 20038c2ecf20Sopenharmony_ci s_ddev->device_alloc_chan_resources = edma_alloc_chan_resources; 20048c2ecf20Sopenharmony_ci s_ddev->device_free_chan_resources = edma_free_chan_resources; 20058c2ecf20Sopenharmony_ci s_ddev->device_issue_pending = edma_issue_pending; 20068c2ecf20Sopenharmony_ci s_ddev->device_tx_status = edma_tx_status; 20078c2ecf20Sopenharmony_ci s_ddev->device_config = edma_slave_config; 20088c2ecf20Sopenharmony_ci s_ddev->device_pause = edma_dma_pause; 20098c2ecf20Sopenharmony_ci s_ddev->device_resume = edma_dma_resume; 20108c2ecf20Sopenharmony_ci s_ddev->device_terminate_all = edma_terminate_all; 20118c2ecf20Sopenharmony_ci s_ddev->device_synchronize = edma_synchronize; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci s_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS; 20148c2ecf20Sopenharmony_ci s_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS; 20158c2ecf20Sopenharmony_ci s_ddev->directions |= (BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV)); 20168c2ecf20Sopenharmony_ci s_ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 20178c2ecf20Sopenharmony_ci s_ddev->max_burst = SZ_32K - 1; /* CIDX: 16bit signed */ 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci s_ddev->dev = ecc->dev; 20208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&s_ddev->channels); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci if (memcpy_channels) { 20238c2ecf20Sopenharmony_ci m_ddev = devm_kzalloc(ecc->dev, sizeof(*m_ddev), GFP_KERNEL); 20248c2ecf20Sopenharmony_ci if (!m_ddev) { 20258c2ecf20Sopenharmony_ci dev_warn(ecc->dev, "memcpy is disabled due to OoM\n"); 20268c2ecf20Sopenharmony_ci memcpy_channels = NULL; 20278c2ecf20Sopenharmony_ci goto ch_setup; 20288c2ecf20Sopenharmony_ci } 20298c2ecf20Sopenharmony_ci ecc->dma_memcpy = m_ddev; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci dma_cap_zero(m_ddev->cap_mask); 20328c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, m_ddev->cap_mask); 20338c2ecf20Sopenharmony_ci dma_cap_set(DMA_INTERLEAVE, m_ddev->cap_mask); 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci m_ddev->device_prep_dma_memcpy = edma_prep_dma_memcpy; 20368c2ecf20Sopenharmony_ci m_ddev->device_prep_interleaved_dma = edma_prep_dma_interleaved; 20378c2ecf20Sopenharmony_ci m_ddev->device_alloc_chan_resources = edma_alloc_chan_resources; 20388c2ecf20Sopenharmony_ci m_ddev->device_free_chan_resources = edma_free_chan_resources; 20398c2ecf20Sopenharmony_ci m_ddev->device_issue_pending = edma_issue_pending; 20408c2ecf20Sopenharmony_ci m_ddev->device_tx_status = edma_tx_status; 20418c2ecf20Sopenharmony_ci m_ddev->device_config = edma_slave_config; 20428c2ecf20Sopenharmony_ci m_ddev->device_pause = edma_dma_pause; 20438c2ecf20Sopenharmony_ci m_ddev->device_resume = edma_dma_resume; 20448c2ecf20Sopenharmony_ci m_ddev->device_terminate_all = edma_terminate_all; 20458c2ecf20Sopenharmony_ci m_ddev->device_synchronize = edma_synchronize; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci m_ddev->src_addr_widths = EDMA_DMA_BUSWIDTHS; 20488c2ecf20Sopenharmony_ci m_ddev->dst_addr_widths = EDMA_DMA_BUSWIDTHS; 20498c2ecf20Sopenharmony_ci m_ddev->directions = BIT(DMA_MEM_TO_MEM); 20508c2ecf20Sopenharmony_ci m_ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci m_ddev->dev = ecc->dev; 20538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&m_ddev->channels); 20548c2ecf20Sopenharmony_ci } else if (!ecc->legacy_mode) { 20558c2ecf20Sopenharmony_ci dev_info(ecc->dev, "memcpy is disabled\n"); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_cich_setup: 20598c2ecf20Sopenharmony_ci for (i = 0; i < ecc->num_channels; i++) { 20608c2ecf20Sopenharmony_ci struct edma_chan *echan = &ecc->slave_chans[i]; 20618c2ecf20Sopenharmony_ci echan->ch_num = EDMA_CTLR_CHAN(ecc->id, i); 20628c2ecf20Sopenharmony_ci echan->ecc = ecc; 20638c2ecf20Sopenharmony_ci echan->vchan.desc_free = edma_desc_free; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci if (m_ddev && edma_is_memcpy_channel(i, memcpy_channels)) 20668c2ecf20Sopenharmony_ci vchan_init(&echan->vchan, m_ddev); 20678c2ecf20Sopenharmony_ci else 20688c2ecf20Sopenharmony_ci vchan_init(&echan->vchan, s_ddev); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&echan->node); 20718c2ecf20Sopenharmony_ci for (j = 0; j < EDMA_MAX_SLOTS; j++) 20728c2ecf20Sopenharmony_ci echan->slot[j] = -1; 20738c2ecf20Sopenharmony_ci } 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_cistatic int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata, 20778c2ecf20Sopenharmony_ci struct edma_cc *ecc) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci int i; 20808c2ecf20Sopenharmony_ci u32 value, cccfg; 20818c2ecf20Sopenharmony_ci s8 (*queue_priority_map)[2]; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci /* Decode the eDMA3 configuration from CCCFG register */ 20848c2ecf20Sopenharmony_ci cccfg = edma_read(ecc, EDMA_CCCFG); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci value = GET_NUM_REGN(cccfg); 20878c2ecf20Sopenharmony_ci ecc->num_region = BIT(value); 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci value = GET_NUM_DMACH(cccfg); 20908c2ecf20Sopenharmony_ci ecc->num_channels = BIT(value + 1); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci value = GET_NUM_QDMACH(cccfg); 20938c2ecf20Sopenharmony_ci ecc->num_qchannels = value * 2; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci value = GET_NUM_PAENTRY(cccfg); 20968c2ecf20Sopenharmony_ci ecc->num_slots = BIT(value + 4); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci value = GET_NUM_EVQUE(cccfg); 20998c2ecf20Sopenharmony_ci ecc->num_tc = value + 1; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci ecc->chmap_exist = (cccfg & CHMAP_EXIST) ? true : false; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci dev_dbg(dev, "eDMA3 CC HW configuration (cccfg: 0x%08x):\n", cccfg); 21048c2ecf20Sopenharmony_ci dev_dbg(dev, "num_region: %u\n", ecc->num_region); 21058c2ecf20Sopenharmony_ci dev_dbg(dev, "num_channels: %u\n", ecc->num_channels); 21068c2ecf20Sopenharmony_ci dev_dbg(dev, "num_qchannels: %u\n", ecc->num_qchannels); 21078c2ecf20Sopenharmony_ci dev_dbg(dev, "num_slots: %u\n", ecc->num_slots); 21088c2ecf20Sopenharmony_ci dev_dbg(dev, "num_tc: %u\n", ecc->num_tc); 21098c2ecf20Sopenharmony_ci dev_dbg(dev, "chmap_exist: %s\n", ecc->chmap_exist ? "yes" : "no"); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci /* Nothing need to be done if queue priority is provided */ 21128c2ecf20Sopenharmony_ci if (pdata->queue_priority_mapping) 21138c2ecf20Sopenharmony_ci return 0; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci /* 21168c2ecf20Sopenharmony_ci * Configure TC/queue priority as follows: 21178c2ecf20Sopenharmony_ci * Q0 - priority 0 21188c2ecf20Sopenharmony_ci * Q1 - priority 1 21198c2ecf20Sopenharmony_ci * Q2 - priority 2 21208c2ecf20Sopenharmony_ci * ... 21218c2ecf20Sopenharmony_ci * The meaning of priority numbers: 0 highest priority, 7 lowest 21228c2ecf20Sopenharmony_ci * priority. So Q0 is the highest priority queue and the last queue has 21238c2ecf20Sopenharmony_ci * the lowest priority. 21248c2ecf20Sopenharmony_ci */ 21258c2ecf20Sopenharmony_ci queue_priority_map = devm_kcalloc(dev, ecc->num_tc + 1, sizeof(s8), 21268c2ecf20Sopenharmony_ci GFP_KERNEL); 21278c2ecf20Sopenharmony_ci if (!queue_priority_map) 21288c2ecf20Sopenharmony_ci return -ENOMEM; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci for (i = 0; i < ecc->num_tc; i++) { 21318c2ecf20Sopenharmony_ci queue_priority_map[i][0] = i; 21328c2ecf20Sopenharmony_ci queue_priority_map[i][1] = i; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci queue_priority_map[i][0] = -1; 21358c2ecf20Sopenharmony_ci queue_priority_map[i][1] = -1; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci pdata->queue_priority_mapping = queue_priority_map; 21388c2ecf20Sopenharmony_ci /* Default queue has the lowest priority */ 21398c2ecf20Sopenharmony_ci pdata->default_queue = i - 1; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci return 0; 21428c2ecf20Sopenharmony_ci} 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 21458c2ecf20Sopenharmony_cistatic int edma_xbar_event_map(struct device *dev, struct edma_soc_info *pdata, 21468c2ecf20Sopenharmony_ci size_t sz) 21478c2ecf20Sopenharmony_ci{ 21488c2ecf20Sopenharmony_ci const char pname[] = "ti,edma-xbar-event-map"; 21498c2ecf20Sopenharmony_ci struct resource res; 21508c2ecf20Sopenharmony_ci void __iomem *xbar; 21518c2ecf20Sopenharmony_ci s16 (*xbar_chans)[2]; 21528c2ecf20Sopenharmony_ci size_t nelm = sz / sizeof(s16); 21538c2ecf20Sopenharmony_ci u32 shift, offset, mux; 21548c2ecf20Sopenharmony_ci int ret, i; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci xbar_chans = devm_kcalloc(dev, nelm + 2, sizeof(s16), GFP_KERNEL); 21578c2ecf20Sopenharmony_ci if (!xbar_chans) 21588c2ecf20Sopenharmony_ci return -ENOMEM; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci ret = of_address_to_resource(dev->of_node, 1, &res); 21618c2ecf20Sopenharmony_ci if (ret) 21628c2ecf20Sopenharmony_ci return -ENOMEM; 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci xbar = devm_ioremap(dev, res.start, resource_size(&res)); 21658c2ecf20Sopenharmony_ci if (!xbar) 21668c2ecf20Sopenharmony_ci return -ENOMEM; 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci ret = of_property_read_u16_array(dev->of_node, pname, (u16 *)xbar_chans, 21698c2ecf20Sopenharmony_ci nelm); 21708c2ecf20Sopenharmony_ci if (ret) 21718c2ecf20Sopenharmony_ci return -EIO; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* Invalidate last entry for the other user of this mess */ 21748c2ecf20Sopenharmony_ci nelm >>= 1; 21758c2ecf20Sopenharmony_ci xbar_chans[nelm][0] = -1; 21768c2ecf20Sopenharmony_ci xbar_chans[nelm][1] = -1; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci for (i = 0; i < nelm; i++) { 21798c2ecf20Sopenharmony_ci shift = (xbar_chans[i][1] & 0x03) << 3; 21808c2ecf20Sopenharmony_ci offset = xbar_chans[i][1] & 0xfffffffc; 21818c2ecf20Sopenharmony_ci mux = readl(xbar + offset); 21828c2ecf20Sopenharmony_ci mux &= ~(0xff << shift); 21838c2ecf20Sopenharmony_ci mux |= xbar_chans[i][0] << shift; 21848c2ecf20Sopenharmony_ci writel(mux, (xbar + offset)); 21858c2ecf20Sopenharmony_ci } 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci pdata->xbar_chans = (const s16 (*)[2]) xbar_chans; 21888c2ecf20Sopenharmony_ci return 0; 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_cistatic struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, 21928c2ecf20Sopenharmony_ci bool legacy_mode) 21938c2ecf20Sopenharmony_ci{ 21948c2ecf20Sopenharmony_ci struct edma_soc_info *info; 21958c2ecf20Sopenharmony_ci struct property *prop; 21968c2ecf20Sopenharmony_ci int sz, ret; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci info = devm_kzalloc(dev, sizeof(struct edma_soc_info), GFP_KERNEL); 21998c2ecf20Sopenharmony_ci if (!info) 22008c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci if (legacy_mode) { 22038c2ecf20Sopenharmony_ci prop = of_find_property(dev->of_node, "ti,edma-xbar-event-map", 22048c2ecf20Sopenharmony_ci &sz); 22058c2ecf20Sopenharmony_ci if (prop) { 22068c2ecf20Sopenharmony_ci ret = edma_xbar_event_map(dev, info, sz); 22078c2ecf20Sopenharmony_ci if (ret) 22088c2ecf20Sopenharmony_ci return ERR_PTR(ret); 22098c2ecf20Sopenharmony_ci } 22108c2ecf20Sopenharmony_ci return info; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci /* Get the list of channels allocated to be used for memcpy */ 22148c2ecf20Sopenharmony_ci prop = of_find_property(dev->of_node, "ti,edma-memcpy-channels", &sz); 22158c2ecf20Sopenharmony_ci if (prop) { 22168c2ecf20Sopenharmony_ci const char pname[] = "ti,edma-memcpy-channels"; 22178c2ecf20Sopenharmony_ci size_t nelm = sz / sizeof(s32); 22188c2ecf20Sopenharmony_ci s32 *memcpy_ch; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci memcpy_ch = devm_kcalloc(dev, nelm + 1, sizeof(s32), 22218c2ecf20Sopenharmony_ci GFP_KERNEL); 22228c2ecf20Sopenharmony_ci if (!memcpy_ch) 22238c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(dev->of_node, pname, 22268c2ecf20Sopenharmony_ci (u32 *)memcpy_ch, nelm); 22278c2ecf20Sopenharmony_ci if (ret) 22288c2ecf20Sopenharmony_ci return ERR_PTR(ret); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci memcpy_ch[nelm] = -1; 22318c2ecf20Sopenharmony_ci info->memcpy_channels = memcpy_ch; 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci prop = of_find_property(dev->of_node, "ti,edma-reserved-slot-ranges", 22358c2ecf20Sopenharmony_ci &sz); 22368c2ecf20Sopenharmony_ci if (prop) { 22378c2ecf20Sopenharmony_ci const char pname[] = "ti,edma-reserved-slot-ranges"; 22388c2ecf20Sopenharmony_ci u32 (*tmp)[2]; 22398c2ecf20Sopenharmony_ci s16 (*rsv_slots)[2]; 22408c2ecf20Sopenharmony_ci size_t nelm = sz / sizeof(*tmp); 22418c2ecf20Sopenharmony_ci struct edma_rsv_info *rsv_info; 22428c2ecf20Sopenharmony_ci int i; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci if (!nelm) 22458c2ecf20Sopenharmony_ci return info; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci tmp = kcalloc(nelm, sizeof(*tmp), GFP_KERNEL); 22488c2ecf20Sopenharmony_ci if (!tmp) 22498c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci rsv_info = devm_kzalloc(dev, sizeof(*rsv_info), GFP_KERNEL); 22528c2ecf20Sopenharmony_ci if (!rsv_info) { 22538c2ecf20Sopenharmony_ci kfree(tmp); 22548c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 22558c2ecf20Sopenharmony_ci } 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci rsv_slots = devm_kcalloc(dev, nelm + 1, sizeof(*rsv_slots), 22588c2ecf20Sopenharmony_ci GFP_KERNEL); 22598c2ecf20Sopenharmony_ci if (!rsv_slots) { 22608c2ecf20Sopenharmony_ci kfree(tmp); 22618c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(dev->of_node, pname, 22658c2ecf20Sopenharmony_ci (u32 *)tmp, nelm * 2); 22668c2ecf20Sopenharmony_ci if (ret) { 22678c2ecf20Sopenharmony_ci kfree(tmp); 22688c2ecf20Sopenharmony_ci return ERR_PTR(ret); 22698c2ecf20Sopenharmony_ci } 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci for (i = 0; i < nelm; i++) { 22728c2ecf20Sopenharmony_ci rsv_slots[i][0] = tmp[i][0]; 22738c2ecf20Sopenharmony_ci rsv_slots[i][1] = tmp[i][1]; 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci rsv_slots[nelm][0] = -1; 22768c2ecf20Sopenharmony_ci rsv_slots[nelm][1] = -1; 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci info->rsv = rsv_info; 22798c2ecf20Sopenharmony_ci info->rsv->rsv_slots = (const s16 (*)[2])rsv_slots; 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci kfree(tmp); 22828c2ecf20Sopenharmony_ci } 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci return info; 22858c2ecf20Sopenharmony_ci} 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic struct dma_chan *of_edma_xlate(struct of_phandle_args *dma_spec, 22888c2ecf20Sopenharmony_ci struct of_dma *ofdma) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci struct edma_cc *ecc = ofdma->of_dma_data; 22918c2ecf20Sopenharmony_ci struct dma_chan *chan = NULL; 22928c2ecf20Sopenharmony_ci struct edma_chan *echan; 22938c2ecf20Sopenharmony_ci int i; 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci if (!ecc || dma_spec->args_count < 1) 22968c2ecf20Sopenharmony_ci return NULL; 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci for (i = 0; i < ecc->num_channels; i++) { 22998c2ecf20Sopenharmony_ci echan = &ecc->slave_chans[i]; 23008c2ecf20Sopenharmony_ci if (echan->ch_num == dma_spec->args[0]) { 23018c2ecf20Sopenharmony_ci chan = &echan->vchan.chan; 23028c2ecf20Sopenharmony_ci break; 23038c2ecf20Sopenharmony_ci } 23048c2ecf20Sopenharmony_ci } 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci if (!chan) 23078c2ecf20Sopenharmony_ci return NULL; 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci if (echan->ecc->legacy_mode && dma_spec->args_count == 1) 23108c2ecf20Sopenharmony_ci goto out; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci if (!echan->ecc->legacy_mode && dma_spec->args_count == 2 && 23138c2ecf20Sopenharmony_ci dma_spec->args[1] < echan->ecc->num_tc) { 23148c2ecf20Sopenharmony_ci echan->tc = &echan->ecc->tc_list[dma_spec->args[1]]; 23158c2ecf20Sopenharmony_ci goto out; 23168c2ecf20Sopenharmony_ci } 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci return NULL; 23198c2ecf20Sopenharmony_ciout: 23208c2ecf20Sopenharmony_ci /* The channel is going to be used as HW synchronized */ 23218c2ecf20Sopenharmony_ci echan->hw_triggered = true; 23228c2ecf20Sopenharmony_ci return dma_get_slave_channel(chan); 23238c2ecf20Sopenharmony_ci} 23248c2ecf20Sopenharmony_ci#else 23258c2ecf20Sopenharmony_cistatic struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, 23268c2ecf20Sopenharmony_ci bool legacy_mode) 23278c2ecf20Sopenharmony_ci{ 23288c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 23298c2ecf20Sopenharmony_ci} 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_cistatic struct dma_chan *of_edma_xlate(struct of_phandle_args *dma_spec, 23328c2ecf20Sopenharmony_ci struct of_dma *ofdma) 23338c2ecf20Sopenharmony_ci{ 23348c2ecf20Sopenharmony_ci return NULL; 23358c2ecf20Sopenharmony_ci} 23368c2ecf20Sopenharmony_ci#endif 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_cistatic bool edma_filter_fn(struct dma_chan *chan, void *param); 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_cistatic int edma_probe(struct platform_device *pdev) 23418c2ecf20Sopenharmony_ci{ 23428c2ecf20Sopenharmony_ci struct edma_soc_info *info = pdev->dev.platform_data; 23438c2ecf20Sopenharmony_ci s8 (*queue_priority_mapping)[2]; 23448c2ecf20Sopenharmony_ci const s16 (*reserved)[2]; 23458c2ecf20Sopenharmony_ci int i, irq; 23468c2ecf20Sopenharmony_ci char *irq_name; 23478c2ecf20Sopenharmony_ci struct resource *mem; 23488c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 23498c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 23508c2ecf20Sopenharmony_ci struct edma_cc *ecc; 23518c2ecf20Sopenharmony_ci bool legacy_mode = true; 23528c2ecf20Sopenharmony_ci int ret; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci if (node) { 23558c2ecf20Sopenharmony_ci const struct of_device_id *match; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci match = of_match_node(edma_of_ids, node); 23588c2ecf20Sopenharmony_ci if (match && (*(u32 *)match->data) == EDMA_BINDING_TPCC) 23598c2ecf20Sopenharmony_ci legacy_mode = false; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci info = edma_setup_info_from_dt(dev, legacy_mode); 23628c2ecf20Sopenharmony_ci if (IS_ERR(info)) { 23638c2ecf20Sopenharmony_ci dev_err(dev, "failed to get DT data\n"); 23648c2ecf20Sopenharmony_ci return PTR_ERR(info); 23658c2ecf20Sopenharmony_ci } 23668c2ecf20Sopenharmony_ci } 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci if (!info) 23698c2ecf20Sopenharmony_ci return -ENODEV; 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 23728c2ecf20Sopenharmony_ci if (ret) 23738c2ecf20Sopenharmony_ci return ret; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); 23768c2ecf20Sopenharmony_ci if (!ecc) 23778c2ecf20Sopenharmony_ci return -ENOMEM; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci ecc->dev = dev; 23808c2ecf20Sopenharmony_ci ecc->id = pdev->id; 23818c2ecf20Sopenharmony_ci ecc->legacy_mode = legacy_mode; 23828c2ecf20Sopenharmony_ci /* When booting with DT the pdev->id is -1 */ 23838c2ecf20Sopenharmony_ci if (ecc->id < 0) 23848c2ecf20Sopenharmony_ci ecc->id = 0; 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "edma3_cc"); 23878c2ecf20Sopenharmony_ci if (!mem) { 23888c2ecf20Sopenharmony_ci dev_dbg(dev, "mem resource not found, using index 0\n"); 23898c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 23908c2ecf20Sopenharmony_ci if (!mem) { 23918c2ecf20Sopenharmony_ci dev_err(dev, "no mem resource?\n"); 23928c2ecf20Sopenharmony_ci return -ENODEV; 23938c2ecf20Sopenharmony_ci } 23948c2ecf20Sopenharmony_ci } 23958c2ecf20Sopenharmony_ci ecc->base = devm_ioremap_resource(dev, mem); 23968c2ecf20Sopenharmony_ci if (IS_ERR(ecc->base)) 23978c2ecf20Sopenharmony_ci return PTR_ERR(ecc->base); 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ecc); 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 24028c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 24038c2ecf20Sopenharmony_ci if (ret < 0) { 24048c2ecf20Sopenharmony_ci dev_err(dev, "pm_runtime_get_sync() failed\n"); 24058c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 24068c2ecf20Sopenharmony_ci return ret; 24078c2ecf20Sopenharmony_ci } 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci /* Get eDMA3 configuration from IP */ 24108c2ecf20Sopenharmony_ci ret = edma_setup_from_hw(dev, info, ecc); 24118c2ecf20Sopenharmony_ci if (ret) 24128c2ecf20Sopenharmony_ci goto err_disable_pm; 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci /* Allocate memory based on the information we got from the IP */ 24158c2ecf20Sopenharmony_ci ecc->slave_chans = devm_kcalloc(dev, ecc->num_channels, 24168c2ecf20Sopenharmony_ci sizeof(*ecc->slave_chans), GFP_KERNEL); 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci ecc->slot_inuse = devm_kcalloc(dev, BITS_TO_LONGS(ecc->num_slots), 24198c2ecf20Sopenharmony_ci sizeof(unsigned long), GFP_KERNEL); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci ecc->channels_mask = devm_kcalloc(dev, 24228c2ecf20Sopenharmony_ci BITS_TO_LONGS(ecc->num_channels), 24238c2ecf20Sopenharmony_ci sizeof(unsigned long), GFP_KERNEL); 24248c2ecf20Sopenharmony_ci if (!ecc->slave_chans || !ecc->slot_inuse || !ecc->channels_mask) { 24258c2ecf20Sopenharmony_ci ret = -ENOMEM; 24268c2ecf20Sopenharmony_ci goto err_disable_pm; 24278c2ecf20Sopenharmony_ci } 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci /* Mark all channels available initially */ 24308c2ecf20Sopenharmony_ci bitmap_fill(ecc->channels_mask, ecc->num_channels); 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci ecc->default_queue = info->default_queue; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci if (info->rsv) { 24358c2ecf20Sopenharmony_ci /* Set the reserved slots in inuse list */ 24368c2ecf20Sopenharmony_ci reserved = info->rsv->rsv_slots; 24378c2ecf20Sopenharmony_ci if (reserved) { 24388c2ecf20Sopenharmony_ci for (i = 0; reserved[i][0] != -1; i++) 24398c2ecf20Sopenharmony_ci bitmap_set(ecc->slot_inuse, reserved[i][0], 24408c2ecf20Sopenharmony_ci reserved[i][1]); 24418c2ecf20Sopenharmony_ci } 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci /* Clear channels not usable for Linux */ 24448c2ecf20Sopenharmony_ci reserved = info->rsv->rsv_chans; 24458c2ecf20Sopenharmony_ci if (reserved) { 24468c2ecf20Sopenharmony_ci for (i = 0; reserved[i][0] != -1; i++) 24478c2ecf20Sopenharmony_ci bitmap_clear(ecc->channels_mask, reserved[i][0], 24488c2ecf20Sopenharmony_ci reserved[i][1]); 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci } 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci for (i = 0; i < ecc->num_slots; i++) { 24538c2ecf20Sopenharmony_ci /* Reset only unused - not reserved - paRAM slots */ 24548c2ecf20Sopenharmony_ci if (!test_bit(i, ecc->slot_inuse)) 24558c2ecf20Sopenharmony_ci edma_write_slot(ecc, i, &dummy_paramset); 24568c2ecf20Sopenharmony_ci } 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, "edma3_ccint"); 24598c2ecf20Sopenharmony_ci if (irq < 0 && node) 24608c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci if (irq > 0) { 24638c2ecf20Sopenharmony_ci irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s_ccint", 24648c2ecf20Sopenharmony_ci dev_name(dev)); 24658c2ecf20Sopenharmony_ci if (!irq_name) { 24668c2ecf20Sopenharmony_ci ret = -ENOMEM; 24678c2ecf20Sopenharmony_ci goto err_disable_pm; 24688c2ecf20Sopenharmony_ci } 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, dma_irq_handler, 0, irq_name, 24718c2ecf20Sopenharmony_ci ecc); 24728c2ecf20Sopenharmony_ci if (ret) { 24738c2ecf20Sopenharmony_ci dev_err(dev, "CCINT (%d) failed --> %d\n", irq, ret); 24748c2ecf20Sopenharmony_ci goto err_disable_pm; 24758c2ecf20Sopenharmony_ci } 24768c2ecf20Sopenharmony_ci ecc->ccint = irq; 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, "edma3_ccerrint"); 24808c2ecf20Sopenharmony_ci if (irq < 0 && node) 24818c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(node, 2); 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci if (irq > 0) { 24848c2ecf20Sopenharmony_ci irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s_ccerrint", 24858c2ecf20Sopenharmony_ci dev_name(dev)); 24868c2ecf20Sopenharmony_ci if (!irq_name) { 24878c2ecf20Sopenharmony_ci ret = -ENOMEM; 24888c2ecf20Sopenharmony_ci goto err_disable_pm; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, dma_ccerr_handler, 0, irq_name, 24928c2ecf20Sopenharmony_ci ecc); 24938c2ecf20Sopenharmony_ci if (ret) { 24948c2ecf20Sopenharmony_ci dev_err(dev, "CCERRINT (%d) failed --> %d\n", irq, ret); 24958c2ecf20Sopenharmony_ci goto err_disable_pm; 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci ecc->ccerrint = irq; 24988c2ecf20Sopenharmony_ci } 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci ecc->dummy_slot = edma_alloc_slot(ecc, EDMA_SLOT_ANY); 25018c2ecf20Sopenharmony_ci if (ecc->dummy_slot < 0) { 25028c2ecf20Sopenharmony_ci dev_err(dev, "Can't allocate PaRAM dummy slot\n"); 25038c2ecf20Sopenharmony_ci ret = ecc->dummy_slot; 25048c2ecf20Sopenharmony_ci goto err_disable_pm; 25058c2ecf20Sopenharmony_ci } 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci queue_priority_mapping = info->queue_priority_mapping; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci if (!ecc->legacy_mode) { 25108c2ecf20Sopenharmony_ci int lowest_priority = 0; 25118c2ecf20Sopenharmony_ci unsigned int array_max; 25128c2ecf20Sopenharmony_ci struct of_phandle_args tc_args; 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci ecc->tc_list = devm_kcalloc(dev, ecc->num_tc, 25158c2ecf20Sopenharmony_ci sizeof(*ecc->tc_list), GFP_KERNEL); 25168c2ecf20Sopenharmony_ci if (!ecc->tc_list) { 25178c2ecf20Sopenharmony_ci ret = -ENOMEM; 25188c2ecf20Sopenharmony_ci goto err_reg1; 25198c2ecf20Sopenharmony_ci } 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci for (i = 0;; i++) { 25228c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(node, "ti,tptcs", 25238c2ecf20Sopenharmony_ci 1, i, &tc_args); 25248c2ecf20Sopenharmony_ci if (ret || i == ecc->num_tc) 25258c2ecf20Sopenharmony_ci break; 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci ecc->tc_list[i].node = tc_args.np; 25288c2ecf20Sopenharmony_ci ecc->tc_list[i].id = i; 25298c2ecf20Sopenharmony_ci queue_priority_mapping[i][1] = tc_args.args[0]; 25308c2ecf20Sopenharmony_ci if (queue_priority_mapping[i][1] > lowest_priority) { 25318c2ecf20Sopenharmony_ci lowest_priority = queue_priority_mapping[i][1]; 25328c2ecf20Sopenharmony_ci info->default_queue = i; 25338c2ecf20Sopenharmony_ci } 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci /* See if we have optional dma-channel-mask array */ 25378c2ecf20Sopenharmony_ci array_max = DIV_ROUND_UP(ecc->num_channels, BITS_PER_TYPE(u32)); 25388c2ecf20Sopenharmony_ci ret = of_property_read_variable_u32_array(node, 25398c2ecf20Sopenharmony_ci "dma-channel-mask", 25408c2ecf20Sopenharmony_ci (u32 *)ecc->channels_mask, 25418c2ecf20Sopenharmony_ci 1, array_max); 25428c2ecf20Sopenharmony_ci if (ret > 0 && ret != array_max) 25438c2ecf20Sopenharmony_ci dev_warn(dev, "dma-channel-mask is not complete.\n"); 25448c2ecf20Sopenharmony_ci else if (ret == -EOVERFLOW || ret == -ENODATA) 25458c2ecf20Sopenharmony_ci dev_warn(dev, 25468c2ecf20Sopenharmony_ci "dma-channel-mask is out of range or empty\n"); 25478c2ecf20Sopenharmony_ci } 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci /* Event queue priority mapping */ 25508c2ecf20Sopenharmony_ci for (i = 0; queue_priority_mapping[i][0] != -1; i++) 25518c2ecf20Sopenharmony_ci edma_assign_priority_to_queue(ecc, queue_priority_mapping[i][0], 25528c2ecf20Sopenharmony_ci queue_priority_mapping[i][1]); 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci edma_write_array2(ecc, EDMA_DRAE, 0, 0, 0x0); 25558c2ecf20Sopenharmony_ci edma_write_array2(ecc, EDMA_DRAE, 0, 1, 0x0); 25568c2ecf20Sopenharmony_ci edma_write_array(ecc, EDMA_QRAE, 0, 0x0); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci ecc->info = info; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci /* Init the dma device and channels */ 25618c2ecf20Sopenharmony_ci edma_dma_init(ecc, legacy_mode); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci for (i = 0; i < ecc->num_channels; i++) { 25648c2ecf20Sopenharmony_ci /* Do not touch reserved channels */ 25658c2ecf20Sopenharmony_ci if (!test_bit(i, ecc->channels_mask)) 25668c2ecf20Sopenharmony_ci continue; 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci /* Assign all channels to the default queue */ 25698c2ecf20Sopenharmony_ci edma_assign_channel_eventq(&ecc->slave_chans[i], 25708c2ecf20Sopenharmony_ci info->default_queue); 25718c2ecf20Sopenharmony_ci /* Set entry slot to the dummy slot */ 25728c2ecf20Sopenharmony_ci edma_set_chmap(&ecc->slave_chans[i], ecc->dummy_slot); 25738c2ecf20Sopenharmony_ci } 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci ecc->dma_slave.filter.map = info->slave_map; 25768c2ecf20Sopenharmony_ci ecc->dma_slave.filter.mapcnt = info->slavecnt; 25778c2ecf20Sopenharmony_ci ecc->dma_slave.filter.fn = edma_filter_fn; 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci ret = dma_async_device_register(&ecc->dma_slave); 25808c2ecf20Sopenharmony_ci if (ret) { 25818c2ecf20Sopenharmony_ci dev_err(dev, "slave ddev registration failed (%d)\n", ret); 25828c2ecf20Sopenharmony_ci goto err_reg1; 25838c2ecf20Sopenharmony_ci } 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci if (ecc->dma_memcpy) { 25868c2ecf20Sopenharmony_ci ret = dma_async_device_register(ecc->dma_memcpy); 25878c2ecf20Sopenharmony_ci if (ret) { 25888c2ecf20Sopenharmony_ci dev_err(dev, "memcpy ddev registration failed (%d)\n", 25898c2ecf20Sopenharmony_ci ret); 25908c2ecf20Sopenharmony_ci dma_async_device_unregister(&ecc->dma_slave); 25918c2ecf20Sopenharmony_ci goto err_reg1; 25928c2ecf20Sopenharmony_ci } 25938c2ecf20Sopenharmony_ci } 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci if (node) 25968c2ecf20Sopenharmony_ci of_dma_controller_register(node, of_edma_xlate, ecc); 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci dev_info(dev, "TI EDMA DMA engine driver\n"); 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci return 0; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_cierr_reg1: 26038c2ecf20Sopenharmony_ci edma_free_slot(ecc, ecc->dummy_slot); 26048c2ecf20Sopenharmony_cierr_disable_pm: 26058c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 26068c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 26078c2ecf20Sopenharmony_ci return ret; 26088c2ecf20Sopenharmony_ci} 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_cistatic void edma_cleanupp_vchan(struct dma_device *dmadev) 26118c2ecf20Sopenharmony_ci{ 26128c2ecf20Sopenharmony_ci struct edma_chan *echan, *_echan; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci list_for_each_entry_safe(echan, _echan, 26158c2ecf20Sopenharmony_ci &dmadev->channels, vchan.chan.device_node) { 26168c2ecf20Sopenharmony_ci list_del(&echan->vchan.chan.device_node); 26178c2ecf20Sopenharmony_ci tasklet_kill(&echan->vchan.task); 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci} 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_cistatic int edma_remove(struct platform_device *pdev) 26228c2ecf20Sopenharmony_ci{ 26238c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 26248c2ecf20Sopenharmony_ci struct edma_cc *ecc = dev_get_drvdata(dev); 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci devm_free_irq(dev, ecc->ccint, ecc); 26278c2ecf20Sopenharmony_ci devm_free_irq(dev, ecc->ccerrint, ecc); 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci edma_cleanupp_vchan(&ecc->dma_slave); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci if (dev->of_node) 26328c2ecf20Sopenharmony_ci of_dma_controller_free(dev->of_node); 26338c2ecf20Sopenharmony_ci dma_async_device_unregister(&ecc->dma_slave); 26348c2ecf20Sopenharmony_ci if (ecc->dma_memcpy) 26358c2ecf20Sopenharmony_ci dma_async_device_unregister(ecc->dma_memcpy); 26368c2ecf20Sopenharmony_ci edma_free_slot(ecc, ecc->dummy_slot); 26378c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 26388c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci return 0; 26418c2ecf20Sopenharmony_ci} 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 26448c2ecf20Sopenharmony_cistatic int edma_pm_suspend(struct device *dev) 26458c2ecf20Sopenharmony_ci{ 26468c2ecf20Sopenharmony_ci struct edma_cc *ecc = dev_get_drvdata(dev); 26478c2ecf20Sopenharmony_ci struct edma_chan *echan = ecc->slave_chans; 26488c2ecf20Sopenharmony_ci int i; 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci for (i = 0; i < ecc->num_channels; i++) { 26518c2ecf20Sopenharmony_ci if (echan[i].alloced) 26528c2ecf20Sopenharmony_ci edma_setup_interrupt(&echan[i], false); 26538c2ecf20Sopenharmony_ci } 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci return 0; 26568c2ecf20Sopenharmony_ci} 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_cistatic int edma_pm_resume(struct device *dev) 26598c2ecf20Sopenharmony_ci{ 26608c2ecf20Sopenharmony_ci struct edma_cc *ecc = dev_get_drvdata(dev); 26618c2ecf20Sopenharmony_ci struct edma_chan *echan = ecc->slave_chans; 26628c2ecf20Sopenharmony_ci int i; 26638c2ecf20Sopenharmony_ci s8 (*queue_priority_mapping)[2]; 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci /* re initialize dummy slot to dummy param set */ 26668c2ecf20Sopenharmony_ci edma_write_slot(ecc, ecc->dummy_slot, &dummy_paramset); 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci queue_priority_mapping = ecc->info->queue_priority_mapping; 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci /* Event queue priority mapping */ 26718c2ecf20Sopenharmony_ci for (i = 0; queue_priority_mapping[i][0] != -1; i++) 26728c2ecf20Sopenharmony_ci edma_assign_priority_to_queue(ecc, queue_priority_mapping[i][0], 26738c2ecf20Sopenharmony_ci queue_priority_mapping[i][1]); 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci for (i = 0; i < ecc->num_channels; i++) { 26768c2ecf20Sopenharmony_ci if (echan[i].alloced) { 26778c2ecf20Sopenharmony_ci /* ensure access through shadow region 0 */ 26788c2ecf20Sopenharmony_ci edma_or_array2(ecc, EDMA_DRAE, 0, 26798c2ecf20Sopenharmony_ci EDMA_REG_ARRAY_INDEX(i), 26808c2ecf20Sopenharmony_ci EDMA_CHANNEL_BIT(i)); 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci edma_setup_interrupt(&echan[i], true); 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci /* Set up channel -> slot mapping for the entry slot */ 26858c2ecf20Sopenharmony_ci edma_set_chmap(&echan[i], echan[i].slot[0]); 26868c2ecf20Sopenharmony_ci } 26878c2ecf20Sopenharmony_ci } 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci return 0; 26908c2ecf20Sopenharmony_ci} 26918c2ecf20Sopenharmony_ci#endif 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_cistatic const struct dev_pm_ops edma_pm_ops = { 26948c2ecf20Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(edma_pm_suspend, edma_pm_resume) 26958c2ecf20Sopenharmony_ci}; 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_cistatic struct platform_driver edma_driver = { 26988c2ecf20Sopenharmony_ci .probe = edma_probe, 26998c2ecf20Sopenharmony_ci .remove = edma_remove, 27008c2ecf20Sopenharmony_ci .driver = { 27018c2ecf20Sopenharmony_ci .name = "edma", 27028c2ecf20Sopenharmony_ci .pm = &edma_pm_ops, 27038c2ecf20Sopenharmony_ci .of_match_table = edma_of_ids, 27048c2ecf20Sopenharmony_ci }, 27058c2ecf20Sopenharmony_ci}; 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_cistatic int edma_tptc_probe(struct platform_device *pdev) 27088c2ecf20Sopenharmony_ci{ 27098c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 27108c2ecf20Sopenharmony_ci return pm_runtime_get_sync(&pdev->dev); 27118c2ecf20Sopenharmony_ci} 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_cistatic struct platform_driver edma_tptc_driver = { 27148c2ecf20Sopenharmony_ci .probe = edma_tptc_probe, 27158c2ecf20Sopenharmony_ci .driver = { 27168c2ecf20Sopenharmony_ci .name = "edma3-tptc", 27178c2ecf20Sopenharmony_ci .of_match_table = edma_tptc_of_ids, 27188c2ecf20Sopenharmony_ci }, 27198c2ecf20Sopenharmony_ci}; 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_cistatic bool edma_filter_fn(struct dma_chan *chan, void *param) 27228c2ecf20Sopenharmony_ci{ 27238c2ecf20Sopenharmony_ci bool match = false; 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci if (chan->device->dev->driver == &edma_driver.driver) { 27268c2ecf20Sopenharmony_ci struct edma_chan *echan = to_edma_chan(chan); 27278c2ecf20Sopenharmony_ci unsigned ch_req = *(unsigned *)param; 27288c2ecf20Sopenharmony_ci if (ch_req == echan->ch_num) { 27298c2ecf20Sopenharmony_ci /* The channel is going to be used as HW synchronized */ 27308c2ecf20Sopenharmony_ci echan->hw_triggered = true; 27318c2ecf20Sopenharmony_ci match = true; 27328c2ecf20Sopenharmony_ci } 27338c2ecf20Sopenharmony_ci } 27348c2ecf20Sopenharmony_ci return match; 27358c2ecf20Sopenharmony_ci} 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_cistatic int edma_init(void) 27388c2ecf20Sopenharmony_ci{ 27398c2ecf20Sopenharmony_ci int ret; 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci ret = platform_driver_register(&edma_tptc_driver); 27428c2ecf20Sopenharmony_ci if (ret) 27438c2ecf20Sopenharmony_ci return ret; 27448c2ecf20Sopenharmony_ci 27458c2ecf20Sopenharmony_ci return platform_driver_register(&edma_driver); 27468c2ecf20Sopenharmony_ci} 27478c2ecf20Sopenharmony_cisubsys_initcall(edma_init); 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_cistatic void __exit edma_exit(void) 27508c2ecf20Sopenharmony_ci{ 27518c2ecf20Sopenharmony_ci platform_driver_unregister(&edma_driver); 27528c2ecf20Sopenharmony_ci platform_driver_unregister(&edma_tptc_driver); 27538c2ecf20Sopenharmony_ci} 27548c2ecf20Sopenharmony_cimodule_exit(edma_exit); 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matt Porter <matt.porter@linaro.org>"); 27578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI EDMA DMA engine driver"); 27588c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2759