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

/* 头文件包含区 */
#include <string.h>
#include "gd5f2gq5ue.h"

/*
 * 调试输出配置
 */
#define DBG_TAG "[NAND]"
#include "dbg_log.h"

/* 私有宏定义区 */
#define GD5F_SPI_TIMEOUT          100
#define GD5F_BBT_SIZE             (GD5F_TOTAL_BLOCKS / 8)

/* BBT 静态数组（1 bit 表示一个块，0=好块 1=坏块） */
static uint8_t s_bbt[GD5F_BBT_SIZE];

/* 外部 SPI 句柄声明 */
extern SPI_HandleTypeDef hspi1;

/* DMA 完成标志（ch395f.c 中的 HAL_SPI_TxRxCpltCallback 会设置此标志） */
volatile uint8_t g_spi1_dma_done;

/* DMA 阈值（字节数超过此值使用 DMA 传输） */
#define GD5F_DMA_THRESHOLD       32

/*
 * 函数功能：SPI1 TX DMA 完成回调（PROGRAM_LOAD 数据发送用）
 */
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
    if (hspi->Instance == SPI1) {
        g_spi1_dma_done = 1U;
    }
}

/*
 * 函数功能：SPI1 RX DMA 完成回调（READ_FROM_CACHE 数据接收用）
 * 函数说明：HAL_SPI_Receive_DMA() 在 2 线 master 模式下
 *           内部走 HAL_SPI_TransmitReceive_DMA 但 state=BUSY_RX，
 *           完成回调是 HAL_SPI_RxCpltCallback 而非 TxRxCpltCallback
 */
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
    if (hspi->Instance == SPI1) {
        g_spi1_dma_done = 1U;
    }
}

/* ======================== 私有函数声明 ======================== */

static int gd5f_page_program(uint32_t page_addr, uint16_t column,
                              const uint8_t *p_buf, size_t size);
static int gd5f_set_feature(uint8_t addr, uint8_t data);

/* ======================== SPI 原语定义 ======================== */

/*
 * 函数功能：等待芯片操作完成（轮询 OIP 位）
 * 入口参数：timeout_ms - 超时时间  uint32_t  > 0
 * 返回值：0 - 操作完成，-2 - 超时
 * 限定条件：SPI 已初始化
 * 函数说明：循环读取状态寄存器直到 OIP 位清零或超时
 */
int gd5f_wait_busy(uint32_t timeout_ms) {
    uint8_t cmd = GD5F_CMD_GET_FEATURE;
    uint8_t addr = GD5F_REG_STATUS;
    uint8_t status = 0;
    uint32_t tick_start = HAL_GetTick();

    while (1) {
        GD5F_CS_LOW();
        HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
        HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
        HAL_SPI_Receive(&hspi1, &status, 1, GD5F_SPI_TIMEOUT);
        GD5F_CS_HIGH();

        if ((status & GD5F_STATUS_OIP) == 0) {
            return GD5F_OK;
        }
        if ((HAL_GetTick() - tick_start) >= timeout_ms) {
            return GD5F_BUSY_TIMEOUT;
        }
    }
}

/*
 * 函数功能：发送写使能命令
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：CS# 拉低后发�?06h 命令再拉�? */
int gd5f_write_enable(void) {
    uint8_t cmd = GD5F_CMD_WRITE_ENABLE;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    return GD5F_OK;
}

/*
 * 函数功能：读取状态寄存器
 * 入口参数：p_status - 状态值输出指�? uint8_t*  不为 NULL
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发�?0Fh + C0h 地址后读�?字节状态�? */
int gd5f_read_status(uint8_t *p_status) {
    uint8_t cmd = GD5F_CMD_GET_FEATURE;
    uint8_t addr = GD5F_REG_STATUS;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
    HAL_SPI_Receive(&hspi1, p_status, 1, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    return GD5F_OK;
}

/*
 * 函数功能：页读取（将数据从存储阵列加载到内部缓存�? * 入口参数：page_addr - 页地址  uint32_t
 * 返回值：0 - 成功，其�?- 错误�? * 限定条件：SPI 已初始化
 * 函数说明：发�?13h + 3字节行地址，等�?OIP 清零
 */
int gd5f_page_read(uint32_t page_addr) {
    uint8_t cmd[4] = {0};

    cmd[0] = GD5F_CMD_PAGE_READ;
    cmd[1] = (page_addr >> 16) & 0xFF;
    cmd[2] = (page_addr >> 8) & 0xFF;
    cmd[3] = page_addr & 0xFF;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    return gd5f_wait_busy(100);
}

/*
 * 函数功能：从内部缓存读取数据
 * 入口参数：column - 列地址（页内偏移）  uint16_t  0 - 2047
 *           p_buf  - 数据输出缓冲�?      uint8_t*  不为 NULL
 *           size   - 读取字节�?          size_t    > 0
 * 返回值：0 - 成功
 * 限定条件：必须先调用 gd5f_page_read 完成数据加载
 * 函数说明：发�?0Bh + 2字节列地址 +1字节 dummy 后读取数�? */
int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
                                 size_t size) {
    uint8_t cmd[4] = {0};

    cmd[0] = GD5F_CMD_READ_FROM_CACHE;
    cmd[1] = (column >> 8) & 0xFF;
    cmd[2] = column & 0xFF;
    cmd[3] = 0x00;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);

    if (size > GD5F_DMA_THRESHOLD) {
        uint32_t tick_start = HAL_GetTick();
        g_spi1_dma_done = 0U;
        if (HAL_SPI_Receive_DMA(&hspi1, p_buf, (uint16_t)size) != HAL_OK) {
            GD5F_CS_HIGH();
            return GD5F_ERROR;
        }
        while (g_spi1_dma_done == 0U) {
            if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
                HAL_SPI_DMAStop(&hspi1);
                GD5F_CS_HIGH();
                return GD5F_BUSY_TIMEOUT;
            }
        }
    } else {
        HAL_SPI_Receive(&hspi1, p_buf, size, GD5F_SPI_TIMEOUT);
    }
    GD5F_CS_HIGH();

    return GD5F_OK;
}

/*
 * 函数功能：页编程（将数据写入指定页）
 * 入口参数：page_addr - 页地址   uint32_t
 *           column    - 列地址   uint16_t  0 - 2047
 *           p_buf     - 数据缓冲�? const uint8_t*
 *           size      - 写入字节�? size_t    > 0
 * 返回值：0 - 成功，其�?- 错误�? * 限定条件：目标区域已擦除
 * 函数说明�?. 写使�?- 02h 加载数据 - 10h 执行编程 - 等待完成
 *           2. 编程完成后检�?P_FAIL �? */
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
                              const uint8_t *p_buf, size_t size) {
    int ret;
    gd5f_write_enable();
    ret = gd5f_program_load(column, p_buf, size);
    if (ret != GD5F_OK) return ret;
    return gd5f_program_exec(page_addr);
}

int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
    uint8_t cmd[3];
    cmd[0] = GD5F_CMD_PROGRAM_LOAD;
    cmd[1] = (uint8_t)(column >> 8);
    cmd[2] = (uint8_t)(column);

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);

    if (size > GD5F_DMA_THRESHOLD) {
        uint32_t tick_start = HAL_GetTick();
        g_spi1_dma_done = 0U;
        if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size)
                != HAL_OK) {
            GD5F_CS_HIGH();
            return GD5F_ERROR;
        }
        while (g_spi1_dma_done == 0U) {
            if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
                HAL_SPI_DMAStop(&hspi1);
                GD5F_CS_HIGH();
                return GD5F_BUSY_TIMEOUT;
            }
        }
    } else {
        HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size, GD5F_SPI_TIMEOUT);
    }
    GD5F_CS_HIGH();

    return GD5F_OK;
}

int gd5f_program_exec(uint32_t page_addr) {
    int ret;
    uint8_t cmd[4];
    uint8_t status;

    cmd[0] = GD5F_CMD_PROGRAM_EXEC;
    cmd[1] = (page_addr >> 16) & 0xFF;
    cmd[2] = (page_addr >> 8) & 0xFF;
    cmd[3] = page_addr & 0xFF;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    ret = gd5f_wait_busy(1000);
    if (ret != GD5F_OK) return ret;

    gd5f_read_status(&status);
    if (status & GD5F_STATUS_P_FAIL) return GD5F_PROGRAM_FAIL;

    return GD5F_OK;
}

int gd5f_check_ecc(void) {
    uint8_t status;
    gd5f_read_status(&status);
    uint8_t ecc = (status >> 4) & 0x03;
    if (ecc == 0x02) return GD5F_ECC_ERROR;
    return GD5F_OK;
}

/*
 * 函数功能：设�?Feature 寄存�? * 入口参数：addr - 寄存器地址  uint8_t
 *           data - 写入数据    uint8_t
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：先写使能，发�?1Fh + 地址 + 数据，等待操作完�? */
static int gd5f_set_feature(uint8_t addr, uint8_t data) {
    uint8_t cmd[3];

    gd5f_write_enable();

    cmd[0] = GD5F_CMD_SET_FEATURE;
    cmd[1] = addr;
    cmd[2] = data;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    gd5f_wait_busy(100);

    return GD5F_OK;
}

/*
 * 函数功能：块擦除（擦除128KB块）
 * 入口参数：block_addr - 块编号  uint32_t  0 - 2047
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已调用
 * 函数说明：1. 写使能 - D8h + 3字节页地址 - 等待完成
 *           2. 擦除完成后检查 E_FAIL */
int gd5f_block_erase(uint32_t block_addr) {
    int ret = GD5F_OK;
    uint8_t cmd[4];
    uint8_t status = 0;
    uint32_t page_addr = block_addr * GD5F_PAGES_PER_BLOCK;

    gd5f_write_enable();

    cmd[0] = GD5F_CMD_BLOCK_ERASE;
    cmd[1] = (page_addr >> 16) & 0xFF;
    cmd[2] = (page_addr >> 8) & 0xFF;
    cmd[3] = page_addr & 0xFF;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    ret = gd5f_wait_busy(5000);
    if (ret != GD5F_OK) {
        return ret;
    }

    gd5f_read_status(&status);
    if (status & GD5F_STATUS_E_FAIL) {
        return GD5F_ERASE_FAIL;
    }

    return GD5F_OK;
}

/*
 * 函数功能：扫描所有块构建 BBT（在 ECC 使能前调用）
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化，ECC 尚未使能
 * 函数说明：读取每个块最后一页（page 63）的 spare byte 0，
 *           若不为 0xFF 则为出厂坏块
 */
static int gd5f_bbt_scan(void) {
    uint32_t block;
    uint8_t spare[8];
    uint32_t bad_count = 0;

    memset(s_bbt, 0, GD5F_BBT_SIZE);

    for (block = 0; block < GD5F_TOTAL_BLOCKS; block++) {
        uint32_t page = (block << 6) | 63;

        gd5f_page_read(page);
        gd5f_read_from_cache(GD5F_PAGE_SIZE, spare, 1);

        if (spare[0] != 0xFF) {
            s_bbt[block >> 3] |= (1 << (block & 7));
            bad_count++;
        }
    }

    DBG_INFO("BBT scan: %lu bad blocks found", bad_count);
    return GD5F_OK;
}

void gd5f2gq5ue_bbt_rescan(void) {
    uint32_t blk;
    uint8_t spare[64];
    memset(s_bbt, 0, GD5F_BBT_SIZE);
    for (blk = 0; blk < GD5F_TOTAL_BLOCKS; blk++) {
        uint32_t page = (blk << 6) | 63;
        gd5f_page_read(page);
        gd5f_read_from_cache(GD5F_PAGE_SIZE, spare, sizeof(spare));
        if (spare[0] != 0xFF) {
            s_bbt[blk >> 3] |= (1 << (blk & 7));
        }
    }
}

void gd5f2gq5ue_bbt_clear(void) {
    memset(s_bbt, 0, GD5F_BBT_SIZE);
}

void gd5f2gq5ue_print_bbt(void) {
    uint32_t bad_count = 0;
    for (uint32_t b = 0; b < GD5F_TOTAL_BLOCKS; b++) {
        if ((s_bbt[b >> 3] >> (b & 7)) & 1) bad_count++;
    }
    DBG_INFO("BBT: %lu/%lu blocks bad", bad_count, GD5F_TOTAL_BLOCKS);
    if (bad_count > 0) {
        uint32_t shown = 0;
        for (uint32_t b = 0; b < GD5F_TOTAL_BLOCKS && shown < 32; b++) {
            if ((s_bbt[b >> 3] >> (b & 7)) & 1) {
                DBG_INFO("  block %lu (offset 0x%08lX)", b, b * GD5F_BLOCK_SIZE);
                shown++;
            }
        }
        if (bad_count > 32) DBG_INFO("  ... and %lu more", bad_count - 32);
    }
}

/* ======================== 公共函数定义 ======================== */

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

    GD5F_CS_HIGH();
    GD5F_WP_HIGH();
    GD5F_HOLD_HIGH();

    HAL_Delay(10);

    DBG_INFO("Resetting NAND...");
    ret = gd5f2gq5ue_reset();
    if (ret != GD5F_OK) {
        DBG_ERROR("Reset failed: %d", ret);
        return ret;
    }

    HAL_Delay(5);

    DBG_INFO("Reading NAND ID...");
    ret = gd5f2gq5ue_read_id(&mid, &did);
    if (ret != GD5F_OK) {
        DBG_ERROR("Read ID failed: %d", ret);
        return ret;
    }

    DBG_INFO("NAND ID: MID=0x%02X, DID=0x%02X", mid, did);

    if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) {
        DBG_ERROR("ID mismatch: expected MID=0x%02X DID=0x%02X, got MID=0x%02X DID=0x%02X",
                  GD5F_MANUFACTURER_ID, GD5F_DEVICE_ID, mid, did);
        return GD5F_ID_MISMATCH;
    }

    DBG_INFO("Scanning bad blocks...");
    gd5f_bbt_scan();

    DBG_INFO("Enabling ECC...");
    gd5f_set_feature(0xB0, 0x10);
    gd5f_wait_busy(100);

    DBG_INFO("Unlocking block protection...");
    gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
    gd5f_wait_busy(100);

    DBG_INFO("NAND init OK");
    return GD5F_OK;
}

/*
 * 函数功能：读取芯�?ID（MID + DID�? * 入口参数：mid - 制造商 ID 输出指针  uint8_t*  不为 NULL
 *           did - 设备 ID 输出指针    uint8_t*  不为 NULL
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发�?9Fh 命令后接�?�?dummy + MID + DID
 */
int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
    uint8_t cmd = GD5F_CMD_READ_ID;
    uint8_t id_buf[3] = {0};

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    HAL_SPI_Receive(&hspi1, id_buf, 3, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    *p_mid = id_buf[1];
    *p_did = id_buf[2];

    return GD5F_OK;
}

/*
 * 函数功能：从 NAND 读取数据（支持跨页）
 * 入口参数：offset - 起始字节偏移  long      0 ~ 总容�?1
 *           p_buf  - 数据缓冲�?    uint8_t*  不为 NULL
 *           size   - 读取字节�?    size_t    > 0
 * 返回值：0 - 成功，其�?- 错误�? * 限定条件：gd5f2gq5ue_init() 已成功调�? * 函数说明：自动处理跨页读�? */
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
    int ret = GD5F_OK;

    while (size > 0) {
        uint32_t page_addr = offset / GD5F_PAGE_SIZE;
        uint16_t column = offset % GD5F_PAGE_SIZE;
        size_t bytes = GD5F_PAGE_SIZE - column;

        if (bytes > size) {
            bytes = size;
        }

        ret = gd5f_page_read(page_addr);
        if (ret != GD5F_OK) {
            return ret;
        }

        ret = gd5f_read_from_cache(column, p_buf, bytes);
        if (ret != GD5F_OK) {
            return ret;
        }

        offset += bytes;
        p_buf += bytes;
        size -= bytes;
    }

    return GD5F_OK;
}

/*
 * 函数功能：向 NAND 写入数据（支持跨页）
 * 入口参数：offset - 起始字节偏移  long          0 ~ 总容�?1
 *           p_buf  - 数据缓冲�?    const uint8_t*  不为 NULL
 *           size   - 写入字节�?    size_t        > 0
 * 返回值：0 - 成功，其�?- 错误�? * 限定条件：gd5f2gq5ue_init() 已成功调用，目标区域已擦�? * 函数说明：自动处理跨页写�? */
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
    int ret = GD5F_OK;

    while (size > 0) {
        uint32_t page_addr = offset / GD5F_PAGE_SIZE;
        uint16_t column = offset % GD5F_PAGE_SIZE;
        size_t bytes = GD5F_PAGE_SIZE - column;

        if (bytes > size) {
            bytes = size;
        }

        ret = gd5f_page_program(page_addr, column, p_buf, bytes);
        if (ret != GD5F_OK) {
            return ret;
        }

        offset += bytes;
        p_buf += bytes;
        size -= bytes;
    }

    return GD5F_OK;
}

/*
 * 函数功能：擦除块（按块擦除，最小单�?128KB�? * 入口参数：offset - 起始字节偏移  long    必须块对�? *           size   - 擦除字节�?   size_t  必须块大小整数�? * 返回值：0 - 成功，其�?- 错误�? * 限定条件：gd5f2gq5ue_init() 已成功调�? * 函数说明：擦除操作以块为单位
 */
int gd5f2gq5ue_erase(long offset, size_t size) {
    int ret = GD5F_OK;

    if (offset % GD5F_BLOCK_SIZE != 0) {
        return GD5F_ERROR;
    }
    if (size % GD5F_BLOCK_SIZE != 0) {
        return GD5F_ERROR;
    }

    while (size > 0) {
        uint32_t block_addr = offset / GD5F_BLOCK_SIZE;

        ret = gd5f_block_erase(block_addr);
        if (ret != GD5F_OK) {
            return ret;
        }

        offset += GD5F_BLOCK_SIZE;
        size -= GD5F_BLOCK_SIZE;
    }

    return GD5F_OK;
}

/*
 * 函数功能：复位芯�? * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发�?FFh 复位命令后等�?5ms
 */
int gd5f2gq5ue_reset(void) {
    uint8_t cmd = GD5F_CMD_RESET;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    HAL_Delay(5);

    return GD5F_OK;
}

/*
 * 函数功能：查询块是否坏块
 * 入口参数：block - 块编号  uint32_t  0 ~ GD5F_TOTAL_BLOCKS-1
 * 返回值：0 - 好块，1 - 坏块
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：通过 BBT 查询
 */
int gd5f2gq5ue_is_block_bad(uint32_t block) {
    if (block >= GD5F_TOTAL_BLOCKS) {
        return 1;
    }
    return (s_bbt[block >> 3] >> (block & 7)) & 1;
}

/*
 * 函数功能：标记块为坏块
 * 入口参数：block - 块编号  uint32_t  0 ~ GD5F_TOTAL_BLOCKS-1
 * 返回值：无
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：更新 BBT
 */
void gd5f2gq5ue_mark_block_bad(uint32_t block) {
    if (block >= GD5F_TOTAL_BLOCKS) {
        return;
    }
    s_bbt[block >> 3] |= (1 << (block & 7));
}

