1042 lines
27 KiB
C
1042 lines
27 KiB
C
/***************************************************************************
|
|
*
|
|
* Copyright 2015-2019 BES.
|
|
* All rights reserved. All unpublished rights reserved.
|
|
*
|
|
* No part of this work may be used or reproduced in any form or by any
|
|
* means, or stored in a database or retrieval system, without prior written
|
|
* permission of BES.
|
|
*
|
|
* Use of this work is governed by a license granted by BES.
|
|
* This work contains confidential and proprietary information of
|
|
* BES. which is protected by copyright, trade secret,
|
|
* trademark and other intellectual property rights.
|
|
*
|
|
****************************************************************************/
|
|
#include "hal_dma.h"
|
|
#include "cmsis_nvic.h"
|
|
#include "hal_chipid.h"
|
|
#include "hal_cmu.h"
|
|
#include "hal_location.h"
|
|
#include "hal_timer.h"
|
|
#include "hal_trace.h"
|
|
#include "plat_addr_map.h"
|
|
#include "reg_dma.h"
|
|
|
|
#if (defined(CHIP_BEST1000) || defined(CHIP_BEST2000) || \
|
|
defined(CHIP_BEST2300)) && \
|
|
!(defined(ROM_BUILD) || defined(PROGRAMMER))
|
|
#define DMA_REMAP
|
|
#endif
|
|
|
|
enum HAL_DMA_INST_T {
|
|
HAL_DMA_INST_AUDMA = 0,
|
|
#if (CHIP_HAS_DMA == 1)
|
|
HAL_DMA_INST_GPDMA = HAL_DMA_INST_AUDMA,
|
|
#else
|
|
HAL_DMA_INST_GPDMA,
|
|
#endif
|
|
|
|
HAL_DMA_INST_QTY
|
|
};
|
|
|
|
struct HAL_DMA_FIFO_ADDR_T {
|
|
uint32_t count;
|
|
const uint32_t *addr;
|
|
};
|
|
|
|
struct HAL_DMA_FIFO_PERIPH_T {
|
|
uint32_t count;
|
|
const enum HAL_DMA_PERIPH_T *periph;
|
|
#ifdef DMA_REMAP
|
|
const enum HAL_DMA_PERIPH_T *periph_remap;
|
|
#endif
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* Private types/enumerations/variables
|
|
****************************************************************************/
|
|
|
|
#include CHIP_SPECIFIC_HDR(hal_dmacfg)
|
|
|
|
static struct DMA_T *const dma[HAL_DMA_INST_QTY] = {
|
|
(struct DMA_T *)AUDMA_BASE,
|
|
#if (CHIP_HAS_DMA > 1)
|
|
(struct DMA_T *)GPDMA_BASE,
|
|
#endif
|
|
};
|
|
|
|
static const IRQn_Type irq_type[HAL_DMA_INST_QTY] = {
|
|
AUDMA_IRQn,
|
|
#if (CHIP_HAS_DMA > 1)
|
|
GPDMA_IRQn,
|
|
#endif
|
|
};
|
|
|
|
static void hal_audma_irq_handler(void);
|
|
#if (CHIP_HAS_DMA > 1)
|
|
static void hal_gpdma_irq_handler(void);
|
|
#endif
|
|
|
|
static const uint32_t irq_entry[HAL_DMA_INST_QTY] = {
|
|
(uint32_t)hal_audma_irq_handler,
|
|
#if (CHIP_HAS_DMA > 1)
|
|
(uint32_t)hal_gpdma_irq_handler,
|
|
#endif
|
|
};
|
|
|
|
static const struct HAL_DMA_FIFO_ADDR_T fifo_addr[HAL_DMA_INST_QTY] = {
|
|
{
|
|
.count = ARRAY_SIZE(audma_fifo_addr),
|
|
.addr = audma_fifo_addr,
|
|
},
|
|
#if (CHIP_HAS_DMA > 1)
|
|
{
|
|
.count = ARRAY_SIZE(gpdma_fifo_addr),
|
|
.addr = gpdma_fifo_addr,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static const struct HAL_DMA_FIFO_PERIPH_T fifo_periph[HAL_DMA_INST_QTY] = {
|
|
{
|
|
.count = ARRAY_SIZE(audma_fifo_periph),
|
|
.periph = audma_fifo_periph,
|
|
#ifdef DMA_REMAP
|
|
.periph_remap = audma_fifo_periph_remap,
|
|
#endif
|
|
},
|
|
#if (CHIP_HAS_DMA > 1)
|
|
{
|
|
.count = ARRAY_SIZE(gpdma_fifo_periph),
|
|
.periph = gpdma_fifo_periph,
|
|
#ifdef DMA_REMAP
|
|
.periph_remap = gpdma_fifo_periph_remap,
|
|
#endif
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static const uint8_t chan_start[HAL_DMA_INST_QTY] = {
|
|
AUDMA_CHAN_START,
|
|
#if (CHIP_HAS_DMA > 1)
|
|
GPDMA_CHAN_START,
|
|
#endif
|
|
};
|
|
|
|
static const uint8_t chan_num[HAL_DMA_INST_QTY] = {
|
|
AUDMA_CHAN_NUM,
|
|
#if (CHIP_HAS_DMA > 1)
|
|
GPDMA_CHAN_NUM,
|
|
#endif
|
|
};
|
|
|
|
/* Channel array to monitor free channel */
|
|
static bool chan_enabled[HAL_DMA_INST_QTY][DMA_NUMBER_CHANNELS];
|
|
|
|
static HAL_DMA_IRQ_HANDLER_T handler[HAL_DMA_INST_QTY][DMA_NUMBER_CHANNELS];
|
|
|
|
#ifdef DMA_REMAP
|
|
static uint32_t periph_remap_bitmap[HAL_DMA_INST_QTY];
|
|
#endif
|
|
|
|
#ifdef CORE_SLEEP_POWER_DOWN
|
|
static uint32_t saved_dma_regs[HAL_DMA_INST_QTY];
|
|
#endif
|
|
|
|
static const char *const err_invalid_inst = "Invalid DMA inst: %u";
|
|
|
|
static const char *const err_invalid_chan[HAL_DMA_INST_QTY] = {
|
|
"Invalid AUDMA chan: %u",
|
|
#if (CHIP_HAS_DMA > 1)
|
|
"Invalid GPDMA chan: %u",
|
|
#endif
|
|
};
|
|
|
|
static bool dma_opened = false;
|
|
|
|
static HAL_DMA_DELAY_FUNC dma_delay = NULL;
|
|
|
|
/*****************************************************************************
|
|
* Public types/enumerations/variables
|
|
****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Private functions
|
|
****************************************************************************/
|
|
static void hal_dma_delay(uint32_t ms) {
|
|
if (dma_delay && !in_isr()) {
|
|
dma_delay(ms);
|
|
} else {
|
|
hal_sys_timer_delay(MS_TO_TICKS(ms));
|
|
}
|
|
}
|
|
|
|
static inline uint8_t generate_chan(enum HAL_DMA_INST_T inst, uint8_t hwch) {
|
|
return ((inst << 4) | (hwch & 0xF));
|
|
}
|
|
|
|
static inline enum HAL_DMA_INST_T get_inst_from_chan(uint8_t ch) {
|
|
return (enum HAL_DMA_INST_T)(ch >> 4);
|
|
}
|
|
|
|
static inline uint8_t get_hwch_from_chan(uint8_t ch) { return (ch & 0xF); }
|
|
|
|
static inline int get_index_from_periph(enum HAL_DMA_PERIPH_T periph,
|
|
enum HAL_DMA_INST_T *pinst,
|
|
uint8_t *pidx) {
|
|
enum HAL_DMA_INST_T inst;
|
|
int i;
|
|
|
|
for (inst = 0; inst < HAL_DMA_INST_QTY; inst++) {
|
|
for (i = 0; i < fifo_periph[inst].count; i++) {
|
|
if (fifo_periph[inst].periph[i] == periph) {
|
|
*pinst = inst;
|
|
*pidx = i;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef DMA_REMAP
|
|
static inline int get_remap_index_from_periph(enum HAL_DMA_INST_T inst,
|
|
enum HAL_DMA_PERIPH_T periph,
|
|
uint8_t *pidx) {
|
|
int i;
|
|
|
|
if (fifo_periph[inst].periph_remap == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i < fifo_periph[inst].count; i++) {
|
|
if (fifo_periph[inst].periph_remap[i] == periph) {
|
|
*pidx = i;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
#endif
|
|
|
|
static inline uint32_t hal_dma_get_periph_addr(enum HAL_DMA_PERIPH_T periph) {
|
|
int ret;
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t index;
|
|
|
|
ret = get_index_from_periph(periph, &inst, &index);
|
|
if (ret) {
|
|
return 0;
|
|
}
|
|
|
|
return fifo_addr[inst].addr[index];
|
|
}
|
|
|
|
/* Initialize the DMA */
|
|
static void hal_dma_open_inst(enum HAL_DMA_INST_T inst) {
|
|
uint8_t i;
|
|
|
|
/* Reset all channel configuration register */
|
|
for (i = 0; i < DMA_NUMBER_CHANNELS; i++) {
|
|
dma[inst]->CH[i].CONFIG = 0;
|
|
}
|
|
|
|
/* Clear all DMA interrupt and error flag */
|
|
dma[inst]->INTTCCLR = ~0UL;
|
|
dma[inst]->INTERRCLR = ~0UL;
|
|
|
|
dma[inst]->DMACONFIG =
|
|
(dma[inst]->DMACONFIG &
|
|
~(DMA_DMACONFIG_AHB1_BIGENDIAN | DMA_DMACONFIG_AHB2_BIGENDIAN |
|
|
DMA_DMACONFIG_CLK_EN_MASK)) |
|
|
DMA_DMACONFIG_EN |
|
|
#ifdef CHIP_BEST3001
|
|
DMA_DMACONFIG_TC_IRQ_EN_MASK |
|
|
#endif
|
|
0;
|
|
|
|
#ifdef CHIP_BEST1400
|
|
dma[inst]->DMACONFIG &= ~DMA_DMACONFIG_TC_IRQ_EN_MASK;
|
|
#endif
|
|
|
|
/* Reset all channels are free */
|
|
for (i = 0; i < DMA_NUMBER_CHANNELS; i++) {
|
|
chan_enabled[inst][i] = false;
|
|
}
|
|
|
|
NVIC_SetVector(irq_type[inst], irq_entry[inst]);
|
|
if (inst == HAL_DMA_INST_AUDMA) {
|
|
NVIC_SetPriority(irq_type[inst], IRQ_PRIORITY_ABOVENORMAL);
|
|
} else {
|
|
NVIC_SetPriority(irq_type[inst], IRQ_PRIORITY_NORMAL);
|
|
}
|
|
NVIC_ClearPendingIRQ(irq_type[inst]);
|
|
NVIC_EnableIRQ(irq_type[inst]);
|
|
}
|
|
|
|
/* Shutdown the DMA */
|
|
static void hal_dma_close_inst(enum HAL_DMA_INST_T inst) {
|
|
NVIC_DisableIRQ(irq_type[inst]);
|
|
dma[inst]->DMACONFIG = 0;
|
|
}
|
|
|
|
static bool hal_dma_chan_busy_inst(enum HAL_DMA_INST_T inst, uint8_t hwch) {
|
|
return !!(dma[inst]->ENBLDCHNS & DMA_STAT_CHAN(hwch));
|
|
}
|
|
|
|
static void hal_dma_handle_chan_irq(enum HAL_DMA_INST_T inst, uint8_t hwch) {
|
|
uint32_t remains;
|
|
struct HAL_DMA_DESC_T *lli;
|
|
bool tcint, errint;
|
|
|
|
/* Check counter terminal status */
|
|
tcint = !!(dma[inst]->INTTCSTAT & DMA_STAT_CHAN(hwch));
|
|
/* Check error terminal status */
|
|
errint = !!(dma[inst]->INTERRSTAT & DMA_STAT_CHAN(hwch));
|
|
|
|
if (tcint || errint) {
|
|
if (tcint) {
|
|
/* Clear terminate counter Interrupt pending */
|
|
dma[inst]->INTTCCLR = DMA_STAT_CHAN(hwch);
|
|
}
|
|
if (errint) {
|
|
/* Clear error counter Interrupt pending */
|
|
dma[inst]->INTERRCLR = DMA_STAT_CHAN(hwch);
|
|
}
|
|
|
|
if (handler[inst][hwch]) {
|
|
remains =
|
|
GET_BITFIELD(dma[inst]->CH[hwch].CONTROL, DMA_CONTROL_TRANSFERSIZE);
|
|
lli = (struct HAL_DMA_DESC_T *)dma[inst]->CH[hwch].LLI;
|
|
handler[inst][hwch](generate_chan(inst, hwch), remains, errint, lli);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hal_dma_irq_handler(enum HAL_DMA_INST_T inst) {
|
|
uint8_t hwch;
|
|
|
|
for (hwch = 0; hwch < DMA_NUMBER_CHANNELS; hwch++) {
|
|
if ((dma[inst]->INTSTAT & DMA_STAT_CHAN(hwch)) == 0) {
|
|
continue;
|
|
}
|
|
hal_dma_handle_chan_irq(inst, hwch);
|
|
}
|
|
}
|
|
|
|
static void hal_audma_irq_handler(void) {
|
|
hal_dma_irq_handler(HAL_DMA_INST_AUDMA);
|
|
}
|
|
|
|
#if (CHIP_HAS_DMA > 1)
|
|
static void hal_gpdma_irq_handler(void) {
|
|
hal_dma_irq_handler(HAL_DMA_INST_GPDMA);
|
|
}
|
|
#endif
|
|
|
|
static enum HAL_DMA_RET_T
|
|
hal_dma_init_control(uint32_t *ctrl, const struct HAL_DMA_CH_CFG_T *cfg,
|
|
int tc_irq) {
|
|
uint32_t addr_inc;
|
|
enum HAL_DMA_FLOW_CONTROL_T type;
|
|
|
|
if (cfg->src_tsize > HAL_DMA_MAX_DESC_XFER_SIZE) {
|
|
return HAL_DMA_ERR;
|
|
}
|
|
|
|
#ifdef CHIP_BEST1000
|
|
type = cfg->type;
|
|
#else
|
|
type = cfg->type & ~HAL_DMA_FLOW_FLAG_MASK;
|
|
#endif
|
|
|
|
switch (type) {
|
|
case HAL_DMA_FLOW_M2M_DMA:
|
|
addr_inc = DMA_CONTROL_SI | DMA_CONTROL_DI;
|
|
break;
|
|
case HAL_DMA_FLOW_M2P_DMA:
|
|
case HAL_DMA_FLOW_M2P_PERIPH:
|
|
addr_inc = DMA_CONTROL_SI;
|
|
break;
|
|
case HAL_DMA_FLOW_P2M_DMA:
|
|
case HAL_DMA_FLOW_P2M_PERIPH:
|
|
addr_inc = DMA_CONTROL_DI;
|
|
break;
|
|
case HAL_DMA_FLOW_P2P_DMA:
|
|
case HAL_DMA_FLOW_P2P_DSTPERIPH:
|
|
case HAL_DMA_FLOW_P2P_SRCPERIPH:
|
|
addr_inc = 0;
|
|
break;
|
|
default:
|
|
return HAL_DMA_ERR;
|
|
}
|
|
|
|
#ifndef CHIP_BEST1000
|
|
if (cfg->type & HAL_DMA_FLOW_FLAG_SI) {
|
|
addr_inc |= DMA_CONTROL_SI;
|
|
}
|
|
if (cfg->type & HAL_DMA_FLOW_FLAG_DI) {
|
|
addr_inc |= DMA_CONTROL_DI;
|
|
}
|
|
#endif
|
|
|
|
*ctrl =
|
|
DMA_CONTROL_TRANSFERSIZE(cfg->src_tsize) |
|
|
DMA_CONTROL_SBSIZE(cfg->src_bsize) | DMA_CONTROL_DBSIZE(cfg->dst_bsize) |
|
|
DMA_CONTROL_SWIDTH(cfg->src_width) | DMA_CONTROL_DWIDTH(cfg->dst_width) |
|
|
(tc_irq ? DMA_CONTROL_TC_IRQ : 0) | addr_inc;
|
|
|
|
return HAL_DMA_OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Generic Public functions
|
|
****************************************************************************/
|
|
|
|
enum HAL_DMA_RET_T hal_dma_init_desc(struct HAL_DMA_DESC_T *desc,
|
|
const struct HAL_DMA_CH_CFG_T *cfg,
|
|
const struct HAL_DMA_DESC_T *next,
|
|
int tc_irq) {
|
|
uint32_t ctrl;
|
|
enum HAL_DMA_RET_T ret;
|
|
enum HAL_DMA_FLOW_CONTROL_T type;
|
|
|
|
ret = hal_dma_init_control(&ctrl, cfg, tc_irq);
|
|
if (ret != HAL_DMA_OK) {
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CHIP_BEST1000
|
|
type = cfg->type;
|
|
#else
|
|
type = cfg->type & ~HAL_DMA_FLOW_FLAG_MASK;
|
|
#endif
|
|
|
|
if (type == HAL_DMA_FLOW_M2M_DMA || type == HAL_DMA_FLOW_M2P_DMA ||
|
|
type == HAL_DMA_FLOW_M2P_PERIPH) {
|
|
desc->src = cfg->src;
|
|
} else {
|
|
desc->src = hal_dma_get_periph_addr(cfg->src_periph);
|
|
}
|
|
if (type == HAL_DMA_FLOW_M2M_DMA || type == HAL_DMA_FLOW_P2M_DMA ||
|
|
type == HAL_DMA_FLOW_P2M_PERIPH) {
|
|
desc->dst = cfg->dst;
|
|
} else {
|
|
desc->dst = hal_dma_get_periph_addr(cfg->dst_periph);
|
|
}
|
|
desc->lli = (uint32_t)next;
|
|
desc->ctrl = ctrl;
|
|
|
|
return HAL_DMA_OK;
|
|
}
|
|
|
|
enum HAL_DMA_RET_T hal_dma_sg_2d_start(const struct HAL_DMA_DESC_T *desc,
|
|
const struct HAL_DMA_CH_CFG_T *cfg,
|
|
const struct HAL_DMA_2D_CFG_T *src_2d,
|
|
const struct HAL_DMA_2D_CFG_T *dst_2d) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
enum HAL_DMA_FLOW_CONTROL_T type;
|
|
uint8_t src_periph, dst_periph;
|
|
enum HAL_DMA_INST_T periph_inst;
|
|
int ret;
|
|
uint32_t irq_mask, try_burst;
|
|
uint32_t lock;
|
|
|
|
inst = get_inst_from_chan(cfg->ch);
|
|
hwch = get_hwch_from_chan(cfg->ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
#ifdef CHIP_BEST1000
|
|
type = cfg->type;
|
|
#else
|
|
type = cfg->type & ~HAL_DMA_FLOW_FLAG_MASK;
|
|
#endif
|
|
|
|
if (type == HAL_DMA_FLOW_M2M_DMA || type == HAL_DMA_FLOW_M2P_DMA ||
|
|
type == HAL_DMA_FLOW_M2P_PERIPH) {
|
|
src_periph = 0;
|
|
} else {
|
|
ret = get_index_from_periph(cfg->src_periph, &periph_inst, &src_periph);
|
|
ASSERT(ret == 0, "Failed to get src periph: %d", cfg->src_periph);
|
|
#ifdef DMA_REMAP
|
|
if (periph_remap_bitmap[periph_inst] & (1 << src_periph)) {
|
|
periph_inst ^= 1;
|
|
ret = get_remap_index_from_periph(periph_inst, cfg->src_periph,
|
|
&src_periph);
|
|
ASSERT(ret == 0, "Failed to get remap src periph: %d", cfg->src_periph);
|
|
}
|
|
#endif
|
|
ASSERT(inst == periph_inst,
|
|
"Mismatch inst in chan=0x%02x and src periph %d", cfg->ch,
|
|
cfg->src_periph);
|
|
}
|
|
if (type == HAL_DMA_FLOW_M2M_DMA || type == HAL_DMA_FLOW_P2M_DMA ||
|
|
type == HAL_DMA_FLOW_P2M_PERIPH) {
|
|
dst_periph = 0;
|
|
} else {
|
|
ret = get_index_from_periph(cfg->dst_periph, &periph_inst, &dst_periph);
|
|
ASSERT(ret == 0, "Failed to get dst periph: %d", cfg->dst_periph);
|
|
#ifdef DMA_REMAP
|
|
if (periph_remap_bitmap[periph_inst] & (1 << dst_periph)) {
|
|
periph_inst ^= 1;
|
|
ret = get_remap_index_from_periph(periph_inst, cfg->dst_periph,
|
|
&dst_periph);
|
|
ASSERT(ret == 0, "Failed to get remap dst periph: %d", cfg->dst_periph);
|
|
}
|
|
#endif
|
|
ASSERT(inst == periph_inst,
|
|
"Mismatch inst in chan=0x%02x and dst periph %d", cfg->ch,
|
|
cfg->dst_periph);
|
|
}
|
|
|
|
if (!chan_enabled[inst][hwch]) {
|
|
// Not acquired
|
|
return HAL_DMA_ERR;
|
|
}
|
|
if (hal_dma_chan_busy_inst(inst, hwch)) {
|
|
// Busy
|
|
return HAL_DMA_ERR;
|
|
}
|
|
|
|
if (cfg->handler == NULL) {
|
|
irq_mask = 0;
|
|
} else {
|
|
irq_mask = DMA_CONFIG_ERR_IRQMASK | DMA_CONFIG_TC_IRQMASK;
|
|
handler[inst][hwch] = cfg->handler;
|
|
}
|
|
|
|
try_burst = cfg->try_burst ? DMA_CONFIG_TRY_BURST : 0;
|
|
|
|
/* Reset the Interrupt status */
|
|
dma[inst]->INTTCCLR = DMA_STAT_CHAN(hwch);
|
|
dma[inst]->INTERRCLR = DMA_STAT_CHAN(hwch);
|
|
|
|
dma[inst]->CH[hwch].SRCADDR = desc->src;
|
|
dma[inst]->CH[hwch].DSTADDR = desc->dst;
|
|
dma[inst]->CH[hwch].LLI = desc->lli;
|
|
dma[inst]->CH[hwch].CONTROL = desc->ctrl;
|
|
dma[inst]->CH[hwch].CONFIG =
|
|
DMA_CONFIG_SRCPERIPH(src_periph) | DMA_CONFIG_DSTPERIPH(dst_periph) |
|
|
DMA_CONFIG_TRANSFERTYPE(type) | irq_mask | try_burst;
|
|
|
|
#ifndef CHIP_BEST1000
|
|
if (src_2d) {
|
|
dma[inst]->_2D[hwch].SRCX =
|
|
DMA_2D_MODIFY(src_2d->xmodify) | DMA_2D_COUNT(src_2d->xcount);
|
|
dma[inst]->_2D[hwch].SRCY =
|
|
DMA_2D_MODIFY(src_2d->ymodify) | DMA_2D_COUNT(src_2d->ycount);
|
|
dma[inst]->_2D[hwch].CTRL |= DMA_2D_CTRL_SRC_EN;
|
|
} else {
|
|
dma[inst]->_2D[hwch].CTRL &= ~DMA_2D_CTRL_SRC_EN;
|
|
}
|
|
if (dst_2d) {
|
|
dma[inst]->_2D[hwch].DSTX =
|
|
DMA_2D_MODIFY(dst_2d->xmodify) | DMA_2D_COUNT(dst_2d->xcount);
|
|
dma[inst]->_2D[hwch].DSTY =
|
|
DMA_2D_MODIFY(dst_2d->ymodify) | DMA_2D_COUNT(dst_2d->ycount);
|
|
dma[inst]->_2D[hwch].CTRL |= DMA_2D_CTRL_DST_EN;
|
|
} else {
|
|
dma[inst]->_2D[hwch].CTRL &= ~DMA_2D_CTRL_DST_EN;
|
|
}
|
|
#endif
|
|
|
|
lock = int_lock();
|
|
if (cfg->start_cb) {
|
|
cfg->start_cb(cfg->ch);
|
|
}
|
|
dma[inst]->CH[hwch].CONFIG |= DMA_CONFIG_EN;
|
|
int_unlock(lock);
|
|
|
|
return HAL_DMA_OK;
|
|
}
|
|
|
|
enum HAL_DMA_RET_T hal_dma_sg_start(const struct HAL_DMA_DESC_T *desc,
|
|
const struct HAL_DMA_CH_CFG_T *cfg) {
|
|
return hal_dma_sg_2d_start(desc, cfg, NULL, NULL);
|
|
}
|
|
|
|
enum HAL_DMA_RET_T hal_dma_start(const struct HAL_DMA_CH_CFG_T *cfg) {
|
|
struct HAL_DMA_DESC_T desc;
|
|
enum HAL_DMA_RET_T ret;
|
|
|
|
ret = hal_dma_init_desc(&desc, cfg, NULL, 1);
|
|
if (ret != HAL_DMA_OK) {
|
|
return ret;
|
|
}
|
|
|
|
ret = hal_dma_sg_start(&desc, cfg);
|
|
if (ret != HAL_DMA_OK) {
|
|
return ret;
|
|
}
|
|
|
|
return HAL_DMA_OK;
|
|
}
|
|
|
|
uint32_t hal_dma_cancel(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint32_t remains;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
dma[inst]->CH[hwch].CONFIG &= ~DMA_CONFIG_EN;
|
|
dma[inst]->INTTCCLR = DMA_STAT_CHAN(hwch);
|
|
dma[inst]->INTERRCLR = DMA_STAT_CHAN(hwch);
|
|
|
|
remains = GET_BITFIELD(dma[inst]->CH[hwch].CONTROL, DMA_CONTROL_TRANSFERSIZE);
|
|
|
|
return remains;
|
|
}
|
|
|
|
uint32_t hal_dma_stop(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint8_t retry = 0;
|
|
const uint8_t max_retry = 10;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
dma[inst]->CH[hwch].CONFIG |= DMA_CONFIG_HALT;
|
|
#if 1
|
|
while ((dma[inst]->CH[hwch].CONFIG & DMA_CONFIG_ACTIVE) &&
|
|
(++retry < max_retry)) {
|
|
if (retry <= 3) {
|
|
hal_sys_timer_delay_us(10);
|
|
} else {
|
|
hal_dma_delay(1);
|
|
}
|
|
}
|
|
#else
|
|
while (dma[inst]->CH[hwch].CONFIG & DMA_CONFIG_ACTIVE)
|
|
;
|
|
#endif
|
|
return hal_dma_cancel(ch);
|
|
}
|
|
|
|
uint8_t hal_dma_get_chan(enum HAL_DMA_PERIPH_T periph,
|
|
enum HAL_DMA_GET_CHAN_T policy) {
|
|
enum HAL_DMA_INST_T inst;
|
|
int ret;
|
|
uint8_t i, hwch;
|
|
uint8_t got = HAL_DMA_CHAN_NONE;
|
|
uint32_t lock;
|
|
|
|
ASSERT(policy == HAL_DMA_HIGH_PRIO || policy == HAL_DMA_LOW_PRIO ||
|
|
policy == HAL_DMA_LOW_PRIO_ONLY,
|
|
"Invalid DMA policy: %d", policy);
|
|
|
|
if (periph == HAL_GPDMA_MEM) {
|
|
inst = HAL_DMA_INST_GPDMA;
|
|
} else if (periph == HAL_AUDMA_MEM) {
|
|
inst = HAL_DMA_INST_AUDMA;
|
|
} else {
|
|
ret = get_index_from_periph(periph, &inst, &i);
|
|
ASSERT(ret == 0, "Invalid DMA periph: %d", periph);
|
|
#ifdef DMA_REMAP
|
|
if (periph_remap_bitmap[inst] & (1 << i)) {
|
|
inst ^= 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
lock = int_lock();
|
|
for (i = 0; i < DMA_NUMBER_CHANNELS; i++) {
|
|
if (policy == HAL_DMA_HIGH_PRIO) {
|
|
hwch = i;
|
|
} else if (policy == HAL_DMA_LOW_PRIO) {
|
|
hwch = DMA_NUMBER_CHANNELS - 1 - i;
|
|
} else {
|
|
hwch = DMA_NUMBER_CHANNELS - 1 - i;
|
|
if (hwch < 6) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!chan_enabled[inst][hwch] && !hal_dma_chan_busy_inst(inst, hwch)) {
|
|
chan_enabled[inst][hwch] = true;
|
|
got = generate_chan(inst, hwch);
|
|
dma[inst]->DMACONFIG |= DMA_DMACONFIG_CLK_EN(1 << hwch);
|
|
break;
|
|
}
|
|
}
|
|
int_unlock(lock);
|
|
|
|
return got;
|
|
}
|
|
|
|
void hal_dma_free_chan(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint32_t lock;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
ASSERT(chan_enabled[inst][hwch], "DMA chan not enabled: inst=%u hwch=%u",
|
|
inst, hwch);
|
|
|
|
hal_dma_cancel(ch);
|
|
|
|
lock = int_lock();
|
|
chan_enabled[inst][hwch] = false;
|
|
dma[inst]->DMACONFIG &= ~DMA_DMACONFIG_CLK_EN(1 << hwch);
|
|
int_unlock(lock);
|
|
}
|
|
|
|
uint32_t hal_dma_get_cur_src_addr(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
return dma[inst]->CH[hwch].SRCADDR;
|
|
}
|
|
|
|
uint32_t hal_dma_get_cur_dst_addr(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
return dma[inst]->CH[hwch].DSTADDR;
|
|
}
|
|
|
|
void SRAM_TEXT_LOC hal_dma_get_cur_src_remain_and_addr(uint8_t ch,
|
|
uint32_t *remain,
|
|
uint32_t *src) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint32_t lock;
|
|
uint32_t size[2];
|
|
uint32_t srcaddr[2];
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
lock = int_lock();
|
|
size[0] = dma[inst]->CH[hwch].CONTROL;
|
|
srcaddr[0] = dma[inst]->CH[hwch].SRCADDR;
|
|
size[1] = dma[inst]->CH[hwch].CONTROL;
|
|
srcaddr[1] = dma[inst]->CH[hwch].SRCADDR;
|
|
int_unlock(lock);
|
|
|
|
size[0] = GET_BITFIELD(size[0], DMA_CONTROL_TRANSFERSIZE);
|
|
size[1] = GET_BITFIELD(size[1], DMA_CONTROL_TRANSFERSIZE);
|
|
|
|
if (size[0] == size[1]) {
|
|
*remain = size[0];
|
|
*src = srcaddr[0];
|
|
} else {
|
|
*remain = size[1];
|
|
*src = srcaddr[1];
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
enum HAL_DMA_RET_T hal_dma_irq_run_chan(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
if ((dma[inst]->INTSTAT & DMA_STAT_CHAN(hwch)) == 0) {
|
|
return HAL_DMA_ERR;
|
|
}
|
|
|
|
hal_dma_handle_chan_irq(inst, hwch);
|
|
|
|
return HAL_DMA_OK;
|
|
}
|
|
|
|
bool hal_dma_chan_busy(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
return hal_dma_chan_busy_inst(inst, hwch);
|
|
}
|
|
|
|
bool hal_dma_busy(void) {
|
|
enum HAL_DMA_INST_T inst;
|
|
int hwch;
|
|
|
|
for (inst = HAL_DMA_INST_AUDMA; inst < HAL_DMA_INST_QTY; inst++) {
|
|
for (hwch = chan_start[inst]; hwch < chan_num[inst]; hwch++) {
|
|
if (hal_dma_chan_busy_inst(inst, hwch)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32_t hal_dma_get_sg_remain_size(uint8_t ch) {
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint32_t remains;
|
|
const struct HAL_DMA_DESC_T *desc, *first;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
remains = GET_BITFIELD(dma[inst]->CH[hwch].CONTROL, DMA_CONTROL_TRANSFERSIZE);
|
|
first = (const struct HAL_DMA_DESC_T *)dma[inst]->CH[hwch].LLI;
|
|
desc = first;
|
|
while (desc) {
|
|
remains += GET_BITFIELD(desc->ctrl, DMA_CONTROL_TRANSFERSIZE);
|
|
desc = (const struct HAL_DMA_DESC_T *)desc->lli;
|
|
if (desc == first) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return remains;
|
|
}
|
|
|
|
void hal_dma_tc_irq_enable(uint8_t ch) {
|
|
#if !(defined(CHIP_BEST1000) || defined(CHIP_BEST2000) || \
|
|
defined(CHIP_BEST3001) || defined(CHIP_BEST3005))
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint32_t lock;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
lock = int_lock();
|
|
#ifdef CHIP_BEST2300
|
|
if (inst == HAL_DMA_INST_AUDMA) {
|
|
hal_cmu_dma_tc_irq_set_chan(hwch);
|
|
}
|
|
#else
|
|
dma[inst]->DMACONFIG |= DMA_DMACONFIG_TC_IRQ_EN(1 << hwch);
|
|
#endif
|
|
int_unlock(lock);
|
|
#endif
|
|
}
|
|
|
|
void hal_dma_tc_irq_disable(uint8_t ch) {
|
|
#if !(defined(CHIP_BEST1000) || defined(CHIP_BEST2000) || \
|
|
defined(CHIP_BEST3001) || defined(CHIP_BEST3005))
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint32_t lock;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
lock = int_lock();
|
|
#ifdef CHIP_BEST2300
|
|
if (inst == HAL_DMA_INST_AUDMA) {
|
|
hal_cmu_dma_tc_irq_clear_chan(hwch);
|
|
}
|
|
#else
|
|
dma[inst]->DMACONFIG &= ~DMA_DMACONFIG_TC_IRQ_EN(1 << hwch);
|
|
#endif
|
|
int_unlock(lock);
|
|
#endif
|
|
}
|
|
|
|
void hal_dma_set_burst_addr_inc(uint8_t ch,
|
|
const struct HAL_DMA_BURST_ADDR_INC_T *inc) {
|
|
#if !(defined(CHIP_BEST1000) || defined(CHIP_BEST2000) || \
|
|
defined(CHIP_BEST3001) || defined(CHIP_BEST3005))
|
|
enum HAL_DMA_INST_T inst;
|
|
uint8_t hwch;
|
|
uint32_t lock;
|
|
|
|
inst = get_inst_from_chan(ch);
|
|
hwch = get_hwch_from_chan(ch);
|
|
|
|
ASSERT(inst < HAL_DMA_INST_QTY, err_invalid_inst, inst);
|
|
ASSERT(hwch < DMA_NUMBER_CHANNELS, err_invalid_chan[inst], hwch);
|
|
|
|
lock = int_lock();
|
|
if (inc && inc->src_inc_en) {
|
|
dma[inst]->CH[hwch].CONTROL |= DMA_CONTROL_BURST_SI;
|
|
dma[inst]->_2D[hwch].SRC_INC = SET_BITFIELD(
|
|
dma[inst]->_2D[hwch].SRC_INC, DMA_BURST_SRC_INC_VAL, inc->src_inc_val);
|
|
} else {
|
|
dma[inst]->CH[hwch].CONTROL &= ~DMA_CONTROL_BURST_SI;
|
|
}
|
|
if (inc && inc->dst_inc_en) {
|
|
dma[inst]->CH[hwch].CONTROL |= DMA_CONTROL_BURST_DI;
|
|
dma[inst]->_2D[hwch].DST_INC = SET_BITFIELD(
|
|
dma[inst]->_2D[hwch].DST_INC, DMA_BURST_DST_INC_VAL, inc->dst_inc_val);
|
|
} else {
|
|
dma[inst]->CH[hwch].CONTROL &= ~DMA_CONTROL_BURST_DI;
|
|
}
|
|
int_unlock(lock);
|
|
#endif
|
|
}
|
|
|
|
void hal_dma_clear_burst_addr_inc(uint8_t ch) {
|
|
hal_dma_set_burst_addr_inc(ch, NULL);
|
|
}
|
|
|
|
void hal_dma_set_desc_burst_addr_inc(
|
|
struct HAL_DMA_DESC_T *desc, const struct HAL_DMA_BURST_ADDR_INC_T *inc) {
|
|
#if !(defined(CHIP_BEST1000) || defined(CHIP_BEST2000) || \
|
|
defined(CHIP_BEST3001) || defined(CHIP_BEST3005))
|
|
if (inc && inc->src_inc_en) {
|
|
desc->ctrl |= DMA_CONTROL_BURST_SI;
|
|
} else {
|
|
desc->ctrl &= ~DMA_CONTROL_BURST_SI;
|
|
}
|
|
if (inc && inc->dst_inc_en) {
|
|
desc->ctrl |= DMA_CONTROL_BURST_DI;
|
|
} else {
|
|
desc->ctrl &= ~DMA_CONTROL_BURST_DI;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void hal_dma_clear_desc_burst_addr_inc(struct HAL_DMA_DESC_T *desc) {
|
|
hal_dma_set_desc_burst_addr_inc(desc, NULL);
|
|
}
|
|
|
|
#ifdef DMA_REMAP
|
|
void hal_dma_remap_periph(enum HAL_DMA_PERIPH_T periph, int enable) {
|
|
enum HAL_DMA_INST_T inst1, inst2;
|
|
uint8_t index1, index2;
|
|
int ret;
|
|
uint32_t lock;
|
|
|
|
// Tag the periph
|
|
ret = get_index_from_periph(periph, &inst1, &index1);
|
|
ASSERT(ret == 0, "Invalid DMA periph for remap1: %d", periph);
|
|
|
|
// Tag the peer periph
|
|
inst2 = inst1 ^ 1;
|
|
ret = get_remap_index_from_periph(inst2, periph, &index2);
|
|
ASSERT(ret == 0, "Invalid DMA periph for remap2: %d", periph);
|
|
|
|
lock = int_lock();
|
|
if (enable) {
|
|
periph_remap_bitmap[inst1] |= (1 << index1);
|
|
periph_remap_bitmap[inst2] |= (1 << index2);
|
|
#ifndef CHIP_BEST1000
|
|
// For best2000, index1 == index2
|
|
hal_cmu_dma_swap_enable(index1);
|
|
#endif
|
|
} else {
|
|
periph_remap_bitmap[inst1] &= ~(1 << index1);
|
|
periph_remap_bitmap[inst2] &= ~(1 << index2);
|
|
#ifndef CHIP_BEST1000
|
|
hal_cmu_dma_swap_disable(index1);
|
|
#endif
|
|
}
|
|
int_unlock(lock);
|
|
}
|
|
#endif
|
|
|
|
void hal_dma_open(void) {
|
|
enum HAL_DMA_INST_T inst;
|
|
|
|
if (dma_opened) {
|
|
return;
|
|
}
|
|
|
|
for (inst = 0; inst < HAL_DMA_INST_QTY; inst++) {
|
|
hal_dma_open_inst(inst);
|
|
}
|
|
|
|
#ifdef DMA_REMAP
|
|
#ifdef CHIP_BEST1000
|
|
if (hal_get_chip_metal_id() >= HAL_CHIP_METAL_ID_4) {
|
|
hal_dma_remap_periph(HAL_AUDMA_I2S0_RX, 1);
|
|
hal_dma_remap_periph(HAL_AUDMA_I2S0_TX, 1);
|
|
hal_dma_remap_periph(HAL_AUDMA_SPDIF0_RX, 1);
|
|
hal_dma_remap_periph(HAL_AUDMA_SPDIF0_TX, 1);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
dma_opened = true;
|
|
}
|
|
|
|
void hal_dma_close(void) {
|
|
enum HAL_DMA_INST_T inst;
|
|
|
|
if (!dma_opened) {
|
|
return;
|
|
}
|
|
|
|
for (inst = 0; inst < HAL_DMA_INST_QTY; inst++) {
|
|
hal_dma_close_inst(inst);
|
|
}
|
|
|
|
dma_opened = false;
|
|
}
|
|
|
|
#ifdef CORE_SLEEP_POWER_DOWN
|
|
void hal_dma_sleep(void) {
|
|
enum HAL_DMA_INST_T inst;
|
|
|
|
for (inst = 0; inst < HAL_DMA_INST_QTY; inst++) {
|
|
saved_dma_regs[inst] = dma[inst]->DMACONFIG;
|
|
}
|
|
}
|
|
|
|
void hal_dma_wakeup(void) {
|
|
enum HAL_DMA_INST_T inst;
|
|
|
|
for (inst = 0; inst < HAL_DMA_INST_QTY; inst++) {
|
|
dma[inst]->DMACONFIG = saved_dma_regs[inst];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
HAL_DMA_DELAY_FUNC hal_dma_set_delay_func(HAL_DMA_DELAY_FUNC new_func) {
|
|
HAL_DMA_DELAY_FUNC old_func = dma_delay;
|
|
dma_delay = new_func;
|
|
return old_func;
|
|
}
|