SPI NAND 移植
SPI NAND 移植工作需要 SoC 端 SPI 模块的驱动能力以及对 SPI NAND 模块的正确配置。
本节以 Fudan Micro
的 FM25S01A
以及 Foresee
的 F35SQA002G
为例,详细说明 SPI NAND 器件的移植流程。SPI NAND 在 U-Boot 和 Kernel
中的实现逻辑类似,文件路径和内容基本一致,本文以 Kernel 中的移植为例。
-
配置 MID、DID 和 NAND_MEMORG 三个参数,具体配置信息以数据手册为准 。
-
配置 READ_FROM_CACHE 中的 dummy 长度。
一般情况下设为 1,部分情况下为 2,具体以数据手册为准。
其他参数不配置或者配置不正确均不影响使用,但可能会对性能或者部分特殊操作产生影响,详细描述可查看 配置 xxx_spinand_table。
准备文件
-
在 source/linux-5.10/drivers/mtd/nand/spi 目录下创建带相应公司标识的文件,如 fmsh.c 或 foresee.c。如果该公司的文件已经存在,则略过此步,直接添加新器件支持即可。
一般情况下,同一个公司的 SPI NAND 的操作接口类似,创建一个文件可方便管理公司的接口配置。
- 在 Makefile
中添加该文件的编译:
spinand-objs := core.o fmsh.o foresee.o …
-
在 bsp/peripheral/spinand/inc/spinand.h 中声明如下:
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
添加驱动索引
-
检查 source/linux-5.10/drivers/mtd/nand/spi/core.c 中的 spinand_manufacturers 信息,查看新设备的厂商是否在列表之中:
static const struct spinand_manufacturer *spinand_manufacturers[] = { &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, ¶gon_spinand_manufacturer, &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, &fmsh_spinand_manufacturer, &foresee_spinand_manufacturer, };
-
检查具体的设备厂商文件,具体的型号是否在列表之中,以 Fudan Micro 为例:
static const struct spinand_info fmsh_spinand_table[] = { SPINAND_INFO("FM25S01A", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&fm25s01_ooblayout, fm25s01_ecc_get_status)), };
配置 MID 和 DID
spinand_manufacturer
数据结构为第一级索引,用来描述厂商的系列器件信息。SPI NAND
的接口和操作命令基本统一,一般无需特殊处理。驱动上为了更好的兼容性,预留了部分接口方便配置使用。const struct spinand_manufacturer winbond_spinand_manufacturer = {
.id = SPINAND_MFR_FMSH, //厂家的 ID,即 MID
.name = "FudanMicro", //厂家名字标识
.chips = fmsh_spinand_table, //本驱动支持的器件
.nchips = ARRAY_SIZE(fmsh_spinand_table), // 支持的器件的个数
.ops = &fmsh_spinand_manuf_ops, // 本公司的器件 私有操作接口
};
struct spinand_manufacturer_ops {
int (*init)(struct spinand_device *spinand); //初始化接口,如果没有特殊操作,可以为空
void (*cleanup)(struct spinand_device *spinand); //清理接口,如果没有特殊操作,可以为空
};
FudanMicro
和 Foresee
未设置特殊的初始化接口,因此均预留为空。
-
Manufacture ID (MID):厂商的唯一标识, 一般在数据手册文档中搜 Manufacture ID 或者 MID 即可获得。
-
Device ID (DID):器件的唯一标识,一般在数据手册文档中搜 Device ID 或者 DID 即可获得。
#define SPINAND_MFR_FMSH 0xA1
#define SPINAND_MFR_FORESEE 0xCD
配置 SPINAND_INFO
SPI NAND 的接口和操作命令基本统一,一般无需特殊处理。驱动上为了更好的兼容性,预留了部分接口方便配置使用,参数主要由 SPINAND_INFO 描述。
#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
__flags, ...)
{
.model = __model, // 对应器件型号,描述字符,不进行具体匹配
.devid = __id, // 对应器件 DID(DeviceID),是该器件的唯一标识
.memorg = __memorg, // 器件存储结构
.eccreq = __eccreq, // 请求ECC的参数
.op_variants = __op_variants, // 读写函数操作集合地址
.flags = __flags, // 功能标识
__VA_ARGS__
}
xxx_spinand_table 数据结构为第二级索引,描述厂商的系列器件信息,是 SPI NAND 驱动的核心。关于 xxx_spinand_table 的详细说明,可查看 配置 xxx_spinand_table。
static const struct spinand_info foresee_spinand_table[] = {
SPINAND_INFO("F35SQA002G",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x72),
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(1, 528),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&f35sqa_ooblayout,
f35sqa_ecc_get_status)),
};
配置 NAND_MEMORG
memorg
通过 NAND_MEMORG 结构描述。一般情况下,器件数据手册会在开头描述
memorg
信息。NAND_MEMORG
的参数示例描述如下:#define NAND_MEMORG(bpc, ps, os, ppe, epl, mbb, ppl, lpt, nt)
{
.bits_per_cell = (bpc), // Cell 是 NAND 的最小单元,一般只能存储 1bit,少有其他值得
.pagesize = (ps), // 页大小,大部分的器件的页通过(N + Mbytes)的方式描述,N 为页大小,M 为 oob
.oobsize = (os), //界外大小,一般用于存放 ECC 校验数据或其它数据,和 pagesize 共同描述
.pages_per_eraseblock = (ppe), // 一个擦除块有多少个页
.eraseblocks_per_lun = (epl), // 一个 lun(die)有多少个擦除块,1Gb/(64 x 2048 x 8) = 1024
.max_bad_eraseblocks_per_lun = (mbb), //器件出厂的最大坏块数,一般在数据手册中通过 bad blocks 查找到
.planes_per_lun = (ppl), //一般设置为 1,单 die
.luns_per_target = (lpt), //一般设置为 1
.ntargets = (nt), //一般设置为 1
}