3.6.4. SPI NAND 加载

官方版本的 SPL 并不支持从 SPI NAND 启动,ZX 增加了从 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

3.6.4.1. 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()  // 根据boot 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()  // 根据boot 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-zx/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-zx/spl_spi_nand.c
            |  // 读取整个 U-Boot 镜像
            |-> mtd_block_isbad(mtd, off);
            |   // 跳过坏块
            |-> mtd_read(); // drivers/mtd/mtdcore.c

3.6.4.2. 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()  // 根据boot 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()  // 根据boot 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-zx/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-zx/spl_spi_nand.c
        |                   |-> mtd_read();
        |
        |-> spl_parse_image_header(spl_image, header);

3.6.4.3. 读数 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);