#include "IC_APS1604M.h"
#include  "bsp_spi.h"

static psram_info_t psram_info;

/******************************************************************************
*@brief : Change SPI CS pin mode 
*         
*@param : cs_mode: REF@spi_cs_mode_enum 
*@return: None
*@Note  : SPI CS pin is hardware controlled in XIP mode, 
*         and software controlled in FIFO mode
******************************************************************************/
static void SPI_CS_Mode_Cfg(spi_cs_mode_enum cs_mode)
{
    GPIO_InitTypeDef    gpio_cfg = {0};
    
    if(cs_mode == SPI_CS_HW)
    {
        gpio_cfg.Pin	   = SPI_PSRAM_CS_PIN;
        gpio_cfg.Mode	   = GPIO_MODE_AF_PP;
        gpio_cfg.Pull	   = GPIO_PULLUP;
        gpio_cfg.Alternate = GPIO_FUNCTION_6;

        GPIO_Init(SPI_PSRAM_CS_PORT, &gpio_cfg);
        SPI_PSRAM_CS_H();        
    }
    else if(cs_mode == SPI_CS_SW)
    {
        gpio_cfg.Pin       = SPI_PSRAM_CS_PIN;
        gpio_cfg.Mode      = GPIO_MODE_OUTPUT_PP;
        gpio_cfg.Pull      = GPIO_PULLUP;
        gpio_cfg.Alternate = GPIO_FUNCTION_0;

        GPIO_Init(SPI_PSRAM_CS_PORT, &gpio_cfg);
        SPI_PSRAM_CS_H();
    }
}

/******************************************************************************
*@brief : get psram information struct
*         
*@param : None
*@return: psram_info_t
******************************************************************************/
psram_info_t *get_psram_info(void)
{
    return &psram_info;
}


/******************************************************************************
*@brief : Initilise PSRAM r/w SPI interface
*
*@param : SPIx: SPI instance for PSRAM interace
*@param : dma_rx_ch: DMA channel instance for PSRAM read. set this param NULL when do Not use DMA
*@param : dma_tx_ch: DMA channel instance for PSRAM write. set this param NULL when do Not use DMA
*@return: HAL_SPI_PSRAM_Status
*@note  : IC_APS1604M_Init Initilized with XIP mode enabled.
          When call PSRAM FIFO mode read and write funcions, e.g. IC_APS1604M_Read() 
          it will automatically first switched to FIFO mode,
          and then switched to XIP mode when FIFO funtions finished.
*
*         SPIx interrupt should be enalbed for DMA read/write.
*         e.g. set NVIC_EnableIRQ(SPI3_IRQn) in HAL_SPI_MspInit()
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_Init(SPI_TypeDef* SPIx, DMA_Channel_TypeDef *dma_rx_ch, DMA_Channel_TypeDef *dma_tx_ch)
{
    GPIO_InitTypeDef	gpio_cfg = {0};
    
    psram_info.SPIx = SPIx;
    
    Spi_Gpio_Init();
    
    Spi_Init();
    
    SPIx->CMD = (APS1604M_CMD_RD | (uint32_t)(APS1604M_CMD_WR << 8));
	SPIx->PARA = 0;
	SPIx->MEMO_ACC = ((0x18 << 14) | (1 << 3) | (1 << 1));
        
    __SPI_PSRAM_SET_1X_MODE();
    
	SPIx->MEMO_ACC |= SPI_ACC_EN;
    
    //IC_APS1604M_Reset();
    
    psram_info.mode = PSRAM_SPI_MODE;
        
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : Read PSRAM id
*         
*@param : eid[6]: The PSRAM eid will be stored in eid[]
*@return: PSRAM ID
******************************************************************************/
uint16_t IC_APS1604M_Read_ID(uint8_t eid[6])
{
    uint8_t tmp[8];
    
    tmp[0] = APS1604M_CMD_RD_ID;
    tmp[1] = 0;
    tmp[2] = 0;
    tmp[3] = 0;
    
    __SWITCH_TO_FIFO_MODE();

    /* CS Select */
    SPI_PSRAM_CS_L();
    /* Send command */
    __SPI_PSRAM_Write_Data(tmp, 4);
    /* Recieve Manufacture ID and Device ID */
    __SPI_PSRAM_Read_Data(tmp, 8);
    /* CS Realse */
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
    
    if(eid != NULL)
    {
        uint8_t i;
        for(i = 0; i < 6; i++)
            eid[i] = tmp[2 + i];
    }
    
    return ((uint16_t)tmp[0] << 8 | (uint16_t)tmp[1]);
}

/******************************************************************************
*@brief : PSRAM read 1x mode
*         
*@param : addr: The PSRAM address for reading
*@param : buf:  Data buffer address pointer for storing PSRAM data
*@param : len:  Data length to read
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_Read(uint32_t addr, void *buf,  uint32_t len)
{
    
    uint8_t tmp[4];
    
    tmp[0] = APS1604M_CMD_RD;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);
    
    __SWITCH_TO_FIFO_MODE();

    SPI_PSRAM_CS_L();
    
    /* Send command */
    __SPI_PSRAM_Write_Data(tmp, 4);
    /* Recieve Data */
    __SPI_PSRAM_Read_Data(buf, len);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
    
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM write 1x mode
*         
*@param : addr: The PSRAM address for writing
*@param : buf:  Data buffer address pointer of writing data
*@param : len:  Data length to write
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_Write(uint32_t addr, void *buf,  uint32_t len)
{
    uint8_t tmp[4];
    
    tmp[0] = APS1604M_CMD_WR;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);

    __SWITCH_TO_FIFO_MODE();

    SPI_PSRAM_CS_L();
    
    /* Send command */
    __SPI_PSRAM_Write_Data(tmp, 4);
    /* Send Data */
    __SPI_PSRAM_Write_Data(buf, len);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
    
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM read 4x mode
*         
*@param : addr: The PSRAM address for reading
*@param : buf:  Data buffer address pointer for storing PSRAM data
*@param : len:  Data length to read
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_QPI_Read(uint32_t addr, void *buf,  uint32_t len)
{
    uint8_t tmp[8];
           
    tmp[0] = APS1604M_CMD_FAST_QRD;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);
    tmp[4] = 0xFF;
	tmp[5] = 0xFF;
	tmp[6] = 0xFF;
    
    __SWITCH_TO_FIFO_MODE();
    SPI_PSRAM_CS_L();
    
    tmp[0] = APS1604M_CMD_FAST_QRD;
    __SPI_PSRAM_Write_Data(tmp, 1);
    
    __SPI_PSRAM_SET_4X_MODE();
    
    __SPI_PSRAM_Write_Data(&tmp[1], 6);
    
    __SPI_PSRAM_Read_Data(buf, len);
    
    SPI_PSRAM_CS_H();

    __SWITCH_TO_XIP_MODE();
    
    
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM write 4x mode
*         
*@param : addr: The PSRAM address for writing
*@param : buf:  Data buffer address pointer of writing data
*@param : len:  Data length to write
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_QPI_Write(uint32_t addr, void *buf,  uint32_t len)
{
    uint8_t tmp[8];
       
    tmp[0] = APS1604M_CMD_QWR;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);
    
    __SWITCH_TO_FIFO_MODE();
    
    SPI_PSRAM_CS_L();
    
    __SPI_PSRAM_Write_Data(tmp, 1);
    
    __SPI_PSRAM_SET_4X_MODE();
    
    __SPI_PSRAM_Write_Data(&tmp[1], 3);
    
    /* Recieve Data */
    __SPI_PSRAM_Write_Data(buf, len);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
    
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM DMA read 1x mode
*         
*@param : addr: The PSRAM address for reading
*@param : buf:  Data buffer address pointer for storing PSRAM data
*@param : len:  Data length to read
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_DMA_Read(uint32_t addr, void *buf,  uint32_t len)
{
    uint8_t tmp[4];
    
//    if(!psram_info.flag_dma_cfged)
//        return ERR_PSRAM_DMANOTCFGED;
    
    tmp[0] = APS1604M_CMD_RD;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);
    
    __SWITCH_TO_FIFO_MODE();

    SPI_PSRAM_CS_L();
    
    __SPI_PSRAM_Write_Data(tmp, 4);

    SPI_Receive_DMA(buf, len);
    while (SPI_GetRxState() == SPI_RX_STATE_RECEIVING);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
        
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM DMA write 1x mode
*         
*@param : addr: The PSRAM address for writing
*@param : buf:  Data buffer address pointer of writing data
*@param : len:  Data length to write
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_DMA_Write(uint32_t addr, void *buf,  uint32_t len)
{
    uint8_t tmp[4];
    
//    if(!psram_info.flag_dma_cfged)
//        return ERR_PSRAM_DMANOTCFGED;

    tmp[0] = APS1604M_CMD_WR;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);

    __SWITCH_TO_FIFO_MODE();

    SPI_PSRAM_CS_L();
    
    __SPI_PSRAM_Write_Data(tmp, 4);

    SPI_Transmit_DMA(buf, len);
    
    while (SPI_GetTxState() == SPI_TX_STATE_SENDING);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
        
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM DMA read 4x mode
*         
*@param : addr: The PSRAM address for reading
*@param : buf:  Data buffer address pointer for storing PSRAM data
*@param : len:  Data length to read
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_QPI_DMA_Read(uint32_t addr, void *buf,  uint32_t len)
{
    uint8_t tmp[8];
    
    
//    if(!psram_info.flag_dma_cfged)
//        return ERR_PSRAM_DMANOTCFGED;
    
    tmp[0] = APS1604M_CMD_FAST_QRD;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);
	tmp[4] = 0xFF;
	tmp[5] = 0xFF;
	tmp[6] = 0xFF;
    
    __SWITCH_TO_FIFO_MODE();
    SPI_PSRAM_CS_L();

	__SPI_PSRAM_Write_Data(tmp, 1);
    
    __SPI_PSRAM_SET_4X_MODE();
    
    __SPI_PSRAM_Write_Data(&tmp[1], 6);

    SPI_Receive_DMA(buf, len);
    while (SPI_GetRxState() == SPI_RX_STATE_RECEIVING);

    SPI_PSRAM_CS_H();
    __SWITCH_TO_XIP_MODE();
    
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM DMA write 4x mode
*         
*@param : addr: The PSRAM address for writing
*@param : buf:  Data buffer address pointer of writing data
*@param : len:  Data length to write
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_QPI_DMA_Write(uint32_t addr, void *buf,  uint32_t len)
{
    uint8_t tmp[8];
    
//    if(!psram_info.flag_dma_cfged)
//        return ERR_PSRAM_DMANOTCFGED;
 
    tmp[0] = APS1604M_CMD_QWR;
    tmp[1] = (uint8_t)(addr >> 16 & 0xFF);
    tmp[2] = (uint8_t)(addr >> 8  & 0xFF);
    tmp[3] = (uint8_t)(addr >> 0  & 0xFF);
    
    __SWITCH_TO_FIFO_MODE();
    
    SPI_PSRAM_CS_L();
    
    __SPI_PSRAM_Write_Data(tmp, 1);
    
    __SPI_PSRAM_SET_4X_MODE();
    
    __SPI_PSRAM_Write_Data(&tmp[1], 3);

    SPI_Transmit_DMA(buf, len);
    while (SPI_GetTxState() == SPI_TX_STATE_SENDING);
        
    SPI_PSRAM_CS_H();
    __SWITCH_TO_XIP_MODE();    

    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : Set PSRAM in QPI mode
*         
*@param : None
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_Enter_QPI_Mode(void)
{
    //0x35
    uint8_t tmp;
    
    tmp = APS1604M_CMD_ENTER_QPI_MODE;

    __SWITCH_TO_FIFO_MODE();
    
    __SPI_PSRAM_SET_1X_MODE();

    SPI_PSRAM_CS_L();
    
    __SPI_PSRAM_Write_Data(&tmp, 1);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
    
    psram_info.mode = PSRAM_QPI_MODE;
    
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : PSRAM Exit QPI mode
*         
*@param : None
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_Exit_QPI_Mode(void)
{
    //0xF5
    uint8_t tmp;
    
    tmp = APS1604M_CMD_EXIT_QPI_MODE;

    __SWITCH_TO_FIFO_MODE();
    
    __SPI_PSRAM_SET_1X_MODE();

    SPI_PSRAM_CS_L();
    
    __SPI_PSRAM_Write_Data(&tmp, 1);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
    
    psram_info.mode = PSRAM_SPI_MODE;
    
    return ERR_PSRAM_NONE;
}

/******************************************************************************
*@brief : Reset PSRAM
*         
*@param : None
*@return: HAL_SPI_PSRAM_Status
******************************************************************************/
HAL_SPI_PSRAM_Status IC_APS1604M_Reset(void)
{
    
    //0x35
    uint8_t tmp;
    
    tmp = APS1604M_CMD_RST_EN;

    __SWITCH_TO_FIFO_MODE();
    
    __SPI_PSRAM_SET_1X_MODE();

    SPI_PSRAM_CS_L();
    
    __SPI_PSRAM_Write_Data(&tmp, 1);
    
    tmp = APS1604M_CMD_RST;
    
    __SPI_PSRAM_Write_Data(&tmp, 1);
    
    SPI_PSRAM_CS_H();
    
    __SWITCH_TO_XIP_MODE();
    
    psram_info.mode = PSRAM_SPI_MODE;
    
    return ERR_PSRAM_NONE;
}

