From 754c8493fff9d2ac7a1a350e01b18ee4640d6555 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 29 Jul 2018 11:31:26 -0600 Subject: [PATCH] arch/arm/src/samd5e5: Leverage the SAMD2L2 DMA controller. --- arch/arm/include/samd5e5/chip.h | 21 + arch/arm/src/samd5e5/Kconfig | 3 - arch/arm/src/samd5e5/Make.defs | 4 + arch/arm/src/samd5e5/chip/sam_dmac.h | 31 +- arch/arm/src/samd5e5/sam_dmac.c | 1367 +++++++++++++++++++++++++ arch/arm/src/samd5e5/sam_dmac.h | 335 ++++++ arch/arm/src/samd5e5/sam_i2c_master.c | 5 +- arch/arm/src/samd5e5/sam_idle.c | 1 - arch/arm/src/samd5e5/sam_serial.c | 1 - arch/arm/src/samd5e5/sam_usart.h | 1 - arch/arm/src/samd5e5/sam_userspace.h | 1 - 11 files changed, 1745 insertions(+), 25 deletions(-) create mode 100644 arch/arm/src/samd5e5/sam_dmac.c create mode 100644 arch/arm/src/samd5e5/sam_dmac.h diff --git a/arch/arm/include/samd5e5/chip.h b/arch/arm/include/samd5e5/chip.h index 0356036c2d..a627234d55 100644 --- a/arch/arm/include/samd5e5/chip.h +++ b/arch/arm/include/samd5e5/chip.h @@ -65,6 +65,7 @@ # define SAMD5E5_NTCCOMP 2 /* TC compare */ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -89,6 +90,7 @@ # define SAMD5E5_NTC 8 /* TC0-TC7 */ # define SAMD5E5_NTCCOMP 2 /* TC compare */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -113,6 +115,7 @@ # define SAMD5E5_NTC 8 /* TC0-TC7 */ # define SAMD5E5_NTCCOMP 2 /* TC compare */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -137,6 +140,7 @@ # define SAMD5E5_NTC 8 /* TC0-TC7 */ # define SAMD5E5_NTCCOMP 2 /* TC compare */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -161,6 +165,7 @@ # define SAMD5E5_NTC 6 /* TC0-TC5 */ # define SAMD5E5_NTCCOMP 2 /* TC compare */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -185,6 +190,7 @@ # define SAMD5E5_NTC 6 /* TC0-TC5 */ # define SAMD5E5_NTCCOMP 2 /* TC compare */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -209,6 +215,7 @@ # define SAMD5E5_NTC 6 /* TC0-TC5 */ # define SAMD5E5_NTCCOMP 2 /* TC compare */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -235,6 +242,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 1 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -261,6 +269,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 1 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -287,6 +296,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -313,6 +323,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -339,6 +350,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -383,6 +395,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -409,6 +422,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -435,6 +449,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -461,6 +476,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -487,6 +503,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 10 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -513,6 +530,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -539,6 +557,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 2 /* SDHC0-1 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -565,6 +584,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ @@ -591,6 +611,7 @@ # define SAMD5E5_NTCC24 2 /* TCC 24-bit */ # define SAMD5E5_NTCC16 3 /* TCC 16-bit */ # define SAMD5E5_NSDHC 1 /* SDHC0 */ +# define SAMD5E5_NDMACHAN 32 /* 32 DMA channels */ # define SAMD5E5_PCCSIZE 14 /* PCC data size */ # define SAMD5E5_NCCL 4 /* CCL */ # define SAMD5E5_NEVTCHAN 32 /* Event system channels */ diff --git a/arch/arm/src/samd5e5/Kconfig b/arch/arm/src/samd5e5/Kconfig index 2410ff1d75..13436aae37 100644 --- a/arch/arm/src/samd5e5/Kconfig +++ b/arch/arm/src/samd5e5/Kconfig @@ -348,8 +348,6 @@ config SAMD5E5_ADC config SAMD5E5_CMCC bool "Cortex M Cache Controller (CMCC)" default n - depends on ARCH_CHIP_SAM4E - config SAMD5E5_DAC bool "Digital-to-Analog Converter" default n @@ -358,7 +356,6 @@ config SAMD5E5_DMAC bool "DMA Controller" default n select ARCH_DMA - depends on EXPERIMENTAL config SAMD5E5_EVSYS bool "Event System" diff --git a/arch/arm/src/samd5e5/Make.defs b/arch/arm/src/samd5e5/Make.defs index 6cfb21e63e..8468325b13 100644 --- a/arch/arm/src/samd5e5/Make.defs +++ b/arch/arm/src/samd5e5/Make.defs @@ -124,6 +124,10 @@ ifeq ($(CONFIG_SAMD5E5_CMCC),y) CHIP_CSRCS += sam_cmcc.c endif +ifeq ($(CONFIG_SAMD5E5_DMAC),y) +CHIP_CSRCS += sam_dmac.c +endif + ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CHIP_CSRCS += sam_idle.c endif diff --git a/arch/arm/src/samd5e5/chip/sam_dmac.h b/arch/arm/src/samd5e5/chip/sam_dmac.h index 671a9ce579..50e8f96cc1 100644 --- a/arch/arm/src/samd5e5/chip/sam_dmac.h +++ b/arch/arm/src/samd5e5/chip/sam_dmac.h @@ -42,14 +42,12 @@ #include -#include "chip/sam_memporymap.h" +#include "chip/sam_memorymap.h" /******************************************************************************************** * Pre-processor Definitions ********************************************************************************************/ -#define SAM_DMAC_NCHANNELS 32 /* 32-DMA channels (max) */ - /* DMAC register offsets ********************************************************************/ #define SAM_DMAC_CTRL_OFFSET 0x0000 /* Control Register */ @@ -68,7 +66,7 @@ #define SAM_DMAC_BASEADDR_OFFSET 0x0034 /* Descriptor Memory Section Base Address Register */ #define SAM_DMAC_WRBADDR_OFFSET 0x0038 /* Write-Back Memory Section Base Address Register */ -#define SAM_DMAC_CHAN_OFFSET(n) (0x0040 + ((n) << 4) +#define SAM_DMAC_CHAN_OFFSET(n) (0x0040 + ((n) << 4)) # define SAM_DMAC_CHCTRLA_OFFSET 0x0000 /* Channel Control A Register */ # define SAM_DMAC_CHCTRLB_OFFSET 0x0004 /* Channel Control B Register */ # define SAM_DMAC_CHPRILVL_OFFSET 0x0005 /* Channel Priority Level */ @@ -246,11 +244,13 @@ # define DMAC_CHCTRLA_TRIGSRC(n) ((uint32_t)(n) << DMAC_CHCTRLA_TRIGSRC_SHIFT) #define DMAC_CHCTRLA_TRIGACT_SHIFT (21) /* Bits 20-21: Trigger Action */ #define DMAC_CHCTRLA_TRIGACT_MASK (3 << DMAC_CHCTRLA_TRIGACT_SHIFT) +# define DMAC_CHCTRLA_TRIGACT(n) ((uint32_t)(n) << DMAC_CHCTRLA_TRIGACT_SHIFT) # define DMAC_CHCTRLA_TRIGACT_BLOCK (0 << DMAC_CHCTRLA_TRIGACT_SHIFT) /* Trigger per block transfer */ # define DMAC_CHCTRLA_TRIGACT_BURST (2 << DMAC_CHCTRLA_TRIGACT_SHIFT) /* Trigger per burst transfer */ # define DMAC_CHCTRLA_TRIGACT_TRANS (3 << DMAC_CHCTRLA_TRIGACT_SHIFT) /* Trigger for each transaction */ #define DMAC_CHCTRLA_BURSTLEN_SHIFT (24) /* Bits 24-27: Burst Length (beats-1) */ #define DMAC_CHCTRLA_BURSTLEN_MASK (15 << DMAC_CHCTRLA_BURSTLEN_SHIFT) +# define DMAC_CHCTRLA_BURSTLEN(n) ((uint32_t)((n) - 1) << DMAC_CHCTRLA_BURSTLEN_SHIFT) # define DMAC_CHCTRLA_BURSTLEN_1BEAT (0 << DMAC_CHCTRLA_BURSTLEN_SHIFT) /* Single-beat burst */ # define DMAC_CHCTRLA_BURSTLEN_2BEATS (1 << DMAC_CHCTRLA_BURSTLEN_SHIFT) /* 2-beats burst length */ # define DMAC_CHCTRLA_BURSTLEN_3BEATS (2 << DMAC_CHCTRLA_BURSTLEN_SHIFT) /* 3-beats burst length */ @@ -269,6 +269,7 @@ # define DMAC_CHCTRLA_BURSTLEN_16BEATS (15 << DMAC_CHCTRLA_BURSTLEN_SHIFT) /* 16-beats burst length */ #define DMAC_CHCTRLA_THRESHOLD_SHIFT (28) /* Bits 28-29: FIFO Threshold (log2 beats) */ #define DMAC_CHCTRLA_THRESHOLD_MASK (3 << DMAC_CHCTRLA_THRESHOLD_SHIFT) +# define DMAC_CHCTRLA_THRESHOLD(n) ((uint32_t)(n) << DMAC_CHCTRLA_THRESHOLD_SHIFT) # define DMAC_CHCTRLA_THRESHOLD_1BEAT (0 << DMAC_CHCTRLA_THRESHOLD_SHIFT) /* Write after 1 beat */ # define DMAC_CHCTRLA_THRESHOLD_2BEATS (1 << DMAC_CHCTRLA_THRESHOLD_SHIFT) /* Write after 2 beats */ # define DMAC_CHCTRLA_THRESHOLD_4BEATS (2 << DMAC_CHCTRLA_THRESHOLD_SHIFT) /* Write after 3 beats */ @@ -285,9 +286,9 @@ #define DMAC_CHCTRLA_TRIGSRC_SERCOM1_RX 0x06 /* Index of SERCOM1 DMA RX trigger */ #define DMAC_CHCTRLA_TRIGSRC_SERCOM1_TX 0x07 /* Index of SERCOM1 DMA TX trigger */ #define DMAC_CHCTRLA_TRIGSRC_SERCOM2_RX 0x08 /* Index of SERCOM2 DMA RX trigger */ -#define DMAC_CHCTRLA_TRIGSRC_SERCOM2_tX 0x09 /* Index of SERCOM2 DMA TX trigger */ +#define DMAC_CHCTRLA_TRIGSRC_SERCOM2_TX 0x09 /* Index of SERCOM2 DMA TX trigger */ #define DMAC_CHCTRLA_TRIGSRC_SERCOM3_RX 0x0a /* Index of SERCOM3 DMA RX trigger */ -#define DMAC_CHCTRLA_TRIGSRC_SERCOM3_tX 0x0b /* Index of SERCOM3 DMA TX trigger */ +#define DMAC_CHCTRLA_TRIGSRC_SERCOM3_TX 0x0b /* Index of SERCOM3 DMA TX trigger */ #define DMAC_CHCTRLA_TRIGSRC_SERCOM4_RX 0x0c /* Index of SERCOM4 DMA RX trigger */ #define DMAC_CHCTRLA_TRIGSRC_SERCOM4_TX 0x0d /* Index of SERCOM4 DMA TX trigger */ #define DMAC_CHCTRLA_TRIGSRC_SERCOM5_RX 0x0e /* Index of SERCOM5 DMA RX trigger */ @@ -314,9 +315,9 @@ #define DMAC_CHCTRLA_TRIGSRC_TCC2_MC0 0x23 /* Index of TCC2 DMA Match/Compare trigger 0 */ #define DMAC_CHCTRLA_TRIGSRC_TCC2_MC1 0x24 /* Index of TCC2 DMA Match/Compare trigger 1 */ #define DMAC_CHCTRLA_TRIGSRC_TCC2_MC2 0x25 /* Index of TCC2 DMA Match/Compare trigger 2 */ -#define DMAC_CHCTRLA_TRIGSRC_TCC2_OVF 0x26 /* TCC3 DMA overflow/underflow/retrigger trigger */ -#define DMAC_CHCTRLA_TRIGSRC_TCC2_MC0 0x27 /* Index of TCC3 DMA Match/Compare trigger 0 */ -#define DMAC_CHCTRLA_TRIGSRC_TCC2_MC1 0x28 /* Index of TCC3 DMA Match/Compare trigger 1 */ +#define DMAC_CHCTRLA_TRIGSRC_TCC3_OVF 0x26 /* TCC3 DMA overflow/underflow/retrigger trigger */ +#define DMAC_CHCTRLA_TRIGSRC_TCC3_MC0 0x27 /* Index of TCC3 DMA Match/Compare trigger 0 */ +#define DMAC_CHCTRLA_TRIGSRC_TCC3_MC1 0x28 /* Index of TCC3 DMA Match/Compare trigger 1 */ #define DMAC_CHCTRLA_TRIGSRC_TCC4_OVF 0x29 /* TCC4 DMA overflow/underflow/retrigger trigger */ #define DMAC_CHCTRLA_TRIGSRC_TCC4_MC0 0x2a /* Index of TCC4 DMA Match/Compare trigger 0 */ #define DMAC_CHCTRLA_TRIGSRC_TCC4_MC1 0x2b /* Index of TCC4 DMA Match/Compare trigger 1 */ @@ -325,10 +326,10 @@ #define DMAC_CHCTRLA_TRIGSRC_TC0_MC1 0x2e /* Index of TC0 DMA Match/Compare trigger 1 */ #define DMAC_CHCTRLA_TRIGSRC_TC1_OVF 0x2f /* TC1 DMA overflow/underflow trigger */ #define DMAC_CHCTRLA_TRIGSRC_TC1_MC0 0x30 /* Index of TC1 DMA Match/Compare trigger 0 */ -#define DMAC_CHCTRLA_TRIGSRC_TC2_MC1 0x31 /* Index of TC1 DMA Match/Compare trigger 1 */ -#define DMAC_CHCTRLA_TRIGSRC_TC2_OVF 0x32 /* TC1 DMA overflow/underflow trigger */ -#define DMAC_CHCTRLA_TRIGSRC_TC2_MC0 0x33 /* Index of TC1 DMA Match/Compare trigger 0 */ -#define DMAC_CHCTRLA_TRIGSRC_TC2_MC1 0x34 /* Index of TC1 DMA Match/Compare trigger 1 */ +#define DMAC_CHCTRLA_TRIGSRC_TC1_MC1 0x31 /* Index of TC1 DMA Match/Compare trigger 1 */ +#define DMAC_CHCTRLA_TRIGSRC_TC2_OVF 0x32 /* TC2 DMA overflow/underflow trigger */ +#define DMAC_CHCTRLA_TRIGSRC_TC2_MC0 0x33 /* Index of TC2 DMA Match/Compare trigger 0 */ +#define DMAC_CHCTRLA_TRIGSRC_TC2_MC1 0x34 /* Index of TC2 DMA Match/Compare trigger 1 */ #define DMAC_CHCTRLA_TRIGSRC_TC3_OVF 0x35 /* TC3 DMA overflow/underflow trigger */ #define DMAC_CHCTRLA_TRIGSRC_TC3_MC0 0x36 /* Index of TC3 DMA Match/Compare trigger 0 */ #define DMAC_CHCTRLA_TRIGSRC_TC3_MC1 0x37 /* Index of TC3 DMA Match/Compare trigger 1 */ @@ -375,11 +376,11 @@ #define DMAC_CHPRILVL_MASK 0x03 /* Channel priority level */ # define DMAC_CHPRILVL(n) ((uint8_t)(n)) -/* Channel Event Contol Register */ +/* Channel Event Control Register */ #define DMAC_CHEVCTRL_EVACT_SHIFT (0) /* Bits 0-2: Channel event input action */ #define DMAC_CHEVCTRL_EVACT_MASK (7 << DMAC_CHEVCTRL_EVACT_SHIFT) -# define DMAC_CHEVCTRL_EVACT_NOACT (0 << DMAC_CHEVCTRL_EVACT_SHIFT) /* No action +# define DMAC_CHEVCTRL_EVACT_NOACT (0 << DMAC_CHEVCTRL_EVACT_SHIFT) /* No action */ # define DMAC_CHEVCTRL_EVACT_TRIG (1 << DMAC_CHEVCTRL_EVACT_SHIFT) /* Transfer/periodic transfer trigger */ # define DMAC_CHEVCTRL_EVACT_CTRIG (2 << DMAC_CHEVCTRL_EVACT_SHIFT) /* Conditional transfer trigger */ # define DMAC_CHEVCTRL_EVACT_CBLOCK (3 << DMAC_CHEVCTRL_EVACT_SHIFT) /* Conditional block transfer */ diff --git a/arch/arm/src/samd5e5/sam_dmac.c b/arch/arm/src/samd5e5/sam_dmac.c new file mode 100644 index 0000000000..16d534a3fe --- /dev/null +++ b/arch/arm/src/samd5e5/sam_dmac.c @@ -0,0 +1,1367 @@ +/**************************************************************************** + * arch/arm/src/samd5e5/sam_dmac.c + * + * Copyright (C) 2015-2017 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 "up_arch.h" +#include "up_internal.h" +#include "sched/sched.h" + +#include "sam_dmac.h" +#include "sam_periphclks.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ +/* Condition out the whole file unless DMA is selected in the configuration */ + +#ifdef CONFIG_SAMD5E5_DMAC + +/* If SAMD/L support is enabled, then OS DMA support should also be enabled */ + +#ifndef CONFIG_ARCH_DMA +# warning "SAMD5E5 DMA enabled but CONFIG_ARCH_DMA disabled" +#endif + +/* Number of additional DMA descriptors in LPRAM */ + +#ifndef CONFIG_SAMD5E5_DMAC_NDESC +# define CONFIG_SAMD5E5_DMAC_NDESC 0 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* The direction of the transfer */ + +enum sam_dmadir_e +{ + DMADIR_UNKOWN = 0, /* We don't know the direction of the + * transfer yet */ + DMADIR_TX, /* Transmit: Memory to peripheral */ + DMADIR_RX /* Receive: Peripheral to memory */ +}; + +/* This structure describes one DMA channel */ + +struct sam_dmach_s +{ + bool dc_inuse; /* TRUE: The DMA channel is in use */ + uint8_t dc_chan; /* DMA channel number (0-31) */ + uint8_t dc_dir; /* See enum sam_dmadir_e */ + uint32_t dc_txflags; /* DMA channel flags for Tx transfers */ + uint32_t dc_rxflags; /* DMA channel flags for Rx transfers */ + dma_callback_t dc_callback; /* Callback invoked when the DMA completes */ + void *dc_arg; /* Argument passed to callback function */ +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + struct dma_desc_s *dc_head; /* First allocated DMA descriptor */ + struct dma_desc_s *dc_tail; /* DMA descriptor list tail */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void sam_takechsem(void); +static inline void sam_givechsem(void); +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 +static void sam_takedsem(void); +static inline void sam_givedsem(void); +#endif +static void sam_dmaterminate(struct sam_dmach_s *dmach, int result); +static int sam_dmainterrupt(int irq, void *context, FAR void *arg); +static struct dma_desc_s *sam_alloc_desc(struct sam_dmach_s *dmach); +static struct dma_desc_s *sam_append_desc(struct sam_dmach_s *dmach, + uint16_t btctrl, uint16_t btcnt, + uint32_t srcaddr, uint32_t dstaddr); +static void sam_free_desc(struct sam_dmach_s *dmach); +static size_t sam_maxtransfer(struct sam_dmach_s *dmach, uint32_t dmaflags); +static uint16_t sam_bytes2beats(struct sam_dmach_s *dmach, uint32_t dmaflags, + size_t nbytes); +static int sam_txbuffer(struct sam_dmach_s *dmach, uint32_t paddr, + uint32_t maddr, uint32_t dmaflags, size_t nbytes); +static int sam_rxbuffer(struct sam_dmach_s *dmach, uint32_t paddr, + uint32_t maddr, uint32_t dmaflagsr, size_t nbytes); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* These semaphores protect the DMA channel and descriptor tables */ + +static sem_t g_chsem; +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 +static sem_t g_dsem; +#endif + +/* This array describes the state of each DMA channel */ + +static struct sam_dmach_s g_dmach[SAMD5E5_NDMACHAN]; + +/* NOTE: Using the same address as the base descriptors for writeback descriptors + * causes TERR and FERR interrupts to be raised immediately after starting DMA. + */ + +static struct dma_desc_s g_base_desc[SAMD5E5_NDMACHAN] + __attribute__ ((section(".lpram"), aligned(16))); +static struct dma_desc_s g_writeback_desc[SAMD5E5_NDMACHAN] + __attribute__ ((section(".lpram"), aligned(16))); + +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 +/* Additional DMA descriptors for (optional) multi-block transfer support. + * Also positioned in LPRAM. + */ + +static struct dma_desc_s g_dma_desc[CONFIG_SAMD5E5_DMAC_NDESC] + __attribute__ ((section(".lpram"), aligned(16))); +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_takechsem() and sam_givechsem() + * + * Description: + * Used to get exclusive access to the DMA channel table + * + ****************************************************************************/ + +static void sam_takechsem(void) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&g_chsem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +static inline void sam_givechsem(void) +{ + (void)nxsem_post(&g_chsem); +} + +/**************************************************************************** + * Name: sam_takedsem() and sam_givedsem() + * + * Description: + * Used to wait for availability of descriptors in the descriptor table. + * + ****************************************************************************/ + +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 +static void sam_takedsem(void) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&g_dsem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +static inline void sam_givedsem(void) +{ + (void)nxsem_post(&g_dsem); +} +#endif + +/**************************************************************************** + * Name: sam_dmaterminate + * + * Description: + * Terminate the DMA transfer and disable the DMA channel + * + ****************************************************************************/ + +static void sam_dmaterminate(struct sam_dmach_s *dmach, int result) +{ + irqstate_t flags; + int chan; + + /* Disable the DMA channel */ + + flags = enter_critical_section(); + chan = dmach->dc_chan; + putreg8(0, SAM_DMAC_CHCTRLA(chan)); + + /* Reset the DMA channel */ + + putreg8(DMAC_CHCTRLA_SWRST, SAM_DMAC_CHCTRLA(chan)); + + /* Disable all channel interrupts */ + + putreg8(1 << dmach->dc_chan, SAM_DMAC_CHINTENCLR(chan)); + leave_critical_section(flags); + + /* Free the DMA descriptor list */ + + sam_free_desc(dmach); + + /* Perform the DMA complete callback */ + + if (dmach->dc_callback) + { + dmach->dc_callback((DMA_HANDLE)dmach, dmach->dc_arg, result); + } + + dmach->dc_callback = NULL; + dmach->dc_arg = NULL; + dmach->dc_dir = DMADIR_UNKOWN; + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sam_dmainterrupt + * + * Description: + * DMA interrupt handler + * + ****************************************************************************/ + +static int sam_dmainterrupt(int irq, void *context, FAR void *arg) +{ + struct sam_dmach_s *dmach; + unsigned int chndx; + uint16_t intpend; + int chan; + + /* Process all pending channel interrupts */ + + while ((intpend = getreg16(SAM_DMAC_INTPEND)) != 0) + { + /* Get the channel that generated the interrupt */ + + chndx = (intpend & DMAC_INTPEND_ID_MASK) >> DMAC_INTPEND_ID_SHIFT; + dmach = &g_dmach[chndx]; + chan = dmach->dc_chan; + + /* Clear all pending channel interrupt */ + + putreg8(DMAC_INT_ALL, SAM_DMAC_CHINTFLAG(chan)); + + /* Check for transfer error interrupt */ + + if ((intpend & DMAC_INTPEND_TERR) != 0) + { + /* Yes... Terminate the transfer with an error? */ + + sam_dmaterminate(dmach, -EIO); + } + + /* Check for channel transfer complete interrupt */ + + else if ((intpend & DMAC_INTPEND_TCMPL) != 0) + { + /* Yes.. Terminate the transfer with success */ + + sam_dmaterminate(dmach, OK); + } + + else if ((intpend & DMAC_INTPEND_TERR) != 0) + { + dmaerr("Invalid descriptor. Channel %d\n", chndx); + sam_dmaterminate(dmach, -EIO); + } + + /* Check for channel suspend interrupt */ + + else if ((intpend & DMAC_INTPEND_SUSP) != 0) + { + dmaerr("Channel suspended. Channel %d\n", chndx); + /* REVISIT: Do we want to do anything here? */ + } + } + + return OK; +} + +/**************************************************************************** + * Name: sam_alloc_desc + * + * Description: + * Allocate one DMA descriptor. If the base DMA descriptor list entry is + * unused, then that value structure will be returned. Otherwise, this + * function will search for a free descriptor in the g_desc[] list. + * + * NOTE: Descriptor list entries are freed by the DMA interrupt handler. + * However, since the setting/clearing of the 'in use' indication is atomic, + * no special actions need be performed. It would be a good thing to add + * logic to handle the case where all of the entries are exhausted and we + * could wait for some to be freed by the interrupt handler. + * + ****************************************************************************/ + +static struct dma_desc_s *sam_alloc_desc(struct sam_dmach_s *dmach) +{ + struct dma_desc_s *desc; + + /* First check if the base descriptor for the DMA channel is available */ + + desc = &g_base_desc[dmach->dc_chan]; + if (desc->srcaddr == 0) + { + /* Yes, return a pointer to the base descriptor */ + + desc->srcaddr = (uint32_t)-1; /* Any non-zero value */ + return desc; + } +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + else + { + int i; + + /* Wait if no descriptor is available. When we get a semaphore count, + * then there will be at least one free descriptor in the table and + * it is ours. + */ + + sam_takedsem(); + + /* Examine each list entry to find an available one -- i.e., one + * with srcaddr == 0. That srcaddr field is set to zero by the DMA + * transfer complete interrupt handler. The following should be safe + * because that is an atomic operation. + */ + + for (i = 0; i < CONFIG_SAMD5E5_DMAC_NDESC; i++) + { + desc = &g_dma_desc[i]; + if (desc->srcaddr == 0) + { + /* Set the srcaddr to any non-zero value to reserve + * the descriptor. + */ + + desc->srcaddr = (uint32_t)-1; /* Any non-zero value */ + + /* Save a pointer to the first allocated DMA descriptor + * (for sam_free_desc). We have to do this because we cannot + * use the link in the base descriptor; it will be overwritten + * by the writeback. + */ + + if (dmach->dc_head == NULL) + { + dmach->dc_head = desc; + } + + return desc; + } + } + + /* Because we hold a count from the counting semaphore, the above + * search loop should always be successful. + */ + + DEBUGPANIC(); + } +#endif + + return NULL; +} + +/**************************************************************************** + * Name: sam_append_desc + * + * Description: + * Allocate and add one descriptor to the DMA channel's descriptor list. + * + ****************************************************************************/ + +static struct dma_desc_s *sam_append_desc(struct sam_dmach_s *dmach, + uint16_t btctrl, uint16_t btcnt, + uint32_t srcaddr, uint32_t dstaddr) +{ + struct dma_desc_s *desc; + + /* Sanity check -- srcaddr == 0 is the indication that the descriptor is + * unused. Obviously setting it to zero would break that usage. + */ + + DEBUGASSERT(srcaddr != 0); + + /* Allocate a DMA descriptor */ + + desc = sam_alloc_desc(dmach); + if (desc != NULL) + { + /* We have it. Initialize the new descriptor list entry */ + + desc->btctrl = btctrl; /* Block Transfer Control Register */ + desc->btcnt = btcnt; /* Block Transfer Count Register */ + desc->srcaddr = srcaddr; /* Block Transfer Source Address Register */ + desc->dstaddr = dstaddr; /* Block Transfer Destination Address Register */ + desc->descaddr = 0; /* Next Address Descriptor Register */ + + /* And then hook it at the tail of the descriptor list */ + +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + if (dmach->dc_tail != NULL) + { + struct dma_desc_s *prev; + + DEBUGASSERT(desc != g_base_desc[dmach->dc_chan]); + + /* Link the previous tail to the new tail */ + + prev->descaddr = (uint32_t)desc; + } + else +#endif + { + /* There is no previous link. This is the new head of the list */ + + DEBUGASSERT(desc == &g_base_desc[dmach->dc_chan]); + } + +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + /* In either case, this is the new tail of the list. */ + + dmach->dc_tail = desc; +#endif + } + else + { + dmaerr("Failed to allocate descriptor\n"); + } + + return desc; +} + +/**************************************************************************** + * Name: sam_free_desc + * + * Description: + * Free all descriptors in the DMA channel's descriptor list. + * + * NOTE: Called from the DMA interrupt handler. + * + ****************************************************************************/ + +static void sam_free_desc(struct sam_dmach_s *dmach) +{ + struct dma_desc_s *desc; +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + struct dma_desc_s *next; +#endif + + /* Get the base descriptor pointer */ + + desc = &g_base_desc[dmach->dc_chan]; +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + next = dmach->dc_head; + + dmach->dc_head = NULL; + dmach->dc_tail = NULL; +#endif + + /* Nullify the base descriptor */ + + memset(desc, 0, sizeof(struct dma_desc_s)); + +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + /* Reset each additional descriptor in the descriptor list (thereby + * freeing them) + */ + + while (next != NULL) + { + desc = next; + DEBUGASSERT(desc->srcaddr != 0); + + next = (struct dma_desc_s *)desc->descaddr; + memset(desc, 0, sizeof(struct dma_desc_s)); + sam_givedsem(); + } +#endif +} + +/**************************************************************************** + * Name: sam_maxtransfer + * + * Description: + * Maximum number of bytes that can be sent/received in one transfer + * + ****************************************************************************/ + +static size_t sam_maxtransfer(struct sam_dmach_s *dmach, uint32_t dmaflags) +{ + int beatsize; + + /* The number of bytes per beat is 2**BEATSIZE */ + + beatsize = (dmaflags & DMACH_FLAG_BEATSIZE_MASK) >> LPSRAM_BTCTRL_STEPSIZE_SHIFT; + + /* Maximum beats is UINT16_MAX */ + + return (size_t)UINT16_MAX << beatsize; +} + +/**************************************************************************** + * Name: sam_bytes2beats + * + * Description: + * Convert a count of bytes into a count of beats + * + ****************************************************************************/ + +static uint16_t sam_bytes2beats(struct sam_dmach_s *dmach, uint32_t dmaflags, + size_t nbytes) +{ + size_t mask; + int beatsize; + size_t nbeats; + + /* The number of bytes per beat is 2**BEATSIZE */ + + beatsize = (dmaflags & DMACH_FLAG_BEATSIZE_MASK) >> LPSRAM_BTCTRL_STEPSIZE_SHIFT; + + /* The number of beats is then the ceiling of the division */ + + mask = (1 << beatsize) - 1; + nbeats = (nbytes + mask) >> beatsize; + DEBUGASSERT(nbeats <= UINT16_MAX); + return (uint16_t)nbeats; +} + +/**************************************************************************** + * 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_dmach_s *dmach, uint32_t paddr, + uint32_t maddr, uint32_t dmaflags, size_t nbytes) +{ + uint16_t btctrl; + uint16_t btcnt; + uint16_t tmp; + + DEBUGASSERT(dmach->dc_dir == DMADIR_UNKOWN || dmach->dc_dir == DMADIR_TX); + + /* Set up the Block Transfer Control Register configuration: + * + * This are fixed register selections: + * + * LPSRAM_BTCTRL_VALID - Descriptor is valid + * LPSRAM_BTCTRL_EVOSEL_DISABLE - No event output + * LPSRAM_BTCTRL_BLOCKACT_INT - Disable channel and generate interrupt + * when the last block transfer completes. + * + * Other settings come from the channel configuration: + * + * LPSRAM_BTCTRL_BEATSIZE - Determined by DMACH_FLAG_BEATSIZE + * LPSRAM_BTCTRL_SRCINC - Determined by DMACH_FLAG_MEM_INCREMENT + * LPSRAM_BTCTRL_DSTINC - Determined by DMACH_FLAG_PERIPH_INCREMENT + * LPSRAM_BTCTRL_STEPSEL - Determined by DMACH_FLAG_STEPSEL + * LPSRAM_BTCTRL_STEPSIZE - Determined by DMACH_FLAG_STEPSIZE + */ + + btctrl = LPSRAM_BTCTRL_VALID | LPSRAM_BTCTRL_EVOSEL_DISABLE | + LPSRAM_BTCTRL_BLOCKACT_INT; + + tmp = (dmaflags & DMACH_FLAG_BEATSIZE_MASK) >> DMACH_FLAG_BEATSIZE_SHIFT; + btctrl |= tmp << LPSRAM_BTCTRL_BEATSIZE_SHIFT; + + /* See Addressing on page 264 of the datasheet. + * When increment is used, we have to adjust the address in the descriptor + * based on the beat count remaining in the block + */ + + /* Set up the Block Transfer Count Register configuration */ + + btcnt = sam_bytes2beats(dmach, dmaflags, nbytes); + + if ((dmaflags & DMACH_FLAG_MEM_INCREMENT) != 0) + { + btctrl |= LPSRAM_BTCTRL_SRCINC; + maddr += nbytes; + } + + if ((dmaflags & DMACH_FLAG_PERIPH_INCREMENT) != 0) + { + btctrl |= LPSRAM_BTCTRL_DSTINC; + paddr += nbytes; + } + + if ((dmaflags & DMACH_FLAG_STEPSEL) == DMACH_FLAG_STEPSEL_PERIPH) + { + btctrl |= LPSRAM_BTCTRL_STEPSEL; + } + + tmp = (dmaflags & DMACH_FLAG_STEPSIZE_MASK) >> LPSRAM_BTCTRL_STEPSIZE_SHIFT; + btctrl |= tmp << LPSRAM_BTCTRL_STEPSIZE_SHIFT; + + /* Add the new descriptor list entry */ + + if (!sam_append_desc(dmach, btctrl, btcnt, maddr, paddr)) + { + return -ENOMEM; + } + + dmach->dc_dir = DMADIR_TX; + 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_dmach_s *dmach, uint32_t paddr, + uint32_t maddr, uint32_t dmaflags, size_t nbytes) +{ + uint16_t btctrl; + uint16_t btcnt; + uint16_t tmp; + + DEBUGASSERT(dmach->dc_dir == DMADIR_UNKOWN || dmach->dc_dir == DMADIR_RX); + + /* Set up the Block Transfer Control Register configuration: + * + * This are fixed register selections: + * + * LPSRAM_BTCTRL_VALID - Descriptor is valid + * LPSRAM_BTCTRL_EVOSEL_DISABLE - No event output + * LPSRAM_BTCTRL_BLOCKACT_INT - Disable channel and generate interrupt + * when the last block transfer completes. + * + * Other settings come from the channel configuration: + * + * LPSRAM_BTCTRL_BEATSIZE - Determined by DMACH_FLAG_BEATSIZE + * LPSRAM_BTCTRL_SRCINC - Determined by DMACH_FLAG_PERIPH_INCREMENT + * LPSRAM_BTCTRL_DSTINC - Determined by DMACH_FLAG_MEM_INCREMENT + * LPSRAM_BTCTRL_STEPSEL - Determined by DMACH_FLAG_STEPSEL + * LPSRAM_BTCTRL_STEPSIZE - Determined by DMACH_FLAG_STEPSIZE + */ + + btctrl = LPSRAM_BTCTRL_VALID | LPSRAM_BTCTRL_EVOSEL_DISABLE | + LPSRAM_BTCTRL_BLOCKACT_INT; + + tmp = (dmaflags & DMACH_FLAG_BEATSIZE_MASK) >> DMACH_FLAG_BEATSIZE_SHIFT; + btctrl |= tmp << LPSRAM_BTCTRL_BEATSIZE_SHIFT; + + /* Set up the Block Transfer Count Register configuration */ + + btcnt = sam_bytes2beats(dmach, dmaflags, nbytes); + + if ((dmaflags & DMACH_FLAG_PERIPH_INCREMENT) != 0) + { + btctrl |= LPSRAM_BTCTRL_SRCINC; + paddr += nbytes; + } + + if ((dmaflags & DMACH_FLAG_MEM_INCREMENT) != 0) + { + btctrl |= LPSRAM_BTCTRL_DSTINC; + maddr += nbytes; + } + + if ((dmaflags & DMACH_FLAG_STEPSEL) == DMACH_FLAG_STEPSEL_MEM) + { + btctrl |= LPSRAM_BTCTRL_STEPSEL; + } + + tmp = (dmaflags & DMACH_FLAG_STEPSIZE_MASK) >> LPSRAM_BTCTRL_STEPSIZE_SHIFT; + btctrl |= tmp << LPSRAM_BTCTRL_STEPSIZE_SHIFT; + + /* Add the new descriptor list entry */ + + if (!sam_append_desc(dmach, btctrl, btcnt, paddr, maddr)) + { + return -ENOMEM; + } + + dmach->dc_dir = DMADIR_RX; + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_dmainitialize + * + * Description: + * Initialize the DMA subsystem + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function up_dmainitialize(void) +{ + dmainfo("Initialize DMAC\n"); + int i; + + /* Initialize global semaphores */ + + nxsem_init(&g_chsem, 0, 1); +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + nxsem_init(&g_dsem, 0, CONFIG_SAMD5E5_DMAC_NDESC); +#endif + + /* Initialized the DMA channel table */ + + for (i = 0; i < SAMD5E5_NDMACHAN; i++) + { + g_dmach[i].dc_chan = i; + } + + /* Clear descriptors (this will not be done automatically because they are + * not in .bss). + */ + + memset(g_base_desc, 0, sizeof(struct dma_desc_s)*SAMD5E5_NDMACHAN); + memset(g_writeback_desc, 0, sizeof(struct dma_desc_s)*SAMD5E5_NDMACHAN); +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + memset(g_dma_desc, 0, sizeof(struct dma_desc_s)*CONFIG_SAMD5E5_DMAC_NDESC); +#endif + + /* Enable DMA clocking */ + + sam_ahb_dmac_enableperiph(); + + /* Disable and reset the DMAC */ + + putreg16(0, SAM_DMAC_CTRL); + putreg16(DMAC_CTRL_SWRST, SAM_DMAC_CTRL); + + /* Attach DMA interrupt vectors */ + + (void)irq_attach(SAM_IRQ_DMACH0, sam_dmainterrupt, NULL); + (void)irq_attach(SAM_IRQ_DMACH1, sam_dmainterrupt, NULL); + (void)irq_attach(SAM_IRQ_DMACH2, sam_dmainterrupt, NULL); + (void)irq_attach(SAM_IRQ_DMACH3, sam_dmainterrupt, NULL); + (void)irq_attach(SAM_IRQ_DMACH4_31, sam_dmainterrupt, NULL); + + /* Set the LPRAM DMA descriptor table addresses. These can only be + * written when the DMAC is disabled. + */ + + putreg32((uint32_t)g_base_desc, SAM_DMAC_BASEADDR); + putreg32((uint32_t)g_writeback_desc, SAM_DMAC_WRBADDR); + + /* Setup the Priority control register for each priority level */ +#warning Missing logic + + /* Enable the DMA controller and all priority levels */ + + putreg16(DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN0 | DMAC_CTRL_LVLEN1 | + DMAC_CTRL_LVLEN2, SAM_DMAC_CTRL); + + /* Enable the IRQs at the NVIC (still disabled at the DMA controller) */ + + up_enable_irq(SAM_IRQ_DMACH0); + up_enable_irq(SAM_IRQ_DMACH1); + up_enable_irq(SAM_IRQ_DMACH2); + up_enable_irq(SAM_IRQ_DMACH3); + up_enable_irq(SAM_IRQ_DMACH4_31); +} + +/**************************************************************************** + * Name: sam_dmachannel + * + * Description: + * Allocate a DMA channel. This function sets aside a DMA channel and + * 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 if the required FIFO size is available, this function + * returns a non-NULL, void* DMA channel handle. NULL is returned on any + * failure. + * + ****************************************************************************/ + +DMA_HANDLE sam_dmachannel(uint32_t txflags, uint32_t rxflags) +{ + struct sam_dmach_s *dmach; + irqstate_t flags; + unsigned int chndx; + + /* Search for an available DMA channel */ + + dmach = NULL; + sam_takechsem(); + + for (chndx = 0; chndx < SAMD5E5_NDMACHAN; chndx++) + { + struct sam_dmach_s *candidate = &g_dmach[chndx]; + if (!candidate->dc_inuse) + { + dmach = candidate; + dmach->dc_inuse = true; + + /* Set the DMA channel flags */ + + dmach->dc_txflags = txflags; + dmach->dc_rxflags = rxflags; + + /* Disable the DMA channel */ + + flags = enter_critical_section(); + putreg8(0, SAM_DMAC_CHCTRLA(chndx)); + + /* Reset the channel */ + + putreg8(DMAC_CHCTRLA_SWRST, SAM_DMAC_CHCTRLA(chndx)); + + /* Disable all channel interrupts */ + + putreg8(1 << chndx, SAM_DMAC_CHINTENCLR(chndx)); + leave_critical_section(flags); + break; + } + } + + sam_givechsem(); + + dmainfo("chflags: %08x returning dmach: %p\n", (int)chflags, dmach); + return (DMA_HANDLE)dmach; +} + +/************************************************************************************ + * 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 txflags, uint32_t rxflags) +{ + struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; + + /* Set the new DMA channel flags. */ + + dmainfo("txflags: %08lx rxflags: %08lx\n", + (unsigned long)txflags, (unsigned long)rxflags); + + dmach->dc_txflags = txflags; + dmach->dc_rxflags = rxflags; +} + +/**************************************************************************** + * 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_dmach_s *dmach = (struct sam_dmach_s *)handle; + + dmainfo("dmach: %p\n", dmach); + DEBUGASSERT((dmach != NULL) && (dmach->dc_inuse)); + + /* Mark the channel no longer in use. Clearing the inuse flag is an atomic + * operation and so should be safe. + */ + + dmach->dc_txflags = 0; + dmach->dc_rxflags = 0; + dmach->dc_inuse = false; +} + +/**************************************************************************** + * 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_dmach_s *dmach = (struct sam_dmach_s *)handle; + ssize_t remaining = (ssize_t)nbytes; + size_t maxtransfer; + int ret = OK; + + dmainfo("dmach: %p paddr: %08x maddr: %08x nbytes: %d\n", + dmach, (int)paddr, (int)maddr, (int)nbytes); + DEBUGASSERT(dmach); + +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + dmainfo("dc_head: %p dc_tail: %p\n", dmach->dc_head, dmach->dc_tail); +#endif + + /* The maximum transfer size in bytes depends upon the maximum number of + * transfers and the number of bytes per transfer. + */ + + maxtransfer = sam_maxtransfer(dmach, dmach->dc_txflags); + + /* If this is a large transfer, break it up into smaller buffers */ + + while (remaining > maxtransfer) + { + /* Set up the maximum size transfer */ + + ret = sam_txbuffer(dmach, paddr, maddr, dmach->dc_txflags, 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 do). + * + * REVISIT: What if stepsize is not 1? + */ + + if ((dmach->dc_txflags & DMACH_FLAG_PERIPH_INCREMENT) != 0) + { + paddr += maxtransfer; + } + + if ((dmach->dc_txflags & DMACH_FLAG_MEM_INCREMENT) != 0) + { + maddr += maxtransfer; + } + } + } + + /* Then set up the final buffer transfer */ + + if (ret == OK && remaining > 0) + { + ret = sam_txbuffer(dmach, paddr, maddr, dmach->dc_txflags, remaining); + } + + 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_dmach_s *dmach = (struct sam_dmach_s *)handle; + ssize_t remaining = (ssize_t)nbytes; + size_t maxtransfer; + int ret = OK; + + dmainfo("dmach: %p paddr: %08x maddr: %08x nbytes: %d\n", + dmach, (int)paddr, (int)maddr, (int)nbytes); + DEBUGASSERT(dmach); + +#if CONFIG_SAMD5E5_DMAC_NDESC > 0 + dmainfo("dc_head: %p dc_tail: %p\n", dmach->dc_head, dmach->dc_tail); +#endif + + /* The maximum transfer size in bytes depends upon the maximum number of + * transfers and the number of bytes per transfer. + */ + + maxtransfer = sam_maxtransfer(dmach, dmach->dc_rxflags); + + /* If this is a large transfer, break it up into smaller buffers */ + + while (remaining > maxtransfer) + { + /* Set up the maximum size transfer */ + + ret = sam_rxbuffer(dmach, paddr, maddr, dmach->dc_rxflags, 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). + * + * REVISIT: What if stepsize is not 1? + */ + + if ((dmach->dc_rxflags & DMACH_FLAG_PERIPH_INCREMENT) != 0) + { + paddr += maxtransfer; + } + + if ((dmach->dc_rxflags & DMACH_FLAG_MEM_INCREMENT) != 0) + { + maddr += maxtransfer; + } + } + } + + /* Then set up the final buffer transfer */ + + if (ret == OK && remaining > 0) + { + ret = sam_rxbuffer(dmach, paddr, maddr, dmach->dc_rxflags, remaining); + } + + return ret; +} + +/**************************************************************************** + * Name: sam_dmastart + * + * Description: + * Start the DMA transfer + * + ****************************************************************************/ + +int sam_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg) +{ + struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; + struct dma_desc_s *head; + irqstate_t flags; + uint32_t dmaflags; + uint32_t ctrla; + uint32_t regval32; + uint8_t regval8; + int chan; + int ret = -EINVAL; + + dmainfo("dmach: %p callback: %p arg: %p\n", dmach, callback, arg); + DEBUGASSERT(dmach != NULL && dmach->dc_chan < SAMD5E5_NDMACHAN); + head = &g_base_desc[dmach->dc_chan]; + chan = dmach->dc_chan; + + /* Verify that the DMA has been setup (i.e., at least one entry in the + * descriptor list, the base entry). + */ + + if (head->srcaddr != 0) + { + /* Save the callback info. This will be invoked when the DMA + * completes. + */ + + dmach->dc_callback = callback; + dmach->dc_arg = arg; + + if (dmach->dc_dir == DMADIR_TX) + { + /* Memory to peripheral */ + + dmaflags = dmach->dc_txflags; + } + else + { + /* Peripheral to memory */ + + dmaflags = dmach->dc_rxflags; + } + + /* Clear any pending interrupts from any previous DMAC transfer. */ + + flags = enter_critical_section(); + putreg8(0, SAM_DMAC_CHCTRLA(chan)); + + /* Setup the Channel Control A Register + * + * DMAC_CHCTRLA_ENABLE - Set later + * DMAC_CHCTRLA_RUNSTDBY - Determined by DMACH_FLAG_RUNINSTDBY + * DMAC_CHCTRLA_TRIGSRC - Determined by DMACH_FLAG_PERIPH_TRIGSRC + * DMAC_CHCTRLA_TRIGACT - Determined by DMACH_FLAG_PERIPH_TRIGACT + * DMAC_CHCTRLA_BURSTLEN - Determined by DMACH_FLAG_BURSTLEN + * DMAC_CHCTRLA_THRESHOLD - Determined by DMACH_FLAG_THRESHOLD + */ + + ctrla = 0; + + /* Run in STANDBY option */ + + if (dmaflags & DMACH_FLAG_RUNINSTDBY) + { + ctrla |= DMAC_CHCTRLA_RUNSTDBY; + } + + /* Trigger source */ + + regval32 = (dmaflags & DMACH_FLAG_PERIPH_TRIGSRC_MASK) >> DMACH_FLAG_PERIPH_TRIGSRC_SHIFT; + ctrla |= DMAC_CHCTRLA_TRIGSRC(regval32) ; + + /* Trigger action */ + + regval32 = (dmaflags & DMAC_CHCTRLA_TRIGACT_MASK) >> DMAC_CHCTRLA_TRIGACT_SHIFT; + ctrla |= DMAC_CHCTRLA_TRIGACT(regval32) ; + + /* Burst length */ + + regval32 = ((dmaflags & DMACH_FLAG_BURSTLEN_MASK) >> DMACH_FLAG_BURSTLEN_SHIFT) + 1; + ctrla |= DMAC_CHCTRLA_BURSTLEN(regval32) ; + + /* Threshold */ + + regval32 = ((dmaflags & DMACH_FLAG_BURSTLEN_MASK) >> DMACH_FLAG_BURSTLEN_SHIFT) + 1; + ctrla |= DMAC_CHCTRLA_THRESHOLD(regval32) ; + + putreg32(ctrla, SAM_DMAC_CHCTRLA(chan)); + + /* Setup the Channel Control B Register + * + * DMAC_CHCTRLB_EVIE=0 - No channel input actions + * DMAC_CHCTRLB_EVOE=0 - Channel event output disabled + * DMAC_CHCTRLB_TRIGACT_BEAT - One trigger required for beat transfer + * DMAC_CHCTRLB_CMD_NOACTION - No action + */ + + putreg32(DMAC_CHCTRLB_CMD_NOACTION, SAM_DMAC_CHCTRLB(chan)); + + /* Set up the channel priority register */ + + regval8 = (dmaflags & DMACH_FLAG_PRIORITY_MASK) >> DMACH_FLAG_PRIORITY_SHIFT; + putreg8(DMAC_CHPRILVL(regval8), SAM_DMAC_CHPRILVL(chan)); + + /* Setup channel event control register + * + * DMAC_CHEVCTRL_EVACT - Normal transfer and trigger + * DMAC_CHEVCTRL_EVMODE - Default, block event output selection + * DMAC_CHEVCTRL_EVIE - No channel input actions + * DMAC_CHEVCTRL_EVOE - Channel event output disabled/ + */ + + regval8 = DMAC_CHEVCTRL_EVACT_TRIG | DMAC_CHEVCTRL_EVOMODE_DEFAULT; + putreg8(DMAC_CHPRILVL(regval8), SAM_DMAC_CHEVCTRL(chan)); + + /* Enable the channel */ + + ctrla = getreg8(SAM_DMAC_CHCTRLA(chan)); + ctrla |= DMAC_CHCTRLA_ENABLE; + putreg8(ctrla, SAM_DMAC_CHCTRLA(chan)); + + /* Enable DMA channel interrupts */ + + putreg8(DMAC_INT_TERR | DMAC_INT_TCMPL, SAM_DMAC_CHINTENSET(chan)); + leave_critical_section(flags); + ret = OK; + } + + 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_dmach_s *dmach = (struct sam_dmach_s *)handle; + irqstate_t flags; + + dmainfo("dmach: %p\n", dmach); + DEBUGASSERT(dmach != NULL); + + flags = enter_critical_section(); + sam_dmaterminate(dmach, -EINTR); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sam_dmasample + * + * Description: + * Sample DMA register contents + * + * Assumptions: + * - DMA handle allocated by sam_dmachannel() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +void sam_dmasample(DMA_HANDLE handle, struct sam_dmaregs_s *regs) +{ + struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; + uintptr_t base; + irqstate_t flags; + int chan; + + /* Sample DMAC registers. */ + + flags = enter_critical_section(); + chan = dmach->dc_chan; + base = SAM_DMAC_CHAN_BASE(chan); + regs->chan = chan; + + regs->ctrl = getreg16(SAM_DMAC_CTRL); /* Control Register */ + regs->crcctrl = getreg16(SAM_DMAC_CRCCTRL); /* CRC Control Register */ + regs->crcdatain = getreg32(SAM_DMAC_CRCDATAIN); /* CRC Data Input Register */ + regs->crcchksum = getreg32(SAM_DMAC_CRCCHKSUM); /* CRC Checksum Register */ + regs->crcstatus = getreg8(SAM_DMAC_CRCSTATUS); /* CRC Status Register */ + regs->dbgctrl = getreg8(SAM_DMAC_DBGCTRL); /* Debug Control Register */ + regs->swtrigctrl = getreg32(SAM_DMAC_SWTRIGCTRL); /* Software Trigger Control Register */ + regs->prictrl0 = getreg32(SAM_DMAC_PRICTRL0); /* Priority Control 0 Register */ + regs->intpend = getreg16(SAM_DMAC_INTPEND); /* Interrupt Pending Register */ + regs->intstatus = getreg32(SAM_DMAC_INTSTATUS); /* Interrupt Status Register */ + regs->busych = getreg32(SAM_DMAC_BUSYCH); /* Busy Channels Register */ + regs->pendch = getreg32(SAM_DMAC_PENDCH); /* Pending Channels Register */ + regs->active = getreg32(SAM_DMAC_ACTIVE); /* Active Channels and Levels Register */ + regs->baseaddr = getreg32(SAM_DMAC_BASEADDR); /* Descriptor Memory Section Base Address Register */ + regs->wrbaddr = getreg32(SAM_DMAC_WRBADDR); /* Write-Back Memory Section Base Address Register */ + + regs->chctrla = getreg32(base + SAM_DMAC_CHCTRLA_OFFSET); /* Channel Control A Register */ + regs->chctrlb = getreg8(base + SAM_DMAC_CHCTRLB_OFFSET); /* Channel Control B Register */ + regs->chprilvl = getreg8(base + SAM_DMAC_CHINTFLAG_OFFSET); /* Channel Priority Level */ + regs->chevctrl = getreg8(base + SAM_DMAC_CHINTFLAG_OFFSET); /* Channel Event Control Register */ + regs->chintflag = getreg8(base + SAM_DMAC_CHINTFLAG_OFFSET); /* Channel Interrupt Flag Status and Clear Register */ + regs->chstatus = getreg8(base + SAM_DMAC_CHSTATU_OFFSETS); /* Channel Status Register */ + + leave_critical_section(flags); +} +#endif /* CONFIG_DEBUG_DMA_INFO */ + +/**************************************************************************** + * Name: sam_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + * Assumptions: + * - DMA handle allocated by sam_dmachannel() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +void sam_dmadump(DMA_HANDLE handle, const struct sam_dmaregs_s *regs, + const char *msg) +{ + struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; + irqstate_t flags; + + flags = enter_critical_section(); + dmainfo("%s\n", msg); + dmainfo(" DMAC Global Registers:\n"); + dmainfo(" CTRL: %04x CRCCTRL: %04x CRCDATAIN: %08x CRCCHKSUM: %08x\n", + regs->ctrl, regs->crcctrl, regs->crcdatain, regs->crcchksum); + dmainfo(" CRCSTATUS: %02x DBGCTRL: %02x SWTRIGCTRL: %08x PRICTRL0: %08x\n", + regs->crcstatus, regs->dbgctrl, regs->swtrigctrl, regs->prictrl0); + dmainfo(" INTPEND: %04x INTSTATUS: %08x BUSYCH: %08x PENDCH: %08x\n", + regs->intpend, regs->intstatus, regs->busych, regs->pendch); + dmainfo(" ACTIVE: %08x BASEADDR: %08x WRBADDR: %08x\n", + regs->active, regs->baseaddr, regs->wrbaddr); + + dmainfo(" DMAC Channel %u Registers:\n", regs->chan); + dmainfo(" CHCRTRLA: %08x CHCRTRLB: %02x CHPRILVL: %02x CHPRILVL: %02x\n", + regs->chctrla, regs->chctrlb, regs->chprilvl, priv->chprilvl); + dmainfo(" CHINFLAG: %02x CHSTATUS: %02x\n", + regs->chintflag, regs->chstatus); +} +#endif /* CONFIG_DEBUG_DMA_INFO */ +#endif /* CONFIG_SAMD5E5_DMAC */ diff --git a/arch/arm/src/samd5e5/sam_dmac.h b/arch/arm/src/samd5e5/sam_dmac.h new file mode 100644 index 0000000000..64b60b8e87 --- /dev/null +++ b/arch/arm/src/samd5e5/sam_dmac.h @@ -0,0 +1,335 @@ +/************************************************************************************ + * arch/arm/src/samd5e5/sam_dmac.h + * + * Copyright (C) 2018 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_SAMD5E5_SAM_DMAC_H +#define __ARCH_ARM_SRC_SAMD5E5_SAM_DMAC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include + +#include "chip/sam_dmac.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* DMA ******************************************************************************/ + +/* Flags used to characterize the desired 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) + */ + +/* Common characteristics + * + * BEATSIZE - The size of one bus transfer or "beat". 8-, 16-, or 32-bits + * STEPSEL - The STEPSIZE may be applied only to the memory to the peripheral. + * STEPSIZE - When the address is incremented, it is increments by how many "beats"? + * PRIORITY - DMA transfer priority + * RUNSTDBY - DMA runs in standby mode + * BURSTLEN - Burst length. + * THRESHOLD - Threshold at which DMA starts to write (multi-BEAT transfers only) + */ + +#define DMACH_FLAG_BEATSIZE_SHIFT (0) /* Bits 0-1: Beat size */ +#define DMACH_FLAG_BEATSIZE_MASK (3 << DMACH_FLAG_BEATSIZE_SHIFT) +# define DMACH_FLAG_BEATSIZE_BYTE (0 << DMACH_FLAG_BEATSIZE_SHIFT) /* 8-bit bus transfer */ +# define DMACH_FLAG_BEATSIZE_HWORD (1 << DMACH_FLAG_BEATSIZE_SHIFT) /* 16-bit bus transfer */ +# define DMACH_FLAG_BEATSIZE_WORD (2 << DMACH_FLAG_BEATSIZE_SHIFT) /* 32-bit bus transfer */ +#define DMACH_FLAG_STEPSEL (1 << 2) /* Bit 2: Step selection */ +# define DMACH_FLAG_STEPSEL_MEM (0) /* 0=Step size applies to memory */ +# define DMACH_FLAG_STEPSEL_PERIPH (1 << 2) /* 1=Step size applies to peripheral */ +#define DMACH_FLAG_STEPSIZE_SHIFT (3) /* Bits 3-5: Address increment step */ +#define DMACH_FLAG_STEPSIZE_MASK (7 << DMACH_FLAG_STEPSIZE_SHIFT) +# define DMACH_FLAG_STEPSIZE_X1 (0 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 1 */ +# define DMACH_FLAG_STEPSIZE_X2 (1 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 2 */ +# define DMACH_FLAG_STEPSIZE_X4 (2 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 4 */ +# define DMACH_FLAG_STEPSIZE_X8 (3 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 8 */ +# define DMACH_FLAG_STEPSIZE_X16 (4 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 16 */ +# define DMACH_FLAG_STEPSIZE_X32 (5 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 32 */ +# define DMACH_FLAG_STEPSIZE_X64 (6 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 64 */ +# define DMACH_FLAG_STEPSIZE_X128 (7 << DMACH_FLAG_STEPSIZE_SHIFT) /* Next ADDR = ADDR + (BEATSIZE+1) * 128 */ +#define DMACH_FLAG_PRIORITY_SHIFT (6) /* Bit 6-7: Arbitration priority */ +#define DMACH_FLAG_PRIORITY_MASK (3 << DMACH_FLAG_PRIORITY_SHIFT) +# define DMACH_FLAG_PRIORITY(n) ((uint32_t)(n) << DMACH_FLAG_PRIORITY_SHIFT) +#define DMACH_FLAG_RUNINSTDBY (1 << 8) /* Bit 8: Run in standby */ +#define DMACH_FLAG_BURSTLEN_SHIFT (9) /* Bits 9-12: Burst length */ +#define DMACH_FLAG_BURSTLEN_MASK (15 << DMACH_FLAG_BURSTLEN_SHIFT) +# define DMACH_FLAG_BURSTLEN(n) ((uint32_t)(n) << DMACH_FLAG_BURSTLEN_SHIFT) /* n=1-16 */ +#define DMACH_FLAG_THRESHOLD_SHIFT (13) /* Bits 13-14: Threshold */ +#define DMACH_FLAG_THRESHOLD_MASK (15 << DMACH_FLAG_THRESHOLD_SHIFT) +# define DMACH_FLAG_THRESHOLD_1BEAT (0 << DMACH_FLAG_THRESHOLD_SHIFT) /* Write after 1 beat */ +# define DMACH_FLAG_THRESHOLD_2BEATS (1 << DMACH_FLAG_THRESHOLD_SHIFT) /* Write after 2 beats */ +# define DMACH_FLAG_THRESHOLD_4BEATS (2 << DMACH_FLAG_THRESHOLD_SHIFT) /* Write after 3 beats */ +# define DMACH_FLAG_THRESHOLD_8BEATS (3 << DMACH_FLAG_THRESHOLD_SHIFT) /* Write after 8 beats */ + +/* Peripheral endpoint characteristics. + * + * PERIPH_TRIGSRC - The TX ID of the peripheral that provides the DMA trigger. This + * is one of the DMAC_CHCTRLA_TRIGSRC_*[_TX] definitions. This trigger source + * is selected when sam_dmatxsetup() is called. + * PERIPH_TRIGACT - Trigger action + * PERIPH_INCREMENT - Indicates the that peripheral address should be incremented on + * each "beat" + */ + +#define DMACH_FLAG_PERIPH_TRIGSRC_SHIFT (15) /* Bits 15-21: See DMAC_CHCTRLA_TRIGSRC_* */ +#define DMACH_FLAG_PERIPH_TRIGSRC_MASK (0x7f << DMACH_FLAG_PERIPH_TRIGSRC_SHIFT) +# define DMACH_FLAG_PERIPH_TRIGSRC(n) ((uint32_t)(n) << DMACH_FLAG_PERIPH_TRIGSRC_SHIFT) +#define DMACH_FLAG_PERIPH_TRIGACT_SHIFT (22) /* Bits 22-23: Tx trigger action */ +#define DMACH_FLAG_PERIPH_TRIGACT_MASK (3 << DMACH_FLAG_PERIPH_TRIGACT_SHIFT) +# define DMACH_FLAG_PERIPH_TRIGACT_BLOCK (0 << DMACH_FLAG_PERIPH_TRIGACT_SHIFT) /* Trigger per block transfer */ +# define DMACH_FLAG_PERIPH_TRIGACT_BURST (2 << DMACH_FLAG_PERIPH_TRIGACT_SHIFT) /* Trigger per burst transfer */ +# define DMACH_FLAG_PERIPH_TRIGACT_TRANS (3 << DMACH_FLAG_PERIPH_TRIGACT_SHIFT) /* Trigger for each transaction */ +#define DMACH_FLAG_PERIPH_INCREMENT (1 << 24) /* Bit 24: Autoincrement peripheral address */ + +/* Memory endpoint characteristics + * + * MEM_INCREMENT - Indicates the that memory address should be incremented on each + * "beat" + */ + +#define DMACH_FLAG_MEM_INCREMENT (1 << 25) /* Bit 25: Autoincrement memory address */ + /* Bits 26-31: Not used */ + +/************************************************************************************ + * 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_INFO +struct sam_dmaregs_s +{ + uint8_t chan; /* Channel index */ + + /* DMAC Registers */ + + uint8_t crcstatus; /* CRC Status Register */ + uint8_t dbgctrl; /* Debug Control Register */ + uint8_t chid; /* Channel ID Register */ + uint8_t chctrlb; /* Channel Control B Register */ + uint8_t chpirlvl; /* Channel Priority Level */ + uint8_t chevctrl; /* Channel Event Control Register */ + uint8_t chintflag; /* Channel Interrupt Flag Status and Clear Register */ + uint8_t chstatus; /* Channel Status Register */ + + uint16_t ctrl; /* Control Register */ + uint16_t crcctrl; /* CRC Control Register */ + uint16_t intpend; /* Interrupt Pending Register */ + + uint32_t crcdatain; /* CRC Data Input Register */ + uint32_t crcchksum; /* CRC Checksum Register */ + uint32_t swtrigctrl; /* Software Trigger Control Register */ + uint32_t prictrl0; /* Priority Control 0 Register */ + uint32_t intstatus; /* Interrupt Status Register */ + uint32_t busych; /* Busy Channels Register */ + uint32_t pendch; /* Pending Channels Register */ + uint32_t active; /* Active Channels and Levels Register */ + uint32_t baseaddr; /* Descriptor Memory Section Base Address Register */ + uint32_t wrbaddr; /* Write-Back Memory Section Base Address Register */ + uint32_t chctrla; /* Channel Control A Register */ +}; +#endif + +/************************************************************************************ + * 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 and 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 if the required FIFO size is available, this function returns + * a non-NULL, void* DMA channel handle. NULL is returned on any failure. + * + ************************************************************************************/ + +DMA_HANDLE sam_dmachannel(uint32_t txflags, uint32_t rxflags); + +/************************************************************************************ + * 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 txflags, uint32_t rxflags); + +/************************************************************************************ + * 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 non-contiguous 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 non-contiguous 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_INFO +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_INFO +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_SAMD5E5_SAM_DMAC_H */ diff --git a/arch/arm/src/samd5e5/sam_i2c_master.c b/arch/arm/src/samd5e5/sam_i2c_master.c index ab8e73430b..95d9528c97 100644 --- a/arch/arm/src/samd5e5/sam_i2c_master.c +++ b/arch/arm/src/samd5e5/sam_i2c_master.c @@ -1182,9 +1182,8 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) i2c_pad_configure(priv); - ctrla = - I2C_CTRLA_MODE_MASTER | I2C_CTRLA_RUNSTDBY | I2C_CTRLA_SPEED_FAST | - I2C_CTRLA_SDAHOLD_450NS | priv->attr->muxconfig; + ctrla = I2C_CTRLA_MODE_MASTER | I2C_CTRLA_RUNSTDBY | I2C_CTRLA_SPEED_FAST | + I2C_CTRLA_SDAHOLD_450NS | priv->attr->muxconfig; i2c_putreg32(priv, ctrla, SAM_I2C_CTRLA_OFFSET); i2c_wait_synchronization(priv); diff --git a/arch/arm/src/samd5e5/sam_idle.c b/arch/arm/src/samd5e5/sam_idle.c index 6eab71706c..0cd9d22382 100644 --- a/arch/arm/src/samd5e5/sam_idle.c +++ b/arch/arm/src/samd5e5/sam_idle.c @@ -46,7 +46,6 @@ #include -#include "chip.h" #include "up_internal.h" /**************************************************************************** diff --git a/arch/arm/src/samd5e5/sam_serial.c b/arch/arm/src/samd5e5/sam_serial.c index d5a2ff3036..c57bfcfb5c 100644 --- a/arch/arm/src/samd5e5/sam_serial.c +++ b/arch/arm/src/samd5e5/sam_serial.c @@ -58,7 +58,6 @@ #include "up_arch.h" #include "up_internal.h" -#include "chip.h" #include "sam_config.h" #include "sam_usart.h" #include "sam_lowputc.h" diff --git a/arch/arm/src/samd5e5/sam_usart.h b/arch/arm/src/samd5e5/sam_usart.h index afdffb5086..3760b0d23e 100644 --- a/arch/arm/src/samd5e5/sam_usart.h +++ b/arch/arm/src/samd5e5/sam_usart.h @@ -48,7 +48,6 @@ #include #include "up_arch.h" -#include "chip.h" #include "chip/sam_usart.h" #include "sam_config.h" diff --git a/arch/arm/src/samd5e5/sam_userspace.h b/arch/arm/src/samd5e5/sam_userspace.h index 5790ba00b7..77b42cfee8 100644 --- a/arch/arm/src/samd5e5/sam_userspace.h +++ b/arch/arm/src/samd5e5/sam_userspace.h @@ -48,7 +48,6 @@ #include #include "up_internal.h" -#include "chip.h" /************************************************************************************ * Pre-processor Definitions