/*****************************************************************************
 * Copyright (c) 2019, Nsing Technologies Pte. Ltd.
 *
 * All rights reserved.
 * ****************************************************************************
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Nations' name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY NATIONS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL NATIONS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************/

/**
 * @file qspi_cfg.c
 * @author Nations
 * @version v1.0.0
 *
 * @copyright Copyright (c) 2019, Nsing Technologies Pte. Ltd. All rights reserved.
 */

#include "qspi_cfg.h"
#include "n32g45x_qspi.h"
#include "n32g45x_dma.h"
#include "n32g45x_gpio.h"

/**
  * @brief  Select format of QSPI.
  * @param qspi_format_sel Select format of QSPI.
                        STANDARD_SPI_FORMAT_SEL:Standard spi
                        DUAL_SPI_FORMAT_SEL:Dual spi
                        QUAD_SPI_FORMAT_SEL:Quad spi
                        XIP_SPI_FORMAT_SEL: Memory mapping mode
  * @param data_dir The direction of transferring data.
                        TX_AND_RX:Transmit and receive data.
                        RX_ONLY:Receive data only.
                        TX_ONLY:Transmit data only.
  * @param count Number of data frames. It is valid only in RX_ONLY mode of DUAL_SPI_FORMAT_SEL or QUAD_SPI_FORMAT_SEL.
  */
void QspiInit(QSPI_FORMAT_SEL qspi_format_sel, QSPI_DATA_DIR data_dir, uint16_t count)
{
    QSPI_InitType QSPI_InitStruct = {0};

    switch (qspi_format_sel)
    {
    case STANDARD_SPI_FORMAT_SEL:
        QSPI_GPIO(QSPI_AFIO_PORT_SEL, 0, 1);

        QSPI_DeInit();
        RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_QSPI, ENABLE);
        QSPI_InitStruct.SPI_FRF = QSPI_CTRL0_SPI_FRF_STANDARD_FORMAT; // standard SPI frame format
        QSPI_InitStruct.TMOD = QSPI_CTRL0_TMOD_TX_AND_RX;             // transfer mode
        QSPI_InitStruct.SCPOL = QSPI_CTRL0_SCPOL_HIGH;                // serial clock polarity
        QSPI_InitStruct.SCPH = QSPI_CTRL0_SCPH_SECOND_EDGE;           // serial clock phase
        QSPI_InitStruct.DFS = QSPI_CTRL0_DFS_8_BIT;                   // data frame size
        QSPI_InitStruct.CLK_DIV = CLOCK_DIVIDER;                      // clock divider
        QSPI_InitStruct.TXFT = QSPI_TXFT_TEI_0;                       // transmit fifo threshold
        QSPI_InitStruct.RXFT = QSPI_RXFT_TFI_0;                       // receive fifo threshold
        QSPI_InitStruct.NDF = CTRL1_NDF_CNT;                          // number of data frames

        QspiInitConfig(&QSPI_InitStruct);

        QSPI_Cmd(ENABLE);
        break;

    case DUAL_SPI_FORMAT_SEL:
        QSPI_GPIO(QSPI_AFIO_PORT_SEL, 0, 0);

        QSPI_DeInit();
        RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_QSPI, ENABLE); // enable clock of qspi
        GPIO_ConfigPinRemap(GPIO_RMP_QSPI_XIP_EN, DISABLE);  // disable XIP

        QSPI_InitStruct.SPI_FRF = QSPI_CTRL0_SPI_FRF_DUAL_FORMAT; // dual SPI frame format
        if (data_dir == TX_ONLY)
        {
            QSPI_InitStruct.TMOD = QSPI_CTRL0_TMOD_TX_ONLY; // TX transfer mode
            QSPI_InitStruct.NDF = 0;                        // number of data frames
            QSPI_InitStruct.ENHANCED_WAIT_CYCLES = 0;       // wait cycles of dummy
        }
        else if (data_dir == RX_ONLY)
        {
            QSPI_InitStruct.TMOD = QSPI_CTRL0_TMOD_RX_ONLY;                     // RX transfer mode
            QSPI_InitStruct.NDF = count;                                        // number of data frames
            QSPI_InitStruct.ENHANCED_WAIT_CYCLES = QSPI_ENH_CTRL0_WAIT_8CYCLES; // wait cycles of dummy
        }
        QSPI_InitStruct.CFS = QSPI_CTRL0_CFS_8_BIT;         // control frame size
        QSPI_InitStruct.SCPOL = QSPI_CTRL0_SCPOL_HIGH;      // serial clock polarity
        QSPI_InitStruct.SCPH = QSPI_CTRL0_SCPH_SECOND_EDGE; // serial clock phase
        QSPI_InitStruct.FRF = QSPI_CTRL0_FRF_MOTOROLA;      // frame format
        QSPI_InitStruct.DFS = QSPI_CTRL0_DFS_8_BIT;         // data frame size
        QSPI_InitStruct.CLK_DIV = CLOCK_DIVIDER;            // clock divider
        QSPI_InitStruct.TXFT = QSPI_TXFT_TEI_0;             // transmit fifo threshold
        QSPI_InitStruct.RXFT = QSPI_RXFT_TFI_0;             // receive fifo threshold

        QSPI_InitStruct.ENHANCED_CLK_STRETCH_EN = QSPI_ENH_CTRL0_CLK_STRETCH_EN; // enable stretch
        QSPI_InitStruct.ENHANCED_ADDR_LEN = QSPI_ENH_CTRL0_ADDR_LEN_24_BIT;      // length of address to transmit
        QSPI_InitStruct.ENHANCED_INST_L = QSPI_ENH_CTRL0_INST_L_8_LINE;          // instruction length

        QspiInitConfig(&QSPI_InitStruct);
        QSPI_Cmd(ENABLE);
        break;

    case QUAD_SPI_FORMAT_SEL:
        QSPI_GPIO(QSPI_AFIO_PORT_SEL, 0, 0);

        QSPI_DeInit();
        RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_QSPI, ENABLE); // enable clock of qspi
        GPIO_ConfigPinRemap(GPIO_RMP_QSPI_XIP_EN, DISABLE);  // disable XIP

        QSPI_InitStruct.SPI_FRF = QSPI_CTRL0_SPI_FRF_QUAD_FORMAT; // quad SPI frame format
        if (data_dir == TX_ONLY)
        {
            QSPI_InitStruct.TMOD = QSPI_CTRL0_TMOD_TX_ONLY; // TX transfer mode
            QSPI_InitStruct.NDF = 0;                        // number of data frames
            QSPI_InitStruct.ENHANCED_WAIT_CYCLES = 0;       // wait cycles of dummy
        }
        else if (data_dir == RX_ONLY)
        {
            QSPI_InitStruct.TMOD = QSPI_CTRL0_TMOD_RX_ONLY;                     // RX transfer mode
            QSPI_InitStruct.NDF = count;                                        // number of data frames
            QSPI_InitStruct.ENHANCED_WAIT_CYCLES = QSPI_ENH_CTRL0_WAIT_8CYCLES; // wait cycles of dummy
        }
        QSPI_InitStruct.CFS = QSPI_CTRL0_CFS_8_BIT;         // control frame size
        QSPI_InitStruct.SCPOL = QSPI_CTRL0_SCPOL_HIGH;      // serial clock polarity
        QSPI_InitStruct.SCPH = QSPI_CTRL0_SCPH_SECOND_EDGE; // serial clock phase
        QSPI_InitStruct.FRF = QSPI_CTRL0_FRF_MOTOROLA;      // frame format
        QSPI_InitStruct.DFS = QSPI_CTRL0_DFS_8_BIT;         // data frame size
        QSPI_InitStruct.CLK_DIV = CLOCK_DIVIDER;            // clock divider
        QSPI_InitStruct.TXFT = QSPI_TXFT_TEI_0;             // transmit fifo threshold
        QSPI_InitStruct.RXFT = QSPI_RXFT_TFI_0;             // receive fifo threshold

        QSPI_InitStruct.ENHANCED_CLK_STRETCH_EN = QSPI_ENH_CTRL0_CLK_STRETCH_EN; // enable stretch
        QSPI_InitStruct.ENHANCED_ADDR_LEN = QSPI_ENH_CTRL0_ADDR_LEN_24_BIT;      // length of address to transmit
        QSPI_InitStruct.ENHANCED_INST_L = QSPI_ENH_CTRL0_INST_L_8_LINE;          // instruction length

        QspiInitConfig(&QSPI_InitStruct);
        QSPI_Cmd(ENABLE);
        break;

    case XIP_SPI_FORMAT_SEL:
        QSPI_GPIO(QSPI_AFIO_PORT_SEL, 0, 0);

        QSPI_DeInit();
        RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_QSPI, ENABLE); // enable clock of qspi
        GPIO_ConfigPinRemap(GPIO_RMP_QSPI_XIP_EN, DISABLE);  // disable XIP

        QSPI_InitStruct.SPI_FRF = QSPI_CTRL0_SPI_FRF_QUAD_FORMAT; // quad SPI frame format
        QSPI_InitStruct.TMOD = QSPI_CTRL0_TMOD_RX_ONLY;           // transfer mode
        QSPI_InitStruct.NDF = count;                              // number of data frames
        QSPI_InitStruct.CFS = QSPI_CTRL0_CFS_8_BIT;               // control frame size
        QSPI_InitStruct.SCPOL = QSPI_CTRL0_SCPOL_HIGH;            // serial clock polarity
        QSPI_InitStruct.SCPH = QSPI_CTRL0_SCPH_SECOND_EDGE;       // serial clock phase
        QSPI_InitStruct.FRF = QSPI_CTRL0_FRF_MOTOROLA;            // frame format
        QSPI_InitStruct.DFS = QSPI_CTRL0_DFS_8_BIT;               // data frame size
        QSPI_InitStruct.CLK_DIV = CLOCK_DIVIDER;                  // clock divider
        QSPI_InitStruct.TXFT = QSPI_TXFT_TEI_0;                   // transmit fifo threshold
        QSPI_InitStruct.RXFT = QSPI_RXFT_TFI_0;                   // receive fifo threshold

        QSPI_InitStruct.ENHANCED_CLK_STRETCH_EN = QSPI_ENH_CTRL0_CLK_STRETCH_EN; // enable stretch

        QSPI_InitStruct.XIP_MBL = QSPI_XIP_CTRL_XIP_MBL_LEN_8_BIT; // XIP mode bits length
        QSPI_InitStruct.XIP_CT_EN = QSPI_XIP_CTRL_XIP_CT_EN;       // enable continuous transfer in XIP mode
        QSPI_InitStruct.XIP_INST_EN = QSPI_XIP_CTRL_XIP_INST_EN;   // enable XIP instruction
        // QSPI_InitStruct.XIP_DFS_HC    = QSPI_XIP_CTRL_DFS_HC; //Fix DFS for XIP transfer
        QSPI_InitStruct.XIP_ADDR_LEN = QSPI_XIP_CTRL_ADDR_24BIT;      // length of address to transmit
        QSPI_InitStruct.XIP_INST_L = QSPI_XIP_CTRL_INST_L_8_LINE;     // instruction length
        QSPI_InitStruct.XIP_WAIT_CYCLES = QSPI_XIP_CTRL_WAIT_8CYCLES; // wait cycles of dummy
        QSPI_InitStruct.XIP_FRF = QSPI_XIP_CTRL_FRF_4_LINE;           // frame format
        QSPI_InitStruct.XIP_MD_BITS = 0xaabb;                         // content of mode stage
        QSPI_InitStruct.ITOC = 0x6b;                                  // 0X6B = QUAD Read
        QSPI_InitStruct.WTOC = 0x6b;                                  // WTOC

        QspiInitConfig(&QSPI_InitStruct);

        /** enable XIP */
        QSPI_XIP_Cmd(ENABLE);
        QSPI_Cmd(ENABLE);

        GPIO_ConfigPinRemap(GPIO_RMP_QSPI_XIP_EN, ENABLE); // enable memory map of XIP mode
        break;

    default:
        break;
    }
}
/**
  * @brief  Configuration of QSPI DMA.
  * @param PeripheraAddr start address of peripheral.
  * @param MemoryAddr start address of memory.
  * @param Len size of buffer.
  * @param TxRx transmit or receive data.
               QSPI_DMA_CTRL_TX_DMA_EN:transmit data
               QSPI_DMA_CTRL_RX_DMA_EN:receive data
  */
void QSPI_DMA_Config(uint32_t PeripheraAddr, uint32_t MemoryAddr, uint16_t Len, uint8_t TxRx)
{
    DMA_InitType DMA_InitStructure;
    DMA_ChannelType *DMAyChx;
    uint32_t Direction;

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA2, ENABLE);

    if (TxRx == QSPI_DMA_CTRL_TX_DMA_EN) // DMA send enable
    {
        QSPI_Tx_DMA_CTRL_Config(ENABLE, 1);
        DMAyChx = QSPI_TX_DMN_CH;
        Direction = DMA_DIR_PERIPH_DST; // data direction:internal memory -> peripheral device
    }
    if (TxRx == QSPI_DMA_CTRL_RX_DMA_EN) // DMA receive enable
    {
        QSPI_Rx_DMA_CTRL_Config(ENABLE, 1);
        DMAyChx = QSPI_RX_DMN_CH;
        Direction = DMA_DIR_PERIPH_SRC; // data direction:peripheral device -> internal memory
    }

    DMA_DeInit(DMAyChx);
    DMA_InitStructure.PeriphAddr = PeripheraAddr; // Peripheral data address
    DMA_InitStructure.MemAddr = MemoryAddr;       // memory address
    DMA_InitStructure.Direction = Direction;      //
    DMA_InitStructure.BufSize = Len;
    DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;         // Peripheral address not increment
    DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;         // DMA memory auto increment
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_WORD; // Peripheral data In word
    DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Word;      //    memory data In word
    DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority = DMA_PRIORITY_HIGH; // DMA channel High Priority
    DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;    // not memory-to-memory mode
    DMA_Init(DMAyChx, &DMA_InitStructure);

    DMA_EnableChannel(DMAyChx, ENABLE); // Enable DMA
}
