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);