SPI NAND
Read time: 4 minute(s)
本章节描述 SPI NAND 驱动的相关配置和使用。
驱动框架
SPI NAND 的操作基于 SPI 命令,除了个别型号可能有不同之外,基本上操作和行为都是一致的, 所以 U-Boot 中已经实现了共用版本的 SPI NAND 驱动,具体器件只需要添加小部分驱动代码即可。
相关配置:
-
CONFIG_MTD
-
CONFIG_DM_SPI
-
CONFIG_SPI_MEM
-
CONFIG_MTD_SPI_NAND
-
CONFIG_SPL_SPI_NAND_ARTINCHIP
源码位于 drivers/mtd/nand/spi/。
驱动接口
SPI NAND 属于 MTD 设备,使用 MTD 相关接口。具体参考:
-
include/linux/mtd/mtd.h
初始化和读写
SPI NAND 是挂载在 SPI 总线上的 MTD 设备,初始化 probe 在 MTD 设备第一次被使用时触发。 调用
mtd_probe_devices() 是对 MTD
设备驱动初始化的常用方式。
mtd_probe_devices(void) |-> mtd_probe_uclass_mtd_devs(void) // drivers/mtd/mtd_uboot.c | // 通过 while 循环,逐个 UCLASS_MTD 设备 find |-> uclass_find_device(UCLASS_MTD, idx, &dev) |-> mtd_probe(dev) |-> device_probe(dev) |-> spinand_probe(dev) // drivers/mtd/nand/spi/core.c | // spinand = dev_get_priv(dev); | // slave = dev_get_parent_priv(dev); | // mtd = dev_get_uclass_priv(dev); | // nand = &spinand->base; | // | // spinand->slave = slave; | |-> spinand_init(spinand); | |-> spinand_detect(spinand); | | |-> spinand_manufacturer_detect(spinand); | | | // drivers/mtd/nand/spi/core.c | | |-> spinand_manufacturers[i]->ops->detect(spinand); | | // 尝试厂商的 detect 函数 | | | |-> spinand_manufacturer_init(spinand); | |-> nanddev_init(nand, &spinand_ops, THIS_MODULE); | | | // drivers/mtd/nand/core.c | | | // mtd->type = memorg->bits_per_cell == 1 ? | | | // MTD_NANDFLASH : MTD_MLCNANDFLASH; | | | | | |-> nanddev_bbt_init(nand) // drivers/mtd/nand/bbt.c | | // 此处仅申请标记坏块的 Cache 空间,不做坏块检查 | | | |-> // mtd = spinand->base.mtd | // | // mtd->_read_oob = spinand_mtd_read; | // mtd->_write_oob = spinand_mtd_write; | // mtd->_block_isbad = spinand_mtd_block_isbad; | // mtd->_block_markbad = spinand_mtd_block_markbad; | // mtd->_block_isreserved = spinand_mtd_block_isreserved; | // mtd->_erase = spinand_mtd_erase; | // | // 此处完成 mtd 的初始化 | |-> add_mtd_device(mtd); |-> idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL); // 添加到 mtd_idr 列表中
NAND 存储设备在访问前,通常要做一次坏块检查。U-Boot
中在添加分区的时候进行检查坏块:
do_mtd_list(); // cmd/mtd.c |-> mtd_probe_devices(); // drivers/mtd/mtd_uboot.c |-> add_mtd_partitions(); // drivers/mtd/mtdpart.c |-> allocate_partition(); // drivers/mtd/mtdpart.c | | // 这里做坏块统计 | |-> mtd_block_isbad(); // drivers/mtd/mtdcore.c | |-> mtd->_block_isbad(mtd, ofs); | spinand_mtd_block_isbad(); // drivers/mtd/nand/spi/core.c | |-> nanddev_isbad(); // drivers/mtd/nand/core.c | |-> spinand_isbad(); // drivers/mtd/nand/spi/core.c | |-> spinand_read_page(); | |-> add_mtd_device(slave); // drivers/mtd/mtdcore.c
上层应用,如 mtd 命令和 UBI,通过 mtd_read / mtd_write API
进行读写等操作。
mtd_read |-> mtd->_read_oob(mtd, from, &ops); part_read_oob(mtd, from, &ops); // drivers/mtd/mtdpart.c |-> mtd->parent->_read_oob(mtd->parent, from + mtd->offset, ops); spinand_mtd_read(mtd, from, &ops); // drivers/mtd/nand/spi/core.c |-> spinand_read_page(spinand, &iter.req, enable_ecc); |-> spinand_load_page_op(spinand, req); | |-> spi_mem_exec_op(spinand->slave, &op); // drivers/spi/spi-mem.c | |-> ops->mem_ops->exec_op(slave, op); | aic_spi_mem_exec_op(slave, op); | // drivers/spi/artinchip_spi.c | |-> spinand_read_from_cache_op(spinand, req); |-> spi_mem_exec_op(spinand->slave, &op); |-> ops->mem_ops->exec_op(slave, op); aic_spi_mem_exec_op(slave, op); // drivers/spi/artinchip_spi.c
mtd_write |-> mtd->_write_oob(mtd, to, &ops); part_write_oob(mtd, to, &ops); // drivers/mtd/mtdpart.c |-> mtd->parent->_write_oob(mtd->parent, to + mtd->offset, ops); spinand_mtd_write(mtd, to, &ops); |-> spinand_write_page(spinand, &iter.req); |-> spinand_write_enable_op(spinand); |-> spinand_write_to_cache_op(spinand, req); |-> spinand_program_op(spinand, req); |-> spi_mem_exec_op(spinand->slave, &op); // drivers/spi/spi-mem.c |-> ops->mem_ops->exec_op(slave, op); aic_spi_mem_exec_op(slave, op); // drivers/spi/artinchip_spi.c
添加新器件
U-Boot 的代码仅配置了数量有限的 SPI NAND 器件,在使用新器件时,需要在代码中增加对新器件的支持。
查看 drivers/mtd/nand/spi/ 源码目录,确认 SPI NAND 器件的厂商是否在支持列表。例如,查看源码目录中是否存在 winbond.c 。如果不存在,则需要添加新厂商支持,详情可查看 SPI NAND 移植。