972a260391
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
1412 lines
40 KiB
C
1412 lines
40 KiB
C
/****************************************************************************
|
|
* arch/arm/src/stm32l4/stm32l4xrxx_dma.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 <inttypes.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 <arch/stm32l4/chip.h>
|
|
|
|
#include "arm_internal.h"
|
|
#include "sched/sched.h"
|
|
#include "stm32l4_dma.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_STM32L4_DMAMUX
|
|
# error "Configuration error, CONFIG_STM32L4_DMAMUX not defined!"
|
|
#endif
|
|
|
|
#ifndef CONFIG_STM32L4_DMAMUX1
|
|
# error "Configuration error, CONFIG_STM32L4_DMAMUX1 not defined!"
|
|
#endif
|
|
|
|
#define DMAMUX_NUM 1
|
|
#define DMA_CONTROLLERS 2
|
|
|
|
#ifdef CONFIG_STM32L4_DMA1
|
|
# define DMA1_NCHAN 7
|
|
#else
|
|
# define DMA1_NCHAN 0
|
|
#endif
|
|
#ifdef CONFIG_STM32L4_DMA2
|
|
# define DMA2_NCHAN 7
|
|
#else
|
|
# define DMA2_NCHAN 0
|
|
#endif
|
|
|
|
#define DMA1_FIRST (0)
|
|
#define DMA1_LAST (DMA1_FIRST+DMA1_NCHAN)
|
|
#define DMA2_FIRST (DMA1_LAST)
|
|
#define DMA2_LAST (DMA2_FIRST+DMA2_NCHAN)
|
|
|
|
/* All available DMA channels */
|
|
|
|
#define DMA_NCHANNELS (DMA1_NCHAN+DMA2_NCHAN)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* This structure described one DMAMUX device */
|
|
|
|
struct stm32l4_dmamux_s
|
|
{
|
|
uint8_t id; /* DMAMUX id */
|
|
uint8_t nchan; /* DMAMUX channels */
|
|
uint32_t base; /* DMAMUX base address */
|
|
};
|
|
|
|
typedef const struct stm32l4_dmamux_s *DMA_MUX;
|
|
|
|
/* This structure describes one DMA controller */
|
|
|
|
struct stm32l4_dma_s
|
|
{
|
|
uint8_t first; /* Offset in stm32l4_dmach_s array */
|
|
uint8_t nchan; /* Number of channels */
|
|
uint8_t dmamux_offset; /* DMAMUX channel offset */
|
|
uint32_t base; /* Base address */
|
|
DMA_MUX dmamux; /* DMAMUX associated with controller */
|
|
};
|
|
|
|
/* This structure describes one DMA channel (DMA1, DMA2) */
|
|
|
|
struct stm32l4_dmach_s
|
|
{
|
|
bool used; /* Channel in use */
|
|
uint8_t dmamux_req; /* Configured DMAMUX input request */
|
|
uint8_t ctrl; /* DMA controller */
|
|
uint8_t chan; /* DMA channel channel id */
|
|
uint8_t irq; /* DMA channel IRQ number */
|
|
uint8_t shift; /* IFCR bit shift value */
|
|
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 */
|
|
};
|
|
|
|
typedef struct stm32l4_dmach_s *DMA_CHANNEL;
|
|
|
|
/* DMA operations */
|
|
|
|
struct stm32l4_dma_ops_s
|
|
{
|
|
/* Disable the DMA transfer */
|
|
|
|
void (*dma_disable)(DMA_CHANNEL dmachan);
|
|
|
|
/* DMA interrupt */
|
|
|
|
int (*dma_interrupt)(int irq, void *context, void *arg);
|
|
|
|
/* Setup the DMA */
|
|
|
|
void (*dma_setup)(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
|
|
size_t ntransfers, uint32_t ccr);
|
|
|
|
/* Start the DMA */
|
|
|
|
void (*dma_start)(DMA_HANDLE handle, dma_callback_t callback,
|
|
void *arg, bool half);
|
|
|
|
/* Read remaining DMA bytes */
|
|
|
|
size_t (*dma_residual)(DMA_HANDLE handle);
|
|
|
|
/* Check the DMA configuration */
|
|
|
|
bool (*dma_capable)(uint32_t maddr, uint32_t count, uint32_t ccr);
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
/* Sample the DMA registers */
|
|
|
|
void (*dma_sample)(DMA_HANDLE handle, struct stm32l4_dmaregs_s *regs);
|
|
|
|
/* Dump the DMA registers */
|
|
|
|
void (*dma_dump)(DMA_HANDLE handle,
|
|
const struct stm32l4_dmaregs_s *regs,
|
|
const char *msg);
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
#if defined(CONFIG_STM32L4_DMA1) || defined(CONFIG_STM32L4_DMA2)
|
|
static void stm32l4_dma12_disable(DMA_CHANNEL dmachan);
|
|
static int stm32l4_dma12_interrupt(int irq, void *context, void *arg);
|
|
static void stm32l4_dma12_setup(DMA_HANDLE handle, uint32_t paddr,
|
|
uint32_t maddr, size_t ntransfers,
|
|
uint32_t ccr);
|
|
static void stm32l4_dma12_start(DMA_HANDLE handle, dma_callback_t callback,
|
|
void *arg, bool half);
|
|
static size_t stm32l4_dma12_residual(DMA_HANDLE handle);
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
static void stm32l4_dma12_sample(DMA_HANDLE handle,
|
|
struct stm32l4_dmaregs_s *regs);
|
|
static void stm32l4_dma12_dump(DMA_HANDLE handle,
|
|
const struct stm32l4_dmaregs_s *regs,
|
|
const char *msg);
|
|
#endif
|
|
#endif
|
|
|
|
static uint32_t dmachan_getbase(DMA_CHANNEL dmachan);
|
|
static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset);
|
|
static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset,
|
|
uint32_t value);
|
|
static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset);
|
|
static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset,
|
|
uint32_t value);
|
|
static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value);
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset);
|
|
static void stm32l4_dmamux_sample(DMA_MUX dmamux, uint8_t chan,
|
|
struct stm32l4_dmaregs_s *regs);
|
|
static void stm32l4_dmamux_dump(DMA_MUX dmamux, uint8_t channel,
|
|
const struct stm32l4_dmaregs_s *regs);
|
|
#endif
|
|
static DMA_CHANNEL stm32l4_dma_channel_get(uint8_t channel,
|
|
uint8_t controller);
|
|
static void stm32l4_gdma_limits_get(uint8_t controller, uint8_t *first,
|
|
uint8_t *last);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* Operations specific to DMA controller */
|
|
|
|
static const struct stm32l4_dma_ops_s g_dma_ops[DMA_CONTROLLERS] =
|
|
{
|
|
#ifdef CONFIG_STM32L4_DMA1
|
|
/* 0 - DMA1 */
|
|
|
|
{
|
|
.dma_disable = stm32l4_dma12_disable,
|
|
.dma_interrupt = stm32l4_dma12_interrupt,
|
|
.dma_setup = stm32l4_dma12_setup,
|
|
.dma_start = stm32l4_dma12_start,
|
|
.dma_residual = stm32l4_dma12_residual,
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
.dma_sample = stm32l4_dma12_sample,
|
|
.dma_dump = stm32l4_dma12_dump,
|
|
#endif
|
|
},
|
|
#else
|
|
{
|
|
NULL
|
|
},
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32L4_DMA2
|
|
/* 1 - DMA2 */
|
|
|
|
{
|
|
.dma_disable = stm32l4_dma12_disable,
|
|
.dma_interrupt = stm32l4_dma12_interrupt,
|
|
.dma_setup = stm32l4_dma12_setup,
|
|
.dma_start = stm32l4_dma12_start,
|
|
.dma_residual = stm32l4_dma12_residual,
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
.dma_sample = stm32l4_dma12_sample,
|
|
.dma_dump = stm32l4_dma12_dump,
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
NULL
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/* This array describes the state of DMAMUX controller */
|
|
|
|
static const struct stm32l4_dmamux_s g_dmamux[DMAMUX_NUM] =
|
|
{
|
|
{
|
|
.id = 1,
|
|
.nchan = 14, /* 0-6 - DMA1, 7-13 - DMA2 */
|
|
.base = STM32L4_DMAMUX1_BASE
|
|
}
|
|
};
|
|
|
|
/* This array describes the state of each controller */
|
|
|
|
static const struct stm32l4_dma_s g_dma[DMA_NCHANNELS] =
|
|
{
|
|
/* 0 - DMA1 */
|
|
|
|
{
|
|
.base = STM32L4_DMA1_BASE,
|
|
.first = DMA1_FIRST,
|
|
.nchan = DMA1_NCHAN,
|
|
.dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 0-6 */
|
|
.dmamux_offset = 0
|
|
},
|
|
|
|
/* 1 - DMA2 */
|
|
|
|
{
|
|
.base = STM32L4_DMA2_BASE,
|
|
.first = DMA2_FIRST,
|
|
.nchan = DMA2_NCHAN,
|
|
.dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 7-13 */
|
|
.dmamux_offset = 7
|
|
}
|
|
};
|
|
|
|
/* This array describes the state of each DMA channel. */
|
|
|
|
static struct stm32l4_dmach_s g_dmach[DMA_NCHANNELS] =
|
|
{
|
|
#ifdef CONFIG_STM32L4_DMA1
|
|
/* DMA1 */
|
|
|
|
{
|
|
.ctrl = DMA1,
|
|
.chan = 0,
|
|
.irq = STM32L4_IRQ_DMA1CH1,
|
|
.shift = DMA_CHAN_SHIFT(0),
|
|
.base = STM32L4_DMA1_BASE + STM32L4_DMACHAN_OFFSET(0),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA1,
|
|
.chan = 1,
|
|
.irq = STM32L4_IRQ_DMA1CH2,
|
|
.shift = DMA_CHAN_SHIFT(1),
|
|
.base = STM32L4_DMA1_BASE + STM32L4_DMACHAN_OFFSET(1),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA1,
|
|
.chan = 2,
|
|
.irq = STM32L4_IRQ_DMA1CH3,
|
|
.shift = DMA_CHAN_SHIFT(2),
|
|
.base = STM32L4_DMA1_BASE + STM32L4_DMACHAN_OFFSET(2),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA1,
|
|
.chan = 3,
|
|
.irq = STM32L4_IRQ_DMA1CH4,
|
|
.shift = DMA_CHAN_SHIFT(3),
|
|
.base = STM32L4_DMA1_BASE + STM32L4_DMACHAN_OFFSET(3),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA1,
|
|
.chan = 4,
|
|
.irq = STM32L4_IRQ_DMA1CH5,
|
|
.shift = DMA_CHAN_SHIFT(4),
|
|
.base = STM32L4_DMA1_BASE + STM32L4_DMACHAN_OFFSET(4),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA1,
|
|
.chan = 5,
|
|
.irq = STM32L4_IRQ_DMA1CH6,
|
|
.shift = DMA_CHAN_SHIFT(5),
|
|
.base = STM32L4_DMA1_BASE + STM32L4_DMACHAN_OFFSET(5),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA1,
|
|
.chan = 6,
|
|
.irq = STM32L4_IRQ_DMA1CH7,
|
|
.shift = DMA_CHAN_SHIFT(6),
|
|
.base = STM32L4_DMA1_BASE + STM32L4_DMACHAN_OFFSET(6),
|
|
},
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32L4_DMA2
|
|
/* DMA2 */
|
|
|
|
{
|
|
.ctrl = DMA2,
|
|
.chan = 0,
|
|
.irq = STM32L4_IRQ_DMA2CH1,
|
|
.shift = DMA_CHAN_SHIFT(0),
|
|
.base = STM32L4_DMA2_BASE + STM32L4_DMACHAN_OFFSET(0),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA2,
|
|
.chan = 1,
|
|
.irq = STM32L4_IRQ_DMA2CH2,
|
|
.shift = DMA_CHAN_SHIFT(1),
|
|
.base = STM32L4_DMA2_BASE + STM32L4_DMACHAN_OFFSET(1),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA2,
|
|
.chan = 2,
|
|
.irq = STM32L4_IRQ_DMA2CH3,
|
|
.shift = DMA_CHAN_SHIFT(2),
|
|
.base = STM32L4_DMA2_BASE + STM32L4_DMACHAN_OFFSET(2),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA2,
|
|
.chan = 3,
|
|
.irq = STM32L4_IRQ_DMA2CH4,
|
|
.shift = DMA_CHAN_SHIFT(3),
|
|
.base = STM32L4_DMA2_BASE + STM32L4_DMACHAN_OFFSET(3),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA2,
|
|
.chan = 4,
|
|
.irq = STM32L4_IRQ_DMA2CH5,
|
|
.shift = DMA_CHAN_SHIFT(4),
|
|
.base = STM32L4_DMA2_BASE + STM32L4_DMACHAN_OFFSET(4),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA2,
|
|
.chan = 5,
|
|
.irq = STM32L4_IRQ_DMA2CH6,
|
|
.shift = DMA_CHAN_SHIFT(5),
|
|
.base = STM32L4_DMA2_BASE + STM32L4_DMACHAN_OFFSET(5),
|
|
},
|
|
|
|
{
|
|
.ctrl = DMA2,
|
|
.chan = 6,
|
|
.irq = STM32L4_IRQ_DMA2CH7,
|
|
.shift = DMA_CHAN_SHIFT(6),
|
|
.base = STM32L4_DMA2_BASE + STM32L4_DMACHAN_OFFSET(6),
|
|
},
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* DMA register access functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: dmachan_getbase
|
|
*
|
|
* Description:
|
|
* Get base DMA address for dmachan
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint32_t dmachan_getbase(DMA_CHANNEL dmachan)
|
|
{
|
|
uint8_t controller = dmachan->ctrl;
|
|
|
|
return g_dma[controller].base;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dmabase_getreg
|
|
*
|
|
* Description:
|
|
* Get non-channel register from DMA controller
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset)
|
|
{
|
|
uint32_t dmabase = dmachan_getbase(dmachan);
|
|
|
|
return getreg32(dmabase + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dmabase_putreg
|
|
*
|
|
* Description:
|
|
* Write to non-channel register in DMA controller
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset,
|
|
uint32_t value)
|
|
{
|
|
uint32_t dmabase = dmachan_getbase(dmachan);
|
|
|
|
putreg32(value, dmabase + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dmachan_getreg
|
|
*
|
|
* Description:
|
|
* Get channel register.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset)
|
|
{
|
|
return getreg32(dmachan->base + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dmachan_putreg
|
|
*
|
|
* Description:
|
|
* Write to channel register.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset,
|
|
uint32_t value)
|
|
{
|
|
putreg32(value, dmachan->base + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dmamux_getreg
|
|
*
|
|
* Description:
|
|
* Write to DMAMUX
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value)
|
|
{
|
|
putreg32(value, dmamux->base + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: dmamux_getreg
|
|
*
|
|
* Description:
|
|
* Get DMAMUX register.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset)
|
|
{
|
|
return getreg32(dmamux->base + offset);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma_channel_get
|
|
*
|
|
* Description:
|
|
* Get the g_dmach table entry associated with a given DMA controller
|
|
* and channel number.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static DMA_CHANNEL stm32l4_dma_channel_get(uint8_t channel,
|
|
uint8_t controller)
|
|
{
|
|
uint8_t first = 0;
|
|
uint8_t nchan = 0;
|
|
|
|
/* Get limits for g_dma array */
|
|
|
|
stm32l4_gdma_limits_get(controller, &first, &nchan);
|
|
|
|
DEBUGASSERT(channel <= nchan);
|
|
|
|
return &g_dmach[first + channel];
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_gdma_limits_get
|
|
*
|
|
* Description:
|
|
* Get g_dma array limits for a given DMA controller.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void stm32l4_gdma_limits_get(uint8_t controller, uint8_t *first,
|
|
uint8_t *nchan)
|
|
{
|
|
DEBUGASSERT(first != NULL);
|
|
DEBUGASSERT(nchan != NULL);
|
|
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
*first = g_dma[controller].first;
|
|
*nchan = g_dma[controller].nchan;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* DMA controller functions
|
|
****************************************************************************/
|
|
|
|
#if defined(CONFIG_STM32L4_DMA1) || defined(CONFIG_STM32L4_DMA2)
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma12_disable
|
|
*
|
|
* Description:
|
|
* Disable DMA channel (DMA1/DMA2)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void stm32l4_dma12_disable(DMA_CHANNEL dmachan)
|
|
{
|
|
uint32_t regval;
|
|
|
|
DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
|
|
|
|
/* Disable all interrupts at the DMA controller */
|
|
|
|
regval = dmachan_getreg(dmachan, STM32L4_DMACHAN_CCR_OFFSET);
|
|
regval &= ~DMA_CCR_ALLINTS;
|
|
|
|
/* Disable the DMA channel */
|
|
|
|
regval &= ~DMA_CCR_EN;
|
|
dmachan_putreg(dmachan, STM32L4_DMACHAN_CCR_OFFSET, regval);
|
|
|
|
/* Clear pending channel interrupts */
|
|
|
|
dmabase_putreg(dmachan, STM32L4_DMA_IFCR_OFFSET,
|
|
DMA_ISR_CHAN_MASK(dmachan->chan));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma12_interrupt
|
|
*
|
|
* Description:
|
|
* DMA channel interrupt handler
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int stm32l4_dma12_interrupt(int irq, void *context, void *arg)
|
|
{
|
|
DMA_CHANNEL dmachan;
|
|
uint32_t isr;
|
|
uint8_t channel;
|
|
uint8_t controller;
|
|
|
|
/* Get the channel and the controller that generated the interrupt */
|
|
|
|
if (0)
|
|
{
|
|
}
|
|
#ifdef CONFIG_STM32L4_DMA1
|
|
else if (irq >= STM32L4_IRQ_DMA1CH1 && irq <= STM32L4_IRQ_DMA1CH7)
|
|
{
|
|
channel = irq - STM32L4_IRQ_DMA1CH1;
|
|
controller = DMA1;
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_STM32L4_DMA2
|
|
else if (irq >= STM32L4_IRQ_DMA2CH1 && irq <= STM32L4_IRQ_DMA2CH5)
|
|
{
|
|
channel = irq - STM32L4_IRQ_DMA2CH1;
|
|
controller = DMA2;
|
|
}
|
|
else if (irq >= STM32L4_IRQ_DMA2CH6 && irq <= STM32L4_IRQ_DMA2CH7)
|
|
{
|
|
channel = irq - STM32L4_IRQ_DMA2CH6 + (6 - 1);
|
|
controller = DMA2;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
DEBUGPANIC();
|
|
return OK;
|
|
}
|
|
|
|
/* Get the channel structure from the stream and controller numbers */
|
|
|
|
dmachan = stm32l4_dma_channel_get(channel, controller);
|
|
|
|
/* Get the interrupt status (for this channel only) */
|
|
|
|
isr = dmabase_getreg(dmachan, STM32L4_DMA_ISR_OFFSET) &
|
|
DMA_ISR_CHAN_MASK(dmachan->chan);
|
|
|
|
/* Invoke the callback */
|
|
|
|
if (dmachan->callback)
|
|
{
|
|
dmachan->callback(dmachan, isr >> DMA_ISR_CHAN_SHIFT(dmachan->chan),
|
|
dmachan->arg);
|
|
}
|
|
|
|
/* Clear the interrupts we are handling */
|
|
|
|
dmabase_putreg(dmachan, STM32L4_DMA_IFCR_OFFSET, isr);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma12_setup
|
|
*
|
|
* Description:
|
|
* Configure DMA before using
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void stm32l4_dma12_setup(DMA_HANDLE handle, uint32_t paddr,
|
|
uint32_t maddr, size_t ntransfers,
|
|
uint32_t ccr)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
uint32_t regval;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
DEBUGASSERT(ntransfers < 65536);
|
|
|
|
DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
|
|
|
|
dmainfo("paddr: %08" PRIx32 " maddr: %08" PRIx32
|
|
" ntransfers: %zd ccr: %08" PRIx32 "\n",
|
|
paddr, maddr, ntransfers, ccr);
|
|
|
|
#ifdef CONFIG_STM32L4_DMACAPABLE
|
|
DEBUGASSERT(g_dma_ops[dmachan->ctrl].dma_capable(maddr, ntransfers, ccr));
|
|
#endif
|
|
|
|
/* Then DMA_CNDTRx register can only be modified if the DMA channel is
|
|
* disabled.
|
|
*/
|
|
|
|
regval = dmachan_getreg(dmachan, STM32L4_DMACHAN_CCR_OFFSET);
|
|
regval &= ~(DMA_CCR_EN);
|
|
dmachan_putreg(dmachan, STM32L4_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(dmachan, STM32L4_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(dmachan, STM32L4_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(dmachan, STM32L4_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
|
|
* & memory incremented mode, peripheral & memory data size, and interrupt
|
|
* after half and/or full transfer in the DMA_CCRx register.
|
|
*/
|
|
|
|
regval = dmachan_getreg(dmachan, STM32L4_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(dmachan, STM32L4_DMACHAN_CCR_OFFSET, regval);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma12_start
|
|
*
|
|
* Description:
|
|
* Start the standard DMA transfer
|
|
****************************************************************************/
|
|
|
|
static void stm32l4_dma12_start(DMA_HANDLE handle, dma_callback_t callback,
|
|
void *arg, bool half)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
uint32_t ccr;
|
|
|
|
DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
|
|
|
|
/* Save the callback info. This will be invoked when the DMA completes */
|
|
|
|
dmachan->callback = callback;
|
|
dmachan->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(dmachan, STM32L4_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(dmachan, STM32L4_DMACHAN_CCR_OFFSET, ccr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma12_residual
|
|
****************************************************************************/
|
|
|
|
static size_t stm32l4_dma12_residual(DMA_HANDLE handle)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
|
|
DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
|
|
|
|
return dmachan_getreg(dmachan, STM32L4_DMACHAN_CNDTR_OFFSET);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma12_sample
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
void stm32l4_dma12_sample(DMA_HANDLE handle, struct stm32l4_dmaregs_s *regs)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
irqstate_t flags;
|
|
|
|
flags = enter_critical_section();
|
|
|
|
regs->isr = dmabase_getreg(dmachan, STM32L4_DMA_ISR_OFFSET);
|
|
regs->ccr = dmachan_getreg(dmachan, STM32L4_DMACHAN_CCR_OFFSET);
|
|
regs->cndtr = dmachan_getreg(dmachan, STM32L4_DMACHAN_CNDTR_OFFSET);
|
|
regs->cpar = dmachan_getreg(dmachan, STM32L4_DMACHAN_CPAR_OFFSET);
|
|
regs->cmar = dmachan_getreg(dmachan, STM32L4_DMACHAN_CMAR_OFFSET);
|
|
|
|
stm32l4_dmamux_sample(g_dma[dmachan->ctrl].dmamux,
|
|
dmachan->chan + g_dma[dmachan->ctrl].dmamux_offset,
|
|
regs);
|
|
|
|
leave_critical_section(flags);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dma12_dump
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
static void stm32l4_dma12_dump(DMA_HANDLE handle,
|
|
const struct stm32l4_dmaregs_s *regs,
|
|
const char *msg)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
|
|
DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2);
|
|
|
|
uint32_t dmabase = dmachan_getbase(dmachan);
|
|
|
|
dmainfo("DMA%d Registers: %s\n",
|
|
dmachan->ctrl + 1,
|
|
msg);
|
|
dmainfo(" ISR[%08x]: %08x\n",
|
|
dmabase + STM32L4_DMA_ISR_OFFSET,
|
|
regs->isr);
|
|
dmainfo(" CCR[%08x]: %08x\n",
|
|
dmachan->base + STM32L4_DMACHAN_CCR_OFFSET,
|
|
regs->ccr);
|
|
dmainfo(" CNDTR[%08x]: %08x\n",
|
|
dmachan->base + STM32L4_DMACHAN_CNDTR_OFFSET,
|
|
regs->cndtr);
|
|
dmainfo(" CPAR[%08x]: %08x\n",
|
|
dmachan->base + STM32L4_DMACHAN_CPAR_OFFSET,
|
|
regs->cpar);
|
|
dmainfo(" CMAR[%08x]: %08x\n",
|
|
dmachan->base + STM32L4_DMACHAN_CMAR_OFFSET,
|
|
regs->cmar);
|
|
|
|
stm32l4_dmamux_dump(g_dma[dmachan->ctrl].dmamux,
|
|
dmachan->chan + g_dma[dmachan->ctrl].dmamux_offset,
|
|
regs);
|
|
}
|
|
#endif
|
|
|
|
#endif /* CONFIG_STM32L4_DMA1 || CONFIG_STM32L4_DMA2 */
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmamux_sample
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
static void stm32l4_dmamux_sample(DMA_MUX dmamux, uint8_t chan,
|
|
struct stm32l4_dmaregs_s *regs)
|
|
{
|
|
regs->dmamux.ccr = dmamux_getreg(dmamux, STM32L4_DMAMUX_CXCR_OFFSET(chan));
|
|
regs->dmamux.csr = dmamux_getreg(dmamux, STM32L4_DMAMUX_CSR_OFFSET);
|
|
regs->dmamux.rg0cr = dmamux_getreg(dmamux, STM32L4_DMAMUX_RG0CR_OFFSET);
|
|
regs->dmamux.rg1cr = dmamux_getreg(dmamux, STM32L4_DMAMUX_RG1CR_OFFSET);
|
|
regs->dmamux.rg2cr = dmamux_getreg(dmamux, STM32L4_DMAMUX_RG2CR_OFFSET);
|
|
regs->dmamux.rg3cr = dmamux_getreg(dmamux, STM32L4_DMAMUX_RG3CR_OFFSET);
|
|
regs->dmamux.rgsr = dmamux_getreg(dmamux, STM32L4_DMAMUX_RGSR_OFFSET);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmamux_dump
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
static void stm32l4_dmamux_dump(DMA_MUX dmamux, uint8_t channel,
|
|
const struct stm32l4_dmaregs_s *regs)
|
|
{
|
|
dmainfo("DMAMUX%d CH=%d\n", dmamux->id, channel);
|
|
dmainfo(" CCR[%08x]: %08x\n",
|
|
dmamux->base + STM32L4_DMAMUX_CXCR_OFFSET(channel),
|
|
regs->dmamux.ccr);
|
|
dmainfo(" CSR[%08x]: %08x\n",
|
|
dmamux->base + STM32L4_DMAMUX_CSR_OFFSET, regs->dmamux.csr);
|
|
dmainfo(" RG0CR[%08x]: %08x\n",
|
|
dmamux->base + STM32L4_DMAMUX_RG0CR_OFFSET, regs->dmamux.rg0cr);
|
|
dmainfo(" RG1CR[%08x]: %08x\n",
|
|
dmamux->base + STM32L4_DMAMUX_RG1CR_OFFSET, regs->dmamux.rg1cr);
|
|
dmainfo(" RG2CR[%08x]: %08x\n",
|
|
dmamux->base + STM32L4_DMAMUX_RG2CR_OFFSET, regs->dmamux.rg2cr);
|
|
dmainfo(" RG3CR[%08x]: %08x\n",
|
|
dmamux->base + STM32L4_DMAMUX_RG3CR_OFFSET, regs->dmamux.rg3cr);
|
|
dmainfo(" RGSR[%08x]: %08x\n",
|
|
dmamux->base + STM32L4_DMAMUX_RGSR_OFFSET, regs->dmamux.rgsr);
|
|
};
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: arm_dma_initialize
|
|
*
|
|
* Description:
|
|
* Initialize the DMA subsystem (DMA1, DMA2)
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void weak_function arm_dma_initialize(void)
|
|
{
|
|
DMA_CHANNEL dmachan;
|
|
uint8_t controller;
|
|
int channel;
|
|
|
|
dmainfo("Initialize DMA\n");
|
|
|
|
/* Initialize DMA channels */
|
|
|
|
for (channel = 0; channel < DMA_NCHANNELS; channel++)
|
|
{
|
|
dmachan = &g_dmach[channel];
|
|
|
|
/* Initialize flag */
|
|
|
|
dmachan->used = false;
|
|
|
|
/* Get DMA controller associated with channel */
|
|
|
|
controller = dmachan->ctrl;
|
|
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
/* Attach standard DMA interrupt vectors */
|
|
|
|
irq_attach(dmachan->irq, g_dma_ops[controller].dma_interrupt,
|
|
dmachan);
|
|
|
|
/* Disable the DMA channel */
|
|
|
|
g_dma_ops[controller].dma_disable(dmachan);
|
|
|
|
/* Enable the IRQ at the NVIC (still disabled at the DMA controller) */
|
|
|
|
up_enable_irq(dmachan->irq);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmachannel
|
|
*
|
|
* Description:
|
|
* Allocate a DMA channel. This function gives the caller mutually
|
|
* exclusive access to the DMA channel specified by the 'dmamap' argument.
|
|
* It is common for both DMA controllers (DMA1 and DMA2).
|
|
*
|
|
* Input Parameters:
|
|
* dmamap - Identifies the stream/channel resource. For the STM32L4+, this
|
|
* is a bit-encoded value as provided by the DMAMAP_* definitions
|
|
* in hardware/stm32l4xrxx_dmamux.h
|
|
*
|
|
* Returned Value:
|
|
* On success, this function returns a non-NULL, void* DMA channel handle.
|
|
* NULL is returned on any failure. This function can fail only if no DMA
|
|
* channel is available.
|
|
*
|
|
* 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 stm32l4_dmachannel(unsigned int dmamap)
|
|
{
|
|
DMA_CHANNEL dmachan;
|
|
uint8_t dmamux_req;
|
|
irqstate_t flags;
|
|
uint8_t controller;
|
|
uint8_t first = 0;
|
|
uint8_t nchan = 0;
|
|
int item = -1;
|
|
int i;
|
|
|
|
/* Get DMA controller from encoded DMAMAP value */
|
|
|
|
controller = DMAMAP_CONTROLLER(dmamap);
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
/* Get DMAMUX channel from encoded DMAMAP value */
|
|
|
|
dmamux_req = DMAMAP_REQUEST(dmamap);
|
|
|
|
/* Get g_dma array limits for given controller */
|
|
|
|
stm32l4_gdma_limits_get(controller, &first, &nchan);
|
|
|
|
/* Find available channel for given controller */
|
|
|
|
flags = enter_critical_section();
|
|
for (i = first; i < first + nchan; i += 1)
|
|
{
|
|
if (g_dmach[i].used == false)
|
|
{
|
|
item = i;
|
|
g_dmach[i].used = true;
|
|
g_dmach[i].dmamux_req = dmamux_req;
|
|
break;
|
|
}
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
|
|
dmainfo("ctrl=%d item=%d\n", controller, item);
|
|
|
|
if (item == -1)
|
|
{
|
|
dmainfo("No available DMA chan for CTRL=%d\n",
|
|
controller);
|
|
|
|
/* No available channel */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Assign DMA item */
|
|
|
|
dmachan = &g_dmach[item];
|
|
|
|
dmainfo("Get g_dmach[%d] CTRL=%d CH=%d\n", i, controller, dmachan->chan);
|
|
|
|
/* Be sure that we have proper DMA controller */
|
|
|
|
DEBUGASSERT(dmachan->ctrl == controller);
|
|
|
|
return (DMA_HANDLE)dmachan;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmafree
|
|
*
|
|
* Description:
|
|
* Release a DMA channel and unmap DMAMUX if required.
|
|
*
|
|
* NOTE: The 'handle' used in this argument must NEVER be used again
|
|
* until stm32l4_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 stm32l4_dmafree(DMA_HANDLE handle)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
uint8_t controller;
|
|
irqstate_t flags;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Get DMA controller */
|
|
|
|
controller = dmachan->ctrl;
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
dmainfo("Free g_dmach[%d] CTRL=%d CH=%d\n", dmachan - g_dmach, controller,
|
|
dmachan->chan);
|
|
UNUSED(controller);
|
|
|
|
/* Release the channel */
|
|
|
|
flags = enter_critical_section();
|
|
dmachan->used = false;
|
|
dmachan->dmamux_req = 0;
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmasetup
|
|
*
|
|
* Description:
|
|
* Configure DMA before using
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32l4_dmasetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
|
|
size_t ntransfers, uint32_t ccr)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
uint8_t controller;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Get DMA controller */
|
|
|
|
controller = dmachan->ctrl;
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
g_dma_ops[controller].dma_setup(handle, paddr, maddr, ntransfers, ccr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmastart
|
|
*
|
|
* Description:
|
|
* Start the DMA transfer
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32l4_dmachannel()
|
|
* - No DMA in progress
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32l4_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg,
|
|
bool half)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
DMA_MUX dmamux;
|
|
uint32_t regval;
|
|
uint8_t dmamux_chan;
|
|
uint8_t controller;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Get DMA controller */
|
|
|
|
controller = dmachan->ctrl;
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
/* Recommended channel configure procedure in reference manual:
|
|
* 1. Set and configure the DMA channel y, except enabling the channel y.
|
|
* 2. Set and configure the related DMAMUX y channel.
|
|
* 3. Last, activate the DMA channel y.
|
|
*/
|
|
|
|
/* Get DMAMUX associated with DMA controller */
|
|
|
|
dmamux = g_dma[controller].dmamux;
|
|
dmamux_chan = dmachan->chan + g_dma[controller].dmamux_offset;
|
|
|
|
/* DMAMUX Set DMA channel source */
|
|
|
|
regval = dmachan->dmamux_req << DMAMUX_CCR_DMAREQID_SHIFT;
|
|
dmamux_putreg(dmamux, STM32L4_DMAMUX_CXCR_OFFSET(dmamux_chan), regval);
|
|
|
|
/* Enable DMA channel */
|
|
|
|
g_dma_ops[controller].dma_start(handle, callback, arg, half);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmastop
|
|
*
|
|
* Description:
|
|
* Cancel the DMA. After stm32l4_dmastop() is called, the DMA channel is
|
|
* reset and stm32l4_dmasetup() must be called before stm32l4_dmastart()
|
|
* can be called again
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32l4_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
void stm32l4_dmastop(DMA_HANDLE handle)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
DMA_MUX dmamux;
|
|
uint8_t dmamux_chan;
|
|
uint8_t controller;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Get DMA controller */
|
|
|
|
controller = dmachan->ctrl;
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
/* Get DMAMUX associated with DMA controller */
|
|
|
|
dmamux = g_dma[controller].dmamux;
|
|
dmamux_chan = dmachan->chan + g_dma[controller].dmamux_offset;
|
|
|
|
/* Disable DMA channel */
|
|
|
|
g_dma_ops[controller].dma_disable(dmachan);
|
|
|
|
/* DMAMUX Clear DMA channel source */
|
|
|
|
dmamux_putreg(dmamux, STM32L4_DMAMUX_CXCR_OFFSET(dmamux_chan), 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmaresidual
|
|
*
|
|
* Description:
|
|
* Read the DMA bytes-remaining register.
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32l4_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
size_t stm32l4_dmaresidual(DMA_HANDLE handle)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
uint8_t controller;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Get DMA controller */
|
|
|
|
controller = dmachan->ctrl;
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
return g_dma_ops[controller].dma_residual(handle);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_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.
|
|
*
|
|
* Input Parameters:
|
|
* cfg - DMA transfer configuration
|
|
*
|
|
* Returned Value:
|
|
* True, if transfer is possible.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_STM32L4_DMACAPABLE
|
|
bool stm32l4_dmacapable(uint32_t maddr, uint32_t count, uint32_t ccr)
|
|
{
|
|
unsigned int msize_shift;
|
|
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.
|
|
*
|
|
* Datasheet 3.13 claims
|
|
* "Access to Flash, SRAM, APB and AHB peripherals as source
|
|
* and destination"
|
|
*/
|
|
|
|
switch (ccr & DMA_CCR_MSIZE_MASK)
|
|
{
|
|
case DMA_CCR_MSIZE_8BITS:
|
|
msize_shift = 0;
|
|
break;
|
|
|
|
case DMA_CCR_MSIZE_16BITS:
|
|
msize_shift = 1;
|
|
break;
|
|
|
|
case DMA_CCR_MSIZE_32BITS:
|
|
msize_shift = 2;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
transfer_size = 1 << msize_shift;
|
|
|
|
if ((maddr & (transfer_size - 1)) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* Verify that the transfer is to a memory region that supports DMA. */
|
|
|
|
mend = maddr + (count << msize_shift) - 1;
|
|
|
|
if ((maddr & STM32L4_REGION_MASK) != (mend & STM32L4_REGION_MASK))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (maddr & STM32L4_REGION_MASK)
|
|
{
|
|
case STM32L4_PERIPH_BASE:
|
|
case STM32L4_FSMC_BASE:
|
|
case STM32L4_FSMC_BANK1:
|
|
case STM32L4_FSMC_BANK2:
|
|
case STM32L4_FSMC_BANK3:
|
|
case STM32L4_QSPI_BANK:
|
|
case STM32L4_SRAM_BASE:
|
|
case STM32L4_SRAM2_BASE:
|
|
case STM32L4_SRAM3_BASE:
|
|
case STM32L4_CODE_BASE:
|
|
|
|
/* All RAM and flash is supported */
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
/* Everything else is unsupported by DMA */
|
|
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmasample
|
|
*
|
|
* Description:
|
|
* Sample DMA register contents
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32l4_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
void stm32l4_dmasample(DMA_HANDLE handle, struct stm32l4_dmaregs_s *regs)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
uint8_t controller;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Get DMA controller */
|
|
|
|
controller = dmachan->ctrl;
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
g_dma_ops[controller].dma_sample(handle, regs);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: stm32l4_dmadump
|
|
*
|
|
* Description:
|
|
* Dump previously sampled DMA register contents
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by stm32l4_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA_INFO
|
|
void stm32l4_dmadump(DMA_HANDLE handle, const struct stm32l4_dmaregs_s *regs,
|
|
const char *msg)
|
|
{
|
|
DMA_CHANNEL dmachan = (DMA_CHANNEL)handle;
|
|
uint8_t controller;
|
|
|
|
DEBUGASSERT(handle != NULL);
|
|
|
|
/* Get DMA controller */
|
|
|
|
controller = dmachan->ctrl;
|
|
DEBUGASSERT(controller >= DMA1 && controller <= DMA2);
|
|
|
|
dmainfo("DMA %d CH%d Registers: %s\n", dmachan->ctrl, dmachan->ctrl, msg);
|
|
|
|
g_dma_ops[controller].dma_dump(handle, regs, msg);
|
|
}
|
|
#endif
|