18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for the Atmel Extensible DMA Controller (aka XDMAC on AT91 systems)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Atmel Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Ludovic Desroches <ludovic.desroches@atmel.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/barrier.h>
118c2ecf20Sopenharmony_ci#include <dt-bindings/dma/at91.h>
128c2ecf20Sopenharmony_ci#include <linux/clk.h>
138c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
148c2ecf20Sopenharmony_ci#include <linux/dmapool.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/irq.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/list.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/of_dma.h>
218c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
228c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
238c2ecf20Sopenharmony_ci#include <linux/pm.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "dmaengine.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* Global registers */
288c2ecf20Sopenharmony_ci#define AT_XDMAC_GTYPE		0x00	/* Global Type Register */
298c2ecf20Sopenharmony_ci#define		AT_XDMAC_NB_CH(i)	(((i) & 0x1F) + 1)		/* Number of Channels Minus One */
308c2ecf20Sopenharmony_ci#define		AT_XDMAC_FIFO_SZ(i)	(((i) >> 5) & 0x7FF)		/* Number of Bytes */
318c2ecf20Sopenharmony_ci#define		AT_XDMAC_NB_REQ(i)	((((i) >> 16) & 0x3F) + 1)	/* Number of Peripheral Requests Minus One */
328c2ecf20Sopenharmony_ci#define AT_XDMAC_GCFG		0x04	/* Global Configuration Register */
338c2ecf20Sopenharmony_ci#define AT_XDMAC_GWAC		0x08	/* Global Weighted Arbiter Configuration Register */
348c2ecf20Sopenharmony_ci#define AT_XDMAC_GIE		0x0C	/* Global Interrupt Enable Register */
358c2ecf20Sopenharmony_ci#define AT_XDMAC_GID		0x10	/* Global Interrupt Disable Register */
368c2ecf20Sopenharmony_ci#define AT_XDMAC_GIM		0x14	/* Global Interrupt Mask Register */
378c2ecf20Sopenharmony_ci#define AT_XDMAC_GIS		0x18	/* Global Interrupt Status Register */
388c2ecf20Sopenharmony_ci#define AT_XDMAC_GE		0x1C	/* Global Channel Enable Register */
398c2ecf20Sopenharmony_ci#define AT_XDMAC_GD		0x20	/* Global Channel Disable Register */
408c2ecf20Sopenharmony_ci#define AT_XDMAC_GS		0x24	/* Global Channel Status Register */
418c2ecf20Sopenharmony_ci#define AT_XDMAC_GRS		0x28	/* Global Channel Read Suspend Register */
428c2ecf20Sopenharmony_ci#define AT_XDMAC_GWS		0x2C	/* Global Write Suspend Register */
438c2ecf20Sopenharmony_ci#define AT_XDMAC_GRWS		0x30	/* Global Channel Read Write Suspend Register */
448c2ecf20Sopenharmony_ci#define AT_XDMAC_GRWR		0x34	/* Global Channel Read Write Resume Register */
458c2ecf20Sopenharmony_ci#define AT_XDMAC_GSWR		0x38	/* Global Channel Software Request Register */
468c2ecf20Sopenharmony_ci#define AT_XDMAC_GSWS		0x3C	/* Global channel Software Request Status Register */
478c2ecf20Sopenharmony_ci#define AT_XDMAC_GSWF		0x40	/* Global Channel Software Flush Request Register */
488c2ecf20Sopenharmony_ci#define AT_XDMAC_VERSION	0xFFC	/* XDMAC Version Register */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Channel relative registers offsets */
518c2ecf20Sopenharmony_ci#define AT_XDMAC_CIE		0x00	/* Channel Interrupt Enable Register */
528c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIE_BIE	BIT(0)	/* End of Block Interrupt Enable Bit */
538c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIE_LIE	BIT(1)	/* End of Linked List Interrupt Enable Bit */
548c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIE_DIE	BIT(2)	/* End of Disable Interrupt Enable Bit */
558c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIE_FIE	BIT(3)	/* End of Flush Interrupt Enable Bit */
568c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIE_RBEIE	BIT(4)	/* Read Bus Error Interrupt Enable Bit */
578c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIE_WBEIE	BIT(5)	/* Write Bus Error Interrupt Enable Bit */
588c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIE_ROIE	BIT(6)	/* Request Overflow Interrupt Enable Bit */
598c2ecf20Sopenharmony_ci#define AT_XDMAC_CID		0x04	/* Channel Interrupt Disable Register */
608c2ecf20Sopenharmony_ci#define		AT_XDMAC_CID_BID	BIT(0)	/* End of Block Interrupt Disable Bit */
618c2ecf20Sopenharmony_ci#define		AT_XDMAC_CID_LID	BIT(1)	/* End of Linked List Interrupt Disable Bit */
628c2ecf20Sopenharmony_ci#define		AT_XDMAC_CID_DID	BIT(2)	/* End of Disable Interrupt Disable Bit */
638c2ecf20Sopenharmony_ci#define		AT_XDMAC_CID_FID	BIT(3)	/* End of Flush Interrupt Disable Bit */
648c2ecf20Sopenharmony_ci#define		AT_XDMAC_CID_RBEID	BIT(4)	/* Read Bus Error Interrupt Disable Bit */
658c2ecf20Sopenharmony_ci#define		AT_XDMAC_CID_WBEID	BIT(5)	/* Write Bus Error Interrupt Disable Bit */
668c2ecf20Sopenharmony_ci#define		AT_XDMAC_CID_ROID	BIT(6)	/* Request Overflow Interrupt Disable Bit */
678c2ecf20Sopenharmony_ci#define AT_XDMAC_CIM		0x08	/* Channel Interrupt Mask Register */
688c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIM_BIM	BIT(0)	/* End of Block Interrupt Mask Bit */
698c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIM_LIM	BIT(1)	/* End of Linked List Interrupt Mask Bit */
708c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIM_DIM	BIT(2)	/* End of Disable Interrupt Mask Bit */
718c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIM_FIM	BIT(3)	/* End of Flush Interrupt Mask Bit */
728c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIM_RBEIM	BIT(4)	/* Read Bus Error Interrupt Mask Bit */
738c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIM_WBEIM	BIT(5)	/* Write Bus Error Interrupt Mask Bit */
748c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIM_ROIM	BIT(6)	/* Request Overflow Interrupt Mask Bit */
758c2ecf20Sopenharmony_ci#define AT_XDMAC_CIS		0x0C	/* Channel Interrupt Status Register */
768c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIS_BIS	BIT(0)	/* End of Block Interrupt Status Bit */
778c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIS_LIS	BIT(1)	/* End of Linked List Interrupt Status Bit */
788c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIS_DIS	BIT(2)	/* End of Disable Interrupt Status Bit */
798c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIS_FIS	BIT(3)	/* End of Flush Interrupt Status Bit */
808c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIS_RBEIS	BIT(4)	/* Read Bus Error Interrupt Status Bit */
818c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIS_WBEIS	BIT(5)	/* Write Bus Error Interrupt Status Bit */
828c2ecf20Sopenharmony_ci#define		AT_XDMAC_CIS_ROIS	BIT(6)	/* Request Overflow Interrupt Status Bit */
838c2ecf20Sopenharmony_ci#define AT_XDMAC_CSA		0x10	/* Channel Source Address Register */
848c2ecf20Sopenharmony_ci#define AT_XDMAC_CDA		0x14	/* Channel Destination Address Register */
858c2ecf20Sopenharmony_ci#define AT_XDMAC_CNDA		0x18	/* Channel Next Descriptor Address Register */
868c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDA_NDAIF(i)	((i) & 0x1)			/* Channel x Next Descriptor Interface */
878c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDA_NDA(i)	((i) & 0xfffffffc)		/* Channel x Next Descriptor Address */
888c2ecf20Sopenharmony_ci#define AT_XDMAC_CNDC		0x1C	/* Channel Next Descriptor Control Register */
898c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDE		(0x1 << 0)		/* Channel x Next Descriptor Enable */
908c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDSUP		(0x1 << 1)		/* Channel x Next Descriptor Source Update */
918c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDDUP		(0x1 << 2)		/* Channel x Next Descriptor Destination Update */
928c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDVIEW_MASK	GENMASK(28, 27)
938c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDVIEW_NDV0	(0x0 << 3)		/* Channel x Next Descriptor View 0 */
948c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDVIEW_NDV1	(0x1 << 3)		/* Channel x Next Descriptor View 1 */
958c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDVIEW_NDV2	(0x2 << 3)		/* Channel x Next Descriptor View 2 */
968c2ecf20Sopenharmony_ci#define		AT_XDMAC_CNDC_NDVIEW_NDV3	(0x3 << 3)		/* Channel x Next Descriptor View 3 */
978c2ecf20Sopenharmony_ci#define AT_XDMAC_CUBC		0x20	/* Channel Microblock Control Register */
988c2ecf20Sopenharmony_ci#define AT_XDMAC_CBC		0x24	/* Channel Block Control Register */
998c2ecf20Sopenharmony_ci#define AT_XDMAC_CC		0x28	/* Channel Configuration Register */
1008c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_TYPE	(0x1 << 0)	/* Channel Transfer Type */
1018c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_TYPE_MEM_TRAN	(0x0 << 0)	/* Memory to Memory Transfer */
1028c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_TYPE_PER_TRAN	(0x1 << 0)	/* Peripheral to Memory or Memory to Peripheral Transfer */
1038c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_MBSIZE_MASK	(0x3 << 1)
1048c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_MBSIZE_SINGLE	(0x0 << 1)
1058c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_MBSIZE_FOUR		(0x1 << 1)
1068c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_MBSIZE_EIGHT	(0x2 << 1)
1078c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_MBSIZE_SIXTEEN	(0x3 << 1)
1088c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_DSYNC	(0x1 << 4)	/* Channel Synchronization */
1098c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DSYNC_PER2MEM	(0x0 << 4)
1108c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DSYNC_MEM2PER	(0x1 << 4)
1118c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_PROT	(0x1 << 5)	/* Channel Protection */
1128c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_PROT_SEC		(0x0 << 5)
1138c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_PROT_UNSEC		(0x1 << 5)
1148c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_SWREQ	(0x1 << 6)	/* Channel Software Request Trigger */
1158c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_SWREQ_HWR_CONNECTED	(0x0 << 6)
1168c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_SWREQ_SWR_CONNECTED	(0x1 << 6)
1178c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_MEMSET	(0x1 << 7)	/* Channel Fill Block of memory */
1188c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_MEMSET_NORMAL_MODE	(0x0 << 7)
1198c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_MEMSET_HW_MODE	(0x1 << 7)
1208c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_CSIZE(i)	((0x7 & (i)) << 8)	/* Channel Chunk Size */
1218c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_DWIDTH_OFFSET	11
1228c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_DWIDTH_MASK	(0x3 << AT_XDMAC_CC_DWIDTH_OFFSET)
1238c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_DWIDTH(i)	((0x3 & (i)) << AT_XDMAC_CC_DWIDTH_OFFSET)	/* Channel Data Width */
1248c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DWIDTH_BYTE		0x0
1258c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DWIDTH_HALFWORD	0x1
1268c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DWIDTH_WORD		0x2
1278c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DWIDTH_DWORD	0x3
1288c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_SIF(i)	((0x1 & (i)) << 13)	/* Channel Source Interface Identifier */
1298c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_DIF(i)	((0x1 & (i)) << 14)	/* Channel Destination Interface Identifier */
1308c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_SAM_MASK	(0x3 << 16)	/* Channel Source Addressing Mode */
1318c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_SAM_FIXED_AM	(0x0 << 16)
1328c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_SAM_INCREMENTED_AM	(0x1 << 16)
1338c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_SAM_UBS_AM		(0x2 << 16)
1348c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_SAM_UBS_DS_AM	(0x3 << 16)
1358c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_DAM_MASK	(0x3 << 18)	/* Channel Source Addressing Mode */
1368c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DAM_FIXED_AM	(0x0 << 18)
1378c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DAM_INCREMENTED_AM	(0x1 << 18)
1388c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DAM_UBS_AM		(0x2 << 18)
1398c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_DAM_UBS_DS_AM	(0x3 << 18)
1408c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_INITD	(0x1 << 21)	/* Channel Initialization Terminated (read only) */
1418c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_INITD_TERMINATED	(0x0 << 21)
1428c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_INITD_IN_PROGRESS	(0x1 << 21)
1438c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_RDIP	(0x1 << 22)	/* Read in Progress (read only) */
1448c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_RDIP_DONE		(0x0 << 22)
1458c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_RDIP_IN_PROGRESS	(0x1 << 22)
1468c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_WRIP	(0x1 << 23)	/* Write in Progress (read only) */
1478c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_WRIP_DONE		(0x0 << 23)
1488c2ecf20Sopenharmony_ci#define			AT_XDMAC_CC_WRIP_IN_PROGRESS	(0x1 << 23)
1498c2ecf20Sopenharmony_ci#define		AT_XDMAC_CC_PERID(i)	((0x7f & (i)) << 24)	/* Channel Peripheral Identifier */
1508c2ecf20Sopenharmony_ci#define AT_XDMAC_CDS_MSP	0x2C	/* Channel Data Stride Memory Set Pattern */
1518c2ecf20Sopenharmony_ci#define AT_XDMAC_CSUS		0x30	/* Channel Source Microblock Stride */
1528c2ecf20Sopenharmony_ci#define AT_XDMAC_CDUS		0x34	/* Channel Destination Microblock Stride */
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#define AT_XDMAC_CHAN_REG_BASE	0x50	/* Channel registers base address */
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* Microblock control members */
1578c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_UBLEN_MAX	0xFFFFFFUL	/* Maximum Microblock Length */
1588c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDE		(0x1 << 24)	/* Next Descriptor Enable */
1598c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NSEN		(0x1 << 25)	/* Next Descriptor Source Update */
1608c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDEN		(0x1 << 26)	/* Next Descriptor Destination Update */
1618c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV0		(0x0 << 27)	/* Next Descriptor View 0 */
1628c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV1		(0x1 << 27)	/* Next Descriptor View 1 */
1638c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV2		(0x2 << 27)	/* Next Descriptor View 2 */
1648c2ecf20Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV3		(0x3 << 27)	/* Next Descriptor View 3 */
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#define AT_XDMAC_MAX_CHAN	0x20
1678c2ecf20Sopenharmony_ci#define AT_XDMAC_MAX_CSIZE	16	/* 16 data */
1688c2ecf20Sopenharmony_ci#define AT_XDMAC_MAX_DWIDTH	8	/* 64 bits */
1698c2ecf20Sopenharmony_ci#define AT_XDMAC_RESIDUE_MAX_RETRIES	5
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci#define AT_XDMAC_DMA_BUSWIDTHS\
1728c2ecf20Sopenharmony_ci	(BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\
1738c2ecf20Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |\
1748c2ecf20Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |\
1758c2ecf20Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |\
1768c2ecf20Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_8_BYTES))
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cienum atc_status {
1798c2ecf20Sopenharmony_ci	AT_XDMAC_CHAN_IS_CYCLIC = 0,
1808c2ecf20Sopenharmony_ci	AT_XDMAC_CHAN_IS_PAUSED,
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/* ----- Channels ----- */
1848c2ecf20Sopenharmony_cistruct at_xdmac_chan {
1858c2ecf20Sopenharmony_ci	struct dma_chan			chan;
1868c2ecf20Sopenharmony_ci	void __iomem			*ch_regs;
1878c2ecf20Sopenharmony_ci	u32				mask;		/* Channel Mask */
1888c2ecf20Sopenharmony_ci	u32				cfg;		/* Channel Configuration Register */
1898c2ecf20Sopenharmony_ci	u8				perid;		/* Peripheral ID */
1908c2ecf20Sopenharmony_ci	u8				perif;		/* Peripheral Interface */
1918c2ecf20Sopenharmony_ci	u8				memif;		/* Memory Interface */
1928c2ecf20Sopenharmony_ci	u32				save_cc;
1938c2ecf20Sopenharmony_ci	u32				save_cim;
1948c2ecf20Sopenharmony_ci	u32				save_cnda;
1958c2ecf20Sopenharmony_ci	u32				save_cndc;
1968c2ecf20Sopenharmony_ci	u32				irq_status;
1978c2ecf20Sopenharmony_ci	unsigned long			status;
1988c2ecf20Sopenharmony_ci	struct tasklet_struct		tasklet;
1998c2ecf20Sopenharmony_ci	struct dma_slave_config		sconfig;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	spinlock_t			lock;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	struct list_head		xfers_list;
2048c2ecf20Sopenharmony_ci	struct list_head		free_descs_list;
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/* ----- Controller ----- */
2098c2ecf20Sopenharmony_cistruct at_xdmac {
2108c2ecf20Sopenharmony_ci	struct dma_device	dma;
2118c2ecf20Sopenharmony_ci	void __iomem		*regs;
2128c2ecf20Sopenharmony_ci	int			irq;
2138c2ecf20Sopenharmony_ci	struct clk		*clk;
2148c2ecf20Sopenharmony_ci	u32			save_gim;
2158c2ecf20Sopenharmony_ci	u32			save_gs;
2168c2ecf20Sopenharmony_ci	struct dma_pool		*at_xdmac_desc_pool;
2178c2ecf20Sopenharmony_ci	struct at_xdmac_chan	chan[];
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/* ----- Descriptors ----- */
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/* Linked List Descriptor */
2248c2ecf20Sopenharmony_cistruct at_xdmac_lld {
2258c2ecf20Sopenharmony_ci	u32 mbr_nda;	/* Next Descriptor Member */
2268c2ecf20Sopenharmony_ci	u32 mbr_ubc;	/* Microblock Control Member */
2278c2ecf20Sopenharmony_ci	u32 mbr_sa;	/* Source Address Member */
2288c2ecf20Sopenharmony_ci	u32 mbr_da;	/* Destination Address Member */
2298c2ecf20Sopenharmony_ci	u32 mbr_cfg;	/* Configuration Register */
2308c2ecf20Sopenharmony_ci	u32 mbr_bc;	/* Block Control Register */
2318c2ecf20Sopenharmony_ci	u32 mbr_ds;	/* Data Stride Register */
2328c2ecf20Sopenharmony_ci	u32 mbr_sus;	/* Source Microblock Stride Register */
2338c2ecf20Sopenharmony_ci	u32 mbr_dus;	/* Destination Microblock Stride Register */
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/* 64-bit alignment needed to update CNDA and CUBC registers in an atomic way. */
2378c2ecf20Sopenharmony_cistruct at_xdmac_desc {
2388c2ecf20Sopenharmony_ci	struct at_xdmac_lld		lld;
2398c2ecf20Sopenharmony_ci	enum dma_transfer_direction	direction;
2408c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor	tx_dma_desc;
2418c2ecf20Sopenharmony_ci	struct list_head		desc_node;
2428c2ecf20Sopenharmony_ci	/* Following members are only used by the first descriptor */
2438c2ecf20Sopenharmony_ci	bool				active_xfer;
2448c2ecf20Sopenharmony_ci	unsigned int			xfer_size;
2458c2ecf20Sopenharmony_ci	struct list_head		descs_list;
2468c2ecf20Sopenharmony_ci	struct list_head		xfer_node;
2478c2ecf20Sopenharmony_ci} __aligned(sizeof(u64));
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic inline void __iomem *at_xdmac_chan_reg_base(struct at_xdmac *atxdmac, unsigned int chan_nb)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	return atxdmac->regs + (AT_XDMAC_CHAN_REG_BASE + chan_nb * 0x40);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci#define at_xdmac_read(atxdmac, reg) readl_relaxed((atxdmac)->regs + (reg))
2558c2ecf20Sopenharmony_ci#define at_xdmac_write(atxdmac, reg, value) \
2568c2ecf20Sopenharmony_ci	writel_relaxed((value), (atxdmac)->regs + (reg))
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci#define at_xdmac_chan_read(atchan, reg) readl_relaxed((atchan)->ch_regs + (reg))
2598c2ecf20Sopenharmony_ci#define at_xdmac_chan_write(atchan, reg, value) writel_relaxed((value), (atchan)->ch_regs + (reg))
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic inline struct at_xdmac_chan *to_at_xdmac_chan(struct dma_chan *dchan)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	return container_of(dchan, struct at_xdmac_chan, chan);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic struct device *chan2dev(struct dma_chan *chan)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	return &chan->dev->device;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic inline struct at_xdmac *to_at_xdmac(struct dma_device *ddev)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	return container_of(ddev, struct at_xdmac, dma);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic inline struct at_xdmac_desc *txd_to_at_desc(struct dma_async_tx_descriptor *txd)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	return container_of(txd, struct at_xdmac_desc, tx_dma_desc);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic inline int at_xdmac_chan_is_cyclic(struct at_xdmac_chan *atchan)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	return test_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic inline int at_xdmac_chan_is_paused(struct at_xdmac_chan *atchan)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	return test_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic inline int at_xdmac_csize(u32 maxburst)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	int csize;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	csize = ffs(maxburst) - 1;
2968c2ecf20Sopenharmony_ci	if (csize > 4)
2978c2ecf20Sopenharmony_ci		csize = -EINVAL;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return csize;
3008c2ecf20Sopenharmony_ci};
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic inline bool at_xdmac_chan_is_peripheral_xfer(u32 cfg)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	return cfg & AT_XDMAC_CC_TYPE_PER_TRAN;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic inline u8 at_xdmac_get_dwidth(u32 cfg)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	return (cfg & AT_XDMAC_CC_DWIDTH_MASK) >> AT_XDMAC_CC_DWIDTH_OFFSET;
3108c2ecf20Sopenharmony_ci};
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic unsigned int init_nr_desc_per_channel = 64;
3138c2ecf20Sopenharmony_cimodule_param(init_nr_desc_per_channel, uint, 0644);
3148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(init_nr_desc_per_channel,
3158c2ecf20Sopenharmony_ci		 "initial descriptors per channel (default: 64)");
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	return at_xdmac_chan_read(atchan, AT_XDMAC_GS) & atchan->mask;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void at_xdmac_off(struct at_xdmac *atxdmac)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GD, -1L);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* Wait that all chans are disabled. */
3288c2ecf20Sopenharmony_ci	while (at_xdmac_read(atxdmac, AT_XDMAC_GS))
3298c2ecf20Sopenharmony_ci		cpu_relax();
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GID, -1L);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/* Call with lock hold. */
3358c2ecf20Sopenharmony_cistatic void at_xdmac_start_xfer(struct at_xdmac_chan *atchan,
3368c2ecf20Sopenharmony_ci				struct at_xdmac_desc *first)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct at_xdmac	*atxdmac = to_at_xdmac(atchan->chan.device);
3398c2ecf20Sopenharmony_ci	u32		reg;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, first);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Set transfer as active to not try to start it again. */
3448c2ecf20Sopenharmony_ci	first->active_xfer = true;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* Tell xdmac where to get the first descriptor. */
3478c2ecf20Sopenharmony_ci	reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys)
3488c2ecf20Sopenharmony_ci	      | AT_XDMAC_CNDA_NDAIF(atchan->memif);
3498c2ecf20Sopenharmony_ci	at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, reg);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/*
3528c2ecf20Sopenharmony_ci	 * When doing non cyclic transfer we need to use the next
3538c2ecf20Sopenharmony_ci	 * descriptor view 2 since some fields of the configuration register
3548c2ecf20Sopenharmony_ci	 * depend on transfer size and src/dest addresses.
3558c2ecf20Sopenharmony_ci	 */
3568c2ecf20Sopenharmony_ci	if (at_xdmac_chan_is_cyclic(atchan))
3578c2ecf20Sopenharmony_ci		reg = AT_XDMAC_CNDC_NDVIEW_NDV1;
3588c2ecf20Sopenharmony_ci	else if ((first->lld.mbr_ubc &
3598c2ecf20Sopenharmony_ci		  AT_XDMAC_CNDC_NDVIEW_MASK) == AT_XDMAC_MBR_UBC_NDV3)
3608c2ecf20Sopenharmony_ci		reg = AT_XDMAC_CNDC_NDVIEW_NDV3;
3618c2ecf20Sopenharmony_ci	else
3628c2ecf20Sopenharmony_ci		reg = AT_XDMAC_CNDC_NDVIEW_NDV2;
3638c2ecf20Sopenharmony_ci	/*
3648c2ecf20Sopenharmony_ci	 * Even if the register will be updated from the configuration in the
3658c2ecf20Sopenharmony_ci	 * descriptor when using view 2 or higher, the PROT bit won't be set
3668c2ecf20Sopenharmony_ci	 * properly. This bit can be modified only by using the channel
3678c2ecf20Sopenharmony_ci	 * configuration register.
3688c2ecf20Sopenharmony_ci	 */
3698c2ecf20Sopenharmony_ci	at_xdmac_chan_write(atchan, AT_XDMAC_CC, first->lld.mbr_cfg);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	reg |= AT_XDMAC_CNDC_NDDUP
3728c2ecf20Sopenharmony_ci	       | AT_XDMAC_CNDC_NDSUP
3738c2ecf20Sopenharmony_ci	       | AT_XDMAC_CNDC_NDE;
3748c2ecf20Sopenharmony_ci	at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, reg);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	dev_vdbg(chan2dev(&atchan->chan),
3778c2ecf20Sopenharmony_ci		 "%s: CC=0x%08x CNDA=0x%08x, CNDC=0x%08x, CSA=0x%08x, CDA=0x%08x, CUBC=0x%08x\n",
3788c2ecf20Sopenharmony_ci		 __func__, at_xdmac_chan_read(atchan, AT_XDMAC_CC),
3798c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CNDA),
3808c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CNDC),
3818c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CSA),
3828c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CDA),
3838c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CUBC));
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	at_xdmac_chan_write(atchan, AT_XDMAC_CID, 0xffffffff);
3868c2ecf20Sopenharmony_ci	reg = AT_XDMAC_CIE_RBEIE | AT_XDMAC_CIE_WBEIE;
3878c2ecf20Sopenharmony_ci	/*
3888c2ecf20Sopenharmony_ci	 * Request Overflow Error is only for peripheral synchronized transfers
3898c2ecf20Sopenharmony_ci	 */
3908c2ecf20Sopenharmony_ci	if (at_xdmac_chan_is_peripheral_xfer(first->lld.mbr_cfg))
3918c2ecf20Sopenharmony_ci		reg |= AT_XDMAC_CIE_ROIE;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/*
3948c2ecf20Sopenharmony_ci	 * There is no end of list when doing cyclic dma, we need to get
3958c2ecf20Sopenharmony_ci	 * an interrupt after each periods.
3968c2ecf20Sopenharmony_ci	 */
3978c2ecf20Sopenharmony_ci	if (at_xdmac_chan_is_cyclic(atchan))
3988c2ecf20Sopenharmony_ci		at_xdmac_chan_write(atchan, AT_XDMAC_CIE,
3998c2ecf20Sopenharmony_ci				    reg | AT_XDMAC_CIE_BIE);
4008c2ecf20Sopenharmony_ci	else
4018c2ecf20Sopenharmony_ci		at_xdmac_chan_write(atchan, AT_XDMAC_CIE,
4028c2ecf20Sopenharmony_ci				    reg | AT_XDMAC_CIE_LIE);
4038c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GIE, atchan->mask);
4048c2ecf20Sopenharmony_ci	dev_vdbg(chan2dev(&atchan->chan),
4058c2ecf20Sopenharmony_ci		 "%s: enable channel (0x%08x)\n", __func__, atchan->mask);
4068c2ecf20Sopenharmony_ci	wmb();
4078c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	dev_vdbg(chan2dev(&atchan->chan),
4108c2ecf20Sopenharmony_ci		 "%s: CC=0x%08x CNDA=0x%08x, CNDC=0x%08x, CSA=0x%08x, CDA=0x%08x, CUBC=0x%08x\n",
4118c2ecf20Sopenharmony_ci		 __func__, at_xdmac_chan_read(atchan, AT_XDMAC_CC),
4128c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CNDA),
4138c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CNDC),
4148c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CSA),
4158c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CDA),
4168c2ecf20Sopenharmony_ci		 at_xdmac_chan_read(atchan, AT_XDMAC_CUBC));
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic dma_cookie_t at_xdmac_tx_submit(struct dma_async_tx_descriptor *tx)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc = txd_to_at_desc(tx);
4238c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(tx->chan);
4248c2ecf20Sopenharmony_ci	dma_cookie_t		cookie;
4258c2ecf20Sopenharmony_ci	unsigned long		irqflags;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, irqflags);
4288c2ecf20Sopenharmony_ci	cookie = dma_cookie_assign(tx);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	list_add_tail(&desc->xfer_node, &atchan->xfers_list);
4318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, irqflags);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	dev_vdbg(chan2dev(tx->chan), "%s: atchan 0x%p, add desc 0x%p to xfers_list\n",
4348c2ecf20Sopenharmony_ci		 __func__, atchan, desc);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return cookie;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic struct at_xdmac_desc *at_xdmac_alloc_desc(struct dma_chan *chan,
4408c2ecf20Sopenharmony_ci						 gfp_t gfp_flags)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc;
4438c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = to_at_xdmac(chan->device);
4448c2ecf20Sopenharmony_ci	dma_addr_t		phys;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	desc = dma_pool_zalloc(atxdmac->at_xdmac_desc_pool, gfp_flags, &phys);
4478c2ecf20Sopenharmony_ci	if (desc) {
4488c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&desc->descs_list);
4498c2ecf20Sopenharmony_ci		dma_async_tx_descriptor_init(&desc->tx_dma_desc, chan);
4508c2ecf20Sopenharmony_ci		desc->tx_dma_desc.tx_submit = at_xdmac_tx_submit;
4518c2ecf20Sopenharmony_ci		desc->tx_dma_desc.phys = phys;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	return desc;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void at_xdmac_init_used_desc(struct at_xdmac_desc *desc)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	memset(&desc->lld, 0, sizeof(desc->lld));
4608c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&desc->descs_list);
4618c2ecf20Sopenharmony_ci	desc->direction = DMA_TRANS_NONE;
4628c2ecf20Sopenharmony_ci	desc->xfer_size = 0;
4638c2ecf20Sopenharmony_ci	desc->active_xfer = false;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci/* Call must be protected by lock. */
4678c2ecf20Sopenharmony_cistatic struct at_xdmac_desc *at_xdmac_get_desc(struct at_xdmac_chan *atchan)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct at_xdmac_desc *desc;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (list_empty(&atchan->free_descs_list)) {
4728c2ecf20Sopenharmony_ci		desc = at_xdmac_alloc_desc(&atchan->chan, GFP_NOWAIT);
4738c2ecf20Sopenharmony_ci	} else {
4748c2ecf20Sopenharmony_ci		desc = list_first_entry(&atchan->free_descs_list,
4758c2ecf20Sopenharmony_ci					struct at_xdmac_desc, desc_node);
4768c2ecf20Sopenharmony_ci		list_del(&desc->desc_node);
4778c2ecf20Sopenharmony_ci		at_xdmac_init_used_desc(desc);
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return desc;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic void at_xdmac_queue_desc(struct dma_chan *chan,
4848c2ecf20Sopenharmony_ci				struct at_xdmac_desc *prev,
4858c2ecf20Sopenharmony_ci				struct at_xdmac_desc *desc)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	if (!prev || !desc)
4888c2ecf20Sopenharmony_ci		return;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	prev->lld.mbr_nda = desc->tx_dma_desc.phys;
4918c2ecf20Sopenharmony_ci	prev->lld.mbr_ubc |= AT_XDMAC_MBR_UBC_NDE;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan),	"%s: chain lld: prev=0x%p, mbr_nda=%pad\n",
4948c2ecf20Sopenharmony_ci		__func__, prev, &prev->lld.mbr_nda);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic inline void at_xdmac_increment_block_count(struct dma_chan *chan,
4988c2ecf20Sopenharmony_ci						  struct at_xdmac_desc *desc)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	if (!desc)
5018c2ecf20Sopenharmony_ci		return;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	desc->lld.mbr_bc++;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan),
5068c2ecf20Sopenharmony_ci		"%s: incrementing the block count of the desc 0x%p\n",
5078c2ecf20Sopenharmony_ci		__func__, desc);
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic struct dma_chan *at_xdmac_xlate(struct of_phandle_args *dma_spec,
5118c2ecf20Sopenharmony_ci				       struct of_dma *of_dma)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = of_dma->of_dma_data;
5148c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan;
5158c2ecf20Sopenharmony_ci	struct dma_chan		*chan;
5168c2ecf20Sopenharmony_ci	struct device		*dev = atxdmac->dma.dev;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (dma_spec->args_count != 1) {
5198c2ecf20Sopenharmony_ci		dev_err(dev, "dma phandler args: bad number of args\n");
5208c2ecf20Sopenharmony_ci		return NULL;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	chan = dma_get_any_slave_channel(&atxdmac->dma);
5248c2ecf20Sopenharmony_ci	if (!chan) {
5258c2ecf20Sopenharmony_ci		dev_err(dev, "can't get a dma channel\n");
5268c2ecf20Sopenharmony_ci		return NULL;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	atchan = to_at_xdmac_chan(chan);
5308c2ecf20Sopenharmony_ci	atchan->memif = AT91_XDMAC_DT_GET_MEM_IF(dma_spec->args[0]);
5318c2ecf20Sopenharmony_ci	atchan->perif = AT91_XDMAC_DT_GET_PER_IF(dma_spec->args[0]);
5328c2ecf20Sopenharmony_ci	atchan->perid = AT91_XDMAC_DT_GET_PERID(dma_spec->args[0]);
5338c2ecf20Sopenharmony_ci	dev_dbg(dev, "chan dt cfg: memif=%u perif=%u perid=%u\n",
5348c2ecf20Sopenharmony_ci		 atchan->memif, atchan->perif, atchan->perid);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return chan;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int at_xdmac_compute_chan_conf(struct dma_chan *chan,
5408c2ecf20Sopenharmony_ci				      enum dma_transfer_direction direction)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
5438c2ecf20Sopenharmony_ci	int			csize, dwidth;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (direction == DMA_DEV_TO_MEM) {
5468c2ecf20Sopenharmony_ci		atchan->cfg =
5478c2ecf20Sopenharmony_ci			AT91_XDMAC_DT_PERID(atchan->perid)
5488c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_DAM_INCREMENTED_AM
5498c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_SAM_FIXED_AM
5508c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_DIF(atchan->memif)
5518c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_SIF(atchan->perif)
5528c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_SWREQ_HWR_CONNECTED
5538c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_DSYNC_PER2MEM
5548c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_MBSIZE_SIXTEEN
5558c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_TYPE_PER_TRAN;
5568c2ecf20Sopenharmony_ci		csize = ffs(atchan->sconfig.src_maxburst) - 1;
5578c2ecf20Sopenharmony_ci		if (csize < 0) {
5588c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "invalid src maxburst value\n");
5598c2ecf20Sopenharmony_ci			return -EINVAL;
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci		atchan->cfg |= AT_XDMAC_CC_CSIZE(csize);
5628c2ecf20Sopenharmony_ci		dwidth = ffs(atchan->sconfig.src_addr_width) - 1;
5638c2ecf20Sopenharmony_ci		if (dwidth < 0) {
5648c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "invalid src addr width value\n");
5658c2ecf20Sopenharmony_ci			return -EINVAL;
5668c2ecf20Sopenharmony_ci		}
5678c2ecf20Sopenharmony_ci		atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth);
5688c2ecf20Sopenharmony_ci	} else if (direction == DMA_MEM_TO_DEV) {
5698c2ecf20Sopenharmony_ci		atchan->cfg =
5708c2ecf20Sopenharmony_ci			AT91_XDMAC_DT_PERID(atchan->perid)
5718c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_DAM_FIXED_AM
5728c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_SAM_INCREMENTED_AM
5738c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_DIF(atchan->perif)
5748c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_SIF(atchan->memif)
5758c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_SWREQ_HWR_CONNECTED
5768c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_DSYNC_MEM2PER
5778c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_MBSIZE_SIXTEEN
5788c2ecf20Sopenharmony_ci			| AT_XDMAC_CC_TYPE_PER_TRAN;
5798c2ecf20Sopenharmony_ci		csize = ffs(atchan->sconfig.dst_maxburst) - 1;
5808c2ecf20Sopenharmony_ci		if (csize < 0) {
5818c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "invalid src maxburst value\n");
5828c2ecf20Sopenharmony_ci			return -EINVAL;
5838c2ecf20Sopenharmony_ci		}
5848c2ecf20Sopenharmony_ci		atchan->cfg |= AT_XDMAC_CC_CSIZE(csize);
5858c2ecf20Sopenharmony_ci		dwidth = ffs(atchan->sconfig.dst_addr_width) - 1;
5868c2ecf20Sopenharmony_ci		if (dwidth < 0) {
5878c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "invalid dst addr width value\n");
5888c2ecf20Sopenharmony_ci			return -EINVAL;
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci		atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth);
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan),	"%s: cfg=0x%08x\n", __func__, atchan->cfg);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci/*
5998c2ecf20Sopenharmony_ci * Only check that maxburst and addr width values are supported by the
6008c2ecf20Sopenharmony_ci * the controller but not that the configuration is good to perform the
6018c2ecf20Sopenharmony_ci * transfer since we don't know the direction at this stage.
6028c2ecf20Sopenharmony_ci */
6038c2ecf20Sopenharmony_cistatic int at_xdmac_check_slave_config(struct dma_slave_config *sconfig)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	if ((sconfig->src_maxburst > AT_XDMAC_MAX_CSIZE)
6068c2ecf20Sopenharmony_ci	    || (sconfig->dst_maxburst > AT_XDMAC_MAX_CSIZE))
6078c2ecf20Sopenharmony_ci		return -EINVAL;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if ((sconfig->src_addr_width > AT_XDMAC_MAX_DWIDTH)
6108c2ecf20Sopenharmony_ci	    || (sconfig->dst_addr_width > AT_XDMAC_MAX_DWIDTH))
6118c2ecf20Sopenharmony_ci		return -EINVAL;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return 0;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int at_xdmac_set_slave_config(struct dma_chan *chan,
6178c2ecf20Sopenharmony_ci				      struct dma_slave_config *sconfig)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (at_xdmac_check_slave_config(sconfig)) {
6228c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan), "invalid slave configuration\n");
6238c2ecf20Sopenharmony_ci		return -EINVAL;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	memcpy(&atchan->sconfig, sconfig, sizeof(atchan->sconfig));
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	return 0;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
6328c2ecf20Sopenharmony_ciat_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
6338c2ecf20Sopenharmony_ci		       unsigned int sg_len, enum dma_transfer_direction direction,
6348c2ecf20Sopenharmony_ci		       unsigned long flags, void *context)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct at_xdmac_chan		*atchan = to_at_xdmac_chan(chan);
6378c2ecf20Sopenharmony_ci	struct at_xdmac_desc		*first = NULL, *prev = NULL;
6388c2ecf20Sopenharmony_ci	struct scatterlist		*sg;
6398c2ecf20Sopenharmony_ci	int				i;
6408c2ecf20Sopenharmony_ci	unsigned int			xfer_size = 0;
6418c2ecf20Sopenharmony_ci	unsigned long			irqflags;
6428c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor	*ret = NULL;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (!sgl)
6458c2ecf20Sopenharmony_ci		return NULL;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (!is_slave_direction(direction)) {
6488c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan), "invalid DMA direction\n");
6498c2ecf20Sopenharmony_ci		return NULL;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s: sg_len=%d, dir=%s, flags=0x%lx\n",
6538c2ecf20Sopenharmony_ci		 __func__, sg_len,
6548c2ecf20Sopenharmony_ci		 direction == DMA_MEM_TO_DEV ? "to device" : "from device",
6558c2ecf20Sopenharmony_ci		 flags);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/* Protect dma_sconfig field that can be modified by set_slave_conf. */
6588c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, irqflags);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (at_xdmac_compute_chan_conf(chan, direction))
6618c2ecf20Sopenharmony_ci		goto spin_unlock;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* Prepare descriptors. */
6648c2ecf20Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i) {
6658c2ecf20Sopenharmony_ci		struct at_xdmac_desc	*desc = NULL;
6668c2ecf20Sopenharmony_ci		u32			len, mem, dwidth, fixed_dwidth;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		len = sg_dma_len(sg);
6698c2ecf20Sopenharmony_ci		mem = sg_dma_address(sg);
6708c2ecf20Sopenharmony_ci		if (unlikely(!len)) {
6718c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "sg data length is zero\n");
6728c2ecf20Sopenharmony_ci			goto spin_unlock;
6738c2ecf20Sopenharmony_ci		}
6748c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: * sg%d len=%u, mem=0x%08x\n",
6758c2ecf20Sopenharmony_ci			 __func__, i, len, mem);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		desc = at_xdmac_get_desc(atchan);
6788c2ecf20Sopenharmony_ci		if (!desc) {
6798c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "can't get descriptor\n");
6808c2ecf20Sopenharmony_ci			if (first)
6818c2ecf20Sopenharmony_ci				list_splice_tail_init(&first->descs_list,
6828c2ecf20Sopenharmony_ci						      &atchan->free_descs_list);
6838c2ecf20Sopenharmony_ci			goto spin_unlock;
6848c2ecf20Sopenharmony_ci		}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		/* Linked list descriptor setup. */
6878c2ecf20Sopenharmony_ci		if (direction == DMA_DEV_TO_MEM) {
6888c2ecf20Sopenharmony_ci			desc->lld.mbr_sa = atchan->sconfig.src_addr;
6898c2ecf20Sopenharmony_ci			desc->lld.mbr_da = mem;
6908c2ecf20Sopenharmony_ci		} else {
6918c2ecf20Sopenharmony_ci			desc->lld.mbr_sa = mem;
6928c2ecf20Sopenharmony_ci			desc->lld.mbr_da = atchan->sconfig.dst_addr;
6938c2ecf20Sopenharmony_ci		}
6948c2ecf20Sopenharmony_ci		dwidth = at_xdmac_get_dwidth(atchan->cfg);
6958c2ecf20Sopenharmony_ci		fixed_dwidth = IS_ALIGNED(len, 1 << dwidth)
6968c2ecf20Sopenharmony_ci			       ? dwidth
6978c2ecf20Sopenharmony_ci			       : AT_XDMAC_CC_DWIDTH_BYTE;
6988c2ecf20Sopenharmony_ci		desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV2			/* next descriptor view */
6998c2ecf20Sopenharmony_ci			| AT_XDMAC_MBR_UBC_NDEN					/* next descriptor dst parameter update */
7008c2ecf20Sopenharmony_ci			| AT_XDMAC_MBR_UBC_NSEN					/* next descriptor src parameter update */
7018c2ecf20Sopenharmony_ci			| (len >> fixed_dwidth);				/* microblock length */
7028c2ecf20Sopenharmony_ci		desc->lld.mbr_cfg = (atchan->cfg & ~AT_XDMAC_CC_DWIDTH_MASK) |
7038c2ecf20Sopenharmony_ci				    AT_XDMAC_CC_DWIDTH(fixed_dwidth);
7048c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan),
7058c2ecf20Sopenharmony_ci			 "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n",
7068c2ecf20Sopenharmony_ci			 __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		/* Chain lld. */
7098c2ecf20Sopenharmony_ci		if (prev)
7108c2ecf20Sopenharmony_ci			at_xdmac_queue_desc(chan, prev, desc);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		prev = desc;
7138c2ecf20Sopenharmony_ci		if (!first)
7148c2ecf20Sopenharmony_ci			first = desc;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n",
7178c2ecf20Sopenharmony_ci			 __func__, desc, first);
7188c2ecf20Sopenharmony_ci		list_add_tail(&desc->desc_node, &first->descs_list);
7198c2ecf20Sopenharmony_ci		xfer_size += len;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	first->tx_dma_desc.flags = flags;
7248c2ecf20Sopenharmony_ci	first->xfer_size = xfer_size;
7258c2ecf20Sopenharmony_ci	first->direction = direction;
7268c2ecf20Sopenharmony_ci	ret = &first->tx_dma_desc;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cispin_unlock:
7298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, irqflags);
7308c2ecf20Sopenharmony_ci	return ret;
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
7348c2ecf20Sopenharmony_ciat_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
7358c2ecf20Sopenharmony_ci			 size_t buf_len, size_t period_len,
7368c2ecf20Sopenharmony_ci			 enum dma_transfer_direction direction,
7378c2ecf20Sopenharmony_ci			 unsigned long flags)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
7408c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*first = NULL, *prev = NULL;
7418c2ecf20Sopenharmony_ci	unsigned int		periods = buf_len / period_len;
7428c2ecf20Sopenharmony_ci	int			i;
7438c2ecf20Sopenharmony_ci	unsigned long		irqflags;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s: buf_addr=%pad, buf_len=%zd, period_len=%zd, dir=%s, flags=0x%lx\n",
7468c2ecf20Sopenharmony_ci		__func__, &buf_addr, buf_len, period_len,
7478c2ecf20Sopenharmony_ci		direction == DMA_MEM_TO_DEV ? "mem2per" : "per2mem", flags);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (!is_slave_direction(direction)) {
7508c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan), "invalid DMA direction\n");
7518c2ecf20Sopenharmony_ci		return NULL;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (test_and_set_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status)) {
7558c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan), "channel currently used\n");
7568c2ecf20Sopenharmony_ci		return NULL;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (at_xdmac_compute_chan_conf(chan, direction))
7608c2ecf20Sopenharmony_ci		return NULL;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	for (i = 0; i < periods; i++) {
7638c2ecf20Sopenharmony_ci		struct at_xdmac_desc	*desc = NULL;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci		spin_lock_irqsave(&atchan->lock, irqflags);
7668c2ecf20Sopenharmony_ci		desc = at_xdmac_get_desc(atchan);
7678c2ecf20Sopenharmony_ci		if (!desc) {
7688c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "can't get descriptor\n");
7698c2ecf20Sopenharmony_ci			if (first)
7708c2ecf20Sopenharmony_ci				list_splice_tail_init(&first->descs_list,
7718c2ecf20Sopenharmony_ci						      &atchan->free_descs_list);
7728c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&atchan->lock, irqflags);
7738c2ecf20Sopenharmony_ci			return NULL;
7748c2ecf20Sopenharmony_ci		}
7758c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&atchan->lock, irqflags);
7768c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan),
7778c2ecf20Sopenharmony_ci			"%s: desc=0x%p, tx_dma_desc.phys=%pad\n",
7788c2ecf20Sopenharmony_ci			__func__, desc, &desc->tx_dma_desc.phys);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci		if (direction == DMA_DEV_TO_MEM) {
7818c2ecf20Sopenharmony_ci			desc->lld.mbr_sa = atchan->sconfig.src_addr;
7828c2ecf20Sopenharmony_ci			desc->lld.mbr_da = buf_addr + i * period_len;
7838c2ecf20Sopenharmony_ci		} else {
7848c2ecf20Sopenharmony_ci			desc->lld.mbr_sa = buf_addr + i * period_len;
7858c2ecf20Sopenharmony_ci			desc->lld.mbr_da = atchan->sconfig.dst_addr;
7868c2ecf20Sopenharmony_ci		}
7878c2ecf20Sopenharmony_ci		desc->lld.mbr_cfg = atchan->cfg;
7888c2ecf20Sopenharmony_ci		desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1
7898c2ecf20Sopenharmony_ci			| AT_XDMAC_MBR_UBC_NDEN
7908c2ecf20Sopenharmony_ci			| AT_XDMAC_MBR_UBC_NSEN
7918c2ecf20Sopenharmony_ci			| period_len >> at_xdmac_get_dwidth(desc->lld.mbr_cfg);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan),
7948c2ecf20Sopenharmony_ci			 "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n",
7958c2ecf20Sopenharmony_ci			 __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		/* Chain lld. */
7988c2ecf20Sopenharmony_ci		if (prev)
7998c2ecf20Sopenharmony_ci			at_xdmac_queue_desc(chan, prev, desc);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci		prev = desc;
8028c2ecf20Sopenharmony_ci		if (!first)
8038c2ecf20Sopenharmony_ci			first = desc;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n",
8068c2ecf20Sopenharmony_ci			 __func__, desc, first);
8078c2ecf20Sopenharmony_ci		list_add_tail(&desc->desc_node, &first->descs_list);
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	at_xdmac_queue_desc(chan, prev, first);
8118c2ecf20Sopenharmony_ci	first->tx_dma_desc.flags = flags;
8128c2ecf20Sopenharmony_ci	first->xfer_size = buf_len;
8138c2ecf20Sopenharmony_ci	first->direction = direction;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	return &first->tx_dma_desc;
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic inline u32 at_xdmac_align_width(struct dma_chan *chan, dma_addr_t addr)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	u32 width;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	/*
8238c2ecf20Sopenharmony_ci	 * Check address alignment to select the greater data width we
8248c2ecf20Sopenharmony_ci	 * can use.
8258c2ecf20Sopenharmony_ci	 *
8268c2ecf20Sopenharmony_ci	 * Some XDMAC implementations don't provide dword transfer, in
8278c2ecf20Sopenharmony_ci	 * this case selecting dword has the same behavior as
8288c2ecf20Sopenharmony_ci	 * selecting word transfers.
8298c2ecf20Sopenharmony_ci	 */
8308c2ecf20Sopenharmony_ci	if (!(addr & 7)) {
8318c2ecf20Sopenharmony_ci		width = AT_XDMAC_CC_DWIDTH_DWORD;
8328c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: dwidth: double word\n", __func__);
8338c2ecf20Sopenharmony_ci	} else if (!(addr & 3)) {
8348c2ecf20Sopenharmony_ci		width = AT_XDMAC_CC_DWIDTH_WORD;
8358c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: dwidth: word\n", __func__);
8368c2ecf20Sopenharmony_ci	} else if (!(addr & 1)) {
8378c2ecf20Sopenharmony_ci		width = AT_XDMAC_CC_DWIDTH_HALFWORD;
8388c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: dwidth: half word\n", __func__);
8398c2ecf20Sopenharmony_ci	} else {
8408c2ecf20Sopenharmony_ci		width = AT_XDMAC_CC_DWIDTH_BYTE;
8418c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: dwidth: byte\n", __func__);
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	return width;
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cistatic struct at_xdmac_desc *
8488c2ecf20Sopenharmony_ciat_xdmac_interleaved_queue_desc(struct dma_chan *chan,
8498c2ecf20Sopenharmony_ci				struct at_xdmac_chan *atchan,
8508c2ecf20Sopenharmony_ci				struct at_xdmac_desc *prev,
8518c2ecf20Sopenharmony_ci				dma_addr_t src, dma_addr_t dst,
8528c2ecf20Sopenharmony_ci				struct dma_interleaved_template *xt,
8538c2ecf20Sopenharmony_ci				struct data_chunk *chunk)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc;
8568c2ecf20Sopenharmony_ci	u32			dwidth;
8578c2ecf20Sopenharmony_ci	unsigned long		flags;
8588c2ecf20Sopenharmony_ci	size_t			ublen;
8598c2ecf20Sopenharmony_ci	/*
8608c2ecf20Sopenharmony_ci	 * WARNING: The channel configuration is set here since there is no
8618c2ecf20Sopenharmony_ci	 * dmaengine_slave_config call in this case. Moreover we don't know the
8628c2ecf20Sopenharmony_ci	 * direction, it involves we can't dynamically set the source and dest
8638c2ecf20Sopenharmony_ci	 * interface so we have to use the same one. Only interface 0 allows EBI
8648c2ecf20Sopenharmony_ci	 * access. Hopefully we can access DDR through both ports (at least on
8658c2ecf20Sopenharmony_ci	 * SAMA5D4x), so we can use the same interface for source and dest,
8668c2ecf20Sopenharmony_ci	 * that solves the fact we don't know the direction.
8678c2ecf20Sopenharmony_ci	 * ERRATA: Even if useless for memory transfers, the PERID has to not
8688c2ecf20Sopenharmony_ci	 * match the one of another channel. If not, it could lead to spurious
8698c2ecf20Sopenharmony_ci	 * flag status.
8708c2ecf20Sopenharmony_ci	 */
8718c2ecf20Sopenharmony_ci	u32			chan_cc = AT_XDMAC_CC_PERID(0x3f)
8728c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_DIF(0)
8738c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_SIF(0)
8748c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_MBSIZE_SIXTEEN
8758c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_TYPE_MEM_TRAN;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	dwidth = at_xdmac_align_width(chan, src | dst | chunk->size);
8788c2ecf20Sopenharmony_ci	if (chunk->size >= (AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth)) {
8798c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan),
8808c2ecf20Sopenharmony_ci			"%s: chunk too big (%zu, max size %lu)...\n",
8818c2ecf20Sopenharmony_ci			__func__, chunk->size,
8828c2ecf20Sopenharmony_ci			AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth);
8838c2ecf20Sopenharmony_ci		return NULL;
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	if (prev)
8878c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan),
8888c2ecf20Sopenharmony_ci			"Adding items at the end of desc 0x%p\n", prev);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (xt->src_inc) {
8918c2ecf20Sopenharmony_ci		if (xt->src_sgl)
8928c2ecf20Sopenharmony_ci			chan_cc |=  AT_XDMAC_CC_SAM_UBS_AM;
8938c2ecf20Sopenharmony_ci		else
8948c2ecf20Sopenharmony_ci			chan_cc |=  AT_XDMAC_CC_SAM_INCREMENTED_AM;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if (xt->dst_inc) {
8988c2ecf20Sopenharmony_ci		if (xt->dst_sgl)
8998c2ecf20Sopenharmony_ci			chan_cc |=  AT_XDMAC_CC_DAM_UBS_AM;
9008c2ecf20Sopenharmony_ci		else
9018c2ecf20Sopenharmony_ci			chan_cc |=  AT_XDMAC_CC_DAM_INCREMENTED_AM;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
9058c2ecf20Sopenharmony_ci	desc = at_xdmac_get_desc(atchan);
9068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
9078c2ecf20Sopenharmony_ci	if (!desc) {
9088c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan), "can't get descriptor\n");
9098c2ecf20Sopenharmony_ci		return NULL;
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	ublen = chunk->size >> dwidth;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	desc->lld.mbr_sa = src;
9178c2ecf20Sopenharmony_ci	desc->lld.mbr_da = dst;
9188c2ecf20Sopenharmony_ci	desc->lld.mbr_sus = dmaengine_get_src_icg(xt, chunk);
9198c2ecf20Sopenharmony_ci	desc->lld.mbr_dus = dmaengine_get_dst_icg(xt, chunk);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV3
9228c2ecf20Sopenharmony_ci		| AT_XDMAC_MBR_UBC_NDEN
9238c2ecf20Sopenharmony_ci		| AT_XDMAC_MBR_UBC_NSEN
9248c2ecf20Sopenharmony_ci		| ublen;
9258c2ecf20Sopenharmony_ci	desc->lld.mbr_cfg = chan_cc;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan),
9288c2ecf20Sopenharmony_ci		"%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
9298c2ecf20Sopenharmony_ci		__func__, &desc->lld.mbr_sa, &desc->lld.mbr_da,
9308c2ecf20Sopenharmony_ci		desc->lld.mbr_ubc, desc->lld.mbr_cfg);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	/* Chain lld. */
9338c2ecf20Sopenharmony_ci	if (prev)
9348c2ecf20Sopenharmony_ci		at_xdmac_queue_desc(chan, prev, desc);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	return desc;
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
9408c2ecf20Sopenharmony_ciat_xdmac_prep_interleaved(struct dma_chan *chan,
9418c2ecf20Sopenharmony_ci			  struct dma_interleaved_template *xt,
9428c2ecf20Sopenharmony_ci			  unsigned long flags)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
9458c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*prev = NULL, *first = NULL;
9468c2ecf20Sopenharmony_ci	dma_addr_t		dst_addr, src_addr;
9478c2ecf20Sopenharmony_ci	size_t			src_skip = 0, dst_skip = 0, len = 0;
9488c2ecf20Sopenharmony_ci	struct data_chunk	*chunk;
9498c2ecf20Sopenharmony_ci	int			i;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	if (!xt || !xt->numf || (xt->dir != DMA_MEM_TO_MEM))
9528c2ecf20Sopenharmony_ci		return NULL;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	/*
9558c2ecf20Sopenharmony_ci	 * TODO: Handle the case where we have to repeat a chain of
9568c2ecf20Sopenharmony_ci	 * descriptors...
9578c2ecf20Sopenharmony_ci	 */
9588c2ecf20Sopenharmony_ci	if ((xt->numf > 1) && (xt->frame_size > 1))
9598c2ecf20Sopenharmony_ci		return NULL;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s: src=%pad, dest=%pad, numf=%zu, frame_size=%zu, flags=0x%lx\n",
9628c2ecf20Sopenharmony_ci		__func__, &xt->src_start, &xt->dst_start,	xt->numf,
9638c2ecf20Sopenharmony_ci		xt->frame_size, flags);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	src_addr = xt->src_start;
9668c2ecf20Sopenharmony_ci	dst_addr = xt->dst_start;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	if (xt->numf > 1) {
9698c2ecf20Sopenharmony_ci		first = at_xdmac_interleaved_queue_desc(chan, atchan,
9708c2ecf20Sopenharmony_ci							NULL,
9718c2ecf20Sopenharmony_ci							src_addr, dst_addr,
9728c2ecf20Sopenharmony_ci							xt, xt->sgl);
9738c2ecf20Sopenharmony_ci		if (!first)
9748c2ecf20Sopenharmony_ci			return NULL;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		/* Length of the block is (BLEN+1) microblocks. */
9778c2ecf20Sopenharmony_ci		for (i = 0; i < xt->numf - 1; i++)
9788c2ecf20Sopenharmony_ci			at_xdmac_increment_block_count(chan, first);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n",
9818c2ecf20Sopenharmony_ci			__func__, first, first);
9828c2ecf20Sopenharmony_ci		list_add_tail(&first->desc_node, &first->descs_list);
9838c2ecf20Sopenharmony_ci	} else {
9848c2ecf20Sopenharmony_ci		for (i = 0; i < xt->frame_size; i++) {
9858c2ecf20Sopenharmony_ci			size_t src_icg = 0, dst_icg = 0;
9868c2ecf20Sopenharmony_ci			struct at_xdmac_desc *desc;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci			chunk = xt->sgl + i;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci			dst_icg = dmaengine_get_dst_icg(xt, chunk);
9918c2ecf20Sopenharmony_ci			src_icg = dmaengine_get_src_icg(xt, chunk);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci			src_skip = chunk->size + src_icg;
9948c2ecf20Sopenharmony_ci			dst_skip = chunk->size + dst_icg;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci			dev_dbg(chan2dev(chan),
9978c2ecf20Sopenharmony_ci				"%s: chunk size=%zu, src icg=%zu, dst icg=%zu\n",
9988c2ecf20Sopenharmony_ci				__func__, chunk->size, src_icg, dst_icg);
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci			desc = at_xdmac_interleaved_queue_desc(chan, atchan,
10018c2ecf20Sopenharmony_ci							       prev,
10028c2ecf20Sopenharmony_ci							       src_addr, dst_addr,
10038c2ecf20Sopenharmony_ci							       xt, chunk);
10048c2ecf20Sopenharmony_ci			if (!desc) {
10058c2ecf20Sopenharmony_ci				if (first)
10068c2ecf20Sopenharmony_ci					list_splice_tail_init(&first->descs_list,
10078c2ecf20Sopenharmony_ci							      &atchan->free_descs_list);
10088c2ecf20Sopenharmony_ci				return NULL;
10098c2ecf20Sopenharmony_ci			}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci			if (!first)
10128c2ecf20Sopenharmony_ci				first = desc;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci			dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n",
10158c2ecf20Sopenharmony_ci				__func__, desc, first);
10168c2ecf20Sopenharmony_ci			list_add_tail(&desc->desc_node, &first->descs_list);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci			if (xt->src_sgl)
10198c2ecf20Sopenharmony_ci				src_addr += src_skip;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci			if (xt->dst_sgl)
10228c2ecf20Sopenharmony_ci				dst_addr += dst_skip;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci			len += chunk->size;
10258c2ecf20Sopenharmony_ci			prev = desc;
10268c2ecf20Sopenharmony_ci		}
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	first->tx_dma_desc.cookie = -EBUSY;
10308c2ecf20Sopenharmony_ci	first->tx_dma_desc.flags = flags;
10318c2ecf20Sopenharmony_ci	first->xfer_size = len;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	return &first->tx_dma_desc;
10348c2ecf20Sopenharmony_ci}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
10378c2ecf20Sopenharmony_ciat_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
10388c2ecf20Sopenharmony_ci			 size_t len, unsigned long flags)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
10418c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*first = NULL, *prev = NULL;
10428c2ecf20Sopenharmony_ci	size_t			remaining_size = len, xfer_size = 0, ublen;
10438c2ecf20Sopenharmony_ci	dma_addr_t		src_addr = src, dst_addr = dest;
10448c2ecf20Sopenharmony_ci	u32			dwidth;
10458c2ecf20Sopenharmony_ci	/*
10468c2ecf20Sopenharmony_ci	 * WARNING: We don't know the direction, it involves we can't
10478c2ecf20Sopenharmony_ci	 * dynamically set the source and dest interface so we have to use the
10488c2ecf20Sopenharmony_ci	 * same one. Only interface 0 allows EBI access. Hopefully we can
10498c2ecf20Sopenharmony_ci	 * access DDR through both ports (at least on SAMA5D4x), so we can use
10508c2ecf20Sopenharmony_ci	 * the same interface for source and dest, that solves the fact we
10518c2ecf20Sopenharmony_ci	 * don't know the direction.
10528c2ecf20Sopenharmony_ci	 * ERRATA: Even if useless for memory transfers, the PERID has to not
10538c2ecf20Sopenharmony_ci	 * match the one of another channel. If not, it could lead to spurious
10548c2ecf20Sopenharmony_ci	 * flag status.
10558c2ecf20Sopenharmony_ci	 */
10568c2ecf20Sopenharmony_ci	u32			chan_cc = AT_XDMAC_CC_PERID(0x3f)
10578c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_DAM_INCREMENTED_AM
10588c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_SAM_INCREMENTED_AM
10598c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_DIF(0)
10608c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_SIF(0)
10618c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_MBSIZE_SIXTEEN
10628c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_TYPE_MEM_TRAN;
10638c2ecf20Sopenharmony_ci	unsigned long		irqflags;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s: src=%pad, dest=%pad, len=%zd, flags=0x%lx\n",
10668c2ecf20Sopenharmony_ci		__func__, &src, &dest, len, flags);
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (unlikely(!len))
10698c2ecf20Sopenharmony_ci		return NULL;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	dwidth = at_xdmac_align_width(chan, src_addr | dst_addr);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	/* Prepare descriptors. */
10748c2ecf20Sopenharmony_ci	while (remaining_size) {
10758c2ecf20Sopenharmony_ci		struct at_xdmac_desc	*desc = NULL;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: remaining_size=%zu\n", __func__, remaining_size);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci		spin_lock_irqsave(&atchan->lock, irqflags);
10808c2ecf20Sopenharmony_ci		desc = at_xdmac_get_desc(atchan);
10818c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&atchan->lock, irqflags);
10828c2ecf20Sopenharmony_ci		if (!desc) {
10838c2ecf20Sopenharmony_ci			dev_err(chan2dev(chan), "can't get descriptor\n");
10848c2ecf20Sopenharmony_ci			if (first)
10858c2ecf20Sopenharmony_ci				list_splice_tail_init(&first->descs_list,
10868c2ecf20Sopenharmony_ci						      &atchan->free_descs_list);
10878c2ecf20Sopenharmony_ci			return NULL;
10888c2ecf20Sopenharmony_ci		}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci		/* Update src and dest addresses. */
10918c2ecf20Sopenharmony_ci		src_addr += xfer_size;
10928c2ecf20Sopenharmony_ci		dst_addr += xfer_size;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci		if (remaining_size >= AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth)
10958c2ecf20Sopenharmony_ci			xfer_size = AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth;
10968c2ecf20Sopenharmony_ci		else
10978c2ecf20Sopenharmony_ci			xfer_size = remaining_size;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: xfer_size=%zu\n", __func__, xfer_size);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci		/* Check remaining length and change data width if needed. */
11028c2ecf20Sopenharmony_ci		dwidth = at_xdmac_align_width(chan,
11038c2ecf20Sopenharmony_ci					      src_addr | dst_addr | xfer_size);
11048c2ecf20Sopenharmony_ci		chan_cc &= ~AT_XDMAC_CC_DWIDTH_MASK;
11058c2ecf20Sopenharmony_ci		chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		ublen = xfer_size >> dwidth;
11088c2ecf20Sopenharmony_ci		remaining_size -= xfer_size;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		desc->lld.mbr_sa = src_addr;
11118c2ecf20Sopenharmony_ci		desc->lld.mbr_da = dst_addr;
11128c2ecf20Sopenharmony_ci		desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV2
11138c2ecf20Sopenharmony_ci			| AT_XDMAC_MBR_UBC_NDEN
11148c2ecf20Sopenharmony_ci			| AT_XDMAC_MBR_UBC_NSEN
11158c2ecf20Sopenharmony_ci			| ublen;
11168c2ecf20Sopenharmony_ci		desc->lld.mbr_cfg = chan_cc;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan),
11198c2ecf20Sopenharmony_ci			 "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
11208c2ecf20Sopenharmony_ci			 __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc, desc->lld.mbr_cfg);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci		/* Chain lld. */
11238c2ecf20Sopenharmony_ci		if (prev)
11248c2ecf20Sopenharmony_ci			at_xdmac_queue_desc(chan, prev, desc);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci		prev = desc;
11278c2ecf20Sopenharmony_ci		if (!first)
11288c2ecf20Sopenharmony_ci			first = desc;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n",
11318c2ecf20Sopenharmony_ci			 __func__, desc, first);
11328c2ecf20Sopenharmony_ci		list_add_tail(&desc->desc_node, &first->descs_list);
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	first->tx_dma_desc.flags = flags;
11368c2ecf20Sopenharmony_ci	first->xfer_size = len;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	return &first->tx_dma_desc;
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_cistatic struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
11428c2ecf20Sopenharmony_ci							 struct at_xdmac_chan *atchan,
11438c2ecf20Sopenharmony_ci							 dma_addr_t dst_addr,
11448c2ecf20Sopenharmony_ci							 size_t len,
11458c2ecf20Sopenharmony_ci							 int value)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc;
11488c2ecf20Sopenharmony_ci	unsigned long		flags;
11498c2ecf20Sopenharmony_ci	size_t			ublen;
11508c2ecf20Sopenharmony_ci	u32			dwidth;
11518c2ecf20Sopenharmony_ci	/*
11528c2ecf20Sopenharmony_ci	 * WARNING: The channel configuration is set here since there is no
11538c2ecf20Sopenharmony_ci	 * dmaengine_slave_config call in this case. Moreover we don't know the
11548c2ecf20Sopenharmony_ci	 * direction, it involves we can't dynamically set the source and dest
11558c2ecf20Sopenharmony_ci	 * interface so we have to use the same one. Only interface 0 allows EBI
11568c2ecf20Sopenharmony_ci	 * access. Hopefully we can access DDR through both ports (at least on
11578c2ecf20Sopenharmony_ci	 * SAMA5D4x), so we can use the same interface for source and dest,
11588c2ecf20Sopenharmony_ci	 * that solves the fact we don't know the direction.
11598c2ecf20Sopenharmony_ci	 * ERRATA: Even if useless for memory transfers, the PERID has to not
11608c2ecf20Sopenharmony_ci	 * match the one of another channel. If not, it could lead to spurious
11618c2ecf20Sopenharmony_ci	 * flag status.
11628c2ecf20Sopenharmony_ci	 */
11638c2ecf20Sopenharmony_ci	u32			chan_cc = AT_XDMAC_CC_PERID(0x3f)
11648c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_DAM_UBS_AM
11658c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_SAM_INCREMENTED_AM
11668c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_DIF(0)
11678c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_SIF(0)
11688c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_MBSIZE_SIXTEEN
11698c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_MEMSET_HW_MODE
11708c2ecf20Sopenharmony_ci					| AT_XDMAC_CC_TYPE_MEM_TRAN;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	dwidth = at_xdmac_align_width(chan, dst_addr);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	if (len >= (AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth)) {
11758c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan),
11768c2ecf20Sopenharmony_ci			"%s: Transfer too large, aborting...\n",
11778c2ecf20Sopenharmony_ci			__func__);
11788c2ecf20Sopenharmony_ci		return NULL;
11798c2ecf20Sopenharmony_ci	}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
11828c2ecf20Sopenharmony_ci	desc = at_xdmac_get_desc(atchan);
11838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
11848c2ecf20Sopenharmony_ci	if (!desc) {
11858c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan), "can't get descriptor\n");
11868c2ecf20Sopenharmony_ci		return NULL;
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth);
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	ublen = len >> dwidth;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	desc->lld.mbr_da = dst_addr;
11948c2ecf20Sopenharmony_ci	desc->lld.mbr_ds = value;
11958c2ecf20Sopenharmony_ci	desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV3
11968c2ecf20Sopenharmony_ci		| AT_XDMAC_MBR_UBC_NDEN
11978c2ecf20Sopenharmony_ci		| AT_XDMAC_MBR_UBC_NSEN
11988c2ecf20Sopenharmony_ci		| ublen;
11998c2ecf20Sopenharmony_ci	desc->lld.mbr_cfg = chan_cc;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan),
12028c2ecf20Sopenharmony_ci		"%s: lld: mbr_da=%pad, mbr_ds=0x%08x, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
12038c2ecf20Sopenharmony_ci		__func__, &desc->lld.mbr_da, desc->lld.mbr_ds, desc->lld.mbr_ubc,
12048c2ecf20Sopenharmony_ci		desc->lld.mbr_cfg);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	return desc;
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
12108c2ecf20Sopenharmony_ciat_xdmac_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
12118c2ecf20Sopenharmony_ci			 size_t len, unsigned long flags)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
12148c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s: dest=%pad, len=%zu, pattern=0x%x, flags=0x%lx\n",
12178c2ecf20Sopenharmony_ci		__func__, &dest, len, value, flags);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	if (unlikely(!len))
12208c2ecf20Sopenharmony_ci		return NULL;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	desc = at_xdmac_memset_create_desc(chan, atchan, dest, len, value);
12238c2ecf20Sopenharmony_ci	list_add_tail(&desc->desc_node, &desc->descs_list);
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	desc->tx_dma_desc.cookie = -EBUSY;
12268c2ecf20Sopenharmony_ci	desc->tx_dma_desc.flags = flags;
12278c2ecf20Sopenharmony_ci	desc->xfer_size = len;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	return &desc->tx_dma_desc;
12308c2ecf20Sopenharmony_ci}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
12338c2ecf20Sopenharmony_ciat_xdmac_prep_dma_memset_sg(struct dma_chan *chan, struct scatterlist *sgl,
12348c2ecf20Sopenharmony_ci			    unsigned int sg_len, int value,
12358c2ecf20Sopenharmony_ci			    unsigned long flags)
12368c2ecf20Sopenharmony_ci{
12378c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
12388c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc, *pdesc = NULL,
12398c2ecf20Sopenharmony_ci				*ppdesc = NULL, *first = NULL;
12408c2ecf20Sopenharmony_ci	struct scatterlist	*sg, *psg = NULL, *ppsg = NULL;
12418c2ecf20Sopenharmony_ci	size_t			stride = 0, pstride = 0, len = 0;
12428c2ecf20Sopenharmony_ci	int			i;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	if (!sgl)
12458c2ecf20Sopenharmony_ci		return NULL;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s: sg_len=%d, value=0x%x, flags=0x%lx\n",
12488c2ecf20Sopenharmony_ci		__func__, sg_len, value, flags);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	/* Prepare descriptors. */
12518c2ecf20Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i) {
12528c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: dest=%pad, len=%d, pattern=0x%x, flags=0x%lx\n",
12538c2ecf20Sopenharmony_ci			__func__, &sg_dma_address(sg), sg_dma_len(sg),
12548c2ecf20Sopenharmony_ci			value, flags);
12558c2ecf20Sopenharmony_ci		desc = at_xdmac_memset_create_desc(chan, atchan,
12568c2ecf20Sopenharmony_ci						   sg_dma_address(sg),
12578c2ecf20Sopenharmony_ci						   sg_dma_len(sg),
12588c2ecf20Sopenharmony_ci						   value);
12598c2ecf20Sopenharmony_ci		if (!desc && first)
12608c2ecf20Sopenharmony_ci			list_splice_tail_init(&first->descs_list,
12618c2ecf20Sopenharmony_ci					      &atchan->free_descs_list);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci		if (!first)
12648c2ecf20Sopenharmony_ci			first = desc;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		/* Update our strides */
12678c2ecf20Sopenharmony_ci		pstride = stride;
12688c2ecf20Sopenharmony_ci		if (psg)
12698c2ecf20Sopenharmony_ci			stride = sg_dma_address(sg) -
12708c2ecf20Sopenharmony_ci				(sg_dma_address(psg) + sg_dma_len(psg));
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci		/*
12738c2ecf20Sopenharmony_ci		 * The scatterlist API gives us only the address and
12748c2ecf20Sopenharmony_ci		 * length of each elements.
12758c2ecf20Sopenharmony_ci		 *
12768c2ecf20Sopenharmony_ci		 * Unfortunately, we don't have the stride, which we
12778c2ecf20Sopenharmony_ci		 * will need to compute.
12788c2ecf20Sopenharmony_ci		 *
12798c2ecf20Sopenharmony_ci		 * That make us end up in a situation like this one:
12808c2ecf20Sopenharmony_ci		 *    len    stride    len    stride    len
12818c2ecf20Sopenharmony_ci		 * +-------+        +-------+        +-------+
12828c2ecf20Sopenharmony_ci		 * |  N-2  |        |  N-1  |        |   N   |
12838c2ecf20Sopenharmony_ci		 * +-------+        +-------+        +-------+
12848c2ecf20Sopenharmony_ci		 *
12858c2ecf20Sopenharmony_ci		 * We need all these three elements (N-2, N-1 and N)
12868c2ecf20Sopenharmony_ci		 * to actually take the decision on whether we need to
12878c2ecf20Sopenharmony_ci		 * queue N-1 or reuse N-2.
12888c2ecf20Sopenharmony_ci		 *
12898c2ecf20Sopenharmony_ci		 * We will only consider N if it is the last element.
12908c2ecf20Sopenharmony_ci		 */
12918c2ecf20Sopenharmony_ci		if (ppdesc && pdesc) {
12928c2ecf20Sopenharmony_ci			if ((stride == pstride) &&
12938c2ecf20Sopenharmony_ci			    (sg_dma_len(ppsg) == sg_dma_len(psg))) {
12948c2ecf20Sopenharmony_ci				dev_dbg(chan2dev(chan),
12958c2ecf20Sopenharmony_ci					"%s: desc 0x%p can be merged with desc 0x%p\n",
12968c2ecf20Sopenharmony_ci					__func__, pdesc, ppdesc);
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci				/*
12998c2ecf20Sopenharmony_ci				 * Increment the block count of the
13008c2ecf20Sopenharmony_ci				 * N-2 descriptor
13018c2ecf20Sopenharmony_ci				 */
13028c2ecf20Sopenharmony_ci				at_xdmac_increment_block_count(chan, ppdesc);
13038c2ecf20Sopenharmony_ci				ppdesc->lld.mbr_dus = stride;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci				/*
13068c2ecf20Sopenharmony_ci				 * Put back the N-1 descriptor in the
13078c2ecf20Sopenharmony_ci				 * free descriptor list
13088c2ecf20Sopenharmony_ci				 */
13098c2ecf20Sopenharmony_ci				list_add_tail(&pdesc->desc_node,
13108c2ecf20Sopenharmony_ci					      &atchan->free_descs_list);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci				/*
13138c2ecf20Sopenharmony_ci				 * Make our N-1 descriptor pointer
13148c2ecf20Sopenharmony_ci				 * point to the N-2 since they were
13158c2ecf20Sopenharmony_ci				 * actually merged.
13168c2ecf20Sopenharmony_ci				 */
13178c2ecf20Sopenharmony_ci				pdesc = ppdesc;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci			/*
13208c2ecf20Sopenharmony_ci			 * Rule out the case where we don't have
13218c2ecf20Sopenharmony_ci			 * pstride computed yet (our second sg
13228c2ecf20Sopenharmony_ci			 * element)
13238c2ecf20Sopenharmony_ci			 *
13248c2ecf20Sopenharmony_ci			 * We also want to catch the case where there
13258c2ecf20Sopenharmony_ci			 * would be a negative stride,
13268c2ecf20Sopenharmony_ci			 */
13278c2ecf20Sopenharmony_ci			} else if (pstride ||
13288c2ecf20Sopenharmony_ci				   sg_dma_address(sg) < sg_dma_address(psg)) {
13298c2ecf20Sopenharmony_ci				/*
13308c2ecf20Sopenharmony_ci				 * Queue the N-1 descriptor after the
13318c2ecf20Sopenharmony_ci				 * N-2
13328c2ecf20Sopenharmony_ci				 */
13338c2ecf20Sopenharmony_ci				at_xdmac_queue_desc(chan, ppdesc, pdesc);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci				/*
13368c2ecf20Sopenharmony_ci				 * Add the N-1 descriptor to the list
13378c2ecf20Sopenharmony_ci				 * of the descriptors used for this
13388c2ecf20Sopenharmony_ci				 * transfer
13398c2ecf20Sopenharmony_ci				 */
13408c2ecf20Sopenharmony_ci				list_add_tail(&desc->desc_node,
13418c2ecf20Sopenharmony_ci					      &first->descs_list);
13428c2ecf20Sopenharmony_ci				dev_dbg(chan2dev(chan),
13438c2ecf20Sopenharmony_ci					"%s: add desc 0x%p to descs_list 0x%p\n",
13448c2ecf20Sopenharmony_ci					__func__, desc, first);
13458c2ecf20Sopenharmony_ci			}
13468c2ecf20Sopenharmony_ci		}
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci		/*
13498c2ecf20Sopenharmony_ci		 * If we are the last element, just see if we have the
13508c2ecf20Sopenharmony_ci		 * same size than the previous element.
13518c2ecf20Sopenharmony_ci		 *
13528c2ecf20Sopenharmony_ci		 * If so, we can merge it with the previous descriptor
13538c2ecf20Sopenharmony_ci		 * since we don't care about the stride anymore.
13548c2ecf20Sopenharmony_ci		 */
13558c2ecf20Sopenharmony_ci		if ((i == (sg_len - 1)) &&
13568c2ecf20Sopenharmony_ci		    sg_dma_len(psg) == sg_dma_len(sg)) {
13578c2ecf20Sopenharmony_ci			dev_dbg(chan2dev(chan),
13588c2ecf20Sopenharmony_ci				"%s: desc 0x%p can be merged with desc 0x%p\n",
13598c2ecf20Sopenharmony_ci				__func__, desc, pdesc);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci			/*
13628c2ecf20Sopenharmony_ci			 * Increment the block count of the N-1
13638c2ecf20Sopenharmony_ci			 * descriptor
13648c2ecf20Sopenharmony_ci			 */
13658c2ecf20Sopenharmony_ci			at_xdmac_increment_block_count(chan, pdesc);
13668c2ecf20Sopenharmony_ci			pdesc->lld.mbr_dus = stride;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci			/*
13698c2ecf20Sopenharmony_ci			 * Put back the N descriptor in the free
13708c2ecf20Sopenharmony_ci			 * descriptor list
13718c2ecf20Sopenharmony_ci			 */
13728c2ecf20Sopenharmony_ci			list_add_tail(&desc->desc_node,
13738c2ecf20Sopenharmony_ci				      &atchan->free_descs_list);
13748c2ecf20Sopenharmony_ci		}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci		/* Update our descriptors */
13778c2ecf20Sopenharmony_ci		ppdesc = pdesc;
13788c2ecf20Sopenharmony_ci		pdesc = desc;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci		/* Update our scatter pointers */
13818c2ecf20Sopenharmony_ci		ppsg = psg;
13828c2ecf20Sopenharmony_ci		psg = sg;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci		len += sg_dma_len(sg);
13858c2ecf20Sopenharmony_ci	}
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	first->tx_dma_desc.cookie = -EBUSY;
13888c2ecf20Sopenharmony_ci	first->tx_dma_desc.flags = flags;
13898c2ecf20Sopenharmony_ci	first->xfer_size = len;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	return &first->tx_dma_desc;
13928c2ecf20Sopenharmony_ci}
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_cistatic enum dma_status
13958c2ecf20Sopenharmony_ciat_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
13968c2ecf20Sopenharmony_ci		struct dma_tx_state *txstate)
13978c2ecf20Sopenharmony_ci{
13988c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
13998c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
14008c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc, *_desc, *iter;
14018c2ecf20Sopenharmony_ci	struct list_head	*descs_list;
14028c2ecf20Sopenharmony_ci	enum dma_status		ret;
14038c2ecf20Sopenharmony_ci	int			residue, retry;
14048c2ecf20Sopenharmony_ci	u32			cur_nda, check_nda, cur_ubc, mask, value;
14058c2ecf20Sopenharmony_ci	u8			dwidth = 0;
14068c2ecf20Sopenharmony_ci	unsigned long		flags;
14078c2ecf20Sopenharmony_ci	bool			initd;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
14108c2ecf20Sopenharmony_ci	if (ret == DMA_COMPLETE)
14118c2ecf20Sopenharmony_ci		return ret;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	if (!txstate)
14148c2ecf20Sopenharmony_ci		return ret;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, xfer_node);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	/*
14218c2ecf20Sopenharmony_ci	 * If the transfer has not been started yet, don't need to compute the
14228c2ecf20Sopenharmony_ci	 * residue, it's the transfer length.
14238c2ecf20Sopenharmony_ci	 */
14248c2ecf20Sopenharmony_ci	if (!desc->active_xfer) {
14258c2ecf20Sopenharmony_ci		dma_set_residue(txstate, desc->xfer_size);
14268c2ecf20Sopenharmony_ci		goto spin_unlock;
14278c2ecf20Sopenharmony_ci	}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	residue = desc->xfer_size;
14308c2ecf20Sopenharmony_ci	/*
14318c2ecf20Sopenharmony_ci	 * Flush FIFO: only relevant when the transfer is source peripheral
14328c2ecf20Sopenharmony_ci	 * synchronized. Flush is needed before reading CUBC because data in
14338c2ecf20Sopenharmony_ci	 * the FIFO are not reported by CUBC. Reporting a residue of the
14348c2ecf20Sopenharmony_ci	 * transfer length while we have data in FIFO can cause issue.
14358c2ecf20Sopenharmony_ci	 * Usecase: atmel USART has a timeout which means I have received
14368c2ecf20Sopenharmony_ci	 * characters but there is no more character received for a while. On
14378c2ecf20Sopenharmony_ci	 * timeout, it requests the residue. If the data are in the DMA FIFO,
14388c2ecf20Sopenharmony_ci	 * we will return a residue of the transfer length. It means no data
14398c2ecf20Sopenharmony_ci	 * received. If an application is waiting for these data, it will hang
14408c2ecf20Sopenharmony_ci	 * since we won't have another USART timeout without receiving new
14418c2ecf20Sopenharmony_ci	 * data.
14428c2ecf20Sopenharmony_ci	 */
14438c2ecf20Sopenharmony_ci	mask = AT_XDMAC_CC_TYPE | AT_XDMAC_CC_DSYNC;
14448c2ecf20Sopenharmony_ci	value = AT_XDMAC_CC_TYPE_PER_TRAN | AT_XDMAC_CC_DSYNC_PER2MEM;
14458c2ecf20Sopenharmony_ci	if ((desc->lld.mbr_cfg & mask) == value) {
14468c2ecf20Sopenharmony_ci		at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
14478c2ecf20Sopenharmony_ci		while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
14488c2ecf20Sopenharmony_ci			cpu_relax();
14498c2ecf20Sopenharmony_ci	}
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/*
14528c2ecf20Sopenharmony_ci	 * The easiest way to compute the residue should be to pause the DMA
14538c2ecf20Sopenharmony_ci	 * but doing this can lead to miss some data as some devices don't
14548c2ecf20Sopenharmony_ci	 * have FIFO.
14558c2ecf20Sopenharmony_ci	 * We need to read several registers because:
14568c2ecf20Sopenharmony_ci	 * - DMA is running therefore a descriptor change is possible while
14578c2ecf20Sopenharmony_ci	 * reading these registers
14588c2ecf20Sopenharmony_ci	 * - When the block transfer is done, the value of the CUBC register
14598c2ecf20Sopenharmony_ci	 * is set to its initial value until the fetch of the next descriptor.
14608c2ecf20Sopenharmony_ci	 * This value will corrupt the residue calculation so we have to skip
14618c2ecf20Sopenharmony_ci	 * it.
14628c2ecf20Sopenharmony_ci	 *
14638c2ecf20Sopenharmony_ci	 * INITD --------                    ------------
14648c2ecf20Sopenharmony_ci	 *              |____________________|
14658c2ecf20Sopenharmony_ci	 *       _______________________  _______________
14668c2ecf20Sopenharmony_ci	 * NDA       @desc2             \/   @desc3
14678c2ecf20Sopenharmony_ci	 *       _______________________/\_______________
14688c2ecf20Sopenharmony_ci	 *       __________  ___________  _______________
14698c2ecf20Sopenharmony_ci	 * CUBC       0    \/ MAX desc1 \/  MAX desc2
14708c2ecf20Sopenharmony_ci	 *       __________/\___________/\_______________
14718c2ecf20Sopenharmony_ci	 *
14728c2ecf20Sopenharmony_ci	 * Since descriptors are aligned on 64 bits, we can assume that
14738c2ecf20Sopenharmony_ci	 * the update of NDA and CUBC is atomic.
14748c2ecf20Sopenharmony_ci	 * Memory barriers are used to ensure the read order of the registers.
14758c2ecf20Sopenharmony_ci	 * A max number of retries is set because unlikely it could never ends.
14768c2ecf20Sopenharmony_ci	 */
14778c2ecf20Sopenharmony_ci	for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) {
14788c2ecf20Sopenharmony_ci		check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
14798c2ecf20Sopenharmony_ci		rmb();
14808c2ecf20Sopenharmony_ci		cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC);
14818c2ecf20Sopenharmony_ci		rmb();
14828c2ecf20Sopenharmony_ci		initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
14838c2ecf20Sopenharmony_ci		rmb();
14848c2ecf20Sopenharmony_ci		cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
14858c2ecf20Sopenharmony_ci		rmb();
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci		if ((check_nda == cur_nda) && initd)
14888c2ecf20Sopenharmony_ci			break;
14898c2ecf20Sopenharmony_ci	}
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	if (unlikely(retry >= AT_XDMAC_RESIDUE_MAX_RETRIES)) {
14928c2ecf20Sopenharmony_ci		ret = DMA_ERROR;
14938c2ecf20Sopenharmony_ci		goto spin_unlock;
14948c2ecf20Sopenharmony_ci	}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	/*
14978c2ecf20Sopenharmony_ci	 * Flush FIFO: only relevant when the transfer is source peripheral
14988c2ecf20Sopenharmony_ci	 * synchronized. Another flush is needed here because CUBC is updated
14998c2ecf20Sopenharmony_ci	 * when the controller sends the data write command. It can lead to
15008c2ecf20Sopenharmony_ci	 * report data that are not written in the memory or the device. The
15018c2ecf20Sopenharmony_ci	 * FIFO flush ensures that data are really written.
15028c2ecf20Sopenharmony_ci	 */
15038c2ecf20Sopenharmony_ci	if ((desc->lld.mbr_cfg & mask) == value) {
15048c2ecf20Sopenharmony_ci		at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
15058c2ecf20Sopenharmony_ci		while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
15068c2ecf20Sopenharmony_ci			cpu_relax();
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	/*
15108c2ecf20Sopenharmony_ci	 * Remove size of all microblocks already transferred and the current
15118c2ecf20Sopenharmony_ci	 * one. Then add the remaining size to transfer of the current
15128c2ecf20Sopenharmony_ci	 * microblock.
15138c2ecf20Sopenharmony_ci	 */
15148c2ecf20Sopenharmony_ci	descs_list = &desc->descs_list;
15158c2ecf20Sopenharmony_ci	list_for_each_entry_safe(iter, _desc, descs_list, desc_node) {
15168c2ecf20Sopenharmony_ci		dwidth = at_xdmac_get_dwidth(iter->lld.mbr_cfg);
15178c2ecf20Sopenharmony_ci		residue -= (iter->lld.mbr_ubc & 0xffffff) << dwidth;
15188c2ecf20Sopenharmony_ci		if ((iter->lld.mbr_nda & 0xfffffffc) == cur_nda) {
15198c2ecf20Sopenharmony_ci			desc = iter;
15208c2ecf20Sopenharmony_ci			break;
15218c2ecf20Sopenharmony_ci		}
15228c2ecf20Sopenharmony_ci	}
15238c2ecf20Sopenharmony_ci	residue += cur_ubc << dwidth;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	dma_set_residue(txstate, residue);
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan),
15288c2ecf20Sopenharmony_ci		 "%s: desc=0x%p, tx_dma_desc.phys=%pad, tx_status=%d, cookie=%d, residue=%d\n",
15298c2ecf20Sopenharmony_ci		 __func__, desc, &desc->tx_dma_desc.phys, ret, cookie, residue);
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_cispin_unlock:
15328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
15338c2ecf20Sopenharmony_ci	return ret;
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_cistatic void at_xdmac_advance_work(struct at_xdmac_chan *atchan)
15378c2ecf20Sopenharmony_ci{
15388c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	/*
15418c2ecf20Sopenharmony_ci	 * If channel is enabled, do nothing, advance_work will be triggered
15428c2ecf20Sopenharmony_ci	 * after the interruption.
15438c2ecf20Sopenharmony_ci	 */
15448c2ecf20Sopenharmony_ci	if (!at_xdmac_chan_is_enabled(atchan) && !list_empty(&atchan->xfers_list)) {
15458c2ecf20Sopenharmony_ci		desc = list_first_entry(&atchan->xfers_list,
15468c2ecf20Sopenharmony_ci					struct at_xdmac_desc,
15478c2ecf20Sopenharmony_ci					xfer_node);
15488c2ecf20Sopenharmony_ci		dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc);
15498c2ecf20Sopenharmony_ci		if (!desc->active_xfer)
15508c2ecf20Sopenharmony_ci			at_xdmac_start_xfer(atchan, desc);
15518c2ecf20Sopenharmony_ci	}
15528c2ecf20Sopenharmony_ci}
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_cistatic void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan)
15558c2ecf20Sopenharmony_ci{
15568c2ecf20Sopenharmony_ci	struct at_xdmac_desc		*desc;
15578c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor	*txd;
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	spin_lock_irq(&atchan->lock);
15608c2ecf20Sopenharmony_ci	if (list_empty(&atchan->xfers_list)) {
15618c2ecf20Sopenharmony_ci		spin_unlock_irq(&atchan->lock);
15628c2ecf20Sopenharmony_ci		return;
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci	desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc,
15658c2ecf20Sopenharmony_ci				xfer_node);
15668c2ecf20Sopenharmony_ci	spin_unlock_irq(&atchan->lock);
15678c2ecf20Sopenharmony_ci	txd = &desc->tx_dma_desc;
15688c2ecf20Sopenharmony_ci	if (txd->flags & DMA_PREP_INTERRUPT)
15698c2ecf20Sopenharmony_ci		dmaengine_desc_get_callback_invoke(txd, NULL);
15708c2ecf20Sopenharmony_ci}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cistatic void at_xdmac_handle_error(struct at_xdmac_chan *atchan)
15738c2ecf20Sopenharmony_ci{
15748c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
15758c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*bad_desc;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	/*
15788c2ecf20Sopenharmony_ci	 * The descriptor currently at the head of the active list is
15798c2ecf20Sopenharmony_ci	 * broken. Since we don't have any way to report errors, we'll
15808c2ecf20Sopenharmony_ci	 * just have to scream loudly and try to continue with other
15818c2ecf20Sopenharmony_ci	 * descriptors queued (if any).
15828c2ecf20Sopenharmony_ci	 */
15838c2ecf20Sopenharmony_ci	if (atchan->irq_status & AT_XDMAC_CIS_RBEIS)
15848c2ecf20Sopenharmony_ci		dev_err(chan2dev(&atchan->chan), "read bus error!!!");
15858c2ecf20Sopenharmony_ci	if (atchan->irq_status & AT_XDMAC_CIS_WBEIS)
15868c2ecf20Sopenharmony_ci		dev_err(chan2dev(&atchan->chan), "write bus error!!!");
15878c2ecf20Sopenharmony_ci	if (atchan->irq_status & AT_XDMAC_CIS_ROIS)
15888c2ecf20Sopenharmony_ci		dev_err(chan2dev(&atchan->chan), "request overflow error!!!");
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	spin_lock_irq(&atchan->lock);
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	/* Channel must be disabled first as it's not done automatically */
15938c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
15948c2ecf20Sopenharmony_ci	while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask)
15958c2ecf20Sopenharmony_ci		cpu_relax();
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	bad_desc = list_first_entry(&atchan->xfers_list,
15988c2ecf20Sopenharmony_ci				    struct at_xdmac_desc,
15998c2ecf20Sopenharmony_ci				    xfer_node);
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	spin_unlock_irq(&atchan->lock);
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	/* Print bad descriptor's details if needed */
16048c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(&atchan->chan),
16058c2ecf20Sopenharmony_ci		"%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n",
16068c2ecf20Sopenharmony_ci		__func__, &bad_desc->lld.mbr_sa, &bad_desc->lld.mbr_da,
16078c2ecf20Sopenharmony_ci		bad_desc->lld.mbr_ubc);
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	/* Then continue with usual descriptor management */
16108c2ecf20Sopenharmony_ci}
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_cistatic void at_xdmac_tasklet(struct tasklet_struct *t)
16138c2ecf20Sopenharmony_ci{
16148c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = from_tasklet(atchan, t, tasklet);
16158c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc;
16168c2ecf20Sopenharmony_ci	u32			error_mask;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n",
16198c2ecf20Sopenharmony_ci		__func__, atchan->irq_status);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	error_mask = AT_XDMAC_CIS_RBEIS
16228c2ecf20Sopenharmony_ci		     | AT_XDMAC_CIS_WBEIS
16238c2ecf20Sopenharmony_ci		     | AT_XDMAC_CIS_ROIS;
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	if (at_xdmac_chan_is_cyclic(atchan)) {
16268c2ecf20Sopenharmony_ci		at_xdmac_handle_cyclic(atchan);
16278c2ecf20Sopenharmony_ci	} else if ((atchan->irq_status & AT_XDMAC_CIS_LIS)
16288c2ecf20Sopenharmony_ci		   || (atchan->irq_status & error_mask)) {
16298c2ecf20Sopenharmony_ci		struct dma_async_tx_descriptor  *txd;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		if (atchan->irq_status & error_mask)
16328c2ecf20Sopenharmony_ci			at_xdmac_handle_error(atchan);
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci		spin_lock_irq(&atchan->lock);
16358c2ecf20Sopenharmony_ci		desc = list_first_entry(&atchan->xfers_list,
16368c2ecf20Sopenharmony_ci					struct at_xdmac_desc,
16378c2ecf20Sopenharmony_ci					xfer_node);
16388c2ecf20Sopenharmony_ci		dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc);
16398c2ecf20Sopenharmony_ci		if (!desc->active_xfer) {
16408c2ecf20Sopenharmony_ci			dev_err(chan2dev(&atchan->chan), "Xfer not active: exiting");
16418c2ecf20Sopenharmony_ci			spin_unlock_irq(&atchan->lock);
16428c2ecf20Sopenharmony_ci			return;
16438c2ecf20Sopenharmony_ci		}
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci		txd = &desc->tx_dma_desc;
16468c2ecf20Sopenharmony_ci		dma_cookie_complete(txd);
16478c2ecf20Sopenharmony_ci		/* Remove the transfer from the transfer list. */
16488c2ecf20Sopenharmony_ci		list_del(&desc->xfer_node);
16498c2ecf20Sopenharmony_ci		spin_unlock_irq(&atchan->lock);
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci		if (txd->flags & DMA_PREP_INTERRUPT)
16528c2ecf20Sopenharmony_ci			dmaengine_desc_get_callback_invoke(txd, NULL);
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci		dma_run_dependencies(txd);
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci		spin_lock_irq(&atchan->lock);
16578c2ecf20Sopenharmony_ci		/* Move the xfer descriptors into the free descriptors list. */
16588c2ecf20Sopenharmony_ci		list_splice_tail_init(&desc->descs_list,
16598c2ecf20Sopenharmony_ci				      &atchan->free_descs_list);
16608c2ecf20Sopenharmony_ci		at_xdmac_advance_work(atchan);
16618c2ecf20Sopenharmony_ci		spin_unlock_irq(&atchan->lock);
16628c2ecf20Sopenharmony_ci	}
16638c2ecf20Sopenharmony_ci}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_cistatic irqreturn_t at_xdmac_interrupt(int irq, void *dev_id)
16668c2ecf20Sopenharmony_ci{
16678c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = (struct at_xdmac *)dev_id;
16688c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan;
16698c2ecf20Sopenharmony_ci	u32			imr, status, pending;
16708c2ecf20Sopenharmony_ci	u32			chan_imr, chan_status;
16718c2ecf20Sopenharmony_ci	int			i, ret = IRQ_NONE;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	do {
16748c2ecf20Sopenharmony_ci		imr = at_xdmac_read(atxdmac, AT_XDMAC_GIM);
16758c2ecf20Sopenharmony_ci		status = at_xdmac_read(atxdmac, AT_XDMAC_GIS);
16768c2ecf20Sopenharmony_ci		pending = status & imr;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci		dev_vdbg(atxdmac->dma.dev,
16798c2ecf20Sopenharmony_ci			 "%s: status=0x%08x, imr=0x%08x, pending=0x%08x\n",
16808c2ecf20Sopenharmony_ci			 __func__, status, imr, pending);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci		if (!pending)
16838c2ecf20Sopenharmony_ci			break;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci		/* We have to find which channel has generated the interrupt. */
16868c2ecf20Sopenharmony_ci		for (i = 0; i < atxdmac->dma.chancnt; i++) {
16878c2ecf20Sopenharmony_ci			if (!((1 << i) & pending))
16888c2ecf20Sopenharmony_ci				continue;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci			atchan = &atxdmac->chan[i];
16918c2ecf20Sopenharmony_ci			chan_imr = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
16928c2ecf20Sopenharmony_ci			chan_status = at_xdmac_chan_read(atchan, AT_XDMAC_CIS);
16938c2ecf20Sopenharmony_ci			atchan->irq_status = chan_status & chan_imr;
16948c2ecf20Sopenharmony_ci			dev_vdbg(atxdmac->dma.dev,
16958c2ecf20Sopenharmony_ci				 "%s: chan%d: imr=0x%x, status=0x%x\n",
16968c2ecf20Sopenharmony_ci				 __func__, i, chan_imr, chan_status);
16978c2ecf20Sopenharmony_ci			dev_vdbg(chan2dev(&atchan->chan),
16988c2ecf20Sopenharmony_ci				 "%s: CC=0x%08x CNDA=0x%08x, CNDC=0x%08x, CSA=0x%08x, CDA=0x%08x, CUBC=0x%08x\n",
16998c2ecf20Sopenharmony_ci				 __func__,
17008c2ecf20Sopenharmony_ci				 at_xdmac_chan_read(atchan, AT_XDMAC_CC),
17018c2ecf20Sopenharmony_ci				 at_xdmac_chan_read(atchan, AT_XDMAC_CNDA),
17028c2ecf20Sopenharmony_ci				 at_xdmac_chan_read(atchan, AT_XDMAC_CNDC),
17038c2ecf20Sopenharmony_ci				 at_xdmac_chan_read(atchan, AT_XDMAC_CSA),
17048c2ecf20Sopenharmony_ci				 at_xdmac_chan_read(atchan, AT_XDMAC_CDA),
17058c2ecf20Sopenharmony_ci				 at_xdmac_chan_read(atchan, AT_XDMAC_CUBC));
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci			if (atchan->irq_status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS))
17088c2ecf20Sopenharmony_ci				at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci			tasklet_schedule(&atchan->tasklet);
17118c2ecf20Sopenharmony_ci			ret = IRQ_HANDLED;
17128c2ecf20Sopenharmony_ci		}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	} while (pending);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	return ret;
17178c2ecf20Sopenharmony_ci}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_cistatic void at_xdmac_issue_pending(struct dma_chan *chan)
17208c2ecf20Sopenharmony_ci{
17218c2ecf20Sopenharmony_ci	struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
17228c2ecf20Sopenharmony_ci	unsigned long flags;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(&atchan->chan), "%s\n", __func__);
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
17278c2ecf20Sopenharmony_ci	at_xdmac_advance_work(atchan);
17288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	return;
17318c2ecf20Sopenharmony_ci}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_cistatic int at_xdmac_device_config(struct dma_chan *chan,
17348c2ecf20Sopenharmony_ci				  struct dma_slave_config *config)
17358c2ecf20Sopenharmony_ci{
17368c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
17378c2ecf20Sopenharmony_ci	int ret;
17388c2ecf20Sopenharmony_ci	unsigned long		flags;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s\n", __func__);
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
17438c2ecf20Sopenharmony_ci	ret = at_xdmac_set_slave_config(chan, config);
17448c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	return ret;
17478c2ecf20Sopenharmony_ci}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_cistatic int at_xdmac_device_pause(struct dma_chan *chan)
17508c2ecf20Sopenharmony_ci{
17518c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
17528c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
17538c2ecf20Sopenharmony_ci	unsigned long		flags;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s\n", __func__);
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	if (test_and_set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status))
17588c2ecf20Sopenharmony_ci		return 0;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
17618c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask);
17628c2ecf20Sopenharmony_ci	while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
17638c2ecf20Sopenharmony_ci	       & (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
17648c2ecf20Sopenharmony_ci		cpu_relax();
17658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	return 0;
17688c2ecf20Sopenharmony_ci}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_cistatic int at_xdmac_device_resume(struct dma_chan *chan)
17718c2ecf20Sopenharmony_ci{
17728c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
17738c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
17748c2ecf20Sopenharmony_ci	unsigned long		flags;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s\n", __func__);
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
17798c2ecf20Sopenharmony_ci	if (!at_xdmac_chan_is_paused(atchan)) {
17808c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&atchan->lock, flags);
17818c2ecf20Sopenharmony_ci		return 0;
17828c2ecf20Sopenharmony_ci	}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask);
17858c2ecf20Sopenharmony_ci	clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
17868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	return 0;
17898c2ecf20Sopenharmony_ci}
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_cistatic int at_xdmac_device_terminate_all(struct dma_chan *chan)
17928c2ecf20Sopenharmony_ci{
17938c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc, *_desc;
17948c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
17958c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = to_at_xdmac(atchan->chan.device);
17968c2ecf20Sopenharmony_ci	unsigned long		flags;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s\n", __func__);
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&atchan->lock, flags);
18018c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
18028c2ecf20Sopenharmony_ci	while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask)
18038c2ecf20Sopenharmony_ci		cpu_relax();
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	/* Cancel all pending transfers. */
18068c2ecf20Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &atchan->xfers_list, xfer_node) {
18078c2ecf20Sopenharmony_ci		list_del(&desc->xfer_node);
18088c2ecf20Sopenharmony_ci		list_splice_tail_init(&desc->descs_list,
18098c2ecf20Sopenharmony_ci				      &atchan->free_descs_list);
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
18138c2ecf20Sopenharmony_ci	clear_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status);
18148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&atchan->lock, flags);
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	return 0;
18178c2ecf20Sopenharmony_ci}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_cistatic int at_xdmac_alloc_chan_resources(struct dma_chan *chan)
18208c2ecf20Sopenharmony_ci{
18218c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
18228c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc;
18238c2ecf20Sopenharmony_ci	int			i;
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	if (at_xdmac_chan_is_enabled(atchan)) {
18268c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan),
18278c2ecf20Sopenharmony_ci			"can't allocate channel resources (channel enabled)\n");
18288c2ecf20Sopenharmony_ci		return -EIO;
18298c2ecf20Sopenharmony_ci	}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	if (!list_empty(&atchan->free_descs_list)) {
18328c2ecf20Sopenharmony_ci		dev_err(chan2dev(chan),
18338c2ecf20Sopenharmony_ci			"can't allocate channel resources (channel not free from a previous use)\n");
18348c2ecf20Sopenharmony_ci		return -EIO;
18358c2ecf20Sopenharmony_ci	}
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	for (i = 0; i < init_nr_desc_per_channel; i++) {
18388c2ecf20Sopenharmony_ci		desc = at_xdmac_alloc_desc(chan, GFP_KERNEL);
18398c2ecf20Sopenharmony_ci		if (!desc) {
18408c2ecf20Sopenharmony_ci			if (i == 0) {
18418c2ecf20Sopenharmony_ci				dev_warn(chan2dev(chan),
18428c2ecf20Sopenharmony_ci					 "can't allocate any descriptors\n");
18438c2ecf20Sopenharmony_ci				return -EIO;
18448c2ecf20Sopenharmony_ci			}
18458c2ecf20Sopenharmony_ci			dev_warn(chan2dev(chan),
18468c2ecf20Sopenharmony_ci				"only %d descriptors have been allocated\n", i);
18478c2ecf20Sopenharmony_ci			break;
18488c2ecf20Sopenharmony_ci		}
18498c2ecf20Sopenharmony_ci		list_add_tail(&desc->desc_node, &atchan->free_descs_list);
18508c2ecf20Sopenharmony_ci	}
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	dma_cookie_init(chan);
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i);
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	return i;
18578c2ecf20Sopenharmony_ci}
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_cistatic void at_xdmac_free_chan_resources(struct dma_chan *chan)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
18628c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = to_at_xdmac(chan->device);
18638c2ecf20Sopenharmony_ci	struct at_xdmac_desc	*desc, *_desc;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	list_for_each_entry_safe(desc, _desc, &atchan->free_descs_list, desc_node) {
18668c2ecf20Sopenharmony_ci		dev_dbg(chan2dev(chan), "%s: freeing descriptor %p\n", __func__, desc);
18678c2ecf20Sopenharmony_ci		list_del(&desc->desc_node);
18688c2ecf20Sopenharmony_ci		dma_pool_free(atxdmac->at_xdmac_desc_pool, desc, desc->tx_dma_desc.phys);
18698c2ecf20Sopenharmony_ci	}
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	return;
18728c2ecf20Sopenharmony_ci}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
18758c2ecf20Sopenharmony_cistatic int atmel_xdmac_prepare(struct device *dev)
18768c2ecf20Sopenharmony_ci{
18778c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = dev_get_drvdata(dev);
18788c2ecf20Sopenharmony_ci	struct dma_chan		*chan, *_chan;
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
18818c2ecf20Sopenharmony_ci		struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci		/* Wait for transfer completion, except in cyclic case. */
18848c2ecf20Sopenharmony_ci		if (at_xdmac_chan_is_enabled(atchan) && !at_xdmac_chan_is_cyclic(atchan))
18858c2ecf20Sopenharmony_ci			return -EAGAIN;
18868c2ecf20Sopenharmony_ci	}
18878c2ecf20Sopenharmony_ci	return 0;
18888c2ecf20Sopenharmony_ci}
18898c2ecf20Sopenharmony_ci#else
18908c2ecf20Sopenharmony_ci#	define atmel_xdmac_prepare NULL
18918c2ecf20Sopenharmony_ci#endif
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
18948c2ecf20Sopenharmony_cistatic int atmel_xdmac_suspend(struct device *dev)
18958c2ecf20Sopenharmony_ci{
18968c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = dev_get_drvdata(dev);
18978c2ecf20Sopenharmony_ci	struct dma_chan		*chan, *_chan;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
19008c2ecf20Sopenharmony_ci		struct at_xdmac_chan	*atchan = to_at_xdmac_chan(chan);
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci		atchan->save_cc = at_xdmac_chan_read(atchan, AT_XDMAC_CC);
19038c2ecf20Sopenharmony_ci		if (at_xdmac_chan_is_cyclic(atchan)) {
19048c2ecf20Sopenharmony_ci			if (!at_xdmac_chan_is_paused(atchan))
19058c2ecf20Sopenharmony_ci				at_xdmac_device_pause(chan);
19068c2ecf20Sopenharmony_ci			atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
19078c2ecf20Sopenharmony_ci			atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA);
19088c2ecf20Sopenharmony_ci			atchan->save_cndc = at_xdmac_chan_read(atchan, AT_XDMAC_CNDC);
19098c2ecf20Sopenharmony_ci		}
19108c2ecf20Sopenharmony_ci	}
19118c2ecf20Sopenharmony_ci	atxdmac->save_gim = at_xdmac_read(atxdmac, AT_XDMAC_GIM);
19128c2ecf20Sopenharmony_ci	atxdmac->save_gs = at_xdmac_read(atxdmac, AT_XDMAC_GS);
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	at_xdmac_off(atxdmac);
19158c2ecf20Sopenharmony_ci	clk_disable_unprepare(atxdmac->clk);
19168c2ecf20Sopenharmony_ci	return 0;
19178c2ecf20Sopenharmony_ci}
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_cistatic int atmel_xdmac_resume(struct device *dev)
19208c2ecf20Sopenharmony_ci{
19218c2ecf20Sopenharmony_ci	struct at_xdmac		*atxdmac = dev_get_drvdata(dev);
19228c2ecf20Sopenharmony_ci	struct at_xdmac_chan	*atchan;
19238c2ecf20Sopenharmony_ci	struct dma_chan		*chan, *_chan;
19248c2ecf20Sopenharmony_ci	int			i;
19258c2ecf20Sopenharmony_ci	int ret;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(atxdmac->clk);
19288c2ecf20Sopenharmony_ci	if (ret)
19298c2ecf20Sopenharmony_ci		return ret;
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	/* Clear pending interrupts. */
19328c2ecf20Sopenharmony_ci	for (i = 0; i < atxdmac->dma.chancnt; i++) {
19338c2ecf20Sopenharmony_ci		atchan = &atxdmac->chan[i];
19348c2ecf20Sopenharmony_ci		while (at_xdmac_chan_read(atchan, AT_XDMAC_CIS))
19358c2ecf20Sopenharmony_ci			cpu_relax();
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	at_xdmac_write(atxdmac, AT_XDMAC_GIE, atxdmac->save_gim);
19398c2ecf20Sopenharmony_ci	list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
19408c2ecf20Sopenharmony_ci		atchan = to_at_xdmac_chan(chan);
19418c2ecf20Sopenharmony_ci		at_xdmac_chan_write(atchan, AT_XDMAC_CC, atchan->save_cc);
19428c2ecf20Sopenharmony_ci		if (at_xdmac_chan_is_cyclic(atchan)) {
19438c2ecf20Sopenharmony_ci			if (at_xdmac_chan_is_paused(atchan))
19448c2ecf20Sopenharmony_ci				at_xdmac_device_resume(chan);
19458c2ecf20Sopenharmony_ci			at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, atchan->save_cnda);
19468c2ecf20Sopenharmony_ci			at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, atchan->save_cndc);
19478c2ecf20Sopenharmony_ci			at_xdmac_chan_write(atchan, AT_XDMAC_CIE, atchan->save_cim);
19488c2ecf20Sopenharmony_ci			wmb();
19498c2ecf20Sopenharmony_ci			if (atxdmac->save_gs & atchan->mask)
19508c2ecf20Sopenharmony_ci				at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask);
19518c2ecf20Sopenharmony_ci		}
19528c2ecf20Sopenharmony_ci	}
19538c2ecf20Sopenharmony_ci	return 0;
19548c2ecf20Sopenharmony_ci}
19558c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_cistatic int at_xdmac_probe(struct platform_device *pdev)
19588c2ecf20Sopenharmony_ci{
19598c2ecf20Sopenharmony_ci	struct at_xdmac	*atxdmac;
19608c2ecf20Sopenharmony_ci	int		irq, size, nr_channels, i, ret;
19618c2ecf20Sopenharmony_ci	void __iomem	*base;
19628c2ecf20Sopenharmony_ci	u32		reg;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
19658c2ecf20Sopenharmony_ci	if (irq < 0)
19668c2ecf20Sopenharmony_ci		return irq;
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
19698c2ecf20Sopenharmony_ci	if (IS_ERR(base))
19708c2ecf20Sopenharmony_ci		return PTR_ERR(base);
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	/*
19738c2ecf20Sopenharmony_ci	 * Read number of xdmac channels, read helper function can't be used
19748c2ecf20Sopenharmony_ci	 * since atxdmac is not yet allocated and we need to know the number
19758c2ecf20Sopenharmony_ci	 * of channels to do the allocation.
19768c2ecf20Sopenharmony_ci	 */
19778c2ecf20Sopenharmony_ci	reg = readl_relaxed(base + AT_XDMAC_GTYPE);
19788c2ecf20Sopenharmony_ci	nr_channels = AT_XDMAC_NB_CH(reg);
19798c2ecf20Sopenharmony_ci	if (nr_channels > AT_XDMAC_MAX_CHAN) {
19808c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "invalid number of channels (%u)\n",
19818c2ecf20Sopenharmony_ci			nr_channels);
19828c2ecf20Sopenharmony_ci		return -EINVAL;
19838c2ecf20Sopenharmony_ci	}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	size = sizeof(*atxdmac);
19868c2ecf20Sopenharmony_ci	size += nr_channels * sizeof(struct at_xdmac_chan);
19878c2ecf20Sopenharmony_ci	atxdmac = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
19888c2ecf20Sopenharmony_ci	if (!atxdmac) {
19898c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't allocate at_xdmac structure\n");
19908c2ecf20Sopenharmony_ci		return -ENOMEM;
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	atxdmac->regs = base;
19948c2ecf20Sopenharmony_ci	atxdmac->irq = irq;
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	atxdmac->clk = devm_clk_get(&pdev->dev, "dma_clk");
19978c2ecf20Sopenharmony_ci	if (IS_ERR(atxdmac->clk)) {
19988c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't get dma_clk\n");
19998c2ecf20Sopenharmony_ci		return PTR_ERR(atxdmac->clk);
20008c2ecf20Sopenharmony_ci	}
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	/* Do not use dev res to prevent races with tasklet */
20038c2ecf20Sopenharmony_ci	ret = request_irq(atxdmac->irq, at_xdmac_interrupt, 0, "at_xdmac", atxdmac);
20048c2ecf20Sopenharmony_ci	if (ret) {
20058c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't request irq\n");
20068c2ecf20Sopenharmony_ci		return ret;
20078c2ecf20Sopenharmony_ci	}
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(atxdmac->clk);
20108c2ecf20Sopenharmony_ci	if (ret) {
20118c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't prepare or enable clock\n");
20128c2ecf20Sopenharmony_ci		goto err_free_irq;
20138c2ecf20Sopenharmony_ci	}
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	atxdmac->at_xdmac_desc_pool =
20168c2ecf20Sopenharmony_ci		dmam_pool_create(dev_name(&pdev->dev), &pdev->dev,
20178c2ecf20Sopenharmony_ci				sizeof(struct at_xdmac_desc), 4, 0);
20188c2ecf20Sopenharmony_ci	if (!atxdmac->at_xdmac_desc_pool) {
20198c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no memory for descriptors dma pool\n");
20208c2ecf20Sopenharmony_ci		ret = -ENOMEM;
20218c2ecf20Sopenharmony_ci		goto err_clk_disable;
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, atxdmac->dma.cap_mask);
20258c2ecf20Sopenharmony_ci	dma_cap_set(DMA_INTERLEAVE, atxdmac->dma.cap_mask);
20268c2ecf20Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, atxdmac->dma.cap_mask);
20278c2ecf20Sopenharmony_ci	dma_cap_set(DMA_MEMSET, atxdmac->dma.cap_mask);
20288c2ecf20Sopenharmony_ci	dma_cap_set(DMA_MEMSET_SG, atxdmac->dma.cap_mask);
20298c2ecf20Sopenharmony_ci	dma_cap_set(DMA_SLAVE, atxdmac->dma.cap_mask);
20308c2ecf20Sopenharmony_ci	/*
20318c2ecf20Sopenharmony_ci	 * Without DMA_PRIVATE the driver is not able to allocate more than
20328c2ecf20Sopenharmony_ci	 * one channel, second allocation fails in private_candidate.
20338c2ecf20Sopenharmony_ci	 */
20348c2ecf20Sopenharmony_ci	dma_cap_set(DMA_PRIVATE, atxdmac->dma.cap_mask);
20358c2ecf20Sopenharmony_ci	atxdmac->dma.dev				= &pdev->dev;
20368c2ecf20Sopenharmony_ci	atxdmac->dma.device_alloc_chan_resources	= at_xdmac_alloc_chan_resources;
20378c2ecf20Sopenharmony_ci	atxdmac->dma.device_free_chan_resources		= at_xdmac_free_chan_resources;
20388c2ecf20Sopenharmony_ci	atxdmac->dma.device_tx_status			= at_xdmac_tx_status;
20398c2ecf20Sopenharmony_ci	atxdmac->dma.device_issue_pending		= at_xdmac_issue_pending;
20408c2ecf20Sopenharmony_ci	atxdmac->dma.device_prep_dma_cyclic		= at_xdmac_prep_dma_cyclic;
20418c2ecf20Sopenharmony_ci	atxdmac->dma.device_prep_interleaved_dma	= at_xdmac_prep_interleaved;
20428c2ecf20Sopenharmony_ci	atxdmac->dma.device_prep_dma_memcpy		= at_xdmac_prep_dma_memcpy;
20438c2ecf20Sopenharmony_ci	atxdmac->dma.device_prep_dma_memset		= at_xdmac_prep_dma_memset;
20448c2ecf20Sopenharmony_ci	atxdmac->dma.device_prep_dma_memset_sg		= at_xdmac_prep_dma_memset_sg;
20458c2ecf20Sopenharmony_ci	atxdmac->dma.device_prep_slave_sg		= at_xdmac_prep_slave_sg;
20468c2ecf20Sopenharmony_ci	atxdmac->dma.device_config			= at_xdmac_device_config;
20478c2ecf20Sopenharmony_ci	atxdmac->dma.device_pause			= at_xdmac_device_pause;
20488c2ecf20Sopenharmony_ci	atxdmac->dma.device_resume			= at_xdmac_device_resume;
20498c2ecf20Sopenharmony_ci	atxdmac->dma.device_terminate_all		= at_xdmac_device_terminate_all;
20508c2ecf20Sopenharmony_ci	atxdmac->dma.src_addr_widths = AT_XDMAC_DMA_BUSWIDTHS;
20518c2ecf20Sopenharmony_ci	atxdmac->dma.dst_addr_widths = AT_XDMAC_DMA_BUSWIDTHS;
20528c2ecf20Sopenharmony_ci	atxdmac->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
20538c2ecf20Sopenharmony_ci	atxdmac->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	/* Disable all chans and interrupts. */
20568c2ecf20Sopenharmony_ci	at_xdmac_off(atxdmac);
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	/* Init channels. */
20598c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&atxdmac->dma.channels);
20608c2ecf20Sopenharmony_ci	for (i = 0; i < nr_channels; i++) {
20618c2ecf20Sopenharmony_ci		struct at_xdmac_chan *atchan = &atxdmac->chan[i];
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci		atchan->chan.device = &atxdmac->dma;
20648c2ecf20Sopenharmony_ci		list_add_tail(&atchan->chan.device_node,
20658c2ecf20Sopenharmony_ci			      &atxdmac->dma.channels);
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci		atchan->ch_regs = at_xdmac_chan_reg_base(atxdmac, i);
20688c2ecf20Sopenharmony_ci		atchan->mask = 1 << i;
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci		spin_lock_init(&atchan->lock);
20718c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&atchan->xfers_list);
20728c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&atchan->free_descs_list);
20738c2ecf20Sopenharmony_ci		tasklet_setup(&atchan->tasklet, at_xdmac_tasklet);
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci		/* Clear pending interrupts. */
20768c2ecf20Sopenharmony_ci		while (at_xdmac_chan_read(atchan, AT_XDMAC_CIS))
20778c2ecf20Sopenharmony_ci			cpu_relax();
20788c2ecf20Sopenharmony_ci	}
20798c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, atxdmac);
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci	ret = dma_async_device_register(&atxdmac->dma);
20828c2ecf20Sopenharmony_ci	if (ret) {
20838c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "fail to register DMA engine device\n");
20848c2ecf20Sopenharmony_ci		goto err_clk_disable;
20858c2ecf20Sopenharmony_ci	}
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	ret = of_dma_controller_register(pdev->dev.of_node,
20888c2ecf20Sopenharmony_ci					 at_xdmac_xlate, atxdmac);
20898c2ecf20Sopenharmony_ci	if (ret) {
20908c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not register of dma controller\n");
20918c2ecf20Sopenharmony_ci		goto err_dma_unregister;
20928c2ecf20Sopenharmony_ci	}
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "%d channels, mapped at 0x%p\n",
20958c2ecf20Sopenharmony_ci		 nr_channels, atxdmac->regs);
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	return 0;
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_cierr_dma_unregister:
21008c2ecf20Sopenharmony_ci	dma_async_device_unregister(&atxdmac->dma);
21018c2ecf20Sopenharmony_cierr_clk_disable:
21028c2ecf20Sopenharmony_ci	clk_disable_unprepare(atxdmac->clk);
21038c2ecf20Sopenharmony_cierr_free_irq:
21048c2ecf20Sopenharmony_ci	free_irq(atxdmac->irq, atxdmac);
21058c2ecf20Sopenharmony_ci	return ret;
21068c2ecf20Sopenharmony_ci}
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_cistatic int at_xdmac_remove(struct platform_device *pdev)
21098c2ecf20Sopenharmony_ci{
21108c2ecf20Sopenharmony_ci	struct at_xdmac	*atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev);
21118c2ecf20Sopenharmony_ci	int		i;
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	at_xdmac_off(atxdmac);
21148c2ecf20Sopenharmony_ci	of_dma_controller_free(pdev->dev.of_node);
21158c2ecf20Sopenharmony_ci	dma_async_device_unregister(&atxdmac->dma);
21168c2ecf20Sopenharmony_ci	clk_disable_unprepare(atxdmac->clk);
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	free_irq(atxdmac->irq, atxdmac);
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci	for (i = 0; i < atxdmac->dma.chancnt; i++) {
21218c2ecf20Sopenharmony_ci		struct at_xdmac_chan *atchan = &atxdmac->chan[i];
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci		tasklet_kill(&atchan->tasklet);
21248c2ecf20Sopenharmony_ci		at_xdmac_free_chan_resources(&atchan->chan);
21258c2ecf20Sopenharmony_ci	}
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	return 0;
21288c2ecf20Sopenharmony_ci}
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_cistatic const struct dev_pm_ops atmel_xdmac_dev_pm_ops = {
21318c2ecf20Sopenharmony_ci	.prepare	= atmel_xdmac_prepare,
21328c2ecf20Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(atmel_xdmac_suspend, atmel_xdmac_resume)
21338c2ecf20Sopenharmony_ci};
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_xdmac_dt_ids[] = {
21368c2ecf20Sopenharmony_ci	{
21378c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d4-dma",
21388c2ecf20Sopenharmony_ci	}, {
21398c2ecf20Sopenharmony_ci		/* sentinel */
21408c2ecf20Sopenharmony_ci	}
21418c2ecf20Sopenharmony_ci};
21428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_xdmac_dt_ids);
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_cistatic struct platform_driver at_xdmac_driver = {
21458c2ecf20Sopenharmony_ci	.probe		= at_xdmac_probe,
21468c2ecf20Sopenharmony_ci	.remove		= at_xdmac_remove,
21478c2ecf20Sopenharmony_ci	.driver = {
21488c2ecf20Sopenharmony_ci		.name		= "at_xdmac",
21498c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(atmel_xdmac_dt_ids),
21508c2ecf20Sopenharmony_ci		.pm		= &atmel_xdmac_dev_pm_ops,
21518c2ecf20Sopenharmony_ci	}
21528c2ecf20Sopenharmony_ci};
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_cistatic int __init at_xdmac_init(void)
21558c2ecf20Sopenharmony_ci{
21568c2ecf20Sopenharmony_ci	return platform_driver_probe(&at_xdmac_driver, at_xdmac_probe);
21578c2ecf20Sopenharmony_ci}
21588c2ecf20Sopenharmony_cisubsys_initcall(at_xdmac_init);
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atmel Extended DMA Controller driver");
21618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
21628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2163