/**
  **********************************************************************************
  * @file	  dma_drv.c
  * @author  Product application department
  * @version V1.0
  * @date	  2020.02.13
  * @brief   DMA模块DRV层驱动.
  * @note 这个文件为应用层提供了操作DMA的接口:
  *
  @verbatim
  ===================================================================================
                                 ##### 更新 #####
  ===================================================================================
  [..] 更新时间:
  [..] 更新人:
  [..] 更新内容:
	 1. nothing.
  @endverbatim
*/

#include "dma_drv.h"
#include "common.h"
#include "debug.h"
#include "ssi_drv.h"


/*** 常量定义 *******************************************************************/
/* global variables */
volatile uint32_t dma_isr_flag =0;
DMAC_TypeDef *m_dma_control ; 
DMAC_ChannelTypeDef *m_dma_channel[DMAC_CHNUM]; 
uint8_t DMA_CHX=0;

/*** 全局变量定义 ***************************************************************/
/*** volatile **********/

/*** @ static **********/

/*** 函数定义 *******************************************************************/

/**
 * @brief 设置DMA控制寄存器
 *
 * @param[in] ch dma通道号，取指【0~3】 
 * @param[in] tt_fc 传送类型和流控制
 * @param[in] src_msize 源传输数据长度
 * @param[in] dest_msize 目标传输数据长度
 * @param[in] sinc  源地址增量
 * @param[in] dinc  目标地址增量
 * @param[in] src_tr_wideh 源数据传输大小
 * @param[in] dst_tr_width 目标数据传输大小
 * @return @ref NONE
 */
void DRV_DMAC_CtrlRegConfig(DMAC_TypeDef *pdmac,
	                        uint32_t ch, 
                            uint32_t tt_fc, 
                            uint32_t src_msize, 
                            uint32_t dest_msize, 
                            uint32_t sinc, 
                            uint32_t dinc, 
                            uint32_t src_tr_wideh, 
                            uint32_t dst_tr_width)
{
	uint32_t tmp = 0;

	if (ch > (DRV_DMAC_CH_MAX-1))
		return;
	
	/* Prepare the DMA Channel configuration */
    tmp |=  (((tt_fc<<DMAC_TT_FC_SHIFT_MASK)&(DMAC_TT_FC_MASK))|   
	         ((src_msize<<DMAC_SRC_MSIZE_SHIFT_MASK)&(DMAC_SRC_MSIZE_MASK))|
	         ((dest_msize<<DMAC_DEST_MSIZE_SHIFT_MASK)&(DMAC_DEST_MSIZE_MASK))|
	         ((sinc<<DMAC_SINC_INC_SHIFT_MASK)&(DMAC_SINC_INC_MASK))|
	         ((dinc<<DMAC_DINC_INC_SHIFT_MASK)&(DMAC_DINC_INC_MASK))|
	         ((src_tr_wideh<<DMAC_SRC_TR_WIDTH_SHIFT_MASK)&(DMAC_SRC_TR_WIDTH_MASK))|
	         ((dst_tr_width<<DMAC_DST_TR_WIDTH_SHIFT_MASK)&(DMAC_DST_TR_WIDTH_MASK)));

	_dmac_set_dmac_ctrln(pdmac,ch, tmp);
	//MSG("dma->ctrlx = %08x\r\n", tmp);
}

/**
 * @brief 获取DMA的RAW状态
 *
 * @param[in] ch dma通道号，取指【0~3】 
 * @return @ref 
 *  - BIT_RESET
 *  - BIT_SET
 */
BitActionTypeDef DRV_DMAC_GetRawStatus(DMAC_TypeDef *pdmac,uint32_t ch)
{
	if (_dmac_get_raw_interrupt_status(pdmac,ch))
		return BIT_SET;

	return BIT_RESET;
}

/**
 * @brief 清除DMA的RAW状态
 *
 * @param[in] ch dma通道号，取指【0~3】 
 * @return @ref NONE
 */
void DRV_DMAC_ClearRawStatus(DMAC_TypeDef *pdmac,uint32_t ch)
{
    _dmac_clr_raw_interrupt_status(pdmac,ch);
}

/**
 * @brief 初始化DMA LLI寄存器.
 *
 * @param[in] ch dma通道号，取指【0~3】 
 * @param[in] pdma_lli dma的LLI结构体 
 *
 * @return @ref NONE
 */
void DRV_DMAC_LliRegInit(DMAC_TypeDef *pdmac,uint32_t ch, DMAC_LLITypeDef *pdma_lli)
{
	if (ch > (DRV_DMAC_CH_MAX-1))
		return;

    pdmac->CH[ch].SRCADDR = pdma_lli->src_addr;
    pdmac->CH[ch].DSTADDR = pdma_lli->dst_addr;
    pdmac->CH[ch].LLI = (uint32_t)pdma_lli;
    //DMAC->CH[ch].CTRL = pdma_lli->control;
    pdmac->CH[ch].CTRL_HIGH = pdma_lli->len;

    //return HAL_OK;
}
/**
 * @brief 禁止TRF中断.
 *
 * @param[in] pdmac DMAC指针;
 * @param[in] ch dma通道号;
 *
 * @return @ref NONE
 */
void DRV_DMAC_DisTfrIt(DMAC_TypeDef *pdmac,uint32_t ch)
{
	pdmac->MASKTFR &= ~(((1<<ch)<<8 | (1<<ch)));
}
/**
* @brief 清队TRF中断标志.
*
* @param[in] pdmac DMAC指针;
* @param[in] ch dma通道号;
*
* @return @ref NONE
*/
void DRV_DMAC_ClrTrfFlag(DMAC_TypeDef *pdmac,uint32_t ch)
{
	pdmac->CLRTFR = (1<<ch);
}

/**
* @brief DMA寄存器初始化.
*
* @param[in] dmac_base_addr DMA基地址;
* @param[in] dma_ch dma通道号;
*
* @return @ref NONE
*/
void DMA_REG_Init(uint32_t dmac_base_addr ,uint8_t dma_ch)
{
	m_dma_control = (DMAC_TypeDef*)(dmac_base_addr);
	DMA_CHX=dma_ch;
	m_dma_control->CONFIG = 0x01;
}


/**
* @brief spi dma传送函数
*
* @param[in] spiid  SPI ID号;
* @param[in] psend  发送数据地址;
* @param[in] precv  接收数据地址;
* @param[in] length  传输数据长度;
* @param[in] binten  是否开启中断模式;
*
* @return @ref NONE
*/
void dma_ssi_m2p_start(uint8_t* psend, uint8_t* precv, uint32_t length, bool binten)
{
	if(((int)precv!=0x60000060)&&((int)precv!=0x70000060))
	{
		return;
	}
	//Tx
	m_dma_control->CH[DMA_CHX].SRCADDR = (uint32_t)psend;
	m_dma_control->CH[DMA_CHX].DSTADDR = (uint32_t)precv;
	
	if(binten == TRUE)
	{
		m_dma_control->CH[DMA_CHX].CTRL = INTEN|DNOCHG|SIEC|M2P_DMA|DWIDTH_B|SWIDTH_B;
		m_dma_control->MASKTFR = CHANNEL_UMASK(DMA_CHX);
		NVIC_Init(3, 3, DMA2_IRQn, 2);
	}
	else
	{
		m_dma_control->CH[DMA_CHX].CTRL = DNOCHG|SIEC|M2P_DMA|DWIDTH_B|SWIDTH_B;
	}
	m_dma_control->CH[DMA_CHX].CTRL_HIGH = length ;  //最大长度为0x0FFF
	m_dma_control->CH[DMA_CHX].CONFIG = (HS_SEL_DST_HARD);
	if((int)precv == 0x60000060)
	{
		m_dma_control->CH[DMA_CHX].CONFIG_HIGH = DST_PER_SPI_TX(3);
	}
	else if((int)precv == 0x70000060) 
	{
		m_dma_control->CH[DMA_CHX].CONFIG_HIGH = DST_PER_SPI_TX(5);
	}
	//enable dma channel
	m_dma_control->CHEN = CHANNEL_WRITE_ENABLE(DMA_CHX)|CHANNEL_ENABLE(DMA_CHX); 
	
}


/**
* @brief spi dma传送函数
*
* @param[in] binten  是否开启中断模式;
*
* @return @ref NONE
*/
void dma_ssi_wait( bool binten)
{
if(binten == TRUE)
	{
		while(1)
		{
			if(dma_isr_flag == (CHANNEL_STAT(DMA_CHX)) )
			{
				dma_isr_flag = 0;
				break;
			}
		}
	}
	else
	{
		while((m_dma_control->RAWTFR & (CHANNEL_STAT(DMA_CHX) )) != (CHANNEL_STAT(DMA_CHX) ));

		m_dma_control->CLRTFR = (CHANNEL_STAT(DMA_CHX));

	}

	while(m_dma_control->CHEN & CHANNEL_STAT(DMA_CHX));
	m_dma_control->CHEN = 0;
	m_dma_control->CONFIG = 0;

}

/**
* @brief spi dma链表接收函数
*
* @param[in] psrc  SPI ID号;
* @param[in] pread  接收数据地址;
* @param[in] length  传输数据长度;
* @param[in] binten  是否开启中断模式;
*
* @return @ref NONE
*/
#define DMA_TRANS_NUM  (4092)
#define LLI_NUM         (17)
DMA_LLI_SSI dma_lli[LLI_NUM];
void dma_ssi_p2m_lli_start(uint8_t* psrc, uint8_t* pread, uint32_t length, bool binten)
{
	uint32_t mod;
	uint32_t loop;
	uint32_t i;
	
	loop = length / DMA_TRANS_NUM;
	mod =  length % DMA_TRANS_NUM;
	if(mod != 0)
		loop++;
	
	if(loop>LLI_NUM)
	{
	  return ;
	}
	
		for(i = 0; i < loop; i++)
	{
		dma_lli[i].src_addr = (uint32_t) psrc;
		dma_lli[i].dst_addr = (uint32_t)(pread + DMA_TRANS_NUM * i);

		if(i == (loop - 1))
		{
			dma_lli[i].next_lli = 0;
			dma_lli[i].len = mod;
			if(binten == TRUE)
			{
				dma_lli[i].control0 = INTEN|DIEC|SNOCHG|P2M_DMA|DWIDTH_B|SWIDTH_B;
			}
			else
			{
				dma_lli[i].control0 = DIEC|SNOCHG|P2M_DMA|DWIDTH_B|SWIDTH_B;
			}
		}
		else
		{
			dma_lli[i].next_lli = (uint32_t)&dma_lli[i + 1];
			dma_lli[i].len = DMA_TRANS_NUM;
			if(binten == TRUE)
			{
				dma_lli[i].control0 = INTEN|DIEC|SNOCHG|P2M_DMA|DWIDTH_B|SWIDTH_B|LLP_SRC_EN|LLP_DST_EN;
			}
			else
			{
				dma_lli[i].control0 = DIEC|SNOCHG|P2M_DMA|DWIDTH_B|SWIDTH_B|LLP_SRC_EN|LLP_DST_EN;
			}
		}
	}
	m_dma_control->CH[DMA_CHX].SRCADDR = dma_lli[0].src_addr;
	m_dma_control->CH[DMA_CHX].DSTADDR = dma_lli[0].dst_addr;
	m_dma_control->CH[DMA_CHX].LLI = (uint32_t)&dma_lli[0];
	m_dma_control->CH[DMA_CHX].CTRL = dma_lli[0].control0;
	m_dma_control->CH[DMA_CHX].CTRL_HIGH =  dma_lli[0].len;
	m_dma_control->CH[DMA_CHX].CONFIG = (HS_SEL_SRC_HARD);
	
	if((int)psrc == 0x60000060)
	{
	 m_dma_control->CH[DMA_CHX].CONFIG_HIGH = SRC_PER_SPI_RX(2);
	}
	else if((int)psrc == 0x70000060) 
	{
		m_dma_control->CH[DMA_CHX].CONFIG_HIGH = SRC_PER_SPI_RX(4);
	}
	//enable dma channel
	if(binten == TRUE)
	{	
	   m_dma_control->MASKTFR = CHANNEL_UMASK(DMA_CHX);
		 NVIC_Init(3, 3, DMA2_IRQn, 2);
	}	
	m_dma_control->CHEN = CHANNEL_WRITE_ENABLE(DMA_CHX)|CHANNEL_ENABLE(DMA_CHX);

}

/**
* @brief 获取dma传输状态标志
*
* @param[in] binten  是否开启中断模式;
*
* @return @ref int
*/
int get_dma_flag(bool binten)
{
  if(binten == TRUE)
	 { 
    return  (dma_isr_flag == (CHANNEL_STAT(DMA_CHX)) );
	 }
	else
	{
	 return ((m_dma_control->RAWTFR & (CHANNEL_STAT(DMA_CHX) )) == (CHANNEL_STAT(DMA_CHX) ));
	}
}

/**
* @brief 清除dma传输状态标志
*
* @param[in] binten  是否开启中断模式;
*
* @return @ref none
*/
void clear_dma_flag(bool binten)
{
if(binten == TRUE)
	{
		dma_isr_flag = 0;
	}
	else
	{
		m_dma_control->CLRTFR = (CHANNEL_STAT(DMA_CHX));	
	}
	while(m_dma_control->CHEN & CHANNEL_STAT(DMA_CHX));
	m_dma_control->CHEN = 0;
	m_dma_control->CONFIG = 0;
}
