//
// Created by lydxh on 24-11-27.
//

#ifndef HW_LIB_FLASH_DEF_H
#define HW_LIB_FLASH_DEF_H

#include <stdint.h>
#include <flash_cfg.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * flash program(write) data mode
 */
enum flash_write_mode {
    FLASH_WM_PAGE_256B = 1 << 0,                            /**< write 1 to 256 bytes per page */
    FLASH_WM_BYTE = 1 << 1,                                 /**< byte write */
    FLASH_WM_AAI = 1 << 2,                                  /**< auto address increment */
    FLASH_WM_DUAL_BUFFER = 1 << 3,                          /**< dual-buffer write, like AT45DB series */
};

/* manufacturer information */
typedef struct {
    char *name;
    uint8_t id;
} flash_mf;

/* flash chip information */
typedef struct {
    char *name;                                  /**< flash chip name */
    uint8_t mf_id;                               /**< manufacturer ID */
    uint8_t type_id;                             /**< memory type ID */
    uint8_t capacity_id;                         /**< capacity ID */
    uint32_t capacity;                           /**< flash capacity (bytes) */
    uint16_t write_mode;                         /**< write mode @see flash_write_mode */
    uint32_t erase_gran;                         /**< erase granularity (bytes) */
    uint8_t erase_gran_cmd;                      /**< erase granularity size block command */
} flash_chip;

#ifdef FLASH_USING_QSPI
/* QSPI flash chip's extended information compared with SPI flash */
typedef struct {
    uint8_t mf_id;                               /**< manufacturer ID */
    uint8_t type_id;                             /**< memory type ID */
    uint8_t capacity_id;                         /**< capacity ID */
    uint8_t read_mode;                           /**< supported read mode on this qspi flash chip */
} flash_qspi_flash_ext_info;
#endif

/* FLASH support manufacturer JEDEC ID */
#define FLASH_MF_ID_CYPRESS                             0x01
#define FLASH_MF_ID_FUJITSU                             0x04
#define FLASH_MF_ID_EON                                 0x1C
#define FLASH_MF_ID_ATMEL                               0x1F
#define FLASH_MF_ID_MICRON                              0x20
#define FLASH_MF_ID_AMIC                                0x37
#define FLASH_MF_ID_NOR_MEM                             0x52
#define FLASH_MF_ID_SANYO                               0x62
#define FLASH_MF_ID_INTEL                               0x89
#define FLASH_MF_ID_ESMT                                0x8C
#define FLASH_MF_ID_FUDAN                               0xA1
#define FLASH_MF_ID_HYUNDAI                             0xAD
#define FLASH_MF_ID_SST                                 0xBF
#define FLASH_MF_ID_MACRONIX                            0xC2
#define FLASH_MF_ID_GIGADEVICE                          0xC8
#define FLASH_MF_ID_ISSI                                0xD5
#define FLASH_MF_ID_WINBOND                             0xEF
#define FLASH_MF_ID_PUYA                                0x85

/* FLASH supported manufacturer information table */
#define FLASH_MF_TABLE                                     \
{                                                         \
    {"Cypress",    FLASH_MF_ID_CYPRESS},                   \
    {"Fujitsu",    FLASH_MF_ID_FUJITSU},                   \
    {"EON",        FLASH_MF_ID_EON},                       \
    {"Atmel",      FLASH_MF_ID_ATMEL},                     \
    {"Micron",     FLASH_MF_ID_MICRON},                    \
    {"AMIC",       FLASH_MF_ID_AMIC},                      \
    {"Sanyo",      FLASH_MF_ID_SANYO},                     \
    {"Intel",      FLASH_MF_ID_INTEL},                     \
    {"ESMT",       FLASH_MF_ID_ESMT},                      \
    {"Fudan",      FLASH_MF_ID_FUDAN},                     \
    {"Hyundai",    FLASH_MF_ID_HYUNDAI},                   \
    {"SST",        FLASH_MF_ID_SST},                       \
    {"GigaDevice", FLASH_MF_ID_GIGADEVICE},                \
    {"ISSI",       FLASH_MF_ID_ISSI},                      \
    {"Winbond",    FLASH_MF_ID_WINBOND},                   \
    {"Macronix",   FLASH_MF_ID_MACRONIX},                  \
    {"NOR-MEM",    FLASH_MF_ID_NOR_MEM},                   \
    {"PUYA",       FLASH_MF_ID_PUYA},                      \
}

#ifdef FLASH_USING_FLASH_INFO_TABLE
/* FLASH supported flash chip information table. If the flash not support JEDEC JESD216 standard,
 * then the FLASH will find the flash chip information by this table. You can add other flash to here then
 *  notice me for update it. The configuration information name and index reference the flash_flash_chip structure.
 * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
 */
#define FLASH_FLASH_CHIP_TABLE                                                                                       \
{                                                                                                                   \
    {"AT45DB161E", FLASH_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_DUAL_BUFFER, 512, 0x81},      \
    {"W25Q40BV", FLASH_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"W25X40CL", FLASH_MF_ID_WINBOND, 0x30, 0x13, 512L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"W25X16AV", FLASH_MF_ID_WINBOND, 0x30, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q16BV", FLASH_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q32BV", FLASH_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q64CV", FLASH_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q64DW", FLASH_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q128BV", FLASH_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                  \
    {"W25Q256FV", FLASH_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                  \
    {"SST25VF080B", FLASH_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20},              \
    {"SST25VF016B", FLASH_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20},              \
    {"M25P32", FLASH_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P80", FLASH_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, FLASH_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P40", FLASH_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, FLASH_WM_PAGE_256B, 64L*1024L, 0xD8},                      \
    {"EN25Q32B", FLASH_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"GD25Q64B", FLASH_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                 \
    {"GD25Q16B", FLASH_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                 \
    {"GD25Q32C", FLASH_MF_ID_GIGADEVICE, 0x40, 0x16, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                 \
    {"S25FL216K", FLASH_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                   \
    {"S25FL032P", FLASH_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                   \
    {"A25L080", FLASH_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"F25L004", FLASH_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20},                     \
    {"PCT25VF016B", FLASH_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, FLASH_WM_BYTE|FLASH_WM_AAI, 4096, 0x20},              \
    {"NM25Q128EVB", FLASH_MF_ID_NOR_MEM, 0x21, 0x18, 16L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                \
    {"P25D05H", FLASH_MF_ID_PUYA, 0x60, 0x13, 5L*1024L,       FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"P25D10H", FLASH_MF_ID_PUYA, 0x60, 0x12, 1L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"P25D20H", FLASH_MF_ID_PUYA, 0x60, 0x11, 2L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"P25D40H", FLASH_MF_ID_PUYA, 0x60, 0x10, 4L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
    {"P25Q80H", FLASH_MF_ID_PUYA, 0x30, 0x14, 8L*1024L*1024L, FLASH_WM_PAGE_256B, 4096, 0x20},                        \
}
#endif /* FLASH_USING_FLASH_INFO_TABLE */

#ifdef FLASH_USING_QSPI
/* This table saves flash read-fast instructions in QSPI mode,
 * FLASH can use this table to select the most appropriate read instruction for flash.
 * | mf_id | type_id | capacity_id | qspi_read_mode |
 */
#define FLASH_FLASH_EXT_INFO_TABLE                                                                  \
{                                                                                                  \
    /* W25Q40BV */                                                                                 \
    {FLASH_MF_ID_WINBOND, 0x40, 0x13, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* W25Q80JV */                                                                                 \
    {FLASH_MF_ID_WINBOND, 0x40, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* W25Q16BV */                                                                                 \
    {FLASH_MF_ID_WINBOND, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* W25Q32BV */                                                                                 \
    {FLASH_MF_ID_WINBOND, 0x40, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_OUTPUT|QUAD_IO},             \
    /* W25Q64JV */                                                                                 \
    {FLASH_MF_ID_WINBOND, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
    /* W25Q128JV */                                                                                \
    {FLASH_MF_ID_WINBOND, 0x40, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
    /* W25Q256FV */                                                                                \
    {FLASH_MF_ID_WINBOND, 0x40, 0x19, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
    /* EN25Q32B */                                                                                 \
    {FLASH_MF_ID_EON, 0x30, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_IO},                             \
    /* S25FL216K */                                                                                \
    {FLASH_MF_ID_CYPRESS, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* A25L080 */                                                                                  \
    {FLASH_MF_ID_AMIC, 0x30, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO},                            \
    /* A25LQ64 */                                                                                  \
    {FLASH_MF_ID_AMIC, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_IO},                    \
    /* MX25L3206E and KH25L3206E */                                                                \
    {FLASH_MF_ID_MACRONIX, 0x20, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT},                                \
    /* MX25L51245G */                                                                              \
    {FLASH_MF_ID_MACRONIX, 0x20, 0x1A, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},    \
    /* GD25Q64B */                                                                                 \
    {FLASH_MF_ID_GIGADEVICE, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT},                              \
    /* NM25Q128EVB */                                                                              \
    {FLASH_MF_ID_NOR_MEM, 0x21, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},          \
}
#endif /* FLASH_USING_QSPI */






/**
 * retry process
 *
 * @param delay delay function for every retry. NULL will not delay for every retry.
 * @param retry retry counts
 * @param result FLASH_ERR_TIMEOUT: retry timeout
 */
#define FLASH_RETRY_PROCESS(delay, retry, result)                               \
    void (*__delay_temp)(void) = (void (*)(void))delay;                        \
    if (retry == 0) {result = FLASH_ERR_TIMEOUT;break;}                         \
    else {if (__delay_temp) {__delay_temp();} retry --;}

/* software version number */
#define FLASH_SW_VERSION                             "1.1.0"
/*
 * all defined supported command
 */
#ifndef FLASH_CMD_WRITE_ENABLE
#define FLASH_CMD_WRITE_ENABLE                          0x06
#endif

#ifndef FLASH_CMD_WRITE_DISABLE
#define FLASH_CMD_WRITE_DISABLE                         0x04
#endif

#ifndef FLASH_CMD_READ_STATUS_REGISTER
#define FLASH_CMD_READ_STATUS_REGISTER                  0x05
#endif

#ifndef FLASH_VOLATILE_SR_WRITE_ENABLE
#define FLASH_VOLATILE_SR_WRITE_ENABLE                  0x50
#endif

#ifndef FLASH_CMD_WRITE_STATUS_REGISTER
#define FLASH_CMD_WRITE_STATUS_REGISTER                 0x01
#endif

#ifndef FLASH_CMD_PAGE_PROGRAM
#define FLASH_CMD_PAGE_PROGRAM                          0x02
#endif

#ifndef FLASH_CMD_AAI_WORD_PROGRAM
#define FLASH_CMD_AAI_WORD_PROGRAM                      0xAD
#endif

#ifndef FLASH_CMD_ERASE_CHIP
#define FLASH_CMD_ERASE_CHIP                            0xC7
#endif

#ifndef FLASH_CMD_READ_DATA
#define FLASH_CMD_READ_DATA                             0x03
#endif

#ifndef FLASH_CMD_FAST_READ_DATA
#define FLASH_CMD_FAST_READ_DATA                        0x0B
#endif

#ifndef FLASH_CMD_DUAL_OUTPUT_READ_DATA
#define FLASH_CMD_DUAL_OUTPUT_READ_DATA                 0x3B
#endif

#ifndef FLASH_CMD_DUAL_IO_READ_DATA
#define FLASH_CMD_DUAL_IO_READ_DATA                     0xBB
#endif

#ifndef FLASH_CMD_QUAD_IO_READ_DATA
#define FLASH_CMD_QUAD_IO_READ_DATA                     0xEB
#endif

#ifndef FLASH_CMD_QUAD_OUTPUT_READ_DATA
#define FLASH_CMD_QUAD_OUTPUT_READ_DATA                 0x6B
#endif

#ifndef FLASH_CMD_MANUFACTURER_DEVICE_ID
#define FLASH_CMD_MANUFACTURER_DEVICE_ID                0x90
#endif

#ifndef FLASH_CMD_JEDEC_ID
#define FLASH_CMD_JEDEC_ID                              0x9F
#endif

#ifndef FLASH_CMD_READ_UNIQUE_ID
#define FLASH_CMD_READ_UNIQUE_ID                        0x4B
#endif

#ifndef FLASH_CMD_READ_SFDP_REGISTER
#define FLASH_CMD_READ_SFDP_REGISTER                    0x5A
#endif

#ifndef FLASH_CMD_ENABLE_RESET
#define FLASH_CMD_ENABLE_RESET                          0x66
#endif

#ifndef FLASH_CMD_RESET
#define FLASH_CMD_RESET                                 0x99
#endif

#ifndef FLASH_CMD_ENTER_4B_ADDRESS_MODE
#define FLASH_CMD_ENTER_4B_ADDRESS_MODE                 0xB7
#endif

#ifndef FLASH_CMD_EXIT_4B_ADDRESS_MODE
#define FLASH_CMD_EXIT_4B_ADDRESS_MODE                  0xE9
#endif

#ifndef FLASH_WRITE_MAX_PAGE_SIZE
#define FLASH_WRITE_MAX_PAGE_SIZE                        256
#endif

/* send dummy data for read data */
#ifndef FLASH_DUMMY_DATA
#define FLASH_DUMMY_DATA                                0xFF
#endif

/* dummy data count for fast read data and etc */
#ifndef FLASH_READ_DUMMY_BYTE_CNT
#ifdef FLASH_USING_FAST_READ
#define FLASH_READ_DUMMY_BYTE_CNT                       1
#else
#define FLASH_READ_DUMMY_BYTE_CNT                       0
#endif
#endif

/* maximum number of erase type support on JESD216 (V1.0) */
#define FLASH_SFDP_ERASE_TYPE_MAX_NUM                      4

/**
 * status register bits
 */
enum {
    FLASH_STATUS_REGISTER_BUSY = (1 << 0),                  /**< busing */
    FLASH_STATUS_REGISTER_WEL = (1 << 1),                   /**< write enable latch */
    FLASH_STATUS_REGISTER_SRP = (1 << 7),                   /**< status register protect */
};

/**
 * error code
 */
typedef enum {
    FLASH_SUCCESS = 0,                                      /**< success */
    FLASH_ERR_NOT_FOUND = 1,                                /**< not found or not supported */
    FLASH_ERR_WRITE = 2,                                    /**< write error */
    FLASH_ERR_READ = 3,                                     /**< read error */
    FLASH_ERR_TIMEOUT = 4,                                  /**< timeout error */
    FLASH_ERR_ADDR_OUT_OF_BOUND = 5,                        /**< address is out of flash bound */
} flash_err;

#ifdef FLASH_USING_QSPI
/**
 * QSPI flash read cmd format
 */
typedef struct {
    uint8_t instruction;
    uint8_t instruction_lines;
    uint8_t address_size;
    uint8_t address_lines;
    uint8_t alternate_bytes_lines;
    uint8_t dummy_cycles;
    uint8_t data_lines;
} flash_qspi_read_cmd_format;
#endif /* FLASH_USING_QSPI */

/* SPI bus write read data function type */
typedef flash_err (*spi_write_read_func)(const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
                                         size_t read_size);

#ifdef FLASH_USING_SFDP
/**
 * the SFDP (Serial Flash Discoverable Parameters) parameter info which used on this library
 */
typedef struct {
    bool available;                              /**< available when read SFDP OK */
    uint8_t major_rev;                           /**< SFDP Major Revision */
    uint8_t minor_rev;                           /**< SFDP Minor Revision */
    uint16_t write_gran;                         /**< write granularity (bytes) */
    uint8_t erase_4k;                            /**< 4 kilobyte erase is supported throughout the device */
    uint8_t erase_4k_cmd;                        /**< 4 Kilobyte erase command */
    bool sr_is_non_vola;                         /**< status register is supports non-volatile */
    uint8_t vola_sr_we_cmd;                      /**< volatile status register write enable command */
    bool addr_3_byte;                            /**< supports 3-Byte addressing */
    bool addr_4_byte;                            /**< supports 4-Byte addressing */
    uint32_t capacity;                           /**< flash capacity (bytes) */
    struct {
        uint32_t size;                           /**< erase sector size (bytes). 0x00: not available */
        uint8_t cmd;                             /**< erase command */
    } eraser[FLASH_SFDP_ERASE_TYPE_MAX_NUM];      /**< supported eraser types table */
    //TODO lots of fast read-related stuff (like modes supported and number of wait states/dummy cycles needed in each)
} flash_sfdp, *flash_sfdp_t;
#endif

/**
 * SPI device
 */
typedef struct __flash_spi {
    /* SPI device name */
    char *name;

    /* SPI bus write read data function */
    flash_err (*wr)(const struct __flash_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
                    size_t read_size);

#ifdef FLASH_USING_QSPI

    /* QSPI fast read function */
    flash_err
    (*qspi_read)(const struct __flash_spi *spi, uint32_t addr, flash_qspi_read_cmd_format *qspi_read_cmd_format,
                 uint8_t *read_buf, size_t read_size);

#endif

    /* lock SPI bus */
    void (*lock)(const struct __flash_spi *spi);

    /* unlock SPI bus */
    void (*unlock)(const struct __flash_spi *spi);

    /* some user data */
    void *user_data;
} flash_spi, *flash_spi_t;


#ifdef __cplusplus
}
#endif
#endif //HW_LIB_FLASH_DEF_H
