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