Changes to stm32_dmacapable interfaces from Mike Smith

This commit is contained in:
Gregory Nutt 2013-10-18 08:06:23 -06:00
parent 973d11f625
commit 944e0fe81d
7 changed files with 361 additions and 80 deletions

View File

@ -5795,4 +5795,9 @@
UART8 DMA configs. From Mike Smit (2013-10-18).
* arch/arm/src/stm32/Kconfig: DMA priority corrections from Mike Smith
(2013-10-18).
* arch/arm/src/stm32/stm32*_dma.c, stm32_sdio.c, and stm32_dma.h:
Changes to the stm32_dmacapable API. In order to correctly verify that
a buffer can be transferred, the transfer count and the CCR value are
required. Implemented stm32_dmacapable for stm32f1xx devices. Enhanced
stm32_dmacapable for stm32f2xx and stm32f4xx devices to check for
additional conditions that will cause DMA to fail or lose data (2013-10-18).

View File

@ -271,9 +271,10 @@ size_t stm32_dmaresidual(DMA_HANDLE handle);
*
* Description:
* Check if the DMA controller can transfer data to/from given memory
* address. This depends on the internal connections in the ARM bus matrix
* of the processor. Note that this only applies to memory addresses, it
* will return false for any peripheral address.
* address with the given configuration. This depends on the internal
* connections in the ARM bus matrix of the processor. Note that this
* only applies to memory addresses, it will return false for any peripheral
* address.
*
* Returned value:
* True, if transfer is possible.
@ -281,9 +282,9 @@ size_t stm32_dmaresidual(DMA_HANDLE handle);
****************************************************************************/
#ifdef CONFIG_STM32_DMACAPABLE
bool stm32_dmacapable(uintptr_t maddr);
bool stm32_dmacapable(uintptr_t maddr, uint32_t count, uint32_t ccr);
#else
# define stm32_dmacapable(maddr) (true)
# define stm32_dmacapable(maddr, count, ccr) (true)
#endif
/****************************************************************************

View File

@ -292,7 +292,7 @@
struct stm32_dev_s
{
struct sdio_dev_s dev; /* Standard, base SDIO interface */
/* STM32-specific extensions */
/* Event support */
@ -453,6 +453,8 @@ static int stm32_registercallback(FAR struct sdio_dev_s *dev,
#ifdef CONFIG_SDIO_DMA
static bool stm32_dmasupported(FAR struct sdio_dev_s *dev);
static int stm32_dmapreflight(FAR struct sdio_dev_s *dev,
FAR uint8_t *buffer, size_t buflen);
static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev,
FAR uint8_t *buffer, size_t buflen);
static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
@ -501,6 +503,7 @@ struct stm32_dev_s g_sdiodev =
.registercallback = stm32_registercallback,
#ifdef CONFIG_SDIO_DMA
.dmasupported = stm32_dmasupported,
.dmapreflight = stm32_dmapreflight,
.dmarecvsetup = stm32_dmarecvsetup,
.dmasendsetup = stm32_dmasendsetup,
#endif
@ -570,7 +573,7 @@ static void stm32_takesem(struct stm32_dev_s *priv)
static inline void stm32_setclkcr(uint32_t clkcr)
{
uint32_t regval = getreg32(STM32_SDIO_CLKCR);
/* Clear CLKDIV, PWRSAV, BYPASS, WIDBUS, NEGEDGE, HWFC_EN bits */
regval &= ~(SDIO_CLKCR_CLKDIV_MASK|SDIO_CLKCR_PWRSAV|SDIO_CLKCR_BYPASS|
@ -1024,7 +1027,7 @@ static void stm32_sendfifo(struct stm32_dev_s *priv)
{
data.b[i] = *ptr++;
}
/* Now the transfer is finished */
priv->remaining = 0;
@ -1192,7 +1195,7 @@ static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupeven
stm32_configxfrints(priv, 0);
/* Clearing pending interrupt status on all transfer related interrupts */
putreg32(SDIO_XFRDONE_ICR, STM32_SDIO_ICR);
/* If this was a DMA transfer, make sure that DMA is stopped */
@ -1452,7 +1455,7 @@ static int stm32_interrupt(int irq, void *context)
#ifdef CONFIG_SDIO_MUXBUS
static int stm32_lock(FAR struct sdio_dev_s *dev, bool lock)
{
{
/* Single SDIO instance so there is only one possibility. The multiplex
* bus is part of board support package.
*/
@ -1598,7 +1601,7 @@ static void stm32_clock(FAR struct sdio_dev_s *dev, enum sdio_clock_e rate)
/* Enable in initial ID mode clocking (<400KHz) */
case CLOCK_IDMODE:
case CLOCK_IDMODE:
clckr = (STM32_CLCKCR_INIT | SDIO_CLKCR_CLKEN);
break;
@ -1731,7 +1734,7 @@ static int stm32_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg)
cmdidx = (cmd & MMCSD_CMDIDX_MASK) >> MMCSD_CMDIDX_SHIFT;
regval |= cmdidx | SDIO_CMD_CPSMEN;
fvdbg("cmd: %08x arg: %08x regval: %08x\n", cmd, arg, regval);
/* Write the SDIO CMD */
@ -1880,7 +1883,7 @@ static int stm32_cancel(FAR struct sdio_dev_s *dev)
/* Clearing pending interrupt status on all transfer- and event- related
* interrupts
*/
putreg32(SDIO_WAITALL_ICR, STM32_SDIO_ICR);
/* Cancel any watchdog timeout */
@ -2119,7 +2122,7 @@ static int stm32_recvlong(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t rlo
ret = -EIO;
}
}
/* Return the long response */
putreg32(SDIO_RESPDONE_ICR|SDIO_CMDDONE_ICR, STM32_SDIO_ICR);
@ -2216,7 +2219,7 @@ static void stm32_waitenable(FAR struct sdio_dev_s *dev,
{
struct stm32_dev_s *priv = (struct stm32_dev_s*)dev;
uint32_t waitmask;
DEBUGASSERT(priv != NULL);
/* Disable event-related interrupts */
@ -2330,7 +2333,7 @@ static sdio_eventset_t stm32_eventwait(FAR struct sdio_dev_s *dev,
stm32_takesem(priv);
wkupevent = priv->wkupevent;
/* Check if the event has occurred. When the event has occurred, then
* evenset will be set to 0 and wkupevent will be set to a nonzero value.
*/
@ -2449,6 +2452,48 @@ static bool stm32_dmasupported(FAR struct sdio_dev_s *dev)
}
#endif
/****************************************************************************
* Name: stm32_dmapreflight
*
* Description:
* Preflight an SDIO DMA operation. If the buffer is not well-formed for
* SDIO DMA transfer (alignment, size, etc.) returns an error.
*
* Input Parameters:
* dev - An instance of the SDIO device interface
* buffer - The memory to DMA to/from
* buflen - The size of the DMA transfer in bytes
*
* Returned Value:
* OK on success; a negated errno on failure
****************************************************************************/
#ifdef CONFIG_SDIO_DMA
static int stm32_dmapreflight(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
size_t buflen)
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
/* Wide bus operation is required for DMA */
if (!priv->widebus)
{
return -EINVAL;
}
/* DMA must be possible to the buffer */
if (!stm32_dmacapable((uintptr_t)buffer, (buflen + 3) >> 2, SDIO_RXDMA32_CONFIG))
{
return -EFAULT;
}
return 0;
}
#endif
/****************************************************************************
* Name: stm32_dmarecvsetup
*
@ -2474,50 +2519,45 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
uint32_t dblocksize;
int ret = -EINVAL;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
DEBUGASSERT(stm32_dmapreflight(dev, buffer, buflen) == 0);
/* Reset the DPSM configuration */
stm32_datadisable();
/* Wide bus operation is required for DMA */
/* Initialize register sampling */
if (priv->widebus)
{
stm32_sampleinit();
stm32_sample(priv, SAMPLENDX_BEFORE_SETUP);
stm32_sampleinit();
stm32_sample(priv, SAMPLENDX_BEFORE_SETUP);
/* Save the destination buffer information for use by the interrupt handler */
/* Save the destination buffer information for use by the interrupt handler */
priv->buffer = (uint32_t*)buffer;
priv->remaining = buflen;
priv->dmamode = true;
priv->buffer = (uint32_t*)buffer;
priv->remaining = buflen;
priv->dmamode = true;
/* Then set up the SDIO data path */
/* Then set up the SDIO data path */
dblocksize = stm32_log2(buflen) << SDIO_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, buflen, dblocksize|SDIO_DCTRL_DTDIR);
dblocksize = stm32_log2(buflen) << SDIO_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, buflen, dblocksize|SDIO_DCTRL_DTDIR);
/* Configure the RX DMA */
/* Configure the RX DMA */
stm32_configxfrints(priv, SDIO_DMARECV_MASK);
stm32_configxfrints(priv, SDIO_DMARECV_MASK);
putreg32(1, SDIO_DCTRL_DMAEN_BB);
stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32_t)buffer,
(buflen + 3) >> 2, SDIO_RXDMA32_CONFIG);
/* Start the DMA */
putreg32(1, SDIO_DCTRL_DMAEN_BB);
stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32_t)buffer,
(buflen + 3) >> 2, SDIO_RXDMA32_CONFIG);
stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE);
stm32_dmastart(priv->dma, stm32_dmacallback, priv, false);
stm32_sample(priv, SAMPLENDX_AFTER_SETUP);
ret = OK;
}
/* Start the DMA */
return ret;
stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE);
stm32_dmastart(priv->dma, stm32_dmacallback, priv, false);
stm32_sample(priv, SAMPLENDX_AFTER_SETUP);
return OK;
}
#endif
@ -2546,54 +2586,48 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
uint32_t dblocksize;
int ret = -EINVAL;
DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
DEBUGASSERT(((uint32_t)buffer & 3) == 0);
DEBUGASSERT(stm32_dmapreflight(dev, buffer, buflen) == 0);
/* Reset the DPSM configuration */
stm32_datadisable();
/* Wide bus operation is required for DMA */
/* Initialize register sampling */
if (priv->widebus)
{
stm32_sampleinit();
stm32_sample(priv, SAMPLENDX_BEFORE_SETUP);
stm32_sampleinit();
stm32_sample(priv, SAMPLENDX_BEFORE_SETUP);
/* Save the source buffer information for use by the interrupt handler */
/* Save the source buffer information for use by the interrupt handler */
priv->buffer = (uint32_t*)buffer;
priv->remaining = buflen;
priv->dmamode = true;
priv->buffer = (uint32_t*)buffer;
priv->remaining = buflen;
priv->dmamode = true;
/* Then set up the SDIO data path */
/* Then set up the SDIO data path */
dblocksize = stm32_log2(buflen) << SDIO_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, buflen, dblocksize);
dblocksize = stm32_log2(buflen) << SDIO_DCTRL_DBLOCKSIZE_SHIFT;
stm32_dataconfig(SDIO_DTIMER_DATATIMEOUT, buflen, dblocksize);
/* Configure the TX DMA */
/* Configure the TX DMA */
stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32_t)buffer,
(buflen + 3) >> 2, SDIO_TXDMA32_CONFIG);
stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32_t)buffer,
(buflen + 3) >> 2, SDIO_TXDMA32_CONFIG);
stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE);
putreg32(1, SDIO_DCTRL_DMAEN_BB);
stm32_sample(priv, SAMPLENDX_BEFORE_ENABLE);
putreg32(1, SDIO_DCTRL_DMAEN_BB);
/* Start the DMA */
/* Start the DMA */
stm32_dmastart(priv->dma, stm32_dmacallback, priv, false);
stm32_sample(priv, SAMPLENDX_AFTER_SETUP);
stm32_dmastart(priv->dma, stm32_dmacallback, priv, false);
stm32_sample(priv, SAMPLENDX_AFTER_SETUP);
/* Enable TX interrrupts */
/* Enable TX interrrupts */
stm32_configxfrints(priv, SDIO_DMASEND_MASK);
stm32_configxfrints(priv, SDIO_DMASEND_MASK);
ret = OK;
}
return ret;
return OK;
}
#endif
@ -2738,7 +2772,7 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno)
/* Configure GPIOs for 4-bit, wide-bus operation (the chip is capable of
* 8-bit wide bus operation but D4-D7 are not configured).
*
*
* If bus is multiplexed then there is a custom bus configuration utility
* in the scope of the board support package.
*/
@ -2772,7 +2806,7 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno)
*
* Input Parameters:
* dev - An instance of the SDIO driver device state structure.
* cardinslot - true is a card has been detected in the slot; false if a
* cardinslot - true is a card has been detected in the slot; false if a
* card has been removed from the slot. Only transitions
* (inserted->removed or removed->inserted should be reported)
*
@ -2799,6 +2833,9 @@ void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot)
{
priv->cdstatus &= ~SDIO_STATUS_PRESENT;
}
irqrestore(flags);
fvdbg("cdstatus OLD: %02x NEW: %02x\n", cdstatus, priv->cdstatus);
/* Perform any requested callback if the status has changed */
@ -2807,7 +2844,6 @@ void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot)
{
stm32_callback(priv);
}
irqrestore(flags);
}
/****************************************************************************

View File

@ -611,8 +611,54 @@ size_t stm32_dmaresidual(DMA_HANDLE handle)
****************************************************************************/
#ifdef CONFIG_STM32_DMACAPABLE
bool stm32_dmacapable(uint32_t maddr)
bool stm32_dmacapable(uint32_t maddr, uint32_t count, uint32_t ccr)
{
uint32_t transfer_size;
uint32_t mend;
/* Verify that the address conforms to the memory transfer size.
* Transfers to/from memory performed by the DMA controller are
* required to be aligned to their size.
*
* See ST RM0090 rev4, section 9.3.11
*
* Compute mend inline to avoid a possible non-constant integer
* multiply.
*/
switch (ccr & STM32_DMA_SCR_MSIZE_MASK)
{
case DMA_SCR_MSIZE_8BITS:
transfer_size = 1;
mend = maddr + count - 1;
break;
case DMA_SCR_MSIZE_16BITS:
transfer_size = 2;
mend = maddr + (count << 1) - 1;
break;
case DMA_SCR_MSIZE_32BITS:
transfer_size = 4;
mend = maddr + (count << 2) - 1;
break;
default
return false;
}
if ((maddr & (transfer_size - 1)) != 0)
{
return false;
}
/* Verify that the transfer is to a memory region that supports DMA. */
if ((maddr & STM32_REGION_MASK) != (mend & STM32_REGION_MASK))
{
return false;
}
switch (maddr & STM32_REGION_MASK)
{
#if defined(CONFIG_STM32_STM32F10XX)
@ -624,10 +670,12 @@ bool stm32_dmacapable(uint32_t maddr)
case STM32_SRAM_BASE:
case STM32_CODE_BASE:
/* All RAM and flash is supported */
return true;
default:
/* Everything else is unsupported by DMA */
return false;
}
}

View File

@ -856,8 +856,91 @@ size_t stm32_dmaresidual(DMA_HANDLE handle)
****************************************************************************/
#ifdef CONFIG_STM32_DMACAPABLE
bool stm32_dmacapable(uint32_t maddr)
bool stm32_dmacapable(uint32_t maddr, uint32_t count, uint32_t ccr)
{
uint32_t transfer_size, burst_length;
uint32_t mend;
/* Verify that the address conforms to the memory transfer size.
* Transfers to/from memory performed by the DMA controller are
* required to be aligned to their size.
*
* See ST RM0090 rev4, section 9.3.11
*
* Compute mend inline to avoid a possible non-constant integer
* multiply.
*/
switch (ccr & STM32_DMA_SCR_MSIZE_MASK)
{
case DMA_SCR_MSIZE_8BITS:
transfer_size = 1;
mend = maddr + count - 1;
break;
case DMA_SCR_MSIZE_16BITS:
transfer_size = 2;
mend = maddr + (count << 1) - 1;
break;
case DMA_SCR_MSIZE_32BITS:
transfer_size = 4;
mend = maddr + (count << 2) - 1;
break;
default
return false;
}
if ((maddr & (transfer_size - 1)) != 0)
{
return false;
}
/* Verify that burst transfers do not cross a 1KiB boundary. */
if ((maddr / 1024) != (mend / 1024))
{
/* The transfer as a whole crosses a 1KiB boundary.
* Verify that no burst does by asserting that the address
* is aligned to the burst length.
*/
switch (ccr & STM32_DMA_SCR_MBURST_MASK)
{
case DMA_SCR_MBURST_SINGLE:
burst_length = transfer_size;
break;
case DMA_SCR_MBURST_INCR4:
burst_length = transfer_size << 2;
break;
case DMA_SCR_MBURST_INCR8:
burst_length = transfer_size << 3;
break;
case DMA_SCR_MBURST_INCR16:
burst_length = transfer_size << 4;
break;
default:
return false;
}
if ((maddr & (burst_length - 1)) != 0)
{
return false;
}
}
/* Verify that the transfer is to a memory region that supports DMA. */
if ((maddr & STM32_REGION_MASK) != (mend & STM32_REGION_MASK))
{
return false;
}
switch (maddr & STM32_REGION_MASK)
{
case STM32_FSMC_BANK1:
@ -867,10 +950,12 @@ bool stm32_dmacapable(uint32_t maddr)
case STM32_SRAM_BASE:
case STM32_CODE_BASE:
/* All RAM and flash is supported */
return true;
default:
/* Everything else is unsupported by DMA */
return false;
}
}

View File

@ -607,6 +607,10 @@ void stm32_dmasetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
dmadbg("paddr: %08x maddr: %08x ntransfers: %d scr: %08x\n",
paddr, maddr, ntransfers, scr);
#ifdef CONFIG_STM32_DMACAPABLE
DEBUGASSERT(stm32_dmacapable(maddr, ntransfers, scr))
#endif
/* "If the stream is enabled, disable it by resetting the EN bit in the
* DMA_SxCR register, then read this bit in order to confirm that there is no
* ongoing stream operation. Writing this bit to 0 is not immediately
@ -859,8 +863,98 @@ size_t stm32_dmaresidual(DMA_HANDLE handle)
****************************************************************************/
#ifdef CONFIG_STM32_DMACAPABLE
bool stm32_dmacapable(uint32_t maddr)
bool stm32_dmacapable(uint32_t maddr, uint32_t count, uint32_t ccr)
{
uint32_t transfer_size, burst_length;
uint32_t mend;
dmavdbg("stm32_dmacapable: 0x%08x/%u 0x%08x\n", maddr, count, ccr);
/* Verify that the address conforms to the memory transfer size.
* Transfers to/from memory performed by the DMA controller are
* required to be aligned to their size.
*
* See ST RM0090 rev4, section 9.3.11
*
* Compute mend inline to avoid a possible non-constant integer
* multiply.
*/
switch (ccr & DMA_SCR_MSIZE_MASK)
{
case DMA_SCR_MSIZE_8BITS:
transfer_size = 1;
mend = maddr + count - 1;
break;
case DMA_SCR_MSIZE_16BITS:
transfer_size = 2;
mend = maddr + (count << 1) - 1;
break;
case DMA_SCR_MSIZE_32BITS:
transfer_size = 4;
mend = maddr + (count << 2) - 1;
break;
default:
dmavdbg("stm32_dmacapable: bad transfer size in CCR\n");
return false;
}
if ((maddr & (transfer_size - 1)) != 0)
{
dmavdbg("stm32_dmacapable: transfer unaligned\n");
return false;
}
/* Verify that burst transfers do not cross a 1KiB boundary. */
if ((maddr / 1024) != (mend / 1024))
{
/* The transfer as a whole crosses a 1KiB boundary.
* Verify that no burst does by asserting that the address
* is aligned to the burst length.
*/
switch (ccr & DMA_SCR_MBURST_MASK)
{
case DMA_SCR_MBURST_SINGLE:
burst_length = transfer_size;
break;
case DMA_SCR_MBURST_INCR4:
burst_length = transfer_size << 2;
break;
case DMA_SCR_MBURST_INCR8:
burst_length = transfer_size << 3;
break;
case DMA_SCR_MBURST_INCR16:
burst_length = transfer_size << 4;
break;
default:
dmavdbg("stm32_dmacapable: bad burst size in CCR\n");
return false;
}
if ((maddr & (burst_length - 1)) != 0)
{
dmavdbg("stm32_dmacapable: burst crosses 1KiB\n");
return false;
}
}
/* Verify that the transfer is to a memory region that supports DMA. */
if ((maddr & STM32_REGION_MASK) != (mend & STM32_REGION_MASK))
{
dmavdbg("stm32_dmacapable: transfer crosses memory region\n");
return false;
}
switch (maddr & STM32_REGION_MASK)
{
case STM32_FSMC_BANK1:
@ -869,21 +963,29 @@ bool stm32_dmacapable(uint32_t maddr)
case STM32_FSMC_BANK4:
case STM32_SRAM_BASE:
/* All RAM is supported */
return true;
break;
case STM32_CODE_BASE:
/* Everything except the CCM ram is supported */
if (maddr >= STM32_CCMRAM_BASE &&
(maddr - STM32_CCMRAM_BASE) < 65536)
{
dmavdbg("stm32_dmacapable: transfer targets CCMRAM\n");
return false;
}
return true;
break;
default:
/* Everything else is unsupported by DMA */
dmavdbg("stm32_dmacapable: transfer targets unknown/unsupported region\n");
return false;
}
dmavdbg("stm32_dmacapable: transfer OK\n");
return true;
}
#endif

View File

@ -78,6 +78,10 @@ config SDIO_DMA
---help---
SDIO driver supports DMA
config SDIO_PREFLIGHT
bool
default n
config SDIO_MUXBUS
bool "SDIO bus share support"
default n