diff --git a/arch/arm/src/sam34/chip/sam3u_dmac.h b/arch/arm/src/sam34/chip/sam3u_dmac.h index 773008a658..a64f9764b3 100644 --- a/arch/arm/src/sam34/chip/sam3u_dmac.h +++ b/arch/arm/src/sam34/chip/sam3u_dmac.h @@ -379,7 +379,7 @@ #define DMACHAN_CTRLB_SRCINCR_MASK (3 << DMACHAN_CTRLB_SRCINCR_SHIFT) # define DMACHAN_CTRLB_SRCINCR_INCR (0 << DMACHAN_CTRLB_SRCINCR_SHIFT) /* Incrementing address */ # define DMACHAN_CTRLB_SRCINCR_FIXED (2 << DMACHAN_CTRLB_SRCINCR_SHIFT) /* Fixed address */ -#define DMACHAN_CTRLB_DSTINCR_SHIFT (28) /* Bits 28-29 */ +#define DMACHAN_CTRLB_DSTINCR_SHIFT (28) /* Bits 28-29 */ #define DMACHAN_CTRLB_DSTINCR_MASK (3 << DMACHAN_CTRLB_DSTINCR_SHIFT) # define DMACHAN_CTRLB_DSTINCR_INCR (0 << DMACHAN_CTRLB_DSTINCR_SHIFT) /* Incrementing address */ # define DMACHAN_CTRLB_DSTINCR_FIXED (2 << DMACHAN_CTRLB_DSTINCR_SHIFT) /* Fixed address */ diff --git a/arch/arm/src/sam34/sam3u_dmac.c b/arch/arm/src/sam34/sam3u_dmac.c index 43d725b0dc..4c568ba5be 100644 --- a/arch/arm/src/sam34/sam3u_dmac.c +++ b/arch/arm/src/sam34/sam3u_dmac.c @@ -252,14 +252,14 @@ static inline void sam_givedsem(void) * ****************************************************************************/ -static unsigned int sam_fifosize(uint8_t dmach_flags) +static unsigned int sam_fifosize(uint8_t chflags) { - dmach_flags &= DMACH_FLAG_FIFOSIZE_MASK; - if (dmach_flags == DMACH_FLAG_FIFO_8BYTES) + chflags &= DMACH_FLAG_FIFOSIZE_MASK; + if (chflags == DMACH_FLAG_FIFO_8BYTES) { return 8; } - else /* if (dmach_flags == DMACH_FLAG_FIFO_32BYTES) */ + else /* if (chflags == DMACH_FLAG_FIFO_32BYTES) */ { return 32; } @@ -273,9 +273,9 @@ static unsigned int sam_fifosize(uint8_t dmach_flags) * ****************************************************************************/ -static inline bool sam_flowcontrol(uint8_t dmach_flags) +static inline bool sam_flowcontrol(uint8_t chflags) { - return ((dmach_flags & DMACH_FLAG_FLOWCONTROL) != 0); + return ((chflags & DMACH_FLAG_FLOWCONTROL) != 0); } /**************************************************************************** @@ -368,7 +368,7 @@ sam_txctrlabits(struct sam_dma_s *dmach) DEBUGASSERT(ndx < 3); regval = g_srcwidth[ndx]; - /* Set the source chuck size (memory chunk size) */ + /* Set the source chunk size (memory chunk size) */ if ((dmach->flags & DMACH_FLAG_MEMCHUNKSIZE) == DMACH_FLAG_MEMCHUNKSIZE_4) { @@ -389,7 +389,7 @@ sam_txctrlabits(struct sam_dma_s *dmach) DEBUGASSERT(ndx < 3); regval |= g_destwidth[ndx]; - /* Set the destination chuck size (peripheral chunk size) */ + /* Set the destination chunk size (peripheral chunk size) */ if ((dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE) == DMACH_FLAG_PERIPHCHUNKSIZE_4) { @@ -405,6 +405,48 @@ sam_txctrlabits(struct sam_dma_s *dmach) return regval; } +/**************************************************************************** + * Name: sam_maxtxtransfer + * + * Description: + * Maximum number of bytes that can be sent in on transfer + * + ****************************************************************************/ + +static size_t sam_maxtxtransfer(struct sam_dmach_s *dmach) +{ + unsigned int srcwidth; + size_t maxtransfer; + + /* Get the maximum transfer size in bytes. BTSIZE is "the number of + * transfers to be performed, that is, for writes it refers to the number + * of source width transfers to perform when DMAC is flow controller. For + * Reads, BTSIZE refers to the number of transfers completed on the Source + * Interface. ..." + */ + + srcwidth = (dmach->flags & DMACH_FLAG_MEMWIDTH_MASK) + >> DMACH_FLAG_MEMWIDTH_SHIFT; + + switch (srcwidth) + { + default: + case 0: /* 8 bits, 1 byte */ + maxtransfer = DMACHAN_CTRLA_BTSIZE_MAX; + break; + + case 1: /* 16 bits, 2 bytes */ + maxtransfer = 2 * DMACHAN_CTRLA_BTSIZE_MAX; + break; + + case 2: /* 32 bits 4 bytes */ + maxtransfer = 4 * DMACHAN_CTRLA_BTSIZE_MAX; + break; + } + + return maxtransfer; +} + /**************************************************************************** * Name: sam_txctrla * @@ -414,19 +456,30 @@ sam_txctrlabits(struct sam_dma_s *dmach) ****************************************************************************/ static inline uint32_t sam_txctrla(struct sam_dma_s *dmach, - uint32_t dmasize, uint32_t ctrla) + uint32_t ctrla, uint32_t dmasize) { + unsigned int srcwidth; + /* 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. */ - /* Adjust the the source transfer size for the source chunk size (memory - * chunk size) - */ + srcwidth = (dmach->flags & DMACH_FLAG_MEMWIDTH_MASK) + >> DMACH_FLAG_MEMWIDTH_SHIFT; - if ((dmach->flags & DMACH_FLAG_MEMCHUNKSIZE) == DMACH_FLAG_MEMCHUNKSIZE_4) + switch (srcwidth) { - dmasize = (dmasize + 3) >> 2; + 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; } DEBUGASSERT(dmasize <= DMACHAN_CTRLA_BTSIZE_MAX); @@ -460,7 +513,7 @@ static inline uint32_t sam_rxctrlabits(struct sam_dma_s *dmach) DEBUGASSERT(ndx < 3); regval = g_srcwidth[ndx]; - /* Set the source chuck size (peripheral chunk size) */ + /* Set the source chunk size (peripheral chunk size) */ if ((dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE) == DMACH_FLAG_PERIPHCHUNKSIZE_4) { @@ -481,7 +534,7 @@ static inline uint32_t sam_rxctrlabits(struct sam_dma_s *dmach) DEBUGASSERT(ndx < 3); regval |= g_destwidth[ndx]; - /* Set the destination chuck size (memory chunk size) */ + /* Set the destination chunk size (memory chunk size) */ if ((dmach->flags & DMACH_FLAG_MEMCHUNKSIZE) == DMACH_FLAG_MEMCHUNKSIZE_4) { @@ -497,6 +550,48 @@ static inline uint32_t sam_rxctrlabits(struct sam_dma_s *dmach) return regval; } +/**************************************************************************** + * Name: sam_maxrxtransfer + * + * Description: + * Maximum number of bytes that can be sent in on transfer + * + ****************************************************************************/ + +static size_t sam_maxrxtransfer(struct sam_dmach_s *dmach) +{ + unsigned int srcwidth; + size_t maxtransfer; + + /* Get the maximum transfer size in bytes. BTSIZE is "the number of + * transfers to be performed, that is, for writes it refers to the number + * of source width transfers to perform when DMAC is flow controller. For + * Reads, BTSIZE refers to the number of transfers completed on the Source + * Interface. ..." + */ + + srcwidth = (dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) + >> DMACH_FLAG_PERIPHWIDTH_SHIFT; + + switch (srcwidth) + { + default: + case 0: /* 8 bits, 1 byte */ + maxtransfer = DMACHAN_CTRLA_BTSIZE_MAX; + break; + + case 1: /* 16 bits, 2 bytes */ + maxtransfer = 2 * DMACHAN_CTRLA_BTSIZE_MAX; + break; + + case 2: /* 32 bits, 4 bytes */ + maxtransfer = 4 * DMACHAN_CTRLA_BTSIZE_MAX; + break; + } + + return maxtransfer; +} + /**************************************************************************** * Name: sam_rxctrla * @@ -506,19 +601,30 @@ static inline uint32_t sam_rxctrlabits(struct sam_dma_s *dmach) ****************************************************************************/ static inline uint32_t sam_rxctrla(struct sam_dma_s *dmach, - uint32_t dmasize, uint32_t ctrla) + uint32_t ctrla, uint32_t dmasize) { + unsigned int srcwidth; + /* 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. */ - /* Adjust the the source transfer size for the source chunk size (peripheral - * chunk size) - */ + srcwidth = (dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) + >> DMACH_FLAG_PERIPHWIDTH_SHIFT; - if ((dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE) == DMACH_FLAG_PERIPHCHUNKSIZE_4) + switch (srcwidth) { - dmasize = (dmasize + 3) >> 2; + 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; } DEBUGASSERT(dmasize <= DMACHAN_CTRLA_BTSIZE_MAX); @@ -907,7 +1013,7 @@ static int sam_rxbuffer(struct sam_dma_s *dmach, uint32_t paddr, ctrlb = sam_rxctrlb(dmach); } - ctrla = sam_rxctrla(dmach, nbytes, regval); + ctrla = sam_rxctrla(dmach, regval, nbytes); /* Add the new link list entry */ @@ -1102,9 +1208,9 @@ static int sam_dmainterrupt(int irq, void *context) regval = getreg32(SAM_DMAC_EBCISR) & getreg32(SAM_DMAC_EBCIMR); - /* Check if the any transfer has completed */ + /* Check if the any transfer has completed or any errors have occurred */ - if (regval & DMAC_EBC_BTC_MASK) + if (regval & DMAC_EBC_ALLINTS) { /* Yes.. Check each bit to see which channel has interrupted */ @@ -1219,15 +1325,15 @@ void weak_function up_dmainitialize(void) * ****************************************************************************/ -DMA_HANDLE sam_dmachannel(uint32_t dmach_flags) +DMA_HANDLE sam_dmachannel(uint32_t chflags) { struct sam_dma_s *dmach; unsigned int chndx; /* Get the search parameters */ - bool flowcontrol = sam_flowcontrol(dmach_flags); - unsigned int fifosize = sam_fifosize(dmach_flags); + bool flowcontrol = sam_flowcontrol(chflags); + unsigned int fifosize = sam_fifosize(chflags); /* Search for an available DMA channel with at least the requested FIFO * size. @@ -1241,7 +1347,7 @@ DMA_HANDLE sam_dmachannel(uint32_t dmach_flags) struct sam_dma_s *candidate = &g_dma[chndx]; if (!candidate->inuse && (sam_fifosize(candidate->flags) >= fifosize) && - (!flowcontrol || sam_flowcontrol(dmach_flags))) + (!flowcontrol || sam_flowcontrol(chflags))) { dmach = candidate; dmach->inuse = true; @@ -1258,23 +1364,49 @@ DMA_HANDLE sam_dmachannel(uint32_t dmach_flags) putreg32(DMAC_CHDR_DIS(chndx), SAM_DMAC_CHDR); - /* See the DMA channel flags, retaining the fifo size and flow + /* Set the DMA channel flags, retaining the fifo size and flow * control settings which are inherent properties of the FIFO * and cannot be changed. */ dmach->flags &= (DMACH_FLAG_FLOWCONTROL | DMACH_FLAG_FIFOSIZE_MASK); - dmach->flags |= (dmach_flags & ~((DMACH_FLAG_FLOWCONTROL | DMACH_FLAG_FIFOSIZE_MASK))); + dmach->flags |= (chflags & ~((DMACH_FLAG_FLOWCONTROL | DMACH_FLAG_FIFOSIZE_MASK))); break; } } sam_givechsem(); - dmavdbg("dmach_flags: %08x returning dmach: %p\n", (int)dmach_flags, dmach); + dmavdbg("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 chflags) +{ + struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; + + /* Set the new DMA channel flags. */ + + dmavdbg("chflags: %08x\n", (int)chflags); + dmach->flags = chflags; +} + /**************************************************************************** * Name: sam_dmafree * @@ -1329,14 +1461,7 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, size_t nby * transfers and the number of bytes per transfer. */ - if ((dmach->flags & DMACH_FLAG_MEMCHUNKSIZE) == DMACH_FLAG_MEMCHUNKSIZE_4) - { - maxtransfer = 4 * DMACHAN_CTRLA_BTSIZE_MAX; - } - else - { - maxtransfer = DMACHAN_CTRLA_BTSIZE_MAX; - } + maxtransfer = sam_maxtxtransfer(dmach); /* If this is a large transfer, break it up into smaller buffers */ @@ -1403,14 +1528,7 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, size_t nby * transfers and the number of bytes per transfer. */ - if ((dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE) == DMACH_FLAG_PERIPHCHUNKSIZE_4) - { - maxtransfer = 4 * DMACHAN_CTRLA_BTSIZE_MAX; - } - else - { - maxtransfer = DMACHAN_CTRLA_BTSIZE_MAX; - } + maxtransfer = sam_maxrxtransfer(dmach); /* If this is a large transfer, break it up into smaller buffers */ diff --git a/arch/arm/src/sam34/sam_dmac.h b/arch/arm/src/sam34/sam_dmac.h index 4e34d46944..672520f508 100644 --- a/arch/arm/src/sam34/sam_dmac.h +++ b/arch/arm/src/sam34/sam_dmac.h @@ -194,6 +194,24 @@ extern "C" DMA_HANDLE sam_dmachannel(uint32_t dmach_flags); +/**************************************************************************** + * 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 * diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index 0c6cdedf2d..b4dd69bcff 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -245,6 +245,32 @@ if SAMA5_SPI0 || SAMA5_SPI1 menu "SPI device driver options" +config SAMA5_SPI_DMA + bool "SPI DMA" + default n + depends on (SAMA5_DMAC0 && SAMA5_SPI0) || (SAMA5_DMAC1 && SAMA5_SPI1) + ---help--- + Use DMA to improve SPI transfer performance. + +config SAMA5_SPI_DMATHRESHOLD + int "SPI DMA threshold" + default 4 + depends on SAMA5_SPI_DMA + ---help--- + When SPI DMA is enabled, small DMA transfers will still be performed + by polling logic. But we need a threshold value to determine what + is small. That value is provided by CONFIG_SAMA5_SPI_DMATHRESHOLD. + +config SAMA5_SPI_DMADEBUG + bool "SPI DMA transfer debug" + depends on SAMA5_SPI_DMA && DEBUG && DEBUG_DMA + default n + ---help--- + Enable special debug instrumentation analyze SPI DMA data transfers. + This logic is as non-invasive as possible: It samples DMA + registers at key points in the data transfer and then dumps all of + the registers at the end of the transfer. + config SAMA5_SPI_REGDEBUG bool "SPI Register level debug" depends on DEBUG diff --git a/arch/arm/src/sama5/chip/sam_dmac.h b/arch/arm/src/sama5/chip/sam_dmac.h index 6e4cf51a2b..68a0cbd464 100755 --- a/arch/arm/src/sama5/chip/sam_dmac.h +++ b/arch/arm/src/sama5/chip/sam_dmac.h @@ -509,11 +509,10 @@ # define DMAC_EBC_DICERR6 (1 << (DMAC_EBC_DICERR_SHIFT+6)) # define DMAC_EBC_DICERR7 (1 << (DMAC_EBC_DICERR_SHIFT+7)) -#define DMAC_EBC_BTCINTS(n) (0x00010001 << (n)) /* BTC+ERR interrupts */ #define DMAC_EBC_CBTCINTS(n) (0x00010100 << (n)) /* CBT+ERR interrupts */ #define DMAC_EBC_CHANINTS(n) (0x00010101 << (n)) /* BTC+CBT+ERR interrupts */ -#define DMAC_EBC_ALLCHANINTS(n) (0x01010101 << (n)) /* All channel interrupts */ -#define DMAC_EBC_ALLINTS (0xffffffff) /* All interrupts */ +#define DMAC_EBC_ALLCHANINTS (0x00ffffff) /* All BTC+CBT+ERR interrupts */ +#define DMAC_EBC_ALLINTS (0xffffffff) /* All channel interrupts */ /* DMAC Channel Handler Enable Register */ @@ -695,7 +694,7 @@ # define DMAC_CH_CTRLB_SRCINCR_INCR (0 << DMAC_CH_CTRLB_SRCINCR_SHIFT) /* Incrementing address */ # define DMAC_CH_CTRLB_SRCINCR_DECR (1 << DMAC_CH_CTRLB_SRCINCR_SHIFT) /* Decrementing address */ # define DMAC_CH_CTRLB_SRCINCR_FIXED (2 << DMAC_CH_CTRLB_SRCINCR_SHIFT) /* Fixed address */ -#define DMAC_CH_CTRLB_DSTINCR_SHIFT (28) /* Bits 28-29 */ +#define DMAC_CH_CTRLB_DSTINCR_SHIFT (28) /* Bits 28-29 */ #define DMAC_CH_CTRLB_DSTINCR_MASK (3 << DMAC_CH_CTRLB_DSTINCR_SHIFT) # define DMAC_CH_CTRLB_DSTINCR_INCR (0 << DMAC_CH_CTRLB_DSTINCR_SHIFT) /* Incrementing address */ # define DMAC_CH_CTRLB_DSTINCR_DECR (1 << DMAC_CH_CTRLB_DSTINCR_SHIFT) /* Decrementing address */ diff --git a/arch/arm/src/sama5/sam_dmac.c b/arch/arm/src/sama5/sam_dmac.c index e595afde51..5aa14d1882 100644 --- a/arch/arm/src/sama5/sam_dmac.c +++ b/arch/arm/src/sama5/sam_dmac.c @@ -848,7 +848,7 @@ static inline uint32_t sam_txctrlabits(struct sam_dmach_s *dmach) DEBUGASSERT(ndx < 4); regval = g_srcwidth[ndx]; - /* Set the source chuck size (memory chunk size) */ + /* Set the source chunk size (memory chunk size) */ chunksize = (dmach->flags & DMACH_FLAG_MEMCHUNKSIZE_MASK) >> DMACH_FLAG_MEMCHUNKSIZE_SHIFT; @@ -862,7 +862,7 @@ static inline uint32_t sam_txctrlabits(struct sam_dmach_s *dmach) DEBUGASSERT(ndx < 4); regval |= g_destwidth[ndx]; - /* Set the destination chuck size (peripheral chunk size) */ + /* Set the destination chunk size (peripheral chunk size) */ chunksize = (dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) >> DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT; @@ -881,37 +881,37 @@ static inline uint32_t sam_txctrlabits(struct sam_dmach_s *dmach) static size_t sam_maxtxtransfer(struct sam_dmach_s *dmach) { - unsigned int chunksize; + unsigned int srcwidth; size_t maxtransfer; - /* Adjust the the source transfer size for the source chunk size (peripheral - * chunk size). BTSIZE is "the number of transfers to be performed, that - * is, for writes it refers to the number of source width transfers - * to perform when DMAC is flow controller. For Reads, BTSIZE refers to - * the number of transfers completed on the Source Interface. ..." + /* Get the maximum transfer size in bytes. BTSIZE is "the number of + * transfers to be performed, that is, for writes it refers to the number + * of source width transfers to perform when DMAC is flow controller. For + * Reads, BTSIZE refers to the number of transfers completed on the Source + * Interface. ..." */ - chunksize = (dmach->flags & DMACH_FLAG_MEMCHUNKSIZE_MASK) - >> DMACH_FLAG_MEMCHUNKSIZE_SHIFT; + srcwidth = (dmach->flags & DMACH_FLAG_MEMWIDTH_MASK) + >> DMACH_FLAG_MEMWIDTH_SHIFT; - switch (chunksize) + switch (srcwidth) { default: - case 0: /* 1 byte */ + case 0: /* 8 bits, 1 byte */ maxtransfer = DMAC_CH_CTRLA_BTSIZE_MAX; break; - case 1: /* 4 bytes */ + case 1: /* 16 bits, 2 bytes */ + maxtransfer = 2 * DMAC_CH_CTRLA_BTSIZE_MAX; + break; + + case 2: /* 32 bits 4 bytes */ maxtransfer = 4 * DMAC_CH_CTRLA_BTSIZE_MAX; break; - case 2: /* 8 bytes */ + case 3: /* 64 bits, 8 bytes */ maxtransfer = 8 * DMAC_CH_CTRLA_BTSIZE_MAX; break; - - case 3: /* 16 bytes */ - maxtransfer = 16 * DMAC_CH_CTRLA_BTSIZE_MAX; - break; } return maxtransfer; @@ -927,7 +927,7 @@ static size_t sam_maxtxtransfer(struct sam_dmach_s *dmach) static uint32_t sam_ntxtransfers(struct sam_dmach_s *dmach, uint32_t dmasize) { - unsigned int chunksize; + unsigned int srcwidth; /* Adjust the the source transfer size for the source chunk size (memory * chunk size). BTSIZE is "the number of transfers to be performed, that @@ -936,26 +936,26 @@ static uint32_t sam_ntxtransfers(struct sam_dmach_s *dmach, uint32_t dmasize) * the number of transfers completed on the Source Interface. ..." */ - chunksize = (dmach->flags & DMACH_FLAG_MEMCHUNKSIZE_MASK) - >> DMACH_FLAG_MEMCHUNKSIZE_SHIFT; + srcwidth = (dmach->flags & DMACH_FLAG_MEMWIDTH_MASK) + >> DMACH_FLAG_MEMWIDTH_SHIFT; - switch (chunksize) + switch (srcwidth) { default: - case 0: /* 1 byte */ + case 0: /* 8 bits, 1 byte */ break; - case 1: /* 4 bytes */ + case 1: /* 16 bits, 2 bytes */ + dmasize = (dmasize + 1) >> 1; + break; + + case 2: /* 32 bits, 4 bytes */ dmasize = (dmasize + 3) >> 2; break; - case 2: /* 8 bytes */ + case 3: /* 64 bits, 8 bytes */ dmasize = (dmasize + 7) >> 3; break; - - case 3: /* 16 bytes */ - dmasize = (dmasize + 15) >> 4; - break; } return dmasize; @@ -970,7 +970,7 @@ static uint32_t sam_ntxtransfers(struct sam_dmach_s *dmach, uint32_t dmasize) ****************************************************************************/ static inline uint32_t sam_txctrla(struct sam_dmach_s *dmach, - uint32_t dmasize, uint32_t ctrla) + uint32_t ctrla, uint32_t dmasize) { uint32_t ntransfers; @@ -1014,7 +1014,7 @@ static inline uint32_t sam_rxctrlabits(struct sam_dmach_s *dmach) DEBUGASSERT(ndx < 4); regval = g_srcwidth[ndx]; - /* Set the source chuck size (peripheral chunk size) */ + /* Set the source chunk size (peripheral chunk size) */ chunksize = (dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) >> DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT; @@ -1030,7 +1030,7 @@ static inline uint32_t sam_rxctrlabits(struct sam_dmach_s *dmach) DEBUGASSERT(ndx < 4); regval |= g_destwidth[ndx]; - /* Set the destination chuck size (memory chunk size) */ + /* Set the destination chunk size (memory chunk size) */ chunksize = (dmach->flags & DMACH_FLAG_MEMCHUNKSIZE_MASK) >> DMACH_FLAG_MEMCHUNKSIZE_SHIFT; @@ -1049,37 +1049,37 @@ static inline uint32_t sam_rxctrlabits(struct sam_dmach_s *dmach) static size_t sam_maxrxtransfer(struct sam_dmach_s *dmach) { - unsigned int chunksize; + unsigned int srcwidth; size_t maxtransfer; - /* Adjust the the source transfer size for the source chunk size (peripheral - * chunk size). BTSIZE is "the number of transfers to be performed, that - * is, for writes it refers to the number of source width transfers - * to perform when DMAC is flow controller. For Reads, BTSIZE refers to - * the number of transfers completed on the Source Interface. ..." + /* Get the maximum transfer size in bytes. BTSIZE is "the number of + * transfers to be performed, that is, for writes it refers to the number + * of source width transfers to perform when DMAC is flow controller. For + * Reads, BTSIZE refers to the number of transfers completed on the Source + * Interface. ..." */ - chunksize = (dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) - >> DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT; + srcwidth = (dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) + >> DMACH_FLAG_PERIPHWIDTH_SHIFT; - switch (chunksize) + switch (srcwidth) { default: - case 0: /* 1 byte */ + case 0: /* 8 bits, 1 byte */ maxtransfer = DMAC_CH_CTRLA_BTSIZE_MAX; break; - case 1: /* 4 bytes */ + case 1: /* 16 bits, 2 bytes */ + maxtransfer = 2 * DMAC_CH_CTRLA_BTSIZE_MAX; + break; + + case 2: /* 32 bits, 4 bytes */ maxtransfer = 4 * DMAC_CH_CTRLA_BTSIZE_MAX; break; - case 2: /* 8 bytes */ + case 3: /* 64 bits, 8 bytes */ maxtransfer = 8 * DMAC_CH_CTRLA_BTSIZE_MAX; break; - - case 3: /* 16 bytes */ - maxtransfer = 16 * DMAC_CH_CTRLA_BTSIZE_MAX; - break; } return maxtransfer; @@ -1095,7 +1095,7 @@ static size_t sam_maxrxtransfer(struct sam_dmach_s *dmach) static uint32_t sam_nrxtransfers(struct sam_dmach_s *dmach, uint32_t dmasize) { - unsigned int chunksize; + unsigned int srcwidth; /* Adjust the the source transfer size for the source chunk size (peripheral * chunk size). BTSIZE is "the number of transfers to be performed, that @@ -1104,26 +1104,26 @@ static uint32_t sam_nrxtransfers(struct sam_dmach_s *dmach, uint32_t dmasize) * the number of transfers completed on the Source Interface. ..." */ - chunksize = (dmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) - >> DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT; + srcwidth = (dmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) + >> DMACH_FLAG_PERIPHWIDTH_SHIFT; - switch (chunksize) + switch (srcwidth) { default: - case 0: /* 1 byte */ + case 0: /* 8 bits, 1 byte */ break; - case 1: /* 4 bytes */ + case 1: /* 16 bits, 2 bytes */ + dmasize = (dmasize + 1) >> 1; + break; + + case 2: /* 32 bits, 4 bytes */ dmasize = (dmasize + 3) >> 2; break; - case 2: /* 8 bytes */ + case 3: /* 64 bits, 8 bytes */ dmasize = (dmasize + 7) >> 3; break; - - case 3: /* 16 bytes */ - dmasize = (dmasize + 15) >> 4; - break; } return dmasize; @@ -1138,7 +1138,7 @@ static uint32_t sam_nrxtransfers(struct sam_dmach_s *dmach, uint32_t dmasize) ****************************************************************************/ static inline uint32_t sam_rxctrla(struct sam_dmach_s *dmach, - uint32_t dmasize, uint32_t ctrla) + uint32_t ctrla, uint32_t dmasize) { uint32_t ntransfers; @@ -1564,7 +1564,7 @@ static int sam_rxbuffer(struct sam_dmach_s *dmach, uint32_t paddr, ctrlb = sam_rxctrlb(dmach); } - ctrla = sam_rxctrla(dmach, nbytes, regval); + ctrla = sam_rxctrla(dmach, regval, nbytes); /* Add the new link list entry */ @@ -1769,9 +1769,9 @@ static int sam_dmac_interrupt(struct sam_dmac_s *dmac) regval = sam_getdmac(dmac, SAM_DMAC_EBCISR_OFFSET) & sam_getdmac(dmac, SAM_DMAC_EBCIMR_OFFSET); - /* Check if the any transfer has completed */ + /* Check if the any transfer has completed or any errors have ocurred. */ - if (regval & DMAC_EBC_BTC_MASK) + if (regval & DMAC_EBC_ALLCHANINTS) { /* Yes.. Check each bit to see which channel has interrupted */ @@ -1933,10 +1933,8 @@ void weak_function up_dmainitialize(void) /**************************************************************************** * Name: sam_dmachannel * - * Description: - * Allocate a DMA channel. This function sets aside a DMA channel with - * the required FIFO size and flow control capabilities (determined by - * dma_flags) then gives the caller exclusive access to the DMA channel. + * 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 @@ -1944,9 +1942,8 @@ void weak_function up_dmainitialize(void) * 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. + * If a DMA channel is available, this function returns a non-NULL, void* + * DMA channel handle. NULL is returned on any failure. * ****************************************************************************/ @@ -1975,7 +1972,7 @@ DMA_HANDLE sam_dmachannel(uint8_t dmacno, uint32_t chflags) #endif { - dmadbg("Bad DMAC number: %d\n", dmacno); + dmadbg("ERROR: Bad DMAC number: %d\n", dmacno); DEBUGPANIC(); return (DMA_HANDLE)NULL; } @@ -2006,7 +2003,7 @@ DMA_HANDLE sam_dmachannel(uint8_t dmacno, uint32_t chflags) sam_putdmac(dmac,DMAC_CHDR_DIS(chndx), SAM_DMAC_CHDR_OFFSET); - /* See the DMA channel flags. */ + /* Set the DMA channel flags. */ dmach->flags = chflags; break; @@ -2015,11 +2012,57 @@ DMA_HANDLE sam_dmachannel(uint8_t dmacno, uint32_t chflags) sam_givechsem(dmac); - dmavdbg("dmacno: %d chflags: %08x returning dmach: %p\n", - (int)dmacno, (int)chflags, dmach); + /* Show the result of the allocation */ + + if (dmach) + { + dmavdbg("DMAC%d CH%d: chflags: %08x returning dmach: %p\n", + (int)dmacno, dmach->chan, (int)chflags, dmach); + } + else + { + dmadbg("ERROR: Failed allocate DMAC%d channel\n", (int)dmacno); + } + 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 chflags) +{ + struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; + + /* Set the new DMA channel flags. */ + + dmach->flags = chflags; + +#if defined(CONFIG_SAMA5_DMAC0) && defined(CONFIG_SAMA5_DMAC1) + dmavdbg("DMAC%d CH%d: chflags: %08x\n", + dmach->dmac, dmach->chan, (int)chflags); +#elif defined(CONFIG_SAMA5_DMAC0) + dmavdbg("DMAC0 CH%d: chflags: %08x\n", + dmach->chan, (int)chflags); +#else + dmavdbg("DMAC1 CH%d: chflags: %08x\n", + dmach->chan, (int)chflags); +#endif +} + /**************************************************************************** * Name: sam_dmafree * @@ -2064,6 +2107,7 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, { struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; size_t maxtransfer; + size_t remaining; int ret = OK; dmavdbg("dmach: %p paddr: %08x maddr: %08x nbytes: %d\n", @@ -2076,10 +2120,11 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, */ maxtransfer = sam_maxtxtransfer(dmach); + remaining = nbytes; /* If this is a large transfer, break it up into smaller buffers */ - while (nbytes > maxtransfer) + while (remaining > maxtransfer) { /* Set up the maximum size transfer */ @@ -2088,7 +2133,7 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, { /* Decrement the number of bytes left to transfer */ - nbytes -= maxtransfer; + remaining -= maxtransfer; /* Increment the memory & peripheral address (if it is appropriate to * do do). @@ -2108,9 +2153,9 @@ int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, /* Then set up the final buffer transfer */ - if (ret == OK && nbytes > 0) + if (ret == OK && remaining > 0) { - ret = sam_txbuffer(dmach, paddr, maddr, nbytes); + ret = sam_txbuffer(dmach, paddr, maddr, remaining); } /* Clean caches associated with the DMA memory */ @@ -2135,6 +2180,7 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, { struct sam_dmach_s *dmach = (struct sam_dmach_s *)handle; size_t maxtransfer; + size_t remaining; int ret = OK; dmavdbg("dmach: %p paddr: %08x maddr: %08x nbytes: %d\n", @@ -2147,10 +2193,11 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, */ maxtransfer = sam_maxrxtransfer(dmach); + remaining = nbytes; /* If this is a large transfer, break it up into smaller buffers */ - while (nbytes > maxtransfer) + while (remaining > maxtransfer) { /* Set up the maximum size transfer */ @@ -2159,7 +2206,7 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, { /* Decrement the number of bytes left to transfer */ - nbytes -= maxtransfer; + remaining -= maxtransfer; /* Increment the memory & peripheral address (if it is appropriate to * do do). @@ -2179,9 +2226,9 @@ int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr, /* Then set up the final buffer transfer */ - if (ret == OK && nbytes > 0) + if (ret == OK && remaining > 0) { - ret = sam_rxbuffer(dmach, paddr, maddr, nbytes); + ret = sam_rxbuffer(dmach, paddr, maddr, remaining); } /* Clean caches associated with the DMA memory */ diff --git a/arch/arm/src/sama5/sam_dmac.h b/arch/arm/src/sama5/sam_dmac.h index 3d4831b21f..1726009247 100644 --- a/arch/arm/src/sama5/sam_dmac.h +++ b/arch/arm/src/sama5/sam_dmac.h @@ -187,100 +187,112 @@ extern "C" * Public Function Prototypes ************************************************************************************/ -/**************************************************************************** +/************************************************************************************ * Name: sam_dmachannel * * Description: - * Allocate a DMA channel. This function sets aside a DMA channel with - * the required FIFO size and flow control capabilities (determined by - * dma_flags) then gives the caller exclusive access to the DMA channel. + * 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. + * 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 if the required FIFO size is available, this function - * returns a non-NULL, void* DMA channel handle. NULL is returned on any - * failure. + * 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 dmach_flags); +DMA_HANDLE sam_dmachannel(uint8_t dmacno, uint32_t chflags); -/**************************************************************************** - * Name: sam_dmafree +/************************************************************************************ + * Name: sam_dmaconfig * * 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. + * 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 dis- - * continuous transfers. Calls to sam_dmatxsetup() and sam_dmarxsetup() - * must not be intermixed on the same transfer, however. + * 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); +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 dis- - * continuous transfers. Calls to sam_dmatxsetup() and sam_dmarxsetup() - * must not be intermixed on the same transfer, however. + * 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); +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 + * 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); @@ -288,17 +300,16 @@ void sam_dmasample(DMA_HANDLE handle, struct sam_dmaregs_s *regs); # 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); +void sam_dmadump(DMA_HANDLE handle, const struct sam_dmaregs_s *regs, const char *msg); #else # define sam_dmadump(handle,regs,msg) #endif diff --git a/arch/arm/src/sama5/sam_hsmci.c b/arch/arm/src/sama5/sam_hsmci.c index 7d5fe09e52..5c34b047f3 100644 --- a/arch/arm/src/sama5/sam_hsmci.c +++ b/arch/arm/src/sama5/sam_hsmci.c @@ -420,7 +420,7 @@ static void sam_cmddump(struct sam_dev_s *priv); /* DMA Helpers **************************************************************/ static void sam_dmacallback(DMA_HANDLE handle, void *arg, int result); -static uint32_t sam_dmaregister(struct sam_dev_s *priv, unsigned int offset); +static uint32_t sam_physregaddr(struct sam_dev_s *priv, unsigned int offset); /* Data Transfer Helpers ****************************************************/ @@ -1074,50 +1074,50 @@ static void sam_dmacallback(DMA_HANDLE handle, void *arg, int result) } /**************************************************************************** - * Name: sam_dmaregister + * Name: sam_physregaddr * * Description: * Return the physical address of an HSMCI register * ****************************************************************************/ -static uint32_t sam_dmaregister(struct sam_dev_s *priv, unsigned int offset) +static uint32_t sam_physregaddr(struct sam_dev_s *priv, unsigned int offset) { /* Get the offset into the 1MB section containing the HSMCI registers */ - uint32_t pbase = priv->base & 0xfff00000; + uint32_t pbase = priv->base & 0x000fffff; -#ifdef CONFIG_HSMCI_HSMCI0 +#ifdef CONFIG_SAMA5_HSMCI0 /* Add in the physical base for HSMCI0 * * We only have to check if this is HSMCI0 if either HSMCI1 or HSMCI2 are * enabled. */ -#if defined(CONFIG_HSMCI_HSMCI1) || defined(CONFIG_HSMCI_HSMCI2) +#if defined(CONFIG_SAMA5_HSMCI1) || defined(CONFIG_SAMA5_HSMCI2) if (priv->hsmci == 0) #endif { pbase |= SAM_PERIPHA_PSECTION; } -#if defined(CONFIG_HSMCI_HSMCI1) || defined(CONFIG_HSMCI_HSMCI2) +#if defined(CONFIG_SAMA5_HSMCI1) || defined(CONFIG_SAMA5_HSMCI2) else #endif #endif -#ifdef CONFIG_HSMCI_HSMCI1 +#ifdef CONFIG_SAMA5_HSMCI1 /* Add in the physical base for HSMCI1 * * We only have to check if this is HSCMCi1 if HSMCI2 is enabled. */ -#ifdef CONFIG_HSMCI_HSMCI2 +#ifdef CONFIG_SAMA5_HSMCI2 if (priv->hsmci == 1) #endif { pbase |= SAM_PERIPHB_PSECTION; } -#ifdef CONFIG_HSMCI_HSMCI2 +#ifdef CONFIG_SAMA5_HSMCI2 else #endif #endif @@ -1127,7 +1127,7 @@ static uint32_t sam_dmaregister(struct sam_dev_s *priv, unsigned int offset) * If we get here, we con't have to check. */ -#ifdef CONFIG_HSMCI_HSMCI2 +#ifdef CONFIG_SAMA5_HSMCI2 { pbase |= SAM_PERIPHB_PSECTION; } @@ -2351,7 +2351,7 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev, if (ret != OK) { fdbg("ERROR: wd_start failed: %d\n", ret); - } + } } /* Loop until the event (or the timeout occurs). Race conditions are avoided @@ -2512,7 +2512,7 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer, /* Physical address of the HSCMI RDR registr */ - rdr = sam_dmaregister(priv, SAM_HSMCI_RDR_OFFSET); + rdr = sam_physregaddr(priv, SAM_HSMCI_RDR_OFFSET); /* Setup register sampling */ @@ -2566,7 +2566,7 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev, /* Physical address of the HSCMI TDR registr */ - tdr = sam_dmaregister(priv, SAM_HSMCI_TDR_OFFSET); + tdr = sam_physregaddr(priv, SAM_HSMCI_TDR_OFFSET); /* Setup register sampling */ diff --git a/arch/arm/src/sama5/sam_spi.c b/arch/arm/src/sama5/sam_spi.c index 53122e9591..fefb349dd5 100644 --- a/arch/arm/src/sama5/sam_spi.c +++ b/arch/arm/src/sama5/sam_spi.c @@ -48,12 +48,16 @@ #include #include #include +#include #include +#include #include +#include #include #include #include +#include #include #include "up_internal.h" @@ -61,6 +65,7 @@ #include "chip.h" #include "sam_pio.h" +#include "sam_dmac.h" #include "sam_spi.h" #include "sam_periphclks.h" #include "chip/sam_pmc.h" @@ -73,6 +78,35 @@ * Definitions ****************************************************************************/ /* Configuration ************************************************************/ +/* When SPI DMA is enabled, small DMA transfers will still be performed by + * polling logic. But we need a threshold value to determine what is small. + * That value is provided by CONFIG_SAMA5_SPI_DMATHRESHOLD. + */ + +#ifndef CONFIG_SAMA5_SPI_DMATHRESHOLD +# define CONFIG_SAMA5_SPI_DMATHRESHOLD 4 +#endif + +#ifdef CONFIG_SAMA5_SPI_DMA + +# if defined(CONFIG_SAMA5_SPI0) && defined(CONFIG_SAMA5_DMAC0) +# define SAMA5_SPI0_DMA true +# else +# define SAMA5_SPI0_DMA false +# endif + +# if defined(CONFIG_SAMA5_SPI1) && defined(CONFIG_SAMA5_DMAC1) +# define SAMA5_SPI1_DMA true +# else +# define SAMA5_SPI1_DMA false +# endif +#endif + +#ifndef CONFIG_SAMA5_SPI_DMA +# undef CONFIG_SAMA5_SPI_DMADEBUG +#endif + +/* Clocking *****************************************************************/ /* Select MCU-specific settings * * SPI is driven by the main clock. @@ -80,6 +114,13 @@ #define SAM_SPI_CLOCK BOARD_MCK_FREQUENCY +/* DMA timeout. The value is not critical; we just don't want the system to + * hang in the event that a DMA does not finish. This is set to + */ + +#define DMA_TIMEOUT_MS (800) +#define DMA_TIMEOUT_TICKS ((DMA_TIMEOUT_MS + (MSEC_PER_TICK-1)) / MSEC_PER_TICK) + /* Debug *******************************************************************/ /* Check if SPI debut is enabled (non-standard.. no support in * include/debug.h @@ -88,9 +129,14 @@ #ifndef CONFIG_DEBUG # undef CONFIG_DEBUG_VERBOSE # undef CONFIG_DEBUG_SPI +# undef CONFIG_SAMA5_SPI_DMADEBUG # undef CONFIG_SAMA5_SPI_REGDEBUG #endif +#ifndef CONFIG_DEBUG_DMA +# undef CONFIG_SAMA5_SPI_DMADEBUG +#endif + #ifdef CONFIG_DEBUG_SPI # define spidbg lldbg # ifdef CONFIG_DEBUG_VERBOSE @@ -103,6 +149,14 @@ # define spivdbg(x...) #endif +#define DMA_INITIAL 0 +#define DMA_AFTER_SETUP 1 +#define DMA_AFTER_START 2 +#define DMA_CALLBACK 3 +#define DMA_TIMEOUT 3 +#define DMA_END_TRANSFER 4 +#define DMA_NSAMPLES 5 + /**************************************************************************** * Private Types ****************************************************************************/ @@ -112,16 +166,34 @@ struct sam_spics_s { struct spi_dev_s spidev; /* Externally visible part of the SPI interface */ + #ifndef CONFIG_SPI_OWNBUS uint32_t frequency; /* Requested clock frequency */ uint32_t actual; /* Actual clock frequency */ uint8_t nbits; /* Width of word in bits (8 to 16) */ uint8_t mode; /* Mode 0,1,2,3 */ #endif + #if defined(CONFIG_SAMA5_SPI0) || defined(CONFIG_SAMA5_SPI1) uint8_t spino; /* SPI controller number (0 or 1) */ #endif uint8_t cs; /* Chip select number */ + +#ifdef CONFIG_SAMA5_SPI_DMA + bool candma; /* DMA is supported */ + sem_t dmawait; /* Used to wait for DMA completion */ + WDOG_ID dmadog; /* Watchdog that handles DMA timeouts */ + int result; /* DMA result */ + DMA_HANDLE rxdma; /* SPI RX DMA handle */ + DMA_HANDLE txdma; /* SPI TX DMA handle */ +#endif + + /* Debug stuff */ + +#ifdef CONFIG_SAMA5_SPI_DMADEBUG + struct sam_dmaregs_s rxdmaregs[DMA_NSAMPLES]; + struct sam_dmaregs_s txdmaregs[DMA_NSAMPLES]; +#endif }; /* Type of board-specific SPI status fuction */ @@ -136,8 +208,11 @@ struct sam_spidev_s { uint32_t base; /* SPI controller register base address */ sem_t spisem; /* Assures mutually exclusive acess to SPI */ - bool initialized; /* TRUE: Controller has been initialized */ select_t select; /* SPI select callout */ + bool initialized; /* TRUE: Controller has been initialized */ +#ifdef CONFIG_SAMA5_SPI_DMA + uint8_t pid; /* Peripheral ID */ +#endif /* Debug stuff */ @@ -177,6 +252,30 @@ static void spi_dumpregs(struct sam_spidev_s *spi, const char *msg); static inline void spi_flush(struct sam_spidev_s *spi); static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics); +/* DMA support */ + +#ifdef CONFIG_SAMA5_SPI_DMA + +#ifdef CONFIG_SAMA5_SPI_DMADEBUG +# define spi_rxdma_sample(s,i) sam_dmasample((s)->rxdma, &(s)->rxdmaregs[i]) +# define spi_txdma_sample(s,i) sam_dmasample((s)->txdma, &(s)->txdmaregs[i]) +static void spi_dma_sampleinit(struct sam_spics_s *spics); +static void spi_dma_sampledone(struct sam_spics_s *spics); + +#else +# define spi_rxdma_sample(s,i) +# define spi_txdma_sample(s,i) +# define spi_dma_sampleinit(s) +# define spi_dma_sampledone(s) + +#endif + +static void spi_rxcallback(DMA_HANDLE handle, void *arg, int result); +static void spi_txcallback(DMA_HANDLE handle, void *arg, int result); +static uint32_t spi_physregaddr(struct sam_spics_s *spics, + unsigned int offset); +#endif + /* SPI methods */ #ifndef CONFIG_SPI_OWNBUS @@ -188,6 +287,10 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency); static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); static void spi_setbits(struct spi_dev_s *dev, int nbits); static uint16_t spi_send(struct spi_dev_s *dev, uint16_t ch); +#ifdef CONFIG_SAMA5_SPI_DMA +static void spi_exchange_nodma(struct spi_dev_s *dev, + const void *txbuffer, void *rxbuffer, size_t nwords); +#endif static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, void *rxbuffer, size_t nwords); #ifndef CONFIG_SPI_EXCHANGE @@ -241,6 +344,9 @@ static struct sam_spidev_s g_spi0dev = { .base = SAM_SPI0_VBASE, .select = sam_spi0select, +#ifdef CONFIG_SAMA5_SPI_DMA + .pid = SAM_PID_SPI0, +#endif }; #endif @@ -276,6 +382,9 @@ static struct sam_spidev_s g_spi1dev = { .base = SAM_SPI1_VBASE, .select = sam_spi1select, +#ifdef CONFIG_SAMA5_SPI_DMA + .pid = SAM_PID_SPI1, +#endif }; #endif @@ -503,6 +612,284 @@ static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics) return ((uint32_t)1 << (spics->cs)) - 1; } +/**************************************************************************** + * Name: spi_dma_sampleinit + * + * Description: + * Initialize sampling of DMA registers (if CONFIG_SAMA5_SPI_DMADEBUG) + * + * Input Parameters: + * spics - Chip select doing the DMA + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMADEBUG +static void spi_dma_sampleinit(struct sam_spics_s *spics) +{ + /* Put contents of register samples into a known state */ + + memset(spics->rxdmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); + memset(spics->txdmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); + + /* Then get the initial samples */ + + sam_dmasample(spics->rxdma, &spics->rxdmaregs[DMA_INITIAL]); + sam_dmasample(spics->txdma, &spics->txdmaregs[DMA_INITIAL]); +} +#endif + +/**************************************************************************** + * Name: spi_dma_sampledone + * + * Description: + * Dump sampled DMA registers + * + * Input Parameters: + * spics - Chip select doing the DMA + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMADEBUG +static void spi_dma_sampledone(struct sam_spics_s *spics) +{ + /* Sample the final registers */ + + sam_dmasample(spics->rxdma, &spics->rxdmaregs[DMA_END_TRANSFER]); + sam_dmasample(spics->txdma, &spics->txdmaregs[DMA_END_TRANSFER]); + + /* Then dump the sampled DMA registers */ + /* Initial register values */ + + sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_INITIAL], + "TX: Initial Registers"); + sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_INITIAL], + "RX: Initial Registers"); + + /* Register values after DMA setup */ + + sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_AFTER_SETUP], + "TX: After DMA Setup"); + sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_AFTER_SETUP], + "RX: After DMA Setup"); + + /* Register values after DMA start */ + + sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_AFTER_START], + "TX: After DMA Start"); + sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_AFTER_START], + "RX: After DMA Start"); + + /* Register values at the time of the TX and RX DMA callbacks + * -OR- DMA timeout. + * + * If the DMA timedout, then there will not be any RX DMA + * callback samples. There is probably no TX DMA callback + * samples either, but we don't know for sure. + */ + + sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_CALLBACK], + "TX: At DMA callback"); + + /* Register values at the end of the DMA */ + + if (spics->result == -ETIMEDOUT) + { + sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_TIMEOUT], + "RX: At DMA timeout"); + } + else + { + sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_CALLBACK], + "RX: At DMA callback"); + } + + sam_dmadump(spics->rxdma, &spics->rxdmaregs[DMA_END_TRANSFER], + "RX: At End-of-Transfer"); + sam_dmadump(spics->txdma, &spics->txdmaregs[DMA_END_TRANSFER], + "TX: At End-of-Transfer"); +} +#endif + +/**************************************************************************** + * Name: spi_dmatimeout + * + * Description: + * The watchdog timeout setup when a has expired without completion of a + * DMA. + * + * Input Parameters: + * argc - The number of arguments (should be 1) + * arg - The argument (state structure reference cast to uint32_t) + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +static void spi_dmatimeout(int argc, uint32_t arg) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)arg; + DEBUGASSERT(spics != NULL); + + /* Sample DMA registers at the time of the timeout */ + + spi_rxdma_sample(spics, DMA_CALLBACK); + + /* Report timeout result, perhaps overwriting any failure reports from + * the TX callback. + */ + + spics->result = -ETIMEDOUT; + + /* Then wake up the waiting thread */ + + sem_post(&spics->dmawait); +} + +/**************************************************************************** + * Name: spi_rxcallback + * + * Description: + * This callback function is invoked at the completion of the SPI RX DMA. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMA +static void spi_rxcallback(DMA_HANDLE handle, void *arg, int result) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)arg; + DEBUGASSERT(spics != NULL); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(spics->dmadog); + + /* Sample DMA registers at the time of the callback */ + + spi_rxdma_sample(spics, DMA_CALLBACK); + + /* Report the result of the transfer only if the TX callback has not already + * reported an error. + */ + + if (spics->result == -EBUSY) + { + /* Save the result of the transfer if no error was previuosly reported */ + + spics->result = result; + } + + /* Then wake up the waiting thread */ + + sem_post(&spics->dmawait); +} +#endif + +/**************************************************************************** + * Name: spi_txcallback + * + * Description: + * This callback function is invoked at the completion of the SPI TX DMA. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMA +static void spi_txcallback(DMA_HANDLE handle, void *arg, int result) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)arg; + DEBUGASSERT(spics != NULL); + + spi_txdma_sample(spics, DMA_CALLBACK); + + /* Do nothing on the TX callback unless an error is reported. This + * callback is not really important because the SPI exchange is not + * complete until the RX callback is received. + */ + + if (result != OK && spics->result == -EBUSY) + { + /* Save the result of the transfer if an error is reported */ + + spics->result = result; + } +} +#endif + +/**************************************************************************** + * Name: spi_physregaddr + * + * Description: + * Return the physical address of an HSMCI register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_SPI_DMA +static uint32_t spi_physregaddr(struct sam_spics_s *spics, + unsigned int offset) +{ + struct sam_spidev_s *spi = spi_device(spics); + + /* Get the offset into the 1MB section containing the SPI registers */ + + uint32_t pbase = spi->base & 0x000fffff; + +#ifdef CONFIG_SAMA5_SPI0 + /* Add in the physical base for SPI0 + * + * We only have to check if this is SPI0 if SPI1 is enabled. + */ + +#if defined(CONFIG_SAMA5_SPI1) + if (spics->spino == 0) +#endif + { + pbase |= SAM_PERIPHA_PSECTION; + } +#if defined(CONFIG_SAMA5_SPI1) + else +#endif +#endif + +#ifdef CONFIG_SAMA5_SPI1 + /* Add in the physical base for SPI1 + * + * If we get here, we con't have to check anything. + */ + + { + pbase |= SAM_PERIPHB_PSECTION; + } +#endif + + return pbase + offset; +} +#endif + /**************************************************************************** * Name: spi_lock * @@ -890,10 +1277,16 @@ static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd) } /**************************************************************************** - * Name: spi_exchange + * Name: spi_exchange (and spi_exchange_nodma) * * Description: - * Exahange a block of data from SPI. Required. + * Exchange a block of data from SPI. There are two versions of this + * function: (1) One that is enabled only when CONFIG_SAMA5_SPI_DMA=y + * that performs DMA SPI transfers, but only when a larger block of + * data is being transferred. And (2) another version that does polled + * SPI transfers. When CONFIG_SAMA5_SPI_DMA=n the latter is the only + * version avaialable; when CONFIG_SAMA5_SPI_DMA=y, this version is only + * used for short SPI transfers and gets renamed as spi_exchange_nodma). * * Input Parameters: * dev - Device-specific state data @@ -910,9 +1303,13 @@ static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd) * ****************************************************************************/ -static void spi_exchange(struct spi_dev_s *dev, - const void *txbuffer, void *rxbuffer, - size_t nwords) +#ifdef CONFIG_SAMA5_SPI_DMA +static void spi_exchange_nodma(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +#else +static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +#endif { struct sam_spics_s *spics = (struct sam_spics_s *)dev; struct sam_spidev_s *spi = spi_device(spics); @@ -1017,6 +1414,226 @@ static void spi_exchange(struct spi_dev_s *dev, } } +#ifdef CONFIG_SAMA5_SPI_DMA +static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + struct sam_spics_s *spics = (struct sam_spics_s *)dev; + struct sam_spidev_s *spi = spi_device(spics); + uint32_t rxflags; + uint32_t txflags; + uint32_t txdummy; + uint32_t rxdummy; + uint32_t paddr; + int ret; + + /* If we cannot do DMA -OR- if this is a small SPI transfer, then let + * spi_exchange_nodma() do the work. + */ + + if (!spics->candma || nwords <= CONFIG_SAMA5_SPI_DMATHRESHOLD) + { + spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords); + return; + } + + spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords); + + spics = (struct sam_spics_s *)dev; + spi = spi_device(spics); + DEBUGASSERT(spics && spi); + + /* Make sure that any previous transfer is flushed from the hardware */ + + spi_flush(spi); + + /* Sample initial DMA registers */ + + spi_dma_sampleinit(spics); + + /* Configure the DMA channels. There are four different cases: + * + * 1) A true exchange with the memory address incrementing on both + * RX and TX channels, + * 2) A read operation with the memory address incrementing only on + * the receive channel, + * 3) A write operation where the memory address increments only on + * the receive channel, and + * 4) A corner case where there the memory address does not increment + * on either channel. This case might be used in certain cases + * where you want to assure that certain number of clocks are + * provided on the SPI bus. + */ + + rxflags = DMACH_FLAG_FIFOCFG_LARGEST | + ((uint32_t)spi->pid << DMACH_FLAG_PERIPHPID_SHIFT) | + DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | + DMACH_FLAG_PERIPHAHB_AHB_IF2 | DMACH_FLAG_PERIPHWIDTH_8BITS | + DMACH_FLAG_PERIPHCHUNKSIZE_1 | + ((uint32_t)(0x3f) << DMACH_FLAG_MEMPID_SHIFT) | + DMACH_FLAG_MEMAHB_AHB_IF0 | DMACH_FLAG_MEMWIDTH_8BITS | + DMACH_FLAG_MEMCHUNKSIZE_1; + + if (!rxbuffer) + { + /* No sink data buffer. Point to our dummy buffer and leave + * the rxflags so that no address increment is performed. + */ + + rxbuffer = (void *)&rxdummy; + } + else + { + /* A receive buffer is available. Use normal TX memory incrementing. */ + + rxflags |= DMACH_FLAG_MEMINCREMENT; + } + + txflags = DMACH_FLAG_FIFOCFG_LARGEST | + ((uint32_t)spi->pid << DMACH_FLAG_PERIPHPID_SHIFT) | + DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | + DMACH_FLAG_PERIPHAHB_AHB_IF2 | DMACH_FLAG_PERIPHWIDTH_8BITS | + DMACH_FLAG_PERIPHCHUNKSIZE_1 | + ((uint32_t)(0x3f) << DMACH_FLAG_MEMPID_SHIFT) | + DMACH_FLAG_MEMAHB_AHB_IF0 | DMACH_FLAG_MEMWIDTH_8BITS | + DMACH_FLAG_MEMCHUNKSIZE_1; + + if (!txbuffer) + { + /* No source data buffer. Point to our dummy buffer and configure + * the txflags so that no address increment is performed. + */ + + txdummy = 0xffffffff; + txbuffer = (const void *)&txdummy; + } + else + { + /* Source data is available. Use normal TX memory incrementing. */ + + txflags |= DMACH_FLAG_MEMINCREMENT; + } + + /* Then configure the DMA channels to make it so */ + + sam_dmaconfig(spics->rxdma, rxflags); + sam_dmaconfig(spics->txdma, txflags); + + /* Configure the exchange transfers */ + + paddr = spi_physregaddr(spics, SAM_SPI_RDR_OFFSET); + ret = sam_dmarxsetup(spics->rxdma, paddr, (uint32_t)rxbuffer, nwords); + if (ret < 0) + { + dmadbg("ERROR: sam_dmarxsetup failed: %d\n", ret); + return; + } + + spi_rxdma_sample(spics, DMA_AFTER_SETUP); + + paddr = spi_physregaddr(spics, SAM_SPI_TDR_OFFSET); + ret = sam_dmatxsetup(spics->txdma, paddr, (uint32_t)txbuffer, nwords); + if (ret < 0) + { + dmadbg("ERROR: sam_dmatxsetup failed: %d\n", ret); + return; + } + + spi_txdma_sample(spics, DMA_AFTER_SETUP); + + /* Start the DMA transfer */ + + spics->result = -EBUSY; + ret = sam_dmastart(spics->rxdma, spi_rxcallback, (void *)spics); + if (ret < 0) + { + dmadbg("ERROR: RX sam_dmastart failed: %d\n", ret); + return; + } + + spi_rxdma_sample(spics, DMA_AFTER_START); + + ret = sam_dmastart(spics->txdma, spi_txcallback, (void *)spics); + if (ret < 0) + { + dmadbg("ERROR: RX sam_dmastart failed: %d\n", ret); + sam_dmastop(spics->rxdma); + return; + } + + spi_txdma_sample(spics, DMA_AFTER_START); + + /* Wait for DMA completion. This is done in a loop becaue there my be + * false alarm semaphore counts that cause sam_wait() not fail to wait + * or to wake-up prematurely (for example due to the receipt of a signal). + * We know that the DMA has completed when the result is anything other + * that -EBUSY. + */ + + do + { + /* Start (or re-start) the watchdog timeout */ + + ret = wd_start(spics->dmadog, DMA_TIMEOUT_TICKS, + (wdentry_t)spi_dmatimeout, 1, (uint32_t)spics); + if (ret != OK) + { + spidbg("ERROR: wd_start failed: %d\n", ret); + } + + /* Wait for the DMA complete */ + + ret = sem_wait(&spics->dmawait); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(spics->dmadog); + + /* Check if we were awakened by an error of some kind */ + + if (ret < 0) + { + /* EINTR is not a failure. That simply means that the wait + * was awakened by a signel. + */ + + int errorcode = errno; + if (errorcode != EINTR) + { + DEBUGPANIC(); + return; + } + } + + /* Not that we might be awkened before the wait is over due to + * residual counts on the semaphore. So, to handle, that case, + * we loop until somthing changes the DMA result to any value other + * than -EBUSY. + */ + } + while (spics->result == -EBUSY); + + /* Dump the sampled DMA registers */ + + spi_dma_sampledone(spics); + + /* Make sure that the DMA is stopped (it will be stopped automatically + * on normal transfers, but not necessarily when the transfer terminates + * on an error condition). + */ + + sam_dmastop(spics->rxdma); + sam_dmastop(spics->txdma); + + /* All we can do is complain if the DMA fails */ + + if (spics->result) + { + spidbg("ERROR: DMA failed with result: %d\n", spics->result); + } +} +#endif /* CONFIG_SAMA5_SPI_DMA */ + /*************************************************************************** * Name: spi_sndblock * @@ -1124,7 +1741,7 @@ struct spi_dev_s *up_spiinitialize(int port) spics = (struct sam_spics_s *)zalloc(sizeof(struct sam_spics_s)); if (!spics) { - spivdbg("ERROR: Failed to allocate a chip select structure\n"); + spidbg("ERROR: Failed to allocate a chip select structure\n"); return NULL; } @@ -1132,6 +1749,39 @@ struct spi_dev_s *up_spiinitialize(int port) * were zeroed by zalloc(). */ +#ifdef CONFIG_SAMA5_SPI_DMA + /* Can we do DMA on this peripheral? */ + + spics->candma = spino ? SAMA5_SPI1_DMA : SAMA5_SPI0_DMA; + + /* Pre-allocate DMA channels. These allocations exploit that fact that + * SPI0 is managed by DMAC0 and SPI1 is managed by DMAC1. Hence, + * the SPI number (spino) is the same as the DMAC number. + */ + + if (spics->candma) + { + spics->rxdma = sam_dmachannel(spino, 0); + if (!spics->rxdma) + { + spidbg("ERROR: Failed to allocate the RX DMA channel\n"); + spics->candma = false; + } + } + + if (spics->candma) + { + spics->txdma = sam_dmachannel(spino, 0); + if (!spics->txdma) + { + spidbg("ERROR: Failed to allocate the TX DMA channel\n"); + sam_dmafree(spics->rxdma); + spics->rxdma = NULL; + spics->candma = false; + } + } +#endif + /* Select the SPI operations */ #if defined(CONFIG_SAMA5_SPI0) && defined(CONFIG_SAMA5_SPI1) @@ -1225,6 +1875,20 @@ struct spi_dev_s *up_spiinitialize(int port) sem_init(&spi->spisem, 0, 1); spi->initialized = true; #endif + +#ifdef CONFIG_SAMA5_SPI_DMA + /* Initialize the SPI semaphore that is used to wake up the waiting + * thread when the DMA transfer completes. + */ + + sem_init(&spics->dmawait, 0, 0); + + /* Create a watchdog time to catch DMA timeouts */ + + spics->dmadog = wd_create(); + DEBUGASSERT(spics->dmadog); +#endif + spi_dumpregs(spi, "After initialization"); }