nand flash 驱动适配

1,参数适配

复制代码
struct SPI_NAND_READ_ECC_CHECK_T {
	const u8	type;
	const u8	ecc_threhsold_value[SPI_NAND_FLASH_THRESHOLD_VALUE_MAX];
	const u8	ext_data[SPI_NAND_FLASH_EXT_DATA_MAX];
};

typedef enum{
	SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND,
	SPI_NAND_FLASH_READ_DUMMY_BYTE_APPEND,
	SPI_NAND_FLASH_READ_DUMMY_BYTE_DEF_NO
} SPI_NAND_FLASH_READ_DUMMY_BYTE_T;

typedef enum{
	SPI_NAND_FLASH_READ_SPEED_MODE_SINGLE =0,
	SPI_NAND_FLASH_READ_SPEED_MODE_DUAL,
	SPI_NAND_FLASH_READ_SPEED_MODE_QUAD,
	SPI_NAND_FLASH_READ_SPEED_MODE_DEF_NO	
} SPI_NAND_FLASH_READ_SPEED_MODE_T;

typedef enum{
	SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE =0,
	SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD,
	SPI_NAND_FLASH_WRITE_SPEED_MODE_DEF_NO	
} SPI_NAND_FLASH_WRITE_SPEED_MODE_T;

struct spi_nand_flash_oobfree{
	u32	offset;
	u32	len;
};

struct spi_nand_flash_ooblayout
{	
	unsigned int	oobsize;
	struct spi_nand_flash_oobfree oobfree[SPI_NAND_FLASH_OOB_FREE_ENTRY_MAX];
};	

struct SPI_NAND_ECC_FAIL_CHECK_INFO_T {
	u8	ecc_check_mask;
	u8	ecc_uncorrected_value;
};


struct SPI_NAND_QUAD_EN_INFO_T {
	u8	quad_en_mask;
	u8	quad_en_value;
};

struct SPI_NAND_ECC_EN_INFO_T {
	u8	ecc_en_addr;
	u8	ecc_en_mask;
	u8	ecc_en_value;
};

struct SPI_NAND_EXTEND_ID_T {
	const u8	extend_len;
	const u8	extend_id[3];
};

struct SPI_NAND_READ_ECC_CHECK_T {
	const u8	type;
	const u8	ecc_threhsold_value[SPI_NAND_FLASH_THRESHOLD_VALUE_MAX];
	const u8	ext_data[SPI_NAND_FLASH_EXT_DATA_MAX];
};

typedef enum{
	SPI_NAND_FLASH_WRITE_LOAD_FIRST =0,
	SPI_NAND_FLASH_WRITE_EN_FIRST,
} SPI_NAND_FLASH_WRITE_EN_TYPE_T;

struct SPI_NAND_FLASH_INFO_T {
#if defined(TCSUPPORT_BL2_OPTIMIZATION) && defined(IMAGE_BL2)
	u8								mfr_id;
	u8								dev_id;
	const u8								*ptr_name;								
#else
	const u8								mfr_id;
	const u8								dev_id;
	const u8								*ptr_name;
#endif
	u32										device_size;	/* Flash total Size */
	u32										page_size;		/* Page Size 		*/
	u32										erase_size;		/* Block Size 		*/
	u32										oob_size;		/* Spare Area (OOB) Size */
	SPI_NAND_FLASH_READ_DUMMY_BYTE_T		dummy_mode;
	SPI_NAND_FLASH_READ_SPEED_MODE_T		read_mode;
	struct spi_nand_flash_ooblayout			*oob_free_layout;
	u8										die_num;
	SPI_NAND_FLASH_WRITE_SPEED_MODE_T		write_mode;
	u32										feature;
	struct SPI_NAND_ECC_FAIL_CHECK_INFO_T	ecc_fail_check_info;
	SPI_NAND_FLASH_WRITE_EN_TYPE_T			write_en_type;
	struct SPI_NAND_UNLOCK_BLOCK_INFO_T		unlock_block_info;
	struct SPI_NAND_QUAD_EN_INFO_T			quad_en;
	struct SPI_NAND_ECC_EN_INFO_T			ecc_en;
	char									otp_page_num;
#if defined(TCSUPPORT_PARALLEL_NAND)
	u32										ext_id;
	u32										timing_setting;
	u8										min_ecc_req;
	u8										addr_cycle;
#endif
	struct SPI_NAND_EXTEND_ID_T				extend_dev_id;
	u8										soc_ecc_ability;
	struct SPI_NAND_READ_ECC_CHECK_T		read_ecc_ceck;
};

参数说明

  1. mfr_id / dev_id / extend_dev_id
  • 含义:制造商 ID、设备 ID 和扩展 ID,用于唯一识别 Flash 芯片。

  • 获取方式 :发送 Read JEDEC ID (9Fh) 命令,返回 1 字节制造商 ID、1 字节设备 ID,之后可能跟扩展 ID 字节。F35SQB512M 的 JEDEC ID 为 0xCD 0x70 0x70(制造商 0xCD,设备第一字节 0x70,扩展字节 0x70)。注意 B 款扩展 ID 长度为 2(0x70,0x70),A 款长度为 1。

  • 代码使用scan_spi_nand_table() 中比较 mfr_iddev_id,然后调用 check_extend_dev_id() 逐字节比较扩展 ID,完全匹配才使用该表项。这解决了同 ID 不同型号的区分问题。

  1. ptr_name

  2. device_size

  • 含义 :Flash 总容量(字节)。F35SQB512M 为 512Mbit = 64MB = 0x04000000

  • 代码使用 :用于边界检查(spi_nand_boundary_check)和 MTD 设备大小设置。

  1. page_size
  • 含义 :每页主数据区大小。F35SQB512M 为 2KB = 0x800
    注意 :芯片物理页包含主区 + 备用区,但驱动中 page_size 仅指主区。
  1. oob_size
  • 含义 :备用区(OOB)总大小。F35SQB512M 在 ECC 使能时,OOB 为 64 字节(其中一部分用于存储 ECC 校验码,剩余给用户)。
    注意oob_size 是物理 OOB 总大小,而不是用户可用大小(用户可用大小由 oob_free_layout 定义)。
  1. erase_size
  • 含义 :物理块大小 = 页数 × 页大小。F35SQB512M 每块 64 页,块大小 = 64 × 2KB = 128KB = 0x20000
  1. die_num
  • 含义:芯片封装内 Die 数量。单片封装为 1。
  1. dummy_mode

    typedef enum {
    SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND, // Dummy 放在地址之前
    SPI_NAND_FLASH_READ_DUMMY_BYTE_APPEND, // Dummy 放在地址之后
    } ... ;

    struct SPI_NAND_QUAD_EN_INFO_T {
    u8 quad_en_mask; // 配置寄存器中 Quad 使能位掩码
    u8 quad_en_value; // 写入的使能值(通常为掩码本身)
    };

  • 取值SPI_NAND_FLASH_READ_DUMMY_BYTE_APPENDPREPEND

  • 含义 :读命令中假周期(dummy bytes)的位置。大多数芯片在地址之后附加 dummy 字节,因此选择 APPEND
    代码使用 :在 spi_nand_protocol_read_from_cache() 中根据此值决定发送 dummy 字节的位置。

dummy mode,append mode和append mode的差异对比

  1. read_mode / write_mode
  • 含义 :默认读写速度模式(SINGLE/DUAL/QUAD)。为提高性能,通常读选用 DUAL 或 QUAD,写一般仅支持 SINGLE(部分芯片支持 QUAD 写入)。
    代码使用 :在 spi_nand_read_internal()spi_nand_write_internal() 中传递给底层协议函数。

标准、dual,quad spi 访问区别

  1. oob_free_layout

    struct spi_nand_flash_oobfree {
    u32 offset; // 在 OOB 中的起始偏移
    u32 len; // 连续可用字节数
    };

    struct spi_nand_flash_ooblayout {
    unsigned int oobsize; // 总 OOB 大小(通常等于 oob_size)
    struct spi_nand_flash_oobfree oobfree[SPI_NAND_FLASH_OOB_FREE_ENTRY_MAX];
    };

  • 含义 :描述 OOB 中用户可用的区域(偏移和长度)。驱动预定义了多种布局(ooblayout_type1 ~ type23)。

  • 示例ooblayout_type1 = .oobsize = 24, .oobfree = {``{4,4}, {16,8}, {32,8}, {48,4}}

    表示物理 OOB 的字节偏移 4~7(4字节)、16~23(8字节)、32~39(8字节)、48~51(4字节)共 24 字节可供用户使用。其余字节(如偏移 0~3、8~15 等)被 ECC 校验码或坏块标记占用。

  • 重要性:必须与芯片实际 OOB 分配匹配,否则上层文件系统(UBI)会读到错误数据。

  • ECC 关联 :当芯片内部 ECC 开启时,ECC 校验码会占用 OOB 的特定区域(通常每 512 字节扇区对应 13~16 字节 ECC 数据),oob_free_layout 必须避开这些区域。

外部ecc和内部ecc对比

使用举例:

复制代码
oobsize = 20, oobfree = {{0,4}, {4,4}, {20,4}, {36,4}, {52,4}}
  1. feature
  • 类型:位掩码,定义芯片支持的高级功能。常见位:

    • SPI_NAND_FLASH_ERASE_STATISTICS:支持擦除统计(在 OOB 中记录擦除次数)。

    • SPI_NAND_FLASH_READ_ECC_ERROR_BIT_CHECK:支持读取时检测 ECC 超阈值(即位翻转过多但仍可纠正),触发 BMT 迁移。

    • SPI_NAND_FLASH_BLANK_PAGE_FIXUP:需要空白页 ECC 修复(某些 Winbond 芯片)。

    • SPI_NAND_FLASH_OOB_RESERVE_FOR_BMT:OOB 中预留区域给 BMT 存储映射信息。

    • 其他:SPI_NAND_FLASH_PLANE_SELECT_HAVESPI_NAND_FLASH_DIE_SELECT_1_HAVE 等。

  1. write_en_type
  • 取值SPI_NAND_FLASH_WRITE_EN_FIRSTSPI_NAND_FLASH_WRITE_LOAD_FIRST

  • 含义 :某些芯片要求先发写使能再发数据加载,有些顺序相反。绝大多数芯片采用 WRITE_EN_FIRST

  1. unlock_block_info

    struct SPI_NAND_UNLOCK_BLOCK_INFO_T {
    u8 unlock_block_mask; // 保护寄存器中的掩码
    u8 unlock_block_value; // 要写入的值(0 表示全部解锁)
    };

  • 通过 Set Feature 写保护寄存器(地址 0xA0),清除块保护。

  • 示例{0x7C, 0x00} 表示将保护寄存器的 bit2~bit6 清零(解除保护)。
    代码使用spi_nand_manufacute_init() 中调用 spi_nand_protocol_set_feature(_SPI_NAND_ADDR_PROTECTION, feature)

  1. quad_en

    struct SPI_NAND_QUAD_EN_INFO_T {
    u8 quad_en_mask; // 配置寄存器中 Quad 使能位掩码
    u8 quad_en_value; // 写入的使能值(通常为掩码本身)
    };

  • 如果芯片支持 Quad SPI,需设置 Feature 寄存器(地址 0xB0)的 QE 位。若芯片默认支持 Quad 或不需要使能,填 {0x0, 0x0}
  1. ecc_en

    struct SPI_NAND_ECC_EN_INFO_T {
    u8 ecc_en_addr; // 特性寄存器地址(如 _SPI_NAND_ADDR_FEATURE = 0xB0)
    u8 ecc_en_mask; // ECC 使能位掩码
    u8 ecc_en_value; // 使能值(掩码与上具体位)
    };

  • 含义:配置芯片内部 ECC 的使能寄存器和位。

    • 地址 ecc_en_addr:通常为 _SPI_NAND_ADDR_FEATURE0xB0)。

    • 掩码 ecc_en_mask:一般为 0x10(bit4)。

    • 使能值 ecc_en_value:使能时该位应设为 1,即 0x10

  • 代码使用SPI_NAND_Flash_Enable_OnDie_ECC() 读取该寄存器,若当前值未使能,则设置并写回。

  • 默认状态:大多数 SPI NAND 上电后 ECC 默认使能,但驱动仍会主动设置以确保状态。

  1. ecc_fail_check_info

    struct SPI_NAND_ECC_FAIL_CHECK_INFO_T {
    u8 ecc_check_mask; // 状态寄存器中需要检查的位掩码
    u8 ecc_uncorrected_value; // 掩码后该值表示不可纠正错误
    };

  • 含义 :从状态寄存器(地址 0xC0)中读取 ECC 状态时,使用 ecc_check_mask 提取相关位,并与 ecc_uncorrected_value 比较,若相等表示发生了不可纠正的 ECC 错误。

  • 典型值{0x30, 0x20}

    • 掩码 0x30(bit4-5)表示 ECC 状态位。

    • 0x20(bit5=1, bit4=0)表示"不可纠正错误"。

  • 数据手册验证:F35SQB512M 的状态寄存器 bit5-4 定义为 ECCS1-0:

    • 00:无错误

    • 01:1-bit 错误并纠正

    • 1x:>1-bit 错误且不可纠正

      因此 0x20(二进制 10 0000)对应 bit5=1, bit4=0,属于不可纠正类。

  • 代码使用ecc_fail_check() 函数读取状态寄存器后,执行:

    复制代码
    status &= ecc_check_mask;
    if (status == ecc_uncorrected_value)
        return DETECTED_BAD_BLOCK;
  1. read_ecc_ceck

    struct SPI_NAND_READ_ECC_CHECK_T {
    const u8 type; // 检测类型
    const u8 ecc_threhsold_value[3]; // 阈值对应的 ECC 状态值
    const u8 ext_data[3]; // 扩展数据(如第二个寄存器地址)
    };

  • 含义 :用于检测 ECC 错误超过阈值(即可纠正但位翻转过多,预示块可能即将损坏)。当检测到这种状态时,驱动会主动触发 BMT 迁移,将数据移到好块,避免未来数据丢失。

  • 类型

    • _SPI_NAND_CHECK_ECC_THROSHOLD_BY_FLASH:芯片内部有专门的阈值寄存器,通过 Set Feature 配置。

    • _SPI_NAND_CHECK_ECC_THROSHOLD_BY_REG:直接通过状态寄存器的某个值判断阈值(如 0x10 表示超过阈值)。

    • _SPI_NAND_CHECK_ECC_THROSHOLD_BY_2REG:需要读取两个寄存器组合判断。

  • 示例(来自代码中 Winbond W25N02KV):

    复制代码
    read_ecc_ceck: {_SPI_NAND_CHECK_ECC_THROSHOLD_BY_FLASH,
                    {0x30, 0x0, 0x0},
                    {_SPI_NAND_ADDR_EXTEND_BFD, 0x60, 0x0}}

    表示通过 Flash 内部阈值检测,地址 0x10 设置阈值,状态寄存器值 0x30 表示超过阈值。

  • 代码使用 :在 ecc_fail_check() 中,如果芯片支持 SPI_NAND_FLASH_READ_ECC_ERROR_BIT_CHECK,则读取状态寄存器后,将状态值与 ecc_threhsold_value[0] 比较,若相等则返回 ECC_EXCEEDED_THRESHOLD,进而触发 update_bmt()

  1. soc_ecc_ability
  • 含义:当使用 SoC 控制器 ECC(而非芯片内部 ECC)时,指示控制器需要提供的 ECC 纠错能力(如 4-bit, 8-bit, 12-bit)。若使用芯片内部 ECC,该字段设为 0。

  • 代码使用 :在 SPI_NAND_Flash_Init() 中,若 isSpiNandAndCtrlECC 为真,则根据 soc_ecc_ability 配置 NFI 的 ECC 引擎(encode_ecc_abiliry, decode_ecc_abiliry 等)。

19. otp_page_num

  • 含义 :OTP(One Time Programmable)页数量。F35SQB512M 提供 62 页 OTP,可用于存储一次性数据。若芯片不支持 OTP,填 -1

2,新增一款nand flash

spi_nand_flash_tables里面新增自己的nand flash

复制代码
 struct spi_nand_flash_ooblayout ooblayout_type1 = {
	.oobsize = 24, 
	.oobfree = {{4,4}, {16,8}, {32,8}, {48,4}, {0, 0}}
};

 {
        mfr_id:                     _SPI_NAND_MANUFACTURER_ID_FORESEE,
        dev_id:                     _SPI_NAND_DEVICE_ID_F35SQA512M,
        ptr_name:                   (const u8 *) "_SPI_NAND_DEVICE_ID_F35SQA512M",
        device_size:                _SPI_NAND_CHIP_SIZE_512MBIT,
        page_size:                  _SPI_NAND_PAGE_SIZE_2KBYTE,
        oob_size:                   _SPI_NAND_OOB_SIZE_64BYTE,
        erase_size:                 _SPI_NAND_BLOCK_SIZE_128KBYTE,
        dummy_mode:                 SPI_NAND_FLASH_READ_DUMMY_BYTE_APPEND,
        read_mode:                  SPI_NAND_FLASH_READ_SPEED_MODE_DUAL,
        write_mode:                 SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE,
        oob_free_layout:            &ooblayout_type1,
        feature:                    SPI_NAND_FLASH_ERASE_STATISTICS,
        die_num:                    1,
        ecc_fail_check_info:        {0x30, 0x20},
        write_en_type:              SPI_NAND_FLASH_WRITE_EN_FIRST,
        unlock_block_info:          {0x7C, 0x0},
        quad_en:                    {0x01, 0x01},
        ecc_en:                     {_SPI_NAND_ADDR_FEATURE, 0x10, 0x10},
#ifdef TCSUPPORT_NAND_FLASH_OTP
        otp_page_num:               -1,
#endif
        extend_dev_id:              _SPI_NAND_DEVICE_ID_F35SQA512M_EXTEND,
    },
成员 类型 说明 江波龙 F35SQA512M
mfr_id u8 制造商 ID,从数据手册获取 0xCD (Foresee)
dev_id u8 设备 ID 0x70
ptr_name const u8* 设备名称字符串 "F35SQA512M"
device_size u32 总容量(字节) 0x04000000 (512 Mbit = 64 MB)
page_size u32 页大小(字节) 0x0800 (2048)
oob_size u32 OOB 大小(字节) 0x40 (64)
erase_size u32 块大小(字节) 0x20000 (128 KB)
dummy_mode enum Dummy 字节插入位置 SPI_NAND_FLASH_READ_DUMMY_BYTE_APPEND
read_mode enum 读操作 SPI 速度模式 SPI_NAND_FLASH_READ_SPEED_MODE_DUAL
write_mode enum 写操作 SPI 速度模式 SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE
oob_free_layout struct* OOB 空闲区域布局指针 &ooblayout_type1
die_num u8 芯片内 Die 数量 1
feature u32 特性标志位(如擦除统计、OTP等) SPI_NAND_FLASH_ERASE_STATISTICS
ecc_fail_check_info struct ECC 错误检测掩码和值 {0x70, 0x70}
write_en_type enum 写使能顺序 SPI_NAND_FLASH_WRITE_LOAD_FIRST
unlock_block_info struct 解锁所有块的寄存器配置 {0x38, 0x0}
quad_en struct Quad 模式使能掩码和值 {0x01, 0x01}
ecc_en struct ECC 使能寄存器地址、掩码、值 {_SPI_NAND_ADDR_ECC, 0x10, 0x10}
otp_page_num char OTP 页数量,-1 表示不支持 -1
extend_dev_id struct 扩展设备 ID(长度和字节数组) {1, {0x70,0,0}}
soc_ecc_ability u8 控制器 ECC 能力(如 4/8/12 bits) 未使用(0)
read_ecc_ceck struct ECC 阈值检测配置 未配置
相关推荐
振南的单片机世界15 小时前
影子寄存器:改ARR下个周期才生效,波形不突变
arm开发·stm32·单片机·嵌入式硬件
陌上花开缓缓归以20 小时前
基于 W25N01KV 的 MTD/BBT/BMT/UBI 框架与坏块导致系统挂死问题剖析
arm开发
szxinmai主板定制专家2 天前
基于ZYNQ MPSOC图像采集与压缩系统总体设计方案
linux·arm开发·人工智能·嵌入式硬件·fpga开发
底层开发智库2 天前
学习ARM新姿势,理论实践的结合
arm开发
szxinmai主板定制专家2 天前
基于ZYNQ MPSOC ARM+FPGA的超高清实时图像采集与压缩系统设计
linux·运维·服务器·arm开发·人工智能·嵌入式硬件·fpga开发
加油20192 天前
嵌入式软件技术栈和学习路线详解
linux·arm开发·数据结构·mqtt·设计模式·嵌入式
虹科汽车电子4 天前
高效传感器与ECU研发测试方案:虹科PSI5模拟器加速智能安全系统落地
arm开发·安全·seskion
szxinmai主板定制专家5 天前
RK3568 + CODESYS+实时系统运动控制器PLC,支持 AI 视觉目标检测,预测性维护,混合多系统部署,多路模拟量采集
arm开发·人工智能·嵌入式硬件·fpga开发
XMAIPC_Robot5 天前
深度无人机自动驾驶仪,中小型无人机硬件在环仿真飞行
运维·arm开发·人工智能·fpga开发·无人机·边缘计算