350 lines
7.7 KiB
C
350 lines
7.7 KiB
C
/*
|
|
* Copyright (c) 2019 Song Qiang <songqiang1304521@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @brief DMA low level driver implementation for F2/F4/F7 series SoCs.
|
|
*/
|
|
|
|
#include <drivers/dma.h>
|
|
#include <soc.h>
|
|
|
|
#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(dma_stm32_v1);
|
|
|
|
/* DMA burst length */
|
|
#define BURST_TRANS_LENGTH_1 0
|
|
|
|
uint32_t table_ll_stream[] = {
|
|
LL_DMA_STREAM_0,
|
|
LL_DMA_STREAM_1,
|
|
LL_DMA_STREAM_2,
|
|
LL_DMA_STREAM_3,
|
|
LL_DMA_STREAM_4,
|
|
LL_DMA_STREAM_5,
|
|
LL_DMA_STREAM_6,
|
|
LL_DMA_STREAM_7,
|
|
};
|
|
|
|
uint32_t table_ll_channel[] = {
|
|
LL_DMA_CHANNEL_0,
|
|
LL_DMA_CHANNEL_1,
|
|
LL_DMA_CHANNEL_2,
|
|
LL_DMA_CHANNEL_3,
|
|
LL_DMA_CHANNEL_4,
|
|
LL_DMA_CHANNEL_5,
|
|
LL_DMA_CHANNEL_6,
|
|
LL_DMA_CHANNEL_7,
|
|
};
|
|
|
|
void (*func_ll_clear_ht[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_ClearFlag_HT0,
|
|
LL_DMA_ClearFlag_HT1,
|
|
LL_DMA_ClearFlag_HT2,
|
|
LL_DMA_ClearFlag_HT3,
|
|
LL_DMA_ClearFlag_HT4,
|
|
LL_DMA_ClearFlag_HT5,
|
|
LL_DMA_ClearFlag_HT6,
|
|
LL_DMA_ClearFlag_HT7,
|
|
};
|
|
|
|
void (*func_ll_clear_tc[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_ClearFlag_TC0,
|
|
LL_DMA_ClearFlag_TC1,
|
|
LL_DMA_ClearFlag_TC2,
|
|
LL_DMA_ClearFlag_TC3,
|
|
LL_DMA_ClearFlag_TC4,
|
|
LL_DMA_ClearFlag_TC5,
|
|
LL_DMA_ClearFlag_TC6,
|
|
LL_DMA_ClearFlag_TC7,
|
|
};
|
|
|
|
uint32_t (*func_ll_is_active_ht[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_IsActiveFlag_HT0,
|
|
LL_DMA_IsActiveFlag_HT1,
|
|
LL_DMA_IsActiveFlag_HT2,
|
|
LL_DMA_IsActiveFlag_HT3,
|
|
LL_DMA_IsActiveFlag_HT4,
|
|
LL_DMA_IsActiveFlag_HT5,
|
|
LL_DMA_IsActiveFlag_HT6,
|
|
LL_DMA_IsActiveFlag_HT7,
|
|
};
|
|
|
|
uint32_t (*func_ll_is_active_tc[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_IsActiveFlag_TC0,
|
|
LL_DMA_IsActiveFlag_TC1,
|
|
LL_DMA_IsActiveFlag_TC2,
|
|
LL_DMA_IsActiveFlag_TC3,
|
|
LL_DMA_IsActiveFlag_TC4,
|
|
LL_DMA_IsActiveFlag_TC5,
|
|
LL_DMA_IsActiveFlag_TC6,
|
|
LL_DMA_IsActiveFlag_TC7,
|
|
};
|
|
|
|
static void (*func_ll_clear_te[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_ClearFlag_TE0,
|
|
LL_DMA_ClearFlag_TE1,
|
|
LL_DMA_ClearFlag_TE2,
|
|
LL_DMA_ClearFlag_TE3,
|
|
LL_DMA_ClearFlag_TE4,
|
|
LL_DMA_ClearFlag_TE5,
|
|
LL_DMA_ClearFlag_TE6,
|
|
LL_DMA_ClearFlag_TE7,
|
|
};
|
|
|
|
static void (*func_ll_clear_dme[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_ClearFlag_DME0,
|
|
LL_DMA_ClearFlag_DME1,
|
|
LL_DMA_ClearFlag_DME2,
|
|
LL_DMA_ClearFlag_DME3,
|
|
LL_DMA_ClearFlag_DME4,
|
|
LL_DMA_ClearFlag_DME5,
|
|
LL_DMA_ClearFlag_DME6,
|
|
LL_DMA_ClearFlag_DME7,
|
|
};
|
|
|
|
static void (*func_ll_clear_fe[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_ClearFlag_FE0,
|
|
LL_DMA_ClearFlag_FE1,
|
|
LL_DMA_ClearFlag_FE2,
|
|
LL_DMA_ClearFlag_FE3,
|
|
LL_DMA_ClearFlag_FE4,
|
|
LL_DMA_ClearFlag_FE5,
|
|
LL_DMA_ClearFlag_FE6,
|
|
LL_DMA_ClearFlag_FE7,
|
|
};
|
|
|
|
static uint32_t (*func_ll_is_active_te[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_IsActiveFlag_TE0,
|
|
LL_DMA_IsActiveFlag_TE1,
|
|
LL_DMA_IsActiveFlag_TE2,
|
|
LL_DMA_IsActiveFlag_TE3,
|
|
LL_DMA_IsActiveFlag_TE4,
|
|
LL_DMA_IsActiveFlag_TE5,
|
|
LL_DMA_IsActiveFlag_TE6,
|
|
LL_DMA_IsActiveFlag_TE7,
|
|
};
|
|
|
|
static uint32_t (*func_ll_is_active_dme[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_IsActiveFlag_DME0,
|
|
LL_DMA_IsActiveFlag_DME1,
|
|
LL_DMA_IsActiveFlag_DME2,
|
|
LL_DMA_IsActiveFlag_DME3,
|
|
LL_DMA_IsActiveFlag_DME4,
|
|
LL_DMA_IsActiveFlag_DME5,
|
|
LL_DMA_IsActiveFlag_DME6,
|
|
LL_DMA_IsActiveFlag_DME7,
|
|
};
|
|
|
|
static uint32_t (*func_ll_is_active_fe[])(DMA_TypeDef *DMAx) = {
|
|
LL_DMA_IsActiveFlag_FE0,
|
|
LL_DMA_IsActiveFlag_FE1,
|
|
LL_DMA_IsActiveFlag_FE2,
|
|
LL_DMA_IsActiveFlag_FE3,
|
|
LL_DMA_IsActiveFlag_FE4,
|
|
LL_DMA_IsActiveFlag_FE5,
|
|
LL_DMA_IsActiveFlag_FE6,
|
|
LL_DMA_IsActiveFlag_FE7,
|
|
};
|
|
|
|
void stm32_dma_dump_stream_irq(DMA_TypeDef *dma, uint32_t id)
|
|
{
|
|
LOG_INF("tc: %d, ht: %d, te: %d, dme: %d, fe: %d",
|
|
func_ll_is_active_tc[id](dma),
|
|
func_ll_is_active_ht[id](dma),
|
|
func_ll_is_active_te[id](dma),
|
|
func_ll_is_active_dme[id](dma),
|
|
func_ll_is_active_fe[id](dma));
|
|
}
|
|
|
|
void stm32_dma_clear_stream_irq(DMA_TypeDef *dma, uint32_t id)
|
|
{
|
|
func_ll_clear_te[id](dma);
|
|
func_ll_clear_dme[id](dma);
|
|
func_ll_clear_fe[id](dma);
|
|
}
|
|
|
|
bool stm32_dma_is_irq_happened(DMA_TypeDef *dma, uint32_t id)
|
|
{
|
|
if (func_ll_is_active_fe[id](dma) && LL_DMA_IsEnabledIT_FE(dma, id)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool stm32_dma_is_unexpected_irq_happened(DMA_TypeDef *dma, uint32_t id)
|
|
{
|
|
if (func_ll_is_active_fe[id](dma) && LL_DMA_IsEnabledIT_FE(dma, id)) {
|
|
LOG_ERR("FiFo error.");
|
|
stm32_dma_dump_stream_irq(dma, id);
|
|
stm32_dma_clear_stream_irq(dma, id);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void stm32_dma_enable_stream(DMA_TypeDef *dma, uint32_t id)
|
|
{
|
|
LL_DMA_EnableStream(dma, table_ll_stream[id]);
|
|
}
|
|
|
|
int stm32_dma_disable_stream(DMA_TypeDef *dma, uint32_t id)
|
|
{
|
|
|
|
if (!LL_DMA_IsEnabledStream(dma, table_ll_stream[id])) {
|
|
return 0;
|
|
}
|
|
LL_DMA_DisableStream(dma, table_ll_stream[id]);
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
void stm32_dma_disable_fifo_irq(DMA_TypeDef *dma, uint32_t id)
|
|
{
|
|
LL_DMA_DisableIT_FE(dma, table_ll_stream[id]);
|
|
}
|
|
|
|
void stm32_dma_config_channel_function(DMA_TypeDef *dma, uint32_t id, uint32_t slot)
|
|
{
|
|
LL_DMA_SetChannelSelection(dma, table_ll_stream[id],
|
|
table_ll_channel[slot]);
|
|
}
|
|
|
|
uint32_t stm32_dma_get_mburst(struct dma_config *config, bool source_periph)
|
|
{
|
|
uint32_t memory_burst;
|
|
|
|
if (source_periph) {
|
|
memory_burst = config->dest_burst_length;
|
|
} else {
|
|
memory_burst = config->source_burst_length;
|
|
}
|
|
|
|
switch (memory_burst) {
|
|
case 1:
|
|
return LL_DMA_MBURST_SINGLE;
|
|
case 4:
|
|
return LL_DMA_MBURST_INC4;
|
|
case 8:
|
|
return LL_DMA_MBURST_INC8;
|
|
case 16:
|
|
return LL_DMA_MBURST_INC16;
|
|
default:
|
|
LOG_ERR("Memory burst size error,"
|
|
"using single burst as default");
|
|
return LL_DMA_MBURST_SINGLE;
|
|
}
|
|
}
|
|
|
|
uint32_t stm32_dma_get_pburst(struct dma_config *config, bool source_periph)
|
|
{
|
|
uint32_t periph_burst;
|
|
|
|
if (source_periph) {
|
|
periph_burst = config->source_burst_length;
|
|
} else {
|
|
periph_burst = config->dest_burst_length;
|
|
}
|
|
|
|
switch (periph_burst) {
|
|
case 1:
|
|
return LL_DMA_PBURST_SINGLE;
|
|
case 4:
|
|
return LL_DMA_PBURST_INC4;
|
|
case 8:
|
|
return LL_DMA_PBURST_INC8;
|
|
case 16:
|
|
return LL_DMA_PBURST_INC16;
|
|
default:
|
|
LOG_ERR("Peripheral burst size error,"
|
|
"using single burst as default");
|
|
return LL_DMA_PBURST_SINGLE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function checks if the msize, mburst and fifo level is
|
|
* compitable. If they are not compitable, refer to the 'FIFO'
|
|
* section in the 'DMA' chapter in the Reference Manual for more
|
|
* information.
|
|
* break is emitted since every path of the code has 'return'.
|
|
* This function does not have the obligation of checking the parameters.
|
|
*/
|
|
bool stm32_dma_check_fifo_mburst(LL_DMA_InitTypeDef *DMAx)
|
|
{
|
|
uint32_t msize = DMAx->MemoryOrM2MDstDataSize;
|
|
uint32_t fifo_level = DMAx->FIFOThreshold;
|
|
uint32_t mburst = DMAx->MemBurst;
|
|
|
|
switch (msize) {
|
|
case LL_DMA_MDATAALIGN_BYTE:
|
|
switch (mburst) {
|
|
case LL_DMA_MBURST_INC4:
|
|
return true;
|
|
case LL_DMA_MBURST_INC8:
|
|
if (fifo_level == LL_DMA_FIFOTHRESHOLD_1_2 ||
|
|
fifo_level == LL_DMA_FIFOTHRESHOLD_FULL) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
case LL_DMA_MBURST_INC16:
|
|
if (fifo_level == LL_DMA_FIFOTHRESHOLD_FULL) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
case LL_DMA_MDATAALIGN_HALFWORD:
|
|
switch (mburst) {
|
|
case LL_DMA_MBURST_INC4:
|
|
if (fifo_level == LL_DMA_FIFOTHRESHOLD_1_2 ||
|
|
fifo_level == LL_DMA_FIFOTHRESHOLD_FULL) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
case LL_DMA_MBURST_INC8:
|
|
if (fifo_level == LL_DMA_FIFOTHRESHOLD_FULL) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
case LL_DMA_MBURST_INC16:
|
|
return false;
|
|
}
|
|
case LL_DMA_MDATAALIGN_WORD:
|
|
if (mburst == LL_DMA_MBURST_INC4 &&
|
|
fifo_level == LL_DMA_FIFOTHRESHOLD_FULL) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint32_t stm32_dma_get_fifo_threshold(uint16_t fifo_mode_control)
|
|
{
|
|
switch (fifo_mode_control) {
|
|
case 0:
|
|
return LL_DMA_FIFOTHRESHOLD_1_4;
|
|
case 1:
|
|
return LL_DMA_FIFOTHRESHOLD_1_2;
|
|
case 2:
|
|
return LL_DMA_FIFOTHRESHOLD_3_4;
|
|
case 3:
|
|
return LL_DMA_FIFOTHRESHOLD_FULL;
|
|
default:
|
|
LOG_WRN("FIFO threshold parameter error, reset to 1/4");
|
|
return LL_DMA_FIFOTHRESHOLD_1_4;
|
|
}
|
|
}
|