Docker-Build mit gcc-arm-none-eabi 4.9-2014q4 (statt Homebrew-Cask) verwendet, Dockerfile/tools/docker.sh dafür aus Task 13 vorgezogen. cmake/sdk.cmake schont gcc 4.9 (kennt -fcommon/-Wno-error=... nicht). Zwei zusätzliche SDK-Stubs (EarlyInit, pre_gpio_config) in src/sys/sdk_stubs.c ergänzt, siehe sdk/PATCHES.md.
406 lines
11 KiB
C
406 lines
11 KiB
C
/**
|
|
* @file wm_dma.c
|
|
*
|
|
* @brief DMA Driver Module
|
|
*
|
|
* @author dave
|
|
*
|
|
* Copyright (c) 2014 Winner Microelectronics Co., Ltd.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include "misc.h"
|
|
#include "wm_dma.h"
|
|
#include "wm_regs.h"
|
|
#include "wm_irq.h"
|
|
#include "wm_osal.h"
|
|
#include "wm_pmu.h"
|
|
|
|
|
|
static u16 dma_used_bit = 0;
|
|
struct tls_dma_channels {
|
|
unsigned char channels[8]; /* list of channels */
|
|
};
|
|
|
|
typedef void (*dma_irq_callback)(void *p);
|
|
|
|
struct dma_irq_context {
|
|
u8 flags;
|
|
dma_irq_callback burst_done_pf;
|
|
void *burst_done_priv;
|
|
dma_irq_callback transfer_done_pf;
|
|
void *transfer_done_priv;
|
|
};
|
|
|
|
static struct dma_irq_context dma_context[8];
|
|
static struct tls_dma_channels channels;
|
|
|
|
extern void wm_delay_ticks(uint32_t ticks);
|
|
|
|
static void dma_irq_proc(void *p)
|
|
{
|
|
unsigned char ch;
|
|
unsigned int int_src;
|
|
|
|
ch = (unsigned char)(unsigned long)p;
|
|
int_src = tls_reg_read32(HR_DMA_INT_SRC);
|
|
|
|
if (ch > 3)
|
|
{
|
|
for (ch = 4; ch < 8; ch++)
|
|
{
|
|
if (int_src & (TLS_DMA_IRQ_BOTH_DONE << ch * 2))
|
|
break;
|
|
}
|
|
|
|
if (8 == ch)
|
|
return;
|
|
}
|
|
|
|
if ((int_src & (TLS_DMA_IRQ_BOTH_DONE << ch * 2)) &&
|
|
(TLS_DMA_IRQ_BOTH_DONE == dma_context[ch].flags))
|
|
{
|
|
tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE);
|
|
if (dma_context[ch].burst_done_pf)
|
|
dma_context[ch].burst_done_pf(dma_context[ch].burst_done_priv);
|
|
}
|
|
else if ((int_src & (TLS_DMA_IRQ_BURST_DONE << ch * 2)) &&
|
|
(TLS_DMA_IRQ_BURST_DONE == dma_context[ch].flags))
|
|
{
|
|
tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE);
|
|
if (dma_context[ch].burst_done_pf)
|
|
dma_context[ch].burst_done_pf(dma_context[ch].burst_done_priv);
|
|
}
|
|
else if ((int_src & (TLS_DMA_IRQ_TRANSFER_DONE << ch * 2)) &&
|
|
(TLS_DMA_IRQ_TRANSFER_DONE == dma_context[ch].flags))
|
|
{
|
|
tls_dma_irq_clr(ch, TLS_DMA_IRQ_BOTH_DONE);
|
|
if (dma_context[ch].transfer_done_pf)
|
|
dma_context[ch].transfer_done_pf(dma_context[ch].transfer_done_priv);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void DMA_Channel0_IRQHandler(void)
|
|
{
|
|
dma_irq_proc((void *)0);
|
|
}
|
|
void DMA_Channel1_IRQHandler(void)
|
|
{
|
|
dma_irq_proc((void *)1);
|
|
}
|
|
void DMA_Channel2_IRQHandler(void)
|
|
{
|
|
dma_irq_proc((void *)2);
|
|
}
|
|
void DMA_Channel3_IRQHandler(void)
|
|
{
|
|
dma_irq_proc((void *)3);
|
|
}
|
|
void DMA_Channel4_7_IRQHandler(void)
|
|
{
|
|
dma_irq_proc((void *)4);
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to clear dma interrupt flag.
|
|
*
|
|
* @param[in] ch Channel no.[0~7]
|
|
* @param[in] flags Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE.
|
|
*
|
|
* @return None
|
|
*
|
|
* @note None
|
|
*/
|
|
void tls_dma_irq_clr(unsigned char ch, unsigned char flags)
|
|
{
|
|
unsigned int int_src = 0;
|
|
|
|
int_src |= flags << 2 * ch;
|
|
|
|
tls_reg_write32(HR_DMA_INT_SRC, int_src);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to register dma interrupt callback function.
|
|
*
|
|
* @param[in] ch Channel no.[0~7]
|
|
* @param[in] callback is the dma interrupt call back function.
|
|
* @param[in] arg the param of the callback function.
|
|
* @param[in] flags Flags setted to TLS_DMA_IRQ_BURST_DONE, TLS_DMA_IRQ_TRANSFER_DONE, TLS_DMA_IRQ_BOTH_DONE.
|
|
*
|
|
* @return None
|
|
*
|
|
* @note None
|
|
*/void tls_dma_irq_register(unsigned char ch, void (*callback)(void *p), void *arg, unsigned char flags)
|
|
{
|
|
unsigned int mask;
|
|
|
|
mask = tls_reg_read32(HR_DMA_INT_MASK);
|
|
mask |= TLS_DMA_IRQ_BOTH_DONE << 2 * ch;
|
|
mask &= ~(flags << 2 * ch);
|
|
tls_reg_write32(HR_DMA_INT_MASK, mask);
|
|
|
|
dma_context[ch].flags = flags;
|
|
if (flags & TLS_DMA_IRQ_BURST_DONE)
|
|
{
|
|
dma_context[ch].burst_done_pf = callback;
|
|
dma_context[ch].burst_done_priv = arg;
|
|
}
|
|
if (flags & TLS_DMA_IRQ_TRANSFER_DONE)
|
|
{
|
|
dma_context[ch].transfer_done_pf = callback;
|
|
dma_context[ch].transfer_done_priv = arg;
|
|
}
|
|
|
|
if (ch > 3)
|
|
ch = 4;
|
|
|
|
tls_irq_enable(DMA0_INT + ch);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to Wait until DMA operation completes
|
|
*
|
|
* @param[in] ch channel no
|
|
*
|
|
* @retval 0 completed
|
|
* @retval -1 failed
|
|
*
|
|
* @note None
|
|
*/
|
|
int tls_dma_wait_complt(unsigned char ch)
|
|
{
|
|
unsigned long timeout = 0;
|
|
|
|
while(DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON)
|
|
{
|
|
tls_os_time_delay(1);
|
|
timeout ++;
|
|
if(timeout > 500)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to Start the DMA controller by Wrap
|
|
*
|
|
* @param[in] ch channel no
|
|
* @param[in] dma_desc pointer to DMA channel descriptor structure
|
|
* @param[in] auto_reload does restart when current transfer complete
|
|
* @param[in] src_zize dource address size
|
|
* @param[in] dest_zize destination address size
|
|
*
|
|
* @retval 1 success
|
|
* @retval 0 failed
|
|
*
|
|
* @note
|
|
* DMA Descriptor:
|
|
* +--------------------------------------------------------------+
|
|
* |Vld[31] | RSV |
|
|
* +--------------------------------------------------------------+
|
|
* | RSV | Dma_Ctrl[16:0] |
|
|
* +--------------------------------------------------------------+
|
|
* | Src_Addr[31:0] |
|
|
* +--------------------------------------------------------------+
|
|
* | Dest_Addr[31:0] |
|
|
* +--------------------------------------------------------------+
|
|
* | Next_Desc_Add[31:0] |
|
|
* +--------------------------------------------------------------+
|
|
*/
|
|
unsigned char tls_dma_start_by_wrap(unsigned char ch, struct tls_dma_descriptor *dma_desc,
|
|
unsigned char auto_reload,
|
|
unsigned short src_zize,
|
|
unsigned short dest_zize)
|
|
{
|
|
if((ch > 7) && !dma_desc) return 1;
|
|
|
|
DMA_SRCWRAPADDR_REG(ch) = dma_desc->src_addr;
|
|
DMA_DESTWRAPADDR_REG(ch) = dma_desc->dest_addr;
|
|
DMA_WRAPSIZE_REG(ch) = (dest_zize << 16) | src_zize;
|
|
DMA_CTRL_REG(ch) = ((dma_desc->dma_ctrl & 0x1ffff) << 1) | (auto_reload ? 0x1: 0x0);
|
|
DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_ON;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to Start the DMA controller
|
|
*
|
|
* @param[in] ch channel no
|
|
* @param[in] dma_desc pointer to DMA channel descriptor structure
|
|
* @param[in] auto_reload does restart when current transfer complete
|
|
*
|
|
* @retval 0 success
|
|
* @retval 1 failed
|
|
*
|
|
* @note
|
|
* DMA Descriptor:
|
|
* +--------------------------------------------------------------+
|
|
* |Vld[31] | RSV |
|
|
* +--------------------------------------------------------------+
|
|
* | RSV | Dma_Ctrl[16:0] |
|
|
* +--------------------------------------------------------------+
|
|
* | Src_Addr[31:0] |
|
|
* +--------------------------------------------------------------+
|
|
* | Dest_Addr[31:0] |
|
|
* +--------------------------------------------------------------+
|
|
* | Next_Desc_Add[31:0] |
|
|
* +--------------------------------------------------------------+
|
|
*/
|
|
unsigned char tls_dma_start(unsigned char ch, struct tls_dma_descriptor *dma_desc, unsigned char auto_reload)
|
|
{
|
|
if((ch > 7) && !dma_desc) return 1;
|
|
|
|
if ((dma_used_bit &(1<<ch)) == 0)
|
|
{
|
|
dma_used_bit |= (1<<ch);
|
|
}
|
|
DMA_SRCADDR_REG(ch) = dma_desc->src_addr;
|
|
DMA_DESTADDR_REG(ch) = dma_desc->dest_addr;
|
|
DMA_CTRL_REG(ch) = ((dma_desc->dma_ctrl & 0x7fffff) << 1) | (auto_reload ? 0x1: 0x0);
|
|
DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_ON;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to To stop current DMA channel transfer
|
|
*
|
|
* @param[in] ch channel no. to be stopped
|
|
*
|
|
* @retval 0 success
|
|
* @retval 1 failed
|
|
*
|
|
* @note
|
|
* If channel stop, DMA_CHNL_CTRL_CHNL_ON bit in DMA_CHNLCTRL_REG is cleared.
|
|
*/
|
|
unsigned char tls_dma_stop(unsigned char ch)
|
|
{
|
|
if(ch > 7) return 1;
|
|
if(DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON)
|
|
{
|
|
DMA_CHNLCTRL_REG(ch) |= DMA_CHNL_CTRL_CHNL_OFF;
|
|
|
|
while(DMA_CHNLCTRL_REG(ch) & DMA_CHNL_CTRL_CHNL_ON);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to Request a free dma channel
|
|
*
|
|
* @param[in] ch channel no.
|
|
* @param[in] flags flags setted to selected channel
|
|
*
|
|
* @return Channel no. that is free now
|
|
*
|
|
* @note
|
|
* If ch is 0, the function will select a random free channel,
|
|
* else return the selected channel no.
|
|
*/
|
|
unsigned char tls_dma_request(unsigned char ch, unsigned char flags)
|
|
{
|
|
|
|
unsigned char freeCh = 0;
|
|
int i = 0;
|
|
|
|
if (ch == 0)
|
|
{
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (!(channels.channels[i] & TLS_DMA_FLAGS_CHANNEL_VALID))
|
|
{
|
|
freeCh = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (8 == i)
|
|
{
|
|
printf("!!!there is no free DMA channel.!!!\n");
|
|
freeCh = 0;
|
|
}
|
|
}
|
|
else if ((ch >0) && (ch < 8))
|
|
{
|
|
if (!(channels.channels[ch] & TLS_DMA_FLAGS_CHANNEL_VALID))
|
|
{
|
|
freeCh = ch;
|
|
}
|
|
else
|
|
{
|
|
printf("!!!there is no free DMA channel.!!!\n");
|
|
freeCh = 0;
|
|
}
|
|
}
|
|
|
|
//if(freeCh != 0)
|
|
{
|
|
channels.channels[freeCh] = flags | TLS_DMA_FLAGS_CHANNEL_VALID;
|
|
DMA_MODE_REG(freeCh) = flags;
|
|
}
|
|
if (dma_used_bit == 0)
|
|
{
|
|
tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_DMA);
|
|
}
|
|
dma_used_bit |= (1<<freeCh);
|
|
return freeCh;
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to Free the DMA channel when not use
|
|
*
|
|
* @param[in] ch channel no. that is ready to free
|
|
*
|
|
* @return None
|
|
*
|
|
* @note None
|
|
*/
|
|
void tls_dma_free(unsigned char ch)
|
|
{
|
|
if(ch < 8)
|
|
{
|
|
tls_dma_stop(ch);
|
|
|
|
DMA_SRCADDR_REG(ch) = 0;
|
|
DMA_DESTADDR_REG(ch) = 0;
|
|
DMA_MODE_REG(ch) = 0;
|
|
DMA_CTRL_REG(ch) = 0;
|
|
// DMA_INTSRC_REG = 0xffff;
|
|
DMA_INTSRC_REG |= 0x03<<(ch*2);
|
|
|
|
channels.channels[ch] = 0x00;
|
|
dma_used_bit &= ~(1<<ch);
|
|
if (dma_used_bit == 0)
|
|
{
|
|
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_DMA);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function is used to Initialize DMA Control
|
|
*
|
|
* @param[in] None
|
|
*
|
|
* @return None
|
|
*
|
|
* @note None
|
|
*/
|
|
void tls_dma_init(void)
|
|
{
|
|
DMA_INTMASK_REG = 0xffff;
|
|
DMA_INTSRC_REG = 0xffff;
|
|
}
|
|
|