Edit online

SPI NAND 加载

4 Dec 2024
Read time: 4 minute(s)

官方版本的 SPL 并不支持从 SPI NAND 启动,ArtInChip 增加了从 SPI NAND 的 MTD 分区 和 UBI 加载 U-Boot 的支持。

common/spl/spl_spi_nand.c 中注册了两个不同的 SPL 程序加载器。
#ifdef CONFIG_SPL_UBI
/* Use priorty 0 to override other SPI device when this device is enabled. */
SPL_LOAD_IMAGE_METHOD("SPINAND_UBI", 0, BOOT_DEVICE_SPI, spl_ubi_load_image);
#else
SPL_LOAD_IMAGE_METHOD("SPINAND", 0, BOOT_DEVICE_SPI, spl_spi_nand_load_image);
#endif

U-Boot 保存在 MTD 分区时

在 SPL 初始化过程中,通过 boot_from_devices(spl_boot_list) 函数调用,检查当前项目 所支持的 SPL 读取的存储介质类型,然后依次检查是否存在对应的程序加载器。
board_init_r()    // common/spl/spl.c
|-> boot_from_devices(spl_boot_list)
    |-> spl_ll_find_loader()  // 根据 device 找到 spl_load_image 指针
            // 这里可能是各种介质的 load image 函数
            // SPL_LOAD_IMAGE_METHOD() 定义的 Loader
            // 可能是 MMC/SPI/BROM/...

找到 SPL SPINAND Loader 之后,从项目配置的指定位置读取数据。

  • CONFIG_SYS_SPI_NAND_U_BOOT_OFFS
    boot_from_devices(spl_boot_list); // common/spl/spl.c
    |-> spl_ll_find_loader()  // 根据 device 找到 spl_load_image 指针
    |   // 此处通过遍历固件的 .u_boot_list_2_spl_image_loader_* 段
    |   // 找到当前使能的存储介质驱动,然后逐个尝试
    |
    |-> spl_load_image(loader);
        |-> loader->load_image(spl_image, &bootdev);
            spl_spi_nand_load_image();  // arch/arm/mach-artinchip/spl_spi_nand.c
            |-> spl_spi_nand_init();
            |   |-> uclass_first_device(UCLASS_MTD, &dev); // drivers/core/uclass.c
            |   |-> mtd_probe(dev);
            |   |-> get_mtd_device(NULL, 0);
            |-> spl_get_load_buffer(-sizeof(*header), sizeof(*header));
            |-> spl_spi_nand_read();
            |   // 读取头信息
            |-> spl_parse_image_header(spl_image, header);
            |-> spl_spi_nand_read();// arch/arm/mach-artinchip/spl_spi_nand.c
                |  // 读取整个 U-Boot 镜像
                |-> mtd_block_isbad(mtd, off);
                |   // 跳过坏块
                |-> mtd_read(); // drivers/mtd/mtdcore.c

U-Boot 保存在 UBI 中时

在 SPL 初始化过程中,通过 boot_from_devices(spl_boot_list) 函数调用,检查当前项目 所支持的 SPL 读取的存储介质类型,然后依次检查是否存在对应的程序加载器。
board_init_r()    // common/spl/spl.c
|-> boot_from_devices(spl_boot_list)
    |-> spl_ll_find_loader()  // 根据 device 找到 spl_load_image 指针
            // 这里可能是各种介质的 load image 函数
            // SPL_LOAD_IMAGE_METHOD() 定义的 Loader
            // 可能是 MMC/SPI/BROM/...

找到 SPL SPINAND UBI Loader 之后,从项目配置的指定位置读取数据。

  • CONFIG_SPL_UBI_INFO_ADDR

  • CONFIG_SPL_UBI_PEB_OFFSET

  • CONFIG_SPL_UBI_VID_OFFSET

  • CONFIG_SPL_UBI_LEB_START

或者从指定 Volume 中读取

  • CONFIG_SPL_UBI_LOAD_MONITOR_VOLNAME
    boot_from_devices(spl_boot_list); // common/spl/spl.c
    |-> spl_ll_find_loader()  // 根据 device 找到 spl_load_image 指针
    |   // 此处通过遍历固件的 .u_boot_list_2_spl_image_loader_* 段
    |   // 找到当前支持的存储介质,然后逐个尝试
    |
    |-> spl_load_image(loader);
        |-> loader->load_image(spl_image, &bootdev);
            spl_ubi_load_image(); // arch/arm/mach-artinchip/spl_spi_nand.c
            |-> spl_spi_nand_init();
            |   |-> uclass_first_device(UCLASS_MTD, &dev); // drivers/core/uclass.c
            |   |-> mtd_probe(dev);
            |   |-> get_mtd_device(NULL, 0);
            |-> spl_get_load_buffer(-sizeof(*header), sizeof(*header));
            |-> ubispl_load_volumes(&info, volumes, 1); // drivers/mtd/ubispl/ubispl.c
            |   |  // 读取整个 U-Boot 镜像的内容
            |   |-> ipl_scan(ubi);
            |   |-> ipl_load(ubi, lv->vol_id, lv->load_addr);
            |       |   // drivers/mtd/ubispl/ubispl.c
            |       |-> ubi_load_block(ubi, laddr, vi, vol_id, lnum, last);
            |           |-> ubi_io_is_bad(ubi, pnum); // drivers/mtd/ubi/io.c
            |           |   // drivers/mtd/ubispl/ubispl.c
            |           |-> ubi_io_read(ubi, laddr, pnum, ubi->leb_start, dlen);
            |               |  // drivers/mtd/ubispl/ubispl.c
            |               |-> ubi->read(pnum + ubi->peb_offset, from, len, buf);
            |                   nand_spl_read_block(pnum + ubi->peb_offset,
            |                   |                       from, len, buf);
            |                   |  // arch/arm/mach-artinchip/spl_spi_nand.c
            |                   |-> mtd_read();
            |
            |-> spl_parse_image_header(spl_image, header);

读数 SPI NAND 数据的流程

mtd_read(); // drivers/mtd/mtdcore.c
|-> mtd->_read_oob(mtd, from, &ops);
    part_read_oob(mtd, from, ops); // drivers/mtd/mtdpart.c
        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-nodm.c
            |       |-> spi_xfer(slave, op_len * 8, op_buf, NULL, flag);
            |
            |-> spinand_read_from_cache_op(spinand, req);
                |-> spi_mem_exec_op(spinand->slave, &op);
                    |-> spi_xfer(slave, op_len * 8, op_buf, NULL, flag);