Merged in extent3d/nuttx/dma (pull request #597)
SAMDL DMA fixes and experimental SPI support * SAMDL: Fix DMA controller support * SAMDL: Added experimental DMA support to SPI driver. spi_exchange() uses a pair of DMA channels for TX and RX Approved-by: Gregory Nutt <gnutt@nuttx.org>
This commit is contained in:
parent
3a029ed185
commit
10de81ebdf
@ -488,6 +488,20 @@ config SAMDL_DMAC
|
||||
select ARCH_DMA
|
||||
depends on SAMDL_HAVE_DMAC && EXPERIMENTAL
|
||||
|
||||
if SAMDL_DMAC && EXPERIMENTAL
|
||||
|
||||
menu "DMA options"
|
||||
|
||||
config SAMDL_SPI_DMA
|
||||
bool "SPI DMA"
|
||||
default n
|
||||
depends on (SAMDL_HAVE_SPI)
|
||||
---help---
|
||||
Use DMA for SPI SERCOM peripherals.
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
||||
config SAMDL_EVSYS
|
||||
bool "Event System"
|
||||
default n
|
||||
@ -767,3 +781,4 @@ config SAMDL_USB_REGDEBUG
|
||||
CONFIG_DEBUG_USB_INFO.
|
||||
|
||||
endif # SAMDL_HAVE_USB
|
||||
|
||||
|
@ -145,15 +145,19 @@ static sem_t g_dsem;
|
||||
|
||||
static struct sam_dmach_s g_dmach[SAMDL_NDMACHAN];
|
||||
|
||||
/* DMA descriptor tables positioned in LPRAM. In this use case, it is
|
||||
* acceptable for the writeback descriptors to overlap the base
|
||||
* descriptors since the base descriptors are always initialized prior
|
||||
* to starting each DMA transaction.
|
||||
/*
|
||||
* NOTE: Using the same address as the base descriptors for writeback descriptors
|
||||
* causes TERR and FERR interrupts to be raised immediately after starting DMA.
|
||||
* This was tested on SAMD21G18A, and it would appear that the writeback
|
||||
* buffer must be located at a different memory address.
|
||||
*
|
||||
* - Matt Thompson
|
||||
*/
|
||||
|
||||
static struct dma_desc_s g_base_desc[SAMDL_NDMACHAN]
|
||||
__attribute__ ((section(".lpram"), aligned(16)));
|
||||
#define g_writeback_desc g_base_desc
|
||||
static struct dma_desc_s g_writeback_desc[SAMDL_NDMACHAN]
|
||||
__attribute__ ((section(".lpram"), aligned(16)));
|
||||
|
||||
#if CONFIG_SAMDL_DMAC_NDESC > 0
|
||||
/* Additional DMA descriptors for (optional) multi-block transfer support.
|
||||
@ -291,9 +295,10 @@ static int sam_dmainterrupt(int irq, void *context, FAR void *arg)
|
||||
unsigned int chndx;
|
||||
uint16_t intpend;
|
||||
|
||||
|
||||
/* Process all pending channel interrupts */
|
||||
|
||||
while (((intpend = getreg16(SAM_DMAC_INTPEND)) & DMAC_INTPEND_PEND) != 0)
|
||||
while ((intpend = getreg16(SAM_DMAC_INTPEND)) != 0)
|
||||
{
|
||||
/* Get the channel that generated the interrupt */
|
||||
|
||||
@ -322,10 +327,17 @@ static int sam_dmainterrupt(int irq, void *context, FAR void *arg)
|
||||
sam_dmaterminate(dmach, OK);
|
||||
}
|
||||
|
||||
else if ((intpend & DMAC_INTPEND_TERR) != 0)
|
||||
{
|
||||
dmaerr("Invalid descriptor. Channel %d\n", chndx);
|
||||
sam_dmaterminate(dmach, -EIO);
|
||||
}
|
||||
|
||||
/* Check for channel suspend interrupt */
|
||||
|
||||
else if ((intpend & DMAC_INTPEND_SUSP) != 0)
|
||||
{
|
||||
dmaerr("Channel suspended. Channel %d\n", chndx);
|
||||
/* REVISIT: Do we want to do anything here? */
|
||||
}
|
||||
}
|
||||
@ -392,6 +404,7 @@ static struct dma_desc_s *sam_alloc_desc(struct sam_dmach_s *dmach)
|
||||
/* Yes, return a pointer to the base descriptor */
|
||||
|
||||
desc->srcaddr = (uint32_t)-1; /* Any non-zero value */
|
||||
|
||||
return desc;
|
||||
}
|
||||
#if CONFIG_SAMDL_DMAC_NDESC > 0
|
||||
@ -406,6 +419,7 @@ static struct dma_desc_s *sam_alloc_desc(struct sam_dmach_s *dmach)
|
||||
|
||||
sam_takedsem();
|
||||
|
||||
|
||||
/* Examine each list entry to find an available one -- i.e., one
|
||||
* with srcaddr == 0. That srcaddr field is set to zero by the DMA
|
||||
* transfer complete interrupt handler. The following should be safe
|
||||
@ -472,7 +486,7 @@ static struct dma_desc_s *sam_append_desc(struct sam_dmach_s *dmach,
|
||||
/* Allocate a DMA descriptor */
|
||||
|
||||
desc = sam_alloc_desc(dmach);
|
||||
if (desc == NULL)
|
||||
if (desc != NULL)
|
||||
{
|
||||
/* We have it. Initialize the new descriptor list entry */
|
||||
|
||||
@ -509,6 +523,10 @@ static struct dma_desc_s *sam_append_desc(struct sam_dmach_s *dmach,
|
||||
dmach->dc_tail = desc;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dmaerr("Failed to allocate descriptor\n");
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
@ -556,9 +574,11 @@ static void sam_free_desc(struct sam_dmach_s *dmach)
|
||||
|
||||
next = (struct dma_desc_s *)desc->descaddr;
|
||||
memset(desc, 0, sizeof(struct dma_desc_s));
|
||||
|
||||
sam_givedsem();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -604,7 +624,7 @@ static uint16_t sam_bytes2beats(struct sam_dmach_s *dmach, size_t nbytes)
|
||||
|
||||
/* The number of beats is then the ceiling of the division */
|
||||
|
||||
mask = (1 < beatsize) - 1;
|
||||
mask = (1 << beatsize) - 1;
|
||||
nbeats = (nbytes + mask) >> beatsize;
|
||||
DEBUGASSERT(nbeats <= UINT16_MAX);
|
||||
return (uint16_t)nbeats;
|
||||
@ -653,14 +673,25 @@ static int sam_txbuffer(struct sam_dmach_s *dmach, uint32_t paddr,
|
||||
tmp = (dmach->dc_flags & DMACH_FLAG_BEATSIZE_MASK) >> DMACH_FLAG_BEATSIZE_SHIFT;
|
||||
btctrl |= tmp << LPSRAM_BTCTRL_BEATSIZE_SHIFT;
|
||||
|
||||
/* See Addressing on page 264 of the datasheet.
|
||||
* When increment is used, we have to adjust the address in the descriptor
|
||||
* based on the beat count remaining in the block
|
||||
*/
|
||||
|
||||
/* Set up the Block Transfer Count Register configuration */
|
||||
|
||||
btcnt = sam_bytes2beats(dmach, nbytes);
|
||||
|
||||
if ((dmach->dc_flags & DMACH_FLAG_MEM_INCREMENT) != 0)
|
||||
{
|
||||
btctrl |= LPSRAM_BTCTRL_SRCINC;
|
||||
maddr += nbytes;
|
||||
}
|
||||
|
||||
if ((dmach->dc_flags & DMACH_FLAG_PERIPH_INCREMENT) != 0)
|
||||
{
|
||||
btctrl |= LPSRAM_BTCTRL_DSTINC;
|
||||
paddr += nbytes;
|
||||
}
|
||||
|
||||
if ((dmach->dc_flags & DMACH_FLAG_STEPSEL) == DMACH_FLAG_STEPSEL_PERIPH)
|
||||
@ -671,10 +702,6 @@ static int sam_txbuffer(struct sam_dmach_s *dmach, uint32_t paddr,
|
||||
tmp = (dmach->dc_flags & DMACH_FLAG_STEPSIZE_MASK) >> LPSRAM_BTCTRL_STEPSIZE_SHIFT;
|
||||
btctrl |= tmp << LPSRAM_BTCTRL_STEPSIZE_SHIFT;
|
||||
|
||||
/* Set up the Block Transfer Count Register configuration */
|
||||
|
||||
btcnt = sam_bytes2beats(dmach, nbytes);
|
||||
|
||||
/* Add the new descriptor list entry */
|
||||
|
||||
if (!sam_append_desc(dmach, btctrl, btcnt, maddr, paddr))
|
||||
@ -729,14 +756,20 @@ static int sam_rxbuffer(struct sam_dmach_s *dmach, uint32_t paddr,
|
||||
tmp = (dmach->dc_flags & DMACH_FLAG_BEATSIZE_MASK) >> DMACH_FLAG_BEATSIZE_SHIFT;
|
||||
btctrl |= tmp << LPSRAM_BTCTRL_BEATSIZE_SHIFT;
|
||||
|
||||
/* Set up the Block Transfer Count Register configuration */
|
||||
|
||||
btcnt = sam_bytes2beats(dmach, nbytes);
|
||||
|
||||
if ((dmach->dc_flags & DMACH_FLAG_PERIPH_INCREMENT) != 0)
|
||||
{
|
||||
btctrl |= LPSRAM_BTCTRL_SRCINC;
|
||||
paddr += nbytes;
|
||||
}
|
||||
|
||||
if ((dmach->dc_flags & DMACH_FLAG_MEM_INCREMENT) != 0)
|
||||
{
|
||||
btctrl |= LPSRAM_BTCTRL_DSTINC;
|
||||
maddr += nbytes;
|
||||
}
|
||||
|
||||
if ((dmach->dc_flags & DMACH_FLAG_STEPSEL) == DMACH_FLAG_STEPSEL_MEM)
|
||||
@ -747,10 +780,6 @@ static int sam_rxbuffer(struct sam_dmach_s *dmach, uint32_t paddr,
|
||||
tmp = (dmach->dc_flags & DMACH_FLAG_STEPSIZE_MASK) >> LPSRAM_BTCTRL_STEPSIZE_SHIFT;
|
||||
btctrl |= tmp << LPSRAM_BTCTRL_STEPSIZE_SHIFT;
|
||||
|
||||
/* Set up the Block Transfer Count Register configuration */
|
||||
|
||||
btcnt = sam_bytes2beats(dmach, nbytes);
|
||||
|
||||
/* Add the new descriptor list entry */
|
||||
|
||||
if (!sam_append_desc(dmach, btctrl, btcnt, paddr, maddr))
|
||||
@ -785,6 +814,7 @@ void weak_function up_dmainitialize(void)
|
||||
/* Initialize global semaphores */
|
||||
|
||||
nxsem_init(&g_chsem, 0, 1);
|
||||
|
||||
#if CONFIG_SAMDL_DMAC_NDESC > 0
|
||||
nxsem_init(&g_dsem, 0, CONFIG_SAMDL_DMAC_NDESC);
|
||||
#endif
|
||||
@ -1183,7 +1213,7 @@ int sam_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
|
||||
}
|
||||
|
||||
chctrlb |= tmp << DMAC_CHCTRLB_TRIGSRC_SHIFT;
|
||||
putreg8(chctrlb, SAM_DMAC_CHCTRLB);
|
||||
putreg32(chctrlb, SAM_DMAC_CHCTRLB);
|
||||
|
||||
/* Setup the Quality of Service Control Register
|
||||
*
|
||||
@ -1330,7 +1360,7 @@ void sam_dmadump(DMA_HANDLE handle, const struct sam_dmaregs_s *regs,
|
||||
regs->ctrl, regs->crcctrl, regs->crcdatain, regs->crcchksum);
|
||||
dmainfo(" CRCSTATUS: %02x DBGCTRL: %02x QOSCTRL: %02x SWTRIGCTRL: %08x\n",
|
||||
regs->crcstatus, regs->dbgctrl, regs->qosctrl, regs->swtrigctrl);
|
||||
dmainfo(" PRICTRL0: %08x INTPEND: %04x INSTSTATUS: %08x BUSYCH: %08x\n",
|
||||
dmainfo(" PRICTRL0: %08x INTPEND: %04x INTSTATUS: %08x BUSYCH: %08x\n",
|
||||
regs->prictrl0, regs->intpend, regs->intstatus, regs->busych);
|
||||
dmainfo(" PENDCH: %08x ACTIVE: %08x BASEADDR: %08x WRBADDR: %08x\n",
|
||||
regs->pendch, regs->active, regs->baseaddr, regs->wrbaddr);
|
||||
|
@ -69,6 +69,10 @@
|
||||
#include "sam_sercom.h"
|
||||
#include "sam_spi.h"
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
#include "sam_dmac.h"
|
||||
#endif
|
||||
|
||||
#include <arch/board/board.h>
|
||||
|
||||
#ifdef SAMDL_HAVE_SPI
|
||||
@ -118,6 +122,16 @@ struct sam_spidev_s
|
||||
uint8_t mode; /* Mode 0,1,2,3 */
|
||||
uint8_t nbits; /* Width of word in bits (8 to 16) */
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
/* DMA */
|
||||
|
||||
uint8_t dma_tx_trig; /* DMA TX trigger source to use */
|
||||
uint8_t dma_rx_trig; /* DMA RX trigger source to use */
|
||||
DMA_HANDLE dma_tx; /* DMA TX channel handle */
|
||||
DMA_HANDLE dma_rx; /* DMA RX channel handle */
|
||||
sem_t dmasem; /* Transfer wait semaphore */
|
||||
#endif
|
||||
|
||||
/* Debug stuff */
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_REGDEBUG
|
||||
@ -154,6 +168,10 @@ static uint32_t spi_getreg32(struct sam_spidev_s *priv,
|
||||
static void spi_putreg32(struct sam_spidev_s *priv, uint32_t regval,
|
||||
unsigned int offset);
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
static void spi_dma_setup(struct sam_spidev_s *priv);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_SPI_INFO
|
||||
static void spi_dumpregs(struct sam_spidev_s *priv, const char *msg);
|
||||
#else
|
||||
@ -237,6 +255,10 @@ static struct sam_spidev_s g_spi0dev =
|
||||
.srcfreq = BOARD_SERCOM0_FREQUENCY,
|
||||
.base = SAM_SERCOM0_BASE,
|
||||
.spilock = SEM_INITIALIZER(1),
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
.dma_tx_trig = DMAC_TRIGSRC_SERCOM0_TX,
|
||||
.dma_rx_trig = DMAC_TRIGSRC_SERCOM0_RX,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -283,6 +305,10 @@ static struct sam_spidev_s g_spi1dev =
|
||||
.srcfreq = BOARD_SERCOM1_FREQUENCY,
|
||||
.base = SAM_SERCOM1_BASE,
|
||||
.spilock = SEM_INITIALIZER(1),
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
.dma_tx_trig = DMAC_TRIGSRC_SERCOM1_TX,
|
||||
.dma_rx_trig = DMAC_TRIGSRC_SERCOM1_RX,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -329,6 +355,10 @@ static struct sam_spidev_s g_spi2dev =
|
||||
.srcfreq = BOARD_SERCOM2_FREQUENCY,
|
||||
.base = SAM_SERCOM2_BASE,
|
||||
.spilock = SEM_INITIALIZER(1),
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
.dma_tx_trig = DMAC_TRIGSRC_SERCOM2_TX,
|
||||
.dma_rx_trig = DMAC_TRIGSRC_SERCOM2_RX,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -375,6 +405,10 @@ static struct sam_spidev_s g_spi3dev =
|
||||
.srcfreq = BOARD_SERCOM3_FREQUENCY,
|
||||
.base = SAM_SERCOM3_BASE,
|
||||
.spilock = SEM_INITIALIZER(1),
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
.dma_tx_trig = DMAC_TRIGSRC_SERCOM3_TX,
|
||||
.dma_rx_trig = DMAC_TRIGSRC_SERCOM3_RX,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -421,6 +455,10 @@ static struct sam_spidev_s g_spi4dev =
|
||||
.srcfreq = BOARD_SERCOM4_FREQUENCY,
|
||||
.base = SAM_SERCOM4_BASE,
|
||||
.spilock = SEM_INITIALIZER(1),
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
.dma_tx_trig = DMAC_TRIGSRC_SERCOM4_TX,
|
||||
.dma_rx_trig = DMAC_TRIGSRC_SERCOM4_RX,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -467,6 +505,10 @@ static struct sam_spidev_s g_spi5dev =
|
||||
.srcfreq = BOARD_SERCOM5_FREQUENCY,
|
||||
.base = SAM_SERCOM5_BASE,
|
||||
.spilock = SEM_INITIALIZER(1),
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
.dma_tx_trig = DMAC_TRIGSRC_SERCOM5_TX,
|
||||
.dma_rx_trig = DMAC_TRIGSRC_SERCOM5_RX,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -1061,6 +1103,30 @@ static uint16_t spi_send(struct spi_dev_s *dev, uint16_t wd)
|
||||
return (uint16_t)rxbyte;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
static void spi_dma_callback(DMA_HANDLE dma, void *arg, int result)
|
||||
{
|
||||
struct sam_spidev_s *priv = (struct sam_spidev_s *)arg;
|
||||
|
||||
if(dma == priv->dma_rx)
|
||||
{
|
||||
|
||||
/* Notify the blocked spi_exchange() call that the transaction
|
||||
* has completed by posting to the semaphore
|
||||
*/
|
||||
|
||||
nxsem_post(&priv->dmasem);
|
||||
}
|
||||
else if(dma == priv->dma_tx)
|
||||
{
|
||||
if(result != OK)
|
||||
{
|
||||
spierr("DMA transmission failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: spi_exchange
|
||||
*
|
||||
@ -1089,14 +1155,51 @@ static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
|
||||
void *rxbuffer, size_t nwords)
|
||||
{
|
||||
struct sam_spidev_s *priv = (struct sam_spidev_s *)dev;
|
||||
|
||||
spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
int ret;
|
||||
uint32_t regval;
|
||||
|
||||
/* Disable SPI while we configure new DMA descriptors */
|
||||
|
||||
regval = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET);
|
||||
regval &= ~SPI_CTRLA_ENABLE;
|
||||
spi_putreg32(priv, regval, SAM_SPI_CTRLA_OFFSET);
|
||||
spi_wait_synchronization(priv);
|
||||
|
||||
/* Setup RX and TX DMA channels */
|
||||
|
||||
sam_dmatxsetup(priv->dma_tx, priv->base + SAM_SPI_DATA_OFFSET, txbuffer, nwords);
|
||||
sam_dmarxsetup(priv->dma_rx, priv->base + SAM_SPI_DATA_OFFSET, rxbuffer, nwords);
|
||||
|
||||
/* Start RX and TX DMA channels */
|
||||
|
||||
sam_dmastart(priv->dma_tx, spi_dma_callback, (void *)priv);
|
||||
sam_dmastart(priv->dma_rx, spi_dma_callback, (void *)priv);
|
||||
|
||||
/* Enable SPI to trigger the TX DMA channel */
|
||||
|
||||
regval = spi_getreg32(priv, SAM_SPI_CTRLA_OFFSET);
|
||||
regval |= SPI_CTRLA_ENABLE;
|
||||
spi_putreg32(priv, regval, SAM_SPI_CTRLA_OFFSET);
|
||||
spi_wait_synchronization(priv);
|
||||
|
||||
do
|
||||
{
|
||||
/* Wait for the DMA callback to notify us that the transfer is complete */
|
||||
|
||||
ret = nxsem_wait(&priv->dmasem);
|
||||
}
|
||||
while (ret < 0 && ret == -EINTR);
|
||||
#else
|
||||
const uint16_t *ptx16;
|
||||
const uint8_t *ptx8;
|
||||
uint16_t *prx16;
|
||||
uint8_t *prx8;
|
||||
uint16_t data;
|
||||
|
||||
spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
|
||||
|
||||
/* Set up data receive and transmit pointers */
|
||||
|
||||
if (priv->nbits > 8)
|
||||
@ -1203,6 +1306,7 @@ static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer,
|
||||
*prx16++ = (uint16_t)data;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -1312,6 +1416,28 @@ static void spi_pad_configure(struct sam_spidev_s *priv)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
static void spi_dma_setup(struct sam_spidev_s *priv)
|
||||
{
|
||||
|
||||
/* Allocate a pair of DMA channels */
|
||||
|
||||
priv->dma_rx = sam_dmachannel(DMACH_FLAG_BEATSIZE_BYTE |
|
||||
DMACH_FLAG_MEM_INCREMENT |
|
||||
DMACH_FLAG_PERIPH_RXTRIG(priv->dma_rx_trig));
|
||||
|
||||
priv->dma_tx = sam_dmachannel(DMACH_FLAG_BEATSIZE_BYTE |
|
||||
DMACH_FLAG_MEM_INCREMENT |
|
||||
DMACH_FLAG_PERIPH_TXTRIG(priv->dma_tx_trig));
|
||||
|
||||
/* Initialize the samaphore used to notify when DMA is complete */
|
||||
|
||||
nxsem_init(&priv->dmasem, 0, 0);
|
||||
nxsem_setprotocol(&priv->dmasem, SEM_PRIO_NONE);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
@ -1398,6 +1524,10 @@ struct spi_dev_s *sam_spibus_initialize(int port)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SAMDL_SPI_DMA
|
||||
spi_dma_setup(priv);
|
||||
#endif
|
||||
|
||||
/* Enable clocking to the SERCOM module in PM */
|
||||
|
||||
flags = enter_critical_section();
|
||||
|
Loading…
x
Reference in New Issue
Block a user