2e54df0f35
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
803 lines
23 KiB
C
803 lines
23 KiB
C
/****************************************************************************
|
|
* arch/arm/src/stm32f0l0g0/stm32_dma_v1.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/semaphore.h>
|
|
|
|
#include <arch/chip/chip.h>
|
|
|
|
#include "arm_arch.h"
|
|
#include "arm_internal.h"
|
|
#include "sched/sched.h"
|
|
#include "chip.h"
|
|
#include "stm32_dma.h"
|
|
#include "stm32.h"
|
|
|
|
/* This file supports the STM32 DMA IP core version 1 - F0, F1, F3, L0, L1,
|
|
* L4
|
|
*
|
|
* F0, L0 and L4 have the additional CSELR register which is used to remap
|
|
* the DMA requests for each channel.
|
|
*/
|
|
|
|
#ifdef CONFIG_STM32F0L0G0_HAVE_DMAMUX
|
|
# error DMAMUX not supported yet
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define DMA1_NCHANNELS 7
|
|
|
|
#if STM32_NDMA > 1
|
|
# define DMA2_NCHANNELS 5
|
|
# define DMA_NCHANNELS (DMA1_NCHANNELS+DMA2_NCHANNELS)
|
|
#else
|
|
# define DMA_NCHANNELS DMA1_NCHANNELS
|
|
#endif
|
|
|
|
/* Convert the DMA channel base address to the DMA register block address */
|
|
|
|
#define DMA_BASE(ch) (ch & 0xfffffc00)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* This structure describes one DMA channel */
|
|
|
|
struct stm32_dma_s
|
|
{
|
|
uint8_t chan; /* DMA channel number (0-6) */
|
|
#ifdef DMA_HAVE_CSELR
|
|
uint8_t function; /* DMA peripheral connected to this channel (0-7) */
|
|
#endif
|
|
uint8_t irq; /* DMA channel IRQ number */
|
|
sem_t sem; /* Used to wait for DMA channel to become available */
|
|
uint32_t base; /* DMA register channel base address */
|
|
dma_callback_t callback; /* Callback invoked when the DMA completes */
|
|
void *arg; /* Argument passed to callback function */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* This array describes the state of each DMA */
|
|
|
|
static struct stm32_dma_s g_dma[DMA_NCHANNELS] =
|
|
{
|
|
{
|
|
.chan = 0,
|
|
.irq = STM32_IRQ_DMA1CH1,
|
|
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(0),
|
|
},
|
|
{
|
|
.chan = 1,
|
|
.irq = STM32_IRQ_DMA1CH2,
|
|
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(1),
|
|
},
|
|
{
|
|
.chan = 2,
|
|
.irq = STM32_IRQ_DMA1CH3,
|
|
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(2),
|
|
},
|
|
{
|
|
.chan = 3,
|
|
.irq = STM32_IRQ_DMA1CH4,
|
|
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(3),
|
|
},
|
|
{
|
|
.chan = 4,
|
|
.irq = STM32_IRQ_DMA1CH5,
|
|
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(4),
|
|
},
|
|
{
|
|
.chan = 5,
|
|
.irq = STM32_IRQ_DMA1CH6,
|
|
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(5),
|
|
},
|
|
{
|
|
.chan = 6,
|
|
.irq = STM32_IRQ_DMA1CH7,
|
|
.base = STM32_DMA1_BASE + STM32_DMACHAN_OFFSET(6),
|
|
},
|
|
#if STM32_NDMA > 1
|
|
{
|
|
.chan = 0,
|
|
.irq = STM32_IRQ_DMA2CH1,
|
|
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(0),
|
|
},
|
|
{
|
|
.chan = 1,
|
|
.irq = STM32_IRQ_DMA2CH2,
|
|
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(1),
|
|
},
|
|
{
|
|
.chan = 2,
|
|
.irq = STM32_IRQ_DMA2CH3,
|
|
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(2),
|
|
},
|
|
{
|
|
.chan = 3,
|
|
.irq = STM32_IRQ_DMA2CH4,
|
|
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(3),
|
|
},
|
|
{
|
|
.chan = 4,
|
|
.irq = STM32_IRQ_DMA2CH5,
|
|
.base = STM32_DMA2_BASE + STM32_DMACHAN_OFFSET(4),
|
|
},
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* DMA register access functions
|
|
****************************************************************************/
|
|
|
|
/* Get non-channel register from DMA1 or DMA2 */
|
|
|
|
static inline uint32_t dmabase_getreg(struct stm32_dma_s *dmach,
|
|
uint32_t offset)
|
|
{
|
|
return getreg32(DMA_BASE(dmach->base) + offset);
|
|
}
|
|
|
|
/* Write to non-channel register in DMA1 or DMA2 */
|
|
|
|
static inline void dmabase_putreg(struct stm32_dma_s *dmach, uint32_t offset,
|
|
uint32_t value)
|
|
{
|
|
putreg32(value, DMA_BASE(dmach->base) + offset);
|
|
}
|
|
|
|
/* Get channel register from DMA1 or DMA2 */
|
|
|
|
static inline uint32_t dmachan_getreg(struct stm32_dma_s *dmach,
|
|
uint32_t offset)
|
|
{
|
|
return getreg32(dmach->base + offset);
|
|
}
|
|
|
|
/* Write to channel register in DMA1 or DMA2 */
|
|
|
|
static inline void dmachan_putreg(struct stm32_dma_s *dmach,
|
|
uint32_t offset, uint32_t value)
|
|
{
|
|
putreg32(value, dmach->base + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmatake() and stm32_dmagive()
|
|
*
|
|
* Description:
|
|
* Used to get exclusive access to a DMA channel.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_dmatake(FAR struct stm32_dma_s *dmach)
|
|
{
|
|
return nxsem_wait_uninterruptible(&dmach->sem);
|
|
}
|
|
|
|
static inline void stm32_dmagive(FAR struct stm32_dma_s *dmach)
|
|
{
|
|
nxsem_post(&dmach->sem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmachandisable
|
|
*
|
|
* Description:
|
|
* Disable the DMA channel
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void stm32_dmachandisable(struct stm32_dma_s *dmach)
|
|
{
|
|
uint32_t 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
|
|
*
|
|
* Description:
|
|
* DMA interrupt handler
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32_dmainterrupt(int irq, void *context, FAR void *arg)
|
|
{
|
|
struct stm32_dma_s *dmach;
|
|
uint32_t isr;
|
|
int chndx = 0;
|
|
|
|
/* Get the channel structure from the interrupt number */
|
|
|
|
if (irq >= STM32_IRQ_DMA1CH1 && irq <= STM32_IRQ_DMA1CH7)
|
|
{
|
|
chndx = irq - STM32_IRQ_DMA1CH1;
|
|
}
|
|
else
|
|
#if STM32_NDMA > 1
|
|
if (irq >= STM32_IRQ_DMA2CH1 && irq <= STM32_IRQ_DMA2CH5)
|
|
{
|
|
chndx = irq - STM32_IRQ_DMA2CH1 + DMA1_NCHANNELS;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
DEBUGPANIC();
|
|
}
|
|
|
|
dmach = &g_dma[chndx];
|
|
|
|
/* Get the interrupt status (for this channel only) */
|
|
|
|
isr = dmabase_getreg(dmach, STM32_DMA_ISR_OFFSET) &
|
|
DMA_ISR_CHAN_MASK(dmach->chan);
|
|
|
|
/* Clear the interrupts we are handling */
|
|
|
|
dmabase_putreg(dmach, STM32_DMA_IFCR_OFFSET, isr);
|
|
|
|
/* Invoke the callback */
|
|
|
|
if (dmach->callback)
|
|
{
|
|
dmach->callback(dmach, isr >> DMA_ISR_CHAN_SHIFT(dmach->chan),
|
|
dmach->arg);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmainitialize
|
|
*
|
|
* Description:
|
|
* Initialize the DMA subsystem
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void weak_function arm_dma_initialize(void)
|
|
{
|
|
struct stm32_dma_s *dmach;
|
|
int chndx;
|
|
|
|
/* Initialize each DMA channel */
|
|
|
|
for (chndx = 0; chndx < DMA_NCHANNELS; chndx++)
|
|
{
|
|
dmach = &g_dma[chndx];
|
|
nxsem_init(&dmach->sem, 0, 1);
|
|
|
|
/* Attach DMA interrupt vectors */
|
|
|
|
irq_attach(dmach->irq, stm32_dmainterrupt, NULL);
|
|
|
|
/* Disable the DMA channel */
|
|
|
|
stm32_dmachandisable(dmach);
|
|
|
|
/* Enable the IRQ at the NVIC (still disabled at the DMA controller) */
|
|
|
|
up_enable_irq(dmach->irq);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmachannel
|
|
*
|
|
* Description:
|
|
* Allocate a DMA channel. This function gives the caller mutually
|
|
* 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.
|
|
*
|
|
* If the DMA channel is not available, then stm32_dmachannel() will wait
|
|
* until the holder of the channel relinquishes the channel by calling
|
|
* stm32_dmafree(). WARNING: If you have two devices sharing a DMA
|
|
* channel and the code never releases the channel, the stm32_dmachannel
|
|
* call for the other will hang forever in this function! Don't let your
|
|
* design do that!
|
|
*
|
|
* Hmm.. I suppose this interface could be extended to make a non-blocking
|
|
* version. Feel free to do that if that is what you need.
|
|
*
|
|
* Input Parameters:
|
|
* chndx - Identifies the stream/channel resource. For the STM32 F1, this
|
|
* is simply the channel number as provided by the DMACHAN_* definitions
|
|
* in chip/stm32f10xxx_dma.h.
|
|
*
|
|
* Returned Value:
|
|
* 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:
|
|
* - The caller does not hold he DMA channel.
|
|
* - The caller can wait for the DMA channel to be freed if it is no
|
|
* available.
|
|
*
|
|
****************************************************************************/
|
|
|
|
DMA_HANDLE stm32_dmachannel(unsigned int chndef)
|
|
{
|
|
int chndx = 0;
|
|
struct stm32_dma_s *dmach = NULL;
|
|
int ret;
|
|
|
|
#ifdef DMA_HAVE_CSELR
|
|
chndx = (chndef & DMACHAN_SETTING_CHANNEL_MASK) >>
|
|
DMACHAN_SETTING_CHANNEL_SHIFT;
|
|
#else
|
|
chndx = chndef;
|
|
#endif
|
|
|
|
dmach = &g_dma[chndx];
|
|
|
|
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
|
|
*/
|
|
|
|
ret = stm32_dmatake(dmach);
|
|
if (ret < 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* The caller now has exclusive use of the DMA channel */
|
|
|
|
#ifdef DMA_HAVE_CSELR
|
|
/* Define the peripheral that will use the channel. This is stored until
|
|
* dmasetup is called.
|
|
*/
|
|
|
|
dmach->function = (chndef & DMACHAN_SETTING_FUNCTION_MASK) >>
|
|
DMACHAN_SETTING_FUNCTION_SHIFT;
|
|
#endif
|
|
|
|
return (DMA_HANDLE)dmach;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmafree
|
|
*
|
|
* Description:
|
|
* Release a DMA channel. If another thread is waiting for this DMA
|
|
* channel in a call to stm32_dmachannel, then this function will re-assign
|
|
* the DMA channel to that thread and wake it up. NOTE: The 'handle' used
|
|
* in this argument must NEVER be used again until stm32_dmachannel() is
|
|
* called again to re-gain access to the channel.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* - The caller holds the DMA channel.
|
|
* - There is no DMA in progress
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32_dmafree(DMA_HANDLE handle)
|
|
{
|
|
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Release the channel */
|
|
|
|
stm32_dmagive(dmach);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmasetup
|
|
*
|
|
* Description:
|
|
* Configure DMA before using
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32_dmasetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
|
|
size_t ntransfers, uint32_t ccr)
|
|
{
|
|
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
|
|
uint32_t regval;
|
|
|
|
/* Then DMA_CNDTRx register can only be modified if the DMA channel is
|
|
* disabled.
|
|
*/
|
|
|
|
regval = dmachan_getreg(dmach, STM32_DMACHAN_CCR_OFFSET);
|
|
regval &= ~(DMA_CCR_EN);
|
|
dmachan_putreg(dmach, STM32_DMACHAN_CCR_OFFSET, regval);
|
|
|
|
/* Set the peripheral register address in the DMA_CPARx register. The data
|
|
* will be moved from/to this address to/from the memory after the
|
|
* peripheral event.
|
|
*/
|
|
|
|
dmachan_putreg(dmach, STM32_DMACHAN_CPAR_OFFSET, paddr);
|
|
|
|
/* Set the memory address in the DMA_CMARx register. The data will be
|
|
* written to or read from this memory after the peripheral event.
|
|
*/
|
|
|
|
dmachan_putreg(dmach, STM32_DMACHAN_CMAR_OFFSET, maddr);
|
|
|
|
/* Configure the total number of data to be transferred in the DMA_CNDTRx
|
|
* register. After each peripheral event, this value will be decremented.
|
|
*/
|
|
|
|
dmachan_putreg(dmach, STM32_DMACHAN_CNDTR_OFFSET, ntransfers);
|
|
|
|
/* Configure the channel priority using the PL[1:0] bits in the DMA_CCRx
|
|
* register. Configure data transfer direction, circular mode, peripheral
|
|
* and memory incremented mode, peripheral & memory data size, and
|
|
* interrupt after half and/or full transfer in the DMA_CCRx register.
|
|
*/
|
|
|
|
regval = dmachan_getreg(dmach, STM32_DMACHAN_CCR_OFFSET);
|
|
regval &= ~(DMA_CCR_MEM2MEM | DMA_CCR_PL_MASK | DMA_CCR_MSIZE_MASK |
|
|
DMA_CCR_PSIZE_MASK | DMA_CCR_MINC | DMA_CCR_PINC |
|
|
DMA_CCR_CIRC | DMA_CCR_DIR);
|
|
ccr &= (DMA_CCR_MEM2MEM | DMA_CCR_PL_MASK | DMA_CCR_MSIZE_MASK |
|
|
DMA_CCR_PSIZE_MASK | DMA_CCR_MINC | DMA_CCR_PINC |
|
|
DMA_CCR_CIRC | DMA_CCR_DIR);
|
|
regval |= ccr;
|
|
dmachan_putreg(dmach, STM32_DMACHAN_CCR_OFFSET, regval);
|
|
|
|
#ifdef DMA_HAVE_CSELR
|
|
/* Define peripheral indicated in dmach->function */
|
|
|
|
regval = dmabase_getreg(dmach, STM32_DMA_CSELR_OFFSET);
|
|
regval &= ~(0x0f << (dmach->chan << 2));
|
|
regval |= (dmach->function << (dmach->chan << 2));
|
|
dmabase_putreg(dmach, STM32_DMA_CSELR_OFFSET, regval);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmastart
|
|
*
|
|
* Description:
|
|
* Start the DMA transfer
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32_dmachannel()
|
|
* - No DMA in progress
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32_dmastart(DMA_HANDLE handle, dma_callback_t callback,
|
|
void *arg, bool half)
|
|
{
|
|
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
|
|
uint32_t ccr;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Save the callback info. This will be invoked whent the DMA completes */
|
|
|
|
dmach->callback = callback;
|
|
dmach->arg = arg;
|
|
|
|
/* Activate the channel by setting the ENABLE bit in the DMA_CCRx register.
|
|
* As soon as the channel is enabled, it can serve any DMA request from the
|
|
* peripheral connected on the channel.
|
|
*/
|
|
|
|
ccr = dmachan_getreg(dmach, STM32_DMACHAN_CCR_OFFSET);
|
|
ccr |= DMA_CCR_EN;
|
|
|
|
/* In normal mode, interrupt at either half or full completion. In circular
|
|
* mode, always interrupt on buffer wrap, and optionally interrupt at the
|
|
* halfway point.
|
|
*/
|
|
|
|
if ((ccr & DMA_CCR_CIRC) == 0)
|
|
{
|
|
/* Once half of the bytes are transferred, the half-transfer flag
|
|
* (HTIF) is set and an interrupt is generated if the Half-Transfer
|
|
* Interrupt Enable bit (HTIE) is set. At the end of the transfer, the
|
|
* Transfer Complete Flag (TCIF) is set and an interrupt is generated
|
|
* if the Transfer Complete Interrupt Enable bit (TCIE) is set.
|
|
*/
|
|
|
|
ccr |= (half ? (DMA_CCR_HTIE | DMA_CCR_TEIE) :
|
|
(DMA_CCR_TCIE | DMA_CCR_TEIE));
|
|
}
|
|
else
|
|
{
|
|
/* In nonstop mode, when the transfer completes it immediately resets
|
|
* and starts again. The transfer-complete interrupt is thus always
|
|
* enabled, and the half-complete interrupt can be used in circular
|
|
* mode to determine when the buffer is half-full or in double-buffered
|
|
* mode to determine when one of the two buffers is full.
|
|
*/
|
|
|
|
ccr |= (half ? DMA_CCR_HTIE : 0) | DMA_CCR_TCIE | DMA_CCR_TEIE;
|
|
}
|
|
|
|
dmachan_putreg(dmach, STM32_DMACHAN_CCR_OFFSET, ccr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmastop
|
|
*
|
|
* Description:
|
|
* Cancel the DMA. After stm32_dmastop() is called, the DMA channel is
|
|
* reset and stm32_dmasetup() must be called before stm32_dmastart() can be
|
|
* called again
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32_dmastop(DMA_HANDLE handle)
|
|
{
|
|
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
|
|
stm32_dmachandisable(dmach);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmaresidual
|
|
*
|
|
* Description:
|
|
* Returns the number of bytes remaining to be transferred
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
size_t stm32_dmaresidual(DMA_HANDLE handle)
|
|
{
|
|
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
|
|
|
|
return dmachan_getreg(dmach, STM32_DMACHAN_CNDTR_OFFSET);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmacapable
|
|
*
|
|
* 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.
|
|
*
|
|
* Returned Value:
|
|
* True, if transfer is possible.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_STM32F0L0G0_DMACAPABLE
|
|
bool stm32_dmacapable(uintptr_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 & DMA_CCR_MSIZE_MASK)
|
|
{
|
|
case DMA_CCR_MSIZE_8BITS:
|
|
transfer_size = 1;
|
|
mend = maddr + count - 1;
|
|
break;
|
|
|
|
case DMA_CCR_MSIZE_16BITS:
|
|
transfer_size = 2;
|
|
mend = maddr + (count << 1) - 1;
|
|
break;
|
|
|
|
case DMA_CCR_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)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmasample
|
|
*
|
|
* Description:
|
|
* Sample DMA register contents
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
void stm32_dmasample(DMA_HANDLE handle, struct stm32_dmaregs_s *regs)
|
|
{
|
|
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
|
|
irqstate_t flags;
|
|
|
|
flags = enter_critical_section();
|
|
regs->isr = dmabase_getreg(dmach, STM32_DMA_ISR_OFFSET);
|
|
#ifdef DMA_HAVE_CSELR
|
|
regs->cselr = dmabase_getreg(dmach, STM32_DMA_CSELR_OFFSET);
|
|
#endif
|
|
regs->ccr = dmachan_getreg(dmach, STM32_DMACHAN_CCR_OFFSET);
|
|
regs->cndtr = dmachan_getreg(dmach, STM32_DMACHAN_CNDTR_OFFSET);
|
|
regs->cpar = dmachan_getreg(dmach, STM32_DMACHAN_CPAR_OFFSET);
|
|
regs->cmar = dmachan_getreg(dmach, STM32_DMACHAN_CMAR_OFFSET);
|
|
leave_critical_section(flags);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dmadump
|
|
*
|
|
* Description:
|
|
* Dump previously sampled DMA register contents
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
void stm32_dmadump(DMA_HANDLE handle, const struct stm32_dmaregs_s *regs,
|
|
const char *msg)
|
|
{
|
|
struct stm32_dma_s *dmach = (struct stm32_dma_s *)handle;
|
|
uint32_t dmabase = DMA_BASE(dmach->base);
|
|
|
|
dmainfo("DMA Registers: %s\n", msg);
|
|
dmainfo(" ISRC[%08x]: %08x\n",
|
|
dmabase + STM32_DMA_ISR_OFFSET, regs->isr);
|
|
#ifdef DMA_HAVE_CSELR
|
|
dmainfo(" CSELR[%08x]: %08x\n",
|
|
dmabase + STM32_DMA_CSELR_OFFSET, regs->cselr);
|
|
#endif
|
|
dmainfo(" CCR[%08x]: %08x\n",
|
|
dmach->base + STM32_DMACHAN_CCR_OFFSET, regs->ccr);
|
|
dmainfo(" CNDTR[%08x]: %08x\n",
|
|
dmach->base + STM32_DMACHAN_CNDTR_OFFSET, regs->cndtr);
|
|
dmainfo(" CPAR[%08x]: %08x\n",
|
|
dmach->base + STM32_DMACHAN_CPAR_OFFSET, regs->cpar);
|
|
dmainfo(" CMAR[%08x]: %08x\n",
|
|
dmach->base + STM32_DMACHAN_CMAR_OFFSET, regs->cmar);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARCH_HIPRI_INTERRUPT
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dma_intack
|
|
*
|
|
* Description:
|
|
* Public visible interface to acknowledge interrupts on DMA channel
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32_dma_intack(unsigned int chndx, uint32_t isr)
|
|
{
|
|
struct stm32_dma_s *dmach = &g_dma[chndx];
|
|
|
|
dmabase_putreg(dmach, STM32_DMA_IFCR_OFFSET, isr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32_dma_intget
|
|
*
|
|
* Description:
|
|
* Public visible interface to get pending interrupts from DMA channel
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint32_t stm32_dma_intget(unsigned int chndx)
|
|
{
|
|
struct stm32_dma_s *dmach = &g_dma[chndx];
|
|
|
|
return dmabase_getreg(dmach, STM32_DMA_ISR_OFFSET) &
|
|
DMA_ISR_CHAN_MASK(dmach->chan);
|
|
}
|
|
#endif /* CONFIG_ARCH_HIPRI_INTERRUPT */
|