/*
 * Copyright 2020-2022 Yuntu Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*!
 * @file qspi_driver.h
 * @version 1.4.0
 */

#ifndef QSPI_DRIVER_H
#define QSPI_DRIVER_H

#include <stddef.h>
#include <stdbool.h>
#include "device_registers.h"
#include "status.h"


/*!
 * @ingroup qspi_drv
 * @{
 */

/*******************************************************************************
 * Enumerations.
 ******************************************************************************/

/*! @brief Number of AHB buffers in the device
 */
#define QSPI_AHB_BUFFERS   4U

/*! @brief Key to lock/unlock LUT
 */
#define QSPI_LUT_LOCK_KEY   0x5AF05AF0U


/*! @brief Lut commands 
 * Implements : qspi_lut_commands_t_Class
 */
typedef enum
{
    QSPI_LUT_CMD_STOP            = 0x00U,    /*!<  End of sequence                            */
    QSPI_LUT_CMD_CMD             = 0x01U,    /*!<  Command                                    */
    QSPI_LUT_CMD_ADDR            = 0x02U,    /*!<  Address                                    */
    QSPI_LUT_CMD_DUMMY           = 0x03U,    /*!<  Dummy cycles                               */
    QSPI_LUT_CMD_CADDR           = 0x04U,    /*!<  Column address                             */
    QSPI_LUT_CMD_MODE            = 0x05U,    /*!<  8-bit mode                                 */
    QSPI_LUT_CMD_MODE2           = 0x06U,    /*!<  2-bit mode                                 */
    QSPI_LUT_CMD_MODE4           = 0x07U,    /*!<  4-bit mode                                 */
    QSPI_LUT_CMD_READ            = 0x08U,    /*!<  Read data                                  */
    QSPI_LUT_CMD_WRITE           = 0x09U,    /*!<  Write data                                 */
    QSPI_LUT_CMD_DATA_TEST       = 0x0AU,    /*!<  Read data for test                         */
    QSPI_LUT_CMD_STEST           = 0x0BU,    /*!<  Read out data loopback test                */

    QSPI_LUT_CMD_JUMP            = 0x0FU,    /*!<  Jump to the instruction pointed by operand */

    QSPI_LUT_CMD_CMD_DDR         = 0x11U,    /*!<  Command - DDR mode                         */
    QSPI_LUT_CMD_ADDR_DDR        = 0x12U,    /*!<  Address - DDR mode                         */
    QSPI_LUT_CMD_CADDR_DDR       = 0x14U,    /*!<  Column address - DDR mode                  */
    QSPI_LUT_CMD_MODE_DDR        = 0x15U,    /*!<  8-bit mode - DDR mode                      */
    QSPI_LUT_CMD_MODE2_DDR       = 0x16U,    /*!<  2-bit mode - DDR mode                      */
    QSPI_LUT_CMD_MODE4_DDR       = 0x17U,    /*!<  4-bit mode - DDR mode                      */
    QSPI_LUT_CMD_READ_DDR        = 0x18U,    /*!<  Read data - DDR mode                       */
    QSPI_LUT_CMD_WRITE_DDR       = 0x19U,    /*!<  Write data - DDR mode                      */
    QSPI_LUT_CMD_DATA_TSET_DDR   = 0x1AU,    /*!<  Read data for test - DDR mode              */
    QSPI_LUT_CMD_STEST_DDR       = 0x1BU,    /*!<  Read out data loopback test - DDR mode     */

} qspi_lut_commands_t;


/*! @brief Lut pad options 
 * Implements : qspi_lut_pads_t_Class
 */
typedef enum
{
    QSPI_LUT_PADS_1              = 0U,    /*!<  1 Pad      */
    QSPI_LUT_PADS_2              = 1U,    /*!<  2 Pads     */
    QSPI_LUT_PADS_4              = 2U,    /*!<  4 Pads     */
    QSPI_LUT_PADS_8              = 3U,    /*!<  8 Pads     */
} qspi_lut_pads_t;

/*! @brief Driver type
 * Implements : qspi_transfer_type_t_Class
 */
typedef enum
{
    QSPI_TRANSFER_TYPE_SYNC         = 0U,   /*!< Synchronous transfer using polling    */
    QSPI_TRANSFER_TYPE_ASYNC_INT    = 1U,   /*!< Interrupt-based asynchronous transfer */
    QSPI_TRANSFER_TYPE_ASYNC_DMA    = 2U,   /*!< DMA-based asynchronous transfer       */
} qspi_transfer_type_t;


/*! @brief Endianess options
 * Implements : qspi_endianess_t_Class
 */
typedef enum
{
    QSPI_END_NO_SWAP         = 0U,  /*!< No swapping  */
    QSPI_END_32BIT_SWAP      = 1U,  /*!< Byte swapping in 32 bit  */
    QSPI_END_64BIT_SWAP      = 2U,  /*!< Word swapping in 64 bit  */
    QSPI_END_ALL_SWAP        = 3U,  /*!< Word swapping in 64 bit and byte swapping in 32 bit  */
} qspi_endianess_t;


/*! @brief Clock phase used for sampling Rx data
 * Implements : qspi_date_rate_t_Class
 */
typedef enum
{
    QSPI_DATE_RATE_SDR         = 0U,    /*!<  Single data rate    */
    QSPI_DATE_RATE_DDR         = 1U,    /*!<  Double data rate    */
} qspi_date_rate_t;


/*! @brief Clock edge used for sampling Rx data
 * Implements : qspi_sample_edge_t_Class
 */
typedef enum
{
    QSPI_SAMPLE_EDGE_RAISE    = 0U,    /*!<  Sampling on rising edge  */
    QSPI_SAMPLE_EDGE_FALL     = 1U,    /*!<  Sampling on falling edge */
} qspi_sample_edge_t;


/*! @brief Alignment of outgoing data with serial clock
 * Implements : qspi_flash_data_align_t_Class
 */
typedef enum
{
    QSPI_FLASH_DATA_ALIGN_REFCLK      = 0U,    /*!<  Data aligned with the posedge of Internal reference clock of QSPI  */
    QSPI_FLASH_DATA_ALIGN_4X_REFCLK   = 1U,    /*!<  Data aligned with 4x serial flash half clock                          */
} qspi_flash_data_align_t;


/*******************************************************************************
* Definitions
******************************************************************************/

/*! @brief QSPI callback function type. */
typedef void (* qspi_callback_t)(uint32_t instance, void * param);

/*!
 * @brief Driver configuration structure
 *
 * This structure is used to provide configuration parameters for the qspi driver
 * at initialization time.
 * Implements : qspi_user_config_t_Class
 */
typedef struct
{
    qspi_date_rate_t dataRate;          /*!< Single/double data rate                                 */
    bool dmaSupport;                    /*!< Enables DMA support in the driver                       */
    uint8_t rxDMAChannel;               /*!< Rx DMA channel number. Only used if dmaSupport is true  */
    uint8_t txDMAChannel;               /*!< Tx DMA channel number. Only used if dmaSupport is true  */
    qspi_callback_t callback;           /*!< User callback for reporting asynchronous events         */
    void * callbackParam;               /*!< Parameter for user callback                             */
    bool dqs;                           /*!< Enable DQS, Only used in HyperFlash/HyperRAM            */
    uint32_t memSizeA;                  /*!< Size of serial flash A                                  */
    uint32_t memSizeB;                  /*!< Size of serial flash B                                  */
    uint8_t csHoldTime;                 /*!< CS hold time, expressed in serial clock cycles          */
    uint8_t csSetupTime;                /*!< CS setup time, expressed in serial clock cycles         */
    uint8_t columnAddr;                 /*!< Width of the column address, 0 if not used              */
    bool wordAddresable;                /*!< True if serial flash is word addressable                */
    bool loopback;                      /*!< Enable loopback mode                                    */
    bool dataTestMode;                  /*!< Enable data test mode, sampling data with data test.    */
    uint8_t sampleDelay;                /*!< Delay (in clock cycles) used for sampling Rx data       */
    qspi_sample_edge_t sampleEdge;      /*!< Clock phase used for sampling Rx data                   */
    uint8_t coarseDelay;                /*!< Loopback or DQS coarse delay configuration              */
    bool dqsLatency;                    /*!< Enable DQS latency for reads (Hyperflash)               */
    qspi_flash_data_align_t dataAlign;  /*!< Alignment of output data sent to serial flash           */
    bool ddrHalf;                       /*!< Half cycle sample offset in DDR mode.                   */
    qspi_endianess_t endianess;         /*!< Endianess configuration                                 */
    uint8_t io2IdleValue;               /*!< (0 / 1) Logic level of IO[2] signal when not used       */
    uint8_t io3IdleValue;               /*!< (0 / 1) Logic level of IO[3] signal when not used       */
    bool clockStop;                     /*!< Enable clock output stop when RxFIFO full               */
#if defined(FEATURE_QSPI_SUPPORT_OTFA) && (FEATURE_QSPI_SUPPORT_OTFA == 1)
    bool otfaEnable;                    /*!< Enable OTFA                                             */
    uint32_t otfaStartAddr;             /*!< OTFA start address                                      */
    uint32_t otfaEndAddr;               /*!< OTFA end address                                        */
#endif
#if defined (FEATURE_QSPI_SUPPORT_BACKUP) && (FEATURE_QSPI_SUPPORT_BACKUP == 1)
    bool backupEnable;                  /*!< Enable backup data check                                */
#endif
} qspi_user_config_t;

/*!
 * @brief AHB configuration structure
 *
 * This structure is used to provide configuration parameters for AHB access 
 * to the external flash
 * Implements : qspi_ahb_config_t_Class
 */
typedef struct
{
    uint8_t masters[QSPI_AHB_BUFFERS];    /*!< List of AHB masters assigned to each buffer          */
    uint16_t sizes[QSPI_AHB_BUFFERS];     /*!< List of buffer sizes                                 */
    bool allMasters;                      /*!< Indicates that any master may access the last buffer */
    bool highPriority;                    /*!< Indicates that the first buffer has high priority    */
} qspi_ahb_config_t;


/*!
 * @brief Driver internal context structure
 *
 * This structure is used by the driver for its internal logic. It must
 * be provided by the application through the QSPI_DRV_Init() function, then
 * it cannot be freed until the driver is de-initialized using QSPI_DRV_Deinit().
 * The application should make no assumptions about the content of this structure.
 */
typedef struct
{
/*! @cond DRIVER_INTERNAL_USE_ONLY */
    uint32_t instance;                  /* QSPI instance number                                 */
#if defined(CPU_YTM32B1HA0)
    bool dataTestMode;                  /* Enable data test mode, sampling data with data test. */
#endif
    bool dmaSupport;                    /* Enables DMA support in the driver                    */
    uint8_t rxDMAChannel;               /* Channel number for DMA rx channel                    */
    uint8_t txDMAChannel;               /* Channel number for DMA tx channel                    */
    status_t status;                    /* Status of the current operation                      */
    bool driverBusy;                    /* Driver is busy with an operation                     */
    qspi_callback_t callback;           /* User callback for reporting asynchronous events      */
    void * callbackParam;               /* Parameter for user callback                          */
    uint8_t * data;                     /* Buffer for data being currently received             */
    const uint8_t * roData;             /* Read-only data (for program or verify operations)    */
    uint32_t size;                      /* Size of data being currently transmitted or received */
    uint8_t operation;                  /* True if receiving in interrupt mode                  */
/*! @endcond */
} qspi_state_t;


/*******************************************************************************
 * API
 ******************************************************************************/
/*!
 * @name QSPI Driver
 * @{
 */

#if defined(__cplusplus)
extern "C" {
#endif


/*!
 * @brief Initializes the qspi driver
 *
 * This function initializes the qspi driver and prepares it for operation.
 *
 * @param instance     QSPI peripheral instance number
 * @param userConfigPtr    Pointer to the qspi user configuration structure. The
 *                         function reads configuration data from this structure and initializes the
 *                         driver accordingly. The application may free this structure after
 *                         the function returns.
 * @param master    Pointer to the qspi context structure. The driver uses
 *                  this memory area for its internal logic. The application must make no
 *                  assumptions about the content of this structure, and must not free this
 *                  memory until the driver is de-initialized using QSPI_DRV_Deinit().
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_Init(uint32_t instance,
                       const qspi_user_config_t * userConfigPtr,
                       qspi_state_t * state);


/*!
 * @brief De-initialize the qspi driver
 *
 * This function de-initializes the qspi driver. The driver can't be used
 * again until reinitialized. The context structure is no longer needed by the driver and
 * can be freed after calling this function.
 *
 * @param instance     QSPI peripheral instance number
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_Deinit(uint32_t instance);


/*!
 * @brief Returns default configuration structure for QSPI
 *
 * @param userConfigPtr    Pointer to the qspi user configuration structure.
 * @return    Always returns STATUS_SUCCESS
 */
status_t QSPI_DRV_GetDefaultConfig(qspi_user_config_t * userConfigPtr);


/*!
 * @brief Sets up AHB accesses to the serial flash
 *
 * @param instance   QSPI peripheral instance number
 * @param config     AHB configuration structure
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_AhbSetup(uint32_t instance, const qspi_ahb_config_t *config);



/*!
 * @brief Configures LUT commands
 *
 * This function configures a pair of LUT commands in the specified LUT register.
 * LUT sequences start at index multiple of 4 and can have up to 8 commands
 *
 * @param instance     QSPI peripheral instance number
 * @param lut          Index of LUT register
 * @param instr0       First instruction
 * @param pad0         Number of pads to use for first instruction
 * @param oprnd0       Operand for first instruction
 * @param instr1       Second instruction
 * @param pad1         Number of pads to use for second instruction
 * @param oprnd1       Operand for second instruction
 * Implements : QSPI_DRV_SetLut_Activity
 */
void QSPI_DRV_SetLut(uint32_t instance,
                     uint8_t lut,
                     qspi_lut_commands_t instr0,
                     qspi_lut_pads_t pad0,
                     uint8_t oprnd0,
                     qspi_lut_commands_t instr1,
                     qspi_lut_pads_t pad1,
                     uint8_t oprnd1);

/*!
 * @brief Set DataTest Pattern
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_SetDataTestPattern_Activity
 */
void QSPI_DRV_SetDataTestPattern(uint32_t instance, uint32_t pattern);


/*!
 * @brief Get Data Test Status (disable or enable)
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_GetDataTestRetryStatus_Activity
 */
bool QSPI_DRV_GetDataTestStatus(uint32_t instance);


/*!
 * @brief Get Data Test Retry Status
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_GetDataTestRetryStatus_Activity
 */
bool QSPI_DRV_GetDataTestRetryStatus(uint32_t instance);


/*!
 * @brief Get Data Test Fail Status
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_GetDataTestFailStatus_Activity
 */
bool QSPI_DRV_GetDataTestFailStatus(uint32_t instance);


/*!
 * @brief Locks LUT table
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_LockLut_Activity
 */
void QSPI_DRV_LockLut(uint32_t instance);



/*!
 * @brief Unlocks LUT table
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_UnlockLut_Activity
 */
void QSPI_DRV_UnlockLut(uint32_t instance);


/*!
 * @brief Clears IP sequence pointer
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_ClearIpSeqPointer_Activity
 */
void QSPI_DRV_ClearIpSeqPointer(uint32_t instance);


/*!
 * @brief Clears AHB sequence pointer
 *
 * @param instance     QSPI peripheral instance number
 * Implements : QSPI_DRV_ClearAHBSeqPointer_Activity
 */
void QSPI_DRV_ClearAHBSeqPointer(uint32_t instance);


/*!
 * @brief Sets sequence ID for AHB operations
 *
 * @param instance     QSPI peripheral instance number
 * @param seqID        Sequence ID in LUT for read operation
 * Implements : QSPI_DRV_SetAhbSeqId_Activity
 */
void QSPI_DRV_SetAhbSeqId(uint32_t instance, uint8_t seqID);


/*!
 * @brief Launches a simple IP command
 *
 * @param instance     QSPI peripheral instance number
 * @param lut          Index of LUT register
 * @param addr         Address of the target serial flash
 * @param timeout      timeout for the transfer in milliseconds
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_IpCommand(uint32_t instance, uint8_t lut, uint32_t addr, uint32_t timeout);


/*!
 * @brief Launches an IP read command
 *
 * This function can launch a read command in 3 modes:
 * - normal read (dataRead != NULL): Data is read from serial flash and placed in the buffer
 * - verify (dataRead == NULL, dataCmp != NULL): Data is read from serial flash and compared to the reference buffer
 * - blank check (dataRead == NULL, dataCmp == NULL): Data is read from serial flash and compared to 0xFF
 * Only normal read mode can use DMA.
 *
 * @param instance       QSPI peripheral instance number
 * @param lut            Index of LUT register
 * @param addr           Start address for read operation in serial flash
 * @param dataRead       Buffer where to store read data
 * @param dataCmp        Buffer to be compared to read data
 * @param size           Size of data buffer
 * @param transferType   Type of transfer
 * @param timeout  timeout for the transfer in milliseconds; only applies for synchronous transfers
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_IpRead(uint32_t instance, 
                         uint8_t lut, 
                         uint32_t addr, 
                         uint8_t * dataRead,
                         const uint8_t * dataCmp,
                         uint32_t size,
                         qspi_transfer_type_t transferType,
                         uint32_t timeout);


/*!
 * @brief Launches an IP write command
 *
 * @param instance       QSPI peripheral instance number
 * @param lut            Index of LUT register
 * @param addr           Start address for write operation in serial flash
 * @param data           Data to be programmed in flash
 * @param size           Size of data buffer
 * @param transferType   Type of transfer
 * @param timeout  timeout for the transfer in milliseconds; only applies for synchronous transfers
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_IpWrite(uint32_t instance, 
                          uint8_t lut, 
                          uint32_t addr, 
                          const uint8_t * data, 
                          uint32_t size,
                          qspi_transfer_type_t transferType,
                          uint32_t timeout);


/*!
 * @brief Launches an IP erase command
 *
 * @param instance     QSPI peripheral instance number
 * @param lut          Index of LUT register
 * @param addr         Start address of erased sector
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_IpErase(uint32_t instance, 
                          uint8_t lut, 
                          uint32_t addr);


/*!
 * @brief Checks the status of the currently running IP command
 *
 * @param instance     QSPI peripheral instance number
 * @return    Error or success status returned by API
 */
status_t QSPI_DRV_IpGetStatus(uint32_t instance);


/*! @}*/
#if defined(__cplusplus)
}
#endif

/*! @}*/

#endif /* QSPI_DRIVER_H */
/*******************************************************************************
 * EOF
 ******************************************************************************/
