summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/lpc17xx/lpc17_gpdma.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/arch/arm/src/lpc17xx/lpc17_gpdma.c')
-rw-r--r--nuttx/arch/arm/src/lpc17xx/lpc17_gpdma.c336
1 files changed, 302 insertions, 34 deletions
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_gpdma.c b/nuttx/arch/arm/src/lpc17xx/lpc17_gpdma.c
index 696309b5f..4edc87dbf 100644
--- a/nuttx/arch/arm/src/lpc17xx/lpc17_gpdma.c
+++ b/nuttx/arch/arm/src/lpc17xx/lpc17_gpdma.c
@@ -63,24 +63,6 @@
* Definitions
****************************************************************************/
-/* Enables debug output from this file (needs CONFIG_DEBUG too) */
-
-#undef DMA_DEBUG /* Define to enable debug */
-#undef DMA_VERBOSE /* Define to enable verbose debug */
-
-#ifdef DMA_DEBUG
-# define dmadbg lldbg
-# ifdef DMA_VERBOSE
-# define spivdbg lldbg
-# else
-# define spivdbg(x...)
-# endif
-#else
-# undef DMA_VERBOSE
-# define dmadbg(x...)
-# define spivdbg(x...)
-#endif
-
/****************************************************************************
* Private Types
****************************************************************************/
@@ -89,6 +71,7 @@
struct lpc17_dmach_s
{
bool inuse; /* True: The channel is in use */
+ uint8_t chn; /* Channel number */
dma_callback_t callback; /* DMA completion callback function */
void *arg; /* Argument to pass to the callback function */
};
@@ -124,23 +107,109 @@ static struct lpc17_gpdma_s g_gpdma;
****************************************************************************/
/****************************************************************************
+ * Name: gpdma_interrupt
+ *
+ * Description:
+ * The common GPDMA interrupt handler.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static int gpdma_interrupt(int irq, FAR void *context)
+{
+ struct lpc17_dmach_s *dmach;
+ uint32_t regval;
+ uint32_t chbit;
+ int result;
+ int i;
+
+ /* Check each DMA channel */
+
+ for (i = 0; i < LPC17_NDMACH; i++)
+ {
+ chbit = DMACH((uint32_t)i);
+
+ /* Is there an interrupt pending for this channel? If the bit for
+ * this channel is set, that indicates that a specific DMA channel
+ * interrupt request is active. The request can be generated from
+ * either the error or terminal count interrupt requests.
+ */
+
+ regval = getreg32(LPC17_DMA_INTST);
+ if ((regval & chbit) != 0)
+ {
+ /* Yes.. Is this channel assigned? Is there a callback function? */
+
+ dmach = &g_gpdma.dmach[i];
+ if (dmach->inuse && dmach->callback)
+ {
+ /* Yes.. did an error occur? */
+
+ regval = getreg32(LPC17_DMA_INTERRST);
+ if ((regval & chbit) != 0)
+ {
+ /* Yes.. report error status */
+
+ result = -EIO;
+ }
+
+ /* Then this must be a terminal transfer event */
+
+ else
+ {
+ /* Let's make sure it is the terminal transfer event. */
+
+ regval = getreg32(LPC17_DMA_INTTCST);
+ if ((regval & chbit) != 0)
+ {
+ result = OK;
+ }
+
+ /* This should not happen */
+
+ else
+ {
+ result = -EINVAL;
+ }
+ }
+
+ /* Perform the callback */
+
+ dmach->callback((DMA_HANDLE)dmach, dmach->arg, result);
+ }
+
+ /* Disable this channel, mask any further interrupts for
+ * this channel, and clear any pending interrupts.
+ */
+
+ lpc17_dmastop((DMA_HANDLE)dmach);
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
- * Name: lpc17_dmainitialize
+ * Name: up_dmainitialize
*
* Description:
* Initialize the GPDMA subsystem.
*
* Returned Value:
- * None
+ * Zero on success; A negated errno value on failure.
*
****************************************************************************/
-void lpc17_dmainitilaize(void)
+void weak_function up_dmainitialize(void)
{
uint32_t regval;
+ int ret;
int i;
/* Enable clocking to the GPDMA block */
@@ -164,6 +233,20 @@ void lpc17_dmainitilaize(void)
/* Initialize the DMA state structure */
sem_init(&g_gpdma.exclsem, 0, 1);
+
+ for (i = 0; i < LPC17_NDMACH; i++)
+ {
+ g_gpdma.dmach[i].chn = i; /* Channel number */
+ g_gpdma.dmach[i].inuse = false; /* Channel is not in-use */
+ }
+
+ /* Attach and enable the common interrupt handler */
+
+ ret = irq_attach(LPC17_IRQ_GPDMA, gpdma_interrupt);
+ if (ret == OK)
+ {
+ up_enable_irq(LPC17_IRQ_GPDMA);
+ }
}
/****************************************************************************
@@ -279,6 +362,10 @@ void lpc17_dmafree(DMA_HANDLE handle)
DEBUGASSERT(dmach && dmach->inuse);
+ /* Make sure that the DMA channel was properly stopped */
+
+ lpc17_dmastop(handle);
+
/* Mark the channel available. This is an atomic operation and needs no
* special protection.
*/
@@ -298,11 +385,81 @@ int lpc17_dmasetup(DMA_HANDLE handle, uint32_t control, uint32_t config,
uint32_t srcaddr, uint32_t destaddr, size_t nxfrs)
{
struct lpc17_dmach_s *dmach = (DMA_HANDLE)handle;
+ uint32_t chbit;
+ uint32_t regval;
+ uint32_t base;
DEBUGASSERT(dmach && dmach->inuse);
-#warning "Missing logic"
- return -ENOSYS;
+ chbit = DMACH((uint32_t)dmach->chn);
+ base = LPC17_DMACH_BASE((uint32_t)dmach->chn);
+
+ /* Put the channel in a known state. Zero disables everything */
+
+ putreg32(0, base + LPC17_DMACH_CONTROL_OFFSET);
+ putreg32(0, base + LPC17_DMACH_CONFIG_OFFSET);
+
+ /* "Programming a DMA channel
+ *
+ * 1. "Choose a free DMA channel with the priority needed. DMA channel 0
+ * has the highest priority and DMA channel 7 the lowest priority.
+ */
+
+ regval = getreg32(LPC17_DMA_ENBLDCHNS);
+ if ((regval & chbit) != 0)
+ {
+ /* There is an active DMA on this channel! */
+
+ return -EBUSY;
+ }
+
+ /* 2. "Clear any pending interrupts on the channel to be used by writing
+ * to the DMACIntTCClear and DMACIntErrClear register. The previous
+ * channel operation might have left interrupt active.
+ */
+
+ putreg32(chbit, LPC17_DMA_INTTCCLR);
+ putreg32(chbit, LPC17_DMA_INTERRCLR);
+
+ /* 3. "Write the source address into the DMACCxSrcAddr register. */
+
+ putreg32(srcaddr, base + LPC17_DMACH_SRCADDR_OFFSET);
+
+ /* 4. "Write the destination address into the DMACCxDestAddr register. */
+
+ putreg32(destaddr, base + LPC17_DMACH_DESTADDR_OFFSET);
+
+ /* 5. "Write the address of the next LLI into the DMACCxLLI register. If
+ * the transfer comprises of a single packet of data then 0 must be
+ * written into this register.
+ */
+
+ putreg32(0, base + LPC17_DMACH_LLI_OFFSET);
+
+ /* 6. "Write the control information into the DMACCxControl register."
+ *
+ * The caller provides all CONTROL register fields except for the transfer
+ * size which is passed as a separate parameter and for the terminal count
+ * interrupt enable bit which is controlled by the driver.
+ */
+
+ regval = control & ~(DMACH_CONTROL_XFRSIZE_MASK|DMACH_CONTROL_I);
+ regval |= ((uint32_t)nxfrs << DMACH_CONTROL_XFRSIZE_SHIFT);
+ putreg32(regval, base + LPC17_DMACH_CONTROL_OFFSET);
+
+ /* 7. "Write the channel configuration information into the DMACCxConfig
+ * register. If the enable bit is set then the DMA channel is
+ * automatically enabled."
+ *
+ * Only the SRCPER, DSTPER, and XFRTTYPE fields of the CONFIG register
+ * are provided by the caller. Little endian is assumed.
+ */
+
+ regval = config & (DMACH_CONFIG_SRCPER_MASK|DMACH_CONFIG_DSTPER_MASK|
+ DMACH_CONFIG_XFRTYPE_MASK);
+ putreg32(regval, base + LPC17_DMACH_CONFIG_OFFSET);
+
+ return OK;
}
/****************************************************************************
@@ -316,6 +473,9 @@ int lpc17_dmasetup(DMA_HANDLE handle, uint32_t control, uint32_t config,
int lpc17_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
{
struct lpc17_dmach_s *dmach = (DMA_HANDLE)handle;
+ uint32_t regval;
+ uint32_t chbit;
+ uint32_t base;
DEBUGASSERT(dmach && dmach->inuse && callback);
@@ -324,9 +484,29 @@ int lpc17_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
dmach->callback = callback;
dmach->arg = arg;
- /* Start the DMA */
-#warning "Missing logic"
- return -ENOSYS;
+ /* Clear any pending DMA interrupts */
+
+ chbit = DMACH((uint32_t)dmach->chn);
+ putreg32(chbit, LPC17_DMA_INTTCCLR);
+ putreg32(chbit, LPC17_DMA_INTERRCLR);
+
+ /* Enable terminal count interrupt */
+
+ base = LPC17_DMACH_BASE((uint32_t)dmach->chn);
+ regval = getreg32(base + LPC17_DMACH_CONTROL_OFFSET);
+ regval |= DMACH_CONTROL_I;
+ putreg32(regval, base + LPC17_DMACH_CONTROL_OFFSET);
+
+ /* Enable the channel and unmask terminal count and error interrupts.
+ * According to the user manual, zero masks and one unmasks (hence,
+ * these are really enables).
+ */
+
+ regval = getreg32(base + LPC17_DMACH_CONFIG_OFFSET);
+ regval |= (DMACH_CONFIG_E | DMACH_CONFIG_IE | DMACH_CONFIG_ITC);
+ putreg32(regval, base + LPC17_DMACH_CONFIG_OFFSET);
+
+ return OK;
}
/****************************************************************************
@@ -342,9 +522,27 @@ int lpc17_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
void lpc17_dmastop(DMA_HANDLE handle)
{
struct lpc17_dmach_s *dmach = (DMA_HANDLE)handle;
+ uint32_t regaddr;
+ uint32_t regval;
+ uint32_t chbit;
DEBUGASSERT(dmach && dmach->inuse);
-#warning "Missing logic"
+
+ /* Disable this channel and mask any further interrupts from the channel.
+ * this channel. The channel is disabled by clearning the channel
+ * enable bit. Any outstanding data in the FIFO’s is lost.
+ */
+
+ regaddr = LPC17_DMACH_CONFIG((uint32_t)dmach->chn);
+ regval = getreg32(regaddr);
+ regval &= ~(DMACH_CONFIG_E | DMACH_CONFIG_IE | DMACH_CONFIG_ITC);
+ putreg32(regval, regaddr);
+
+ /* Clear any pending interrupts for this channel */
+
+ chbit = DMACH((uint32_t)dmach->chn);
+ putreg32(chbit, LPC17_DMA_INTTCCLR);
+ putreg32(chbit, LPC17_DMA_INTERRCLR);
}
/****************************************************************************
@@ -359,9 +557,33 @@ void lpc17_dmastop(DMA_HANDLE handle)
void lpc17_dmasample(DMA_HANDLE handle, struct lpc17_dmaregs_s *regs)
{
struct lpc17_dmach_s *dmach = (DMA_HANDLE)handle;
-
- DEBUGASSERT(dmach && dmach->inuse);
-#warning "Missing logic"
+ uint32_t base;
+
+ DEBUGASSERT(dmach);
+
+ /* Sample the global DMA registers */
+
+ regs->gbl.intst = getreg32(LPC17_DMA_INTST);
+ regs->gbl.inttcst = getreg32(LPC17_DMA_INTTCST);
+ regs->gbl.interrst = getreg32(LPC17_DMA_INTERRST);
+ regs->gbl.rawinttcst = getreg32(LPC17_DMA_RAWINTTCST);
+ regs->gbl.rawinterrst = getreg32(LPC17_DMA_RAWINTERRST);
+ regs->gbl.enbldchns = getreg32(LPC17_DMA_ENBLDCHNS);
+ regs->gbl.softbreq = getreg32(LPC17_DMA_SOFTBREQ);
+ regs->gbl.softsreq = getreg32(LPC17_DMA_SOFTSREQ);
+ regs->gbl.softlbreq = getreg32(LPC17_DMA_SOFTLBREQ);
+ regs->gbl.softlsreq = getreg32(LPC17_DMA_SOFTLSREQ);
+ regs->gbl.config = getreg32(LPC17_DMA_CONFIG);
+ regs->gbl.sync = getreg32(LPC17_DMA_SYNC);
+
+ /* Sample the DMA channel registers */
+
+ base = LPC17_DMACH_BASE((uint32_t)dmach->chn);
+ regs->ch.srcaddr = getreg32(base + LPC17_DMACH_SRCADDR_OFFSET);
+ regs->ch.destaddr = getreg32(base + LPC17_DMACH_DESTADDR_OFFSET);
+ regs->ch.lli = getreg32(base + LPC17_DMACH_LLI_OFFSET);
+ regs->ch.control = getreg32(base + LPC17_DMACH_CONTROL_OFFSET);
+ regs->ch.config = getreg32(base + LPC17_DMACH_CONFIG_OFFSET);
}
#endif /* CONFIG_DEBUG_DMA */
@@ -374,12 +596,58 @@ void lpc17_dmasample(DMA_HANDLE handle, struct lpc17_dmaregs_s *regs)
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA
-void lpc17_dmadump(DMA_HANDLE handle, const struct lpc17_dmaregs_s *regs, const char *msg)
+void lpc17_dmadump(DMA_HANDLE handle, const struct lpc17_dmaregs_s *regs,
+ const char *msg)
{
struct lpc17_dmach_s *dmach = (DMA_HANDLE)handle;
-
- DEBUGASSERT(dmach && dmach->inuse);
-#warning "Missing logic"
+ uint32_t base;
+
+ DEBUGASSERT(dmach);
+
+ /* Dump the sampled global DMA registers */
+
+ dmadbg("Global GPDMA Registers: %s\n", msg);
+ dmadbg(" INTST[%08x]: %08x\n",
+ LPC17_DMA_INTST, regs->gbl.intst);
+ dmadbg(" INTTCST[%08x]: %08x\n",
+ LPC17_DMA_INTTCST, regs->gbl.inttcst);
+ dmadbg(" INTERRST[%08x]: %08x\n",
+ LPC17_DMA_INTERRST, regs->gbl.interrst);
+ dmadbg(" RAWINTTCST[%08x]: %08x\n",
+ LPC17_DMA_RAWINTTCST, regs->gbl.rawinttcst);
+ dmadbg(" RAWINTERRST[%08x]: %08x\n",
+ LPC17_DMA_RAWINTERRST, regs->gbl.rawinterrst);
+ dmadbg(" ENBLDCHNS[%08x]: %08x\n",
+ LPC17_DMA_ENBLDCHNS, regs->gbl.enbldchns);
+ dmadbg(" SOFTBREQ[%08x]: %08x\n",
+ LPC17_DMA_SOFTBREQ, regs->gbl.softbreq);
+ dmadbg(" SOFTSREQ[%08x]: %08x\n",
+ LPC17_DMA_SOFTSREQ, regs->gbl.softsreq);
+ dmadbg(" SOFTLBREQ[%08x]: %08x\n",
+ LPC17_DMA_SOFTLBREQ, regs->gbl.softlbreq);
+ dmadbg(" SOFTLSREQ[%08x]: %08x\n",
+ LPC17_DMA_SOFTLSREQ, regs->gbl.softlsreq);
+ dmadbg(" CONFIG[%08x]: %08x\n",
+ LPC17_DMA_CONFIG, regs->gbl.config);
+ dmadbg(" SYNC[%08x]: %08x\n",
+ LPC17_DMA_SYNC, regs->gbl.sync);
+
+ /* Dump the DMA channel registers */
+
+ base = LPC17_DMACH_BASE((uint32_t)dmach->chn);
+
+ dmadbg("Channel GPDMA Registers: %d\n", dmach->chn);
+
+ dmadbg(" SRCADDR[%08x]: %08x\n",
+ base + LPC17_DMACH_SRCADDR_OFFSET, regs->ch.srcaddr);
+ dmadbg(" DESTADDR[%08x]: %08x\n",
+ base + LPC17_DMACH_DESTADDR_OFFSET, regs->ch.destaddr);
+ dmadbg(" LLI[%08x]: %08x\n",
+ base + LPC17_DMACH_LLI_OFFSET, regs->ch.lli);
+ dmadbg(" CONTROL[%08x]: %08x\n",
+ base + LPC17_DMACH_CONTROL_OFFSET, regs->ch.control);
+ dmadbg(" CONFIG[%08x]: %08x\n",
+ base + LPC17_DMACH_CONFIG_OFFSET, regs->ch.config);
}
#endif /* CONFIG_DEBUG_DMA */