/*
 * Copyright 2014-2015 Freescale Semiconductor, Inc.
 * Copyright 2016-2018 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#ifndef __FLEXSPI_NOR_FLASH_H__
#define __FLEXSPI_NOR_FLASH_H__

#include <fsl_flexspi.h>
#include "fsl_common.h"
#include "bootloader_common.h"

/*  */
#define NOR_CMD_INDEX_READ CMD_INDEX_READ               //!< 0
#define NOR_CMD_INDEX_READSTATUS CMD_INDEX_READSTATUS   //!< 1
#define NOR_CMD_INDEX_WRITEENABLE CMD_INDEX_WRITEENABLE //!< 2
#define NOR_CMD_INDEX_ERASESECTOR 3                     //!< 3
#define NOR_CMD_INDEX_PAGEPROGRAM CMD_INDEX_WRITE       //!< 4
#define NOR_CMD_INDEX_CHIPERASE 5                       //!< 5
#define NOR_CMD_INDEX_DUMMY 6                           //!< 6
#define NOR_CMD_INDEX_ERASEBLOCK 7                      //!< 7

#define NOR_CMD_LUT_SEQ_IDX_READ CMD_LUT_SEQ_IDX_READ //!< 0  READ LUT sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_READSTATUS \
    CMD_LUT_SEQ_IDX_READSTATUS //!< 1  Read Status LUT sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_READSTATUS_XPI \
    2 //!< 2  Read status DPI/QPI/OPI sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE \
    CMD_LUT_SEQ_IDX_WRITEENABLE //!< 3  Write Enable sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_WRITEENABLE_XPI \
    4 //!< 4  Write Enable DPI/QPI/OPI sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_ERASESECTOR 5 //!< 5  Erase Sector sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 8  //!< 8 Erase Block sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM \
    CMD_LUT_SEQ_IDX_WRITE                //!< 9  Program sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 //!< 11 Chip Erase sequence in lookupTable id stored in config block
#define NOR_CMD_LUT_SEQ_IDX_READ_SFDP 13 //!< 13 Read SFDP sequence in lookupTable id stored in config block
#define NOR_CMD_LUT_SEQ_IDX_RESTORE_NOCMD \
    14 //!< 14 Restore 0-4-4/0-8-8 mode sequence id in lookupTable stored in config block
#define NOR_CMD_LUT_SEQ_IDX_EXIT_NOCMD \
    15 //!< 15 Exit 0-4-4/0-8-8 mode sequence id in lookupTable stored in config blobk

/* FlexSPI NOR status */
enum _flexspi_nor_status
{
    kStatus_FLEXSPINOR_ProgramFail = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 0), //!< Status for Page programming failure
    kStatus_FLEXSPINOR_EraseSectorFail = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 1), //!< Status for Sector Erase failure
    kStatus_FLEXSPINOR_EraseAllFail = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 2),    //!< Status for Chip Erase failure
    kStatus_FLEXSPINOR_WaitTimeout = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 3),     //!< Status for timeout
    kStatus_FlexSPINOR_NotSupported = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 4),    // Status for PageSize overflow
    kStatus_FlexSPINOR_WriteAlignmentError = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 5), //!< Status for Alignement error
    kStatus_FlexSPINOR_CommandFailure =
        MAKE_STATUS(kStatusGroup_FLEXSPINOR, 6), //!< Status for Erase/Program Verify Error
    kStatus_FlexSPINOR_SFDP_NotFound = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 7), //!< Status for SFDP read failure
    kStatus_FLEXSPINOR_Unsupported_SFDP_Version =
        MAKE_STATUS(kStatusGroup_FLEXSPINOR, 8), //!< Status for Unrecognized SFDP version
    kStatus_FLEXSPINOR_Flash_NotFound = MAKE_STATUS(kStatusGroup_FLEXSPINOR, 9), //!< Status for Flash detection failure
    kStatus_FLEXSPINOR_DTRRead_DummyProbeFailed =
        MAKE_STATUS(kStatusGroup_FLEXSPINOR, 10), //!< Status for DDR Read dummy probe failure
};

enum
{
    kSerialNorCfgOption_Tag = 0x0c,
    kSerialNorCfgOption_DeviceType_ReadSFDP_SDR = 0,
    kSerialNorCfgOption_DeviceType_ReadSFDP_DDR = 1,
    kSerialNorCfgOption_DeviceType_HyperFLASH1V8 = 2,
    kSerialNorCfgOption_DeviceType_HyperFLASH3V0 = 3,
    kSerialNorCfgOption_DeviceType_MacronixOctalDDR = 4,
    kSerialNorCfgOption_DeviceType_MacronixOctalSDR = 5,
    kSerialNorCfgOption_DeviceType_MicronOctalDDR = 6,
    kSerialNorCfgOption_DeviceType_MicronOctalSDR = 7,
    kSerialNorCfgOption_DeviceType_AdestoOctalDDR = 8,
    kSerialNorCfgOption_DeviceType_AdestoOctalSDR = 9,
};

enum
{
    kSerialNorQuadMode_NotConfig = 0,
    kSerialNorQuadMode_StatusReg1_Bit6 = 1,
    kSerialNorQuadMode_StatusReg2_Bit1 = 2,
    kSerialNorQuadMode_StatusReg2_Bit7 = 3,
    kSerialNorQuadMode_StatusReg2_Bit1_0x31 = 4,
};

enum
{
    kSerialNorEnhanceMode_Disabled = 0,
    kSerialNorEnhanceMode_0_4_4_Mode = 1,
    kSerialNorEnhanceMode_0_8_8_Mode = 2,
    kSerialNorEnhanceMode_DataOrderSwapped = 3,
    kSerialNorEnhanceMode_2ndPinMux = 4,
};

enum
{
    kSerialNorConnection_SinglePortA,
    kSerialNorConnection_Parallel,
    kSerialNorConnection_SinglePortB,
    kSerialNorConnection_BothPorts
};

/*
 * Serial NOR Configuration Option
 */
typedef struct _serial_nor_config_option
{
    union
    {
        struct
        {
            uint32_t max_freq : 4;          //!< Maximum supported Frequency
            uint32_t misc_mode : 4;         //!< miscellaneous mode
            uint32_t quad_mode_setting : 4; //!< Quad mode setting
            uint32_t cmd_pads : 4;          //!< Command pads
            uint32_t query_pads : 4;        //!< SFDP read pads
            uint32_t device_type : 4;       //!< Device type
            uint32_t option_size : 4;       //!< Option size, in terms of uint32_t, size = (option_size + 1) * 4
            uint32_t tag : 4;               //!< Tag, must be 0x0E
        } B;
        uint32_t U;
    } option0;

    union
    {
        struct
        {
            uint32_t dummy_cycles : 8;     //!< Dummy cycles before read
            uint32_t status_override : 8;  //!< Override status register value during device mode configuration
            uint32_t pinmux_group : 4;     //!< The pinmux group selection
            uint32_t dqs_pinmux_group : 4; //!< The DQS Pinmux Group Selection
            uint32_t drive_strength : 4;   //!< The Drive Strength of FlexSPI Pads
            uint32_t flash_connection : 4; //!< Flash connection option: 0 - Single Flash connected to port A, 1 -
            //! Parallel mode, 2 - Single Flash connected to Port B
        } B;
        uint32_t U;
    } option1;

} serial_nor_config_option_t;

typedef union
{
    struct
    {
        uint8_t por_mode;
        uint8_t current_mode;
        uint8_t exit_no_cmd_sequence;
        uint8_t restore_sequence;
    } B;
    uint32_t U;
} flash_run_context_t;

enum
{
    kRestoreSequence_None = 0,
    kRestoreSequence_HW_Reset = 1,
    kRestoreSequence_4QPI_FF = 2,
    kRestoreSequence_5QPI_FF = 3,
    kRestoreSequence_8QPI_FF = 4,
    kRestoreSequence_Send_F0 = 5,
    kRestoreSequence_Send_66_99 = 6,
    kRestoreSequence_Send_6699_9966 = 7,
    kRestoreSequence_Send_06_FF, // Adesto EcoXIP
};

enum
{
    kFlashInstMode_ExtendedSpi = 0x00,
    kFlashInstMode_0_4_4_SDR = 0x01,
    kFlashInstMode_0_4_4_DDR = 0x02,
    kFlashInstMode_QPI_SDR = 0x41,
    kFlashInstMode_QPI_DDR = 0x42,
    kFlashInstMode_OPI_SDR = 0x81,
    kFlashInstMode_OPI_DDR = 0x82,
};

/*
 *  Serial NOR configuration block
 */
typedef struct _flexspi_nor_config
{
    flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI
    uint32_t pageSize;              //!< Page size of Serial NOR
    uint32_t sectorSize;            //!< Sector size of Serial NOR
    uint8_t ipcmdSerialClkFreq;     //!< Clock frequency for IP command
    uint8_t isUniformBlockSize;     //!< Sector/Block size is the same
    uint8_t isDataOrderSwapped;     //!< Data order (D0, D1, D2, D3) is swapped (D1,D0, D3, D2)
    uint8_t reserved0[1];           //!< Reserved for future use
    uint8_t serialNorType;          //!< Serial NOR Flash type: 0/1/2/3
    uint8_t needExitNoCmdMode;      //!< Need to exit NoCmd mode before other IP command
    uint8_t halfClkForNonReadCmd;   //!< Half the Serial Clock for non-read command: true/false
    uint8_t needRestoreNoCmdMode;   //!< Need to Restore NoCmd mode after IP commmand execution
    uint32_t blockSize;             //!< Block size
    uint32_t reserve2[11];          //!< Reserved for future use
} flexspi_nor_config_t;

#ifdef __cplusplus
extern "C" {
#endif

//!@brief Initialize Serial NOR devices via FlexSPI
status_t flexspi_nor_flash_init(uint32_t instance, flexspi_nor_config_t *config);

//!@brief Program data to Serial NOR via FlexSPI
status_t flexspi_nor_flash_page_program(uint32_t instance,
                                        flexspi_nor_config_t *config,
                                        uint32_t dstAddr,
                                        const uint32_t *src);

//!@brief Erase all the Serial NOR devices connected on FlexSPI
status_t flexspi_nor_flash_erase_all(uint32_t instance, flexspi_nor_config_t *config);

//!@brief Erase one sector specified by address
status_t flexspi_nor_flash_erase_sector(uint32_t instance, flexspi_nor_config_t *config, uint32_t address);

//!@brief Erase one block specified by address
status_t flexspi_nor_flash_erase_block(uint32_t instance, flexspi_nor_config_t *config, uint32_t address);

//!@brief Get FlexSPI NOR Configuration Block based on specified option
status_t flexspi_nor_get_config(uint32_t instance, flexspi_nor_config_t *config, serial_nor_config_option_t *option);

//!@brief Erase Flash Region specified by address and length
status_t flexspi_nor_flash_erase(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, uint32_t length);

//!@brief Read data from Serial NOR
status_t flexspi_nor_flash_read(
    uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t start, uint32_t bytes);

//!@brief Write FlexSPI persistent content
extern status_t flexspi_nor_write_persistent(const uint32_t data);

//!@brief Read FlexSPI persistent content
extern status_t flexspi_nor_read_persistent(uint32_t *data);

//!@brief Restore Flash to SPI protocol
status_t flexspi_nor_restore_spi_protocol(uint32_t instance,
                                          flexspi_nor_config_t *config,
                                          flash_run_context_t *run_ctx);

#ifdef __cplusplus
}
#endif

#endif // __FLEXSPI_NOR_FLASH_H__
