SAM3,4,A5 DMA fixes; SAMA5 SPI driver now supports DMA transfers
This commit is contained in:
parent
2b36e7e266
commit
a2ba8992a9
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user