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