Linux SPI-NAND 驱动开发指南

linux spi-nand 驱动开发指南1 概述1.1 编写目的1.2 适用范围1.3 相关人员3 流程设计3.1 体系结构3.2 源码结构3.3 关键数据定义3.3.1 flash 设备信息数据结构3.3.2 flash chip 数据结构3.3.3 aw_spinand_chip_request3.3.4 ubi_ec_hdr3.3.5 ubi_vid_hdr3.4 关键接口说明3.4.1 mtd 层接口3.4.1.1 aw_rawnand_mtd_erase3.4.1.2 aw_rawnand_mtd_read3.4.1.3 aw_rawnand_mtd_read_oob3.4.1.4 aw_rawnand_mtd_write3.4.1.5 aw_rawnand_mtd_write_oob3.4.1.6 aw_rawnand_mtd_block_isbad3.4.1.7 aw_rawnand_mtd_block_markbad3.4.2 物理层接口3.4.2.1 aw_spinand_chip_read_single_page3.4.2.3 aw_spinand_chip_erase_single_block3.4.2.4 aw_spinand_chip_isbad_single_block3.4.2.5 aw_spinand_chip_markbad_single_block4 模块配置4.1 uboot 模块配置4.2 kernel 模块配置4.3 env.cfg
linux spi-nand 驱动开发指南
1 概述
1.1 编写目的
介绍 sunxi spinand mtd/ubi 驱动设计, 方便相关驱动和应用开发人员
1.2 适用范围
本设计适用于所有 sunxi 平台
1.3 相关人员
nand 模块开发人员,及应用开发人员等
2 术语、缩略语及概念
mtd:(memory technology device)是用于访问存储设备的 linux 子系统。本模块是mtd 子系统的 flash 驱动部分
ubi:ubi 子系统是基于 mtd 子系统的,在 mtd 上实现 nand 特性的管理逻辑,向上屏蔽nand 的特性
坏块 (bad block):制作工艺和 nand 本身的物理性质导致在出厂和正常使用过程中都会产生坏块
3 流程设计
3.1 体系结构
nand mtd/ubi 驱动主要包括 5 大组件,如下图:
图 3-1: ubi 架构
说明:
mtd standard interface: 对接 mtd 层通用读写接口
flash bad block manager: 驱动层对 flash 坏块的管理
flash spl: 主要是实现读写 boot0、boot1,可用于 ioctl 对boot0、boot1 的升级
securestorage:主要是给上层提供私有数据的管理 spi:host端控制器层的实现。
3.2 源码结构
kernel 源码目录:linux-5.4/drivers/mtd/awnand/spinand
.
├── kconfig
├── makefile
├── physic
│   ├── bbt.c
│   ├── cache.c
│   ├── core.c
│   ├── ecc.c
│   ├── id.c
│   ├── makefile
│   ├── ops.c
│   └── physic.h
├── secure-storage.c
├── sunxi-common.c
├── sunxi-core.c
├── sunxi-debug.c
├── sunxi-nftl-core.c
└── sunxi-spinand.h
内核目录下
`-- include
    `-- linux
       `-- mtd
            |-- aw-spinand.h
3.3 关键数据定义
3.3.1 flash 设备信息数据结构
struct aw_spinand_phy_info {
    const char *model;
    unsigned char nandid[max_id_len];
    unsigned int diecntperchip;
    unsigned int blkcntperdie;
    unsigned int pagecntperblk;
    unsigned int sectcntperpage;
    unsigned int oobsizeperpage;
    #define bad_blk_flag_mark 0x03
    #define bad_blk_flag_frist_1_page 0x00
    #define bad_blk_flag_first_2_page 0x01
    #define bad_blk_flag_last_1_page 0x02
    #define bad_blk_flag_last_2_page 0x03
    int badblockflag;
    #define spinand_dual_read bit(0)
    #define spinand_quad_read bit(1)
    #define spinand_quad_program bit(2)
    #define spinand_quad_no_need_enable bit(3)
    #define spinand_onedummy_after_randomread bit(8)
    int operationopt;
    int maxerasetimes;
    #define has_ext_ecc_se01 bit(0)
    #define has_ext_ecc_status bit(1)
    enum ecc_status_shift ecc_status_shift;
    int eccflag;
    enum ecc_limit_err ecctype;
    enum ecc_oob_protected eccprotectedtype;
};
说明:
• model:flash 的 model 名字
• nandid:flash 的 id 码
• diecntperchip:每 chip 的 die 个数
• blkcntperdie:每 die 有多少个 block
• pagecntperblk:每 block 有多少个 page
• sectcntperpage:每 page 有多少个扇区
• oobsizeperpage:每 page 的 obb 大小
• badblockflag:坏块标志存放在每个 block 的那个 page 中
bad_blk_flag_frist_1_page
bad_blk_flag_first_2_page
bad_blk_flag_last_1_page
bad_blk_flag_last_2_page
• operationopt:支持的操作
spinand_dual_read
spinand_quad_read
spinand_quad_program
spinand_quad_no_need_enable
spinand_onedummy_after_randomread
• maxerasetimes:最大擦除数据
• eccflag:特性物料读 ecc status 说需目录不同
• gd5f1gq4ucyig 通过 0fh + c0h 获取 ecc status,则无需配置 eccflag
• mx35lf1ge4ab 通过 7ch + one dummy byte 获取 ecc status,则配置 eccflag = has_ext_ecc_status
• ecctype:设置 ecc 值对应的状态关系
• eccprotectedtype:在 spare 去选择收 ecc 保护的 16byte 作为 oob 区
例(mx35lf2ge4ad):
{
    .model = mx35lf2ge4ad,
    .nandid = {0xc2, 0x26, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff},
    .diecntperchip  = 1,
    .sectcntperpage = 4,
    .pagecntperblk  = 64,
    .blkcntperdie   = 2048,
    .oobsizeperpage = 64,
    .operationopt = spinand_quad_read | spinand_quad_program | spinand_dual_read,
    .maxerasetimes = 65000,
    .eccflag = has_ext_ecc_status,
    .ecctype = bit4_limit5_to_8_err9_to_15,
    .eccprotectedtype = size16_off4_len4_off8,
    .badblockflag = bad_blk_flag_first_2_page,
},


3.3.2 flash chip 数据结构
struct aw_spinand_chip {
    struct aw_spinand_chip_ops *ops;
    struct aw_spinand_ecc *ecc;
    struct aw_spinand_cache *cache;
    struct aw_spinand_info *info;
    struct aw_spinand_bbt *bbt;
    struct spi_device *spi;
    unsigned int rx_bit;
    unsigned int tx_bit;
    unsigned int freq;
    void *priv;
};
此结构定义了 flash chip 层的物理模型数据结构以及 chip 层对 flash 的操作接口。
• aw_spinand_chip_ops:flash 读、写、擦等操作接口
• aw_spinand_ecc:flash ecc 读、写和校验操作接口
• aw_spinand_cache:对缓存 page 的管理,提高读写效率
• aw_spinand_info:flash id、page size 等信息及获取信息的操作接口
• aw_spinand_bbt:flash 坏块表及管理等操作接口
• spi_device:spi 父设备的操作结构体
• rx_bit:读状态操作标志
• tx_bit:写状态操作标志
3.3.3 aw_spinand_chip_request
struct aw_spinand_chip_request {
    unsigned int block;
    unsigned int page;
    unsigned int pageoff;
    unsigned int ooblen;
    unsigned int datalen;
    void *databuf;
    void *oobbuf;
    unsigned int oobleft;
    unsigned int dataleft;
};
操作目标结构体,改结构体填充我们待操作的 block 的那个 page 的多少偏移的数据
databuf/oobbuf
• block:待操作块
• page:待操作页
• pageoff:操作偏移
• ooblen:操作 oob 长度
• datalen:操作数据长度
• databuf:操作目标数据
• oobbuf:操作目标 oob
3.3.4 ubi_ec_hdr
struct ubi_ec_hdr {
    __be32 magic;
    __u8 version;
    __u8 padding1[3];
    __be64 ec; /* warning: the current limit is 31-bit anyway! */
    __be32 vid_hdr_offset;
    __be32 data_offset;
    __be32 image_seq;
    __u8 padding2[32];
    __be32 hdr_crc;
} __packed;
@magic: erase counter header magic number (%ubi_ec_hdr_magic)
@version: version of ubi implementation which is supposed to accept this ubi image
@padding1: reserved for future, zeroes
@ec: the erase counter
@vid_hdr_offset: where the vid header starts
@data_offset: where the user data start
@image_seq: image sequence number
@padding2: reserved for future, zeroes
@hdr_crc: erase counter header crc checksum
ec: erase count,记录块的擦除次数,在 ubiattach 的时候指定一个 mtd,如果 peb 上没有
ec,则用平均的 ec 值,写入 ec 值只有在擦除的时候才会增加 1
3.3.5 ubi_vid_hdr
struct ubi_vid_hdr {
    __be32 magic;
    __u8 version;
    __u8 vol_type;
    __u8 copy_flag;
    __u8 compat;
    __be32 vol_id;
    __be32 lnum;
    __u8 padding1[4];
    __be32 data_size;
    __be32 used_ebs;
    __be32 data_pad;
    __be32 data_crc;
    __u8 padding2[4];
    __be64 sqnum;
    __u8 padding3[12];
    __be32 hdr_crc;
} __packed;
@magic: volume identifier header magic number (%ubi_vid_hdr_magic)
@version: ubi implementation version which is supposed to accept this ubi image(%ubi_version)
@vol_type: volume type (%ubi_vid_dynamic or %ubi_vid_static)
@copy_flag: if this logical eraseblock was copied from another physical eraseblock(for wear-leveling reasons)
@compat: compatibility of this volume(%0, %ubi_compat_delete, %ubi_compat_ignore,%ubi_compat_preserve, or %ubi_compat_reject)
@vol_id: id of this volume
@lnum: logical eraseblock number
@padding1: reserved for future, zeroes
@data_size: how many bytes of data this logical eraseblock contains
@used_ebs: total number of used logical eraseblocks in this volume
@data_pad: how many bytes at the end of this physical eraseblock are not used
@data_crc: crc checksum of the data stored in this logical eraseblock
@padding2: reserved for future, zeroes
@sqnum: sequence number
@padding3: reserved for future, zeroes
@hdr_crc: volume identifier header crc checksum
参数说明
@sqnum 是创建此 vid 头时的全局序列计数器的值。每次 ubi 写一个新的 vid 头到 flash 时,全局序列计数器都会增加,比如当它将一个逻辑的 eraseblock 映射到一个新的物理的 erase-block 时。全局序列计数器是一个无符号 64 位整数,我们假设它永远不会溢出。@sqnum(序列号) 用于区分新旧版本的逻辑擦除块。
有两种情况,可能有多个物理 eraseblock 对应同一个逻辑 eraseblock,即在卷标识头中有相同的 @vol_id 和 @lnum 值。假设我们有一个逻辑的擦除块 l,它被映射到物理的擦除块 p。
因为 ubi 可以异步擦除物理上的擦除块,所以可能出现以下情况:l 被异步擦除,所以 p 被安排擦除,然后 l 被写入,即。映射到另一个物理的擦除块 p1,所以 p1 被写入,然后不干净的重启发生。结果-有两个物理的 eraseblock p 和 p1 对应同一个逻辑的 eraseblock l。但是 p1 的序列号更大,所以 ubi 在连接 flash 时选择 p1。
ubi 不时地将逻辑擦除块移动到其他物理擦除块,以达到损耗均衡的目的。例如,如果 ubi将 l 从 p 移动到 p1,在 p 被物理擦除之前会发生不干净的重启,有两个物理擦除块 p 和 p1 对应于 l, ubi 必须在 flash 连接时选择其中一个。@sqnum 字段表示哪个 peb 是原始的 (显然 p 的 @sqnum 更低) 和副本。但是选择具有更高序列号的物理擦除块是不够的,因为不干净的重新引导可能发生在复制过程的中间,因此 p 中的数据被损坏(p->p1 没复制完)。仅仅选择序号较低的物理擦除块是不够的,因为那里的数据可能很旧 (考虑在复制之后向 p1 添加更多数据的情况)。此外,不干净的重启可能发生在擦除 p 刚刚开始的时候,所以它会导致不稳定的 p,“大部分” 是 ok 的,但仍然有不稳定的情况。
ubi 使用 @copy_flag 字段表示这个逻辑擦除块是一个副本。ubi 还计算数据的 crc,当数据被移动时,并将其存储在副本 (p1) 的 @data_crc 字段。因此,当 ubi 需要从两个 (p 或 p1)中选择一个物理擦除块时,会检查新块 (p1) 的 @copy_flag。如果它被清除,情况就简单了,新的就会被选中。如果设置了该值,则检查副本 (p1) 的数据 crc。如果 crc 校验和是正确的,这个物理擦除块被选中 (p1)。否则,将选择较老的 p。如果是静态卷,@data_crc 字段包含逻辑擦除块内容的 crc 校验和。对于动态卷,它不包含 crc 校验和规则。唯一的例外情况是,当物理擦除块的数据被磨损均衡子系统移动时,磨损均衡子系统计算数据 crc,并将其存储在 @data_crc 字段中。
@used_ebs 字段仅用于静态卷,它表示该卷的数据需要多少个擦除块。对于动态卷,这个字段不被使用并且总是包含 0。
@data_pad 在创建卷时使用对齐参数计算。因此,@data_pad 字段有效地减少了该卷的逻辑擦除块的大小。当一个人在 ubi 卷上使用面向块的软件 (比如,cramfs) 时,这是非常方便的。
leb 与 peb
图 3-2: peb-leb
3.4 关键接口说明
3.4.1 mtd 层接口
3.4.1.1 aw_rawnand_mtd_erase
static int aw_rawnand_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
description:mtd erase interface
@mtd:mtd device structure
@instr:erase operation descrition structure
return:success return 0,fail return fail code
3.4.1.2 aw_rawnand_mtd_read
static int aw_rawnand_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf)
description:mtd read interface
@mtd:mtd device structure
@from:offset to read from mtd device
@len: data len
@retlen:had read data len
@buf:data buffer
return:success return max_bitflips,fail return fail code
3.4.1.3 aw_rawnand_mtd_read_oob
static int aw_rawnand_mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
description:mtd read data with oob
@mtd:mtd device structure
@ops:oob eperation descrition structure
return:success return max_bitflips,fail return fail code
3.4.1.4 aw_rawnand_mtd_write
static int aw_rawnand_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
description:mtd write data interface
@to:offset to mtd device
@len:want write data len
@retlen:return the writen len
@buf:data buffer
return:success return 0, fail return code fail
3.4.1.5 aw_rawnand_mtd_write_oob
static int aw_rawnand_mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops * ops)
description:write data with oob
@mtd:mtd device structure
@to:offset to mtd device
@ops:oob operation descrition structure
return:success return 0, fail return code fail
3.4.1.6 aw_rawnand_mtd_block_isbad
static int aw_rawnand_mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
description:check block is badblock or not
@mtd:mtd device structure
@ofs: offset the mtd device start (align to simu block size)
return:true if the block is bad, or false if the block is good
3.4.1.7 aw_rawnand_mtd_block_markbad
static int aw_rawnand_mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
description:mark block at the given offset as bad block
@mtd:mtd device structure
@ofs:offset the mtd device start
return:success to mark return 0, or fail return fail code.
3.4.2 物理层接口
3.4.2.1 aw_spinand_chip_read_single_page
static int aw_spinand_chip_read_single_page(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:read physics on a page
@chip:see 3.3.2
@req:see 3.3.3
return:zero on success, else a negative error code.
3.4.2.2 aw_spinand_chip_write_single_page
static int aw_spinand_chip_write_single_page(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:write physics on a page
@chip:see 3.3.2
@req:see 3.3.3
return:zero on success, else a negative error code.
3.4.2.3 aw_spinand_chip_erase_single_block
static int aw_spinand_chip_erase_single_block(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:erase physics on a block
@chip:see 3.3.2
@req: see 3.3.3
return:zero on success, else a negative error code.
3.4.2.4 aw_spinand_chip_isbad_single_block
static int aw_spinand_chip_isbad_single_block(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:set to bad block
@chip:see 3.3.2
@req:see 3.3.3
return:zero on success, else a negative error code.
3.4.2.5 aw_spinand_chip_markbad_single_block
static int aw_spinand_chip_markbad_single_block(struct aw_spinand_chip *chip, struct aw_spinand_chip_request *req)
description:set to bad block
@chip:see 3.3.2
@req:see 3.3.3
return:zero on success, else a negative error code.
4 模块配置
4.1 uboot 模块配置
device drivers-->sunxi flash support--> [*]support sunxi nand devices [*]support sunxi nand ubifs devices [*]support comm nand v1 interface
如下图:
图 4-1: u-boot-spinand-menuconfig
4.2 kernel 模块配置
device drivers->memory technology device(mtd) support-->sunxi-nand
图 4-2: ubi
图 4-3: ker_nand-cfg
图 4-4: ker_spinand
device drivers->spi support
图 4-5: spi-1
图 4-6: spi-2
device drivers->dma engine support
图 4-7: dma-1
图 4-8: dma-2
device drivers->soc(system on chip)
图 4-9: sid
file systems-->miscellaneous filesystems-->
图 4-10: menuconfig_spinand_ubifs
4.3 env.cfg
在 env.cfg 中添加修改下值,setargs_nand_ubi 先 copy 一份 setargs_nand 再添加对应变量
图 4-11: build-mkcmd


步进电机选型要求 步进电机型号大全
5G发牌四周年丨四载筑基,向阳生长
虚拟内存的概念与设置方法
汽车市场发展趋势:智能化
智能停车系统的市场潜力及市场价值
Linux SPI-NAND 驱动开发指南
常见半导体光电器件的基本工作原理说明
AEM宣布收购MEMS测试解决方案供应商Afore
蒙特空气设备:转轮除湿机为电池生产营造良好的外部环境
一加5最新消息:一加宣布愚人节发布一款你猜不到的新品:一加5渲染视频偷跑
韩媒称三星将于2021年推出3款可折叠手机
NVIDIA将在3月GTC 2020上发布Ampere GPU传初期可能采三星7nm EUV
算法工程师涉及哪些领域
三星与特斯拉、现代汽车集团合作,探索智能家居与联网汽车
2020年第四季度,全球智能手机出货达到3.596亿部
首届能源电子产业创新大赛,锦浪科技斩获工信部创新产品殊荣
oracle判断字符串包含某个字符
不会织毛衣?没事,AI会
实现指定个数脉冲输出的四种模式
智能电视仍存在强制观看开机广告现象