Edit online

SPI NAND

4 Dec 2024
Read time: 2 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/ 源码目录是否有该器件厂商的驱动。如

    winbond.c

    如果不存在,则需要添加新厂商支持。

  • 添加新厂商支持之后,需要将将该厂商的配置添加到系统中

    drivers/mtd/nand/spi/core.c

    struct spinand_manufacturer *spinand_manufacturers[]

  • 检查厂商驱动中是否支持该器件

    winbond.c

    struct spinand_info winbond_spinand_table[]

    如果没有,则添加新器件到列表中即可。