#ifndef __GD5F2GQ5UE_H
#define __GD5F2GQ5UE_H

/*
 * 模块名称：GD5F2GQ5UE SPI NAND Flash 驱动
 * 模块功能：提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
 * 适用平台：STM32F407ZGT6 + SPI1 硬件 SPI
 * 作者：王建锋
 * 创建日期：2026-07-16
 * 修改记录：
 *     2026-07-16  王建锋  创建初始版本
 *     2026-07-17  王建锋  切换为硬件 SPI，修正擦除地址，参考 NuttX 驱动
 */

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include <stdint.h>

/* ======================== 宏定义 ======================== */

/* SPI 命令码 */
#define GD5F_CMD_WRITE_ENABLE        0x06
#define GD5F_CMD_WRITE_DISABLE       0x04
#define GD5F_CMD_GET_FEATURE         0x0F
#define GD5F_CMD_SET_FEATURE         0x1F
#define GD5F_CMD_READ_ID             0x9F
#define GD5F_CMD_PAGE_READ           0x13
#define GD5F_CMD_READ_FROM_CACHE     0x0B
#define GD5F_CMD_PROGRAM_LOAD        0x02
#define GD5F_CMD_PROGRAM_EXEC        0x10
#define GD5F_CMD_BLOCK_ERASE         0xD8
#define GD5F_CMD_RESET               0xFF

/* 寄存器地址 */
#define GD5F_REG_PROTECT             0xA0
#define GD5F_REG_FEATURE             0xB0
#define GD5F_REG_STATUS              0xC0
#define GD5F_REG_DRIVER              0xD0

/* 状态位定义 */
#define GD5F_STATUS_OIP              (1 << 0)
#define GD5F_STATUS_WEL              (1 << 1)
#define GD5F_STATUS_E_FAIL           (1 << 2)
#define GD5F_STATUS_P_FAIL           (1 << 3)
#define GD5F_STATUS_ECCS0            (1 << 4)
#define GD5F_STATUS_ECCS1            (1 << 5)

/* Feature 位定义 */
#define GD5F_FEATURE_ECC_EN          (1 << 4)
#define GD5F_FEATURE_QE              (1 << 0)

/* 芯片参数 */
#define GD5F_PAGE_SIZE               2048
#define GD5F_SPARE_SIZE              64
#define GD5F_TOTAL_PAGE_SIZE         2112
#define GD5F_PAGES_PER_BLOCK         64
#define GD5F_BLOCK_SIZE              (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE)
#define GD5F_TOTAL_BLOCKS            2048
#define GD5F_TOTAL_SIZE              (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)

/* 坏块表持久化：保留块池数量（从 NAND 顶部向下挑选出厂好块存储 BBT） */
#define GD5F_BBT_POOL_COUNT          4

/* 制造商 ID 和设备 ID */
#define GD5F_MANUFACTURER_ID         0xC8
#define GD5F_DEVICE_ID               0x52

/* 返回值定义 */
#define GD5F_OK                      0
#define GD5F_ERROR                  -1
#define GD5F_BUSY_TIMEOUT           -2
#define GD5F_ECC_ERROR             -3
#define GD5F_PROGRAM_FAIL          -4
#define GD5F_ERASE_FAIL            -5
#define GD5F_ID_MISMATCH           -6

/* 控制引脚宏 */
#define GD5F_CS_LOW()       HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_RESET)
#define GD5F_CS_HIGH()      HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_SET)
#define GD5F_WP_HIGH()      HAL_GPIO_WritePin(GD_WP_GPIO_Port, GD_WP_Pin, GPIO_PIN_SET)
#define GD5F_HOLD_HIGH()    HAL_GPIO_WritePin(GD_HOLD_GPIO_Port, GD_HOLD_Pin, GPIO_PIN_SET)

/* ======================== 函数声明 ======================== */

/*
 * 函数功能：初始化 GD5F2GQ5UE（读 ID + 使能 ECC + 解除块保护）
 * 入口参数：无
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：SPI1 和相关 GPIO 已由 CubeMX 初始化完成
 * 函数说明：1. 发送复位命令并等待完成
 *           2. 读取芯片 ID 并校验
 *           3. 使能内部 ECC (B0h bit4)
 *           4. 解除所有块保护 (A0h = 0x00)
 */
int32_t gd5f2gq5ue_init(void);

/*
 * 函数功能：读取芯片 ID（MID + DID）
 * 入口参数：mid - 制造商 ID 输出指针  uint8_t*  不为 NULL
 *           did - 设备 ID 输出指针    uint8_t*  不为 NULL
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发送 9Fh 命令后接收1个 dummy + MID + DID
 */
int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did);

/*
 * 函数功能：从 NAND 读取数据（支持跨页）
 * 入口参数：offset - 起始字节偏移  int32_t     0 - GD5F_TOTAL_SIZE-1
 *           buf    - 数据缓冲区     uint8_t* 不为 NULL
 *           size   - 读取字节数     uint32_t   > 0
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：自动处理跨页读取，每次读取不超过当前页剩余空间
 */
int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);

/*
 * 函数功能：向 NAND 写入数据（支持跨页）
 * 入口参数：offset - 起始字节偏移  int32_t        0 - GD5F_TOTAL_SIZE-1
 *           buf    - 数据缓冲区     uint8_t*    不为 NULL
 *           size   - 写入字节数     uint32_t      > 0
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已成功调用，目标区域已擦除
 * 函数说明：自动处理跨页写入，每次写入不超过当前页剩余空间
 */
int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);

/*
 * 函数功能：擦除块（按块擦除，最小单位 128KB）
 * 入口参数：offset - 起始字节偏移  int32_t    必须 GD5F_BLOCK_SIZE 对齐
 *           size   - 擦除字节数    uint32_t  必须 GD5F_BLOCK_SIZE 整数倍
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：擦除操作以块为单位，offset 和 size 必须块对齐
 */
int gd5f2gq5ue_erase(long offset, size_t size);

/*
 * 函数功能：复位芯片
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发送 FFh 复位命令后等待 5ms
 */
int32_t gd5f2gq5ue_reset(void);

/*
 * 函数功能：查询块是否坏块
 * 入口参数：block - 块编号  uint32_t  0 ~ GD5F_TOTAL_BLOCKS-1
 * 返回值：0 - 好块，1 - 坏块
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：通过 BBT（Bad Block Table）查询，BBT 在 init 阶段 ECC 使能前扫描构建
 */
int32_t gd5f2gq5ue_is_block_bad(uint32_t block);

/*
 * 函数功能：标记块为坏块
 * 入口参数：block - 块编号  uint32_t  0 ~ GD5F_TOTAL_BLOCKS-1
 * 返回值：无
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：更新 RAM 中的 BBT，并持久化到保留块池（带版本号 + CRC，
 *           轮转写入，掉电安全）；仅在管理区内的块才纳入持久化 BBT
 */
void gd5f2gq5ue_mark_block_bad(uint32_t block);
void gd5f2gq5ue_bbt_clear(void);
void gd5f2gq5ue_print_bbt(void);

/*
 * 函数功能：重建 BBT（重新扫描出厂坏块并持久化，覆盖运行时注入的坏块）
 * 入口参数：无
 * 返回值：GD5F_OK - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：用于测试（如 TC-STO-03 注入坏块后）或出厂重置后恢复。
 *           临时关闭 ECC 重新扫描每块 page0 spare[0] 的出厂坏块标记，覆盖 RAM BBT，
 *           再写回持久化 BBT 池（版本 +1，掉电安全），从而清除运行时 mark 的坏块。
 *           调用后建议再执行 nand_ftl_format() 清空 dhara map。
 */
int gd5f2gq5ue_bbt_rebuild(void);

/*
 * 函数功能：获取 dhara 可用块数（总块数减去保留块池）
 * 入口参数：无
 * 返回值：dhara 可见的块数量（= 保留块池起始块号）
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：保留块池用于存储持久化 BBT，不交给 dhara 管理
 */
uint32_t gd5f_get_usable_blocks(void);

/*
 * 函数功能：从闪存读取当前生效的 BBT 位图（测试/调试用）
 * 入口参数：p_bbt     - 位图输出缓冲  uint8_t*  不为 NULL，长度 >= GD5F_BBT_SIZE
 *           p_version - 版本号输出    uint32_t* 不为 NULL
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：返回闪存中版本号最高的有效 BBT 拷贝（供测试验证持久化）
 */
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version);

/*
 * 函数功能：擦除保留块池（撤销持久化 BBT，回到仅工厂扫描态）
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：仅供测试/出厂重置使用；擦除后下次启动 load 找不到副本，
 *           仅保留 RAM 中工厂扫描结果（调用方需自行还原 RAM BBT）。
 */
int32_t gd5f_bbt_wipe_pool(void);

/*
 * 函数功能：从保留块池重新加载持久化 BBT 到 RAM（测试/调试用）
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：将闪存中版本号最高的有效副本 OR 进 RAM BBT
 */
int32_t gd5f_bbt_reload(void);

/* ==================== SPI 原语（FTL 共享） ==================== */

int32_t gd5f_wait_busy(uint32_t timeout_ms);
int32_t gd5f_write_enable(void);
int32_t gd5f_read_status(uint8_t *p_status);
int32_t gd5f_page_read(uint32_t page_addr);
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, uint32_t size);
int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, uint32_t size);
int32_t gd5f_program_exec(uint32_t page_addr);
int32_t gd5f_block_erase(uint32_t block_addr);
int32_t gd5f_check_ecc(void);

#ifdef __cplusplus
}
#endif

#endif /* __GD5F2GQ5UE_H */
