162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Atmel Extensible DMA Controller (aka XDMAC on AT91 systems) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Atmel Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Ludovic Desroches <ludovic.desroches@atmel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <asm/barrier.h> 1162306a36Sopenharmony_ci#include <dt-bindings/dma/at91.h> 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/dmaengine.h> 1462306a36Sopenharmony_ci#include <linux/dmapool.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/irq.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/of_dma.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/pm.h> 2462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "dmaengine.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Global registers */ 2962306a36Sopenharmony_ci#define AT_XDMAC_GTYPE 0x00 /* Global Type Register */ 3062306a36Sopenharmony_ci#define AT_XDMAC_NB_CH(i) (((i) & 0x1F) + 1) /* Number of Channels Minus One */ 3162306a36Sopenharmony_ci#define AT_XDMAC_FIFO_SZ(i) (((i) >> 5) & 0x7FF) /* Number of Bytes */ 3262306a36Sopenharmony_ci#define AT_XDMAC_NB_REQ(i) ((((i) >> 16) & 0x3F) + 1) /* Number of Peripheral Requests Minus One */ 3362306a36Sopenharmony_ci#define AT_XDMAC_GCFG 0x04 /* Global Configuration Register */ 3462306a36Sopenharmony_ci#define AT_XDMAC_WRHP(i) (((i) & 0xF) << 4) 3562306a36Sopenharmony_ci#define AT_XDMAC_WRMP(i) (((i) & 0xF) << 8) 3662306a36Sopenharmony_ci#define AT_XDMAC_WRLP(i) (((i) & 0xF) << 12) 3762306a36Sopenharmony_ci#define AT_XDMAC_RDHP(i) (((i) & 0xF) << 16) 3862306a36Sopenharmony_ci#define AT_XDMAC_RDMP(i) (((i) & 0xF) << 20) 3962306a36Sopenharmony_ci#define AT_XDMAC_RDLP(i) (((i) & 0xF) << 24) 4062306a36Sopenharmony_ci#define AT_XDMAC_RDSG(i) (((i) & 0xF) << 28) 4162306a36Sopenharmony_ci#define AT_XDMAC_GCFG_M2M (AT_XDMAC_RDLP(0xF) | AT_XDMAC_WRLP(0xF)) 4262306a36Sopenharmony_ci#define AT_XDMAC_GCFG_P2M (AT_XDMAC_RDSG(0x1) | AT_XDMAC_RDHP(0x3) | \ 4362306a36Sopenharmony_ci AT_XDMAC_WRHP(0x5)) 4462306a36Sopenharmony_ci#define AT_XDMAC_GWAC 0x08 /* Global Weighted Arbiter Configuration Register */ 4562306a36Sopenharmony_ci#define AT_XDMAC_PW0(i) (((i) & 0xF) << 0) 4662306a36Sopenharmony_ci#define AT_XDMAC_PW1(i) (((i) & 0xF) << 4) 4762306a36Sopenharmony_ci#define AT_XDMAC_PW2(i) (((i) & 0xF) << 8) 4862306a36Sopenharmony_ci#define AT_XDMAC_PW3(i) (((i) & 0xF) << 12) 4962306a36Sopenharmony_ci#define AT_XDMAC_GWAC_M2M 0 5062306a36Sopenharmony_ci#define AT_XDMAC_GWAC_P2M (AT_XDMAC_PW0(0xF) | AT_XDMAC_PW2(0xF)) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define AT_XDMAC_GIE 0x0C /* Global Interrupt Enable Register */ 5362306a36Sopenharmony_ci#define AT_XDMAC_GID 0x10 /* Global Interrupt Disable Register */ 5462306a36Sopenharmony_ci#define AT_XDMAC_GIM 0x14 /* Global Interrupt Mask Register */ 5562306a36Sopenharmony_ci#define AT_XDMAC_GIS 0x18 /* Global Interrupt Status Register */ 5662306a36Sopenharmony_ci#define AT_XDMAC_GE 0x1C /* Global Channel Enable Register */ 5762306a36Sopenharmony_ci#define AT_XDMAC_GD 0x20 /* Global Channel Disable Register */ 5862306a36Sopenharmony_ci#define AT_XDMAC_GS 0x24 /* Global Channel Status Register */ 5962306a36Sopenharmony_ci#define AT_XDMAC_VERSION 0xFFC /* XDMAC Version Register */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Channel relative registers offsets */ 6262306a36Sopenharmony_ci#define AT_XDMAC_CIE 0x00 /* Channel Interrupt Enable Register */ 6362306a36Sopenharmony_ci#define AT_XDMAC_CIE_BIE BIT(0) /* End of Block Interrupt Enable Bit */ 6462306a36Sopenharmony_ci#define AT_XDMAC_CIE_LIE BIT(1) /* End of Linked List Interrupt Enable Bit */ 6562306a36Sopenharmony_ci#define AT_XDMAC_CIE_DIE BIT(2) /* End of Disable Interrupt Enable Bit */ 6662306a36Sopenharmony_ci#define AT_XDMAC_CIE_FIE BIT(3) /* End of Flush Interrupt Enable Bit */ 6762306a36Sopenharmony_ci#define AT_XDMAC_CIE_RBEIE BIT(4) /* Read Bus Error Interrupt Enable Bit */ 6862306a36Sopenharmony_ci#define AT_XDMAC_CIE_WBEIE BIT(5) /* Write Bus Error Interrupt Enable Bit */ 6962306a36Sopenharmony_ci#define AT_XDMAC_CIE_ROIE BIT(6) /* Request Overflow Interrupt Enable Bit */ 7062306a36Sopenharmony_ci#define AT_XDMAC_CID 0x04 /* Channel Interrupt Disable Register */ 7162306a36Sopenharmony_ci#define AT_XDMAC_CID_BID BIT(0) /* End of Block Interrupt Disable Bit */ 7262306a36Sopenharmony_ci#define AT_XDMAC_CID_LID BIT(1) /* End of Linked List Interrupt Disable Bit */ 7362306a36Sopenharmony_ci#define AT_XDMAC_CID_DID BIT(2) /* End of Disable Interrupt Disable Bit */ 7462306a36Sopenharmony_ci#define AT_XDMAC_CID_FID BIT(3) /* End of Flush Interrupt Disable Bit */ 7562306a36Sopenharmony_ci#define AT_XDMAC_CID_RBEID BIT(4) /* Read Bus Error Interrupt Disable Bit */ 7662306a36Sopenharmony_ci#define AT_XDMAC_CID_WBEID BIT(5) /* Write Bus Error Interrupt Disable Bit */ 7762306a36Sopenharmony_ci#define AT_XDMAC_CID_ROID BIT(6) /* Request Overflow Interrupt Disable Bit */ 7862306a36Sopenharmony_ci#define AT_XDMAC_CIM 0x08 /* Channel Interrupt Mask Register */ 7962306a36Sopenharmony_ci#define AT_XDMAC_CIM_BIM BIT(0) /* End of Block Interrupt Mask Bit */ 8062306a36Sopenharmony_ci#define AT_XDMAC_CIM_LIM BIT(1) /* End of Linked List Interrupt Mask Bit */ 8162306a36Sopenharmony_ci#define AT_XDMAC_CIM_DIM BIT(2) /* End of Disable Interrupt Mask Bit */ 8262306a36Sopenharmony_ci#define AT_XDMAC_CIM_FIM BIT(3) /* End of Flush Interrupt Mask Bit */ 8362306a36Sopenharmony_ci#define AT_XDMAC_CIM_RBEIM BIT(4) /* Read Bus Error Interrupt Mask Bit */ 8462306a36Sopenharmony_ci#define AT_XDMAC_CIM_WBEIM BIT(5) /* Write Bus Error Interrupt Mask Bit */ 8562306a36Sopenharmony_ci#define AT_XDMAC_CIM_ROIM BIT(6) /* Request Overflow Interrupt Mask Bit */ 8662306a36Sopenharmony_ci#define AT_XDMAC_CIS 0x0C /* Channel Interrupt Status Register */ 8762306a36Sopenharmony_ci#define AT_XDMAC_CIS_BIS BIT(0) /* End of Block Interrupt Status Bit */ 8862306a36Sopenharmony_ci#define AT_XDMAC_CIS_LIS BIT(1) /* End of Linked List Interrupt Status Bit */ 8962306a36Sopenharmony_ci#define AT_XDMAC_CIS_DIS BIT(2) /* End of Disable Interrupt Status Bit */ 9062306a36Sopenharmony_ci#define AT_XDMAC_CIS_FIS BIT(3) /* End of Flush Interrupt Status Bit */ 9162306a36Sopenharmony_ci#define AT_XDMAC_CIS_RBEIS BIT(4) /* Read Bus Error Interrupt Status Bit */ 9262306a36Sopenharmony_ci#define AT_XDMAC_CIS_WBEIS BIT(5) /* Write Bus Error Interrupt Status Bit */ 9362306a36Sopenharmony_ci#define AT_XDMAC_CIS_ROIS BIT(6) /* Request Overflow Interrupt Status Bit */ 9462306a36Sopenharmony_ci#define AT_XDMAC_CSA 0x10 /* Channel Source Address Register */ 9562306a36Sopenharmony_ci#define AT_XDMAC_CDA 0x14 /* Channel Destination Address Register */ 9662306a36Sopenharmony_ci#define AT_XDMAC_CNDA 0x18 /* Channel Next Descriptor Address Register */ 9762306a36Sopenharmony_ci#define AT_XDMAC_CNDA_NDAIF(i) ((i) & 0x1) /* Channel x Next Descriptor Interface */ 9862306a36Sopenharmony_ci#define AT_XDMAC_CNDA_NDA(i) ((i) & 0xfffffffc) /* Channel x Next Descriptor Address */ 9962306a36Sopenharmony_ci#define AT_XDMAC_CNDC 0x1C /* Channel Next Descriptor Control Register */ 10062306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDE (0x1 << 0) /* Channel x Next Descriptor Enable */ 10162306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDSUP (0x1 << 1) /* Channel x Next Descriptor Source Update */ 10262306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDDUP (0x1 << 2) /* Channel x Next Descriptor Destination Update */ 10362306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDVIEW_MASK GENMASK(28, 27) 10462306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDVIEW_NDV0 (0x0 << 3) /* Channel x Next Descriptor View 0 */ 10562306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDVIEW_NDV1 (0x1 << 3) /* Channel x Next Descriptor View 1 */ 10662306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDVIEW_NDV2 (0x2 << 3) /* Channel x Next Descriptor View 2 */ 10762306a36Sopenharmony_ci#define AT_XDMAC_CNDC_NDVIEW_NDV3 (0x3 << 3) /* Channel x Next Descriptor View 3 */ 10862306a36Sopenharmony_ci#define AT_XDMAC_CUBC 0x20 /* Channel Microblock Control Register */ 10962306a36Sopenharmony_ci#define AT_XDMAC_CBC 0x24 /* Channel Block Control Register */ 11062306a36Sopenharmony_ci#define AT_XDMAC_CC 0x28 /* Channel Configuration Register */ 11162306a36Sopenharmony_ci#define AT_XDMAC_CC_TYPE (0x1 << 0) /* Channel Transfer Type */ 11262306a36Sopenharmony_ci#define AT_XDMAC_CC_TYPE_MEM_TRAN (0x0 << 0) /* Memory to Memory Transfer */ 11362306a36Sopenharmony_ci#define AT_XDMAC_CC_TYPE_PER_TRAN (0x1 << 0) /* Peripheral to Memory or Memory to Peripheral Transfer */ 11462306a36Sopenharmony_ci#define AT_XDMAC_CC_MBSIZE_MASK (0x3 << 1) 11562306a36Sopenharmony_ci#define AT_XDMAC_CC_MBSIZE_SINGLE (0x0 << 1) 11662306a36Sopenharmony_ci#define AT_XDMAC_CC_MBSIZE_FOUR (0x1 << 1) 11762306a36Sopenharmony_ci#define AT_XDMAC_CC_MBSIZE_EIGHT (0x2 << 1) 11862306a36Sopenharmony_ci#define AT_XDMAC_CC_MBSIZE_SIXTEEN (0x3 << 1) 11962306a36Sopenharmony_ci#define AT_XDMAC_CC_DSYNC (0x1 << 4) /* Channel Synchronization */ 12062306a36Sopenharmony_ci#define AT_XDMAC_CC_DSYNC_PER2MEM (0x0 << 4) 12162306a36Sopenharmony_ci#define AT_XDMAC_CC_DSYNC_MEM2PER (0x1 << 4) 12262306a36Sopenharmony_ci#define AT_XDMAC_CC_PROT (0x1 << 5) /* Channel Protection */ 12362306a36Sopenharmony_ci#define AT_XDMAC_CC_PROT_SEC (0x0 << 5) 12462306a36Sopenharmony_ci#define AT_XDMAC_CC_PROT_UNSEC (0x1 << 5) 12562306a36Sopenharmony_ci#define AT_XDMAC_CC_SWREQ (0x1 << 6) /* Channel Software Request Trigger */ 12662306a36Sopenharmony_ci#define AT_XDMAC_CC_SWREQ_HWR_CONNECTED (0x0 << 6) 12762306a36Sopenharmony_ci#define AT_XDMAC_CC_SWREQ_SWR_CONNECTED (0x1 << 6) 12862306a36Sopenharmony_ci#define AT_XDMAC_CC_MEMSET (0x1 << 7) /* Channel Fill Block of memory */ 12962306a36Sopenharmony_ci#define AT_XDMAC_CC_MEMSET_NORMAL_MODE (0x0 << 7) 13062306a36Sopenharmony_ci#define AT_XDMAC_CC_MEMSET_HW_MODE (0x1 << 7) 13162306a36Sopenharmony_ci#define AT_XDMAC_CC_CSIZE(i) ((0x7 & (i)) << 8) /* Channel Chunk Size */ 13262306a36Sopenharmony_ci#define AT_XDMAC_CC_DWIDTH_OFFSET 11 13362306a36Sopenharmony_ci#define AT_XDMAC_CC_DWIDTH_MASK (0x3 << AT_XDMAC_CC_DWIDTH_OFFSET) 13462306a36Sopenharmony_ci#define AT_XDMAC_CC_DWIDTH(i) ((0x3 & (i)) << AT_XDMAC_CC_DWIDTH_OFFSET) /* Channel Data Width */ 13562306a36Sopenharmony_ci#define AT_XDMAC_CC_DWIDTH_BYTE 0x0 13662306a36Sopenharmony_ci#define AT_XDMAC_CC_DWIDTH_HALFWORD 0x1 13762306a36Sopenharmony_ci#define AT_XDMAC_CC_DWIDTH_WORD 0x2 13862306a36Sopenharmony_ci#define AT_XDMAC_CC_DWIDTH_DWORD 0x3 13962306a36Sopenharmony_ci#define AT_XDMAC_CC_SIF(i) ((0x1 & (i)) << 13) /* Channel Source Interface Identifier */ 14062306a36Sopenharmony_ci#define AT_XDMAC_CC_DIF(i) ((0x1 & (i)) << 14) /* Channel Destination Interface Identifier */ 14162306a36Sopenharmony_ci#define AT_XDMAC_CC_SAM_MASK (0x3 << 16) /* Channel Source Addressing Mode */ 14262306a36Sopenharmony_ci#define AT_XDMAC_CC_SAM_FIXED_AM (0x0 << 16) 14362306a36Sopenharmony_ci#define AT_XDMAC_CC_SAM_INCREMENTED_AM (0x1 << 16) 14462306a36Sopenharmony_ci#define AT_XDMAC_CC_SAM_UBS_AM (0x2 << 16) 14562306a36Sopenharmony_ci#define AT_XDMAC_CC_SAM_UBS_DS_AM (0x3 << 16) 14662306a36Sopenharmony_ci#define AT_XDMAC_CC_DAM_MASK (0x3 << 18) /* Channel Source Addressing Mode */ 14762306a36Sopenharmony_ci#define AT_XDMAC_CC_DAM_FIXED_AM (0x0 << 18) 14862306a36Sopenharmony_ci#define AT_XDMAC_CC_DAM_INCREMENTED_AM (0x1 << 18) 14962306a36Sopenharmony_ci#define AT_XDMAC_CC_DAM_UBS_AM (0x2 << 18) 15062306a36Sopenharmony_ci#define AT_XDMAC_CC_DAM_UBS_DS_AM (0x3 << 18) 15162306a36Sopenharmony_ci#define AT_XDMAC_CC_INITD (0x1 << 21) /* Channel Initialization Terminated (read only) */ 15262306a36Sopenharmony_ci#define AT_XDMAC_CC_INITD_TERMINATED (0x0 << 21) 15362306a36Sopenharmony_ci#define AT_XDMAC_CC_INITD_IN_PROGRESS (0x1 << 21) 15462306a36Sopenharmony_ci#define AT_XDMAC_CC_RDIP (0x1 << 22) /* Read in Progress (read only) */ 15562306a36Sopenharmony_ci#define AT_XDMAC_CC_RDIP_DONE (0x0 << 22) 15662306a36Sopenharmony_ci#define AT_XDMAC_CC_RDIP_IN_PROGRESS (0x1 << 22) 15762306a36Sopenharmony_ci#define AT_XDMAC_CC_WRIP (0x1 << 23) /* Write in Progress (read only) */ 15862306a36Sopenharmony_ci#define AT_XDMAC_CC_WRIP_DONE (0x0 << 23) 15962306a36Sopenharmony_ci#define AT_XDMAC_CC_WRIP_IN_PROGRESS (0x1 << 23) 16062306a36Sopenharmony_ci#define AT_XDMAC_CC_PERID(i) ((0x7f & (i)) << 24) /* Channel Peripheral Identifier */ 16162306a36Sopenharmony_ci#define AT_XDMAC_CDS_MSP 0x2C /* Channel Data Stride Memory Set Pattern */ 16262306a36Sopenharmony_ci#define AT_XDMAC_CSUS 0x30 /* Channel Source Microblock Stride */ 16362306a36Sopenharmony_ci#define AT_XDMAC_CDUS 0x34 /* Channel Destination Microblock Stride */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* Microblock control members */ 16662306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_UBLEN_MAX 0xFFFFFFUL /* Maximum Microblock Length */ 16762306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDE (0x1 << 24) /* Next Descriptor Enable */ 16862306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NSEN (0x1 << 25) /* Next Descriptor Source Update */ 16962306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDEN (0x1 << 26) /* Next Descriptor Destination Update */ 17062306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV0 (0x0 << 27) /* Next Descriptor View 0 */ 17162306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV1 (0x1 << 27) /* Next Descriptor View 1 */ 17262306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV2 (0x2 << 27) /* Next Descriptor View 2 */ 17362306a36Sopenharmony_ci#define AT_XDMAC_MBR_UBC_NDV3 (0x3 << 27) /* Next Descriptor View 3 */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define AT_XDMAC_MAX_CHAN 0x20 17662306a36Sopenharmony_ci#define AT_XDMAC_MAX_CSIZE 16 /* 16 data */ 17762306a36Sopenharmony_ci#define AT_XDMAC_MAX_DWIDTH 8 /* 64 bits */ 17862306a36Sopenharmony_ci#define AT_XDMAC_RESIDUE_MAX_RETRIES 5 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#define AT_XDMAC_DMA_BUSWIDTHS\ 18162306a36Sopenharmony_ci (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\ 18262306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |\ 18362306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |\ 18462306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |\ 18562306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cienum atc_status { 18862306a36Sopenharmony_ci AT_XDMAC_CHAN_IS_CYCLIC = 0, 18962306a36Sopenharmony_ci AT_XDMAC_CHAN_IS_PAUSED, 19062306a36Sopenharmony_ci AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistruct at_xdmac_layout { 19462306a36Sopenharmony_ci /* Global Channel Read Suspend Register */ 19562306a36Sopenharmony_ci u8 grs; 19662306a36Sopenharmony_ci /* Global Write Suspend Register */ 19762306a36Sopenharmony_ci u8 gws; 19862306a36Sopenharmony_ci /* Global Channel Read Write Suspend Register */ 19962306a36Sopenharmony_ci u8 grws; 20062306a36Sopenharmony_ci /* Global Channel Read Write Resume Register */ 20162306a36Sopenharmony_ci u8 grwr; 20262306a36Sopenharmony_ci /* Global Channel Software Request Register */ 20362306a36Sopenharmony_ci u8 gswr; 20462306a36Sopenharmony_ci /* Global channel Software Request Status Register */ 20562306a36Sopenharmony_ci u8 gsws; 20662306a36Sopenharmony_ci /* Global Channel Software Flush Request Register */ 20762306a36Sopenharmony_ci u8 gswf; 20862306a36Sopenharmony_ci /* Channel reg base */ 20962306a36Sopenharmony_ci u8 chan_cc_reg_base; 21062306a36Sopenharmony_ci /* Source/Destination Interface must be specified or not */ 21162306a36Sopenharmony_ci bool sdif; 21262306a36Sopenharmony_ci /* AXI queue priority configuration supported */ 21362306a36Sopenharmony_ci bool axi_config; 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* ----- Channels ----- */ 21762306a36Sopenharmony_cistruct at_xdmac_chan { 21862306a36Sopenharmony_ci struct dma_chan chan; 21962306a36Sopenharmony_ci void __iomem *ch_regs; 22062306a36Sopenharmony_ci u32 mask; /* Channel Mask */ 22162306a36Sopenharmony_ci u32 cfg; /* Channel Configuration Register */ 22262306a36Sopenharmony_ci u8 perid; /* Peripheral ID */ 22362306a36Sopenharmony_ci u8 perif; /* Peripheral Interface */ 22462306a36Sopenharmony_ci u8 memif; /* Memory Interface */ 22562306a36Sopenharmony_ci u32 save_cc; 22662306a36Sopenharmony_ci u32 save_cim; 22762306a36Sopenharmony_ci u32 save_cnda; 22862306a36Sopenharmony_ci u32 save_cndc; 22962306a36Sopenharmony_ci u32 irq_status; 23062306a36Sopenharmony_ci unsigned long status; 23162306a36Sopenharmony_ci struct tasklet_struct tasklet; 23262306a36Sopenharmony_ci struct dma_slave_config sconfig; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci spinlock_t lock; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci struct list_head xfers_list; 23762306a36Sopenharmony_ci struct list_head free_descs_list; 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* ----- Controller ----- */ 24262306a36Sopenharmony_cistruct at_xdmac { 24362306a36Sopenharmony_ci struct dma_device dma; 24462306a36Sopenharmony_ci void __iomem *regs; 24562306a36Sopenharmony_ci struct device *dev; 24662306a36Sopenharmony_ci int irq; 24762306a36Sopenharmony_ci struct clk *clk; 24862306a36Sopenharmony_ci u32 save_gim; 24962306a36Sopenharmony_ci u32 save_gs; 25062306a36Sopenharmony_ci struct dma_pool *at_xdmac_desc_pool; 25162306a36Sopenharmony_ci const struct at_xdmac_layout *layout; 25262306a36Sopenharmony_ci struct at_xdmac_chan chan[]; 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* ----- Descriptors ----- */ 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* Linked List Descriptor */ 25962306a36Sopenharmony_cistruct at_xdmac_lld { 26062306a36Sopenharmony_ci u32 mbr_nda; /* Next Descriptor Member */ 26162306a36Sopenharmony_ci u32 mbr_ubc; /* Microblock Control Member */ 26262306a36Sopenharmony_ci u32 mbr_sa; /* Source Address Member */ 26362306a36Sopenharmony_ci u32 mbr_da; /* Destination Address Member */ 26462306a36Sopenharmony_ci u32 mbr_cfg; /* Configuration Register */ 26562306a36Sopenharmony_ci u32 mbr_bc; /* Block Control Register */ 26662306a36Sopenharmony_ci u32 mbr_ds; /* Data Stride Register */ 26762306a36Sopenharmony_ci u32 mbr_sus; /* Source Microblock Stride Register */ 26862306a36Sopenharmony_ci u32 mbr_dus; /* Destination Microblock Stride Register */ 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 64-bit alignment needed to update CNDA and CUBC registers in an atomic way. */ 27262306a36Sopenharmony_cistruct at_xdmac_desc { 27362306a36Sopenharmony_ci struct at_xdmac_lld lld; 27462306a36Sopenharmony_ci enum dma_transfer_direction direction; 27562306a36Sopenharmony_ci struct dma_async_tx_descriptor tx_dma_desc; 27662306a36Sopenharmony_ci struct list_head desc_node; 27762306a36Sopenharmony_ci /* Following members are only used by the first descriptor */ 27862306a36Sopenharmony_ci bool active_xfer; 27962306a36Sopenharmony_ci unsigned int xfer_size; 28062306a36Sopenharmony_ci struct list_head descs_list; 28162306a36Sopenharmony_ci struct list_head xfer_node; 28262306a36Sopenharmony_ci} __aligned(sizeof(u64)); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic const struct at_xdmac_layout at_xdmac_sama5d4_layout = { 28562306a36Sopenharmony_ci .grs = 0x28, 28662306a36Sopenharmony_ci .gws = 0x2C, 28762306a36Sopenharmony_ci .grws = 0x30, 28862306a36Sopenharmony_ci .grwr = 0x34, 28962306a36Sopenharmony_ci .gswr = 0x38, 29062306a36Sopenharmony_ci .gsws = 0x3C, 29162306a36Sopenharmony_ci .gswf = 0x40, 29262306a36Sopenharmony_ci .chan_cc_reg_base = 0x50, 29362306a36Sopenharmony_ci .sdif = true, 29462306a36Sopenharmony_ci .axi_config = false, 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const struct at_xdmac_layout at_xdmac_sama7g5_layout = { 29862306a36Sopenharmony_ci .grs = 0x30, 29962306a36Sopenharmony_ci .gws = 0x38, 30062306a36Sopenharmony_ci .grws = 0x40, 30162306a36Sopenharmony_ci .grwr = 0x44, 30262306a36Sopenharmony_ci .gswr = 0x48, 30362306a36Sopenharmony_ci .gsws = 0x4C, 30462306a36Sopenharmony_ci .gswf = 0x50, 30562306a36Sopenharmony_ci .chan_cc_reg_base = 0x60, 30662306a36Sopenharmony_ci .sdif = false, 30762306a36Sopenharmony_ci .axi_config = true, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic inline void __iomem *at_xdmac_chan_reg_base(struct at_xdmac *atxdmac, unsigned int chan_nb) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci return atxdmac->regs + (atxdmac->layout->chan_cc_reg_base + chan_nb * 0x40); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci#define at_xdmac_read(atxdmac, reg) readl_relaxed((atxdmac)->regs + (reg)) 31662306a36Sopenharmony_ci#define at_xdmac_write(atxdmac, reg, value) \ 31762306a36Sopenharmony_ci writel_relaxed((value), (atxdmac)->regs + (reg)) 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci#define at_xdmac_chan_read(atchan, reg) readl_relaxed((atchan)->ch_regs + (reg)) 32062306a36Sopenharmony_ci#define at_xdmac_chan_write(atchan, reg, value) writel_relaxed((value), (atchan)->ch_regs + (reg)) 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic inline struct at_xdmac_chan *to_at_xdmac_chan(struct dma_chan *dchan) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci return container_of(dchan, struct at_xdmac_chan, chan); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic struct device *chan2dev(struct dma_chan *chan) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return &chan->dev->device; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic inline struct at_xdmac *to_at_xdmac(struct dma_device *ddev) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci return container_of(ddev, struct at_xdmac, dma); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic inline struct at_xdmac_desc *txd_to_at_desc(struct dma_async_tx_descriptor *txd) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci return container_of(txd, struct at_xdmac_desc, tx_dma_desc); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic inline int at_xdmac_chan_is_cyclic(struct at_xdmac_chan *atchan) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci return test_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic inline int at_xdmac_chan_is_paused(struct at_xdmac_chan *atchan) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci return test_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic inline int at_xdmac_chan_is_paused_internal(struct at_xdmac_chan *atchan) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci return test_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic inline bool at_xdmac_chan_is_peripheral_xfer(u32 cfg) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci return cfg & AT_XDMAC_CC_TYPE_PER_TRAN; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic inline u8 at_xdmac_get_dwidth(u32 cfg) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci return (cfg & AT_XDMAC_CC_DWIDTH_MASK) >> AT_XDMAC_CC_DWIDTH_OFFSET; 36562306a36Sopenharmony_ci}; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic unsigned int init_nr_desc_per_channel = 64; 36862306a36Sopenharmony_cimodule_param(init_nr_desc_per_channel, uint, 0644); 36962306a36Sopenharmony_ciMODULE_PARM_DESC(init_nr_desc_per_channel, 37062306a36Sopenharmony_ci "initial descriptors per channel (default: 64)"); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void at_xdmac_runtime_suspend_descriptors(struct at_xdmac_chan *atchan) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 37662306a36Sopenharmony_ci struct at_xdmac_desc *desc, *_desc; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &atchan->xfers_list, xfer_node) { 37962306a36Sopenharmony_ci if (!desc->active_xfer) 38062306a36Sopenharmony_ci continue; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 38362306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int at_xdmac_runtime_resume_descriptors(struct at_xdmac_chan *atchan) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 39062306a36Sopenharmony_ci struct at_xdmac_desc *desc, *_desc; 39162306a36Sopenharmony_ci int ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &atchan->xfers_list, xfer_node) { 39462306a36Sopenharmony_ci if (!desc->active_xfer) 39562306a36Sopenharmony_ci continue; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 39862306a36Sopenharmony_ci if (ret < 0) 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 40862306a36Sopenharmony_ci int ret; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 41162306a36Sopenharmony_ci if (ret < 0) 41262306a36Sopenharmony_ci return false; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ret = !!(at_xdmac_chan_read(atchan, AT_XDMAC_GS) & atchan->mask); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 41762306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return ret; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void at_xdmac_off(struct at_xdmac *atxdmac, bool suspend_descriptors) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct dma_chan *chan, *_chan; 42562306a36Sopenharmony_ci struct at_xdmac_chan *atchan; 42662306a36Sopenharmony_ci int ret; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 42962306a36Sopenharmony_ci if (ret < 0) 43062306a36Sopenharmony_ci return; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GD, -1L); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Wait that all chans are disabled. */ 43562306a36Sopenharmony_ci while (at_xdmac_read(atxdmac, AT_XDMAC_GS)) 43662306a36Sopenharmony_ci cpu_relax(); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GID, -1L); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Decrement runtime PM ref counter for each active descriptor. */ 44162306a36Sopenharmony_ci if (!list_empty(&atxdmac->dma.channels) && suspend_descriptors) { 44262306a36Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, 44362306a36Sopenharmony_ci device_node) { 44462306a36Sopenharmony_ci atchan = to_at_xdmac_chan(chan); 44562306a36Sopenharmony_ci at_xdmac_runtime_suspend_descriptors(atchan); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 45062306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* Call with lock hold. */ 45462306a36Sopenharmony_cistatic void at_xdmac_start_xfer(struct at_xdmac_chan *atchan, 45562306a36Sopenharmony_ci struct at_xdmac_desc *first) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 45862306a36Sopenharmony_ci u32 reg; 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 46262306a36Sopenharmony_ci if (ret < 0) 46362306a36Sopenharmony_ci return; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, first); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Set transfer as active to not try to start it again. */ 46862306a36Sopenharmony_ci first->active_xfer = true; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Tell xdmac where to get the first descriptor. */ 47162306a36Sopenharmony_ci reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys); 47262306a36Sopenharmony_ci if (atxdmac->layout->sdif) 47362306a36Sopenharmony_ci reg |= AT_XDMAC_CNDA_NDAIF(atchan->memif); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, reg); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* 47862306a36Sopenharmony_ci * When doing non cyclic transfer we need to use the next 47962306a36Sopenharmony_ci * descriptor view 2 since some fields of the configuration register 48062306a36Sopenharmony_ci * depend on transfer size and src/dest addresses. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci if (at_xdmac_chan_is_cyclic(atchan)) 48362306a36Sopenharmony_ci reg = AT_XDMAC_CNDC_NDVIEW_NDV1; 48462306a36Sopenharmony_ci else if ((first->lld.mbr_ubc & 48562306a36Sopenharmony_ci AT_XDMAC_CNDC_NDVIEW_MASK) == AT_XDMAC_MBR_UBC_NDV3) 48662306a36Sopenharmony_ci reg = AT_XDMAC_CNDC_NDVIEW_NDV3; 48762306a36Sopenharmony_ci else 48862306a36Sopenharmony_ci reg = AT_XDMAC_CNDC_NDVIEW_NDV2; 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * Even if the register will be updated from the configuration in the 49162306a36Sopenharmony_ci * descriptor when using view 2 or higher, the PROT bit won't be set 49262306a36Sopenharmony_ci * properly. This bit can be modified only by using the channel 49362306a36Sopenharmony_ci * configuration register. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CC, first->lld.mbr_cfg); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci reg |= AT_XDMAC_CNDC_NDDUP 49862306a36Sopenharmony_ci | AT_XDMAC_CNDC_NDSUP 49962306a36Sopenharmony_ci | AT_XDMAC_CNDC_NDE; 50062306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, reg); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci dev_vdbg(chan2dev(&atchan->chan), 50362306a36Sopenharmony_ci "%s: CC=0x%08x CNDA=0x%08x, CNDC=0x%08x, CSA=0x%08x, CDA=0x%08x, CUBC=0x%08x\n", 50462306a36Sopenharmony_ci __func__, at_xdmac_chan_read(atchan, AT_XDMAC_CC), 50562306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CNDA), 50662306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CNDC), 50762306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CSA), 50862306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CDA), 50962306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CUBC)); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CID, 0xffffffff); 51262306a36Sopenharmony_ci reg = AT_XDMAC_CIE_RBEIE | AT_XDMAC_CIE_WBEIE; 51362306a36Sopenharmony_ci /* 51462306a36Sopenharmony_ci * Request Overflow Error is only for peripheral synchronized transfers 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci if (at_xdmac_chan_is_peripheral_xfer(first->lld.mbr_cfg)) 51762306a36Sopenharmony_ci reg |= AT_XDMAC_CIE_ROIE; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* 52062306a36Sopenharmony_ci * There is no end of list when doing cyclic dma, we need to get 52162306a36Sopenharmony_ci * an interrupt after each periods. 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci if (at_xdmac_chan_is_cyclic(atchan)) 52462306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CIE, 52562306a36Sopenharmony_ci reg | AT_XDMAC_CIE_BIE); 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CIE, 52862306a36Sopenharmony_ci reg | AT_XDMAC_CIE_LIE); 52962306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GIE, atchan->mask); 53062306a36Sopenharmony_ci dev_vdbg(chan2dev(&atchan->chan), 53162306a36Sopenharmony_ci "%s: enable channel (0x%08x)\n", __func__, atchan->mask); 53262306a36Sopenharmony_ci wmb(); 53362306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci dev_vdbg(chan2dev(&atchan->chan), 53662306a36Sopenharmony_ci "%s: CC=0x%08x CNDA=0x%08x, CNDC=0x%08x, CSA=0x%08x, CDA=0x%08x, CUBC=0x%08x\n", 53762306a36Sopenharmony_ci __func__, at_xdmac_chan_read(atchan, AT_XDMAC_CC), 53862306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CNDA), 53962306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CNDC), 54062306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CSA), 54162306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CDA), 54262306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CUBC)); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic dma_cookie_t at_xdmac_tx_submit(struct dma_async_tx_descriptor *tx) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct at_xdmac_desc *desc = txd_to_at_desc(tx); 54862306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(tx->chan); 54962306a36Sopenharmony_ci dma_cookie_t cookie; 55062306a36Sopenharmony_ci unsigned long irqflags; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, irqflags); 55362306a36Sopenharmony_ci cookie = dma_cookie_assign(tx); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci list_add_tail(&desc->xfer_node, &atchan->xfers_list); 55662306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, irqflags); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci dev_vdbg(chan2dev(tx->chan), "%s: atchan 0x%p, add desc 0x%p to xfers_list\n", 55962306a36Sopenharmony_ci __func__, atchan, desc); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return cookie; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic struct at_xdmac_desc *at_xdmac_alloc_desc(struct dma_chan *chan, 56562306a36Sopenharmony_ci gfp_t gfp_flags) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct at_xdmac_desc *desc; 56862306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(chan->device); 56962306a36Sopenharmony_ci dma_addr_t phys; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci desc = dma_pool_zalloc(atxdmac->at_xdmac_desc_pool, gfp_flags, &phys); 57262306a36Sopenharmony_ci if (desc) { 57362306a36Sopenharmony_ci INIT_LIST_HEAD(&desc->descs_list); 57462306a36Sopenharmony_ci dma_async_tx_descriptor_init(&desc->tx_dma_desc, chan); 57562306a36Sopenharmony_ci desc->tx_dma_desc.tx_submit = at_xdmac_tx_submit; 57662306a36Sopenharmony_ci desc->tx_dma_desc.phys = phys; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return desc; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void at_xdmac_init_used_desc(struct at_xdmac_desc *desc) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci memset(&desc->lld, 0, sizeof(desc->lld)); 58562306a36Sopenharmony_ci INIT_LIST_HEAD(&desc->descs_list); 58662306a36Sopenharmony_ci desc->direction = DMA_TRANS_NONE; 58762306a36Sopenharmony_ci desc->xfer_size = 0; 58862306a36Sopenharmony_ci desc->active_xfer = false; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/* Call must be protected by lock. */ 59262306a36Sopenharmony_cistatic struct at_xdmac_desc *at_xdmac_get_desc(struct at_xdmac_chan *atchan) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct at_xdmac_desc *desc; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (list_empty(&atchan->free_descs_list)) { 59762306a36Sopenharmony_ci desc = at_xdmac_alloc_desc(&atchan->chan, GFP_NOWAIT); 59862306a36Sopenharmony_ci } else { 59962306a36Sopenharmony_ci desc = list_first_entry(&atchan->free_descs_list, 60062306a36Sopenharmony_ci struct at_xdmac_desc, desc_node); 60162306a36Sopenharmony_ci list_del(&desc->desc_node); 60262306a36Sopenharmony_ci at_xdmac_init_used_desc(desc); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return desc; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic void at_xdmac_queue_desc(struct dma_chan *chan, 60962306a36Sopenharmony_ci struct at_xdmac_desc *prev, 61062306a36Sopenharmony_ci struct at_xdmac_desc *desc) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci if (!prev || !desc) 61362306a36Sopenharmony_ci return; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci prev->lld.mbr_nda = desc->tx_dma_desc.phys; 61662306a36Sopenharmony_ci prev->lld.mbr_ubc |= AT_XDMAC_MBR_UBC_NDE; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: chain lld: prev=0x%p, mbr_nda=%pad\n", 61962306a36Sopenharmony_ci __func__, prev, &prev->lld.mbr_nda); 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic inline void at_xdmac_increment_block_count(struct dma_chan *chan, 62362306a36Sopenharmony_ci struct at_xdmac_desc *desc) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci if (!desc) 62662306a36Sopenharmony_ci return; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci desc->lld.mbr_bc++; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 63162306a36Sopenharmony_ci "%s: incrementing the block count of the desc 0x%p\n", 63262306a36Sopenharmony_ci __func__, desc); 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic struct dma_chan *at_xdmac_xlate(struct of_phandle_args *dma_spec, 63662306a36Sopenharmony_ci struct of_dma *of_dma) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct at_xdmac *atxdmac = of_dma->of_dma_data; 63962306a36Sopenharmony_ci struct at_xdmac_chan *atchan; 64062306a36Sopenharmony_ci struct dma_chan *chan; 64162306a36Sopenharmony_ci struct device *dev = atxdmac->dma.dev; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (dma_spec->args_count != 1) { 64462306a36Sopenharmony_ci dev_err(dev, "dma phandler args: bad number of args\n"); 64562306a36Sopenharmony_ci return NULL; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci chan = dma_get_any_slave_channel(&atxdmac->dma); 64962306a36Sopenharmony_ci if (!chan) { 65062306a36Sopenharmony_ci dev_err(dev, "can't get a dma channel\n"); 65162306a36Sopenharmony_ci return NULL; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci atchan = to_at_xdmac_chan(chan); 65562306a36Sopenharmony_ci atchan->memif = AT91_XDMAC_DT_GET_MEM_IF(dma_spec->args[0]); 65662306a36Sopenharmony_ci atchan->perif = AT91_XDMAC_DT_GET_PER_IF(dma_spec->args[0]); 65762306a36Sopenharmony_ci atchan->perid = AT91_XDMAC_DT_GET_PERID(dma_spec->args[0]); 65862306a36Sopenharmony_ci dev_dbg(dev, "chan dt cfg: memif=%u perif=%u perid=%u\n", 65962306a36Sopenharmony_ci atchan->memif, atchan->perif, atchan->perid); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return chan; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int at_xdmac_compute_chan_conf(struct dma_chan *chan, 66562306a36Sopenharmony_ci enum dma_transfer_direction direction) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 66862306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 66962306a36Sopenharmony_ci int csize, dwidth; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) { 67262306a36Sopenharmony_ci atchan->cfg = 67362306a36Sopenharmony_ci AT91_XDMAC_DT_PERID(atchan->perid) 67462306a36Sopenharmony_ci | AT_XDMAC_CC_DAM_INCREMENTED_AM 67562306a36Sopenharmony_ci | AT_XDMAC_CC_SAM_FIXED_AM 67662306a36Sopenharmony_ci | AT_XDMAC_CC_SWREQ_HWR_CONNECTED 67762306a36Sopenharmony_ci | AT_XDMAC_CC_DSYNC_PER2MEM 67862306a36Sopenharmony_ci | AT_XDMAC_CC_MBSIZE_SIXTEEN 67962306a36Sopenharmony_ci | AT_XDMAC_CC_TYPE_PER_TRAN; 68062306a36Sopenharmony_ci if (atxdmac->layout->sdif) 68162306a36Sopenharmony_ci atchan->cfg |= AT_XDMAC_CC_DIF(atchan->memif) | 68262306a36Sopenharmony_ci AT_XDMAC_CC_SIF(atchan->perif); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci csize = ffs(atchan->sconfig.src_maxburst) - 1; 68562306a36Sopenharmony_ci if (csize < 0) { 68662306a36Sopenharmony_ci dev_err(chan2dev(chan), "invalid src maxburst value\n"); 68762306a36Sopenharmony_ci return -EINVAL; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci atchan->cfg |= AT_XDMAC_CC_CSIZE(csize); 69062306a36Sopenharmony_ci dwidth = ffs(atchan->sconfig.src_addr_width) - 1; 69162306a36Sopenharmony_ci if (dwidth < 0) { 69262306a36Sopenharmony_ci dev_err(chan2dev(chan), "invalid src addr width value\n"); 69362306a36Sopenharmony_ci return -EINVAL; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth); 69662306a36Sopenharmony_ci } else if (direction == DMA_MEM_TO_DEV) { 69762306a36Sopenharmony_ci atchan->cfg = 69862306a36Sopenharmony_ci AT91_XDMAC_DT_PERID(atchan->perid) 69962306a36Sopenharmony_ci | AT_XDMAC_CC_DAM_FIXED_AM 70062306a36Sopenharmony_ci | AT_XDMAC_CC_SAM_INCREMENTED_AM 70162306a36Sopenharmony_ci | AT_XDMAC_CC_SWREQ_HWR_CONNECTED 70262306a36Sopenharmony_ci | AT_XDMAC_CC_DSYNC_MEM2PER 70362306a36Sopenharmony_ci | AT_XDMAC_CC_MBSIZE_SIXTEEN 70462306a36Sopenharmony_ci | AT_XDMAC_CC_TYPE_PER_TRAN; 70562306a36Sopenharmony_ci if (atxdmac->layout->sdif) 70662306a36Sopenharmony_ci atchan->cfg |= AT_XDMAC_CC_DIF(atchan->perif) | 70762306a36Sopenharmony_ci AT_XDMAC_CC_SIF(atchan->memif); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci csize = ffs(atchan->sconfig.dst_maxburst) - 1; 71062306a36Sopenharmony_ci if (csize < 0) { 71162306a36Sopenharmony_ci dev_err(chan2dev(chan), "invalid src maxburst value\n"); 71262306a36Sopenharmony_ci return -EINVAL; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci atchan->cfg |= AT_XDMAC_CC_CSIZE(csize); 71562306a36Sopenharmony_ci dwidth = ffs(atchan->sconfig.dst_addr_width) - 1; 71662306a36Sopenharmony_ci if (dwidth < 0) { 71762306a36Sopenharmony_ci dev_err(chan2dev(chan), "invalid dst addr width value\n"); 71862306a36Sopenharmony_ci return -EINVAL; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: cfg=0x%08x\n", __func__, atchan->cfg); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * Only check that maxburst and addr width values are supported by 73062306a36Sopenharmony_ci * the controller but not that the configuration is good to perform the 73162306a36Sopenharmony_ci * transfer since we don't know the direction at this stage. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_cistatic int at_xdmac_check_slave_config(struct dma_slave_config *sconfig) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci if ((sconfig->src_maxburst > AT_XDMAC_MAX_CSIZE) 73662306a36Sopenharmony_ci || (sconfig->dst_maxburst > AT_XDMAC_MAX_CSIZE)) 73762306a36Sopenharmony_ci return -EINVAL; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if ((sconfig->src_addr_width > AT_XDMAC_MAX_DWIDTH) 74062306a36Sopenharmony_ci || (sconfig->dst_addr_width > AT_XDMAC_MAX_DWIDTH)) 74162306a36Sopenharmony_ci return -EINVAL; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int at_xdmac_set_slave_config(struct dma_chan *chan, 74762306a36Sopenharmony_ci struct dma_slave_config *sconfig) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (at_xdmac_check_slave_config(sconfig)) { 75262306a36Sopenharmony_ci dev_err(chan2dev(chan), "invalid slave configuration\n"); 75362306a36Sopenharmony_ci return -EINVAL; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci memcpy(&atchan->sconfig, sconfig, sizeof(atchan->sconfig)); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci return 0; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 76262306a36Sopenharmony_ciat_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, 76362306a36Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction direction, 76462306a36Sopenharmony_ci unsigned long flags, void *context) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 76762306a36Sopenharmony_ci struct at_xdmac_desc *first = NULL, *prev = NULL; 76862306a36Sopenharmony_ci struct scatterlist *sg; 76962306a36Sopenharmony_ci int i; 77062306a36Sopenharmony_ci unsigned int xfer_size = 0; 77162306a36Sopenharmony_ci unsigned long irqflags; 77262306a36Sopenharmony_ci struct dma_async_tx_descriptor *ret = NULL; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (!sgl) 77562306a36Sopenharmony_ci return NULL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!is_slave_direction(direction)) { 77862306a36Sopenharmony_ci dev_err(chan2dev(chan), "invalid DMA direction\n"); 77962306a36Sopenharmony_ci return NULL; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: sg_len=%d, dir=%s, flags=0x%lx\n", 78362306a36Sopenharmony_ci __func__, sg_len, 78462306a36Sopenharmony_ci direction == DMA_MEM_TO_DEV ? "to device" : "from device", 78562306a36Sopenharmony_ci flags); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* Protect dma_sconfig field that can be modified by set_slave_conf. */ 78862306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, irqflags); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (at_xdmac_compute_chan_conf(chan, direction)) 79162306a36Sopenharmony_ci goto spin_unlock; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* Prepare descriptors. */ 79462306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 79562306a36Sopenharmony_ci struct at_xdmac_desc *desc = NULL; 79662306a36Sopenharmony_ci u32 len, mem, dwidth, fixed_dwidth; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci len = sg_dma_len(sg); 79962306a36Sopenharmony_ci mem = sg_dma_address(sg); 80062306a36Sopenharmony_ci if (unlikely(!len)) { 80162306a36Sopenharmony_ci dev_err(chan2dev(chan), "sg data length is zero\n"); 80262306a36Sopenharmony_ci goto spin_unlock; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: * sg%d len=%u, mem=0x%08x\n", 80562306a36Sopenharmony_ci __func__, i, len, mem); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci desc = at_xdmac_get_desc(atchan); 80862306a36Sopenharmony_ci if (!desc) { 80962306a36Sopenharmony_ci dev_err(chan2dev(chan), "can't get descriptor\n"); 81062306a36Sopenharmony_ci if (first) 81162306a36Sopenharmony_ci list_splice_tail_init(&first->descs_list, 81262306a36Sopenharmony_ci &atchan->free_descs_list); 81362306a36Sopenharmony_ci goto spin_unlock; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* Linked list descriptor setup. */ 81762306a36Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) { 81862306a36Sopenharmony_ci desc->lld.mbr_sa = atchan->sconfig.src_addr; 81962306a36Sopenharmony_ci desc->lld.mbr_da = mem; 82062306a36Sopenharmony_ci } else { 82162306a36Sopenharmony_ci desc->lld.mbr_sa = mem; 82262306a36Sopenharmony_ci desc->lld.mbr_da = atchan->sconfig.dst_addr; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci dwidth = at_xdmac_get_dwidth(atchan->cfg); 82562306a36Sopenharmony_ci fixed_dwidth = IS_ALIGNED(len, 1 << dwidth) 82662306a36Sopenharmony_ci ? dwidth 82762306a36Sopenharmony_ci : AT_XDMAC_CC_DWIDTH_BYTE; 82862306a36Sopenharmony_ci desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV2 /* next descriptor view */ 82962306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NDEN /* next descriptor dst parameter update */ 83062306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NSEN /* next descriptor src parameter update */ 83162306a36Sopenharmony_ci | (len >> fixed_dwidth); /* microblock length */ 83262306a36Sopenharmony_ci desc->lld.mbr_cfg = (atchan->cfg & ~AT_XDMAC_CC_DWIDTH_MASK) | 83362306a36Sopenharmony_ci AT_XDMAC_CC_DWIDTH(fixed_dwidth); 83462306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 83562306a36Sopenharmony_ci "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n", 83662306a36Sopenharmony_ci __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Chain lld. */ 83962306a36Sopenharmony_ci if (prev) 84062306a36Sopenharmony_ci at_xdmac_queue_desc(chan, prev, desc); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci prev = desc; 84362306a36Sopenharmony_ci if (!first) 84462306a36Sopenharmony_ci first = desc; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n", 84762306a36Sopenharmony_ci __func__, desc, first); 84862306a36Sopenharmony_ci list_add_tail(&desc->desc_node, &first->descs_list); 84962306a36Sopenharmony_ci xfer_size += len; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci first->tx_dma_desc.flags = flags; 85462306a36Sopenharmony_ci first->xfer_size = xfer_size; 85562306a36Sopenharmony_ci first->direction = direction; 85662306a36Sopenharmony_ci ret = &first->tx_dma_desc; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cispin_unlock: 85962306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, irqflags); 86062306a36Sopenharmony_ci return ret; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 86462306a36Sopenharmony_ciat_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, 86562306a36Sopenharmony_ci size_t buf_len, size_t period_len, 86662306a36Sopenharmony_ci enum dma_transfer_direction direction, 86762306a36Sopenharmony_ci unsigned long flags) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 87062306a36Sopenharmony_ci struct at_xdmac_desc *first = NULL, *prev = NULL; 87162306a36Sopenharmony_ci unsigned int periods = buf_len / period_len; 87262306a36Sopenharmony_ci int i; 87362306a36Sopenharmony_ci unsigned long irqflags; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: buf_addr=%pad, buf_len=%zd, period_len=%zd, dir=%s, flags=0x%lx\n", 87662306a36Sopenharmony_ci __func__, &buf_addr, buf_len, period_len, 87762306a36Sopenharmony_ci direction == DMA_MEM_TO_DEV ? "mem2per" : "per2mem", flags); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (!is_slave_direction(direction)) { 88062306a36Sopenharmony_ci dev_err(chan2dev(chan), "invalid DMA direction\n"); 88162306a36Sopenharmony_ci return NULL; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (test_and_set_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status)) { 88562306a36Sopenharmony_ci dev_err(chan2dev(chan), "channel currently used\n"); 88662306a36Sopenharmony_ci return NULL; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (at_xdmac_compute_chan_conf(chan, direction)) 89062306a36Sopenharmony_ci return NULL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci for (i = 0; i < periods; i++) { 89362306a36Sopenharmony_ci struct at_xdmac_desc *desc = NULL; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, irqflags); 89662306a36Sopenharmony_ci desc = at_xdmac_get_desc(atchan); 89762306a36Sopenharmony_ci if (!desc) { 89862306a36Sopenharmony_ci dev_err(chan2dev(chan), "can't get descriptor\n"); 89962306a36Sopenharmony_ci if (first) 90062306a36Sopenharmony_ci list_splice_tail_init(&first->descs_list, 90162306a36Sopenharmony_ci &atchan->free_descs_list); 90262306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, irqflags); 90362306a36Sopenharmony_ci return NULL; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, irqflags); 90662306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 90762306a36Sopenharmony_ci "%s: desc=0x%p, tx_dma_desc.phys=%pad\n", 90862306a36Sopenharmony_ci __func__, desc, &desc->tx_dma_desc.phys); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) { 91162306a36Sopenharmony_ci desc->lld.mbr_sa = atchan->sconfig.src_addr; 91262306a36Sopenharmony_ci desc->lld.mbr_da = buf_addr + i * period_len; 91362306a36Sopenharmony_ci } else { 91462306a36Sopenharmony_ci desc->lld.mbr_sa = buf_addr + i * period_len; 91562306a36Sopenharmony_ci desc->lld.mbr_da = atchan->sconfig.dst_addr; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci desc->lld.mbr_cfg = atchan->cfg; 91862306a36Sopenharmony_ci desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1 91962306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NDEN 92062306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NSEN 92162306a36Sopenharmony_ci | period_len >> at_xdmac_get_dwidth(desc->lld.mbr_cfg); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 92462306a36Sopenharmony_ci "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n", 92562306a36Sopenharmony_ci __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* Chain lld. */ 92862306a36Sopenharmony_ci if (prev) 92962306a36Sopenharmony_ci at_xdmac_queue_desc(chan, prev, desc); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci prev = desc; 93262306a36Sopenharmony_ci if (!first) 93362306a36Sopenharmony_ci first = desc; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n", 93662306a36Sopenharmony_ci __func__, desc, first); 93762306a36Sopenharmony_ci list_add_tail(&desc->desc_node, &first->descs_list); 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci at_xdmac_queue_desc(chan, prev, first); 94162306a36Sopenharmony_ci first->tx_dma_desc.flags = flags; 94262306a36Sopenharmony_ci first->xfer_size = buf_len; 94362306a36Sopenharmony_ci first->direction = direction; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return &first->tx_dma_desc; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic inline u32 at_xdmac_align_width(struct dma_chan *chan, dma_addr_t addr) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci u32 width; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* 95362306a36Sopenharmony_ci * Check address alignment to select the greater data width we 95462306a36Sopenharmony_ci * can use. 95562306a36Sopenharmony_ci * 95662306a36Sopenharmony_ci * Some XDMAC implementations don't provide dword transfer, in 95762306a36Sopenharmony_ci * this case selecting dword has the same behavior as 95862306a36Sopenharmony_ci * selecting word transfers. 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci if (!(addr & 7)) { 96162306a36Sopenharmony_ci width = AT_XDMAC_CC_DWIDTH_DWORD; 96262306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: dwidth: double word\n", __func__); 96362306a36Sopenharmony_ci } else if (!(addr & 3)) { 96462306a36Sopenharmony_ci width = AT_XDMAC_CC_DWIDTH_WORD; 96562306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: dwidth: word\n", __func__); 96662306a36Sopenharmony_ci } else if (!(addr & 1)) { 96762306a36Sopenharmony_ci width = AT_XDMAC_CC_DWIDTH_HALFWORD; 96862306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: dwidth: half word\n", __func__); 96962306a36Sopenharmony_ci } else { 97062306a36Sopenharmony_ci width = AT_XDMAC_CC_DWIDTH_BYTE; 97162306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: dwidth: byte\n", __func__); 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci return width; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic struct at_xdmac_desc * 97862306a36Sopenharmony_ciat_xdmac_interleaved_queue_desc(struct dma_chan *chan, 97962306a36Sopenharmony_ci struct at_xdmac_chan *atchan, 98062306a36Sopenharmony_ci struct at_xdmac_desc *prev, 98162306a36Sopenharmony_ci dma_addr_t src, dma_addr_t dst, 98262306a36Sopenharmony_ci struct dma_interleaved_template *xt, 98362306a36Sopenharmony_ci struct data_chunk *chunk) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci struct at_xdmac_desc *desc; 98662306a36Sopenharmony_ci u32 dwidth; 98762306a36Sopenharmony_ci unsigned long flags; 98862306a36Sopenharmony_ci size_t ublen; 98962306a36Sopenharmony_ci /* 99062306a36Sopenharmony_ci * WARNING: The channel configuration is set here since there is no 99162306a36Sopenharmony_ci * dmaengine_slave_config call in this case. Moreover we don't know the 99262306a36Sopenharmony_ci * direction, it involves we can't dynamically set the source and dest 99362306a36Sopenharmony_ci * interface so we have to use the same one. Only interface 0 allows EBI 99462306a36Sopenharmony_ci * access. Hopefully we can access DDR through both ports (at least on 99562306a36Sopenharmony_ci * SAMA5D4x), so we can use the same interface for source and dest, 99662306a36Sopenharmony_ci * that solves the fact we don't know the direction. 99762306a36Sopenharmony_ci * ERRATA: Even if useless for memory transfers, the PERID has to not 99862306a36Sopenharmony_ci * match the one of another channel. If not, it could lead to spurious 99962306a36Sopenharmony_ci * flag status. 100062306a36Sopenharmony_ci * For SAMA7G5x case, the SIF and DIF fields are no longer used. 100162306a36Sopenharmony_ci * Thus, no need to have the SIF/DIF interfaces here. 100262306a36Sopenharmony_ci * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as 100362306a36Sopenharmony_ci * zero. 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_ci u32 chan_cc = AT_XDMAC_CC_PERID(0x7f) 100662306a36Sopenharmony_ci | AT_XDMAC_CC_MBSIZE_SIXTEEN 100762306a36Sopenharmony_ci | AT_XDMAC_CC_TYPE_MEM_TRAN; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci dwidth = at_xdmac_align_width(chan, src | dst | chunk->size); 101062306a36Sopenharmony_ci if (chunk->size >= (AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth)) { 101162306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 101262306a36Sopenharmony_ci "%s: chunk too big (%zu, max size %lu)...\n", 101362306a36Sopenharmony_ci __func__, chunk->size, 101462306a36Sopenharmony_ci AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth); 101562306a36Sopenharmony_ci return NULL; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (prev) 101962306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 102062306a36Sopenharmony_ci "Adding items at the end of desc 0x%p\n", prev); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (xt->src_inc) { 102362306a36Sopenharmony_ci if (xt->src_sgl) 102462306a36Sopenharmony_ci chan_cc |= AT_XDMAC_CC_SAM_UBS_AM; 102562306a36Sopenharmony_ci else 102662306a36Sopenharmony_ci chan_cc |= AT_XDMAC_CC_SAM_INCREMENTED_AM; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (xt->dst_inc) { 103062306a36Sopenharmony_ci if (xt->dst_sgl) 103162306a36Sopenharmony_ci chan_cc |= AT_XDMAC_CC_DAM_UBS_AM; 103262306a36Sopenharmony_ci else 103362306a36Sopenharmony_ci chan_cc |= AT_XDMAC_CC_DAM_INCREMENTED_AM; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 103762306a36Sopenharmony_ci desc = at_xdmac_get_desc(atchan); 103862306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 103962306a36Sopenharmony_ci if (!desc) { 104062306a36Sopenharmony_ci dev_err(chan2dev(chan), "can't get descriptor\n"); 104162306a36Sopenharmony_ci return NULL; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci ublen = chunk->size >> dwidth; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci desc->lld.mbr_sa = src; 104962306a36Sopenharmony_ci desc->lld.mbr_da = dst; 105062306a36Sopenharmony_ci desc->lld.mbr_sus = dmaengine_get_src_icg(xt, chunk); 105162306a36Sopenharmony_ci desc->lld.mbr_dus = dmaengine_get_dst_icg(xt, chunk); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV3 105462306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NDEN 105562306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NSEN 105662306a36Sopenharmony_ci | ublen; 105762306a36Sopenharmony_ci desc->lld.mbr_cfg = chan_cc; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 106062306a36Sopenharmony_ci "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n", 106162306a36Sopenharmony_ci __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, 106262306a36Sopenharmony_ci desc->lld.mbr_ubc, desc->lld.mbr_cfg); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci /* Chain lld. */ 106562306a36Sopenharmony_ci if (prev) 106662306a36Sopenharmony_ci at_xdmac_queue_desc(chan, prev, desc); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci return desc; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 107262306a36Sopenharmony_ciat_xdmac_prep_interleaved(struct dma_chan *chan, 107362306a36Sopenharmony_ci struct dma_interleaved_template *xt, 107462306a36Sopenharmony_ci unsigned long flags) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 107762306a36Sopenharmony_ci struct at_xdmac_desc *prev = NULL, *first = NULL; 107862306a36Sopenharmony_ci dma_addr_t dst_addr, src_addr; 107962306a36Sopenharmony_ci size_t src_skip = 0, dst_skip = 0, len = 0; 108062306a36Sopenharmony_ci struct data_chunk *chunk; 108162306a36Sopenharmony_ci int i; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (!xt || !xt->numf || (xt->dir != DMA_MEM_TO_MEM)) 108462306a36Sopenharmony_ci return NULL; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* 108762306a36Sopenharmony_ci * TODO: Handle the case where we have to repeat a chain of 108862306a36Sopenharmony_ci * descriptors... 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ci if ((xt->numf > 1) && (xt->frame_size > 1)) 109162306a36Sopenharmony_ci return NULL; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: src=%pad, dest=%pad, numf=%zu, frame_size=%zu, flags=0x%lx\n", 109462306a36Sopenharmony_ci __func__, &xt->src_start, &xt->dst_start, xt->numf, 109562306a36Sopenharmony_ci xt->frame_size, flags); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci src_addr = xt->src_start; 109862306a36Sopenharmony_ci dst_addr = xt->dst_start; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (xt->numf > 1) { 110162306a36Sopenharmony_ci first = at_xdmac_interleaved_queue_desc(chan, atchan, 110262306a36Sopenharmony_ci NULL, 110362306a36Sopenharmony_ci src_addr, dst_addr, 110462306a36Sopenharmony_ci xt, xt->sgl); 110562306a36Sopenharmony_ci if (!first) 110662306a36Sopenharmony_ci return NULL; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci /* Length of the block is (BLEN+1) microblocks. */ 110962306a36Sopenharmony_ci for (i = 0; i < xt->numf - 1; i++) 111062306a36Sopenharmony_ci at_xdmac_increment_block_count(chan, first); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n", 111362306a36Sopenharmony_ci __func__, first, first); 111462306a36Sopenharmony_ci list_add_tail(&first->desc_node, &first->descs_list); 111562306a36Sopenharmony_ci } else { 111662306a36Sopenharmony_ci for (i = 0; i < xt->frame_size; i++) { 111762306a36Sopenharmony_ci size_t src_icg = 0, dst_icg = 0; 111862306a36Sopenharmony_ci struct at_xdmac_desc *desc; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci chunk = xt->sgl + i; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci dst_icg = dmaengine_get_dst_icg(xt, chunk); 112362306a36Sopenharmony_ci src_icg = dmaengine_get_src_icg(xt, chunk); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci src_skip = chunk->size + src_icg; 112662306a36Sopenharmony_ci dst_skip = chunk->size + dst_icg; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 112962306a36Sopenharmony_ci "%s: chunk size=%zu, src icg=%zu, dst icg=%zu\n", 113062306a36Sopenharmony_ci __func__, chunk->size, src_icg, dst_icg); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci desc = at_xdmac_interleaved_queue_desc(chan, atchan, 113362306a36Sopenharmony_ci prev, 113462306a36Sopenharmony_ci src_addr, dst_addr, 113562306a36Sopenharmony_ci xt, chunk); 113662306a36Sopenharmony_ci if (!desc) { 113762306a36Sopenharmony_ci if (first) 113862306a36Sopenharmony_ci list_splice_tail_init(&first->descs_list, 113962306a36Sopenharmony_ci &atchan->free_descs_list); 114062306a36Sopenharmony_ci return NULL; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (!first) 114462306a36Sopenharmony_ci first = desc; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n", 114762306a36Sopenharmony_ci __func__, desc, first); 114862306a36Sopenharmony_ci list_add_tail(&desc->desc_node, &first->descs_list); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (xt->src_sgl) 115162306a36Sopenharmony_ci src_addr += src_skip; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (xt->dst_sgl) 115462306a36Sopenharmony_ci dst_addr += dst_skip; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci len += chunk->size; 115762306a36Sopenharmony_ci prev = desc; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci first->tx_dma_desc.cookie = -EBUSY; 116262306a36Sopenharmony_ci first->tx_dma_desc.flags = flags; 116362306a36Sopenharmony_ci first->xfer_size = len; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci return &first->tx_dma_desc; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 116962306a36Sopenharmony_ciat_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, 117062306a36Sopenharmony_ci size_t len, unsigned long flags) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 117362306a36Sopenharmony_ci struct at_xdmac_desc *first = NULL, *prev = NULL; 117462306a36Sopenharmony_ci size_t remaining_size = len, xfer_size = 0, ublen; 117562306a36Sopenharmony_ci dma_addr_t src_addr = src, dst_addr = dest; 117662306a36Sopenharmony_ci u32 dwidth; 117762306a36Sopenharmony_ci /* 117862306a36Sopenharmony_ci * WARNING: We don't know the direction, it involves we can't 117962306a36Sopenharmony_ci * dynamically set the source and dest interface so we have to use the 118062306a36Sopenharmony_ci * same one. Only interface 0 allows EBI access. Hopefully we can 118162306a36Sopenharmony_ci * access DDR through both ports (at least on SAMA5D4x), so we can use 118262306a36Sopenharmony_ci * the same interface for source and dest, that solves the fact we 118362306a36Sopenharmony_ci * don't know the direction. 118462306a36Sopenharmony_ci * ERRATA: Even if useless for memory transfers, the PERID has to not 118562306a36Sopenharmony_ci * match the one of another channel. If not, it could lead to spurious 118662306a36Sopenharmony_ci * flag status. 118762306a36Sopenharmony_ci * For SAMA7G5x case, the SIF and DIF fields are no longer used. 118862306a36Sopenharmony_ci * Thus, no need to have the SIF/DIF interfaces here. 118962306a36Sopenharmony_ci * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as 119062306a36Sopenharmony_ci * zero. 119162306a36Sopenharmony_ci */ 119262306a36Sopenharmony_ci u32 chan_cc = AT_XDMAC_CC_PERID(0x7f) 119362306a36Sopenharmony_ci | AT_XDMAC_CC_DAM_INCREMENTED_AM 119462306a36Sopenharmony_ci | AT_XDMAC_CC_SAM_INCREMENTED_AM 119562306a36Sopenharmony_ci | AT_XDMAC_CC_MBSIZE_SIXTEEN 119662306a36Sopenharmony_ci | AT_XDMAC_CC_TYPE_MEM_TRAN; 119762306a36Sopenharmony_ci unsigned long irqflags; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: src=%pad, dest=%pad, len=%zd, flags=0x%lx\n", 120062306a36Sopenharmony_ci __func__, &src, &dest, len, flags); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (unlikely(!len)) 120362306a36Sopenharmony_ci return NULL; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci dwidth = at_xdmac_align_width(chan, src_addr | dst_addr); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* Prepare descriptors. */ 120862306a36Sopenharmony_ci while (remaining_size) { 120962306a36Sopenharmony_ci struct at_xdmac_desc *desc = NULL; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: remaining_size=%zu\n", __func__, remaining_size); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, irqflags); 121462306a36Sopenharmony_ci desc = at_xdmac_get_desc(atchan); 121562306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, irqflags); 121662306a36Sopenharmony_ci if (!desc) { 121762306a36Sopenharmony_ci dev_err(chan2dev(chan), "can't get descriptor\n"); 121862306a36Sopenharmony_ci if (first) 121962306a36Sopenharmony_ci list_splice_tail_init(&first->descs_list, 122062306a36Sopenharmony_ci &atchan->free_descs_list); 122162306a36Sopenharmony_ci return NULL; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* Update src and dest addresses. */ 122562306a36Sopenharmony_ci src_addr += xfer_size; 122662306a36Sopenharmony_ci dst_addr += xfer_size; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (remaining_size >= AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth) 122962306a36Sopenharmony_ci xfer_size = AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth; 123062306a36Sopenharmony_ci else 123162306a36Sopenharmony_ci xfer_size = remaining_size; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: xfer_size=%zu\n", __func__, xfer_size); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci /* Check remaining length and change data width if needed. */ 123662306a36Sopenharmony_ci dwidth = at_xdmac_align_width(chan, 123762306a36Sopenharmony_ci src_addr | dst_addr | xfer_size); 123862306a36Sopenharmony_ci chan_cc &= ~AT_XDMAC_CC_DWIDTH_MASK; 123962306a36Sopenharmony_ci chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci ublen = xfer_size >> dwidth; 124262306a36Sopenharmony_ci remaining_size -= xfer_size; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci desc->lld.mbr_sa = src_addr; 124562306a36Sopenharmony_ci desc->lld.mbr_da = dst_addr; 124662306a36Sopenharmony_ci desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV2 124762306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NDEN 124862306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NSEN 124962306a36Sopenharmony_ci | ublen; 125062306a36Sopenharmony_ci desc->lld.mbr_cfg = chan_cc; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 125362306a36Sopenharmony_ci "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n", 125462306a36Sopenharmony_ci __func__, &desc->lld.mbr_sa, &desc->lld.mbr_da, desc->lld.mbr_ubc, desc->lld.mbr_cfg); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* Chain lld. */ 125762306a36Sopenharmony_ci if (prev) 125862306a36Sopenharmony_ci at_xdmac_queue_desc(chan, prev, desc); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci prev = desc; 126162306a36Sopenharmony_ci if (!first) 126262306a36Sopenharmony_ci first = desc; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n", 126562306a36Sopenharmony_ci __func__, desc, first); 126662306a36Sopenharmony_ci list_add_tail(&desc->desc_node, &first->descs_list); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci first->tx_dma_desc.flags = flags; 127062306a36Sopenharmony_ci first->xfer_size = len; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci return &first->tx_dma_desc; 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan, 127662306a36Sopenharmony_ci struct at_xdmac_chan *atchan, 127762306a36Sopenharmony_ci dma_addr_t dst_addr, 127862306a36Sopenharmony_ci size_t len, 127962306a36Sopenharmony_ci int value) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci struct at_xdmac_desc *desc; 128262306a36Sopenharmony_ci unsigned long flags; 128362306a36Sopenharmony_ci size_t ublen; 128462306a36Sopenharmony_ci u32 dwidth; 128562306a36Sopenharmony_ci char pattern; 128662306a36Sopenharmony_ci /* 128762306a36Sopenharmony_ci * WARNING: The channel configuration is set here since there is no 128862306a36Sopenharmony_ci * dmaengine_slave_config call in this case. Moreover we don't know the 128962306a36Sopenharmony_ci * direction, it involves we can't dynamically set the source and dest 129062306a36Sopenharmony_ci * interface so we have to use the same one. Only interface 0 allows EBI 129162306a36Sopenharmony_ci * access. Hopefully we can access DDR through both ports (at least on 129262306a36Sopenharmony_ci * SAMA5D4x), so we can use the same interface for source and dest, 129362306a36Sopenharmony_ci * that solves the fact we don't know the direction. 129462306a36Sopenharmony_ci * ERRATA: Even if useless for memory transfers, the PERID has to not 129562306a36Sopenharmony_ci * match the one of another channel. If not, it could lead to spurious 129662306a36Sopenharmony_ci * flag status. 129762306a36Sopenharmony_ci * For SAMA7G5x case, the SIF and DIF fields are no longer used. 129862306a36Sopenharmony_ci * Thus, no need to have the SIF/DIF interfaces here. 129962306a36Sopenharmony_ci * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as 130062306a36Sopenharmony_ci * zero. 130162306a36Sopenharmony_ci */ 130262306a36Sopenharmony_ci u32 chan_cc = AT_XDMAC_CC_PERID(0x7f) 130362306a36Sopenharmony_ci | AT_XDMAC_CC_DAM_UBS_AM 130462306a36Sopenharmony_ci | AT_XDMAC_CC_SAM_INCREMENTED_AM 130562306a36Sopenharmony_ci | AT_XDMAC_CC_MBSIZE_SIXTEEN 130662306a36Sopenharmony_ci | AT_XDMAC_CC_MEMSET_HW_MODE 130762306a36Sopenharmony_ci | AT_XDMAC_CC_TYPE_MEM_TRAN; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci dwidth = at_xdmac_align_width(chan, dst_addr); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (len >= (AT_XDMAC_MBR_UBC_UBLEN_MAX << dwidth)) { 131262306a36Sopenharmony_ci dev_err(chan2dev(chan), 131362306a36Sopenharmony_ci "%s: Transfer too large, aborting...\n", 131462306a36Sopenharmony_ci __func__); 131562306a36Sopenharmony_ci return NULL; 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 131962306a36Sopenharmony_ci desc = at_xdmac_get_desc(atchan); 132062306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 132162306a36Sopenharmony_ci if (!desc) { 132262306a36Sopenharmony_ci dev_err(chan2dev(chan), "can't get descriptor\n"); 132362306a36Sopenharmony_ci return NULL; 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* Only the first byte of value is to be used according to dmaengine */ 132962306a36Sopenharmony_ci pattern = (char)value; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ublen = len >> dwidth; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci desc->lld.mbr_da = dst_addr; 133462306a36Sopenharmony_ci desc->lld.mbr_ds = (pattern << 24) | 133562306a36Sopenharmony_ci (pattern << 16) | 133662306a36Sopenharmony_ci (pattern << 8) | 133762306a36Sopenharmony_ci pattern; 133862306a36Sopenharmony_ci desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV3 133962306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NDEN 134062306a36Sopenharmony_ci | AT_XDMAC_MBR_UBC_NSEN 134162306a36Sopenharmony_ci | ublen; 134262306a36Sopenharmony_ci desc->lld.mbr_cfg = chan_cc; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 134562306a36Sopenharmony_ci "%s: lld: mbr_da=%pad, mbr_ds=0x%08x, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n", 134662306a36Sopenharmony_ci __func__, &desc->lld.mbr_da, desc->lld.mbr_ds, desc->lld.mbr_ubc, 134762306a36Sopenharmony_ci desc->lld.mbr_cfg); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return desc; 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 135362306a36Sopenharmony_ciat_xdmac_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value, 135462306a36Sopenharmony_ci size_t len, unsigned long flags) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 135762306a36Sopenharmony_ci struct at_xdmac_desc *desc; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: dest=%pad, len=%zu, pattern=0x%x, flags=0x%lx\n", 136062306a36Sopenharmony_ci __func__, &dest, len, value, flags); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (unlikely(!len)) 136362306a36Sopenharmony_ci return NULL; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci desc = at_xdmac_memset_create_desc(chan, atchan, dest, len, value); 136662306a36Sopenharmony_ci list_add_tail(&desc->desc_node, &desc->descs_list); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci desc->tx_dma_desc.cookie = -EBUSY; 136962306a36Sopenharmony_ci desc->tx_dma_desc.flags = flags; 137062306a36Sopenharmony_ci desc->xfer_size = len; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci return &desc->tx_dma_desc; 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 137662306a36Sopenharmony_ciat_xdmac_prep_dma_memset_sg(struct dma_chan *chan, struct scatterlist *sgl, 137762306a36Sopenharmony_ci unsigned int sg_len, int value, 137862306a36Sopenharmony_ci unsigned long flags) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 138162306a36Sopenharmony_ci struct at_xdmac_desc *desc, *pdesc = NULL, 138262306a36Sopenharmony_ci *ppdesc = NULL, *first = NULL; 138362306a36Sopenharmony_ci struct scatterlist *sg, *psg = NULL, *ppsg = NULL; 138462306a36Sopenharmony_ci size_t stride = 0, pstride = 0, len = 0; 138562306a36Sopenharmony_ci int i; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci if (!sgl) 138862306a36Sopenharmony_ci return NULL; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: sg_len=%d, value=0x%x, flags=0x%lx\n", 139162306a36Sopenharmony_ci __func__, sg_len, value, flags); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci /* Prepare descriptors. */ 139462306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 139562306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: dest=%pad, len=%d, pattern=0x%x, flags=0x%lx\n", 139662306a36Sopenharmony_ci __func__, &sg_dma_address(sg), sg_dma_len(sg), 139762306a36Sopenharmony_ci value, flags); 139862306a36Sopenharmony_ci desc = at_xdmac_memset_create_desc(chan, atchan, 139962306a36Sopenharmony_ci sg_dma_address(sg), 140062306a36Sopenharmony_ci sg_dma_len(sg), 140162306a36Sopenharmony_ci value); 140262306a36Sopenharmony_ci if (!desc && first) 140362306a36Sopenharmony_ci list_splice_tail_init(&first->descs_list, 140462306a36Sopenharmony_ci &atchan->free_descs_list); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (!first) 140762306a36Sopenharmony_ci first = desc; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci /* Update our strides */ 141062306a36Sopenharmony_ci pstride = stride; 141162306a36Sopenharmony_ci if (psg) 141262306a36Sopenharmony_ci stride = sg_dma_address(sg) - 141362306a36Sopenharmony_ci (sg_dma_address(psg) + sg_dma_len(psg)); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* 141662306a36Sopenharmony_ci * The scatterlist API gives us only the address and 141762306a36Sopenharmony_ci * length of each elements. 141862306a36Sopenharmony_ci * 141962306a36Sopenharmony_ci * Unfortunately, we don't have the stride, which we 142062306a36Sopenharmony_ci * will need to compute. 142162306a36Sopenharmony_ci * 142262306a36Sopenharmony_ci * That make us end up in a situation like this one: 142362306a36Sopenharmony_ci * len stride len stride len 142462306a36Sopenharmony_ci * +-------+ +-------+ +-------+ 142562306a36Sopenharmony_ci * | N-2 | | N-1 | | N | 142662306a36Sopenharmony_ci * +-------+ +-------+ +-------+ 142762306a36Sopenharmony_ci * 142862306a36Sopenharmony_ci * We need all these three elements (N-2, N-1 and N) 142962306a36Sopenharmony_ci * to actually take the decision on whether we need to 143062306a36Sopenharmony_ci * queue N-1 or reuse N-2. 143162306a36Sopenharmony_ci * 143262306a36Sopenharmony_ci * We will only consider N if it is the last element. 143362306a36Sopenharmony_ci */ 143462306a36Sopenharmony_ci if (ppdesc && pdesc) { 143562306a36Sopenharmony_ci if ((stride == pstride) && 143662306a36Sopenharmony_ci (sg_dma_len(ppsg) == sg_dma_len(psg))) { 143762306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 143862306a36Sopenharmony_ci "%s: desc 0x%p can be merged with desc 0x%p\n", 143962306a36Sopenharmony_ci __func__, pdesc, ppdesc); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* 144262306a36Sopenharmony_ci * Increment the block count of the 144362306a36Sopenharmony_ci * N-2 descriptor 144462306a36Sopenharmony_ci */ 144562306a36Sopenharmony_ci at_xdmac_increment_block_count(chan, ppdesc); 144662306a36Sopenharmony_ci ppdesc->lld.mbr_dus = stride; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci /* 144962306a36Sopenharmony_ci * Put back the N-1 descriptor in the 145062306a36Sopenharmony_ci * free descriptor list 145162306a36Sopenharmony_ci */ 145262306a36Sopenharmony_ci list_add_tail(&pdesc->desc_node, 145362306a36Sopenharmony_ci &atchan->free_descs_list); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* 145662306a36Sopenharmony_ci * Make our N-1 descriptor pointer 145762306a36Sopenharmony_ci * point to the N-2 since they were 145862306a36Sopenharmony_ci * actually merged. 145962306a36Sopenharmony_ci */ 146062306a36Sopenharmony_ci pdesc = ppdesc; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci /* 146362306a36Sopenharmony_ci * Rule out the case where we don't have 146462306a36Sopenharmony_ci * pstride computed yet (our second sg 146562306a36Sopenharmony_ci * element) 146662306a36Sopenharmony_ci * 146762306a36Sopenharmony_ci * We also want to catch the case where there 146862306a36Sopenharmony_ci * would be a negative stride, 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_ci } else if (pstride || 147162306a36Sopenharmony_ci sg_dma_address(sg) < sg_dma_address(psg)) { 147262306a36Sopenharmony_ci /* 147362306a36Sopenharmony_ci * Queue the N-1 descriptor after the 147462306a36Sopenharmony_ci * N-2 147562306a36Sopenharmony_ci */ 147662306a36Sopenharmony_ci at_xdmac_queue_desc(chan, ppdesc, pdesc); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci /* 147962306a36Sopenharmony_ci * Add the N-1 descriptor to the list 148062306a36Sopenharmony_ci * of the descriptors used for this 148162306a36Sopenharmony_ci * transfer 148262306a36Sopenharmony_ci */ 148362306a36Sopenharmony_ci list_add_tail(&desc->desc_node, 148462306a36Sopenharmony_ci &first->descs_list); 148562306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 148662306a36Sopenharmony_ci "%s: add desc 0x%p to descs_list 0x%p\n", 148762306a36Sopenharmony_ci __func__, desc, first); 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci /* 149262306a36Sopenharmony_ci * If we are the last element, just see if we have the 149362306a36Sopenharmony_ci * same size than the previous element. 149462306a36Sopenharmony_ci * 149562306a36Sopenharmony_ci * If so, we can merge it with the previous descriptor 149662306a36Sopenharmony_ci * since we don't care about the stride anymore. 149762306a36Sopenharmony_ci */ 149862306a36Sopenharmony_ci if ((i == (sg_len - 1)) && 149962306a36Sopenharmony_ci sg_dma_len(psg) == sg_dma_len(sg)) { 150062306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 150162306a36Sopenharmony_ci "%s: desc 0x%p can be merged with desc 0x%p\n", 150262306a36Sopenharmony_ci __func__, desc, pdesc); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci /* 150562306a36Sopenharmony_ci * Increment the block count of the N-1 150662306a36Sopenharmony_ci * descriptor 150762306a36Sopenharmony_ci */ 150862306a36Sopenharmony_ci at_xdmac_increment_block_count(chan, pdesc); 150962306a36Sopenharmony_ci pdesc->lld.mbr_dus = stride; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* 151262306a36Sopenharmony_ci * Put back the N descriptor in the free 151362306a36Sopenharmony_ci * descriptor list 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_ci list_add_tail(&desc->desc_node, 151662306a36Sopenharmony_ci &atchan->free_descs_list); 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* Update our descriptors */ 152062306a36Sopenharmony_ci ppdesc = pdesc; 152162306a36Sopenharmony_ci pdesc = desc; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci /* Update our scatter pointers */ 152462306a36Sopenharmony_ci ppsg = psg; 152562306a36Sopenharmony_ci psg = sg; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci len += sg_dma_len(sg); 152862306a36Sopenharmony_ci } 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci first->tx_dma_desc.cookie = -EBUSY; 153162306a36Sopenharmony_ci first->tx_dma_desc.flags = flags; 153262306a36Sopenharmony_ci first->xfer_size = len; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci return &first->tx_dma_desc; 153562306a36Sopenharmony_ci} 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_cistatic enum dma_status 153862306a36Sopenharmony_ciat_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, 153962306a36Sopenharmony_ci struct dma_tx_state *txstate) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 154262306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 154362306a36Sopenharmony_ci struct at_xdmac_desc *desc, *_desc, *iter; 154462306a36Sopenharmony_ci struct list_head *descs_list; 154562306a36Sopenharmony_ci enum dma_status ret; 154662306a36Sopenharmony_ci int residue, retry, pm_status; 154762306a36Sopenharmony_ci u32 cur_nda, check_nda, cur_ubc, mask, value; 154862306a36Sopenharmony_ci u8 dwidth = 0; 154962306a36Sopenharmony_ci unsigned long flags; 155062306a36Sopenharmony_ci bool initd; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 155362306a36Sopenharmony_ci if (ret == DMA_COMPLETE || !txstate) 155462306a36Sopenharmony_ci return ret; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci pm_status = pm_runtime_resume_and_get(atxdmac->dev); 155762306a36Sopenharmony_ci if (pm_status < 0) 155862306a36Sopenharmony_ci return DMA_ERROR; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, xfer_node); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci /* 156562306a36Sopenharmony_ci * If the transfer has not been started yet, don't need to compute the 156662306a36Sopenharmony_ci * residue, it's the transfer length. 156762306a36Sopenharmony_ci */ 156862306a36Sopenharmony_ci if (!desc->active_xfer) { 156962306a36Sopenharmony_ci dma_set_residue(txstate, desc->xfer_size); 157062306a36Sopenharmony_ci goto spin_unlock; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci residue = desc->xfer_size; 157462306a36Sopenharmony_ci /* 157562306a36Sopenharmony_ci * Flush FIFO: only relevant when the transfer is source peripheral 157662306a36Sopenharmony_ci * synchronized. Flush is needed before reading CUBC because data in 157762306a36Sopenharmony_ci * the FIFO are not reported by CUBC. Reporting a residue of the 157862306a36Sopenharmony_ci * transfer length while we have data in FIFO can cause issue. 157962306a36Sopenharmony_ci * Usecase: atmel USART has a timeout which means I have received 158062306a36Sopenharmony_ci * characters but there is no more character received for a while. On 158162306a36Sopenharmony_ci * timeout, it requests the residue. If the data are in the DMA FIFO, 158262306a36Sopenharmony_ci * we will return a residue of the transfer length. It means no data 158362306a36Sopenharmony_ci * received. If an application is waiting for these data, it will hang 158462306a36Sopenharmony_ci * since we won't have another USART timeout without receiving new 158562306a36Sopenharmony_ci * data. 158662306a36Sopenharmony_ci */ 158762306a36Sopenharmony_ci mask = AT_XDMAC_CC_TYPE | AT_XDMAC_CC_DSYNC; 158862306a36Sopenharmony_ci value = AT_XDMAC_CC_TYPE_PER_TRAN | AT_XDMAC_CC_DSYNC_PER2MEM; 158962306a36Sopenharmony_ci if ((desc->lld.mbr_cfg & mask) == value) { 159062306a36Sopenharmony_ci at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask); 159162306a36Sopenharmony_ci while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS)) 159262306a36Sopenharmony_ci cpu_relax(); 159362306a36Sopenharmony_ci } 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci /* 159662306a36Sopenharmony_ci * The easiest way to compute the residue should be to pause the DMA 159762306a36Sopenharmony_ci * but doing this can lead to miss some data as some devices don't 159862306a36Sopenharmony_ci * have FIFO. 159962306a36Sopenharmony_ci * We need to read several registers because: 160062306a36Sopenharmony_ci * - DMA is running therefore a descriptor change is possible while 160162306a36Sopenharmony_ci * reading these registers 160262306a36Sopenharmony_ci * - When the block transfer is done, the value of the CUBC register 160362306a36Sopenharmony_ci * is set to its initial value until the fetch of the next descriptor. 160462306a36Sopenharmony_ci * This value will corrupt the residue calculation so we have to skip 160562306a36Sopenharmony_ci * it. 160662306a36Sopenharmony_ci * 160762306a36Sopenharmony_ci * INITD -------- ------------ 160862306a36Sopenharmony_ci * |____________________| 160962306a36Sopenharmony_ci * _______________________ _______________ 161062306a36Sopenharmony_ci * NDA @desc2 \/ @desc3 161162306a36Sopenharmony_ci * _______________________/\_______________ 161262306a36Sopenharmony_ci * __________ ___________ _______________ 161362306a36Sopenharmony_ci * CUBC 0 \/ MAX desc1 \/ MAX desc2 161462306a36Sopenharmony_ci * __________/\___________/\_______________ 161562306a36Sopenharmony_ci * 161662306a36Sopenharmony_ci * Since descriptors are aligned on 64 bits, we can assume that 161762306a36Sopenharmony_ci * the update of NDA and CUBC is atomic. 161862306a36Sopenharmony_ci * Memory barriers are used to ensure the read order of the registers. 161962306a36Sopenharmony_ci * A max number of retries is set because unlikely it could never ends. 162062306a36Sopenharmony_ci */ 162162306a36Sopenharmony_ci for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) { 162262306a36Sopenharmony_ci check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; 162362306a36Sopenharmony_ci rmb(); 162462306a36Sopenharmony_ci cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC); 162562306a36Sopenharmony_ci rmb(); 162662306a36Sopenharmony_ci initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD); 162762306a36Sopenharmony_ci rmb(); 162862306a36Sopenharmony_ci cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; 162962306a36Sopenharmony_ci rmb(); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci if ((check_nda == cur_nda) && initd) 163262306a36Sopenharmony_ci break; 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci if (unlikely(retry >= AT_XDMAC_RESIDUE_MAX_RETRIES)) { 163662306a36Sopenharmony_ci ret = DMA_ERROR; 163762306a36Sopenharmony_ci goto spin_unlock; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci /* 164162306a36Sopenharmony_ci * Flush FIFO: only relevant when the transfer is source peripheral 164262306a36Sopenharmony_ci * synchronized. Another flush is needed here because CUBC is updated 164362306a36Sopenharmony_ci * when the controller sends the data write command. It can lead to 164462306a36Sopenharmony_ci * report data that are not written in the memory or the device. The 164562306a36Sopenharmony_ci * FIFO flush ensures that data are really written. 164662306a36Sopenharmony_ci */ 164762306a36Sopenharmony_ci if ((desc->lld.mbr_cfg & mask) == value) { 164862306a36Sopenharmony_ci at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask); 164962306a36Sopenharmony_ci while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS)) 165062306a36Sopenharmony_ci cpu_relax(); 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci /* 165462306a36Sopenharmony_ci * Remove size of all microblocks already transferred and the current 165562306a36Sopenharmony_ci * one. Then add the remaining size to transfer of the current 165662306a36Sopenharmony_ci * microblock. 165762306a36Sopenharmony_ci */ 165862306a36Sopenharmony_ci descs_list = &desc->descs_list; 165962306a36Sopenharmony_ci list_for_each_entry_safe(iter, _desc, descs_list, desc_node) { 166062306a36Sopenharmony_ci dwidth = at_xdmac_get_dwidth(iter->lld.mbr_cfg); 166162306a36Sopenharmony_ci residue -= (iter->lld.mbr_ubc & 0xffffff) << dwidth; 166262306a36Sopenharmony_ci if ((iter->lld.mbr_nda & 0xfffffffc) == cur_nda) { 166362306a36Sopenharmony_ci desc = iter; 166462306a36Sopenharmony_ci break; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci residue += cur_ubc << dwidth; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci dma_set_residue(txstate, residue); 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci dev_dbg(chan2dev(chan), 167262306a36Sopenharmony_ci "%s: desc=0x%p, tx_dma_desc.phys=%pad, tx_status=%d, cookie=%d, residue=%d\n", 167362306a36Sopenharmony_ci __func__, desc, &desc->tx_dma_desc.phys, ret, cookie, residue); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_cispin_unlock: 167662306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 167762306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 167862306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 167962306a36Sopenharmony_ci return ret; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_cistatic void at_xdmac_advance_work(struct at_xdmac_chan *atchan) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct at_xdmac_desc *desc; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci /* 168762306a36Sopenharmony_ci * If channel is enabled, do nothing, advance_work will be triggered 168862306a36Sopenharmony_ci * after the interruption. 168962306a36Sopenharmony_ci */ 169062306a36Sopenharmony_ci if (at_xdmac_chan_is_enabled(atchan) || list_empty(&atchan->xfers_list)) 169162306a36Sopenharmony_ci return; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, 169462306a36Sopenharmony_ci xfer_node); 169562306a36Sopenharmony_ci dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc); 169662306a36Sopenharmony_ci if (!desc->active_xfer) 169762306a36Sopenharmony_ci at_xdmac_start_xfer(atchan, desc); 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_cistatic void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci struct at_xdmac_desc *desc; 170362306a36Sopenharmony_ci struct dma_async_tx_descriptor *txd; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci spin_lock_irq(&atchan->lock); 170662306a36Sopenharmony_ci dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n", 170762306a36Sopenharmony_ci __func__, atchan->irq_status); 170862306a36Sopenharmony_ci if (list_empty(&atchan->xfers_list)) { 170962306a36Sopenharmony_ci spin_unlock_irq(&atchan->lock); 171062306a36Sopenharmony_ci return; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, 171362306a36Sopenharmony_ci xfer_node); 171462306a36Sopenharmony_ci spin_unlock_irq(&atchan->lock); 171562306a36Sopenharmony_ci txd = &desc->tx_dma_desc; 171662306a36Sopenharmony_ci if (txd->flags & DMA_PREP_INTERRUPT) 171762306a36Sopenharmony_ci dmaengine_desc_get_callback_invoke(txd, NULL); 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci/* Called with atchan->lock held. */ 172162306a36Sopenharmony_cistatic void at_xdmac_handle_error(struct at_xdmac_chan *atchan) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 172462306a36Sopenharmony_ci struct at_xdmac_desc *bad_desc; 172562306a36Sopenharmony_ci int ret; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 172862306a36Sopenharmony_ci if (ret < 0) 172962306a36Sopenharmony_ci return; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci /* 173262306a36Sopenharmony_ci * The descriptor currently at the head of the active list is 173362306a36Sopenharmony_ci * broken. Since we don't have any way to report errors, we'll 173462306a36Sopenharmony_ci * just have to scream loudly and try to continue with other 173562306a36Sopenharmony_ci * descriptors queued (if any). 173662306a36Sopenharmony_ci */ 173762306a36Sopenharmony_ci if (atchan->irq_status & AT_XDMAC_CIS_RBEIS) 173862306a36Sopenharmony_ci dev_err(chan2dev(&atchan->chan), "read bus error!!!"); 173962306a36Sopenharmony_ci if (atchan->irq_status & AT_XDMAC_CIS_WBEIS) 174062306a36Sopenharmony_ci dev_err(chan2dev(&atchan->chan), "write bus error!!!"); 174162306a36Sopenharmony_ci if (atchan->irq_status & AT_XDMAC_CIS_ROIS) 174262306a36Sopenharmony_ci dev_err(chan2dev(&atchan->chan), "request overflow error!!!"); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci /* Channel must be disabled first as it's not done automatically */ 174562306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); 174662306a36Sopenharmony_ci while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask) 174762306a36Sopenharmony_ci cpu_relax(); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci bad_desc = list_first_entry(&atchan->xfers_list, 175062306a36Sopenharmony_ci struct at_xdmac_desc, 175162306a36Sopenharmony_ci xfer_node); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci /* Print bad descriptor's details if needed */ 175462306a36Sopenharmony_ci dev_dbg(chan2dev(&atchan->chan), 175562306a36Sopenharmony_ci "%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n", 175662306a36Sopenharmony_ci __func__, &bad_desc->lld.mbr_sa, &bad_desc->lld.mbr_da, 175762306a36Sopenharmony_ci bad_desc->lld.mbr_ubc); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 176062306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci /* Then continue with usual descriptor management */ 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cistatic void at_xdmac_tasklet(struct tasklet_struct *t) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci struct at_xdmac_chan *atchan = from_tasklet(atchan, t, tasklet); 176862306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 176962306a36Sopenharmony_ci struct at_xdmac_desc *desc; 177062306a36Sopenharmony_ci struct dma_async_tx_descriptor *txd; 177162306a36Sopenharmony_ci u32 error_mask; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (at_xdmac_chan_is_cyclic(atchan)) 177462306a36Sopenharmony_ci return at_xdmac_handle_cyclic(atchan); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci error_mask = AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS | 177762306a36Sopenharmony_ci AT_XDMAC_CIS_ROIS; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci spin_lock_irq(&atchan->lock); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci dev_dbg(chan2dev(&atchan->chan), "%s: status=0x%08x\n", 178262306a36Sopenharmony_ci __func__, atchan->irq_status); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci if (!(atchan->irq_status & AT_XDMAC_CIS_LIS) && 178562306a36Sopenharmony_ci !(atchan->irq_status & error_mask)) { 178662306a36Sopenharmony_ci spin_unlock_irq(&atchan->lock); 178762306a36Sopenharmony_ci return; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (atchan->irq_status & error_mask) 179162306a36Sopenharmony_ci at_xdmac_handle_error(atchan); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, 179462306a36Sopenharmony_ci xfer_node); 179562306a36Sopenharmony_ci dev_vdbg(chan2dev(&atchan->chan), "%s: desc 0x%p\n", __func__, desc); 179662306a36Sopenharmony_ci if (!desc->active_xfer) { 179762306a36Sopenharmony_ci dev_err(chan2dev(&atchan->chan), "Xfer not active: exiting"); 179862306a36Sopenharmony_ci spin_unlock_irq(&atchan->lock); 179962306a36Sopenharmony_ci return; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci txd = &desc->tx_dma_desc; 180362306a36Sopenharmony_ci dma_cookie_complete(txd); 180462306a36Sopenharmony_ci /* Remove the transfer from the transfer list. */ 180562306a36Sopenharmony_ci list_del(&desc->xfer_node); 180662306a36Sopenharmony_ci spin_unlock_irq(&atchan->lock); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci if (txd->flags & DMA_PREP_INTERRUPT) 180962306a36Sopenharmony_ci dmaengine_desc_get_callback_invoke(txd, NULL); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci dma_run_dependencies(txd); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci spin_lock_irq(&atchan->lock); 181462306a36Sopenharmony_ci /* Move the xfer descriptors into the free descriptors list. */ 181562306a36Sopenharmony_ci list_splice_tail_init(&desc->descs_list, &atchan->free_descs_list); 181662306a36Sopenharmony_ci at_xdmac_advance_work(atchan); 181762306a36Sopenharmony_ci spin_unlock_irq(&atchan->lock); 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci /* 182062306a36Sopenharmony_ci * Decrement runtime PM ref counter incremented in 182162306a36Sopenharmony_ci * at_xdmac_start_xfer(). 182262306a36Sopenharmony_ci */ 182362306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 182462306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_cistatic irqreturn_t at_xdmac_interrupt(int irq, void *dev_id) 182862306a36Sopenharmony_ci{ 182962306a36Sopenharmony_ci struct at_xdmac *atxdmac = (struct at_xdmac *)dev_id; 183062306a36Sopenharmony_ci struct at_xdmac_chan *atchan; 183162306a36Sopenharmony_ci u32 imr, status, pending; 183262306a36Sopenharmony_ci u32 chan_imr, chan_status; 183362306a36Sopenharmony_ci int i, ret = IRQ_NONE; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci do { 183662306a36Sopenharmony_ci imr = at_xdmac_read(atxdmac, AT_XDMAC_GIM); 183762306a36Sopenharmony_ci status = at_xdmac_read(atxdmac, AT_XDMAC_GIS); 183862306a36Sopenharmony_ci pending = status & imr; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci dev_vdbg(atxdmac->dma.dev, 184162306a36Sopenharmony_ci "%s: status=0x%08x, imr=0x%08x, pending=0x%08x\n", 184262306a36Sopenharmony_ci __func__, status, imr, pending); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci if (!pending) 184562306a36Sopenharmony_ci break; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci /* We have to find which channel has generated the interrupt. */ 184862306a36Sopenharmony_ci for (i = 0; i < atxdmac->dma.chancnt; i++) { 184962306a36Sopenharmony_ci if (!((1 << i) & pending)) 185062306a36Sopenharmony_ci continue; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci atchan = &atxdmac->chan[i]; 185362306a36Sopenharmony_ci chan_imr = at_xdmac_chan_read(atchan, AT_XDMAC_CIM); 185462306a36Sopenharmony_ci chan_status = at_xdmac_chan_read(atchan, AT_XDMAC_CIS); 185562306a36Sopenharmony_ci atchan->irq_status = chan_status & chan_imr; 185662306a36Sopenharmony_ci dev_vdbg(atxdmac->dma.dev, 185762306a36Sopenharmony_ci "%s: chan%d: imr=0x%x, status=0x%x\n", 185862306a36Sopenharmony_ci __func__, i, chan_imr, chan_status); 185962306a36Sopenharmony_ci dev_vdbg(chan2dev(&atchan->chan), 186062306a36Sopenharmony_ci "%s: CC=0x%08x CNDA=0x%08x, CNDC=0x%08x, CSA=0x%08x, CDA=0x%08x, CUBC=0x%08x\n", 186162306a36Sopenharmony_ci __func__, 186262306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CC), 186362306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CNDA), 186462306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CNDC), 186562306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CSA), 186662306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CDA), 186762306a36Sopenharmony_ci at_xdmac_chan_read(atchan, AT_XDMAC_CUBC)); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (atchan->irq_status & (AT_XDMAC_CIS_RBEIS | AT_XDMAC_CIS_WBEIS)) 187062306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci tasklet_schedule(&atchan->tasklet); 187362306a36Sopenharmony_ci ret = IRQ_HANDLED; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci } while (pending); 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci return ret; 187962306a36Sopenharmony_ci} 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_cistatic void at_xdmac_issue_pending(struct dma_chan *chan) 188262306a36Sopenharmony_ci{ 188362306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 188462306a36Sopenharmony_ci unsigned long flags; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci dev_dbg(chan2dev(&atchan->chan), "%s\n", __func__); 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 188962306a36Sopenharmony_ci at_xdmac_advance_work(atchan); 189062306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci return; 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_cistatic int at_xdmac_device_config(struct dma_chan *chan, 189662306a36Sopenharmony_ci struct dma_slave_config *config) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 189962306a36Sopenharmony_ci int ret; 190062306a36Sopenharmony_ci unsigned long flags; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s\n", __func__); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 190562306a36Sopenharmony_ci ret = at_xdmac_set_slave_config(chan, config); 190662306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci return ret; 190962306a36Sopenharmony_ci} 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_cistatic void at_xdmac_device_pause_set(struct at_xdmac *atxdmac, 191262306a36Sopenharmony_ci struct at_xdmac_chan *atchan) 191362306a36Sopenharmony_ci{ 191462306a36Sopenharmony_ci at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask); 191562306a36Sopenharmony_ci while (at_xdmac_chan_read(atchan, AT_XDMAC_CC) & 191662306a36Sopenharmony_ci (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP)) 191762306a36Sopenharmony_ci cpu_relax(); 191862306a36Sopenharmony_ci} 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cistatic void at_xdmac_device_pause_internal(struct at_xdmac_chan *atchan) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 192362306a36Sopenharmony_ci unsigned long flags; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 192662306a36Sopenharmony_ci set_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status); 192762306a36Sopenharmony_ci at_xdmac_device_pause_set(atxdmac, atchan); 192862306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_cistatic int at_xdmac_device_pause(struct dma_chan *chan) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 193462306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 193562306a36Sopenharmony_ci unsigned long flags; 193662306a36Sopenharmony_ci int ret; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s\n", __func__); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci if (test_and_set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status)) 194162306a36Sopenharmony_ci return 0; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 194462306a36Sopenharmony_ci if (ret < 0) 194562306a36Sopenharmony_ci return ret; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci at_xdmac_device_pause_set(atxdmac, atchan); 195062306a36Sopenharmony_ci /* Decrement runtime PM ref counter for each active descriptor. */ 195162306a36Sopenharmony_ci at_xdmac_runtime_suspend_descriptors(atchan); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 195662306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci return 0; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic void at_xdmac_device_resume_internal(struct at_xdmac_chan *atchan) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 196462306a36Sopenharmony_ci unsigned long flags; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 196762306a36Sopenharmony_ci at_xdmac_write(atxdmac, atxdmac->layout->grwr, atchan->mask); 196862306a36Sopenharmony_ci clear_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status); 196962306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 197062306a36Sopenharmony_ci} 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_cistatic int at_xdmac_device_resume(struct dma_chan *chan) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 197562306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 197662306a36Sopenharmony_ci unsigned long flags; 197762306a36Sopenharmony_ci int ret; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s\n", __func__); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 198262306a36Sopenharmony_ci if (ret < 0) 198362306a36Sopenharmony_ci return ret; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 198662306a36Sopenharmony_ci if (!at_xdmac_chan_is_paused(atchan)) 198762306a36Sopenharmony_ci goto unlock; 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci /* Increment runtime PM ref counter for each active descriptor. */ 199062306a36Sopenharmony_ci ret = at_xdmac_runtime_resume_descriptors(atchan); 199162306a36Sopenharmony_ci if (ret < 0) 199262306a36Sopenharmony_ci goto unlock; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci at_xdmac_write(atxdmac, atxdmac->layout->grwr, atchan->mask); 199562306a36Sopenharmony_ci clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ciunlock: 199862306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 199962306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 200062306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci return ret; 200362306a36Sopenharmony_ci} 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_cistatic int at_xdmac_device_terminate_all(struct dma_chan *chan) 200662306a36Sopenharmony_ci{ 200762306a36Sopenharmony_ci struct at_xdmac_desc *desc, *_desc; 200862306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 200962306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device); 201062306a36Sopenharmony_ci unsigned long flags; 201162306a36Sopenharmony_ci int ret; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s\n", __func__); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 201662306a36Sopenharmony_ci if (ret < 0) 201762306a36Sopenharmony_ci return ret; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci spin_lock_irqsave(&atchan->lock, flags); 202062306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask); 202162306a36Sopenharmony_ci while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask) 202262306a36Sopenharmony_ci cpu_relax(); 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci /* Cancel all pending transfers. */ 202562306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &atchan->xfers_list, xfer_node) { 202662306a36Sopenharmony_ci list_del(&desc->xfer_node); 202762306a36Sopenharmony_ci list_splice_tail_init(&desc->descs_list, 202862306a36Sopenharmony_ci &atchan->free_descs_list); 202962306a36Sopenharmony_ci /* 203062306a36Sopenharmony_ci * We incremented the runtime PM reference count on 203162306a36Sopenharmony_ci * at_xdmac_start_xfer() for this descriptor. Now it's time 203262306a36Sopenharmony_ci * to release it. 203362306a36Sopenharmony_ci */ 203462306a36Sopenharmony_ci if (desc->active_xfer) { 203562306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 203662306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status); 204162306a36Sopenharmony_ci clear_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status); 204262306a36Sopenharmony_ci spin_unlock_irqrestore(&atchan->lock, flags); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 204562306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci return 0; 204862306a36Sopenharmony_ci} 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_cistatic int at_xdmac_alloc_chan_resources(struct dma_chan *chan) 205162306a36Sopenharmony_ci{ 205262306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 205362306a36Sopenharmony_ci struct at_xdmac_desc *desc; 205462306a36Sopenharmony_ci int i; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci if (at_xdmac_chan_is_enabled(atchan)) { 205762306a36Sopenharmony_ci dev_err(chan2dev(chan), 205862306a36Sopenharmony_ci "can't allocate channel resources (channel enabled)\n"); 205962306a36Sopenharmony_ci return -EIO; 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci if (!list_empty(&atchan->free_descs_list)) { 206362306a36Sopenharmony_ci dev_err(chan2dev(chan), 206462306a36Sopenharmony_ci "can't allocate channel resources (channel not free from a previous use)\n"); 206562306a36Sopenharmony_ci return -EIO; 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci for (i = 0; i < init_nr_desc_per_channel; i++) { 206962306a36Sopenharmony_ci desc = at_xdmac_alloc_desc(chan, GFP_KERNEL); 207062306a36Sopenharmony_ci if (!desc) { 207162306a36Sopenharmony_ci if (i == 0) { 207262306a36Sopenharmony_ci dev_warn(chan2dev(chan), 207362306a36Sopenharmony_ci "can't allocate any descriptors\n"); 207462306a36Sopenharmony_ci return -EIO; 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci dev_warn(chan2dev(chan), 207762306a36Sopenharmony_ci "only %d descriptors have been allocated\n", i); 207862306a36Sopenharmony_ci break; 207962306a36Sopenharmony_ci } 208062306a36Sopenharmony_ci list_add_tail(&desc->desc_node, &atchan->free_descs_list); 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci dma_cookie_init(chan); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci return i; 208862306a36Sopenharmony_ci} 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_cistatic void at_xdmac_free_chan_resources(struct dma_chan *chan) 209162306a36Sopenharmony_ci{ 209262306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 209362306a36Sopenharmony_ci struct at_xdmac *atxdmac = to_at_xdmac(chan->device); 209462306a36Sopenharmony_ci struct at_xdmac_desc *desc, *_desc; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci list_for_each_entry_safe(desc, _desc, &atchan->free_descs_list, desc_node) { 209762306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: freeing descriptor %p\n", __func__, desc); 209862306a36Sopenharmony_ci list_del(&desc->desc_node); 209962306a36Sopenharmony_ci dma_pool_free(atxdmac->at_xdmac_desc_pool, desc, desc->tx_dma_desc.phys); 210062306a36Sopenharmony_ci } 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci return; 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic void at_xdmac_axi_config(struct platform_device *pdev) 210662306a36Sopenharmony_ci{ 210762306a36Sopenharmony_ci struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev); 210862306a36Sopenharmony_ci bool dev_m2m = false; 210962306a36Sopenharmony_ci u32 dma_requests; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci if (!atxdmac->layout->axi_config) 211262306a36Sopenharmony_ci return; /* Not supported */ 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci if (!of_property_read_u32(pdev->dev.of_node, "dma-requests", 211562306a36Sopenharmony_ci &dma_requests)) { 211662306a36Sopenharmony_ci dev_info(&pdev->dev, "controller in mem2mem mode.\n"); 211762306a36Sopenharmony_ci dev_m2m = true; 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci if (dev_m2m) { 212162306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GCFG, AT_XDMAC_GCFG_M2M); 212262306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GWAC, AT_XDMAC_GWAC_M2M); 212362306a36Sopenharmony_ci } else { 212462306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GCFG, AT_XDMAC_GCFG_P2M); 212562306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GWAC, AT_XDMAC_GWAC_P2M); 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci} 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cistatic int __maybe_unused atmel_xdmac_prepare(struct device *dev) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci struct at_xdmac *atxdmac = dev_get_drvdata(dev); 213262306a36Sopenharmony_ci struct dma_chan *chan, *_chan; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) { 213562306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci /* Wait for transfer completion, except in cyclic case. */ 213862306a36Sopenharmony_ci if (at_xdmac_chan_is_enabled(atchan) && !at_xdmac_chan_is_cyclic(atchan)) 213962306a36Sopenharmony_ci return -EAGAIN; 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci return 0; 214262306a36Sopenharmony_ci} 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_cistatic int __maybe_unused atmel_xdmac_suspend(struct device *dev) 214562306a36Sopenharmony_ci{ 214662306a36Sopenharmony_ci struct at_xdmac *atxdmac = dev_get_drvdata(dev); 214762306a36Sopenharmony_ci struct dma_chan *chan, *_chan; 214862306a36Sopenharmony_ci int ret; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(atxdmac->dev); 215162306a36Sopenharmony_ci if (ret < 0) 215262306a36Sopenharmony_ci return ret; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) { 215562306a36Sopenharmony_ci struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci atchan->save_cc = at_xdmac_chan_read(atchan, AT_XDMAC_CC); 215862306a36Sopenharmony_ci if (at_xdmac_chan_is_cyclic(atchan)) { 215962306a36Sopenharmony_ci if (!at_xdmac_chan_is_paused(atchan)) { 216062306a36Sopenharmony_ci dev_warn(chan2dev(chan), "%s: channel %d not paused\n", 216162306a36Sopenharmony_ci __func__, chan->chan_id); 216262306a36Sopenharmony_ci at_xdmac_device_pause_internal(atchan); 216362306a36Sopenharmony_ci at_xdmac_runtime_suspend_descriptors(atchan); 216462306a36Sopenharmony_ci } 216562306a36Sopenharmony_ci atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM); 216662306a36Sopenharmony_ci atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA); 216762306a36Sopenharmony_ci atchan->save_cndc = at_xdmac_chan_read(atchan, AT_XDMAC_CNDC); 216862306a36Sopenharmony_ci } 216962306a36Sopenharmony_ci } 217062306a36Sopenharmony_ci atxdmac->save_gim = at_xdmac_read(atxdmac, AT_XDMAC_GIM); 217162306a36Sopenharmony_ci atxdmac->save_gs = at_xdmac_read(atxdmac, AT_XDMAC_GS); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci at_xdmac_off(atxdmac, false); 217462306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 217562306a36Sopenharmony_ci pm_runtime_put_noidle(atxdmac->dev); 217662306a36Sopenharmony_ci clk_disable_unprepare(atxdmac->clk); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci return 0; 217962306a36Sopenharmony_ci} 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_cistatic int __maybe_unused atmel_xdmac_resume(struct device *dev) 218262306a36Sopenharmony_ci{ 218362306a36Sopenharmony_ci struct at_xdmac *atxdmac = dev_get_drvdata(dev); 218462306a36Sopenharmony_ci struct at_xdmac_chan *atchan; 218562306a36Sopenharmony_ci struct dma_chan *chan, *_chan; 218662306a36Sopenharmony_ci struct platform_device *pdev = container_of(dev, struct platform_device, dev); 218762306a36Sopenharmony_ci int i, ret; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci ret = clk_prepare_enable(atxdmac->clk); 219062306a36Sopenharmony_ci if (ret) 219162306a36Sopenharmony_ci return ret; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci pm_runtime_get_noresume(atxdmac->dev); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci at_xdmac_axi_config(pdev); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci /* Clear pending interrupts. */ 219862306a36Sopenharmony_ci for (i = 0; i < atxdmac->dma.chancnt; i++) { 219962306a36Sopenharmony_ci atchan = &atxdmac->chan[i]; 220062306a36Sopenharmony_ci while (at_xdmac_chan_read(atchan, AT_XDMAC_CIS)) 220162306a36Sopenharmony_ci cpu_relax(); 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GIE, atxdmac->save_gim); 220562306a36Sopenharmony_ci list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) { 220662306a36Sopenharmony_ci atchan = to_at_xdmac_chan(chan); 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CC, atchan->save_cc); 220962306a36Sopenharmony_ci if (at_xdmac_chan_is_cyclic(atchan)) { 221062306a36Sopenharmony_ci /* 221162306a36Sopenharmony_ci * Resume only channels not explicitly paused by 221262306a36Sopenharmony_ci * consumers. 221362306a36Sopenharmony_ci */ 221462306a36Sopenharmony_ci if (at_xdmac_chan_is_paused_internal(atchan)) { 221562306a36Sopenharmony_ci ret = at_xdmac_runtime_resume_descriptors(atchan); 221662306a36Sopenharmony_ci if (ret < 0) 221762306a36Sopenharmony_ci return ret; 221862306a36Sopenharmony_ci at_xdmac_device_resume_internal(atchan); 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci /* 222262306a36Sopenharmony_ci * We may resume from a deep sleep state where power 222362306a36Sopenharmony_ci * to DMA controller is cut-off. Thus, restore the 222462306a36Sopenharmony_ci * suspend state of channels set though dmaengine API. 222562306a36Sopenharmony_ci */ 222662306a36Sopenharmony_ci else if (at_xdmac_chan_is_paused(atchan)) 222762306a36Sopenharmony_ci at_xdmac_device_pause_set(atxdmac, atchan); 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, atchan->save_cnda); 223062306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, atchan->save_cndc); 223162306a36Sopenharmony_ci at_xdmac_chan_write(atchan, AT_XDMAC_CIE, atchan->save_cim); 223262306a36Sopenharmony_ci wmb(); 223362306a36Sopenharmony_ci if (atxdmac->save_gs & atchan->mask) 223462306a36Sopenharmony_ci at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask); 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci pm_runtime_mark_last_busy(atxdmac->dev); 223962306a36Sopenharmony_ci pm_runtime_put_autosuspend(atxdmac->dev); 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci return 0; 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_cistatic int __maybe_unused atmel_xdmac_runtime_suspend(struct device *dev) 224562306a36Sopenharmony_ci{ 224662306a36Sopenharmony_ci struct at_xdmac *atxdmac = dev_get_drvdata(dev); 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci clk_disable(atxdmac->clk); 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci return 0; 225162306a36Sopenharmony_ci} 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_cistatic int __maybe_unused atmel_xdmac_runtime_resume(struct device *dev) 225462306a36Sopenharmony_ci{ 225562306a36Sopenharmony_ci struct at_xdmac *atxdmac = dev_get_drvdata(dev); 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci return clk_enable(atxdmac->clk); 225862306a36Sopenharmony_ci} 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_cistatic int at_xdmac_probe(struct platform_device *pdev) 226162306a36Sopenharmony_ci{ 226262306a36Sopenharmony_ci struct at_xdmac *atxdmac; 226362306a36Sopenharmony_ci int irq, nr_channels, i, ret; 226462306a36Sopenharmony_ci void __iomem *base; 226562306a36Sopenharmony_ci u32 reg; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 226862306a36Sopenharmony_ci if (irq < 0) 226962306a36Sopenharmony_ci return irq; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 227262306a36Sopenharmony_ci if (IS_ERR(base)) 227362306a36Sopenharmony_ci return PTR_ERR(base); 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci /* 227662306a36Sopenharmony_ci * Read number of xdmac channels, read helper function can't be used 227762306a36Sopenharmony_ci * since atxdmac is not yet allocated and we need to know the number 227862306a36Sopenharmony_ci * of channels to do the allocation. 227962306a36Sopenharmony_ci */ 228062306a36Sopenharmony_ci reg = readl_relaxed(base + AT_XDMAC_GTYPE); 228162306a36Sopenharmony_ci nr_channels = AT_XDMAC_NB_CH(reg); 228262306a36Sopenharmony_ci if (nr_channels > AT_XDMAC_MAX_CHAN) { 228362306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid number of channels (%u)\n", 228462306a36Sopenharmony_ci nr_channels); 228562306a36Sopenharmony_ci return -EINVAL; 228662306a36Sopenharmony_ci } 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci atxdmac = devm_kzalloc(&pdev->dev, 228962306a36Sopenharmony_ci struct_size(atxdmac, chan, nr_channels), 229062306a36Sopenharmony_ci GFP_KERNEL); 229162306a36Sopenharmony_ci if (!atxdmac) { 229262306a36Sopenharmony_ci dev_err(&pdev->dev, "can't allocate at_xdmac structure\n"); 229362306a36Sopenharmony_ci return -ENOMEM; 229462306a36Sopenharmony_ci } 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci atxdmac->regs = base; 229762306a36Sopenharmony_ci atxdmac->irq = irq; 229862306a36Sopenharmony_ci atxdmac->dev = &pdev->dev; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci atxdmac->layout = of_device_get_match_data(&pdev->dev); 230162306a36Sopenharmony_ci if (!atxdmac->layout) 230262306a36Sopenharmony_ci return -ENODEV; 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci atxdmac->clk = devm_clk_get(&pdev->dev, "dma_clk"); 230562306a36Sopenharmony_ci if (IS_ERR(atxdmac->clk)) { 230662306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get dma_clk\n"); 230762306a36Sopenharmony_ci return PTR_ERR(atxdmac->clk); 230862306a36Sopenharmony_ci } 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci /* Do not use dev res to prevent races with tasklet */ 231162306a36Sopenharmony_ci ret = request_irq(atxdmac->irq, at_xdmac_interrupt, 0, "at_xdmac", atxdmac); 231262306a36Sopenharmony_ci if (ret) { 231362306a36Sopenharmony_ci dev_err(&pdev->dev, "can't request irq\n"); 231462306a36Sopenharmony_ci return ret; 231562306a36Sopenharmony_ci } 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci ret = clk_prepare_enable(atxdmac->clk); 231862306a36Sopenharmony_ci if (ret) { 231962306a36Sopenharmony_ci dev_err(&pdev->dev, "can't prepare or enable clock\n"); 232062306a36Sopenharmony_ci goto err_free_irq; 232162306a36Sopenharmony_ci } 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci atxdmac->at_xdmac_desc_pool = 232462306a36Sopenharmony_ci dmam_pool_create(dev_name(&pdev->dev), &pdev->dev, 232562306a36Sopenharmony_ci sizeof(struct at_xdmac_desc), 4, 0); 232662306a36Sopenharmony_ci if (!atxdmac->at_xdmac_desc_pool) { 232762306a36Sopenharmony_ci dev_err(&pdev->dev, "no memory for descriptors dma pool\n"); 232862306a36Sopenharmony_ci ret = -ENOMEM; 232962306a36Sopenharmony_ci goto err_clk_disable; 233062306a36Sopenharmony_ci } 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci dma_cap_set(DMA_CYCLIC, atxdmac->dma.cap_mask); 233362306a36Sopenharmony_ci dma_cap_set(DMA_INTERLEAVE, atxdmac->dma.cap_mask); 233462306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, atxdmac->dma.cap_mask); 233562306a36Sopenharmony_ci dma_cap_set(DMA_MEMSET, atxdmac->dma.cap_mask); 233662306a36Sopenharmony_ci dma_cap_set(DMA_MEMSET_SG, atxdmac->dma.cap_mask); 233762306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, atxdmac->dma.cap_mask); 233862306a36Sopenharmony_ci /* 233962306a36Sopenharmony_ci * Without DMA_PRIVATE the driver is not able to allocate more than 234062306a36Sopenharmony_ci * one channel, second allocation fails in private_candidate. 234162306a36Sopenharmony_ci */ 234262306a36Sopenharmony_ci dma_cap_set(DMA_PRIVATE, atxdmac->dma.cap_mask); 234362306a36Sopenharmony_ci atxdmac->dma.dev = &pdev->dev; 234462306a36Sopenharmony_ci atxdmac->dma.device_alloc_chan_resources = at_xdmac_alloc_chan_resources; 234562306a36Sopenharmony_ci atxdmac->dma.device_free_chan_resources = at_xdmac_free_chan_resources; 234662306a36Sopenharmony_ci atxdmac->dma.device_tx_status = at_xdmac_tx_status; 234762306a36Sopenharmony_ci atxdmac->dma.device_issue_pending = at_xdmac_issue_pending; 234862306a36Sopenharmony_ci atxdmac->dma.device_prep_dma_cyclic = at_xdmac_prep_dma_cyclic; 234962306a36Sopenharmony_ci atxdmac->dma.device_prep_interleaved_dma = at_xdmac_prep_interleaved; 235062306a36Sopenharmony_ci atxdmac->dma.device_prep_dma_memcpy = at_xdmac_prep_dma_memcpy; 235162306a36Sopenharmony_ci atxdmac->dma.device_prep_dma_memset = at_xdmac_prep_dma_memset; 235262306a36Sopenharmony_ci atxdmac->dma.device_prep_dma_memset_sg = at_xdmac_prep_dma_memset_sg; 235362306a36Sopenharmony_ci atxdmac->dma.device_prep_slave_sg = at_xdmac_prep_slave_sg; 235462306a36Sopenharmony_ci atxdmac->dma.device_config = at_xdmac_device_config; 235562306a36Sopenharmony_ci atxdmac->dma.device_pause = at_xdmac_device_pause; 235662306a36Sopenharmony_ci atxdmac->dma.device_resume = at_xdmac_device_resume; 235762306a36Sopenharmony_ci atxdmac->dma.device_terminate_all = at_xdmac_device_terminate_all; 235862306a36Sopenharmony_ci atxdmac->dma.src_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; 235962306a36Sopenharmony_ci atxdmac->dma.dst_addr_widths = AT_XDMAC_DMA_BUSWIDTHS; 236062306a36Sopenharmony_ci atxdmac->dma.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 236162306a36Sopenharmony_ci atxdmac->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci platform_set_drvdata(pdev, atxdmac); 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 500); 236662306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 236762306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 236862306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 236962306a36Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci /* Init channels. */ 237262306a36Sopenharmony_ci INIT_LIST_HEAD(&atxdmac->dma.channels); 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci /* Disable all chans and interrupts. */ 237562306a36Sopenharmony_ci at_xdmac_off(atxdmac, true); 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci for (i = 0; i < nr_channels; i++) { 237862306a36Sopenharmony_ci struct at_xdmac_chan *atchan = &atxdmac->chan[i]; 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci atchan->chan.device = &atxdmac->dma; 238162306a36Sopenharmony_ci list_add_tail(&atchan->chan.device_node, 238262306a36Sopenharmony_ci &atxdmac->dma.channels); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci atchan->ch_regs = at_xdmac_chan_reg_base(atxdmac, i); 238562306a36Sopenharmony_ci atchan->mask = 1 << i; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci spin_lock_init(&atchan->lock); 238862306a36Sopenharmony_ci INIT_LIST_HEAD(&atchan->xfers_list); 238962306a36Sopenharmony_ci INIT_LIST_HEAD(&atchan->free_descs_list); 239062306a36Sopenharmony_ci tasklet_setup(&atchan->tasklet, at_xdmac_tasklet); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci /* Clear pending interrupts. */ 239362306a36Sopenharmony_ci while (at_xdmac_chan_read(atchan, AT_XDMAC_CIS)) 239462306a36Sopenharmony_ci cpu_relax(); 239562306a36Sopenharmony_ci } 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci ret = dma_async_device_register(&atxdmac->dma); 239862306a36Sopenharmony_ci if (ret) { 239962306a36Sopenharmony_ci dev_err(&pdev->dev, "fail to register DMA engine device\n"); 240062306a36Sopenharmony_ci goto err_pm_disable; 240162306a36Sopenharmony_ci } 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci ret = of_dma_controller_register(pdev->dev.of_node, 240462306a36Sopenharmony_ci at_xdmac_xlate, atxdmac); 240562306a36Sopenharmony_ci if (ret) { 240662306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register of dma controller\n"); 240762306a36Sopenharmony_ci goto err_dma_unregister; 240862306a36Sopenharmony_ci } 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci dev_info(&pdev->dev, "%d channels, mapped at 0x%p\n", 241162306a36Sopenharmony_ci nr_channels, atxdmac->regs); 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci at_xdmac_axi_config(pdev); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 241662306a36Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci return 0; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_cierr_dma_unregister: 242162306a36Sopenharmony_ci dma_async_device_unregister(&atxdmac->dma); 242262306a36Sopenharmony_cierr_pm_disable: 242362306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 242462306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 242562306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 242662306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 242762306a36Sopenharmony_cierr_clk_disable: 242862306a36Sopenharmony_ci clk_disable_unprepare(atxdmac->clk); 242962306a36Sopenharmony_cierr_free_irq: 243062306a36Sopenharmony_ci free_irq(atxdmac->irq, atxdmac); 243162306a36Sopenharmony_ci return ret; 243262306a36Sopenharmony_ci} 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_cistatic int at_xdmac_remove(struct platform_device *pdev) 243562306a36Sopenharmony_ci{ 243662306a36Sopenharmony_ci struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev); 243762306a36Sopenharmony_ci int i; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci at_xdmac_off(atxdmac, true); 244062306a36Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 244162306a36Sopenharmony_ci dma_async_device_unregister(&atxdmac->dma); 244262306a36Sopenharmony_ci pm_runtime_disable(atxdmac->dev); 244362306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 244462306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 244562306a36Sopenharmony_ci clk_disable_unprepare(atxdmac->clk); 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci free_irq(atxdmac->irq, atxdmac); 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci for (i = 0; i < atxdmac->dma.chancnt; i++) { 245062306a36Sopenharmony_ci struct at_xdmac_chan *atchan = &atxdmac->chan[i]; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci tasklet_kill(&atchan->tasklet); 245362306a36Sopenharmony_ci at_xdmac_free_chan_resources(&atchan->chan); 245462306a36Sopenharmony_ci } 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci return 0; 245762306a36Sopenharmony_ci} 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_cistatic const struct dev_pm_ops __maybe_unused atmel_xdmac_dev_pm_ops = { 246062306a36Sopenharmony_ci .prepare = atmel_xdmac_prepare, 246162306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(atmel_xdmac_suspend, atmel_xdmac_resume) 246262306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(atmel_xdmac_runtime_suspend, 246362306a36Sopenharmony_ci atmel_xdmac_runtime_resume, NULL) 246462306a36Sopenharmony_ci}; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_cistatic const struct of_device_id atmel_xdmac_dt_ids[] = { 246762306a36Sopenharmony_ci { 246862306a36Sopenharmony_ci .compatible = "atmel,sama5d4-dma", 246962306a36Sopenharmony_ci .data = &at_xdmac_sama5d4_layout, 247062306a36Sopenharmony_ci }, { 247162306a36Sopenharmony_ci .compatible = "microchip,sama7g5-dma", 247262306a36Sopenharmony_ci .data = &at_xdmac_sama7g5_layout, 247362306a36Sopenharmony_ci }, { 247462306a36Sopenharmony_ci /* sentinel */ 247562306a36Sopenharmony_ci } 247662306a36Sopenharmony_ci}; 247762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_xdmac_dt_ids); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic struct platform_driver at_xdmac_driver = { 248062306a36Sopenharmony_ci .probe = at_xdmac_probe, 248162306a36Sopenharmony_ci .remove = at_xdmac_remove, 248262306a36Sopenharmony_ci .driver = { 248362306a36Sopenharmony_ci .name = "at_xdmac", 248462306a36Sopenharmony_ci .of_match_table = of_match_ptr(atmel_xdmac_dt_ids), 248562306a36Sopenharmony_ci .pm = pm_ptr(&atmel_xdmac_dev_pm_ops), 248662306a36Sopenharmony_ci } 248762306a36Sopenharmony_ci}; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_cistatic int __init at_xdmac_init(void) 249062306a36Sopenharmony_ci{ 249162306a36Sopenharmony_ci return platform_driver_register(&at_xdmac_driver); 249262306a36Sopenharmony_ci} 249362306a36Sopenharmony_cisubsys_initcall(at_xdmac_init); 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_cistatic void __exit at_xdmac_exit(void) 249662306a36Sopenharmony_ci{ 249762306a36Sopenharmony_ci platform_driver_unregister(&at_xdmac_driver); 249862306a36Sopenharmony_ci} 249962306a36Sopenharmony_cimodule_exit(at_xdmac_exit); 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel Extended DMA Controller driver"); 250262306a36Sopenharmony_ciMODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); 250362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2504