nuttx/arch/arm/src/lpc54xx/lpc54_dma.c
Gregory Nutt 2aa85fd17e arch/arm, board/arm: Rename all up_* functions to arm_*
Summary

The naming standard at https://cwiki.apache.org/confluence/display/NUTTX/Naming+FAQ requires that all MCU-private functions begin with the name of the architecture, not up_.

This PR addresses only these name changes for the ARM-private functions prototyped in arm_internal.h

This change to the files only modifies the name of called functions.  nxstyle fixes were made for all core architecture files.  However, there are well over 5000 additional complaints from MCU drivers and board logic that are unrelated to to this change but were affected by the name change.  It is not humanly possible to fix all of these.   I ask that this change be treated like other cosmetic changes that we have done which do not require full nxstyle compliance.

Impact

There should be not impact of this change (other that one step toward more consistent naming).
Testing

stm32f4discovery:netnsh
2020-05-01 18:28:13 +01:00

637 lines
18 KiB
C

/****************************************************************************
* arch/arm/src/lpc54xx/lpc54_dma.c
*
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include "arm_internal.h"
#include "arm_arch.h"
#include "hardware/lpc54_inputmux.h"
#include "hardware/lpc54_dma.h"
#include "lpc54_enableclk.h"
#include "lpc54_reset.h"
#include "lpc54_dma.h"
#ifdef CONFIG_LPC54_DMA
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure represents the state of one DMA channel */
struct lpc54_dmach_s
{
bool inuse; /* True: The channel is in use */
dma_callback_t callback; /* DMA completion callback function */
void *arg; /* Argument to pass to the callback function */
};
/* This structure represents the state of the LPC54 DMA block */
struct lpc54_dma_s
{
sem_t exclsem; /* For exclusive access to the DMA channel list */
/* This is the state of each DMA channel */
struct lpc54_dmach_s dmach[LPC54_DMA_NCHANNELS];
};
/****************************************************************************
* Private Data
****************************************************************************/
/* The state of the LPC54 DMA block */
static struct lpc54_dma_s g_dma;
/* The SRAMBASE register must be configured with an address (preferably in
* on-chip SRAM) where DMA descriptors will be stored. Each DMA channel has
* an entry for the channel descriptor in the SRAM table.
*/
static struct lpc54_dmachan_desc_s g_dma_desc[LPC54_DMA_NCHANNELS];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: lpc54_dma_dispatch
*
* Description:
* Dispatch a DMA interrupt.
*
* Returned Value:
* None
*
****************************************************************************/
static void lpc54_dma_dispatch(int ch, int result)
{
struct lpc54_dmach_s *dmach;
/* Yes.. Is this channel assigned? Is there a callback function? */
dmach = &g_dma.dmach[ch];
if (dmach->inuse && dmach->callback != NULL)
{
/* Perform the callback */
dmach->callback(ch, dmach->arg, result);
}
/* Disable this channel, mask any further interrupts for this channel, and
* clear any pending interrupts.
*/
lpc54_dmastop(ch);
}
/****************************************************************************
* Name: lpc54_dma_interrupt
*
* Description:
* The common DMA interrupt handler.
*
* Returned Value:
* None
*
****************************************************************************/
static int lpc54_dma_interrupt(int irq, FAR void *context, FAR void *arg)
{
uint32_t pending;
uint32_t bitmask;
int ch;
/* Check for pending DMA channel error interrupts */
pending = getreg32(LPC54_DMA_ERRINT0);
putreg32(pending, LPC54_DMA_ERRINT0);
for (ch = 0; pending != 0 && ch < LPC54_DMA_NCHANNELS; ch++)
{
/* Check if there is a pending error on this channel */
bitmask = DMA_CHANNEL((uint32_t)ch);
if ((pending & bitmask) != 0)
{
/* Dispatch the DMA channel error event */
lpc54_dma_dispatch(ch, -EIO);
pending &= ~bitmask;
}
}
/* Check for pending DMA interrupt A events */
pending = getreg32(LPC54_DMA_INTA0);
putreg32(pending, LPC54_DMA_INTA0);
for (ch = 0; pending != 0 && ch < LPC54_DMA_NCHANNELS; ch++)
{
/* Check if there is a pending interrupt A on this channel */
bitmask = DMA_CHANNEL((uint32_t)ch);
if ((pending & bitmask) != 0)
{
/* Dispatch DMA channel interrupt A event */
lpc54_dma_dispatch(ch, OK);
pending &= ~bitmask;
}
}
#if 0 /* interrupt B is not used */
/* Check for pending DMA interrupt B events */
pending = getreg32(LPC54_DMA_INTB0);
putreg32(pending, LPC54_DMA_INTB0);
for (ch = 0; pending != 0 && ch < LPC54_DMA_NCHANNELS; ch++)
{
/* Check if there is a pending interrupt A on this channel */
bitmask = DMA_CHANNEL((uint32_t)ch);
if ((pending & bitmask) != 0)
{
/* Dispatch DMA channel interrupt B event */
lpc54_dma_dispatch(ch, OK);
pending &= ~bitmask;
}
}
#endif
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: arm_dma_initialize
*
* Description:
* Initialize the DMA subsystem. Called from up_initialize() early in the
* boot-up sequence. Prototyped in arm_internal.h.
*
* Returned Value:
* None
*
****************************************************************************/
void weak_function arm_dma_initialize(void)
{
int ret;
/* Enable clocking to the DMA block */
lpc54_dma_enableclk();
/* Reset the DMA peripheral */
lpc54_reset_dma();
/* Disable and clear all DMA interrupts */
putreg32(DMA_ALL_CHANNELS, LPC54_DMA_INTENCLR0);
putreg32(DMA_ALL_CHANNELS, LPC54_DMA_ERRINT0);
putreg32(DMA_ALL_CHANNELS, LPC54_DMA_INTA0);
putreg32(DMA_ALL_CHANNELS, LPC54_DMA_INTB0);
/* Initialize the DMA state structure */
nxsem_init(&g_dma.exclsem, 0, 1);
/* Set the SRAMBASE to the beginning a array of DMA descriptors, one for
* each DMA channel.
*/
putreg32((uint32_t)g_dma_desc, LPC54_DMA_SRAMBASE);
/* Attach and enable the DMA interrupt handler */
ret = irq_attach(LPC54_IRQ_DMA, lpc54_dma_interrupt, NULL);
if (ret == OK)
{
up_enable_irq(LPC54_IRQ_DMA);
}
/* Enable the DMA controller */
putreg32(DMA_CTRL_ENABLE, LPC54_DMA_CTRL);
}
/****************************************************************************
* Name: lpc54_dma_setup
*
* Description:
* Configure DMA for one transfer.
*
* Input Parameters:
* ch - DMA channel number
* cfg - The content of the DMA channel configuration register. See
* peripheral channel definitions in chip/lpc54_dma.h. The
* caller must provide all fields: PERIPHREQEN, TRIGPOL,
* TRIGTYPE, TRIGBURST, BURSTPOWER, SRCBURSTWRAP, DSTBURSTWRAP,
* and CHPRIORITY.
* xfrcfg - The content of the DMA channel configuration register. See
* peripheral channel definitions in chip/lpc54_dma.h. The
* caller must provide all fields: WIDTH, SRCINC, and DSTINC.\
* All of fields are managed by the DMA driver
* trigsrc - See input mux DMA trigger ITRIG_INMUX_* definitions in
* chip/lpc54_inputmux.h.
* srcaddr - Source address of the DMA transfer
* dstaddr - Destination address of the DMA transfer
* nbytes - Number of bytes to transfer
*
****************************************************************************/
int lpc54_dma_setup(int ch, uint32_t cfg, uint32_t xfrcfg, uint8_t trigsrc,
uintptr_t srcaddr, uintptr_t dstaddr, size_t nbytes)
{
struct lpc54_dmach_s *dmach;
uintptr_t base;
uintptr_t regaddr;
uint32_t nxfrs;
uint32_t width;
uint32_t incr;
int ret;
DEBUGASSERT((unsigned)ch < LPC54_DMA_NCHANNELS && nbytes < 4096);
dmach = &g_dma.dmach[ch];
/* Get exclusive access to the DMA data structures and interface */
ret = nxsem_wait(&g_dma.exclsem);
if (ret < 0)
{
return ret;
}
/* Make sure that the DMA channel is not in use */
DEBUGASSERT(!dmach->inuse);
if (dmach->inuse)
{
ret = -EBUSY;
goto errout_with_exclsem;
}
dmach->inuse = true;
/* Make sure that the trigger is not active */
base = LPC54_DMA_CHAN_BASE(ch);
putreg32(0, base + LPC54_DMA_CFG_OFFSET);
/* Number of transfers */
switch (xfrcfg & DMA_XFERCFG_WIDTH_MASK)
{
default:
case DMA_XFERCFG_WIDTH_8BIT:
width = 1;
nxfrs = nbytes;
break;
case DMA_XFERCFG_WIDTH_16BIT:
width = 2;
nxfrs = ((nbytes + 1) >> 1);
break;
case DMA_XFERCFG_WIDTH_32BIT:
width = 4;
nxfrs = ((nbytes + 3) >> 2);
break;
}
/* Check if the number of transfers can be performed */
if (nxfrs > LPC54_DMA_MAXXFRS)
{
return -E2BIG;
}
/* Set up the channel DMA descriptor */
g_dma_desc[ch].reserved = 0;
switch (cfg & DMA_XFERCFG_SRCINC_MASK)
{
default:
case DMA_XFERCFG_SRCINC_NONE:
incr = 0;
break;
case DMA_XFERCFG_SRCINC_1X:
incr = width;
break;
case DMA_XFERCFG_SRCINC_2X:
incr = width << 1;
break;
case DMA_XFERCFG_SRCINC_4X:
incr = width << 2;
incr = 0;
break;
}
g_dma_desc[ch].srcend = (uint32_t)srcaddr + nxfrs * incr;
switch (cfg & DMA_XFERCFG_DSTINC_MASK)
{
default:
case DMA_XFERCFG_DSTINC_NONE:
incr = 0;
break;
case DMA_XFERCFG_DSTINC_1X:
incr = width;
break;
case DMA_XFERCFG_DSTINC_2X:
incr = width << 1;
break;
case DMA_XFERCFG_DSTINC_4X:
incr = width << 2;
incr = 0;
break;
}
g_dma_desc[ch].dstend = (uint32_t)dstaddr + nxfrs * incr;
g_dma_desc[ch].link = 0;
/* Set the trigger source */
regaddr = LPC54_MUX_DMA_ITRIG_INMUX(ch);
putreg32(MUX_DMA_ITRIG_INMUX(trigsrc), regaddr);
/* Set the channel configuration register.
*
* PERIPHREQEN - Provided by caller
* TRIGPOL - Provided by caller
* TRIGTYPE - Provided by caller
* TRIGBURST - Provided by caller
* BURSTPOWER - Provided by caller
* SRCBURSTWRAP - Provided by caller
* DSTBURSTWRAP - Provided by caller
* CHPRIORITY - Provided by caller
*/
putreg32(cfg, base + LPC54_DMA_CFG_OFFSET);
/* Set the channel transfer configuration register
*
* CFGVALID - Current channel descriptor is valid.
* RELOAD - No reload
* SWTRIG - No software trigger
* CLRTRIG - Trigger cleared when descriptor is exhausted
* SETINTA - Use interrupt A
* SETINTB - Don't use interrupt B
* WIDTH - Provided by caller
* SRCINC - Provided by caller
* DSTINC - Provided by caller
* XFERCOUNT - Derived from with and nbytes
*/
xfrcfg &= ~(DMA_XFERCFG_RELOAD | DMA_XFERCFG_SWTRIG | DMA_XFERCFG_SETINTB |
DMA_XFERCFG_XFERCOUNT_MASK);
xfrcfg |= (DMA_XFERCFG_CFGVALID | DMA_XFERCFG_CLRTRIG | DMA_XFERCFG_SETINTA);
xfrcfg |= DMA_XFERCFG_XFERCOUNT(nxfrs);
putreg32(xfrcfg, base + LPC54_DMA_XFERCFG_OFFSET);
ret = OK;
errout_with_exclsem:
nxsem_post(&g_dma.exclsem);
return ret;
}
/****************************************************************************
* Name: lpc54_dmastart
*
* Description:
* Start the DMA transfer
*
****************************************************************************/
int lpc54_dmastart(int ch, dma_callback_t callback, void *arg)
{
struct lpc54_dmach_s *dmach;
uintptr_t regaddr;
uint32_t bitmask;
DEBUGASSERT((unsigned)ch < LPC54_DMA_NCHANNELS);
dmach = &g_dma.dmach[ch];
DEBUGASSERT(dmach->inuse && callback != NULL);
/* Save the callback information */
dmach->callback = callback;
dmach->arg = arg;
/* Clear any pending DMA interrupts */
bitmask = DMA_CHANNEL(ch);
putreg32(bitmask, LPC54_DMA_ERRINT0);
putreg32(bitmask, LPC54_DMA_INTA0);
putreg32(bitmask, LPC54_DMA_INTB0);
/* Enable the channel and enable interrupt A and error interrupts. */
putreg32(bitmask, LPC54_DMA_ENABLESET0); /* Enable the channel */
putreg32(bitmask, LPC54_DMA_INTENSET0); /* Enable channel interrupts */
/* Enable the trigger for this channel */
regaddr = LPC54_DMA_CTLSTAT(ch);
modifyreg32(regaddr, 0, DMA_CTLSTAT_TRIG);
return OK;
}
/****************************************************************************
* Name: lpc54_dmastop
*
* Description:
* Cancel the DMA. After lpc54_dmastop() is called, the DMA channel is
* reset and lpc54_dmasetup() must be called before lpc54_dmastart() can be
* called again
*
* This function will be called either by the user directly, by the user
* indirectly via lpc54_dmafree(), or from lpc54_dma_interrupt when the
* transfer completes.
*
****************************************************************************/
void lpc54_dmastop(int ch)
{
struct lpc54_dmach_s *dmach;
uint32_t bitmask;
DEBUGASSERT((unsigned)ch < LPC54_DMA_NCHANNELS);
dmach = &g_dma.dmach[ch];
DEBUGASSERT(dmach->inuse);
/* Disable this channel and mask any further interrupts from the channel.
* this channel.
*/
bitmask = DMA_CHANNEL(ch);
putreg32(bitmask, LPC54_DMA_INTENCLR0); /* Disable channel interrupts */
putreg32(bitmask, LPC54_DMA_ENABLECLR0); /* Disable the channel */
/* Clear any pending interrupts for this channel */
putreg32(bitmask, LPC54_DMA_ERRINT0);
putreg32(bitmask, LPC54_DMA_INTA0);
putreg32(bitmask, LPC54_DMA_INTB0);
/* This channel is no longer in use */
g_dma.dmach[ch].inuse = false;
}
/****************************************************************************
* Name: lpc54_dmasample
*
* Description:
* Sample DMA register contents
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA
void lpc54_dmasample(int ch, struct lpc54_dmaregs_s *regs)
{
uintptr_t base;
DEBUGASSERT((unsigned)ch < LPC54_DMA_NCHANNELS);
/* Sample the global DMA registers */
regs->gbl.ctrl = getreg32(LPC54_DMA_CTRL);
regs->gbl.intstat = getreg32(LPC54_DMA_INTSTAT);
regs->gbl.srambase = getreg32(LPC54_DMA_SRAMBASE);
regs->gbl.enableset0 = getreg32(LPC54_DMA_ENABLESET0);
regs->gbl.active0 = getreg32(LPC54_DMA_ACTIVE0);
regs->gbl.busy0 = getreg32(LPC54_DMA_BUSY0);
regs->gbl.errint0 = getreg32(LPC54_DMA_ERRINT0);
regs->gbl.intenset0 = getreg32(LPC54_DMA_INTENSET0);
regs->gbl.inta0 = getreg32(LPC54_DMA_INTA0);
regs->gbl.intb0 = getreg32(LPC54_DMA_INTB0);
/* Sample the DMA channel registers */
base = LPC54_DMA_CHAN_BASE(ch);
regs->ch.cfg = getreg32(base + LPC54_DMA_CFG_OFFSET);
regs->ch.ctlstat = getreg32(base + LPC54_DMA_CTLSTAT_OFFSET);
regs->ch.xfercfg = getreg32(base + LPC54_DMA_XFERCFG_OFFSET);
}
#endif /* CONFIG_DEBUG_DMA */
/****************************************************************************
* Name: lpc54_dmadump
*
* Description:
* Dump previously sampled DMA register contents
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA
void lpc54_dmadump(int ch, const struct lpc54_dmaregs_s *regs,
const char *msg)
{
uintptr_t base;
DEBUGASSERT((unsigned)ch < LPC54_DMA_NCHANNELS && regs != NULL && msg != NULL);
/* Dump the sampled global DMA registers */
dmainfo("Global DMA Registers: %s\n", msg);
dmainfo(" CTRL[%08x]: %08lx\n",
LPC54_DMA_CTRL, (unsigned long)regs->gbl.ctrl);
dmainfo(" INTSTAT[%08x]: %08lx\n",
LPC54_DMA_INTSTAT, (unsigned long)regs->gbl.intstat);
dmainfo(" SRAMBASE[%08x]: %08lx\n",
LPC54_DMA_SRAMBASE, (unsigned long)regs->gbl.srambase);
dmainfo(" ENABLESET0[%08x]: %08lx\n",
LPC54_DMA_ENABLESET0, (unsigned long)regs->gbl.enableset0);
dmainfo(" ACTIVE0[%08x]: %08lx\n",
LPC54_DMA_ACTIVE0, (unsigned long)regs->gbl.active0);
dmainfo(" BUSY0[%08x]: %08lx\n",
LPC54_DMA_BUSY0, (unsigned long)regs->gbl.busy0);
dmainfo(" ERRINT0[%08x]: %08lx\n",
LPC54_DMA_ERRINT0, (unsigned long)regs->gbl.errint0);
dmainfo(" INTENSET0[%08x]: %08lx\n",
LPC54_DMA_INTENSET0, (unsigned long)regs->gbl.intenset0);
dmainfo(" INTA0[%08x]: %08lx\n",
LPC54_DMA_INTA0, (unsigned long)regs->gbl.inta0);
dmainfo(" INTB0[%08x]: %08lx\n",
LPC54_DMA_INTB0, (unsigned long)regs->gbl.intb0);
/* Dump the DMA channel registers */
base = LPC54_DMA_CHAN_BASE(ch);
dmainfo("Channel DMA Registers: %d\n", ch);
dmainfo(" CFG[%08x]: %08lx\n",
base + LPC54_DMA_CFG_OFFSET, (unsigned long)regs->ch.cfg);
dmainfo(" CTLSTAT[%08x]: %08lx\n",
base + LPC54_DMA_CTLSTAT_OFFSET, (unsigned long)regs->ch.ctlstat);
dmainfo(" XFERCFG[%08x]: %08lx\n",
base + LPC54_DMA_XFERCFG_OFFSET, (unsigned long)regs->ch.xfercfg);
}
#endif /* CONFIG_DEBUG_DMA */
#endif /* CONFIG_LPC54_DMA */