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)), };
#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__
}
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
}
FM25S01A
以 FM25S01A
为例,配置的参数为 NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1,
1)
-
single cell, 则 bpc 设置为 1
-
Page size: 2048 + 64 bytes, 则 pagesize = 2048, oobsize = 64
-
Block size: 64 pages, 则 pages_per_eraseblock = 64
-
eraseblocks_per_lun 通过计算所得,该器件容量为 1Gb,一个 block 的容量为 (64 pages x 2048 bytes)= 128KB,则 1Gb / 128KB = 1024, 注意 bit 和 byte 的单位
-
max_bad_eraseblocks_per_lun:0014h (16 进制) = 20(十进制)
-
ppl,lpt,nt 描述的是该器件中有几个单元,大部分为 1,lun 对应器件上的 die,即有几个晶圆
F35SQA002G
以 F35SQA002G
为例,配置的参数为 NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1)
-
cell 未明确, bpc 设置为 1
-
Page size: 2k + 64 bytes, 则 pagesize = 2048, oobsize = 64
-
Block size: 64 pages, 则 pages_per_eraseblock = 64
-
eraseblocks_per_lun 通过计算所得,该器件容量为 2Gb(256MB),一个 block 的容量为 (64 pages x 2048 bytes)= 128KB,则 256M / 128KB = 2048
-
max_bad_eraseblocks_per_lun:0028h (16 进制) = 40 (十进制)
-
ppl,lpt,nt 描述的是该器件中有几个单元,大部分为 1,lun 对应器件上的 die,即有几个晶圆
NAND_ECCREQ
ECC: Error Correcting Code, NAND_ECCREQ 用来描述每 stp bytes 有多少位的内部 ECC,一般通过搜
`ECC``
获得, 如上图所示
-
FM25S01A 为 NAND_ECCREQ(1, 512)
-
F35SQA002G 为 NAND_ECCREQ(1, 528)
-
#define NAND_ECCREQ(str, stp) { .strength = (str), .step_size = (stp) }
SPINAND_INFO_OP_VARIANTS
#define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \
{ \
.read_cache = __read, \
.write_cache = __write, \
.update_cache = __update, \
}
- read_cache_variants
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), //command id:0xeb SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), //command id:0x6b SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), //command id:0xbb SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), //command id:0x3b SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), //command id:0x0b SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); //command id:0x03
在数据手册中搜索 commands 或者 command set 获得 command 列表来设置- SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0)
- 第一个参数是地址,将在后续被赋值,所以此处设置为规定值:0
- 第二个参数是 dummy 的 byte 长度,大部分为 1,少数部分为 2,通过在数据手册中查找获得
- 第三个参数为读取数据的 buf,后续赋值,因此固定为 NULL
- 第四个参数为读取数据的长度,后续赋值,所以设置为固定值:0
- 如果有些器件对 0xeb 的 dummy 没有明确标识,则设置为 1
- 4 个参数中,实际只需要从数据手册中去获取 dummy 的长度即可,其他均赋固定值
- 有些器件支持的命令比较少,需求去掉一些不支持的操作,一般根据 variants 的 command id 来判断,如```F35SQA002G```就不支持 0xeb,0xbb 命令
- SPINAND_PAGE_READ_FROM_CACHE_OP
-
该命令被设置两次,分别是 normal read 0x3 和 fast read 0xb
-
第一个参数为 fast read 标志,对于支持 0x0b 和 0x03 两个命令的器件,此二者必须成对存在,并且参数一致
-
- SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0)
- write_cache_variants
所有器件的设置都一致,不需要根据数据手册更改
static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0));
- update_cache_variants
所有器件的设置都一致,不需要根据数据手册更改
static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0));
SPINAND_ECCINFO
SPI NAND 的坏块管理分两部分,器件内部硬件逻辑进行的管理和器件对外提供给用户手工操作的管理逻辑, ECCINFO 即对外暴露的给用户手工操作的区域
-
即便不进行 ECCINFO 的设置,坏块管理还是处于工作状态
-
器件自身的坏块管理更高效,因此一般占 ECC 容量的大部分
-
现在大部分器件都不再提供用户操作接口,因为使用门槛比较高
-
Toshiba 的 TC58CYG0S3HRAIJ 有对外暴露 64Byte ECC
-
FM25S01A 和 F35SQA002G 两款器件都没有提供外部使用接口
-
#define SPINAND_ECCINFO(__ooblayout, __get_status) .eccinfo = { .ooblayout = __ooblayout, //ooblayot 操作函数集合 .get_status = __get_status, //获取 ECC 状态 } Toshiba 设置 static int tx58cxgxsxraix_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section > 0) return -ERANGE; region->offset = mtd->oobsize / 2; //ECC 总长度为 128Byte,64B 为对外 region->length = mtd->oobsize / 2; //长度也为 64 return 0; }