SAM3,4,A5 DMA fixes; SAMA5 SPI driver now supports DMA transfers

This commit is contained in:
Gregory Nutt 2013-08-09 13:12:16 -06:00
parent 2b36e7e266
commit a2ba8992a9
9 changed files with 1087 additions and 204 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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
*

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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
@ -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");
}