summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-06-12 16:33:04 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-06-12 16:33:04 -0600
commit41db326ad35f69b49dfc8dbea69c794cb31f40e2 (patch)
treef3670ded814424a92fba6de88e91fa7c6d3fb602
parenta686eede88b28e293f519ca4d609de67de9c07c5 (diff)
downloadpx4-nuttx-41db326ad35f69b49dfc8dbea69c794cb31f40e2.tar.gz
px4-nuttx-41db326ad35f69b49dfc8dbea69c794cb31f40e2.tar.bz2
px4-nuttx-41db326ad35f69b49dfc8dbea69c794cb31f40e2.zip
SAMA5D4: Initial XDMAC driver logic; initial check-in is little more the the DMAC driver with some name changes
-rwxr-xr-xnuttx/arch/arm/include/sama5/sama5d4_irq.h4
-rw-r--r--nuttx/arch/arm/src/sama5/Kconfig10
-rw-r--r--nuttx/arch/arm/src/sama5/Make.defs8
-rwxr-xr-xnuttx/arch/arm/src/sama5/chip/sam_dmac.h1
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_xdmac.h260
-rw-r--r--nuttx/arch/arm/src/sama5/sam_xdmac.c2621
6 files changed, 2845 insertions, 59 deletions
diff --git a/nuttx/arch/arm/include/sama5/sama5d4_irq.h b/nuttx/arch/arm/include/sama5/sama5d4_irq.h
index b75d5f93a..a35758ca1 100755
--- a/nuttx/arch/arm/include/sama5/sama5d4_irq.h
+++ b/nuttx/arch/arm/include/sama5/sama5d4_irq.h
@@ -119,7 +119,7 @@
/* 60 Undefined */
#define SAM_PID_SMD (61) /* SMD Soft Modem */
#define SAM_PID_TWI3 (62) /* Two-Wire Interface 3 */
-#define SAM_PID_CATB (63) /* Watchdog timer Interrupt */
+#define SAM_PID_CATB (63) /* Capacitive Touch Module */
#define SAM_PID_SFR (64) /* Special Function Register */
#define SAM_PID_AIC (65) /* Advanced Interrupt Controller */
#define SAM_PID_SAIC (66) /* Secured Advanced Interrupt Controller */
@@ -192,7 +192,7 @@
#define SAM_IRQ_SMD SAM_PID_SMD /* SMD Soft Modem */
#define SAM_IRQ_TWI3 SAM_PID_TWI3 /* Two-Wire Interface 3 */
-#define SAM_IRQ_CATB SAM_PID_CATB /* Watchdog timer Interrupt */
+#define SAM_IRQ_CATB SAM_PID_CATB /* Capacitive Touch Module */
#define SAM_IRQ_SFR SAM_PID_SFR /* Special Function Register */
#define SAM_IRQ_AIC SAM_PID_AIC /* Advanced Interrupt Controller */
#define SAM_IRQ_SAIC SAM_PID_SAIC /* Secured Advanced Interrupt Controller */
diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig
index b9841df6d..c4a04e993 100644
--- a/nuttx/arch/arm/src/sama5/Kconfig
+++ b/nuttx/arch/arm/src/sama5/Kconfig
@@ -401,8 +401,14 @@ config SAMA5_DMAC1
select ARCH_DMA
depends on SAMA5_HAVE_DMA
-config SAMA5_XDMAC
- bool "XDMA Controller (XDMAC)"
+config SAMA5_XDMAC0
+ bool "XDMA Controller (XDMAC0)"
+ default n
+ select ARCH_DMA
+ depends on SAMA5_HAVE_XDMA
+
+config SAMA5_XDMAC1
+ bool "XDMA Controller (XDMAC1)"
default n
select ARCH_DMA
depends on SAMA5_HAVE_XDMA
diff --git a/nuttx/arch/arm/src/sama5/Make.defs b/nuttx/arch/arm/src/sama5/Make.defs
index ae3cada4e..416a3a785 100644
--- a/nuttx/arch/arm/src/sama5/Make.defs
+++ b/nuttx/arch/arm/src/sama5/Make.defs
@@ -111,6 +111,14 @@ CHIP_CSRCS += sam_dmac.c
endif
endif
+ifeq ($(CONFIG_SAMA5_XDMAC0),y)
+CHIP_CSRCS += sam_xdmac.c
+else
+ifeq ($(CONFIG_SAMA5_XDMAC1),y)
+CHIP_CSRCS += sam_xdmac.c
+endif
+endif
+
ifeq ($(CONFIG_SAMA5_PIO_IRQ),y)
CHIP_CSRCS += sam_pioirq.c
endif
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_dmac.h b/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
index 67b47f405..3dc39d3d4 100755
--- a/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
+++ b/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
@@ -779,7 +779,6 @@
#define DMAC0_CH_SMD_TX (15) /* SMD Transmit */
#define DMAC0_CH_SMD_RX (16) /* SMD Receive */
-
/* DMA Controller 1 Channel Definitions */
#define DMAC1_CH_HSMCI1 (0) /* HSMCI1 Receive/transmit */
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_xdmac.h b/nuttx/arch/arm/src/sama5/chip/sam_xdmac.h
index a81b0886c..66e0e2019 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_xdmac.h
+++ b/nuttx/arch/arm/src/sama5/chip/sam_xdmac.h
@@ -66,7 +66,29 @@
#define SAM_XDMAC_GSWS_OFFSET 0x003c /* Global Channel Software Request Status Register */
#define SAM_XDMAC_GSWF_OFFSET 0x0040 /* Global Channel Software Flush Request Register */
/* 0x0044–0x004c Reserved */
-#define SAM_XDMACH_OFFSET(n) (0x0050 + ((n) << 6))
+
+/* Offsets to the base of the DMA channel registers */
+
+#define SAM_XDMAC_CH_OFFSET(n) (0x0050 + ((n) << 6))
+# define SAM_XDMAC_CH0_OFFSET 0x0050
+# define SAM_XDMAC_CH1_OFFSET 0x0090
+# define SAM_XDMAC_CH2_OFFSET 0x00d0
+# define SAM_XDMAC_CH3_OFFSET 0x0110
+# define SAM_XDMAC_CH4_OFFSET 0x0150
+# define SAM_XDMAC_CH5_OFFSET 0x0190
+# define SAM_XDMAC_CH6_OFFSET 0x01d0
+# define SAM_XDMAC_CH7_OFFSET 0x0210
+# define SAM_XDMAC_CH8_OFFSET 0x0250
+# define SAM_XDMAC_CH9_OFFSET 0x0290
+# define SAM_XDMAC_CH10_OFFSET 0x02d0
+# define SAM_XDMAC_CH11_OFFSET 0x0310
+# define SAM_XDMAC_CH12_OFFSET 0x0350
+# define SAM_XDMAC_CH13_OFFSET 0x0390
+# define SAM_XDMAC_CH14_OFFSET 0x03d0
+# define SAM_XDMAC_CH15_OFFSET 0x0410
+
+/* Offsets to channel registers relative to the base of the DMA channel registers */
+
#define SAM_XDMACH_CIE_OFFSET 0x0000 /* Channel Interrupt Enable Register */
#define SAM_XDMACH_CID_OFFSET 0x0004 /* Channel Interrupt Disable Register */
#define SAM_XDMACH_CIM_OFFSET 0x0008 /* Channel Interrupt Mask Register */
@@ -104,21 +126,42 @@
#define SAM_XDMAC0_GSWS (SAM_XDMAC0_VBASE+SAM_XDMAC_GSWS_OFFSET)
#define SAM_XDMAC0_GSWF (SAM_XDMAC0_VBASE+SAM_XDMAC_GSWF_OFFSET)
-#define SAM_XDMACH0_BASE(n) (SAM_XDMAC0_VBASE+SAM_XDMACH_OFFSET(n))
-#define SAM_XDMACH0_CIE(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CIE_OFFSET)
-#define SAM_XDMACH0_CID(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CID_OFFSET)
-#define SAM_XDMACH0_CIM(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CIM_OFFSET)
-#define SAM_XDMACH0_CIS(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CIS_OFFSET)
-#define SAM_XDMACH0_CSA(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CSA_OFFSET)
-#define SAM_XDMACH0_CDA(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CDA_OFFSET)
-#define SAM_XDMACH0_CNDA(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CNDA_OFFSET)
-#define SAM_XDMACH0_CNDC(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CNDC_OFFSET)
-#define SAM_XDMACH0_CUBC(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CUBC_OFFSET)
-#define SAM_XDMACH0_CBC(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CBC_OFFSET)
-#define SAM_XDMACH0_CC(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CC_OFFSET)
-#define SAM_XDMACH0_CDSMSP(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CDSMSP_OFFSET)
-#define SAM_XDMACH0_CSUS(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CSUS_OFFSET)
-#define SAM_XDMACH0_CDUS(n) (SAM_XDMACH0_BASE(n)+SAM_XDMACH_CDUS_OFFSET)
+/* Base addresses of XDMAC0 channel registers */
+
+#define SAM_XDMAC0_CH_BASE(n) (SAM_XDMAC0_VBASE+SAM_XDMAC_CH_OFFSET(n))
+# define SAM_XDMAC0_CH0_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH0_OFFSET)
+# define SAM_XDMAC0_CH1_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH1_OFFSET)
+# define SAM_XDMAC0_CH2_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH2_OFFSET)
+# define SAM_XDMAC0_CH3_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH3_OFFSET)
+# define SAM_XDMAC0_CH4_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH4_OFFSET)
+# define SAM_XDMAC0_CH5_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH5_OFFSET)
+# define SAM_XDMAC0_CH6_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH6_OFFSET)
+# define SAM_XDMAC0_CH7_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH7_OFFSET)
+# define SAM_XDMAC0_CH8_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH8_OFFSET)
+# define SAM_XDMAC0_CH9_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH9_OFFSET)
+# define SAM_XDMAC0_CH10_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH10_OFFSET)
+# define SAM_XDMAC0_CH11_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH11_OFFSET)
+# define SAM_XDMAC0_CH12_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH12_OFFSET)
+# define SAM_XDMAC0_CH13_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH13_OFFSET)
+# define SAM_XDMAC0_CH14_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH14_OFFSET)
+# define SAM_XDMAC0_CH15_BASE (SAM_XDMAC0_VBASE+SAM_XDMAC_CH15_OFFSET)
+
+/* Addresses of XDMAC0 channel registers */
+
+#define SAM_XDMACH0_CIE(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CIE_OFFSET)
+#define SAM_XDMACH0_CID(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CID_OFFSET)
+#define SAM_XDMACH0_CIM(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CIM_OFFSET)
+#define SAM_XDMACH0_CIS(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CIS_OFFSET)
+#define SAM_XDMACH0_CSA(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CSA_OFFSET)
+#define SAM_XDMACH0_CDA(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CDA_OFFSET)
+#define SAM_XDMACH0_CNDA(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CNDA_OFFSET)
+#define SAM_XDMACH0_CNDC(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CNDC_OFFSET)
+#define SAM_XDMACH0_CUBC(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CUBC_OFFSET)
+#define SAM_XDMACH0_CBC(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CBC_OFFSET)
+#define SAM_XDMACH0_CC(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CC_OFFSET)
+#define SAM_XDMACH0_CDSMSP(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CDSMSP_OFFSET)
+#define SAM_XDMACH0_CSUS(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CSUS_OFFSET)
+#define SAM_XDMACH0_CDUS(n) (SAM_XDMACH0_CH_BASE(n)+SAM_XDMACH_CDUS_OFFSET)
#define SAM_XDMAC1_GTYPE (SAM_XDMAC1_VBASE+SAM_XDMAC_GTYPE_OFFSET)
#define SAM_XDMAC1_GCFG (SAM_XDMAC1_VBASE+SAM_XDMAC_GCFG_OFFSET)
@@ -138,21 +181,42 @@
#define SAM_XDMAC1_GSWS (SAM_XDMAC1_VBASE+SAM_XDMAC_GSWS_OFFSET)
#define SAM_XDMAC1_GSWF (SAM_XDMAC1_VBASE+SAM_XDMAC_GSWF_OFFSET)
-#define SAM_XDMACH1_BASE(n) (SAM_XDMAC1_VBASE+SAM_XDMACH_OFFSET(n))
-#define SAM_XDMACH1_CIE(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CIE_OFFSET)
-#define SAM_XDMACH1_CID(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CID_OFFSET)
-#define SAM_XDMACH1_CIM(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CIM_OFFSET)
-#define SAM_XDMACH1_CIS(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CIS_OFFSET)
-#define SAM_XDMACH1_CSA(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CSA_OFFSET)
-#define SAM_XDMACH1_CDA(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CDA_OFFSET)
-#define SAM_XDMACH1_CNDA(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CNDA_OFFSET)
-#define SAM_XDMACH1_CNDC(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CNDC_OFFSET)
-#define SAM_XDMACH1_CUBC(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CUBC_OFFSET)
-#define SAM_XDMACH1_CBC(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CBC_OFFSET)
-#define SAM_XDMACH1_CC(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CC_OFFSET)
-#define SAM_XDMACH1_CDSMSP(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CDSMSP_OFFSET)
-#define SAM_XDMACH1_CSUS(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CSUS_OFFSET)
-#define SAM_XDMACH1_CDUS(n) (SAM_XDMACH1_BASE(n)+SAM_XDMACH_CDUS_OFFSET)
+/* Base addresses of XDMAC1 channel registers */
+
+#define SAM_XDMAC1_CH_BASE(n) (SAM_XDMAC1_VBASE+SAM_XDMAC_CH_OFFSET(n))
+# define SAM_XDMAC1_CH0_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH0_OFFSET)
+# define SAM_XDMAC1_CH1_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH1_OFFSET)
+# define SAM_XDMAC1_CH2_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH2_OFFSET)
+# define SAM_XDMAC1_CH3_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH3_OFFSET)
+# define SAM_XDMAC1_CH4_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH4_OFFSET)
+# define SAM_XDMAC1_CH5_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH5_OFFSET)
+# define SAM_XDMAC1_CH6_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH6_OFFSET)
+# define SAM_XDMAC1_CH7_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH7_OFFSET)
+# define SAM_XDMAC1_CH8_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH8_OFFSET)
+# define SAM_XDMAC1_CH9_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH9_OFFSET)
+# define SAM_XDMAC1_CH10_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH10_OFFSET)
+# define SAM_XDMAC1_CH11_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH11_OFFSET)
+# define SAM_XDMAC1_CH12_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH12_OFFSET)
+# define SAM_XDMAC1_CH13_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH13_OFFSET)
+# define SAM_XDMAC1_CH14_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH14_OFFSET)
+# define SAM_XDMAC1_CH15_BASE (SAM_XDMAC1_VBASE+SAM_XDMAC_CH15_OFFSET)
+
+/* Addresses of XDMAC0 channel registers */
+
+#define SAM_XDMACH1_CIE(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CIE_OFFSET)
+#define SAM_XDMACH1_CID(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CID_OFFSET)
+#define SAM_XDMACH1_CIM(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CIM_OFFSET)
+#define SAM_XDMACH1_CIS(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CIS_OFFSET)
+#define SAM_XDMACH1_CSA(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CSA_OFFSET)
+#define SAM_XDMACH1_CDA(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CDA_OFFSET)
+#define SAM_XDMACH1_CNDA(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CNDA_OFFSET)
+#define SAM_XDMACH1_CNDC(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CNDC_OFFSET)
+#define SAM_XDMACH1_CUBC(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CUBC_OFFSET)
+#define SAM_XDMACH1_CBC(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CBC_OFFSET)
+#define SAM_XDMACH1_CC(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CC_OFFSET)
+#define SAM_XDMACH1_CDSMSP(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CDSMSP_OFFSET)
+#define SAM_XDMACH1_CSUS(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CSUS_OFFSET)
+#define SAM_XDMACH1_CDUS(n) (SAM_XDMACH1_CH_BASE(n)+SAM_XDMACH_CDUS_OFFSET)
/* XDMAC Register Bit Definitions ***************************************************/
@@ -227,10 +291,10 @@
/* Channel Next Descriptor Control Register */
-#define XDMACH_CNDC_NDE (1 << 0) /* Bit 0: Channel x Next Descriptor Enable */
-#define XDMACH_CNDC_NDSUP (1 << 1) /* Bit 1: Channel x Next Descriptor Source Update */
-#define XDMACH_CNDC_NDDUP (1 << 2) /* Bit 2: Channel x Next Descriptor Destination Update */
-#define XDMACH_CNDC_NDVIEW_SHIFT (3) /* Bits 3-4: Channel x Next Descriptor View */
+#define XDMACH_CNDC_NDE (1 << 0) /* Bit 0: Channel Next Descriptor Enable */
+#define XDMACH_CNDC_NDSUP (1 << 1) /* Bit 1: Channel Next Descriptor Source Update */
+#define XDMACH_CNDC_NDDUP (1 << 2) /* Bit 2: Channel Next Descriptor Destination Update */
+#define XDMACH_CNDC_NDVIEW_SHIFT (3) /* Bits 3-4: Channel Next Descriptor View */
#define XDMACH_CNDC_NDVIEW_MASK (3 << XDMACH_CNDC_NDVIEW_SHIFT)
# define XDMACH_CNDC_NDVIEW_NDV0 (0 << XDMACH_CNDC_NDVIEW_SHIFT) /* Next Descriptor View 0 */
# define XDMACH_CNDC_NDVIEW_NDV1 (1 << XDMACH_CNDC_NDVIEW_SHIFT) /* Next Descriptor View 1 */
@@ -239,47 +303,47 @@
/* Channel Microblock Control Register */
-#define XDMACH_CUBC_UBLEN_MASK (0x00ffffff) /* Bits 0-23: Channel x Microblock Length */
+#define XDMACH_CUBC_UBLEN_MASK (0x00ffffff) /* Bits 0-23: Channel Microblock Length */
/* Channel Block Control Register */
-#define XDMACH_CBC_BLEN_MASK (0x000000fff) /* Bits 0-11: Channel x Block Length */
+#define XDMACH_CBC_BLEN_MASK (0x000000fff) /* Bits 0-11: Channel Block Length */
/* Channel Configuration Register */
-#define XDMACH_CC_TYPE (1 << 0) /* Bit 0: Channel x Transfer Type */
-#define XDMACH_CC_MBSIZE_SHIFT (1) /* Bits 1-2: Channel x Memory Burst Size */
+#define XDMACH_CC_TYPE (1 << 0) /* Bit 0: Channel Transfer Type */
+#define XDMACH_CC_MBSIZE_SHIFT (1) /* Bits 1-2: Channel Memory Burst Size */
#define XDMACH_CC_MBSIZE_MASK (3 << XDMACH_CC_MBSIZE_SHIFT)
# define XDMACH_CC_MBSIZE_1 (0 << XDMACH_CC_MBSIZE_SHIFT) /* The memory burst size is set to one */
# define XDMACH_CC_MBSIZE_4 (1 << XDMACH_CC_MBSIZE_SHIFT) /* The memory burst size is set to four */
# define XDMACH_CC_MBSIZE_8 (2 << XDMACH_CC_MBSIZE_SHIFT) /* The memory burst size is set to eight */
# define XDMACH_CC_MBSIZE_16 (3 << XDMACH_CC_MBSIZE_SHIFT) /* The memory burst size is set to sixteen */
-#define XDMACH_CC_DSYNC (1 << 4) /* Bit 4: Channel x Synchronization */
-#define XDMACH_CC_PROT (1 << 5) /* Bit 5: Channel x Protection */
-#define XDMACH_CC_SWREQ (1 << 6) /* Bit 6: Channel x Software Request Trigger */
-#define XDMACH_CC_MEMSET (1 << 7) /* Bit 7: Channel x Fill Block of memory */
-#define XDMACH_CC_CSIZE_SHIFT (8) /* Bits 8-10: Channel x Chunk Size */
+#define XDMACH_CC_DSYNC (1 << 4) /* Bit 4: Channel Synchronization */
+#define XDMACH_CC_PROT (1 << 5) /* Bit 5: Channel Protection */
+#define XDMACH_CC_SWREQ (1 << 6) /* Bit 6: Channel Software Request Trigger */
+#define XDMACH_CC_MEMSET (1 << 7) /* Bit 7: Channel Fill Block of memory */
+#define XDMACH_CC_CSIZE_SHIFT (8) /* Bits 8-10: Channel Chunk Size */
#define XDMACH_CC_CSIZE_MASK (7 << XDMACH_CC_CSIZE_SHIFT)
# define XDMACH_CC_CSIZE_1 (0 << XDMACH_CC_CSIZE_SHIFT) /* 1 data transferred */
# define XDMACH_CC_CSIZE_2 (1 << XDMACH_CC_CSIZE_SHIFT) /* 2 data transferred */
# define XDMACH_CC_CSIZE_4 (2 << XDMACH_CC_CSIZE_SHIFT) /* 4 data transferred */
# define XDMACH_CC_CSIZE_8 (3 << XDMACH_CC_CSIZE_SHIFT) /* 8 data transferred */
# define XDMACH_CC_CSIZE_16 (4 << XDMACH_CC_CSIZE_SHIFT) /* 16 data transferred */
-#define XDMACH_CC_DWIDTH_SHIFT (11) /* Bits 11-12: Channel x Data Width */
+#define XDMACH_CC_DWIDTH_SHIFT (11) /* Bits 11-12: Channel Data Width */
#define XDMACH_CC_DWIDTH_MASK (3 << XDMACH_CC_DWIDTH_SHIFT)
# define XDMACH_CC_DWIDTH_BYTE (0 << XDMACH_CC_DWIDTH_SHIFT) /* The data size is set to 8 bits */
# define XDMACH_CC_DWIDTH_HWORD (1 << XDMACH_CC_DWIDTH_SHIFT) /* The data size is set to 16 bits */
# define XDMACH_CC_DWIDTH_WORD (2 << XDMACH_CC_DWIDTH_SHIFT) /* The data size is set to 32 bits */
# define XDMACH_CC_DWIDTH_DWORD (3 << XDMACH_CC_DWIDTH_SHIFT) /* The data size is set to 64 bits */
-#define XDMACH_CC_SIF (1 << 13) /* Bit 13: Channel x Source Interface Identifier */
-#define XDMACH_CC_DIF (1 << 14) /* Bit 14: Channel x Destination Interface Identifier */
-#define XDMACH_CC_SAM_SHIFT (16) /* Bits 16-17: Channel x Source Addressing Mode */
+#define XDMACH_CC_SIF (1 << 13) /* Bit 13: Channel Source Interface Identifier */
+#define XDMACH_CC_DIF (1 << 14) /* Bit 14: Channel Destination Interface Identifier */
+#define XDMACH_CC_SAM_SHIFT (16) /* Bits 16-17: Channel Source Addressing Mode */
#define XDMACH_CC_SAM_MASK (3 << XDMACH_CC_SAM_SHIFT)
# define XDMACH_CC_SAM_FIXED (0 << XDMACH_CC_SAM_SHIFT) /* The address remains unchanged */
# define XDMACH_CC_SAM_INCR (1 << XDMACH_CC_SAM_SHIFT) /* Address is incremented */
# define XDMACH_CC_SAM_UBS (2 << XDMACH_CC_SAM_SHIFT) /* Microblock stride is added */
# define XDMACH_CC_SAM_UBSDS (3 << XDMACH_CC_SAM_SHIFT) /* Microblock stride and data stride is added */
-#define XDMACH_CC_DAM_SHIFT (18) /* Bits 18-19: Channel x Destination Addressing Mode */
+#define XDMACH_CC_DAM_SHIFT (18) /* Bits 18-19: Channel Destination Addressing Mode */
#define XDMACH_CC_DAM_MASK (3 << XDMACH_CC_DAM_SHIFT)
# define XDMACH_CC_DAM_FIXED (0 << XDMACH_CC_DAM_SHIFT) /* The address remains unchanged */
# define XDMACH_CC_DAM_INCR (1 << XDMACH_CC_DAM_SHIFT) /* Address is incremented */
@@ -288,25 +352,113 @@
#define XDMACH_CC_INITD (1 << 21) /* Bit 21: Channel Initialization Terminated */
#define XDMACH_CC_RDIP (1 << 22) /* Bit 22: Read in Progress */
#define XDMACH_CC_WRIP (1 << 23) /* Bit 23: Write in Progress */
-#define XDMACH_CC_PERID_SHIFT (24) /* Bits 24-30: Channel x Peripheral Identifier */
+#define XDMACH_CC_PERID_SHIFT (24) /* Bits 24-30: Channel Peripheral Identifier */
#define XDMACH_CC_PERID_MASK (0x7f << XDMACH_CC_PERID_SHIFT)
# define XDMACH_CC_PERID(n) ((uint32_t)(n) << XDMACH_CC_PERID_SHIFT)
/* Channel Data Stride Memory Set Pattern */
-#define XDMACH_CDSMSP_SDS_MSP_SHIFT (0) /* Bits 0-15: Channel x Source Data stride or Memory Set Pattern */
+#define XDMACH_CDSMSP_SDS_MSP_SHIFT (0) /* Bits 0-15: Channel Source Data stride or Memory Set Pattern */
#define XDMACH_CDSMSP_SDS_MSP_MASK (0xffff << XDMACH_CDSMSP_SDS_MSP_SHIFT)
# define XDMACH_CDSMSP_SDS_MSP(n) ((uint32_t)(n) << XDMACH_CDSMSP_SDS_MSP_SHIFT)
-#define XDMACH_CDSMSP_DDS_MSP_SHIFT (16) /* Bits 16-31: Channel x Destination Data Stride or Memory Set Pattern */
+#define XDMACH_CDSMSP_DDS_MSP_SHIFT (16) /* Bits 16-31: Channel Destination Data Stride or Memory Set Pattern */
#define XDMACH_CDSMSP_DDS_MSP_MASK (0xffff << XDMACH_CDSMSP_DDS_MSP_SHIFT)
# define XDMACH_CDSMSP_DDS_MSP(n) ((uint32_t)(n) << XDMACH_CDSMSP_DDS_MSP_SHIFT)
/* Channel Source Microblock Stride */
-#define XDMACH_CSUS_SUBS_MASK (0x00ffffff) /* Bits 0-23: Channel x Source Microblock Stride */
+#define XDMACH_CSUS_SUBS_MASK (0x00ffffff) /* Bits 0-23: Channel Source Microblock Stride */
/* Channel Destination Microblock Stride */
-#define XDMACH_CDUS_DUBS_MASK (0x00ffffff) /* Bits 0-23: Channel x Destination Microblock Stride */
+#define XDMACH_CDUS_DUBS_MASK (0x00ffffff) /* Bits 0-23: Channel Destination Microblock Stride */
+
+/* XDMA Channel Definitions *************************************************************/
+/* XDMA Controller 0 Channel Definitions */
+
+#define XDMAC0_CH_HSMCI0 0 /* HSMCI0 Receive/Transmit */
+#define XDMAC0_CH_HSMCI1 1 /* HSMCI1 Receive/Transmit */
+#define XDMAC0_CH_TWI0_TX 2 /* TWI0 Transmit */
+#define XDMAC0_CH_TWI0_RX 3 /* TWI0 Receive */
+#define XDMAC0_CH_TWI1_TX 4 /* TWI1 Transmit */
+#define XDMAC0_CH_TWI1_RX 5 /* TWI1 Receive */
+#define XDMAC0_CH_TWI2_TX 6 /* TWI2 Transmit */
+#define XDMAC0_CH_TWI2_RX 7 /* TWI2 Receive */
+#define XDMAC0_CH_TWI3_TX 8 /* TWI3 Transmit */
+#define XDMAC0_CH_TWI3_RX 9 /* TWI3 Receive */
+#define XDMAC0_CH_SPI0_TX 10 /* SPI0 Transmit */
+#define XDMAC0_CH_SPI0_RX 11 /* SPI0 Receive */
+#define XDMAC0_CH_SPI1_TX 12 /* SPI1 Transmit */
+#define XDMAC0_CH_SPI1_RX 13 /* SPI1 Receive */
+#define XDMAC0_CH_SPI2_TX 14 /* SPI2 Transmit */
+#define XDMAC0_CH_SPI2_RX 15 /* SPI2 Receive */
+#define XDMAC0_CH_USART2_TX 16 /* USART2 Transmit */
+#define XDMAC0_CH_USART2_RX 17 /* USART2 Receive */
+#define XDMAC0_CH_USART3_TX 18 /* USART3 Transmit */
+#define XDMAC0_CH_USART3_RX 19 /* USART3 Receive */
+#define XDMAC0_CH_USART4_TX 20 /* USART4 Transmit */
+#define XDMAC0_CH_USART4_RX 21 /* USART4 Receive */
+#define XDMAC0_CH_UART0_TX 22 /* UART0 Transmit */
+#define XDMAC0_CH_UART0_RX 23 /* UART0 Receive */
+#define XDMAC0_CH_UART1_TX 24 /* UART1 Transmit */
+#define XDMAC0_CH_UART1_RX 25 /* UART1 Receive */
+#define XDMAC0_CH_SSC0_TX 26 /* SSC0 Transmit */
+#define XDMAC0_CH_SSC0_RX 27 /* SSC0 Receive */
+#define XDMAC0_CH_SSC1_TX 28 /* SSC1 Transmit */
+#define XDMAC0_CH_SSC1_RX 29 /* SSC1 Receive */
+#define XDMAC0_CH_DBGU_TX 30 /* DBGU Transmit */
+#define XDMAC0_CH_DBGU_RX 31 /* DBGU Receive */
+#define XDMAC0_CH_ADC_RX 32 /* ADC Receive */
+#define XDMAC0_CH_SMD_TX 33 /* SMD Transmit */
+#define XDMAC0_CH_SMD_RX 34 /* SMD Receive */
+#define XDMAC0_CH_USART0_TX 36 /* USART0 Transmit */
+#define XDMAC0_CH_USART0_RX 37 /* USART0 Receive */
+#define XDMAC0_CH_USART1_TX 38 /* USART1 Transmit */
+#define XDMAC0_CH_USART1_RX 39 /* USART1 Receive */
+#define XDMAC0_CH_AES_RX 40 /* AES Receive */
+#define XDMAC0_CH_AES_TX 41 /* AES Transmit */
+#define XDMAC0_CH_TDES_TX 42 /* TDES Transmit */
+#define XDMAC0_CH_TDES_RX 43 /* TDES Receive */
+#define XDMAC0_CH_SHA_TX 44 /* SHA Transmit */
+#define XDMAC0_CH_CATB_TX 46 /* CATB Transmit */
+#define XDMAC0_CH_CATB_RX 47 /* CATB Receive */
+
+/* XDMA Controller 1 Channel Definitions */
+
+#define XDMAC1_CH_HSMCI0 0 /* HSMCI0 Receive/Transmit */
+#define XDMAC1_CH_HSMCI1 1 /* HSMCI1 Receive/Transmit */
+#define XDMAC1_CH_TWI0_TX 2 /* TWI0 Transmit */
+#define XDMAC1_CH_TWI0_RX 3 /* TWI0 Receive */
+#define XDMAC1_CH_TWI1_TX 4 /* TWI1 Transmit */
+#define XDMAC1_CH_TWI1_RX 5 /* TWI1 Receive */
+#define XDMAC1_CH_TWI2_TX 6 /* TWI2 Transmit */
+#define XDMAC1_CH_TWI2_RX 7 /* TWI2 Receive */
+#define XDMAC1_CH_TWI3_TX 8 /* TWI3 Transmit */
+#define XDMAC1_CH_TWI3_RX 9 /* TWI3 Receive */
+#define XDMAC1_CH_SPI0_TX 10 /* SPI0 Transmit */
+#define XDMAC1_CH_SPI0_RX 11 /* SPI0 Receive */
+#define XDMAC1_CH_SPI1_TX 12 /* SPI1 Transmit */
+#define XDMAC1_CH_SPI1_RX 13 /* SPI1 Receive */
+#define XDMAC1_CH_SPI2_TX 14 /* SPI2 Transmit */
+#define XDMAC1_CH_SPI2_RX 15 /* SPI2 Receive */
+#define XDMAC1_CH_USART2_TX 16 /* USART2 Transmit */
+#define XDMAC1_CH_USART2_RX 17 /* USART2 Receive */
+#define XDMAC1_CH_USART3_TX 18 /* USART3 Transmit */
+#define XDMAC1_CH_USART3_RX 19 /* USART3 Receive */
+#define XDMAC1_CH_USART4_TX 20 /* USART4 Transmit */
+#define XDMAC1_CH_USART4_RX 21 /* USART4 Receive */
+#define XDMAC1_CH_UART0_TX 22 /* UART0 Transmit */
+#define XDMAC1_CH_UART0_RX 23 /* UART0 Receive */
+#define XDMAC1_CH_UART1_TX 24 /* UART1 Transmit */
+#define XDMAC1_CH_UART1_RX 25 /* UART1 Receive */
+#define XDMAC1_CH_SSC0_TX 26 /* SSC0 Transmit */
+#define XDMAC1_CH_SSC0_RX 27 /* SSC0 Receive */
+#define XDMAC1_CH_SSC1_TX 28 /* SSC1 Transmit */
+#define XDMAC1_CH_SSC1_RX 29 /* SSC1 Receive */
+#define XDMAC1_CH_DBGU_TX 30 /* DBGU Transmit */
+#define XDMAC1_CH_DBGU_RX 31 /* DBGU Receive */
+#define XDMAC1_CH_ADC_RX 32 /* ADC Receive */
+#define XDMAC1_CH_SMD_TX 33 /* SMD Transmit */
+#define XDMAC1_CH_SMD_RX 34 /* SMD Receive */
#endif /* __ARCH_ARM_SRC_SAMA5_CHIP_SAM_XDMAC_H */
diff --git a/nuttx/arch/arm/src/sama5/sam_xdmac.c b/nuttx/arch/arm/src/sama5/sam_xdmac.c
new file mode 100644
index 000000000..5c99be01b
--- /dev/null
+++ b/nuttx/arch/arm/src/sama5/sam_xdmac.c
@@ -0,0 +1,2621 @@
+/****************************************************************************
+ * arch/arm/src/sama5/sam3u_xdmac.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <semaphore.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <arch/irq.h>
+
+#include "up_arch.h"
+#include "cache.h"
+#include "up_internal.h"
+#include "os_internal.h"
+
+#include "chip.h"
+#include "sam_dmac.h"
+#include "sam_periphclks.h"
+#include "sam_memories.h"
+#include "chip/sam_pmc.h"
+#include "chip/sam_xdmac.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* All of the currently supported SAMA5 chips support two DMA controllers
+ * of 8 DMA Channels each.
+ */
+
+#if SAM_NDMAC < 1
+# undef CONFIG_SAMA5_XDMAC1
+# undef CONFIG_SAMA5_XDMAC0
+#elif SAM_NDMAC < 2
+# undef CONFIG_SAMA5_XDMAC1
+#endif
+
+/* Condition out the whole file unless DMA is selected in the configuration */
+
+#if defined(CONFIG_SAMA5_XDMAC0) || defined(CONFIG_SAMA5_XDMAC1)
+
+/* If SAMA5 DMA support is enabled, then OS DMA support should also be
+ * enabled
+ */
+
+#ifndef CONFIG_ARCH_DMA
+# warning "SAMA5 DMA enabled but CONFIG_ARCH_DMA disabled"
+#endif
+
+/* Check the number of link list descriptors to allocate */
+
+#ifndef CONFIG_SAMA5_NLLDESC
+# define CONFIG_SAMA5_NLLDESC SAM_NDMACHAN
+#endif
+
+#if CONFIG_SAMA5_NLLDESC < SAM_NDMACHAN
+# warning "At least SAM_NDMACHAN descriptors must be allocated"
+
+# undef CONFIG_SAMA5_NLLDESC
+# define CONFIG_SAMA5_NLLDESC SAM_NDMACHAN
+#endif
+
+/* Register values **********************************************************/
+
+#define XDMAC_CH_CTRLB_BOTHDSCR \
+ (XDMAC_CH_CTRLB_SRCDSCR | XDMAC_CH_CTRLB_DSTDSCR)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This structure maps a peripheral ID to an DMA channel */
+
+struct sam_pidmap_s
+{
+ uint8_t pid; /* Peripheral identifier */
+ uint8_t pchan; /* DMA channel */
+};
+
+/* This structure descibes one DMA channel */
+
+struct sam_xdmach_s
+{
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ uint8_t xdmac; /* DMA controller number (0-1) */
+#endif
+ uint8_t chan; /* DMA channel number (0-6) */
+ bool inuse; /* TRUE: The DMA channel is in use */
+ bool rx; /* TRUE: Peripheral to memory transfer */
+ uint32_t flags; /* DMA channel flags */
+ uint32_t base; /* DMA register channel base address */
+ uint32_t cfg; /* Pre-calculated CFG register for transfer */
+ dma_callback_t callback; /* Callback invoked when the DMA completes */
+ void *arg; /* Argument passed to callback function */
+ uint32_t rxaddr; /* RX memory address */
+ size_t rxsize; /* Size of RX memory region */
+ struct dma_linklist_s *llhead; /* DMA link list head */
+ struct dma_linklist_s *lltail; /* DMA link list head */
+};
+
+/* This structure describes the stae of one DMA controller */
+
+struct sam_xdmac_s
+{
+ /* These semaphores protect the DMA channel and descriptor tables */
+
+ sem_t chsem; /* Protects channel table */
+ sem_t dsem; /* Protects descriptor table */
+ uint32_t base; /* DMA register channel base address */
+
+ /* This array describes the available link list descriptors */
+
+ struct dma_linklist_s *desc;
+
+ /* This array describes each DMA channel */
+
+ struct sam_xdmach_s *xdmach;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* CTRLA field lookups */
+
+static const uint32_t g_chanwidth[4] =
+{
+ XDMACH_CC_DWIDTH_BYTE,
+ XDMACH_CC_DWIDTH_HWORD,
+ XDMACH_CC_DWIDTH_WORD,
+ XDMACH_CC_DWIDTH_DWORD
+};
+
+static const uint32_t g_fifocfg[3] =
+{
+ XDMAC_CH_CFG_FIFOCFG_ALAP,
+ XDMAC_CH_CFG_FIFOCFG_HALF,
+ XDMAC_CH_CFG_FIFOCFG_ASAP
+};
+
+/* These tables map peripheral IDs to channels. A lookup is performed
+ * before each DMA transfer in order to map the peripheral IDs to the
+ * correct channel. This must be done because the channel can change with
+ * the direction of the transfer.
+ */
+
+#ifdef CONFIG_SAMA5_XDMAC0
+/* DMA controller 0, RX DMA: */
+
+static const struct sam_pidmap_s g_xdmac0_rxchan[] =
+{
+ { SAM_PID_HSMCI0, XDMAC0_CH_HSMCI0 }, /* HSMCI0 Receive/Transmit */
+ { SAM_PID_HSMCI1, XDMAC0_CH_HSMCI1 }, /* HSMCI1 Receive/Transmit */
+ { SAM_PID_TWI0, XDMAC0_CH_TWI0_RX }, /* TWI0 Receive */
+ { SAM_PID_TWI1, XDMAC0_CH_TWI1_RX }, /* TWI1 Receive */
+ { SAM_PID_TWI2, XDMAC0_CH_TWI2_RX }, /* TWI2 Receive */
+ { SAM_PID_TWI3, XDMAC0_CH_TWI3_RX }, /* TWI3 Receive */
+ { SAM_PID_SPI0, XDMAC0_CH_SPI0_RX }, /* SPI0 Receive */
+ { SAM_PID_SPI1, XDMAC0_CH_SPI1_RX }, /* SPI1 Receive */
+ { SAM_PID_SPI2, XDMAC0_CH_SPI2_RX }, /* SPI2 Receive */
+ { SAM_PID_USART2, XDMAC0_CH_USART2_RX }, /* USART2 Receive */
+ { SAM_PID_USART3, XDMAC0_CH_USART3_RX }, /* USART3 Receive */
+ { SAM_PID_USART4, XDMAC0_CH_USART4_RX }, /* USART4 Receive */
+ { SAM_PID_UART0, XDMAC0_CH_UART0_RX }, /* UART0 Receive */
+ { SAM_PID_UART1, XDMAC0_CH_UART1_RX }, /* UART1 Receive */
+ { SAM_PID_SSC0, XDMAC0_CH_SSC0_RX }, /* SSC0 Receive */
+ { SAM_PID_SSC1, XDMAC0_CH_SSC1_RX }, /* SSC1 Receive */
+ { SAM_PID_DBGU, XDMAC0_CH_DBGU_RX }, /* DBGU Receive */
+ { SAM_PID_ADC, XDMAC0_CH_ADC_RX }, /* ADC Receive */
+ { SAM_PID_SMD, XDMAC0_CH_SMD_RX }, /* SMD Receive */
+ { SAM_PID_USART0, XDMAC0_CH_USART0_RX }, /* USART0 Receive */
+ { SAM_PID_USART1, XDMAC0_CH_USART1_RX }, /* USART1 Receive */
+ { SAM_PID_AES, XDMAC0_CH_AES_RX }, /* AES Receive */
+ { SAM_PID_TDES, XDMAC0_CH_TDES_RX }, /* TDES Receive */
+ { SAM_PID_CATB, XDMAC0_CH_CATB_RX }, /* CATB Receive */
+};
+#define NXDMAC0_RXCHANNELS (sizeof(g_xdmac0_rxchan) / sizeof(struct sam_pidmap_s))
+
+/* DMA controller 0, TX DMA: */
+
+static const struct sam_pidmap_s g_xdmac0_txchan[] =
+{
+ { SAM_PID_HSMCI0, XDMAC0_CH_HSMCI0 }, /* HSMCI0 Receive/Transmit */
+ { SAM_PID_HSMCI1, XDMAC0_CH_HSMCI1 }, /* HSMCI1 Receive/Transmit */
+ { SAM_PID_TWI0, XDMAC0_CH_TWI0_TX }, /* TWI0 Transmit */
+ { SAM_PID_TWI1, XDMAC0_CH_TWI1_TX }, /* TWI1 Transmit */
+ { SAM_PID_TWI2, XDMAC0_CH_TWI2_TX }, /* TWI2 Transmit */
+ { SAM_PID_TWI3, XDMAC0_CH_TWI3_TX }, /* TWI3 Transmit */
+ { SAM_PID_SPI0, XDMAC0_CH_SPI0_TX }, /* SPI0 Transmit */
+ { SAM_PID_SPI1, XDMAC0_CH_SPI1_TX }, /* SPI1 Transmit */
+ { SAM_PID_SPI2, XDMAC0_CH_SPI2_TX }, /* SPI2 Transmit */
+ { SAM_PID_USART2, XDMAC0_CH_USART2_TX }, /* USART2 Transmit */
+ { SAM_PID_USART3, XDMAC0_CH_USART3_TX }, /* USART3 Transmit */
+ { SAM_PID_USART4, XDMAC0_CH_USART4_TX }, /* USART4 Transmit */
+ { SAM_PID_UART0, XDMAC0_CH_UART0_TX }, /* UART0 Transmit */
+ { SAM_PID_UART1, XDMAC0_CH_UART1_TX }, /* UART1 Transmit */
+ { SAM_PID_SSC0, XDMAC0_CH_SSC0_TX }, /* SSC0 Transmit */
+ { SAM_PID_SSC1, XDMAC0_CH_SSC1_TX }, /* SSC1 Transmit */
+ { SAM_PID_DBGU, XDMAC0_CH_DBGU_TX }, /* DBGU Transmit */
+ { SAM_PID_SMD, XDMAC0_CH_SMD_TX }, /* SMD Transmit */
+ { SAM_PID_USART0, XDMAC0_CH_USART0_TX }, /* USART0 Transmit */
+ { SAM_PID_USART1, XDMAC0_CH_USART1_TX }, /* USART1 Transmit */
+ { SAM_PID_AES, XDMAC0_CH_AES_TX }, /* AES Transmit */
+ { SAM_PID_TDES, XDMAC0_CH_TDES_TX }, /* TDES Transmit */
+ { SAM_PID_SHA, XDMAC0_CH_SHA_TX }, /* SHA Transmit */
+ { SAM_PID_CATB, XDMAC0_CH_CATB_TX }, /* CATB Transmit */
+};
+#define NXDMAC0_TXCHANNELS (sizeof(g_xdmac0_txchan) / sizeof(struct sam_pidmap_s))
+#endif
+
+#ifdef CONFIG_SAMA5_XDMAC1
+/* DMA controller 1, RX DMA: */
+
+static const struct sam_pidmap_s g_xdmac1_rxchan[] =
+{
+ { SAM_PID_HSMCI0, XDMAC1_CH_HSMCI0 }, /* HSMCI0 Receive/Transmit */
+ { SAM_PID_HSMCI1, XDMAC1_CH_HSMCI1 }, /* HSMCI1 Receive/Transmit */
+ { SAM_PID_TWI0, XDMAC1_CH_TWI0_RX }, /* TWI0 Receive */
+ { SAM_PID_TWI1, XDMAC1_CH_TWI1_RX }, /* TWI1 Receive */
+ { SAM_PID_TWI2, XDMAC1_CH_TWI2_RX }, /* TWI2 Receive */
+ { SAM_PID_TWI3, XDMAC1_CH_TWI3_RX }, /* TWI3 Receive */
+ { SAM_PID_SPI0, XDMAC1_CH_SPI0_RX }, /* SPI0 Receive */
+ { SAM_PID_SPI1, XDMAC1_CH_SPI1_RX }, /* SPI1 Receive */
+ { SAM_PID_SPI2, XDMAC1_CH_SPI2_RX }, /* SPI2 Receive */
+ { SAM_PID_USART2, XDMAC1_CH_USART2_RX }, /* USART2 Receive */
+ { SAM_PID_USART3, XDMAC1_CH_USART3_RX }, /* USART3 Receive */
+ { SAM_PID_USART4, XDMAC1_CH_USART4_RX }, /* USART4 Receive */
+ { SAM_PID_UART0, XDMAC1_CH_UART0_RX }, /* UART0 Receive */
+ { SAM_PID_UART1, XDMAC1_CH_UART1_RX }, /* UART1 Receive */
+ { SAM_PID_SSC0, XDMAC1_CH_SSC0_RX }, /* SSC0 Receive */
+ { SAM_PID_SSC1, XDMAC1_CH_SSC1_RX }, /* SSC1 Receive */
+ { SAM_PID_DBGU, XDMAC1_CH_DBGU_RX }, /* DBGU Receive */
+ { SAM_PID_ADC, XDMAC1_CH_ADC_RX }, /* ADC Receive */
+ { SAM_PID_SMD, XDMAC1_CH_SMD_RX }, /* SMD Receive */
+
+};
+#define NXDMAC1_RXCHANNELS (sizeof(g_xdmac1_rxchan) / sizeof(struct sam_pidmap_s))
+
+/* DMA controller 1, TX DMA: */
+
+static const struct sam_pidmap_s g_xdmac1_txchan[] =
+{
+ { SAM_PID_HSMCI0, XDMAC1_CH_HSMCI0 }, /* HSMCI0 Receive/Transmit */
+ { SAM_PID_HSMCI1, XDMAC1_CH_HSMCI1 }, /* HSMCI1 Receive/Transmit */
+ { SAM_PID_TWI0, XDMAC1_CH_TWI0_TX }, /* TWI0 Transmit */
+ { SAM_PID_TWI1, XDMAC1_CH_TWI1_TX }, /* TWI1 Transmit */
+ { SAM_PID_TWI2, XDMAC1_CH_TWI2_TX }, /* TWI2 Transmit */
+ { SAM_PID_TWI3, XDMAC1_CH_TWI3_TX }, /* TWI3 Transmit */
+ { SAM_PID_SPI0, XDMAC1_CH_SPI0_TX }, /* SPI0 Transmit */
+ { SAM_PID_SPI1, XDMAC1_CH_SPI1_TX }, /* SPI1 Transmit */
+ { SAM_PID_SPI2, XDMAC1_CH_SPI2_TX }, /* SPI2 Transmit */
+ { SAM_PID_USART2, XDMAC1_CH_USART2_TX }, /* USART2 Transmit */
+ { SAM_PID_USART3, XDMAC1_CH_USART3_TX }, /* USART3 Transmit */
+ { SAM_PID_USART4, XDMAC1_CH_USART4_TX }, /* USART4 Transmit */
+ { SAM_PID_UART0, XDMAC1_CH_UART0_TX }, /* UART0 Transmit */
+ { SAM_PID_UART1, XDMAC1_CH_UART1_TX }, /* UART1 Transmit */
+ { SAM_PID_SSC0, XDMAC1_CH_SSC0_TX }, /* SSC0 Transmit */
+ { SAM_PID_SSC1, XDMAC1_CH_SSC1_TX }, /* SSC1 Transmit */
+ { SAM_PID_DBGU, XDMAC1_CH_DBGU_TX }, /* DBGU Transmit */
+ { SAM_PID_SMD, XDMAC1_CH_SMD_TX }, /* SMD Transmit */
+};
+#define NXDMAC1_TXCHANNELS (sizeof(g_xdmac1_txchan) / sizeof(struct sam_pidmap_s))
+#endif
+
+#ifdef CONFIG_SAMA5_XDMAC0
+
+/* This array describes the available link list descriptors */
+
+struct dma_linklist_s g_desc0[CONFIG_SAMA5_NLLDESC];
+
+/* This array describes the state of each XDMAC0 channel 0 */
+
+static struct sam_xdmach_s g_xdmach0[SAM_NDMACHAN] =
+{
+#if SAM_NDMACHAN > 0
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 0,
+ .base = SAM_XDMAC0_CH0_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 1
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 1,
+ .base = SAM_XDMAC0_CH1_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 2
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 2,
+ .base = SAM_XDMAC0_CH2_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 3
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 3,
+ .base = SAM_XDMAC0_CH3_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 4
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 4,
+ .base = SAM_XDMAC0_CH4_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 5
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 5,
+ .base = SAM_XDMAC0_CH5_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 6
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 6,
+ .base = SAM_XDMAC0_CH6_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 7
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 7,
+ .base = SAM_XDMAC0_CH7_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 8
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 8,
+ .base = SAM_XDMAC0_CH8_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 9
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 9,
+ .base = SAM_XDMAC0_CH9_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 10
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 10,
+ .base = SAM_XDMAC0_CH10_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 11
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 11,
+ .base = SAM_XDMAC0_CH11_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 12
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 12,
+ .base = SAM_XDMAC0_CH12_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 13
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 13,
+ .base = SAM_XDMAC0_CH13_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 14
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 14,
+ .base = SAM_XDMAC0_CH14_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 15
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 0,
+#endif
+ .chan = 15,
+ .base = SAM_XDMAC0_CH15_BASE,
+ }
+#endif
+};
+
+/* This describes the overall state of DMA controller 0 */
+
+static struct sam_xdmac_s g_xdmac0 =
+{
+ /* XDMAC 0 base address */
+
+ .base = SAM_XDMAC0_VBASE,
+
+ /* This array describes the available link list descriptors */
+
+ .desc = g_desc0,
+
+ /* This array describes each DMA channel */
+
+ .xdmach = g_xdmach0,
+};
+
+#endif /* CONFIG_SAMA5_XDMAC0 */
+
+/* This array describes the state of DMA controller 1 */
+
+#ifdef CONFIG_SAMA5_XDMAC1
+/* This array describes the available link list descriptors */
+
+struct dma_linklist_s g_desc1[CONFIG_SAMA5_NLLDESC];
+
+static struct sam_xdmach_s g_xdmach1[SAM_NDMACHAN] =
+{
+#if SAM_NDMACHAN > 0
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 0,
+ .base = SAM_XDMAC1_CH0_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 1
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 1,
+ .base = SAM_XDMAC1_CH1_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 2
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 2,
+ .base = SAM_XDMAC1_CH2_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 3
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 3,
+ .base = SAM_XDMAC1_CH3_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 4
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 4,
+ .base = SAM_XDMAC1_CH4_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 5
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 5,
+ .base = SAM_XDMAC1_CH5_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 6
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 6,
+ .base = SAM_XDMAC1_CH6_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 7
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 7,
+ .base = SAM_XDMAC1_CH7_BASE,
+ },
+#endif
+#if SAM_NDMACHAN > 8
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 8,
+ .base = SAM_XDMAC1_CH8_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 9
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 9,
+ .base = SAM_XDMAC1_CH9_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 10
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 10,
+ .base = SAM_XDMAC1_CH10_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 11
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 11,
+ .base = SAM_XDMAC1_CH11_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 12
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 12,
+ .base = SAM_XDMAC1_CH12_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 13
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 13,
+ .base = SAM_XDMAC1_CH13_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 14
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 14,
+ .base = SAM_XDMAC1_CH14_BASE,
+ }
+#endif
+#if SAM_NDMACHAN > 15
+ {
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ .xdmac = 1,
+#endif
+ .chan = 15,
+ .base = SAM_XDMAC1_CH15_BASE,
+ }
+#endif
+};
+
+/* This describes the overall state of DMA controller 1 */
+
+static struct sam_xdmac_s g_xdmac1 =
+{
+ /* XDMAC 0 base address */
+
+ .base = SAM_XDMAC1_VBASE,
+
+ /* This array describes the available link list descriptors */
+
+ .desc = g_desc1,
+
+ /* This array describes each DMA channel */
+
+ .xdmach = g_xdmach1,
+};
+
+#endif /* CONFIG_SAMA5_XDMAC1 */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_takechsem() and sam_givechsem()
+ *
+ * Description:
+ * Used to get exclusive access to the DMA channel table
+ *
+ ****************************************************************************/
+
+static void sam_takechsem(struct sam_xdmac_s *xdmac)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&xdmac->chsem) != 0)
+ {
+ /* The only case that an error should occur here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+static inline void sam_givechsem(struct sam_xdmac_s *xdmac)
+{
+ (void)sem_post(&xdmac->chsem);
+}
+
+/****************************************************************************
+ * Name: sam_takedsem() and sam_givedsem()
+ *
+ * Description:
+ * Used to wait for availability of descriptors in the descriptor table.
+ *
+ ****************************************************************************/
+
+static void sam_takedsem(struct sam_xdmac_s *xdmac)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&xdmac->dsem) != 0)
+ {
+ /* The only case that an error should occur here is if the wait was
+ * awakened by a signal.
+ */
+
+ ASSERT(errno == EINTR);
+ }
+}
+
+static inline void sam_givedsem(struct sam_xdmac_s *xdmac)
+{
+ (void)sem_post(&xdmac->dsem);
+}
+
+/****************************************************************************
+ * Name: sam_getdmac
+ *
+ * Description:
+ * Read a global XDMAC register
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_getdmac(struct sam_xdmac_s *xdmac,
+ unsigned int offset)
+{
+ return getreg32(xdmac->base + offset);
+}
+
+/****************************************************************************
+ * Name: sam_putdmac
+ *
+ * Description:
+ * Write a value to a global XDMAC register
+ *
+ ****************************************************************************/
+
+static inline void sam_putdmac(struct sam_xdmac_s *xdmac, uint32_t value,
+ unsigned int offset)
+{
+ putreg32(value, xdmac->base + offset);
+}
+
+/****************************************************************************
+ * Name: sam_getdmach
+ *
+ * Description:
+ * Read a XDMAC channel register
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_getdmach(struct sam_xdmach_s *xdmach,
+ unsigned int offset)
+{
+ return getreg32(xdmach->base + offset);
+}
+
+/****************************************************************************
+ * Name: sam_putdmach
+ *
+ * Description:
+ * Write a value to a XDMAC channel register
+ *
+ ****************************************************************************/
+
+static inline void sam_putdmach(struct sam_xdmach_s *xdmach, uint32_t value,
+ unsigned int offset)
+{
+ putreg32(value, xdmach->base + offset);
+}
+
+/****************************************************************************
+ * Name: sam_controller
+ *
+ * Description:
+ * Given a DMA channel instance, return a pointer to the parent DMA
+ * controller instance.
+ *
+ ****************************************************************************/
+
+static inline struct sam_xdmac_s *sam_controller(struct sam_xdmach_s *xdmach)
+{
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ return xdmach->xdmac ? &g_xdmac1 : &g_xdmac0;
+#elif defined(CONFIG_SAMA5_XDMAC0)
+ return &g_xdmac0;
+#else
+ return &g_xdmac1;
+#endif
+}
+
+/****************************************************************************
+ * Name: sam_fifocfg
+ *
+ * Description:
+ * Decode the FIFO config from the flags
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_fifocfg(struct sam_xdmach_s *xdmach)
+{
+ unsigned int ndx;
+
+ ndx = (xdmach->flags & DMACH_FLAG_FIFOCFG_MASK) >> DMACH_FLAG_FIFOCFG_SHIFT;
+ DEBUGASSERT(ndx < 3);
+ return g_fifocfg[ndx];
+}
+
+
+/****************************************************************************
+ * Name: sam_channel, sam_source_channel, and sam_sink_channel
+ *
+ * Description:
+ * Return the RX or TX channel associated with a PID. As a clarification:
+ *
+ * The source channel refers to the source of data for the DMA. This is,
+ * either (1) memory for a TX DMA or (2) a peripheral register for an RX
+ * DMA.
+ *
+ * The sink channel is the recipient of the DMA data. This is either
+ * (1) memory for an RX DMA, or (2) a peripheral register for a TX DMA.
+ *
+ ****************************************************************************/
+
+static uint8_t sam_channel(uint8_t pid, const struct sam_pidmap_s *table,
+ unsigned int nentries)
+{
+ int i;
+
+ /* Search until either the entry with the matching PID is found or until
+ * all of the table entries have been examined without finding the PID.
+ */
+
+ for (i = 0; i < nentries; i++, table++)
+ {
+ if (table->pid == pid)
+ {
+ return table->pchan;
+ }
+ }
+
+ dmadbg("No channel found for pid %d\n", pid);
+ DEBUGPANIC();
+ return 0x3f;
+}
+
+static uint32_t sam_source_channel(struct sam_xdmach_s *xdmach, uint8_t pid,
+ bool isperiph)
+{
+ const struct sam_pidmap_s *table;
+ unsigned int nentries;
+
+ if (!isperiph)
+ {
+ /* The source is memory, not a peripheral. */
+
+ return 0x3f;
+ }
+ else
+
+#ifdef CONFIG_SAMA5_XDMAC0
+#ifdef CONFIG_SAMA5_XDMAC1
+ if (xdmach->xdmac == 0)
+#endif
+ {
+ /* Use the XDMAC0 lookup table */
+
+ table = g_xdmac0_rxchan;
+ nentries = NXDMAC0_RXCHANNELS;
+ }
+#endif
+
+#ifdef CONFIG_SAMA5_XDMAC1
+#ifdef CONFIG_SAMA5_XDMAC0
+ else
+#endif
+ {
+ /* Use the XDMAC1 lookup table */
+
+ table = g_xdmac1_rxchan;
+ nentries = NXDMAC1_RXCHANNELS;
+ }
+#endif
+
+ return (uint32_t)sam_channel(pid, table, nentries);
+}
+
+static uint32_t sam_sink_channel(struct sam_xdmach_s *xdmach, uint8_t pid,
+ bool isperiph)
+{
+ const struct sam_pidmap_s *table;
+ unsigned int nentries;
+
+ if (!isperiph)
+ {
+ /* The source is memory, not a peripheral. */
+
+ return 0x3f;
+ }
+ else
+
+#ifdef CONFIG_SAMA5_XDMAC0
+#ifdef CONFIG_SAMA5_XDMAC1
+ if (xdmach->xdmac == 0)
+#endif
+ {
+ /* Use the XDMAC0 lookup table */
+
+ table = g_xdmac0_txchan;
+ nentries = NXDMAC0_TXCHANNELS;
+ }
+#endif
+
+#ifdef CONFIG_SAMA5_XDMAC1
+#ifdef CONFIG_SAMA5_XDMAC0
+ else
+#endif
+ {
+ /* Use the XDMAC1 lookup table */
+
+ table = g_xdmac1_txchan;
+ nentries = NXDMAC1_TXCHANNELS;
+ }
+#endif
+
+ return (uint32_t)sam_channel(pid, table, nentries);
+}
+
+/****************************************************************************
+ * Name: sam_txcfg
+ *
+ * Description:
+ * Decode the flags to get the correct CFG register bit settings for
+ * a transmit (memory to peripheral) transfer.
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_txcfg(struct sam_xdmach_s *xdmach)
+{
+ uint32_t regval;
+ unsigned int pid;
+ unsigned int pchan;
+ bool isperiph;
+
+ /* Set transfer (memory to peripheral) DMA channel configuration register */
+
+ regval = XDMAC_CH_CFG_SOD;
+
+ pid = (xdmach->flags & DMACH_FLAG_MEMPID_MASK) >> DMACH_FLAG_MEMPID_SHIFT;
+ isperiph = ((xdmach->flags & DMACH_FLAG_MEMISPERIPH) != 0);
+ pchan = sam_source_channel(xdmach, pid, isperiph);
+
+ regval |= ((pchan & 0x0f) << XDMAC_CH_CFG_SRCPER_SHIFT);
+ regval |= ((pchan & 0x30) << (XDMAC_CH_CFG_SRCPERMSB_SHIFT-4));
+ regval |= (xdmach->flags & DMACH_FLAG_MEMH2SEL) != 0 ? XDMAC_CH_CFG_SRCH2SEL : 0;
+
+ pid = (xdmach->flags & DMACH_FLAG_PERIPHPID_MASK) >> DMACH_FLAG_PERIPHPID_SHIFT;
+ isperiph = ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0);
+ pchan = sam_sink_channel(xdmach, pid, isperiph);
+
+ regval |= ((pchan & 0x0f) << XDMAC_CH_CFG_DSTPER_SHIFT);
+ regval |= ((pchan & 0x30) << (XDMAC_CH_CFG_DSTPERMSB_SHIFT-4));
+ regval |= (xdmach->flags & DMACH_FLAG_PERIPHH2SEL) != 0 ? XDMAC_CH_CFG_DSTH2SEL : 0;
+
+ regval |= sam_fifocfg(xdmach);
+ return regval;
+}
+
+/****************************************************************************
+ * Name: sam_rxcfg
+ *
+ * Description:
+ * Decode the flags to get the correct CFG register bit settings for
+ * a receive (peripheral to memory) transfer.
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_rxcfg(struct sam_xdmach_s *xdmach)
+{
+ uint32_t regval;
+ unsigned int pid;
+ unsigned int pchan;
+ bool isperiph;
+
+ /* Set received (peripheral to memory) DMA channel config */
+
+ regval = XDMAC_CH_CFG_SOD;
+
+ pid = (xdmach->flags & DMACH_FLAG_PERIPHPID_MASK) >> DMACH_FLAG_PERIPHPID_SHIFT;
+ isperiph = ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0);
+ pchan = sam_source_channel(xdmach, pid, isperiph);
+
+ regval |= ((pchan & 0x0f) << XDMAC_CH_CFG_SRCPER_SHIFT);
+ regval |= ((pchan & 0x30) << (XDMAC_CH_CFG_SRCPERMSB_SHIFT-4));
+ regval |= (xdmach->flags & DMACH_FLAG_PERIPHH2SEL) != 0 ? XDMAC_CH_CFG_SRCH2SEL : 0;
+
+ pid = (xdmach->flags & DMACH_FLAG_MEMPID_MASK) >> DMACH_FLAG_MEMPID_SHIFT;
+ isperiph = ((xdmach->flags & DMACH_FLAG_MEMISPERIPH) != 0);
+ pchan = sam_sink_channel(xdmach, pid, isperiph);
+
+ regval |= ((pchan & 0x0f) << XDMAC_CH_CFG_DSTPER_SHIFT);
+ regval |= ((pchan & 0x30) << (XDMAC_CH_CFG_DSTPERMSB_SHIFT-4));
+ regval |= (xdmach->flags & DMACH_FLAG_MEMH2SEL) != 0 ? XDMAC_CH_CFG_DSTH2SEL : 0;
+
+ regval |= sam_fifocfg(xdmach);
+ return regval;
+}
+
+/****************************************************************************
+ * Name: sam_txctrlabits
+ *
+ * Description:
+ * Decode the flags to get the correct CTRLA register bit settings for
+ * a transmit (memory to peripheral) transfer. These are only the "fixed"
+ * CTRLA values and need to be updated with the actual transfer size before
+ * being written to CTRLA sam_txctrla).
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_txctrlabits(struct sam_xdmach_s *xdmach)
+{
+ uint32_t regval;
+ unsigned int ndx;
+ unsigned int chunksize;
+
+ DEBUGASSERT(xdmach);
+
+ /* Since this is a transmit, the source is described by the memory selections.
+ * Set the source width (memory width).
+ */
+
+ ndx = (xdmach->flags & DMACH_FLAG_MEMWIDTH_MASK) >> DMACH_FLAG_MEMWIDTH_SHIFT;
+ DEBUGASSERT(ndx < 4);
+ regval = g_chanwidth[ndx];
+
+ /* Set the source chunk size (memory chunk size) */
+
+ chunksize = (xdmach->flags & DMACH_FLAG_MEMCHUNKSIZE_MASK)
+ >> DMACH_FLAG_MEMCHUNKSIZE_SHIFT;
+ regval |= chunksize << XDMAC_CH_CTRLA_SCSIZE_SHIFT;
+
+ /* Since this is a transmit, the destination is described by the peripheral selections.
+ * Set the destination width (peripheral width).
+ */
+
+ ndx = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >> DMACH_FLAG_PERIPHWIDTH_SHIFT;
+ DEBUGASSERT(ndx < 4);
+ regval |= g_chanwidth[ndx];
+
+ /* Set the destination chunk size (peripheral chunk size) */
+
+ chunksize = (xdmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK)
+ >> DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT;
+ regval |= chunksize << XDMAC_CH_CTRLA_DCSIZE_SHIFT;
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: sam_maxtxtransfer
+ *
+ * Description:
+ * Maximum number of bytes that can be sent in on transfer
+ *
+ ****************************************************************************/
+
+static size_t sam_maxtxtransfer(struct sam_xdmach_s *xdmach)
+{
+ unsigned int srcwidth;
+ size_t maxtransfer;
+
+ /* Get the maximum transfer size in bytes. BTSIZE is "the number of
+ * transfers to be performed, that is, for writes it refers to the number
+ * of source width transfers to perform when XDMAC is flow controller. For
+ * Reads, BTSIZE refers to the number of transfers completed on the Source
+ * Interface. ..."
+ */
+
+ srcwidth = (xdmach->flags & DMACH_FLAG_MEMWIDTH_MASK)
+ >> DMACH_FLAG_MEMWIDTH_SHIFT;
+
+ switch (srcwidth)
+ {
+ default:
+ case 0: /* 8 bits, 1 byte */
+ maxtransfer = XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+
+ case 1: /* 16 bits, 2 bytes */
+ maxtransfer = 2 * XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+
+ case 2: /* 32 bits 4 bytes */
+ maxtransfer = 4 * XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+
+ case 3: /* 64 bits, 8 bytes */
+ maxtransfer = 8 * XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+ }
+
+ return maxtransfer;
+}
+
+/****************************************************************************
+ * Name: sam_ntxtransfers
+ *
+ * Description:
+ * Number of TX transfers via DMA
+ *
+ ****************************************************************************/
+
+static uint32_t sam_ntxtransfers(struct sam_xdmach_s *xdmach, uint32_t dmasize)
+{
+ unsigned int srcwidth;
+
+ /* Adjust the source transfer size for the source chunk size (memory
+ * chunk size). BTSIZE is "the number of transfers to be performed, that
+ * is, for writes it refers to the number of source width transfers
+ * to perform when XDMAC is flow controller. For Reads, BTSIZE refers to
+ * the number of transfers completed on the Source Interface. ..."
+ */
+
+ srcwidth = (xdmach->flags & DMACH_FLAG_MEMWIDTH_MASK)
+ >> DMACH_FLAG_MEMWIDTH_SHIFT;
+
+ switch (srcwidth)
+ {
+ default:
+ case 0: /* 8 bits, 1 byte */
+ break;
+
+ case 1: /* 16 bits, 2 bytes */
+ dmasize = (dmasize + 1) >> 1;
+ break;
+
+ case 2: /* 32 bits, 4 bytes */
+ dmasize = (dmasize + 3) >> 2;
+ break;
+
+ case 3: /* 64 bits, 8 bytes */
+ dmasize = (dmasize + 7) >> 3;
+ break;
+ }
+
+ return dmasize;
+}
+
+/****************************************************************************
+ * Name: sam_txctrla
+ *
+ * Description:
+ * 'OR' in the variable CTRLA bits
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_txctrla(struct sam_xdmach_s *xdmach,
+ uint32_t ctrla, uint32_t dmasize)
+{
+ uint32_t ntransfers;
+
+ /* Set the buffer transfer size field. This is the number of transfers to
+ * be performed, that is, the number of source width transfers to perform.
+ */
+
+ ntransfers = sam_ntxtransfers(xdmach, dmasize);
+
+ DEBUGASSERT(ntransfers <= XDMAC_CH_CTRLA_BTSIZE_MAX);
+ return (ctrla & ~XDMAC_CH_CTRLA_BTSIZE_MASK) |
+ (ntransfers << XDMAC_CH_CTRLA_BTSIZE_SHIFT);
+}
+
+/****************************************************************************
+ * Name: sam_rxctrlabits
+ *
+ * Description:
+ * Decode the flags to get the correct CTRLA register bit settings for
+ * a read (peripheral to memory) transfer. These are only the "fixed" CTRLA
+ * values and need to be updated with the actual transfer size before being
+ * written to CTRLA sam_rxctrla).
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_rxctrlabits(struct sam_xdmach_s *xdmach)
+{
+ uint32_t regval;
+ unsigned int ndx;
+ unsigned int chunksize;
+
+ DEBUGASSERT(xdmach);
+
+ /* Since this is a receive, the source is described by the peripheral
+ * selections. Set the source width (peripheral width).
+ */
+
+ ndx = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK)
+ >> DMACH_FLAG_PERIPHWIDTH_SHIFT;
+
+ DEBUGASSERT(ndx < 4);
+ regval = g_chanwidth[ndx];
+
+ /* Set the source chunk size (peripheral chunk size) */
+
+ chunksize = (xdmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK)
+ >> DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT;
+ regval |= chunksize << XDMAC_CH_CTRLA_SCSIZE_SHIFT;
+
+ /* Since this is a receive, the destination is described by the memory
+ * selections. Set the destination width (memory width).
+ */
+
+ ndx = (xdmach->flags & DMACH_FLAG_MEMWIDTH_MASK)
+ >> DMACH_FLAG_MEMWIDTH_SHIFT;
+
+ DEBUGASSERT(ndx < 4);
+ regval |= g_chanwidth[ndx];
+
+ /* Set the destination chunk size (memory chunk size) */
+
+ chunksize = (xdmach->flags & DMACH_FLAG_MEMCHUNKSIZE_MASK)
+ >> DMACH_FLAG_MEMCHUNKSIZE_SHIFT;
+ regval |= chunksize << XDMAC_CH_CTRLA_DCSIZE_SHIFT;
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: sam_maxrxtransfer
+ *
+ * Description:
+ * Maximum number of bytes that can be sent in on transfer
+ *
+ ****************************************************************************/
+
+static size_t sam_maxrxtransfer(struct sam_xdmach_s *xdmach)
+{
+ unsigned int srcwidth;
+ size_t maxtransfer;
+
+ /* Get the maximum transfer size in bytes. BTSIZE is "the number of
+ * transfers to be performed, that is, for writes it refers to the number
+ * of source width transfers to perform when XDMAC is flow controller. For
+ * Reads, BTSIZE refers to the number of transfers completed on the Source
+ * Interface. ..."
+ */
+
+ srcwidth = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK)
+ >> DMACH_FLAG_PERIPHWIDTH_SHIFT;
+
+ switch (srcwidth)
+ {
+ default:
+ case 0: /* 8 bits, 1 byte */
+ maxtransfer = XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+
+ case 1: /* 16 bits, 2 bytes */
+ maxtransfer = 2 * XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+
+ case 2: /* 32 bits, 4 bytes */
+ maxtransfer = 4 * XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+
+ case 3: /* 64 bits, 8 bytes */
+ maxtransfer = 8 * XDMAC_CH_CTRLA_BTSIZE_MAX;
+ break;
+ }
+
+ return maxtransfer;
+}
+
+/****************************************************************************
+ * Name: sam_nrxtransfers
+ *
+ * Description:
+ * Number of RX transfers via DMA
+ *
+ ****************************************************************************/
+
+static uint32_t sam_nrxtransfers(struct sam_xdmach_s *xdmach, uint32_t dmasize)
+{
+ unsigned int srcwidth;
+
+ /* Adjust the source transfer size for the source chunk size (peripheral
+ * chunk size). BTSIZE is "the number of transfers to be performed, that
+ * is, for writes it refers to the number of source width transfers
+ * to perform when XDMAC is flow controller. For Reads, BTSIZE refers to
+ * the number of transfers completed on the Source Interface. ..."
+ */
+
+ srcwidth = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK)
+ >> DMACH_FLAG_PERIPHWIDTH_SHIFT;
+
+ switch (srcwidth)
+ {
+ default:
+ case 0: /* 8 bits, 1 byte */
+ break;
+
+ case 1: /* 16 bits, 2 bytes */
+ dmasize = (dmasize + 1) >> 1;
+ break;
+
+ case 2: /* 32 bits, 4 bytes */
+ dmasize = (dmasize + 3) >> 2;
+ break;
+
+ case 3: /* 64 bits, 8 bytes */
+ dmasize = (dmasize + 7) >> 3;
+ break;
+ }
+
+ return dmasize;
+}
+
+/****************************************************************************
+ * Name: sam_rxctrla
+ *
+ * Description:
+ * 'OR' in the variable CTRLA bits
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_rxctrla(struct sam_xdmach_s *xdmach,
+ uint32_t ctrla, uint32_t dmasize)
+{
+ uint32_t ntransfers;
+
+ /* Set the buffer transfer size field. This is the number of transfers to
+ * be performed, that is, the number of source width transfers to perform.
+ */
+
+ ntransfers = sam_nrxtransfers(xdmach, dmasize);
+
+ DEBUGASSERT(ntransfers <= XDMAC_CH_CTRLA_BTSIZE_MAX);
+ return (ctrla & ~XDMAC_CH_CTRLA_BTSIZE_MASK) |
+ (ntransfers << XDMAC_CH_CTRLA_BTSIZE_SHIFT);
+}
+
+/****************************************************************************
+ * Name: sam_txctrlb
+ *
+ * Description:
+ * Decode the flags to get the correct CTRLB register bit settings for
+ * a transmit (memory to peripheral) transfer.
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_txctrlb(struct sam_xdmach_s *xdmach)
+{
+ uint32_t regval;
+ unsigned int ahbif;
+
+ /* Assume that we will not be using the link list and disable the source
+ * and destination descriptors. The default will be single transfer mode.
+ */
+
+ regval = XDMAC_CH_CTRLB_BOTHDSCR | XDMAC_CH_CTRLB_IEN;
+
+ /* Select flow control (even if the channel doesn't support it). The
+ * naming convention from TX is memory to peripheral, but that is really
+ * be determined by bits in the DMA flags.
+ */
+
+ /* Is the memory source really a peripheral? */
+
+ if ((xdmach->flags & DMACH_FLAG_MEMISPERIPH) != 0)
+ {
+ /* Yes.. is the peripheral destination also a peripheral? */
+
+ if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
+ {
+ /* Yes.. Use peripheral-to-peripheral flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_P2P;
+ }
+ else
+ {
+ /* No.. Use peripheral-to-memory flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_P2M;
+ }
+ }
+ else
+ {
+ /* No, the source is memory. Is the peripheral destination a
+ * peripheral
+ */
+
+ if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
+ {
+ /* Yes.. Use memory-to-peripheral flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_M2P;
+ }
+ else
+ {
+ /* No.. Use memory-to-memory flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_M2M;
+ }
+ }
+
+ /* Source ABH layer */
+
+ ahbif = (xdmach->flags & DMACH_FLAG_MEMAHB_MASK) >> DMACH_FLAG_MEMAHB_SHIFT;
+ regval |= (ahbif << XDMAC_CH_CTRLB_SIF_SHIFT);
+
+ /* Select source address incrementing */
+
+ if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) == 0)
+ {
+ regval |= XDMAC_CH_CTRLB_SRCINCR_FIXED;
+ }
+
+ /* Destination ABH layer */
+
+ ahbif = (xdmach->flags & DMACH_FLAG_PERIPHAHB_MASK) >> DMACH_FLAG_PERIPHAHB_SHIFT;
+ regval |= (ahbif << XDMAC_CH_CTRLB_DIF_SHIFT);
+
+ /* Select destination address incrementing */
+
+ if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) == 0)
+ {
+ regval |= XDMAC_CH_CTRLB_DSTINCR_FIXED;
+ }
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: sam_rxctrlb
+ *
+ * Description:
+ * Decode the flags to get the correct CTRLB register bit settings for
+ * a receive (peripheral to memory) transfer.
+ *
+ ****************************************************************************/
+
+static inline uint32_t sam_rxctrlb(struct sam_xdmach_s *xdmach)
+{
+ uint32_t regval;
+ unsigned int ahbif;
+
+ /* Assume that we will not be using the link list and disable the source
+ * and destination descriptors. The default will be single transfer mode.
+ */
+
+ regval = XDMAC_CH_CTRLB_BOTHDSCR | XDMAC_CH_CTRLB_IEN;
+
+ /* Select flow control (even if the channel doesn't support it). The
+ * naming convention from RX is peripheral to memory, but that is really
+ * be determined by bits in the DMA flags.
+ */
+
+ /* Is the peripheral source really a peripheral? */
+
+ if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
+ {
+ /* Yes.. is the memory destination also a peripheral? */
+
+ if ((xdmach->flags & DMACH_FLAG_MEMISPERIPH) != 0)
+ {
+ /* Yes.. Use peripheral-to-peripheral flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_P2P;
+ }
+ else
+ {
+ /* No.. Use peripheral-to-memory flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_P2M;
+ }
+ }
+ else
+ {
+ /* No, the peripheral source is memory. Is the memory destination
+ * a peripheral
+ */
+
+ if ((xdmach->flags & DMACH_FLAG_MEMISPERIPH) != 0)
+ {
+ /* Yes.. Use memory-to-peripheral flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_M2P;
+ }
+ else
+ {
+ /* No.. Use memory-to-memory flow control */
+
+ regval |= XDMAC_CH_CTRLB_FC_M2M;
+ }
+ }
+
+ /* Source ABH layer */
+
+ ahbif = (xdmach->flags & DMACH_FLAG_PERIPHAHB_MASK) >> DMACH_FLAG_PERIPHAHB_SHIFT;
+ regval |= (ahbif << XDMAC_CH_CTRLB_SIF_SHIFT);
+
+ /* Select source address incrementing */
+
+ if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) == 0)
+ {
+ regval |= XDMAC_CH_CTRLB_SRCINCR_FIXED;
+ }
+
+ /* Destination ABH layer */
+
+ ahbif = (xdmach->flags & DMACH_FLAG_MEMAHB_MASK) >> DMACH_FLAG_MEMAHB_SHIFT;
+ regval |= (ahbif << XDMAC_CH_CTRLB_DIF_SHIFT);
+
+ /* Select address incrementing */
+
+ if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) == 0)
+ {
+ regval |= XDMAC_CH_CTRLB_DSTINCR_FIXED;
+ }
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: sam_allocdesc
+ *
+ * Description:
+ * Allocate and add one descriptor to the DMA channel's link list.
+ *
+ * NOTE: link list entries are freed by the DMA interrupt handler.
+ * However, since the setting/clearing of the 'in use' indication is
+ * atomic, no special actions need be performed. It would be a good thing
+ * to add logic to handle the case where all of the entries are exhausted
+ * and we could wait for some to be freed by the interrupt handler.
+ *
+ ****************************************************************************/
+
+static struct dma_linklist_s *
+sam_allocdesc(struct sam_xdmach_s *xdmach, struct dma_linklist_s *prev,
+ uint32_t saddr, uint32_t daddr, uint32_t ctrla, uint32_t ctrlb)
+{
+ struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+ struct dma_linklist_s *desc = NULL;
+ int i;
+
+ /* Sanity check -- saddr == 0 is the indication that the link is unused.
+ * Obviously setting it to zero would break that usage.
+ */
+
+#ifdef CONFIG_DEBUG
+ if (saddr != 0)
+#endif
+ {
+ /* Table a descriptor table semaphore count. When we get one, then there
+ * is at least one free descriptor in the table and it is ours.
+ */
+
+ sam_takedsem(xdmac);
+
+ /* Examine each link list entry to find an available one -- i.e., one
+ * with saddr == 0. That saddr field is set to zero by the DMA transfer
+ * complete interrupt handler. The following should be safe because
+ * that is an atomic operation.
+ */
+
+ sam_takechsem(xdmac);
+ for (i = 0; i < CONFIG_SAMA5_NLLDESC; i++)
+ {
+ if (xdmac->desc[i].saddr == 0)
+ {
+ /* We have it. Initialize the new link list entry */
+
+ desc = &xdmac->desc[i];
+ desc->saddr = saddr; /* Source address */
+ desc->daddr = daddr; /* Destination address */
+ desc->ctrla = ctrla; /* Control A value */
+ desc->ctrlb = ctrlb; /* Control B value */
+ desc->dscr = 0; /* Next descriptor address */
+
+ /* And then hook it at the tail of the link list */
+
+ if (!prev)
+ {
+ /* There is no previous link. This is the new head of
+ * the list
+ */
+
+ DEBUGASSERT(xdmach->llhead == NULL && xdmach->lltail == NULL);
+ xdmach->llhead = desc;
+ }
+ else
+ {
+ DEBUGASSERT(xdmach->llhead != NULL && xdmach->lltail == prev);
+
+ /* When the second link is added to the list, that is the
+ * cue that we are going to do the link list transfer.
+ *
+ * Enable the source and destination descriptor in the link
+ * list entry just before this one. We assume that both
+ * source and destination buffers are non-continuous, but
+ * this should work even if that is not the case.
+ */
+
+ prev->ctrlb &= ~XDMAC_CH_CTRLB_BOTHDSCR;
+
+ /* Link the previous tail to the new tail.
+ * REVISIT: This assumes that the next description is fetched
+ * via AHB IF0.
+ */
+
+ prev->dscr = (uint32_t)desc;
+ }
+
+ /* In any event, this is the new tail of the list. The source
+ * and destination descriptors must be disabled for the last entry
+ * in the link list. */
+
+ desc->ctrlb |= XDMAC_CH_CTRLB_BOTHDSCR;
+ xdmach->lltail = desc;
+
+ /* Assume that we will be doing multple buffer transfers and that
+ * that hardware will be accessing the descriptor via DMA.
+ */
+
+ cp15_clean_dcache((uintptr_t)desc,
+ (uintptr_t)desc + sizeof(struct dma_linklist_s));
+ break;
+ }
+ }
+
+ /* Because we hold a count from the counting semaphore, the above
+ * search loop should always be successful.
+ */
+
+ sam_givechsem(xdmac);
+ DEBUGASSERT(desc != NULL);
+ }
+
+ return desc;
+}
+
+/****************************************************************************
+ * Name: sam_freelinklist
+ *
+ * Description:
+ * Free all descriptors in the DMA channel's link list.
+ *
+ * NOTE: Called from the DMA interrupt handler.
+ *
+ ****************************************************************************/
+
+static void sam_freelinklist(struct sam_xdmach_s *xdmach)
+{
+ struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+ struct dma_linklist_s *desc;
+ uintptr_t paddr;
+
+ /* Get the head of the link list and detach the link list from the DMA
+ * channel
+ */
+
+ desc = xdmach->llhead;
+ xdmach->llhead = NULL;
+ xdmach->lltail = NULL;
+
+ while (desc != NULL)
+ {
+ /* Valid, in-use descriptors never have saddr == 0 */
+
+ DEBUGASSERT(desc->saddr != 0);
+
+ /* Get the physical address of the next descriptor in the list */
+
+ paddr = desc->dscr;
+
+ /* Free the descriptor by simply nullifying it and bumping up the
+ * semaphore count.
+ */
+
+ memset(desc, 0, sizeof(struct dma_linklist_s));
+ sam_givedsem(xdmac);
+
+ /* Get the virtual address of the next descriptor in the list */
+
+ desc = (struct dma_linklist_s *)sam_virtramaddr(paddr);
+ }
+}
+
+/****************************************************************************
+ * Name: sam_txbuffer
+ *
+ * Description:
+ * Configure DMA for transmit of one buffer (memory to peripheral). This
+ * function may be called multiple times to handle large and/or dis-
+ * continuous transfers.
+ *
+ ****************************************************************************/
+
+static int sam_txbuffer(struct sam_xdmach_s *xdmach, uint32_t paddr,
+ uint32_t maddr, size_t nbytes)
+{
+ uint32_t regval;
+ uint32_t ctrla;
+ uint32_t ctrlb;
+
+ /* If we are appending a buffer to a linklist, then re-use the CTRLA/B
+ * values. Otherwise, create them from the properties of the transfer.
+ */
+
+ if (xdmach->llhead)
+ {
+ regval = xdmach->llhead->ctrla;
+ ctrlb = xdmach->llhead->ctrlb;
+ }
+ else
+ {
+ regval = sam_txctrlabits(xdmach);
+ ctrlb = sam_txctrlb(xdmach);
+ }
+
+ ctrla = sam_txctrla(xdmach, regval, nbytes);
+
+ /* Add the new link list entry */
+
+ if (!sam_allocdesc(xdmach, xdmach->lltail, maddr, paddr, ctrla, ctrlb))
+ {
+ return -ENOMEM;
+ }
+
+ /* Pre-calculate the transmit CFG register setting (it won't be used until
+ * the DMA is started).
+ */
+
+ xdmach->cfg = sam_txcfg(xdmach);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_rxbuffer
+ *
+ * Description:
+ * Configure DMA for receipt of one buffer (peripheral to memory). This
+ * function may be called multiple times to handle large and/or dis-
+ * continuous transfers.
+ *
+ ****************************************************************************/
+
+static int sam_rxbuffer(struct sam_xdmach_s *xdmach, uint32_t paddr,
+ uint32_t maddr, size_t nbytes)
+{
+ uint32_t regval;
+ uint32_t ctrla;
+ uint32_t ctrlb;
+
+ /* If we are appending a buffer to a linklist, then re-use the CTRLA/B
+ * values. Otherwise, create them from the properties of the transfer.
+ */
+
+ if (xdmach->llhead)
+ {
+ regval = xdmach->llhead->ctrla;
+ ctrlb = xdmach->llhead->ctrlb;
+ }
+ else
+ {
+ regval = sam_rxctrlabits(xdmach);
+ ctrlb = sam_rxctrlb(xdmach);
+ }
+
+ ctrla = sam_rxctrla(xdmach, regval, nbytes);
+
+ /* Add the new link list entry */
+
+ if (!sam_allocdesc(xdmach, xdmach->lltail, paddr, maddr, ctrla, ctrlb))
+ {
+ return -ENOMEM;
+ }
+
+ /* Pre-calculate the receive CFG register setting (it won't be used until
+ * the DMA is started).
+ */
+
+ xdmach->cfg = sam_rxcfg(xdmach);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_single
+ *
+ * Description:
+ * Start a single buffer DMA.
+ *
+ ****************************************************************************/
+
+static inline int sam_single(struct sam_xdmach_s *xdmach)
+{
+ struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+ struct dma_linklist_s *llhead = xdmach->llhead;
+
+ /* Clear any pending interrupts from any previous XDMAC transfer by reading
+ * the interrupt status register.
+ */
+
+ (void)sam_getdmac(xdmac, SAM_XDMAC_EBCISR_OFFSET);
+
+ /* Write the starting source address in the SADDR register */
+
+ DEBUGASSERT(llhead != NULL && llhead->saddr != 0);
+ sam_putdmach(xdmach, llhead->saddr, SAM_XDMAC_CH_SADDR_OFFSET);
+
+ /* Write the starting destination address in the DADDR register */
+
+ sam_putdmach(xdmach, llhead->daddr, SAM_XDMAC_CH_DADDR_OFFSET);
+
+ /* Clear the next descriptor address */
+
+ sam_putdmach(xdmach, 0, SAM_XDMAC_CH_DSCR_OFFSET);
+
+ /* Set up the CTRLA register */
+
+ sam_putdmach(xdmach, llhead->ctrla, SAM_XDMAC_CH_CTRLA_OFFSET);
+
+ /* Set up the CTRLB register */
+
+ sam_putdmach(xdmach, llhead->ctrlb, SAM_XDMAC_CH_CTRLB_OFFSET);
+
+ /* Both the DST and SRC DSCR bits should be '1' in CTRLB */
+
+ DEBUGASSERT((llhead->ctrlb & XDMAC_CH_CTRLB_BOTHDSCR) ==
+ XDMAC_CH_CTRLB_BOTHDSCR);
+
+ /* Set up the CFG register */
+
+ sam_putdmach(xdmach, xdmach->cfg, SAM_XDMAC_CH_CFG_OFFSET);
+
+ /* Enable the channel by writing a ‘1’ to the CHER enable bit */
+
+ sam_putdmac(xdmac, XDMAC_CHER_ENA(xdmach->chan), SAM_XDMAC_CHER_OFFSET);
+
+ /* The DMA has been started. Once the transfer completes, hardware sets
+ * the interrupts and disables the channel. We will receive buffer
+ * complete and transfer complete interrupts.
+ *
+ * Enable error, buffer complete and transfer complete interrupts.
+ * (Since there is only a single buffer, we don't need the buffer
+ * complete interrupt).
+ */
+
+ sam_putdmac(xdmac, XDMAC_EBC_CBTCINTS(xdmach->chan), SAM_XDMAC_EBCIER_OFFSET);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_multiple
+ *
+ * Description:
+ * Start a multiple buffer DMA.
+ *
+ ****************************************************************************/
+
+static inline int sam_multiple(struct sam_xdmach_s *xdmach)
+{
+ struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+ struct dma_linklist_s *llhead = xdmach->llhead;
+
+ DEBUGASSERT(llhead != NULL && llhead->saddr != 0);
+
+ /* Check the first and last CTRLB values */
+
+ DEBUGASSERT((llhead->ctrlb & XDMAC_CH_CTRLB_BOTHDSCR) == 0);
+ DEBUGASSERT((xdmach->lltail->ctrlb & XDMAC_CH_CTRLB_BOTHDSCR) ==
+ XDMAC_CH_CTRLB_BOTHDSCR);
+
+ /* Clear any pending interrupts from any previous XDMAC transfer by reading
+ * the status register
+ */
+
+ (void)sam_getdmac(xdmac, SAM_XDMAC_EBCISR_OFFSET);
+
+ /* Set up the initial CTRLA register */
+
+ sam_putdmach(xdmach, llhead->ctrla, SAM_XDMAC_CH_CTRLA_OFFSET);
+
+ /* Set up the CTRLB register (will enable descriptors) */
+
+ sam_putdmach(xdmach, llhead->ctrlb, SAM_XDMAC_CH_CTRLB_OFFSET);
+
+ /* Write the channel configuration information into the CFG register */
+
+ sam_putdmach(xdmach, xdmach->cfg, SAM_XDMAC_CH_CFG_OFFSET);
+
+ /* Program the DSCR register with the pointer to the firstlink list entry. */
+
+ sam_putdmach(xdmach, (uint32_t)llhead, SAM_XDMAC_CH_DSCR_OFFSET);
+
+ /* Finally, enable the channel by writing a ‘1’ to the CHER enable */
+
+ sam_putdmac(xdmac, XDMAC_CHER_ENA(xdmach->chan), SAM_XDMAC_CHER_OFFSET);
+
+ /* As each buffer of data is transferred, the CTRLA register is written
+ * back into the link list entry. The CTRLA contains updated BTSIZE and
+ * DONE bits. Additionally, the CTRLA DONE bit is asserted when the
+ * buffer transfer has completed.
+ *
+ * The XDMAC transfer continues until the CTRLB register disables the
+ * descriptor (DSCR bits) registers at the final buffer tranfer.
+ *
+ * Enable error, buffer complete and transfer complete interrupts. We
+ * don't really need the buffer complete interrupts, but we will take them
+ * just to handle stall conditions.
+ */
+
+ sam_putdmac(xdmac, XDMAC_EBC_CHANINTS(xdmach->chan), SAM_XDMAC_EBCIER_OFFSET);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_dmaterminate
+ *
+ * Description:
+ * Terminate the DMA transfer and disable the DMA channel
+ *
+ ****************************************************************************/
+
+static void sam_dmaterminate(struct sam_xdmach_s *xdmach, int result)
+{
+ struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+
+ /* Disable all channel interrupts */
+
+ sam_putdmac(xdmac, XDMAC_EBC_CHANINTS(xdmach->chan), SAM_XDMAC_EBCIDR_OFFSET);
+
+ /* Disable the channel by writing one to the write-only channel disable
+ * register.
+ */
+
+ sam_putdmac(xdmac, XDMAC_CHDR_DIS(xdmach->chan), SAM_XDMAC_CHDR_OFFSET);
+
+ /* Free the linklist */
+
+ sam_freelinklist(xdmach);
+
+ /* If this was an RX DMA (peripheral-to-memory), then invalidate the cache
+ * to force reloads from memory.
+ */
+
+ if (xdmach->rx)
+ {
+ cp15_invalidate_dcache(xdmach->rxaddr, xdmach->rxaddr + xdmach->rxsize);
+ }
+
+ /* Perform the DMA complete callback */
+
+ if (xdmach->callback)
+ {
+ xdmach->callback((DMA_HANDLE)xdmach, xdmach->arg, result);
+ }
+
+ xdmach->callback = NULL;
+ xdmach->arg = NULL;
+}
+
+/****************************************************************************
+ * Name: sam_xdmac_interrupt
+ *
+ * Description:
+ * DMA interrupt handler
+ *
+ ****************************************************************************/
+
+static int sam_xdmac_interrupt(struct sam_xdmac_s *xdmac)
+{
+ struct sam_xdmach_s *xdmach;
+ unsigned int chndx;
+ uint32_t regval;
+
+ /* Get the XDMAC status register value. Ignore all masked interrupt
+ * status bits.
+ */
+
+ regval = sam_getdmac(xdmac, SAM_XDMAC_EBCISR_OFFSET) &
+ sam_getdmac(xdmac, SAM_XDMAC_EBCIMR_OFFSET);
+
+ /* Check if the any transfer has completed or any errors have ocurred. */
+
+ if (regval & XDMAC_EBC_ALLCHANINTS)
+ {
+ /* Yes.. Check each bit to see which channel has interrupted */
+
+ for (chndx = 0; chndx < SAM_NDMACHAN; chndx++)
+ {
+ /* Are any interrupts pending for this channel? */
+
+ if ((regval & XDMAC_EBC_CHANINTS(chndx)) != 0)
+ {
+ xdmach = &xdmac->xdmach[chndx];
+
+ /* Yes.. Did an error occur? */
+
+ if ((regval & XDMAC_EBC_ERR(chndx)) != 0)
+ {
+ /* Yes... Terminate the transfer with an error? */
+
+ dmalldbg("ERROR: DMA failed: %08x\n", regval);
+ sam_dmaterminate(xdmach, -EIO);
+ }
+
+ /* Is the transfer complete? */
+
+ else if ((regval & XDMAC_EBC_CBTC(chndx)) != 0)
+ {
+ /* Yes.. Terminate the transfer with success */
+
+ sam_dmaterminate(xdmach, OK);
+ }
+
+ /* Otherwise, this must be a Bufffer Transfer Complete (BTC)
+ * interrupt as part of a multiple buffer transfer.
+ */
+
+ else /* if ((regval & XDMAC_EBC_BTC(chndx)) != 0) */
+ {
+ /* Write the KEEPON field to clear the STALL states */
+
+ sam_putdmac(xdmac, XDMAC_CHER_KEEP(xdmach->chan),
+ SAM_XDMAC_CHER_OFFSET);
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_xdmac0_interrupt and sam_xdmac1_interrupt
+ *
+ * Description:
+ * DMA interrupt handler
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SAMA5_XDMAC0
+static int sam_xdmac0_interrupt(int irq, void *context)
+{
+ return sam_xdmac_interrupt(&g_xdmac0);
+}
+#endif
+
+#ifdef CONFIG_SAMA5_XDMAC1
+static int sam_xdmac1_interrupt(int irq, void *context)
+{
+ return sam_xdmac_interrupt(&g_xdmac1);
+}
+#endif
+
+/****************************************************************************
+ * Name: sam_dmainitialize
+ *
+ * Description:
+ * Initialize the DMA subsystem
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void sam_dmainitialize(struct sam_xdmac_s *xdmac)
+{
+ /* Disable all DMA interrupts */
+
+ sam_putdmac(xdmac, XDMAC_EBC_ALLINTS, SAM_XDMAC_EBCIDR_OFFSET);
+
+ /* Disable all DMA channels */
+
+ sam_putdmac(xdmac, XDMAC_CHDR_DIS_ALL, SAM_XDMAC_CHDR_OFFSET);
+
+ /* Enable the DMA controller */
+
+ sam_putdmac(xdmac,XDMAC_EN_ENABLE, SAM_XDMAC_EN_OFFSET);
+
+ /* Initialize semaphores */
+
+ sem_init(&xdmac->chsem, 0, 1);
+ sem_init(&xdmac->dsem, 0, SAM_NDMACHAN);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_dmainitialize
+ *
+ * Description:
+ * Initialize the DMA subsystem
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void weak_function up_dmainitialize(void)
+{
+#ifdef CONFIG_SAMA5_XDMAC0
+ dmallvdbg("Initialize XDMAC0\n");
+
+ /* Enable peripheral clock */
+
+ sam_xdmac0_enableclk();
+
+ /* Attach DMA interrupt vector */
+
+ (void)irq_attach(SAM_IRQ_XDMAC0, sam_xdmac0_interrupt);
+
+ /* Initialize the controller */
+
+ sam_dmainitialize(&g_xdmac0);
+
+ /* Enable the IRQ at the AIC (still disabled at the DMA controller) */
+
+ up_enable_irq(SAM_IRQ_XDMAC0);
+#endif
+
+#ifdef CONFIG_SAMA5_XDMAC1
+ dmallvdbg("Initialize XDMAC1\n");
+
+ /* Enable peripheral clock */
+
+ sam_xdmac1_enableclk();
+
+ /* Attach DMA interrupt vector */
+
+ (void)irq_attach(SAM_IRQ_XDMAC1, sam_xdmac1_interrupt);
+
+ /* Initialize the controller */
+
+ sam_dmainitialize(&g_xdmac1);
+
+ /* Enable the IRQ at the AIC (still disabled at the DMA controller) */
+
+ up_enable_irq(SAM_IRQ_XDMAC1);
+#endif
+}
+
+/****************************************************************************
+ * Name: sam_xdmachannel
+ *
+ * Allocate a DMA channel. This function sets aside a DMA channel then
+ * gives the caller exclusive access to the DMA channel.
+ *
+ * The naming convention in all of the DMA interfaces is that one side is
+ * the 'peripheral' and the other is 'memory'. Howerver, the interface
+ * could still be used if, for example, both sides were memory although
+ * the naming would be awkward.
+ *
+ * Returned Value:
+ * If a DMA channel is available, this function returns a non-NULL, void*
+ * DMA channel handle. NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+DMA_HANDLE sam_xdmachannel(uint8_t dmacno, uint32_t chflags)
+{
+ struct sam_xdmac_s *xdmac;
+ struct sam_xdmach_s *xdmach;
+ unsigned int chndx;
+
+ /* Pick the DMA controller */
+
+#ifdef CONFIG_SAMA5_XDMAC0
+ if (dmacno == 0)
+ {
+ xdmac = &g_xdmac0;
+ }
+ else
+#endif
+
+#ifdef CONFIG_SAMA5_XDMAC1
+ if (dmacno == 1)
+ {
+ xdmac = &g_xdmac1;
+ }
+ else
+#endif
+
+ {
+ dmadbg("ERROR: Bad XDMAC number: %d\n", dmacno);
+ DEBUGPANIC();
+ return (DMA_HANDLE)NULL;
+ }
+
+ /* Search for an available DMA channel with at least the requested FIFO
+ * size.
+ */
+
+ xdmach = NULL;
+ sam_takechsem(xdmac);
+ for (chndx = 0; chndx < SAM_NDMACHAN; chndx++)
+ {
+ struct sam_xdmach_s *candidate = &xdmac->xdmach[chndx];
+ if (!candidate->inuse)
+ {
+ xdmach = candidate;
+ xdmach->inuse = true;
+
+ /* Read the status register to clear any pending interrupts on the
+ * channel
+ */
+
+ (void)sam_getdmac(xdmac, SAM_XDMAC_EBCISR_OFFSET);
+
+ /* Disable the channel by writing one to the write-only channel
+ * disable register
+ */
+
+ sam_putdmac(xdmac,XDMAC_CHDR_DIS(chndx), SAM_XDMAC_CHDR_OFFSET);
+
+ /* Set the DMA channel flags. */
+
+ xdmach->flags = chflags;
+ break;
+ }
+ }
+
+ sam_givechsem(xdmac);
+
+ /* Show the result of the allocation */
+
+ if (xdmach)
+ {
+ dmavdbg("XDMAC%d CH%d: chflags: %08x returning xdmach: %p\n",
+ (int)dmacno, xdmach->chan, (int)chflags, xdmach);
+ }
+ else
+ {
+ dmadbg("ERROR: Failed allocate XDMAC%d channel\n", (int)dmacno);
+ }
+
+ return (DMA_HANDLE)xdmach;
+}
+
+/************************************************************************************
+ * Name: sam_xdmaconfig
+ *
+ * Description:
+ * There are two channel usage models: (1) The channel is allocated and configured
+ * in one step. This is the typical case where a DMA channel performs a constant
+ * role. The alternative is (2) where the DMA channel is reconfigured on the fly.
+ * In this case, the chflags provided to sam_xdmachannel are not used and
+ * sam_xdmaconfig() is called before each DMA to configure the DMA channel
+ * appropriately.
+ *
+ * Returned Value:
+ * None
+ *
+ ************************************************************************************/
+
+void sam_xdmaconfig(DMA_HANDLE handle, uint32_t chflags)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+
+ /* Set the new DMA channel flags. */
+
+ xdmach->flags = chflags;
+
+#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
+ dmavdbg("XDMAC%d CH%d: chflags: %08x\n",
+ xdmach->xdmac, xdmach->chan, (int)chflags);
+#elif defined(CONFIG_SAMA5_XDMAC0)
+ dmavdbg("XDMAC0 CH%d: chflags: %08x\n",
+ xdmach->chan, (int)chflags);
+#else
+ dmavdbg("XDMAC1 CH%d: chflags: %08x\n",
+ xdmach->chan, (int)chflags);
+#endif
+}
+
+/****************************************************************************
+ * Name: sam_dmafree
+ *
+ * Description:
+ * Release a DMA channel. NOTE: The 'handle' used in this argument must
+ * NEVER be used again until sam_xdmachannel() is called again to re-gain
+ * a valid handle.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void sam_dmafree(DMA_HANDLE handle)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+
+ dmavdbg("xdmach: %p\n", xdmach);
+ DEBUGASSERT((xdmach != NULL) && (xdmach->inuse));
+
+ /* Mark the channel no longer in use. Clearing the inuse flag is an atomic
+ * operation and so should be safe.
+ */
+
+ xdmach->flags = 0;
+ xdmach->inuse = false; /* No longer in use */
+}
+
+/****************************************************************************
+ * Name: sam_dmatxsetup
+ *
+ * Description:
+ * Configure DMA for transmit of one buffer (memory to peripheral). This
+ * function may be called multiple times to handle large and/or dis-
+ * continuous transfers. Calls to sam_dmatxsetup() and sam_dmarxsetup()
+ * must not be intermixed on the same transfer, however.
+ *
+ ****************************************************************************/
+
+int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
+ size_t nbytes)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+ size_t maxtransfer;
+ size_t remaining;
+ int ret = OK;
+
+ dmavdbg("xdmach: %p paddr: %08x maddr: %08x nbytes: %d\n",
+ xdmach, (int)paddr, (int)maddr, (int)nbytes);
+ DEBUGASSERT(xdmach);
+ dmavdbg("llhead: %p lltail: %p\n", xdmach->llhead, xdmach->lltail);
+
+ /* The maximum transfer size in bytes depends upon the maximum number of
+ * transfers and the number of bytes per transfer.
+ */
+
+ maxtransfer = sam_maxtxtransfer(xdmach);
+ remaining = nbytes;
+
+ /* If this is a large transfer, break it up into smaller buffers */
+
+ while (remaining > maxtransfer)
+ {
+ /* Set up the maximum size transfer */
+
+ ret = sam_txbuffer(xdmach, paddr, maddr, maxtransfer);
+ if (ret == OK);
+ {
+ /* Decrement the number of bytes left to transfer */
+
+ remaining -= maxtransfer;
+
+ /* Increment the memory & peripheral address (if it is appropriate to
+ * do so).
+ */
+
+ if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
+ {
+ paddr += maxtransfer;
+ }
+
+ if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
+ {
+ maddr += maxtransfer;
+ }
+ }
+ }
+
+ /* Then set up the final buffer transfer */
+
+ if (ret == OK && remaining > 0)
+ {
+ ret = sam_txbuffer(xdmach, paddr, maddr, remaining);
+ }
+
+ /* Save an indication so that the DMA interrupt completion logic will know
+ * that this was not an RX transfer.
+ */
+
+ xdmach->rx = false;
+
+ /* Clean caches associated with the DMA memory */
+
+ cp15_clean_dcache(maddr, maddr + nbytes);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: sam_dmarxsetup
+ *
+ * Description:
+ * Configure DMA for receipt of one buffer (peripheral to memory). This
+ * function may be called multiple times to handle large and/or dis-
+ * continuous transfers. Calls to sam_dmatxsetup() and sam_dmarxsetup()
+ * must not be intermixed on the same transfer, however.
+ *
+ ****************************************************************************/
+
+int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
+ size_t nbytes)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+ size_t maxtransfer;
+ size_t remaining;
+ int ret = OK;
+
+ dmavdbg("xdmach: %p paddr: %08x maddr: %08x nbytes: %d\n",
+ xdmach, (int)paddr, (int)maddr, (int)nbytes);
+ DEBUGASSERT(xdmach);
+ dmavdbg("llhead: %p lltail: %p\n", xdmach->llhead, xdmach->lltail);
+
+ /* The maximum transfer size in bytes depends upon the maximum number of
+ * transfers and the number of bytes per transfer.
+ */
+
+ maxtransfer = sam_maxrxtransfer(xdmach);
+ remaining = nbytes;
+
+ /* If this is a large transfer, break it up into smaller buffers */
+
+ while (remaining > maxtransfer)
+ {
+ /* Set up the maximum size transfer */
+
+ ret = sam_rxbuffer(xdmach, paddr, maddr, maxtransfer);
+ if (ret == OK);
+ {
+ /* Decrement the number of bytes left to transfer */
+
+ remaining -= maxtransfer;
+
+ /* Increment the memory & peripheral address (if it is appropriate to
+ * do so).
+ */
+
+ if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
+ {
+ paddr += maxtransfer;
+ }
+
+ if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
+ {
+ maddr += maxtransfer;
+ }
+ }
+ }
+
+ /* Then set up the final buffer transfer */
+
+ if (ret == OK && remaining > 0)
+ {
+ ret = sam_rxbuffer(xdmach, paddr, maddr, remaining);
+ }
+
+ /* Save an indication so that the DMA interrupt completion logic will know
+ * that this was an RX transfer and will invalidate the cache.
+ */
+
+ xdmach->rx = true;
+ xdmach->rxaddr = maddr;
+ xdmach->rxsize = (xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0 ? nbytes : sizeof(uint32_t);
+
+ /* Clean caches associated with the DMA memory */
+
+ cp15_clean_dcache(maddr, maddr + nbytes);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: sam_dmastart
+ *
+ * Description:
+ * Start the DMA transfer
+ *
+ ****************************************************************************/
+
+int sam_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+ int ret = -EINVAL;
+
+ dmavdbg("xdmach: %p callback: %p arg: %p\n", xdmach, callback, arg);
+ DEBUGASSERT(xdmach != NULL);
+
+ /* Verify that the DMA has been setup (i.e., at least one entry in the
+ * link list).
+ */
+
+ if (xdmach->llhead)
+ {
+ /* Save the callback info. This will be invoked whent the DMA commpletes */
+
+ xdmach->callback = callback;
+ xdmach->arg = arg;
+
+ /* Is this a single block transfer? Or a multiple block tranfer? */
+
+ if (xdmach->llhead == xdmach->lltail)
+ {
+ ret = sam_single(xdmach);
+ }
+ else
+ {
+ ret = sam_multiple(xdmach);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: sam_dmastop
+ *
+ * Description:
+ * Cancel the DMA. After sam_dmastop() is called, the DMA channel is
+ * reset and sam_dmarx/txsetup() must be called before sam_dmastart() can be
+ * called again
+ *
+ ****************************************************************************/
+
+void sam_dmastop(DMA_HANDLE handle)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+ irqstate_t flags;
+
+ dmavdbg("xdmach: %p\n", xdmach);
+ DEBUGASSERT(xdmach != NULL);
+
+ flags = irqsave();
+ sam_dmaterminate(xdmach, -EINTR);
+ irqrestore(flags);
+}
+
+/****************************************************************************
+ * Name: sam_dmasample
+ *
+ * Description:
+ * Sample DMA register contents
+ *
+ * Assumptions:
+ * - DMA handle allocated by sam_xdmachannel()
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA
+void sam_dmasample(DMA_HANDLE handle, struct sam_dmaregs_s *regs)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+ struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+ irqstate_t flags;
+
+ /* Sample global registers. NOTE: reading EBCISR clears interrupts, but
+ * that should be okay IF interrupts are enabled when this function is
+ * called. But there is a race condition where this instrumentation could
+ * cause lost interrupts.
+ */
+
+ flags = irqsave();
+ regs->gcfg = sam_getdmac(xdmac, SAM_XDMAC_GCFG_OFFSET);
+ regs->en = sam_getdmac(xdmac, SAM_XDMAC_EN_OFFSET);
+ regs->sreq = sam_getdmac(xdmac, SAM_XDMAC_SREQ_OFFSET);
+ regs->creq = sam_getdmac(xdmac, SAM_XDMAC_CREQ_OFFSET);
+ regs->last = sam_getdmac(xdmac, SAM_XDMAC_LAST_OFFSET);
+ regs->ebcimr = sam_getdmac(xdmac, SAM_XDMAC_EBCIMR_OFFSET);
+ regs->ebcisr = sam_getdmac(xdmac, SAM_XDMAC_EBCISR_OFFSET);
+ regs->chsr = sam_getdmac(xdmac, SAM_XDMAC_CHSR_OFFSET);
+ regs->wpmr = sam_getdmac(xdmac, SAM_XDMAC_WPMR_OFFSET);
+ regs->wpsr = sam_getdmac(xdmac, SAM_XDMAC_WPSR_OFFSET);
+
+ /* Sample channel registers */
+
+ regs->saddr = sam_getdmach(xdmach, SAM_XDMAC_CH_SADDR_OFFSET);
+ regs->daddr = sam_getdmach(xdmach, SAM_XDMAC_CH_DADDR_OFFSET);
+ regs->dscr = sam_getdmach(xdmach, SAM_XDMAC_CH_DSCR_OFFSET);
+ regs->ctrla = sam_getdmach(xdmach, SAM_XDMAC_CH_CTRLA_OFFSET);
+ regs->ctrlb = sam_getdmach(xdmach, SAM_XDMAC_CH_CTRLB_OFFSET);
+ regs->cfg = sam_getdmach(xdmach, SAM_XDMAC_CH_CFG_OFFSET);
+ regs->spip = sam_getdmach(xdmach, SAM_XDMAC_CH_SPIP_OFFSET);
+ regs->dpip = sam_getdmach(xdmach, SAM_XDMAC_CH_DPIP_OFFSET);
+ irqrestore(flags);
+}
+#endif /* CONFIG_DEBUG_DMA */
+
+/****************************************************************************
+ * Name: sam_dmadump
+ *
+ * Description:
+ * Dump previously sampled DMA register contents
+ *
+ * Assumptions:
+ * - DMA handle allocated by sam_xdmachannel()
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_DMA
+void sam_dmadump(DMA_HANDLE handle, const struct sam_dmaregs_s *regs,
+ const char *msg)
+{
+ struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
+ struct sam_xdmac_s *xdmac = sam_controller(xdmach);
+
+ dmadbg("%s\n", msg);
+ dmadbg(" DMA Global Registers:\n");
+ dmadbg(" GCFG[%08x]: %08x\n", xdmac->base + SAM_XDMAC_GCFG_OFFSET, regs->gcfg);
+ dmadbg(" EN[%08x]: %08x\n", xdmac->base + SAM_XDMAC_EN_OFFSET, regs->en);
+ dmadbg(" SREQ[%08x]: %08x\n", xdmac->base + SAM_XDMAC_SREQ_OFFSET, regs->sreq);
+ dmadbg(" CREQ[%08x]: %08x\n", xdmac->base + SAM_XDMAC_CREQ_OFFSET, regs->creq);
+ dmadbg(" LAST[%08x]: %08x\n", xdmac->base + SAM_XDMAC_LAST_OFFSET, regs->last);
+ dmadbg(" EBCIMR[%08x]: %08x\n", xdmac->base + SAM_XDMAC_EBCIMR_OFFSET, regs->ebcimr);
+ dmadbg(" EBCISR[%08x]: %08x\n", xdmac->base + SAM_XDMAC_EBCISR_OFFSET, regs->ebcisr);
+ dmadbg(" CHSR[%08x]: %08x\n", xdmac->base + SAM_XDMAC_CHSR_OFFSET, regs->chsr);
+ dmadbg(" WPMR[%08x]: %08x\n", xdmac->base + SAM_XDMAC_WPMR_OFFSET, regs->wpmr);
+ dmadbg(" WPSR[%08x]: %08x\n", xdmac->base + SAM_XDMAC_WPSR_OFFSET, regs->wpsr);
+ dmadbg(" DMA Channel Registers:\n");
+ dmadbg(" SADDR[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_SADDR_OFFSET, regs->saddr);
+ dmadbg(" DADDR[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_DADDR_OFFSET, regs->daddr);
+ dmadbg(" DSCR[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_DSCR_OFFSET, regs->dscr);
+ dmadbg(" CTRLA[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_CTRLA_OFFSET, regs->ctrla);
+ dmadbg(" CTRLB[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_CTRLB_OFFSET, regs->ctrlb);
+ dmadbg(" CFG[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_CFG_OFFSET, regs->cfg);
+ dmadbg(" SPIP[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_SPIP_OFFSET, regs->spip);
+ dmadbg(" DPIP[%08x]: %08x\n", xdmach->base + SAM_XDMAC_CH_DPIP_OFFSET, regs->dpip);
+}
+#endif /* CONFIG_DEBUG_DMA */
+#endif /* CONFIG_SAMA5_XDMAC0 || CONFIG_SAMA5_XDMAC1 */