Numerous fixes for basic STM32 SDIO DMA access

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2279 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2009-11-26 00:18:22 +00:00
parent f702470d68
commit 5df8a7a194
6 changed files with 229 additions and 91 deletions

View File

@ -124,6 +124,19 @@ void up_initialize(void)
up_irqinitialize();
/* Initialize the DMA subsystem if the weak function stm32_dmainitialize has been
* brought into the build
*/
#ifdef CONFIG_ARCH_DMA
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (up_dmainitialize)
#endif
{
up_dmainitialize();
}
#endif
/* Initialize the system timer interrupt */
#if !defined(CONFIG_SUPPRESS_INTERRUPTS) && !defined(CONFIG_SUPPRESS_TIMER_INTS)

View File

@ -156,6 +156,9 @@ extern void up_boot(void);
extern void up_copystate(uint32 *dest, uint32 *src);
extern void up_decodeirq(uint32 *regs);
extern void up_irqinitialize(void);
#ifdef CONFIG_ARCH_DMA
extern void weak_function up_dmainitialize(void);
#endif
extern int up_saveusercontext(uint32 *saveregs);
extern void up_fullcontextrestore(uint32 *restoreregs) __attribute__ ((noreturn));
extern void up_switchcontext(uint32 *saveregs, uint32 *restoreregs);

View File

@ -44,9 +44,12 @@
#include <debug.h>
#include <errno.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <arch/irq.h>
#include "up_arch.h"
#include "up_internal.h"
#include "os_internal.h"
#include "chip.h"
#include "stm32_dma.h"
@ -64,6 +67,10 @@
# define DMA_NCHANNELS DMA1_NCHANNELS
#endif
#ifndef CONFIG_DMA_PRI
# define CONFIG_DMA_PRI NVIC_SYSH_PRIORITY_DEFAULT
#endif
/* Convert the DMA channel base address to the DMA register block address */
#define DMA_BASE(ch) (ch & 0xfffffc00)
@ -76,7 +83,7 @@
struct stm32_dma_s
{
ubyte chan; /* DMA channel number */
ubyte chan; /* DMA channel number (0-6) */
ubyte irq; /* DMA channel IRQ number */
sem_t sem; /* Used to wait for DMA channel to become available */
uint32 base; /* DMA register channel base address */
@ -93,58 +100,58 @@ struct stm32_dma_s
static struct stm32_dma_s g_dma[DMA_NCHANNELS] =
{
{
.chan = STM32_DMA1_CHAN1,
.chan = 0,
.irq = STM32_IRQ_DMA1CH1,
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(0),
},
{
.chan = STM32_DMA1_CHAN2,
.chan = 1,
.irq = STM32_IRQ_DMA1CH2,
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(1),
},
{
.chan = STM32_DMA1_CHAN3,
.chan = 2,
.irq = STM32_IRQ_DMA1CH3,
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(2),
},
{
.chan = STM32_DMA1_CHAN4,
.chan = 3,
.irq = STM32_IRQ_DMA1CH4,
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(3),
},
{
.chan = STM32_DMA1_CHAN5,
.chan = 4,
.irq = STM32_IRQ_DMA1CH5,
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(4),
},
{
.chan = STM32_DMA1_CHAN6,
.chan = 5,
.irq = STM32_IRQ_DMA1CH6,
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(5),
},
{
.chan = STM32_DMA1_CHAN7,
.chan = 6,
.irq = STM32_IRQ_DMA1CH7,
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(6),
},
#if STM32_NDMA > 1
{
.chan = STM32_DMA2_CHAN1,
.chan = 0,
.irq = STM32_IRQ_DMA2CH1,
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(0),
},
{
.chan = STM32_DMA2_CHAN2,
.chan = 1,
.irq = STM32_IRQ_DMA2CH2,
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(1),
},
{
.chan = STM32_DMA2_CHAN3,
.chan = 2,
.irq = STM32_IRQ_DMA2CH3,
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(2),
},
{
.chan = STM32_DMA2_CHAN4,
.chan = 3,
#ifdef CONFIG_STM32_CONNECTIVITY_LINE
.irq = STM32_IRQ_DMA2CH4,
#else
@ -153,7 +160,7 @@ static struct stm32_dma_s g_dma[DMA_NCHANNELS] =
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(3),
},
{
.chan = STM32_DMA2_CHAN5,
.chan = 4,
#ifdef CONFIG_STM32_CONNECTIVITY_LINE
.irq = STM32_IRQ_DMA2CH5,
#else
@ -227,6 +234,33 @@ static inline void stm32_dmagive(FAR struct stm32_dma_s *dmach)
(void)sem_post(&dmach->sem);
}
/************************************************************************************
* Name: stm32_dmachandisable
*
* Description:
* Disable the DMA channel
*
************************************************************************************/
static void stm32_dmachandisable(struct stm32_dma_s *dmach)
{
uint32 regval;
/* Disable all interrupts at the DMA controller */
regval = dmachan_getreg(dmach, STM32_DMACHAN_CCR_OFFSET);
regval &= ~DMA_CCR_ALLINTS;
/* Disable the DMA channel */
regval &= ~DMA_CCR_EN;
dmachan_putreg(dmach, STM32_DMACHAN_CCR_OFFSET, regval);
/* Clear pending channel interrupts */
dmabase_putreg(dmach, STM32_DMA_IFCR_OFFSET, DMA_ISR_CHAN_MASK(dmach->chan));
}
/************************************************************************************
* Name: stm32_dmainterrupt
*
@ -239,13 +273,13 @@ static int stm32_dmainterrupt(int irq, void *context)
{
struct stm32_dma_s *dmach;
uint32 isr;
int chan;
int chndx;
/* Get the channel structure from the interrupt number */
if (irq >= STM32_IRQ_DMA1CH1 && irq <= STM32_IRQ_DMA1CH7)
{
chan = irq - STM32_IRQ_DMA1CH1;
chndx = irq - STM32_IRQ_DMA1CH1;
}
else
#if STM32_NDMA > 1
@ -255,28 +289,28 @@ static int stm32_dmainterrupt(int irq, void *context)
if (irq >= STM32_IRQ_DMA2CH1 && irq <= STM32_IRQ_DMA2CH45)
#endif
{
chan = irq - STM32_IRQ_DMA2CH1 + DMA1_NCHANNELS;
chndx = irq - STM32_IRQ_DMA2CH1 + DMA1_NCHANNELS;
}
else
#endif
{
PANIC(OSERR_INTERNAL);
}
dmach = &g_dma[chan];
dmach = &g_dma[chndx];
/* Get the interrupt status (for this channel only) */
isr = dmabase_getreg(dmach, STM32_DMA_ISR_OFFSET) & ~DMA_ISR_CHAN_MASK(chan);
isr = dmabase_getreg(dmach, STM32_DMA_ISR_OFFSET) & DMA_ISR_CHAN_MASK(dmach->chan);
/* Clear pending interrupts (for this channel only) */
/* Disable the DMA channel */
dmabase_putreg(dmach, STM32_DMA_IFCR_OFFSET, isr);
stm32_dmachandisable(dmach);
/* Invoke the callback */
if (dmach->callback)
{
dmach->callback(dmach, isr >> DMA_ISR_CHAN_SHIFT(chan), dmach->arg);
dmach->callback(dmach, isr >> DMA_ISR_CHAN_SHIFT(dmach->chan), dmach->arg);
}
return OK;
}
@ -296,16 +330,33 @@ static int stm32_dmainterrupt(int irq, void *context)
*
****************************************************************************/
void weak_function stm32_dmainitialize(void)
void weak_function up_dmainitialize(void)
{
int chan;
struct stm32_dma_s *dmach;
int chndx;
/* Initialize each DMA channel */
for (chndx = 0; chndx < DMA_NCHANNELS; chndx++)
{
dmach = &g_dma[chndx];
sem_init(&dmach->sem, 0, 1);
/* Attach DMA interrupt vectors */
for (chan = 0; chan < DMA_NCHANNELS; chan++)
{
sem_init(&g_dma[chan].sem, 0, 1);
irq_attach(g_dma[chan].irq, stm32_dmainterrupt);
(void)irq_attach(dmach->irq, stm32_dmainterrupt);
/* Disable the DMA channel */
stm32_dmachandisable(dmach);
/* Enable the IRQ at the NVIC (still disabled at the DMA controller) */
up_enable_irq(dmach->irq);
/* Set the interrrupt priority */
up_prioritize_irq(dmach->irq, CONFIG_DMA_PRI);
}
}
@ -314,7 +365,7 @@ void weak_function stm32_dmainitialize(void)
*
* Description:
* Allocate a DMA channel. This function gives the caller mutually
* exclusive access to the DMA channel specified by the 'chan' argument.
* exclusive access to the DMA channel specified by the 'chndx' argument.
* DMA channels are shared on the STM32: Devices sharing the same DMA
* channel cannot do DMA concurrently! See the DMACHAN_* definitions in
* stm32_dma.h.
@ -330,8 +381,8 @@ void weak_function stm32_dmainitialize(void)
* version. Feel free to do that if that is what you need.
*
* Returned Value:
* Provided that 'chan' is valid, this function ALWAYS returns a non-NULL,
* void* DMA channel handle. (If 'chan' is invalid, the function will
* Provided that 'chndx' is valid, this function ALWAYS returns a non-NULL,
* void* DMA channel handle. (If 'chndx' is invalid, the function will
* assert if debug is enabled or do something ignorant otherwise).
*
* Assumptions:
@ -341,11 +392,11 @@ void weak_function stm32_dmainitialize(void)
*
****************************************************************************/
DMA_HANDLE stm32_dmachannel(int chan)
DMA_HANDLE stm32_dmachannel(int chndx)
{
struct stm32_dma_s *dmach = &g_dma[chan];
struct stm32_dma_s *dmach = &g_dma[chndx];
DEBUGASSERT(chan < DMA_NCHANNELS);
DEBUGASSERT(chndx < DMA_NCHANNELS);
/* Get exclusive access to the DMA channel -- OR wait until the channel
* is available if it is currently being used by another driver
@ -478,3 +529,44 @@ void stm32_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg, boole
ccr |= (half ? (DMA_CCR_HTIE|DMA_CCR_TEIE) : (DMA_CCR_TCIE|DMA_CCR_TEIE));
dmachan_putreg(dmach, STM32_DMACHAN_CCR_OFFSET, ccr);
}
/****************************************************************************
* Name: stm32_dmadump
*
* Description:
* Dump DMA register contents
*
* Assumptions:
* - DMA handle allocated by stm32_dmachannel()
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA
void stm32_dmadump(DMA_HANDLE handle, const char *msg)
{
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
uint32 dmabase = DMA_BASE(dmach->base);
irqstate_t flags;
uint32 addr;
dmalldbg("%s: base: %08x Channel base: %08x \n", msg, dmabase, dmach->base);
flags = irqsave();
addr = dmabase + STM32_DMA_ISR_OFFSET;
dmalldbg(" ISRC[%08x]: %08x\n", addr, getreg32(addr));
addr = dmach->base + STM32_DMACHAN_CCR_OFFSET;
dmalldbg(" CCR[%08x]: %08x\n", addr, getreg32(addr));
addr = dmach->base + STM32_DMACHAN_CNDTR_OFFSET;
dmalldbg(" CNDTR[%08x]: %08x\n", addr, getreg32(addr));
addr = dmach->base + STM32_DMACHAN_CPAR_OFFSET;
dmalldbg(" CPAR[%08x]: %08x\n", addr, getreg32(addr));
addr = dmach->base + STM32_DMACHAN_CMAR_OFFSET;
dmalldbg(" CMAR[%08x]: %08x\n", addr, getreg32(addr));
irqrestore(flags);
}
#endif

View File

@ -229,6 +229,7 @@
#define DMA_IFCR_CHAN6_MASK (DMA_CHAN_MASK << DMA_IFCR_CHAN6_SHIFT)
#define DMA_IFCR_CHAN7_SHIFT (24) /* Bits 27-24: DMA Channel 7 interrupt flag clear */
#define DMA_IFCR_CHAN7_MASK (DMA_CHAN_MASK << DMA_IFCR_CHAN7_SHIFT)
#define DMA_IFCR_ALLCHANNELS (0x0fffffff)
#define DMA_IFCR_CGIF(n) (DMA_CHAN_GIF_BIT << DMA_IFCR_CHAN_SHIFT(n))
#define DMA_IFCR_CTCIF(n) (DMA_CHAN_TCIF_BIT << DMA_IFCR_CHAN_SHIFT(n))
@ -263,6 +264,8 @@
#define DMA_CCR_TCIE (1 << 1) /* Bit 1: Transfer complete interrupt enable */
#define DMA_CCR_EN (1 << 0) /* Bit 0: Channel enable */
#define DMA_CCR_ALLINTS (DMA_CCR_TEIE|DMA_CCR_HTIE|DMA_CCR_TCIE)
/* DMA channel number of data register */
#define DMA_CNDTR_NDT_SHIFT (0) /* Bits 15-0: Number of data to Transfer */

View File

@ -50,6 +50,12 @@
* Definitions
************************************************************************************/
/* Configuration ********************************************************************/
#if !defined(CONFIG_DEBUG) || !defined(CONFIG_DEBUG_VERBOSE)
# undef CONFIG_DEBUG_DMA
#endif
/* NVIC priority levels *************************************************************/
#define NVIC_SYSH_PRIORITY_MIN 0xff /* All bits set in minimum priority */
@ -265,19 +271,6 @@ EXTERN int stm32_dumpgpio(uint32 pinset, const char *msg);
# define stm32_dumpgpio(p,m)
#endif
/****************************************************************************
* Name: stm32_dmainitialize
*
* Description:
* Initialize the DMA subsystem
*
* Returned Value:
* None
*
****************************************************************************/
EXTERN void weak_function stm32_dmainitialize(void);
/****************************************************************************
* Name: stm32_dmachannel
*
@ -359,6 +352,23 @@ EXTERN void stm32_dmasetup(DMA_HANDLE handle, uint32 paddr, uint32 maddr,
EXTERN void stm32_dmastart(DMA_HANDLE handle, dma_callback_t callback,
void *arg, boolean half);
/****************************************************************************
* Name: stm32_dmadump
*
* Description:
* Dump DMA register contents
*
* Assumptions:
* - DMA handle allocated by stm32_dmachannel()
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA
EXTERN void stm32_dmadump(DMA_HANDLE handle, const char *msg);
#else
# define stm32_dmadump(handle)
#endif
/************************************************************************************
* Function: stm32_ethinitialize
*

View File

@ -118,10 +118,10 @@
/* DMA CCR register settings */
#define SDIO_RXDMA16_CONFIG (CONFIG_SDIO_DMAPRIO|DMA_CCR_MSIZE_16BITS|\
DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC)
#define SDIO_TXDMA16_CONFIG (CONFIG_SDIO_DMAPRIO|DMA_CCR_MSIZE_16BITS|\
DMA_CCR_PSIZE_16BITS|DMA_CCR_MINC|DMA_CCR_DIR)
#define SDIO_RXDMA32_CONFIG (CONFIG_SDIO_DMAPRIO|DMA_CCR_MSIZE_32BITS|\
DMA_CCR_PSIZE_32BITS|DMA_CCR_MINC)
#define SDIO_TXDMA32_CONFIG (CONFIG_SDIO_DMAPRIO|DMA_CCR_MSIZE_32BITS|\
DMA_CCR_PSIZE_32BITS|DMA_CCR_MINC|DMA_CCR_DIR)
/* FIFO sizes */
@ -528,6 +528,8 @@ static void stm32_dmacallback(DMA_HANDLE handle, ubyte isr, void *arg)
/* We don't really do anything at the completion of DMA. The termination
* of the transfer is driven by the SDIO interrupts.
*/
stm32_dmadump(handle, "DMA Callback");
}
#endif
@ -837,6 +839,15 @@ static void stm32_endtransfer(struct stm32_dev_s *priv, sdio_eventset_t wkupeven
priv->remaining = 0;
/* DMA debug instrumentation */
#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_DEBUG_DMA)
if (priv->dmamode)
{
stm32_dmadump(priv->dma, "End of Transfer");
}
#endif
/* Is a data transfer complete event expected? */
if ((priv->waitevents & wkupevent) != 0)
@ -1978,6 +1989,8 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR ubyte *buffer,
if (priv->widebus)
{
stm32_dmadump(priv->dma, "Before RECV Setup");
/* Save the destination buffer information for use by the interrupt handler */
priv->buffer = (uint32*)buffer;
@ -1995,11 +2008,12 @@ static int stm32_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR ubyte *buffer,
putreg32(1, SDIO_DCTRL_DMAEN_BB);
stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32)buffer,
(buflen + 3) >> 2, SDIO_RXDMA16_CONFIG);
(buflen + 3) >> 2, SDIO_RXDMA32_CONFIG);
/* Start the DMA */
stm32_dmastart(priv->dma, stm32_dmacallback, priv, FALSE);
stm32_dmadump(priv->dma, "After RECV Setup");
ret = OK;
}
return ret;
@ -2044,6 +2058,8 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
if (priv->widebus)
{
stm32_dmadump(priv->dma, "Before SEND Setup");
/* Save the source buffer information for use by the interrupt handler */
priv->buffer = (uint32*)buffer;
@ -2062,12 +2078,13 @@ static int stm32_dmasendsetup(FAR struct sdio_dev_s *dev,
/* Configure the TX DMA */
stm32_dmasetup(priv->dma, STM32_SDIO_FIFO, (uint32)buffer,
(buflen + 3) >> 2, SDIO_TXDMA16_CONFIG);
(buflen + 3) >> 2, SDIO_TXDMA32_CONFIG);
putreg32(1, SDIO_DCTRL_DMAEN_BB);
/* Start the DMA */
stm32_dmastart(priv->dma, stm32_dmacallback, priv, FALSE);
stm32_dmadump(priv->dma, "After SEND Setup");
ret = OK;
}
return ret;