From 66d48615c63f4d6da8fe11b6b86dd77bd243c1ac Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 9 Mar 2015 10:11:12 -0600 Subject: [PATCH] SAMV7: Leverage XDMAC driver from the SAMA5D4. --- arch/arm/src/armv7-m/cache.h | 178 +++ arch/arm/src/samv7/Kconfig | 1 + arch/arm/src/samv7/Make.defs | 4 + arch/arm/src/samv7/chip/sam_xdmac.h | 463 ++++++ arch/arm/src/samv7/sam_xdmac.c | 2108 +++++++++++++++++++++++++++ arch/arm/src/samv7/sam_xdmac.h | 361 +++++ 6 files changed, 3115 insertions(+) create mode 100644 arch/arm/src/armv7-m/cache.h create mode 100755 arch/arm/src/samv7/chip/sam_xdmac.h create mode 100644 arch/arm/src/samv7/sam_xdmac.c create mode 100644 arch/arm/src/samv7/sam_xdmac.h diff --git a/arch/arm/src/armv7-m/cache.h b/arch/arm/src/armv7-m/cache.h new file mode 100644 index 0000000000..87c8a4c687 --- /dev/null +++ b/arch/arm/src/armv7-m/cache.h @@ -0,0 +1,178 @@ +/************************************************************************************ + * arch/arm/src/armv7-m/cache.h + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_ARMV7_M_CACHE_H +#define __ARCH_ARM_SRC_ARMV7_M_CACHE_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + + /************************************************************************************ + * Inline Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Name: arch_invalidate_dcache + * + * Description: + * Invalidate the data cache within the specified region; we will be + * performing a DMA operation in this region and we want to purge old data + * in the cache. + * + * Input Parameters: + * start - virtual start address of region + * end - virtual end address of region + 1 + * + * Returned Value: + * None + * + * Assumptions: + * This operation is not atomic. This function assumes that the caller + * has exclusive access to the address range so that no harm is done if + * the operation is pre-empted. + * + ****************************************************************************/ + +static inline void arch_invalidate_dcache(uintptr_t start, uintptr_t end) +{ +#warning Missing logic +} + +/**************************************************************************** + * Name: arch_invalidate_dcache_all + * + * Description: + * Invalidate the entire contents of D cache. + * + * NOTE: This function forces L1 and L2 cache operations to be atomic + * by disabling interrupts. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void arch_invalidate_dcache_all(void) +{ +#warning Missing logic +} + +/**************************************************************************** + * Name: arch_clean_dcache + * + * Description: + * Clean the data cache within the specified region by flushing the + * contents of the data cache to memory. + * + * Input Parameters: + * start - virtual start address of region + * end - virtual end address of region + 1 + * + * Returned Value: + * None + * + * Assumptions: + * This operation is not atomic. This function assumes that the caller + * has exclusive access to the address range so that no harm is done if + * the operation is pre-empted. + * + ****************************************************************************/ + +static inline void arch_clean_dcache(uintptr_t start, uintptr_t end) +{ +#warning Missing logic +} + +/**************************************************************************** + * Name: arch_flush_dcache + * + * Description: + * Flush the data cache within the specified region by cleaning and + * invalidating the D cache. + * + * Input Parameters: + * start - virtual start address of region + * end - virtual end address of region + 1 + * + * Returned Value: + * None + * + * Assumptions: + * This operation is not atomic. This function assumes that the caller + * has exclusive access to the address range so that no harm is done if + * the operation is pre-empted. + * + ****************************************************************************/ + +static inline void arch_flush_dcache(uintptr_t start, uintptr_t end) +{ +#warning Missing logic +} + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#ifdef __cplusplus +} +#endif +#endif /* __ASSEMBLY__ */ + +#endif /* __ARCH_ARM_SRC_ARMV7_M_CACHE_H */ diff --git a/arch/arm/src/samv7/Kconfig b/arch/arm/src/samv7/Kconfig index 65f0be69db..ba83832fea 100644 --- a/arch/arm/src/samv7/Kconfig +++ b/arch/arm/src/samv7/Kconfig @@ -212,6 +212,7 @@ config SAMV7_EMAC config SAMV7_XDMAC bool "Central DMA (XDMA)" default n + select ARCH_DMA config SAMV7_HSMCI bool "High Speed Multimedia Card Interface (HSMCI)" diff --git a/arch/arm/src/samv7/Make.defs b/arch/arm/src/samv7/Make.defs index cfb138ad9a..e7547a1676 100644 --- a/arch/arm/src/samv7/Make.defs +++ b/arch/arm/src/samv7/Make.defs @@ -103,3 +103,7 @@ endif ifeq ($(CONFIG_SAMV7_GPIO_IRQ),y) CHIP_CSRCS += sam_gpioirq.c endif + +ifeq ($(CONFIG_SAMV7_XDMAC),y) +CHIP_CSRCS += sam_xdmac.c +endif diff --git a/arch/arm/src/samv7/chip/sam_xdmac.h b/arch/arm/src/samv7/chip/sam_xdmac.h new file mode 100755 index 0000000000..cac9a5857a --- /dev/null +++ b/arch/arm/src/samv7/chip/sam_xdmac.h @@ -0,0 +1,463 @@ +/************************************************************************************ + * arch/arm/src/samv7/chip/sam_xdmac.h + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMV7_CHIP_SAM_XDMAC_H +#define __ARCH_ARM_SRC_SAMV7_CHIP_SAM_XDMAC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include + +/************************************************************************************ + * Included Files + ************************************************************************************/ +/* XDMAC Register Offsets ***********************************************************/ + +#define SAM_XDMAC_GTYPE_OFFSET 0x0000 /* Global Type Register */ +#define SAM_XDMAC_GCFG_OFFSET 0x0004 /* Global Configuration Register */ +#define SAM_XDMAC_GWAC_OFFSET 0x0008 /* Global Weighted Arbiter Configuration Register */ +#define SAM_XDMAC_GIE_OFFSET 0x000c /* Global Interrupt Enable Register */ +#define SAM_XDMAC_GID_OFFSET 0x0010 /* Global Interrupt Disable Register */ +#define SAM_XDMAC_GIM_OFFSET 0x0014 /* Global Interrupt Mask Register */ +#define SAM_XDMAC_GIS_OFFSET 0x0018 /* Global Interrupt Status Register */ +#define SAM_XDMAC_GE_OFFSET 0x001c /* Global Channel Enable Register */ +#define SAM_XDMAC_GD_OFFSET 0x0020 /* Global Channel Disable Register */ +#define SAM_XDMAC_GS_OFFSET 0x0024 /* Global Channel Status Register */ +#define SAM_XDMAC_GRS_OFFSET 0x0028 /* Global Channel Read Suspend Register */ +#define SAM_XDMAC_GWS_OFFSET 0x002c /* Global Channel Write Suspend Register */ +#define SAM_XDMAC_GRWS_OFFSET 0x0030 /* Global Channel Read Write Suspend Register */ +#define SAM_XDMAC_GRWR_OFFSET 0x0034 /* Global Channel Read Write Resume Register */ +#define SAM_XDMAC_GSWR_OFFSET 0x0038 /* Global Channel Software Request Register */ +#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 */ + +/* Offsets to the base of the DMA channel registers */ + +#define SAM_XDMACH_OFFSET(n) (0x0050 + ((n) << 6)) +# define SAM_XDMACH0_OFFSET 0x0050 +# define SAM_XDMACH1_OFFSET 0x0090 +# define SAM_XDMACH2_OFFSET 0x00d0 +# define SAM_XDMACH3_OFFSET 0x0110 +# define SAM_XDMACH4_OFFSET 0x0150 +# define SAM_XDMACH5_OFFSET 0x0190 +# define SAM_XDMACH6_OFFSET 0x01d0 +# define SAM_XDMACH7_OFFSET 0x0210 +# define SAM_XDMACH8_OFFSET 0x0250 +# define SAM_XDMACH9_OFFSET 0x0290 +# define SAM_XDMACH10_OFFSET 0x02d0 +# define SAM_XDMACH11_OFFSET 0x0310 +# define SAM_XDMACH12_OFFSET 0x0350 +# define SAM_XDMACH13_OFFSET 0x0390 +# define SAM_XDMACH14_OFFSET 0x03d0 +# define SAM_XDMACH15_OFFSET 0x0410 +# define SAM_XDMACH16_OFFSET 0x0450 +# define SAM_XDMACH17_OFFSET 0x0490 +# define SAM_XDMACH18_OFFSET 0x04d0 +# define SAM_XDMACH19_OFFSET 0x0510 +# define SAM_XDMACH20_OFFSET 0x0550 +# define SAM_XDMACH21_OFFSET 0x0590 +# define SAM_XDMACH22_OFFSET 0x05d0 +# define SAM_XDMACH23_OFFSET 0x0610 + +/* 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 */ +#define SAM_XDMACH_CIS_OFFSET 0x000c /* Channel Interrupt Status Register */ +#define SAM_XDMACH_CSA_OFFSET 0x0010 /* Channel Source Address Register */ +#define SAM_XDMACH_CDA_OFFSET 0x0014 /* Channel Destination Address Register */ +#define SAM_XDMACH_CNDA_OFFSET 0x0018 /* Channel Next Descriptor Address Register */ +#define SAM_XDMACH_CNDC_OFFSET 0x001c /* Channel Next Descriptor Control Register */ +#define SAM_XDMACH_CUBC_OFFSET 0x0020 /* Channel Microblock Control Register */ +#define SAM_XDMACH_CBC_OFFSET 0x0024 /* Channel Block Control Register */ +#define SAM_XDMACH_CC_OFFSET 0x0028 /* Channel Configuration Register */ +#define SAM_XDMACH_CDSMSP_OFFSET 0x002c /* Channel Data Stride Memory Set Pattern */ +#define SAM_XDMACH_CSUS_OFFSET 0x0030 /* Channel Source Microblock Stride */ +#define SAM_XDMACH_CDUS_OFFSET 0x0034 /* Channel Destination Microblock Stride */ + /* 0x0038-0x003c Reserved */ + /* 0x0fec–0x0ffc Reserved */ + +/* XDMAC Register Addresses *********************************************************/ + +#define SAM_XDMAC_GTYPE (SAM_XDMAC_BASE+SAM_XDMAC_GTYPE_OFFSET) +#define SAM_XDMAC_GCFG (SAM_XDMAC_BASE+SAM_XDMAC_GCFG_OFFSET) +#define SAM_XDMAC_GWAC (SAM_XDMAC_BASE+SAM_XDMAC_GWAC_OFFSET) +#define SAM_XDMAC_GIE (SAM_XDMAC_BASE+SAM_XDMAC_GIE_OFFSET) +#define SAM_XDMAC_GID (SAM_XDMAC_BASE+SAM_XDMAC_GID_OFFSET) +#define SAM_XDMAC_GIM (SAM_XDMAC_BASE+SAM_XDMAC_GIM_OFFSET) +#define SAM_XDMAC_GIS (SAM_XDMAC_BASE+SAM_XDMAC_GIS_OFFSET) +#define SAM_XDMAC_GE (SAM_XDMAC_BASE+SAM_XDMAC_GE_OFFSET) +#define SAM_XDMAC_GD (SAM_XDMAC_BASE+SAM_XDMAC_GD_OFFSET) +#define SAM_XDMAC_GS (SAM_XDMAC_BASE+SAM_XDMAC_GS_OFFSET) +#define SAM_XDMAC_GRS (SAM_XDMAC_BASE+SAM_XDMAC_GRS_OFFSET) +#define SAM_XDMAC_GWS (SAM_XDMAC_BASE+SAM_XDMAC_GWS_OFFSET) +#define SAM_XDMAC_GRWS (SAM_XDMAC_BASE+SAM_XDMAC_GRWS_OFFSET) +#define SAM_XDMAC_GRWR (SAM_XDMAC_BASE+SAM_XDMAC_GRWR_OFFSET) +#define SAM_XDMAC_GSWR (SAM_XDMAC_BASE+SAM_XDMAC_GSWR_OFFSET) +#define SAM_XDMAC_GSWS (SAM_XDMAC_BASE+SAM_XDMAC_GSWS_OFFSET) +#define SAM_XDMAC_GSWF (SAM_XDMAC_BASE+SAM_XDMAC_GSWF_OFFSET) + +/* Base addresses of XDMAC channel registers */ + +#define SAM_XDMACH_BASE(n) (SAM_XDMAC_BASE+SAM_XDMACH_OFFSET(n)) +# define SAM_XDMACH0_BASE (SAM_XDMAC_BASE+SAM_XDMACH0_OFFSET) +# define SAM_XDMACH1_BASE (SAM_XDMAC_BASE+SAM_XDMACH1_OFFSET) +# define SAM_XDMACH2_BASE (SAM_XDMAC_BASE+SAM_XDMACH2_OFFSET) +# define SAM_XDMACH3_BASE (SAM_XDMAC_BASE+SAM_XDMACH3_OFFSET) +# define SAM_XDMACH4_BASE (SAM_XDMAC_BASE+SAM_XDMACH4_OFFSET) +# define SAM_XDMACH5_BASE (SAM_XDMAC_BASE+SAM_XDMACH5_OFFSET) +# define SAM_XDMACH6_BASE (SAM_XDMAC_BASE+SAM_XDMACH6_OFFSET) +# define SAM_XDMACH7_BASE (SAM_XDMAC_BASE+SAM_XDMACH7_OFFSET) +# define SAM_XDMACH8_BASE (SAM_XDMAC_BASE+SAM_XDMACH8_OFFSET) +# define SAM_XDMACH9_BASE (SAM_XDMAC_BASE+SAM_XDMACH9_OFFSET) +# define SAM_XDMACH10_BASE (SAM_XDMAC_BASE+SAM_XDMACH10_OFFSET) +# define SAM_XDMACH11_BASE (SAM_XDMAC_BASE+SAM_XDMACH11_OFFSET) +# define SAM_XDMACH12_BASE (SAM_XDMAC_BASE+SAM_XDMACH12_OFFSET) +# define SAM_XDMACH13_BASE (SAM_XDMAC_BASE+SAM_XDMACH13_OFFSET) +# define SAM_XDMACH14_BASE (SAM_XDMAC_BASE+SAM_XDMACH14_OFFSET) +# define SAM_XDMACH15_BASE (SAM_XDMAC_BASE+SAM_XDMACH15_OFFSET) +# define SAM_XDMACH16_BASE (SAM_XDMAC_BASE+SAM_XDMACH16_OFFSET) +# define SAM_XDMACH17_BASE (SAM_XDMAC_BASE+SAM_XDMACH17_OFFSET) +# define SAM_XDMACH18_BASE (SAM_XDMAC_BASE+SAM_XDMACH18_OFFSET) +# define SAM_XDMACH19_BASE (SAM_XDMAC_BASE+SAM_XDMACH19_OFFSET) +# define SAM_XDMACH20_BASE (SAM_XDMAC_BASE+SAM_XDMACH20_OFFSET) +# define SAM_XDMACH21_BASE (SAM_XDMAC_BASE+SAM_XDMACH21_OFFSET) +# define SAM_XDMACH22_BASE (SAM_XDMAC_BASE+SAM_XDMACH22_OFFSET) +# define SAM_XDMACH23_BASE (SAM_XDMAC_BASE+SAM_XDMACH23_OFFSET) + +/* Addresses of XDMAC channel registers */ + +#define SAM_XDMACH_CIE(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CIE_OFFSET) +#define SAM_XDMACH_CID(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CID_OFFSET) +#define SAM_XDMACH_CIM(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CIM_OFFSET) +#define SAM_XDMACH_CIS(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CIS_OFFSET) +#define SAM_XDMACH_CSA(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CSA_OFFSET) +#define SAM_XDMACH_CDA(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CDA_OFFSET) +#define SAM_XDMACH_CNDA(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CNDA_OFFSET) +#define SAM_XDMACH_CNDC(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CNDC_OFFSET) +#define SAM_XDMACH_CUBC(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CUBC_OFFSET) +#define SAM_XDMACH_CBC(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CBC_OFFSET) +#define SAM_XDMACH_CC(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CC_OFFSET) +#define SAM_XDMACH_CDSMSP(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CDSMSP_OFFSET) +#define SAM_XDMACH_CSUS(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CSUS_OFFSET) +#define SAM_XDMACH_CDUS(n) (SAM_XDMACH_BASE(n)+SAM_XDMACH_CDUS_OFFSET) + +/* XDMAC Register Bit Definitions ***************************************************/ + +/* Global Type Register */ + +#define XDMAC_GTYPE_NB_CH_SHIFT (0) /* Bits 0-4: Number of Channels Minus One */ +#define XDMAC_GTYPE_NB_CH_MASK (31 << XDMAC_GTYPE_NB_CH_SHIFT) + #define XDMAC_GTYPE_NB_CH(n) ((uint32_t)(n) << XDMAC_GTYPE_NB_CH_SHIFT) +#define XDMAC_GTYPE_FIFO_SZ_SHIFT (5) /* Bits 5-15: Number of Bytes */ +#define XDMAC_GTYPE_FIFO_SZ_MASK (0x7ff << XDMAC_GTYPE_FIFO_SZ_SHIFT) +# define XDMAC_GTYPE_FIFO_SZ(n) ((uint32_t)(n) << XDMAC_GTYPE_FIFO_SZ_SHIFT) +#define XDMAC_GTYPE_NB_REQ_SHIFT (16) /* Bits 16-22: Number of Peripheral Requests Minus One */ +#define XDMAC_GTYPE_NB_REQ_MASK (0x7f << XDMAC_GTYPE_NB_REQ_SHIFT) +# define XDMAC_GTYPE_NB_REQ(n) ((uint32_t)(n) << XDMAC_GTYPE_NB_REQ_SHIFT) + +/* Global Configuration Register */ + +#define XDMAC_GCFG_CGDISREG (1 << 0) /* Bit 0: Configuration Registers Clock Gating Disable */ +#define XDMAC_GCFG_CGDISPIPE (1 << 1) /* Bit 1: Pipeline Clock Gating Disable */ +#define XDMAC_GCFG_CGDISFIFO (1 << 2) /* Bit 2: FIFO Clock Gating Disable */ +#define XDMAC_GCFG_CGDISIF (1 << 3) /* Bit 3: Bus Interface Clock Gating Disable */ +#define XDMAC_GCFG_BXKBEN (1 << 8) /* Bit 8: Boundary X Kilo byte Enable */ + +/* Global Weighted Arbiter Configuration Register */ + +#define XDMAC_GWAC_PW0_SHIFT (0) /* Bits 0-3: Pool Weight 0 */ +#define XDMAC_GWAC_PW0_MASK (15 << XDMAC_GWAC_PW0_SHIFT) +# define XDMAC_GWAC_PW0(n) ((uint32_t)(n) << XDMAC_GWAC_PW0_SHIFT) +#define XDMAC_GWAC_PW1_SHIFT (4) /* Bits 4-7: Pool Weight 1 */ +#define XDMAC_GWAC_PW1_MASK (15 << XDMAC_GWAC_PW1_SHIFT) +# define XDMAC_GWAC_PW1(n) ((uint32_t)(n) << XDMAC_GWAC_PW1_SHIFT) +#define XDMAC_GWAC_PW2_SHIFT (8) /* Bits 8-11: Pool Weight 2 */ +#define XDMAC_GWAC_PW2_MASK (15 << XDMAC_GWAC_PW2_SHIFT) +# define XDMAC_GWAC_PW2(n) ((uint32_t)(n) << XDMAC_GWAC_PW2_SHIFT) +#define XDMAC_GWAC_PW3_SHIFT (12) /* Bits 12-15: Pool Weight 3 */ +#define XDMAC_GWAC_PW3_MASK (15 << XDMAC_GWAC_PW3_SHIFT) +# define XDMAC_GWAC_PW3(n) ((uint32_t)(n) << XDMAC_GWAC_PW3_SHIFT) + +/* All of these registers have the same layout: + * + * - Global Interrupt Enable Register, Global Interrupt Disable Register, Interrupt + * Mask Register, and Global Interrupt Status Register. + * + * - Global Channel Enable Register, Global Channel Disable Register, and Global + * Channel Status Register + * + * - Global Channel Read Suspend Register, Global Channel Write Suspend Register, + * Channel Read Write Suspend Register, and Global Channel Read Write Resume + * Register + * + * - Global Channel Software Request Register, Global Channel Software Request + * Status Register, and Global Channel Software Flush Request Register + */ + +#define XDMAC_CHAN(n) (1 << (n)) +#define XDMAC_CHAN_ALL (0x0000ffff) + +/* Channel Interrupt Enable Register, Channel Interrupt Disable Register, Channel + * Interrupt Mask Register, and Channel Interrupt Status Register. + */ + +#define XDMAC_CHINT_BI (1 << 0) /* Bit 0: End of Block Interrupt */ +#define XDMAC_CHINT_LI (1 << 1) /* Bit 1: End of Linked List Interrupt */ +#define XDMAC_CHINT_DI (1 << 2) /* Bit 2: End of Disable Interrupt */ +#define XDMAC_CHINT_FI (1 << 3) /* Bit 3: End of Flush Interrupt */ +#define XDMAC_CHINT_RBI (1 << 4) /* Bit 4: Read Bus Error Interrupt */ +#define XDMAC_CHINT_WBI (1 << 5) /* Bit 5: Write Bus Error Interrupt */ +#define XDMAC_CHINT_ROI (1 << 6) /* Bit 6: Request Overflow Error Interrupt Disable Bit */ + +#define XDMAC_CHINT_ERRORS (0x00000070) +#define XDMAC_CHINT_ALL (0x0000007f) + +/* Channel Source Address (SA) Register (aligned 32-bit address) */ +/* Channel Destination Address (DA) Register (aligned 32-bit address) */ + +/* Channel Next Descriptor Address (CNDA) Register (aligned 32-bit address) */ + +#define XDMACH_CNDA_NDAIF (1 << 0) /* Bit 0: Channel Next Descriptor Interface */ +#define XDMACH_CNDA_NDA_MASK (0xfffffffc) /* Bit 2-31: Channel Next Descriptor Address */ + +/* Channel Next Descriptor Control Register */ + +#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 */ +# define XDMACH_CNDC_NDVIEW_NDV2 (2 << XDMACH_CNDC_NDVIEW_SHIFT) /* Next Descriptor View 2 */ +# define XDMACH_CNDC_NDVIEW_NDV3 (3 << XDMACH_CNDC_NDVIEW_SHIFT) /* Next Descriptor View 3 */ + +/* Channel Microblock Control Register */ + +#define XDMACH_CUBC_UBLEN_SHIFT (0) /* Bits 0-23: Channel Microblock Length */ +#define XDMACH_CUBC_UBLEN_MASK (0x00ffffff << XDMACH_CUBC_UBLEN_SHIFT) +# define XDMACH_CUBC_UBLEN_MAX (0x00ffffff) + +/* Channel Block Control Register */ + +#define XDMACH_CBC_BLEN_MASK (0x000000fff) /* Bits 0-11: Channel Block Length */ + +/* Channel Configuration Register */ + +#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(n) ((uint32_t)(n) << XDMACH_CC_MBSIZE_SHIFT) /* n=0-3 */ +# 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 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 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_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 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 */ +# define XDMACH_CC_DAM_UBS (2 << XDMACH_CC_DAM_SHIFT) /* Microblock stride is added */ +# define XDMACH_CC_DAM_UBSDS (3 << XDMACH_CC_DAM_SHIFT) /* Microblock stride and data stride is added */ +#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 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 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 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 Source Microblock Stride */ + +/* Channel Destination Microblock Stride */ + +#define XDMACH_CDUS_DUBS_MASK (0x00ffffff) /* Bits 0-23: Channel Destination Microblock Stride */ + +/* XDMA Channel Definitions *************************************************************/ + +#define XDMACH_HSMCI 0 +#define XDMACH_SPI0_TX 1 +#define XDMACH_SPI0_RX 2 +#define XDMACH_SPI1_TX 3 +#define XDMACH_SPI1_RX 4 +#define XDMACH_QSPI_TX 5 +#define XDMACH_QSPI_RX 6 +#define XDMACH_USART0_TX 7 +#define XDMACH_USART0_RX 8 +#define XDMACH_USART1_TX 9 +#define XDMACH_USART1_RX 10 +#define XDMACH_USART2_TX 11 +#define XDMACH_USART2_RX 12 +#define XDMACH_PWM0_TX 13 +#define XDMACH_TWIHS0_TX 14 +#define XDMACH_TWIHS0_RX 15 +#define XDMACH_TWIHS1_TX 16 +#define XDMACH_TWIHS1_RX 17 +#define XDMACH_TWIHS2_TX 18 +#define XDMACH_TWIHS2_RX 19 +#define XDMACH_UART0_TX 20 +#define XDMACH_UART0_RX 21 +#define XDMACH_UART1_TX 22 +#define XDMACH_UART1_RX 23 +#define XDMACH_UART2_TX 24 +#define XDMACH_UART2_RX 25 +#define XDMACH_UART3_TX 26 +#define XDMACH_UART3_RX 27 +#define XDMACH_UART4_TX 28 +#define XDMACH_UART4_RX 29 +#define XDMACH_DACC_TX 30 +#define XDMACH_SSC_TX 32 +#define XDMACH_SSC_RX 33 +#define XDMACH_PIOA_RX 34 +#define XDMACH_AFEC0_RX 35 +#define XDMACH_AFEC1_RX 36 +#define XDMACH_AES_TX 37 +#define XDMACH_AES_RX 38 +#define XDMACH_PWM1_TX 39 +#define XDMACH_TC0_RX 40 +#define XDMACH_TC1_RX 41 +#define XDMACH_TC2_RX 42 +#define XDMACH_TC3_RX 43 + +/* Descriptor structure member definitions **********************************************/ + +/* Next Descriptor Address (32-bit address) */ + +/* Microblock Control */ + +#define CHNEXT_UBC_UBLEN_SHIFT (0) /* Bits 0-23: Microblock Length */ +#define CHNEXT_UBC_UBLEN_MASK (0x00ffffff << CHNEXT_UBC_UBLEN_SHIFT) +# define CHNEXT_UBC_UBLEN(n) ((uint32_t)(n) << CHNEXT_UBC_UBLEN_SHIFT) +#define CHNEXT_UBC_NDE (1 << 24) /* Bit 24: Next Descriptor Enable */ +#define CHNEXT_UBC_NSEN (1 << 25) /* Bit 25: Next Descriptor Source Update */ +#define CHNEXT_UBC_NDEN (1 << 26) /* Bit 26: Next Descriptor Destination Update */ +#define CHNEXT_UBC_NVIEW_SHIFT (27) /* Bits 27-29: Next Descriptor View */ +#define CHNEXT_UBC_NVIEW_MASK (3 << CHNEXT_UBC_NVIEW_SHIFT) +# define CHNEXT_UBC_NVIEW_0 (0 << CHNEXT_UBC_NVIEW_SHIFT) /* Next Descriptor View 0 */ +# define CHNEXT_UBC_NVIEW_1 (1 << CHNEXT_UBC_NVIEW_SHIFT) /* Next Descriptor View 1 */ +# define CHNEXT_UBC_NVIEW_2 (2 << CHNEXT_UBC_NVIEW_SHIFT) /* Next Descriptor View 2 */ +# define CHNEXT_UBC_NVIEW_3 (3 << CHNEXT_UBC_NVIEW_SHIFT) /* Next Descriptor View 3 */ + +/* Source Address (32-bit address) */ +/* Destination Address (32-bit address) */ + +/* Configuration Register */ +/* Block Control */ + +/* Data Stride (32-bit value) */ +/* Source Microblock Stride (32-bit value) */ +/* Destination Microblock Stride (32-bit value) */ + +/**************************************************************************************** + * Public Types + ****************************************************************************************/ + +struct chnext_view0_s +{ + uint32_t cnda; /* Next Descriptor Address */ + uint32_t cubc; /* Microblock Control */ + uint32_t cta; /* Transfer Address */ +}; + +struct chnext_view1_s +{ + uint32_t cnda; /* Next Descriptor Address */ + uint32_t cubc; /* Microblock Control */ + uint32_t csa; /* Source Address */ + uint32_t cda; /* Destination Address */ +}; + +struct chnext_view2_s +{ + uint32_t cnda; /* Next Descriptor Address */ + uint32_t cubc; /* Microblock Control */ + uint32_t csa; /* Source Address */ + uint32_t cda; /* Destination Address */ + uint32_t cc; /* Configuration Register */ +}; + +struct chnext_view3_s +{ + uint32_t cnda; /* Next Descriptor Address */ + uint32_t cubc; /* Microblock Control */ + uint32_t csa; /* Source Address */ + uint32_t cda; /* Destination Address */ + uint32_t cc; /* Configuration Register */ + uint32_t cbc; /* Block Control */ + uint32_t cds; /* Data Stride */ + uint32_t csus; /* Source Microblock Stride */ + uint32_t cdus; /* Destination Microblock Stride */ +}; + +#endif /* __ARCH_ARM_SRC_SAMV7_CHIP_SAM_XDMAC_H */ diff --git a/arch/arm/src/samv7/sam_xdmac.c b/arch/arm/src/samv7/sam_xdmac.c new file mode 100644 index 0000000000..bd10681363 --- /dev/null +++ b/arch/arm/src/samv7/sam_xdmac.c @@ -0,0 +1,2108 @@ +/**************************************************************************** + * arch/arm/src/samv7/sam_xdmac.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "up_arch.h" +#include "cache.h" +#include "up_internal.h" +#include "sched/sched.h" + +#include "sam_xdmac.h" +#include "sam_periphclks.h" +#include "chip/sam_pmc.h" +#include "chip/sam_xdmac.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Condition out the whole file unless DMA is selected in the configuration */ + +#ifdef CONFIG_SAMV7_XDMAC + +/* If SAMV7 DMA support is enabled, then OS DMA support should also be + * enabled + */ + +#ifndef CONFIG_ARCH_DMA +# warning "SAMV7 DMA enabled but CONFIG_ARCH_DMA disabled" +#endif + +/* Check the number of link list descriptors to allocate */ + +#ifndef CONFIG_SAMV7_NLLDESC +# define CONFIG_SAMV7_NLLDESC SAMV7_NDMACHAN +#endif + +#if CONFIG_SAMV7_NLLDESC < SAMV7_NDMACHAN +# warning "At least SAMV7_NDMACHAN descriptors must be allocated" + +# undef CONFIG_SAMV7_NLLDESC +# define CONFIG_SAMV7_NLLDESC SAMV7_NDMACHAN +#endif + +/**************************************************************************** + * 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 describes one DMA channel */ + +struct sam_xdmach_s +{ + uint8_t chan; /* DMA channel number (0-15) */ + 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 cc; /* Pre-calculated CC 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 to be invalidated */ + size_t rxsize; /* Size of RX memory region */ + struct chnext_view1_s *llhead; /* DMA link list head */ + struct chnext_view1_s *lltail; /* DMA link list head */ +}; + +/* This structure describes the state 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 */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Channel Control (CC) Register field lookups */ + +static const uint32_t g_chanwidth[3] = +{ + XDMACH_CC_DWIDTH_BYTE, + XDMACH_CC_DWIDTH_HWORD, + XDMACH_CC_DWIDTH_WORD, +}; + +/* 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. + */ + +/* RX DMA: */ + +static const struct sam_pidmap_s g_xdmac_rxchan[] = +{ + { SAM_PID_HSMCI, XDMACH_HSMCI }, /* HSMCI Receive/Transmit */ + { SAM_PID_SPI0, XDMACH_SPI0_RX }, /* SPI0 Receive */ + { SAM_PID_SPI1, XDMACH_SPI1_RX }, /* SPI1 Receive */ + { SAM_PID_QSPI, XDMACH_QSPI_RX }, /* QSPI Receive */ + { SAM_PID_USART0, XDMACH_USART0_RX }, /* USART0 Receive */ + { SAM_PID_USART1, XDMACH_USART1_RX }, /* USART1 Receive */ + { SAM_PID_USART2, XDMACH_USART2_RX }, /* USART2 Receive */ + { SAM_PID_TWIHS0, XDMACH_TWIHS0_RX }, /* TWIHS0 Receive */ + { SAM_PID_TWIHS1, XDMACH_TWIHS1_RX }, /* TWIHS1 Receive */ + { SAM_PID_TWIHS2, XDMACH_TWIHS2_RX }, /* TWIHS2 Receive */ + { SAM_PID_UART0, XDMACH_UART0_RX }, /* UART0 Receive */ + { SAM_PID_UART1, XDMACH_UART1_RX }, /* UART1 Receive */ + { SAM_PID_UART2, XDMACH_UART2_RX }, /* UART2 Receive */ + { SAM_PID_UART3, XDMACH_UART3_RX }, /* UART3 Receive */ + { SAM_PID_UART4, XDMACH_UART4_RX }, /* UART4 Receive */ + { SAM_PID_SSC, XDMACH_SSC_RX }, /* SSC Receive */ + { SAM_PID_PIOA, XDMACH_PIOA_RX }, /* PIOA Receive */ + { SAM_PID_AFEC0, XDMACH_AFEC0_RX }, /* AFEC0 Receive */ + { SAM_PID_AFEC1, XDMACH_AFEC1_RX }, /* AFEC1 Receive */ + { SAM_PID_AES, XDMACH_AES_RX }, /* AES Receive */ + { SAM_PID_TC0, XDMACH_TC0_RX }, /* TC0 Receive */ + { SAM_PID_TC1, XDMACH_TC1_RX }, /* TC1 Receive */ + { SAM_PID_TC2, XDMACH_TC2_RX }, /* TC2 Receive */ + { SAM_PID_TC3, XDMACH_TC3_RX } /* TC3 Receive */ +}; +#define NXDMAC_RXCHANNELS (sizeof(g_xdmac_rxchan) / sizeof(struct sam_pidmap_s)) + +/* TX DMA: */ + +static const struct sam_pidmap_s g_xdmac_txchan[] = +{ + { SAM_PID_HSMCI, XDMACH_HSMCI }, /* HSMCI Receive/Transmit */ + { SAM_PID_SPI0, XDMACH_SPI0_TX }, /* SPI0 Transmit */ + { SAM_PID_SPI1, XDMACH_SPI1_TX }, /* SPI1 Transmit */ + { SAM_PID_QSPI, XDMACH_QSPI_TX }, /* QSPI Transmit */ + { SAM_PID_USART0, XDMACH_USART0_TX }, /* USART0 Transmit */ + { SAM_PID_USART1, XDMACH_USART1_TX }, /* USART1 Transmit */ + { SAM_PID_USART2, XDMACH_USART2_TX }, /* USART2 Transmit */ + { SAM_PID_PWM0, XDMACH_PWM0_TX }, /* PWM0 Transmit */ + { SAM_PID_TWIHS0, XDMACH_TWIHS0_TX }, /* TWIHS0 Transmit */ + { SAM_PID_TWIHS1, XDMACH_TWIHS1_TX }, /* TWIHS1 Transmit */ + { SAM_PID_TWIHS2, XDMACH_TWIHS2_TX }, /* TWIHS2 Transmit */ + { SAM_PID_UART0, XDMACH_UART0_TX }, /* UART0 Transmit */ + { SAM_PID_UART1, XDMACH_UART1_TX }, /* UART1 Transmit */ + { SAM_PID_UART2, XDMACH_UART2_TX }, /* UART2 Transmit */ + { SAM_PID_UART3, XDMACH_UART3_TX }, /* UART3 Transmit */ + { SAM_PID_UART4, XDMACH_UART4_TX }, /* UART4 Transmit */ + { SAM_PID_DACC, XDMACH_DACC_TX }, /* DACC Transmit */ + { SAM_PID_SSC, XDMACH_SSC_TX }, /* SSC Transmit */ + { SAM_PID_AES, XDMACH_AES_TX }, /* AES Transmit */ + { SAM_PID_PWM1, XDMACH_PWM1_TX } /* PWM01Transmit */ +}; +#define NXDMAC_TXCHANNELS (sizeof(g_xdmac_txchan) / sizeof(struct sam_pidmap_s)) + +/* This array describes the available link list descriptors */ + +struct chnext_view1_s g_lldesc[CONFIG_SAMV7_NLLDESC]; + +/* This array describes the state of each XDMAC channel 0 */ + +static struct sam_xdmach_s g_xdmach[SAMV7_NDMACHAN] = +{ +#if SAMV7_NDMACHAN > 0 + { + .chan = 0, + .base = SAM_XDMACH0_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 1 + { + .chan = 1, + .base = SAM_XDMACH1_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 2 + { + .chan = 2, + .base = SAM_XDMACH2_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 3 + { + .chan = 3, + .base = SAM_XDMACH3_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 4 + { + .chan = 4, + .base = SAM_XDMACH4_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 5 + { + .chan = 5, + .base = SAM_XDMACH5_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 6 + { + .chan = 6, + .base = SAM_XDMACH6_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 7 + { + .chan = 7, + .base = SAM_XDMACH7_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 8 + { + .chan = 8, + .base = SAM_XDMACH8_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 9 + { + .chan = 9, + .base = SAM_XDMACH9_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 10 + { + .chan = 10, + .base = SAM_XDMACH10_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 11 + { + .chan = 11, + .base = SAM_XDMACH11_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 12 + { + .chan = 12, + .base = SAM_XDMACH12_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 13 + { + .chan = 13, + .base = SAM_XDMACH13_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 14 + { + .chan = 14, + .base = SAM_XDMACH14_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 15 + { + .chan = 15, + .base = SAM_XDMACH15_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 16 + { + .chan = 16, + .base = SAM_XDMACH16_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 17 + { + .chan = 17, + .base = SAM_XDMACH17_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 18 + { + .chan = 18, + .base = SAM_XDMACH18_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 19 + { + .chan = 19, + .base = SAM_XDMACH19_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 20 + { + .chan = 20, + .base = SAM_XDMACH10_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 21 + { + .chan = 21, + .base = SAM_XDMACH11_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 22 + { + .chan = 22, + .base = SAM_XDMACH22_BASE, + }, +#endif +#if SAMV7_NDMACHAN > 23 + { + .chan = 23, + .base = SAM_XDMACH23_BASE, + } +#endif +}; + +/* This describes the overall state of DMA controller */ + +static struct sam_xdmac_s g_xdmac; + +/**************************************************************************** + * 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(SAM_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, SAM_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) +{ + /* This function is useful only on the SAMV7D4 where there are two + * XDMAC controllers. + */ + + return &g_xdmac; +} + +/**************************************************************************** + * Name: sam_physramaddr and sam_virtramaddr + * + * Description: + * Useful only if address mapping is required. If needed, these should + * problem be moved to file contain common address mapping logic + * (sam_memories) + * + ****************************************************************************/ + +static inline uintptr_t sam_physramaddr(uintptr_t virtaddr) +{ +#warning REVISIT -- Do DCTM address need to be remapped for DMA? + return virtaddr; +} + +static inline uintptr_t sam_virtramaddr(uintptr_t physaddr) +{ +#warning REVISIT -- Do DCTM address need to be remapped for DMA? + return physaddr; +} + +/**************************************************************************** + * 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) +{ + return (uint32_t)sam_channel(pid, g_xdmac_rxchan, NXDMAC_RXCHANNELS); +} + +static uint32_t sam_sink_channel(struct sam_xdmach_s *xdmach, uint8_t pid) +{ + return (uint32_t)sam_channel(pid, g_xdmac_txchan, NXDMAC_TXCHANNELS); +} + +/**************************************************************************** + * Name: sam_maxtransfer + * + * Description: + * Maximum number of bytes that can be sent in one transfer + * + ****************************************************************************/ + +static size_t sam_maxtransfer(struct sam_xdmach_s *xdmach) +{ + unsigned int xfrwidth; + size_t maxtransfer; + + /* Get the maximum transfer size in bytes */ + + xfrwidth = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >> + DMACH_FLAG_PERIPHWIDTH_SHIFT; + + switch (xfrwidth) + { + default: + case 0: /* 8 bits, 1 byte */ + maxtransfer = XDMACH_CUBC_UBLEN_MAX; + break; + + case 1: /* 16 bits, 2 bytes */ + maxtransfer = 2 * XDMACH_CUBC_UBLEN_MAX; + break; + + case 2: /* 32 bits 4 bytes */ + maxtransfer = 4 * XDMACH_CUBC_UBLEN_MAX; + break; + + case 3: /* 64 bits, 8 bytes */ + maxtransfer = 8 * XDMACH_CUBC_UBLEN_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_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_cubc + * + * Description: + * Calculate the CUBC transfer size + * + ****************************************************************************/ + +static inline uint32_t sam_cubc(struct sam_xdmach_s *xdmach, + 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 <= XDMACH_CUBC_UBLEN_MAX); + return (ntransfers << XDMACH_CUBC_UBLEN_SHIFT); +} + +/**************************************************************************** + * Name: sam_txcc + * + * Description: + * Decode the flags to get the correct Channel Control (CC) Register bit + * settings for a transmit (memory to peripheral) transfer. + * + * Single Block: + * 1. Clear TYPE for a memory to memory transfer, otherwise set + * this bit for memory to/from peripheral transfer. + * 2. Program MBSIZE to the memory burst size used. + * 3. Program SAM/DAM to the memory addressing scheme. + * 4. Program SYNC to select the peripheral transfer direction. + * 5. Set PROT to activate a secure channel. + * 6. Program CSIZE to configure the channel chunk size (only + * relevant for peripheral synchronized transfer). + * 7. Program DWIDTH to configure the transfer data width. + * 8. Program SIF, DIF to configure the master interface + * used to read data and write data respectively. + * 9. Program PERID to select the active hardware request line + * (only relevant for a peripheral synchronized transfer). + * 10. Set SWREQ to use software request (only relevant for a + * peripheral synchronized transfer). + * + ****************************************************************************/ + +static inline uint32_t sam_txcc(struct sam_xdmach_s *xdmach) +{ + uint32_t regval = 0; + uint32_t field; + + /* 1. Clear TYPE for a memory to memory transfer, otherwise set + * this bit for memory to/from peripheral transfer. + * + * By convention, TX refers to a memory to peripheral transfer. So the + * source is memory. Is the "peripheral" destination a peripheral? or + * it is really memory? + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0) + { + /* Yes.. Use peripheral synchronized mode */ + + regval |= XDMACH_CC_TYPE; + } + + /* 2. Program MBSIZE to the memory burst size used. + * + * NOTE: This assumes the same encoding in the DMACH flags as in the CC + * register MBSIZE field. + */ + + field = (xdmach->flags & DMACH_FLAG_MEMBURST_MASK) >> + DMACH_FLAG_MEMBURST_SHIFT; + regval |= (field << XDMACH_CC_MBSIZE_SHIFT); + + /* 3. Program SAM/DAM to the memory addressing scheme. + * + * NOTE: This assumes that 0 means non-incrementing. + * TX -> Source is memory. + */ + + if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0) + { + regval |= XDMACH_CC_SAM_INCR; + } + + /* TX -> Destination is peripheral. */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0) + { + regval |= XDMACH_CC_DAM_INCR; + } + + /* 4. Program DSYNC to select the peripheral transfer direction. + * + * TX -> Memory to peripheral + */ + + regval |= XDMACH_CC_DSYNC; + +#if 0 /* REVISIT */ + /* 5. Set PROT to activate a secure channel (REVISIT). */ + + regval |= XDMACH_CC_PROT; /* Channel is unsecured */ +#endif + + /* 6. Program CSIZE to configure the channel chunk size (only + * relevant for peripheral synchronized transfer). + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0) + { + /* Peripheral synchronized mode -- set chunk size. + * NOTE that we assume that encoding in the XDMACH flags is the same + * as in the CC register CSIZE field. + */ + + field = (xdmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) >> + DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT; + regval |= (field << XDMACH_CC_CSIZE_SHIFT); + } + + /* 7. Program DWIDTH to configure the transfer data width. + * + * NOTE that we assume that encoding in the XDMACH flags is the same as in + * the CC register CSIZE field. + */ + + field = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >> + DMACH_FLAG_PERIPHWIDTH_SHIFT; + regval |= (field << XDMACH_CC_DWIDTH_SHIFT); + + /* 8. Program SIF, DIF to configure the master interface + * used to read data and write data respectively. + * + * TX -> Source is memory + */ + + if ((xdmach->flags & DMACH_FLAG_MEMAHB_MASK) == DMACH_FLAG_MEMAHB_AHB_IF1) + { + regval |= XDMACH_CC_SIF; + } + + /* TX -> Destination is peripheral */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHAHB_MASK) == DMACH_FLAG_PERIPHAHB_AHB_IF1) + { + regval |= XDMACH_CC_DIF; + } + + /* 9. Program PERID to select the active hardware request line + * (only relevant for a peripheral synchronized transfer). + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0) + { + int pid; + + /* Get the PID from the DMACH flags */ + + pid = (xdmach->flags & DMACH_FLAG_PERIPHPID_MASK) >> + DMACH_FLAG_PERIPHPID_SHIFT; + + /* Look up the DMA channel code for TX: Peripheral is the sink. */ + + field = sam_sink_channel(xdmach, pid); + regval |= (field << XDMACH_CC_CSIZE_SHIFT); + +#if 0 /* Not supported */ + /* 10. Set SWREQ to use software request (only relevant for a + * peripheral synchronized transfer). + */ + + regval |= XDMACH_CC_SWREQ; +#endif + } + + return regval; +} + +/**************************************************************************** + * Name: sam_rxcc + * + * Description: + * Decode the flags to get the correct Channel Control (CC) Register bit + * settings for a receive (peripheral to memory) transfer. + * + * 1. Clear TYPE for a memory to memory transfer, otherwise set + * this bit for memory to/from peripheral transfer. + * 2. Program MBSIZE to the memory burst size used. + * 3. Program SAM/DAM to the memory addressing scheme. + * 4. Program SYNC to select the peripheral transfer direction. + * 5. Set PROT to activate a secure channel. + * 6. Program CSIZE to configure the channel chunk size (only + * relevant for peripheral synchronized transfer). + * 7. Program DWIDTH to configure the transfer data width. + * 8. Program SIF, DIF to configure the master interface + * used to read data and write data respectively. + * 9. Program PERID to select the active hardware request line + * (only relevant for a peripheral synchronized transfer). + * 10. Set SWREQ to use software request (only relevant for a + * peripheral synchronized transfer). + * + ****************************************************************************/ + +static inline uint32_t sam_rxcc(struct sam_xdmach_s *xdmach) +{ + uint32_t regval = 0; + uint32_t field; + + /* 1. Clear TYPE for a memory to memory transfer, otherwise set + * this bit for memory to/from peripheral transfer. + * + * By convention, RX refers to a peripheral to memory transfer. So the + * source is peripheral. Is the "peripheral" source a peripheral? or + * is it also memory? + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0) + { + /* Yes.. Use peripheral synchronized mode */ + + regval |= XDMACH_CC_TYPE; + } + + /* 2. Program MBSIZE to the memory burst size used. + * + * NOTE: This assumes the same encoding in the DMACH flags as in the CC + * register MBSIZE field. + */ + + field = (xdmach->flags & DMACH_FLAG_MEMBURST_MASK) >> + DMACH_FLAG_MEMBURST_SHIFT; + regval |= (field << XDMACH_CC_MBSIZE_SHIFT); + + /* 3. Program SAM/DAM to the memory addressing scheme. + * + * NOTE: This assumes that 0 means non-incrementing. + * RX -> Source is peripheral. + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0) + { + regval |= XDMACH_CC_SAM_INCR; + } + + /* RX -> Destination is memory. */ + + if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0) + { + regval |= XDMACH_CC_DAM_INCR; + } + + /* 4. Program DSYNC to select the peripheral transfer direction. + * + * RX -> Peripheral to memory (DSYNC == 0) + */ + +#if 0 /* REVISIT */ + /* 5. Set PROT to activate a secure channel (REVISIT) */ + + regval |= XDMACH_CC_PROT; /* Channel is unsecured */ +#endif + + /* 6. Program CSIZE to configure the channel chunk size (only + * relevant for peripheral synchronized transfer). + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0) + { + /* Peripheral synchronized mode -- set chunk size. + * NOTE that we assume that encoding in the XDMACH flags is the same + * as in the CC register CSIZE field. + */ + + field = (xdmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) >> + DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT; + regval |= (field << XDMACH_CC_CSIZE_SHIFT); + } + + /* 7. Program DWIDTH to configure the transfer data width. + * + * NOTE that we assume that encoding in the XDMACH flags is the same as in + * the CC register CSIZE field. + */ + + field = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >> + DMACH_FLAG_PERIPHWIDTH_SHIFT; + regval |= (field << XDMACH_CC_DWIDTH_SHIFT); + + /* 8. Program SIF, DIF to configure the master interface + * used to read data and write data respectively. + * + * RX -> Source is peripheral + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHAHB_MASK) == DMACH_FLAG_PERIPHAHB_AHB_IF1) + { + regval |= XDMACH_CC_SIF; + } + + /* RX -> Destination is memory */ + + if ((xdmach->flags & DMACH_FLAG_MEMAHB_MASK) == DMACH_FLAG_MEMAHB_AHB_IF1) + { + regval |= XDMACH_CC_DIF; + } + + /* 9. Program PERID to select the active hardware request line + * (only relevant for a peripheral synchronized transfer). + */ + + if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0) + { + int pid; + + /* Get the PID from the DMACH flags */ + + pid = (xdmach->flags & DMACH_FLAG_PERIPHPID_MASK) >> + DMACH_FLAG_PERIPHPID_SHIFT; + + /* Look up the DMA channel code for RX: Peripheral is the source. */ + + field = sam_source_channel(xdmach, pid); + regval |= (field << XDMACH_CC_CSIZE_SHIFT); + +#if 0 /* Not supported */ + /* 10. Set SWREQ to use software request (only relevant for a + * peripheral synchronized transfer). + */ + + regval |= XDMACH_CC_SWREQ; +#endif + } + + 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 chnext_view1_s * +sam_allocdesc(struct sam_xdmach_s *xdmach, struct chnext_view1_s *prev, + uint32_t csa, uint32_t cda, uint32_t cubc) +{ + struct sam_xdmac_s *xdmac = sam_controller(xdmach); + struct chnext_view1_s *descr = NULL; + int i; + + /* Sanity check -- csa == 0 is the indication that the link is unused. + * Obviously setting it to zero would break that usage. + */ + +#ifdef CONFIG_DEBUG + if (csa != 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 csa == 0. That csa 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_SAMV7_NLLDESC; i++) + { + if (g_lldesc[i].csa == 0) + { + /* We have it. Initialize the new link list entry */ + + descr = &g_lldesc[i]; + descr->cnda = 0; /* Next Descriptor Address */ + descr->cubc = cubc; /* Channel Microblock Control Register */ + descr->csa = csa; /* Source address */ + descr->cda = cda; /* Destination 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 = descr; + } + 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. + * + * Set the NDE field in the previous descriptor; Clear the + * NDE field in the final descriptor. + */ + + prev->cubc |= CHNEXT_UBC_NDE; + + /* Link the previous tail to the new tail. + * REVISIT: This assumes that the next description is fetched + * via AHB IF0. + */ + + prev->cnda = (uint32_t)sam_physramaddr((uintptr_t)descr); + } + + /* In any event, this is the new tail of the list */ + + xdmach->lltail = descr; + + /* Assume that we will be doing multiple buffer transfers and that + * that hardware will be accessing the descriptor via DMA. + */ + + arch_clean_dcache((uintptr_t)descr, + (uintptr_t)descr + sizeof(struct chnext_view1_s)); + break; + } + } + + /* Because we hold a count from the counting semaphore, the above + * search loop should always be successful. + */ + + sam_givechsem(xdmac); + DEBUGASSERT(descr != NULL); + } + + return descr; +} + +/**************************************************************************** + * 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 chnext_view1_s *descr; + uintptr_t paddr; + + /* Get the head of the link list and detach the link list from the DMA + * channel + */ + + descr = xdmach->llhead; + xdmach->llhead = NULL; + xdmach->lltail = NULL; + + while (descr != NULL) + { + /* Valid, in-use descriptors never have csa == 0 */ + + DEBUGASSERT(descr->csa != 0); + + /* Get the physical address of the next descriptor in the list */ + + paddr = descr->cnda; + + /* Free the descriptor by simply nullifying it and bumping up the + * semaphore count. + */ + + memset(descr, 0, sizeof(struct chnext_view1_s)); + sam_givedsem(xdmac); + + /* Get the virtual address of the next descriptor in the list */ + + descr = (struct chnext_view1_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 cubc; + + /* If we are appending a buffer to a linklist, then re-use the previously + * calculated CC register value. Otherwise, create the CC register value + * from the properties of the transfer. + */ + + if (!xdmach->llhead) + { + xdmach->cc = sam_txcc(xdmach); + } + + /* Calculate the number of transfers for CUBC */ + + cubc = sam_cubc(xdmach, nbytes); + cubc |= (CHNEXT_UBC_NVIEW_1 | CHNEXT_UBC_NSEN); + + /* Add the new link list entry */ + + if (!sam_allocdesc(xdmach, xdmach->lltail, maddr, paddr, cubc)) + { + return -ENOMEM; + } + + 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 cubc; + + /* If we are appending a buffer to a linklist, then re-use the previously + * calculated CC register value. Otherwise, create the CC register value + * from the properties of the transfer. + */ + + if (!xdmach->llhead) + { + xdmach->cc = sam_rxcc(xdmach); + } + + /* Calculate the number of transfers for CUBC */ + + cubc = sam_cubc(xdmach, nbytes); + cubc |= (CHNEXT_UBC_NVIEW_1 | CHNEXT_UBC_NSEN); + + /* Add the new link list entry */ + + if (!sam_allocdesc(xdmach, xdmach->lltail, paddr, maddr, cubc)) + { + return -ENOMEM; + } + + 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 chnext_view1_s *llhead = xdmach->llhead; + + /* 1. Read the XDMAC Global Channel Status Register (XDMAC_GS) to choose a + * free channel. + * + * In this implementation, the free channel is assigned in a different + * manner. + */ + + /* 2. Clear any pending interrupts from any previous XDMAC transfer by + * reading the XDMAC Channel Interrupt Status Register (CIS). + */ + + (void)sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET); + + /* 3. Write the starting source address in the Channel Source Address (CSA) + * Register. + */ + + DEBUGASSERT(llhead != NULL && llhead->csa != 0); + sam_putdmach(xdmach, llhead->csa, SAM_XDMACH_CSA_OFFSET); + + /* 4. Write the starting destination address in the Channel Destination + * Address Register (CDA). + */ + + sam_putdmach(xdmach, llhead->cda, SAM_XDMACH_CDA_OFFSET); + + /* 5. Program field UBLEN in the XDMAC Channel Microblock Control (CUBC) + * Register with the number of data. + */ + + sam_putdmach(xdmach, llhead->cubc, SAM_XDMACH_CUBC_OFFSET); + + /* 6. Program the Channel Control (CC) Register */ + + sam_putdmach(xdmach, xdmach->cc, SAM_XDMACH_CC_OFFSET); + + /* 7. Clear the following five registers: + * + * XDMAC Channel Next Descriptor Control (CNDC) Register + * XDMAC Channel Block Control (CBC) Register + * XDMAC Channel Data Stride Memory Set Pattern (CDSMSP) Register + * XDMAC Channel Source Microblock Stride (CSUS) Register + * XDMAC Channel Destination Microblock Stride (CDUS)Register + * + * This respectively indicates that the linked list is disabled, there is + * only one block and striding is disabled + */ + + sam_putdmach(xdmach, 0, SAM_XDMACH_CNDC_OFFSET); + sam_putdmach(xdmach, 0, SAM_XDMACH_CBC_OFFSET); + sam_putdmach(xdmach, 0, SAM_XDMACH_CDSMSP_OFFSET); + sam_putdmach(xdmach, 0, SAM_XDMACH_CSUS_OFFSET); + sam_putdmach(xdmach, 0, SAM_XDMACH_CDUS_OFFSET); + + /* 8. Enable the Microblock interrupt by setting the "End of Block" + * interrupt bit in the XDMAC Channel Interrupt Enable (CIE) Register. + */ + + sam_putdmach(xdmach, XDMAC_CHINT_BI | XDMAC_CHINT_ERRORS, + SAM_XDMACH_CIE_OFFSET); + + /* Enable the Channel Interrupt Enable bit by setting the corresponding + * bit in the XDMAC Global Interrupt Enable (GIE) Register. + */ + + sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GIE_OFFSET); + + /* 9. Enable the channel by setting the corresponding bit in the + * XDMAC Global Channel Enable (GE) Register. The channel bit will + * be set in the GS register by hardware. + */ + + sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GE_OFFSET); + + /* 10. The DMA has been started. Once completed, the DMA channel sets the + * corresponding "End of Block Interrupt" Status bit in the channel + * CIS register and generates an global interrupt for the channel. + * The channel bit will be cleared in the GS register by hardware. + */ + + 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); +#ifdef CONFIG_DEBUG + struct chnext_view1_s *llhead = xdmach->llhead; +#endif + uintptr_t paddr; + uint32_t regval; + + DEBUGASSERT(llhead != NULL && llhead->csa != 0); + + /* 1. Read the XDMAC Global Channel Status Register (XDMAC_GS) to choose a + * free channel. + * + * In this implementation, the free channel is assigned in a different + * manner. + */ + + /* 2. Clear any pending interrupts from any previous XDMAC transfer by + * reading the XDMAC Channel Interrupt Status Register (CIS). + */ + + (void)sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET); + + /* 3. Build a linked list of transfer descriptors in memory. The + * descriptor view is programmable on a per descriptor basis. The + * linked list items structure must be word aligned. CUBC NDE + * must be configured to 0 in the last descriptor to terminate the + * list. + * + * This was done during the RX/TX setup phases of the DMA transfer. + */ + + /* Since we are using view1, I imagine that we need to set the Channel + * Control (CC) Register. + */ + + sam_putdmach(xdmach, xdmach->cc, SAM_XDMACH_CC_OFFSET); + + /* 4. Program field NDA in the XDMAC Channel Next Descriptor Address + * (CNDA) Register with the first descriptor address and bit NDAIF + * with the master interface identifier. + * + * REVIST: Using NDAIF=0. Is that correct? + */ + + paddr = sam_physramaddr((uintptr_t)xdmach->llhead); + sam_putdmach(xdmach, (uint32_t)paddr, SAM_XDMACH_CNDA_OFFSET); + + /* 5. Program the CNDC register: + * + * a. Set NDE to enable the descriptor fetch. + * b. Set NDSUP to update the source address at the descriptor fetch + * time, otherwise clear this bit. + * c. Set NDDUP to update the destination address at the descriptor + * fetch time, otherwise clear this bit. + * d. Program the NDVIEW field to define the length of the first + * descriptor. + */ + + regval = (XDMACH_CNDC_NDE | XDMACH_CNDC_NDVIEW_NDV2); + + /* Update the source address if this is a memory-to-* transfer. + * + * TYPE = 0 -> memory-to-memory + * TYPE = 1 && DSYNC = 1 -> memory-to-peripheral + */ + + if ((xdmach->cc & XDMACH_CC_TYPE) == 0 || + (xdmach->cc & XDMACH_CC_DSYNC) != 0) + { + regval |= XDMACH_CNDC_NDSUP; + } + + /* Update the destination address if this is a *-to-memory transfer. + * + * TYPE = 0 -> memory-to-memory + * TYPE = 1 && DSYNC = 0 -> peripheral-to-memory + */ + + if ((xdmach->cc & XDMACH_CC_TYPE) == 0 || + (xdmach->cc & XDMACH_CC_TYPE) == 0) + { + regval |= XDMACH_CNDC_NDDUP; + } + + sam_putdmach(xdmach, regval, SAM_XDMACH_CNDC_OFFSET); + + /* 6. Enable the End of Linked List interrupt by setting the LI bit in the + * CIE register. + */ + + sam_putdmach(xdmach, XDMAC_CHINT_LI | XDMAC_CHINT_ERRORS, + SAM_XDMACH_CIE_OFFSET); + + /* Enable the Channel Interrupt Enable bit by setting the corresponding + * bit in the XDMAC Global Interrupt Enable (GIE) Register. + */ + + sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GIE_OFFSET); + + /* 7. Enable the channel by setting the corresponding bit in the + * XDMAC Global Channel Enable (GE) Register. The channel bit will + * be set in the GS register by hardware. + */ + + sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GE_OFFSET); + + /* 8. The DMA has been started. Once completed, the DMA channel sets the + * corresponding "End of Block Interrupt" Status bit in the channel + * CIS register and generates an global interrupt for the channel. + * The channel bit will be cleared in the GS register by hardware. + */ + + 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); + uint32_t chanbit = XDMAC_CHAN(xdmach->chan); + + /* Disable all channel interrupts */ + + sam_putdmac(xdmac, chanbit, SAM_XDMAC_GID_OFFSET); + sam_putdmach(xdmach, XDMAC_CHINT_ALL, SAM_XDMACH_CID_OFFSET); + + /* Under normal operation, the software enables a channel by setting the + * associated bit in the Global Channel Enable (GE) Register. The hardware + * then disables a channel on transfer completion by clearing channel bit + * in the GS register. To disable a channel, setting the channel bit in the + * GD register and poll the channel bit in GS register to determine when + * the channel has been disabled. + */ + + sam_putdmac(xdmac, chanbit, SAM_XDMAC_GD_OFFSET); + while ((sam_getdmac(xdmac, SAM_XDMAC_GS_OFFSET) & chanbit) != 0); + + /* 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) + { + arch_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(int irq, void *context) +{ + struct sam_xdmac_s *xdmac = &g_xdmac; + struct sam_xdmach_s *xdmach; + unsigned int chndx; + uint32_t gpending; + uint32_t chpending; + uint32_t bit; + + /* Get the set of pending, unmasked global XDMAC interrupts */ + + gpending = sam_getdmac(xdmac, SAM_XDMAC_GIS_OFFSET) & + sam_getdmac(xdmac, SAM_XDMAC_GIM_OFFSET); + + /* Yes.. Check each bit to see which channel(s) have interrupted */ + + for (chndx = 0; chndx < SAMV7_NDMACHAN && gpending != 0; chndx++) + { + /* Are any interrupts pending for this channel? */ + + bit = XDMAC_CHAN(chndx); + if ((gpending & bit) != 0) + { + xdmach = &g_xdmach[chndx]; + + /* Get the set of interrupts pending for this channel */ + + chpending = sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET) & + sam_getdmach(xdmach, SAM_XDMACH_CIM_OFFSET); + + /* Yes.. Did an error occur? */ + + if ((chpending & XDMAC_CHINT_ERRORS) != 0) + { + /* Yes... Terminate the transfer with an error? */ + + dmalldbg("ERROR: DMA failed: %08x\n", chpending); + sam_dmaterminate(xdmach, -EIO); + } + + /* Is the transfer complete? */ + + else if ((chpending & (XDMAC_CHINT_BI | XDMAC_CHINT_LI)) != 0) + { + /* Yes.. Terminate the transfer with success */ + + sam_dmaterminate(xdmach, OK); + } + + /* Else what? */ + + else + { + dmalldbg("ERROR: Unexpected interrupt: %08x\n", chpending); + DEBUGPANIC(); + } + + /* Clear the bit in the sampled set of pending global interrupts */ + + gpending &= !bit; + } + } + + return OK; +} + +/**************************************************************************** + * 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_CHAN_ALL, SAM_XDMAC_GID_OFFSET); + + /* Disable all DMA channels */ + + sam_putdmac(xdmac, XDMAC_CHAN_ALL, SAM_XDMAC_GD_OFFSET); + + /* Initialize semaphores */ + + sem_init(&xdmac->chsem, 0, 1); + sem_init(&xdmac->dsem, 0, SAMV7_NDMACHAN); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_dmainitialize + * + * Description: + * Initialize the DMA subsystem + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function up_dmainitialize(void) +{ + dmallvdbg("Initialize XDMAC\n"); + + /* Enable peripheral clock */ + + sam_xdmac_enableclk(); + + /* Attach DMA interrupt vector */ + + (void)irq_attach(SAM_IRQ_XDMAC, sam_xdmac_interrupt); + + /* Initialize the controller */ + + sam_dmainitialize(&g_xdmac); + + /* Enable the IRQ at the AIC (still disabled at the DMA controller) */ + + up_enable_irq(SAM_IRQ_XDMAC); +} + +/**************************************************************************** + * Name: sam_dmachannel + * + * 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_dmachannel(uint8_t dmacno, uint32_t chflags) +{ + struct sam_xdmac_s *xdmac = &g_xdmac; + struct sam_xdmach_s *xdmach; + unsigned int chndx; + + /* Search for an available DMA channel with at least the requested FIFO + * size. + */ + + xdmach = NULL; + sam_takechsem(xdmac); + for (chndx = 0; chndx < SAMV7_NDMACHAN; chndx++) + { + struct sam_xdmach_s *candidate = &g_xdmach[chndx]; + if (!candidate->inuse) + { + xdmach = candidate; + xdmach->inuse = true; + + /* Clear the pending Interrupt Status bits by reading the XDMAC + * Channel Interrupt Status (CIS) Register + */ + + (void)sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET); + + /* Disable the channel by writing one to the write-only Global + * Channel Disable (GD) Register + */ + + sam_putdmac(xdmac, XDMAC_CHAN(chndx), SAM_XDMAC_GD_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_dmaconfig + * + * 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_dmachannel are not used and + * sam_dmaconfig() is called before each DMA to configure the DMA channel + * appropriately. + * + * Returned Value: + * None + * + ************************************************************************************/ + +void sam_dmaconfig(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; + dmavdbg("XDMAC CH%d: chflags: %08x\n", xdmach->chan, (int)chflags); +} + +/**************************************************************************** + * Name: sam_dmafree + * + * Description: + * Release a DMA channel. NOTE: The 'handle' used in this argument must + * NEVER be used again until sam_dmachannel() 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; + struct sam_xdmac_s *xdmac; + + dmavdbg("xdmach: %p\n", xdmach); + DEBUGASSERT((xdmach != NULL) && (xdmach->inuse)); + + xdmac = sam_controller(xdmach); + + /* Make sure that the channel is disabled by writing one to the write-only + * Global Channel Disable (GD) Register + */ + + sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GD_OFFSET); + + /* 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_maxtransfer(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 */ + + arch_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_maxtransfer(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 */ + + arch_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_dmachannel() + * + ****************************************************************************/ + +#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 GIS 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->gtype = sam_getdmac(xdmac, SAM_XDMAC_GTYPE_OFFSET); + regs->gcfg = sam_getdmac(xdmac, SAM_XDMAC_GCFG_OFFSET); + regs->gwac = sam_getdmac(xdmac, SAM_XDMAC_GWAC_OFFSET); + regs->gim = sam_getdmac(xdmac, SAM_XDMAC_GIM_OFFSET); + regs->gis = sam_getdmac(xdmac, SAM_XDMAC_GIS_OFFSET); + regs->gs = sam_getdmac(xdmac, SAM_XDMAC_GS_OFFSET); + regs->grs = sam_getdmac(xdmac, SAM_XDMAC_GRS_OFFSET); + regs->gws = sam_getdmac(xdmac, SAM_XDMAC_GWS_OFFSET); + regs->gsws = sam_getdmac(xdmac, SAM_XDMAC_GSWS_OFFSET); + + /* Sample channel registers */ + + regs->cim = sam_getdmach(xdmach, SAM_XDMACH_CIM_OFFSET); + regs->cis = sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET); + regs->csa = sam_getdmach(xdmach, SAM_XDMACH_CSA_OFFSET); + regs->cda = sam_getdmach(xdmach, SAM_XDMACH_CDA_OFFSET); + regs->cnda = sam_getdmach(xdmach, SAM_XDMACH_CNDA_OFFSET); + regs->cndc = sam_getdmach(xdmach, SAM_XDMACH_CNDC_OFFSET); + regs->cubc = sam_getdmach(xdmach, SAM_XDMACH_CUBC_OFFSET); + regs->cbc = sam_getdmach(xdmach, SAM_XDMACH_CBC_OFFSET); + regs->cc = sam_getdmach(xdmach, SAM_XDMACH_CC_OFFSET); + regs->cdsmsp = sam_getdmach(xdmach, SAM_XDMACH_CDSMSP_OFFSET); + regs->csus = sam_getdmach(xdmach, SAM_XDMACH_CSUS_OFFSET); + regs->cdus = sam_getdmach(xdmach, SAM_XDMACH_CDUS_OFFSET); + + irqrestore(flags); +} +#endif /* CONFIG_DEBUG_DMA */ + +/**************************************************************************** + * Name: sam_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + * Assumptions: + * - DMA handle allocated by sam_dmachannel() + * + ****************************************************************************/ + +#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(" GTYPE[%08x]: %08x\n", SAM_XDMAC_GTYPE, regs->gtype); + dmadbg(" GCFG[%08x]: %08x\n", SAM_XDMAC_GCFG, regs->gcfg); + dmadbg(" GWAC[%08x]: %08x\n", SAM_XDMAC_GWAC, regs->gwac); + dmadbg(" GIM[%08x]: %08x\n", SAM_XDMAC_GIM, regs->gim); + dmadbg(" GIS[%08x]: %08x\n", SAM_XDMAC_GIS, regs->gis); + dmadbg(" GS[%08x]: %08x\n", SAM_XDMAC_GS, regs->gs); + dmadbg(" GRS[%08x]: %08x\n", SAM_XDMAC_GRS, regs->grs); + dmadbg(" GWS[%08x]: %08x\n", SAM_XDMAC_GWS, regs->gws); + dmadbg(" GSWS[%08x]: %08x\n", SAM_XDMAC_GSWS, regs->gsws); + dmadbg(" DMA Channel Registers:\n"); + dmadbg(" CIM[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CIM_OFFSET, regs->cim); + dmadbg(" CIS[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CIS_OFFSET, regs->cis); + dmadbg(" CSA[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CSA_OFFSET, regs->csa); + dmadbg(" CDA[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CDA_OFFSET, regs->cda); + dmadbg(" CNDA[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CNDA_OFFSET, regs->cnda); + dmadbg(" CNDC[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CNDC_OFFSET, regs->cndc); + dmadbg(" CUBC[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CUBC_OFFSET, regs->cubc); + dmadbg(" CBC[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CBC_OFFSET, regs->cbc); + dmadbg(" CC[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CC_OFFSET, regs->cc); + dmadbg(" CDSMSP[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CDSMSP_OFFSET, regs->cdsmsp); + dmadbg(" CSUS[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CSUS_OFFSET, regs->csus); + dmadbg(" CDUS[%08x]: %08x\n", xdmach->base + SAM_XDMACH_CDUS_OFFSET, regs->cdus); +} +#endif /* CONFIG_DEBUG_DMA */ +#endif /* CONFIG_SAMV7_XDMAC */ diff --git a/arch/arm/src/samv7/sam_xdmac.h b/arch/arm/src/samv7/sam_xdmac.h new file mode 100644 index 0000000000..a4b14e1a90 --- /dev/null +++ b/arch/arm/src/samv7/sam_xdmac.h @@ -0,0 +1,361 @@ +/************************************************************************************ + * arch/arm/src/samv7/sam_dmac.h + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMV7_SAM_XDMAC_H +#define __ARCH_ARM_SRC_SAMV7_SAM_XDMAC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include + +#include "chip.h" + +/************************************************************************************ + * Definitions + ************************************************************************************/ + +/* Configuration ********************************************************************/ + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_DMA +#endif + +/* DMA ******************************************************************************/ + +/* Flags used to characterize the DMA channel. The naming convention is that one + * side is the peripheral and the other is memory (however, the interface could still + * be used if, for example, both sides were memory although the naming would be + * awkward) + * + * Encoding: + * + * .... .... .... MMMM .PPP PPPP PPPP PPPP + * .... .... .... .... .... .... .... .... Configurable properties of the channel + * .... .... .... .... .PPP PPPP PPPP PPPP Peripheral endpoint characteristics + * .... .... .... MMMM .... .... .... .... Memory endpoint characteristics + */ + +/* Bits 0-1: Configurable properties of the channel + * + * .... .... .... .... .... .... .... .... Configurable properties of the channel + * + * NOTE: Many "peripheral" attributes are really "channel" attributes for + * the samv7D4's XDMAC since it does not support peripheral-to-peripheral + * DMA. + */ + +# define DMACH_FLAG_FIFOCFG_LARGEST (0) /* No FIFO controls */ +# define DMACH_FLAG_FIFOCFG_HALF (0) +# define DMACH_FLAG_FIFOCFG_SINGLE (0) + +/* Bits 0-15: Peripheral endpoint characteristics + * + * .... .... .... .... .PPP PPPP PPPP PPPP Peripheral endpoint characteristics + * .... .... .... .... .... .... .III IIII Peripheral ID, range 0-67 + * .... .... .... .... .... .... .... .... No HW Handshaking + * .... .... .... .... .... .... P... .... 0=memory; 1=peripheral + * .... .... .... .... .... ...N .... .... Peripheral ABH layer number + * .... .... .... .... .... .WW. .... .... Peripheral width + * .... .... .... .... .... A... .... .... Auto-increment peripheral address + * .... .... .... .... .SSS .... .... .... Peripheral chunk size + */ + +#define DMACH_FLAG_PERIPHPID_SHIFT (0) /* Bits 0-7: Peripheral PID */ +#define DMACH_FLAG_PERIPHPID_MASK (0x7f << DMACH_FLAG_PERIPHPID_SHIFT) +# define DMACH_FLAG_PERIPHPID(n) ((uint32_t)(n) << DMACH_FLAG_PERIPHPID_SHIFT) +# define DMACH_FLAG_PERIPHPID_MAX DMACH_FLAG_PERIPHPID_MASK +#define DMACH_FLAG_PERIPHH2SEL (0) /* No HW handshaking */ +#define DMACH_FLAG_PERIPHISPERIPH (1 << 7) /* Bit 7: 0=memory; 1=peripheral */ +#define DMACH_FLAG_PERIPHAHB_MASK (1 << 8) /* Bit 8: Peripheral ABH layer 1 */ +# define DMACH_FLAG_PERIPHAHB_AHB_IF0 (0) +# define DMACH_FLAG_PERIPHAHB_AHB_IF1 DMACH_FLAG_PERIPHAHB_MASK +#define DMACH_FLAG_PERIPHWIDTH_SHIFT (9) /* Bits 9-10: Peripheral width */ +#define DMACH_FLAG_PERIPHWIDTH_MASK (3 << DMACH_FLAG_PERIPHWIDTH_SHIFT) +# define DMACH_FLAG_PERIPHWIDTH_8BITS (0 << DMACH_FLAG_PERIPHWIDTH_SHIFT) /* 8 bits */ +# define DMACH_FLAG_PERIPHWIDTH_16BITS (1 << DMACH_FLAG_PERIPHWIDTH_SHIFT) /* 16 bits */ +# define DMACH_FLAG_PERIPHWIDTH_32BITS (2 << DMACH_FLAG_PERIPHWIDTH_SHIFT) /* 32 bits */ +# define DMACH_FLAG_PERIPHWIDTH_64BITS (3 << DMACH_FLAG_PERIPHWIDTH_SHIFT) /* 64 bits */ +#define DMACH_FLAG_PERIPHINCREMENT (1 << 11) /* Bit 11: Auto-increment peripheral address */ +#define DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT (12) /* Bits 12-14: Peripheral chunk size */ +#define DMACH_FLAG_PERIPHCHUNKSIZE_MASK (7 << DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT) +# define DMACH_FLAG_PERIPHCHUNKSIZE_1 (0 << DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT) /* Peripheral chunksize=1 */ +# define DMACH_FLAG_PERIPHCHUNKSIZE_2 (1 << DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT) /* No chunksize=2 */ +# define DMACH_FLAG_PERIPHCHUNKSIZE_4 (2 << DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT) /* Peripheral chunksize=4 */ +# define DMACH_FLAG_PERIPHCHUNKSIZE_8 (3 << DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT) /* Peripheral chunksize=8 */ +# define DMACH_FLAG_PERIPHCHUNKSIZE_16 (4 << DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT) /* Peripheral chunksize=16 */ + +/* Bits 16-19: Memory endpoint characteristics + * + * .... .... .... MMMM .... .... .... .... Memory endpoint characteristics + * .... .... .... .... .... .... .... .... No memory peripheral ID, range 0-49 + * .... .... .... .... .... .... .... .... No HW Handshaking + * .... .... .... .... .... .... .... .... No peripheral-to-peripheral + * .... .... .... ...N .... .... .... .... Memory ABH layer number + * .... .... .... .... .... .... .... .... No memory width + * .... .... .... ..A. .... .... .... .... Auto-increment memory address + * .... .... .... .... .... .... .... .... No memory chunk size + * .... .... .... BB.. .... .... .... .... Memory burst size + */ + +#define DMACH_FLAG_MEMPID(n) (0) /* No memory peripheral identifier */ +# define DMACH_FLAG_MEMPID_MAX (0) +#define DMACH_FLAG_MEMH2SEL (0) /* No HW handshaking */ +#define DMACH_FLAG_MEMISPERIPH (0) /* No peripheral-to-peripheral */ +#define DMACH_FLAG_MEMAHB_MASK (1 << 16) /* Bit 16: Memory ABH layer 1 */ +# define DMACH_FLAG_MEMAHB_AHB_IF0 (0) +# define DMACH_FLAG_MEMAHB_AHB_IF1 DMACH_FLAG_MEMAHB_MASK + +#define DMACH_FLAG_MEMWIDTH_8BITS (0) /* Only peripheral data width */ +#define DMACH_FLAG_MEMWIDTH_16BITS (0) +#define DMACH_FLAG_MEMWIDTH_32BITS (0) +#define DMACH_FLAG_MEMWIDTH_64BITS (0) + +#define DMACH_FLAG_MEMINCREMENT (1 << 17) /* Bit 17: Auto-increment memory address */ + +#define DMACH_FLAG_MEMCHUNKSIZE_1 (0) /* Only peripheral chunk size */ +#define DMACH_FLAG_MEMCHUNKSIZE_2 (0) +#define DMACH_FLAG_MEMCHUNKSIZE_4 (0) +#define DMACH_FLAG_MEMCHUNKSIZE_8 (0) +#define DMACH_FLAG_MEMCHUNKSIZE_16 (0) + +#define DMACH_FLAG_MEMBURST_SHIFT (18) /* Bits 18-19: Memory burst size */ +#define DMACH_FLAG_MEMBURST_MASK (3 << DMACH_FLAG_MEMBURST_SHIFT) +# define DMACH_FLAG_MEMBURST_1 (0 << DMACH_FLAG_MEMBURST_SHIFT) +# define DMACH_FLAG_MEMBURST_4 (1 << DMACH_FLAG_MEMBURST_SHIFT) +# define DMACH_FLAG_MEMBURST_8 (2 << DMACH_FLAG_MEMBURST_SHIFT) +# define DMACH_FLAG_MEMBURST_16 (3 << DMACH_FLAG_MEMBURST_SHIFT) + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +typedef FAR void *DMA_HANDLE; +typedef void (*dma_callback_t)(DMA_HANDLE handle, void *arg, int result); + +/* The following is used for sampling DMA registers when CONFIG DEBUG_DMA is selected */ + +#ifdef CONFIG_DEBUG_DMA +struct sam_dmaregs_s +{ + /* Global Registers */ + + uint32_t gtype; /* Global Type Register */ + uint32_t gcfg; /* Global Configuration Register */ + uint32_t gwac; /* Global Weighted Arbiter Configuration Register */ + uint32_t gim; /* Global Interrupt Mask Register */ + uint32_t gis; /* Global Interrupt Status Register */ + uint32_t gs; /* Global Channel Status Register */ + uint32_t grs; /* Global Channel Read Suspend Register */ + uint32_t gws; /* Global Channel Write Suspend Register */ + uint32_t gsws; /* Global Channel Software Request Status Register */ + + /* Channel Registers */ + + uint32_t cim; /* Channel Interrupt Mask Register */ + uint32_t cis; /* Channel Interrupt Status Register */ + uint32_t csa; /* Channel Source Address Register */ + uint32_t cda; /* Channel Destination Address Register */ + uint32_t cnda; /* Channel Next Descriptor Address Register */ + uint32_t cndc; /* Channel Next Descriptor Control Register */ + uint32_t cubc; /* Channel Microblock Control Register */ + uint32_t cbc; /* Channel Block Control Register */ + uint32_t cc; /* Channel Configuration Register */ + uint32_t cdsmsp; /* Channel Data Stride Memory Set Pattern */ + uint32_t csus; /* Channel Source Microblock Stride */ + uint32_t cdus; /* Channel Destination Microblock Stride */ +}; +#endif /* CONFIG_DEBUG_DMA */ + +/************************************************************************************ + * Inline Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +/************************************************************************************ + * Name: sam_dmachannel + * + * Description: + * 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'. However, 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_dmachannel(uint8_t dmacno, uint32_t chflags); + +/************************************************************************************ + * Name: sam_dmaconfig + * + * 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_dmachannel are not used and + * sam_dmaconfig() is called before each DMA to configure the DMA channel + * appropriately. + * + * Returned Value: + * None + * + ************************************************************************************/ + +void sam_dmaconfig(DMA_HANDLE handle, uint32_t chflags); + +/************************************************************************************ + * Name: sam_dmafree + * + * Description: + * Release a DMA channel. NOTE: The 'handle' used in this argument must NEVER be + * used again until sam_dmachannel() is called again to re-gain a valid handle. + * + * Returned Value: + * None + * + ************************************************************************************/ + +void sam_dmafree(DMA_HANDLE handle); + +/************************************************************************************ + * 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 discontinuous 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); + +/************************************************************************************ + * 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 discontinuous 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); + +/************************************************************************************ + * Name: sam_dmastart + * + * Description: + * Start the DMA transfer + * + ************************************************************************************/ + +int sam_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg); + +/************************************************************************************ + * 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); + +/************************************************************************************ + * Name: sam_dmasample + * + * Description: + * Sample DMA register contents + * + ************************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void sam_dmasample(DMA_HANDLE handle, struct sam_dmaregs_s *regs); +#else +# define sam_dmasample(handle,regs) +#endif + +/************************************************************************************ + * Name: sam_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + ************************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA +void sam_dmadump(DMA_HANDLE handle, const struct sam_dmaregs_s *regs, const char *msg); +#else +# define sam_dmadump(handle,regs,msg) +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_SAMV7_SAM_XDMAC_H */