summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-08-09 13:12:16 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-08-09 13:12:16 -0600
commit2b72f7461679664e129e11f55604573245154fba (patch)
treec28fa0ee2626d35ef43f779c864d4e8b6699973c
parentc140d27900b95ceed867ef36102fcdeaa91a5cc8 (diff)
downloadnuttx-2b72f7461679664e129e11f55604573245154fba.tar.gz
nuttx-2b72f7461679664e129e11f55604573245154fba.tar.bz2
nuttx-2b72f7461679664e129e11f55604573245154fba.zip
SAM3,4,A5 DMA fixes; SAMA5 SPI driver now supports DMA transfers
-rw-r--r--nuttx/arch/arm/src/sam34/chip/sam3u_dmac.h2
-rw-r--r--nuttx/arch/arm/src/sam34/sam3u_dmac.c214
-rw-r--r--nuttx/arch/arm/src/sam34/sam_dmac.h18
-rw-r--r--nuttx/arch/arm/src/sama5/Kconfig26
-rwxr-xr-xnuttx/arch/arm/src/sama5/chip/sam_dmac.h7
-rw-r--r--nuttx/arch/arm/src/sama5/sam_dmac.c211
-rw-r--r--nuttx/arch/arm/src/sama5/sam_dmac.h105
-rw-r--r--nuttx/arch/arm/src/sama5/sam_hsmci.c28
-rw-r--r--nuttx/arch/arm/src/sama5/sam_spi.c678
-rw-r--r--nuttx/configs/sama5d3x-ek/README.txt10
-rw-r--r--nuttx/configs/sama5d3x-ek/src/sam_nsh.c1
-rw-r--r--nuttx/drivers/mtd/at25.c1
12 files changed, 1097 insertions, 204 deletions
diff --git a/nuttx/arch/arm/src/sam34/chip/sam3u_dmac.h b/nuttx/arch/arm/src/sam34/chip/sam3u_dmac.h
index 773008a65..a64f9764b 100644
--- a/nuttx/arch/arm/src/sam34/chip/sam3u_dmac.h
+++ b/nuttx/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/nuttx/arch/arm/src/sam34/sam3u_dmac.c b/nuttx/arch/arm/src/sam34/sam3u_dmac.c
index 43d725b0d..4c568ba5b 100644
--- a/nuttx/arch/arm/src/sam34/sam3u_dmac.c
+++ b/nuttx/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)
{
@@ -406,6 +406,48 @@ sam_txctrlabits(struct sam_dma_s *dmach)
}
/****************************************************************************
+ * 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
*
* Description:
@@ -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)
{
@@ -498,6 +551,48 @@ static inline uint32_t sam_rxctrlabits(struct sam_dma_s *dmach)
}
/****************************************************************************
+ * 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
*
* Description:
@@ -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/nuttx/arch/arm/src/sam34/sam_dmac.h b/nuttx/arch/arm/src/sam34/sam_dmac.h
index 4e34d4694..672520f50 100644
--- a/nuttx/arch/arm/src/sam34/sam_dmac.h
+++ b/nuttx/arch/arm/src/sam34/sam_dmac.h
@@ -195,6 +195,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
*
* Description:
diff --git a/nuttx/arch/arm/src/sama5/Kconfig b/nuttx/arch/arm/src/sama5/Kconfig
index 0c6cdedf2..b4dd69bcf 100644
--- a/nuttx/arch/arm/src/sama5/Kconfig
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/chip/sam_dmac.h b/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
index 6e4cf51a2..68a0cbd46 100755
--- a/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/sam_dmac.c b/nuttx/arch/arm/src/sama5/sam_dmac.c
index e595afde5..5aa14d188 100644
--- a/nuttx/arch/arm/src/sama5/sam_dmac.c
+++ b/nuttx/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,36 +881,36 @@ 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 */
- maxtransfer = 4 * DMAC_CH_CTRLA_BTSIZE_MAX;
+ case 1: /* 16 bits, 2 bytes */
+ maxtransfer = 2 * DMAC_CH_CTRLA_BTSIZE_MAX;
break;
- case 2: /* 8 bytes */
- maxtransfer = 8 * DMAC_CH_CTRLA_BTSIZE_MAX;
+ case 2: /* 32 bits 4 bytes */
+ maxtransfer = 4 * DMAC_CH_CTRLA_BTSIZE_MAX;
break;
- case 3: /* 16 bytes */
- maxtransfer = 16 * DMAC_CH_CTRLA_BTSIZE_MAX;
+ case 3: /* 64 bits, 8 bytes */
+ maxtransfer = 8 * DMAC_CH_CTRLA_BTSIZE_MAX;
break;
}
@@ -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,25 +936,25 @@ 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 */
- dmasize = (dmasize + 3) >> 2;
+ case 1: /* 16 bits, 2 bytes */
+ dmasize = (dmasize + 1) >> 1;
break;
- case 2: /* 8 bytes */
- dmasize = (dmasize + 7) >> 3;
+ case 2: /* 32 bits, 4 bytes */
+ dmasize = (dmasize + 3) >> 2;
break;
- case 3: /* 16 bytes */
- dmasize = (dmasize + 15) >> 4;
+ case 3: /* 64 bits, 8 bytes */
+ dmasize = (dmasize + 7) >> 3;
break;
}
@@ -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,36 +1049,36 @@ 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 */
- maxtransfer = 4 * DMAC_CH_CTRLA_BTSIZE_MAX;
+ case 1: /* 16 bits, 2 bytes */
+ maxtransfer = 2 * DMAC_CH_CTRLA_BTSIZE_MAX;
break;
- case 2: /* 8 bytes */
- maxtransfer = 8 * DMAC_CH_CTRLA_BTSIZE_MAX;
+ case 2: /* 32 bits, 4 bytes */
+ maxtransfer = 4 * DMAC_CH_CTRLA_BTSIZE_MAX;
break;
- case 3: /* 16 bytes */
- maxtransfer = 16 * DMAC_CH_CTRLA_BTSIZE_MAX;
+ case 3: /* 64 bits, 8 bytes */
+ maxtransfer = 8 * DMAC_CH_CTRLA_BTSIZE_MAX;
break;
}
@@ -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,25 +1104,25 @@ 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 */
- dmasize = (dmasize + 3) >> 2;
+ case 1: /* 16 bits, 2 bytes */
+ dmasize = (dmasize + 1) >> 1;
break;
- case 2: /* 8 bytes */
- dmasize = (dmasize + 7) >> 3;
+ case 2: /* 32 bits, 4 bytes */
+ dmasize = (dmasize + 3) >> 2;
break;
- case 3: /* 16 bytes */
- dmasize = (dmasize + 15) >> 4;
+ case 3: /* 64 bits, 8 bytes */
+ dmasize = (dmasize + 7) >> 3;
break;
}
@@ -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/nuttx/arch/arm/src/sama5/sam_dmac.h b/nuttx/arch/arm/src/sama5/sam_dmac.h
index 3d4831b21..172600924 100644
--- a/nuttx/arch/arm/src/sama5/sam_dmac.h
+++ b/nuttx/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_dmaconfig
+ *
+ * Description:
+ * There are two channel usage models: (1) The channel is allocated and configured
+ * in one step. This is the typical case where a DMA channel performs a constant
+ * role. The alternative is (2) where the DMA channel is reconfigured on the fly.
+ * In this case, the chflags provided to sam_dmachannel are not used and
+ * sam_dmaconfig() is called before each DMA to configure the DMA channel
+ * appropriately.
+ *
+ * Returned Value:
+ * None
+ *
+ ************************************************************************************/
+
+void sam_dmaconfig(DMA_HANDLE handle, uint32_t chflags);
+
+/************************************************************************************
* Name: sam_dmafree
*
* Description:
- * Release a DMA channel. NOTE: The 'handle' used in this argument must
- * NEVER be used again until sam_dmachannel() is called again to re-gain
- * a valid handle.
+ * 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/nuttx/arch/arm/src/sama5/sam_hsmci.c b/nuttx/arch/arm/src/sama5/sam_hsmci.c
index 7d5fe09e5..5c34b047f 100644
--- a/nuttx/arch/arm/src/sama5/sam_hsmci.c
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/sam_spi.c b/nuttx/arch/arm/src/sama5/sam_spi.c
index 53122e959..fefb349dd 100644
--- a/nuttx/arch/arm/src/sama5/sam_spi.c
+++ b/nuttx/arch/arm/src/sama5/sam_spi.c
@@ -48,12 +48,16 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <semaphore.h>
+#include <wdog.h>
#include <errno.h>
+#include <assert.h>
#include <debug.h>
#include <arch/board/board.h>
#include <nuttx/arch.h>
+#include <nuttx/clock.h>
#include <nuttx/spi/spi.h>
#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
@@ -504,6 +613,284 @@ static inline uint32_t spi_cs2pcs(struct sam_spics_s *spics)
}
/****************************************************************************
+ * 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
*
* Description:
@@ -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");
}
diff --git a/nuttx/configs/sama5d3x-ek/README.txt b/nuttx/configs/sama5d3x-ek/README.txt
index aef5e235f..a572ff497 100644
--- a/nuttx/configs/sama5d3x-ek/README.txt
+++ b/nuttx/configs/sama5d3x-ek/README.txt
@@ -987,6 +987,16 @@ Configurations
CONFIG_SAMA5_AT25_AUTOMOUNT=y : Mounts AT25 for NSH
CONFIG_SAMA5_AT25_FTL=y : Create block driver for FAT
+ The SPI driver can be built to do polled or DMA SPI data transfers.
+ The following additional changes will enable SPI DMA:
+
+ System Type -> SAMA5 Peripheral Support
+ CONFIG_SAMA5_DMAC0=y : Enable DMA controller 0
+
+ System Type -> SPI device driver options
+ CONFIG_SAMA5_SPI_DMA=y : Use DMA for SPI transfers
+ CONFIG_SAMA5_SPI_DMATHRESHOLD=4 : Don't DMA for small transfers
+
NOTE that you must close JP1 on the Embest/Ronetix board in
order to enable the AT25 FLASH chip select.
diff --git a/nuttx/configs/sama5d3x-ek/src/sam_nsh.c b/nuttx/configs/sama5d3x-ek/src/sam_nsh.c
index 2fb783a5d..b84418a6a 100644
--- a/nuttx/configs/sama5d3x-ek/src/sam_nsh.c
+++ b/nuttx/configs/sama5d3x-ek/src/sam_nsh.c
@@ -114,6 +114,7 @@ int nsh_archinitialize(void)
{
fdbg("ERROR: sam_at25_initialize failed: %d\n", ret);
return ret;
+ }
#endif
#ifdef HAVE_HSMCI_MTD
diff --git a/nuttx/drivers/mtd/at25.c b/nuttx/drivers/mtd/at25.c
index 914c08c1b..86556ac33 100644
--- a/nuttx/drivers/mtd/at25.c
+++ b/nuttx/drivers/mtd/at25.c
@@ -205,7 +205,6 @@ static inline int at25_readid(struct at25_dev_s *priv)
{
uint16_t manufacturer;
uint16_t memory;
- uint16_t version;
fvdbg("priv: %p\n", priv);