openwrt spi flash 分區適配過程

openwrt spi flash 分區適配過程

這裏基於 openwrt mt7620a 平臺來跟蹤,主要是想理清 dts 裏的分區描述是如何一步步轉化成內核分區行爲。linux

先來看看 dts 中關於分區的描述:數組

palmbus@10000000 {
        spi@b00 {
            status = "okay";

            m25p80@0 {
                #address-cells = <1>;
                #size-cells = <1>;
                compatible = "w25q128";
                reg = <0 0>;
                linux,modalias = "m25p80", "w25q128";
                spi-max-frequency = <10000000>;

                partition@0 {
                    label = "u-boot";
                    reg = <0x0 0x30000>;
                    read-only;
                };

                partition@30000 {
                    label = "u-boot-env";
                    reg = <0x30000 0x10000>;
                    read-only;
                };

                factory: partition@40000 {
                    label = "factory";
                    reg = <0x40000 0x10000>;
                    read-only;
                };

                partition@50000 {
                    label = "firmware";
                    reg = <0x50000 0xfb0000>;
                };
            };
        };

dts 描述的是一個樹狀結構。spi 控制器掛在 platform 總線上,spi flash (w25q128) 掛在 spi 總線上。 探測到 spi flash 的流程以下:函數

  1. plat_of_setup() 遍歷 palmbus 上的設備,併爲每個動態建立 platform_device,添加到系統總線上 device_add()。對於 spi 這裏會建立一個名爲 "ralink,rt2880-spi" 的 platfrom_device 並添加到系統中。
  2. drivers/spi/spi-rt2880.c 中會註冊 spi 的 platform_driver,與上一步的 platfrom_device match 上了以後,觸發調用 rt2880_spi_probe() 。
  3. spi_register_master() 向系統註冊 spi 主控制器,並最後調用 of_register_spi_devices(master) 看看 dts 中在 spi 總線上有哪些設備。
  4. 對 dts 中描述的每個 spi 總線下的設備,爲其建立相應的 spi_device,同時根據 dts 中描述的 reg, spi-cpha, spi-cpol, spi-cs-high, spi-3wire, spi-max-frequency 等屬性來配置該 spi 設備。對於這裏,建立了一個名爲 「m25p80」 的 spi_device。
  5. drivers/mtd/device/m25p80.c 中有名爲 「m25p80" 的 spi_driver,因而 match 上了。觸發執行 m25p_probe()。
  6. m25p_probe() 中讀到了這顆 spi flash 的 id 後,確認了一些基本信息(如頁大小、塊大小), 最後調用 mtd_device_parse_register() 開始真正的分區。

分區解析器

part_parser 用來按照某種規則將分區信息解析出來。這些規則能夠有不少,內核裏調用 register_mtd_parser() 便可註冊一個新的解析器。ui

drivers/mtd/mtdpart.c 中維護了一個鏈表 part_parsers,解析器按註冊順序添加到這個鏈表裏。code

parse_mtd_partitions() 中,若是未指定解析器的話,則默認只容許用 cmdlinepart, ofpart 兩種解析器。對於咱們這裏,實際上起做用的是 ofpart。orm

static struct mtd_part_parser ofpart_parser = {
    .owner = THIS_MODULE,
    .parse_fn = parse_ofpart_partitions,
    .name = "ofpart",
};

parse_ofpart_partitions() 遍歷 dts 中 spi flash 設備下的分區描述信息,取出其中的 reg, label, name, read-only, lock 等信息以填充一個 struct mtd_partition 結構體。上面 dts 裏描述了 4 個分區, 就有一個大小爲 4 的 struct mtd_partition 數組,最後由 add_mtd_partitions() 添加爲各 mtd 分區。cmd

分區的狀況能夠待系統啓動後在 /proc/mtd 文件中查看到。flash

# cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00010000 00010000 "factory"
mtd3: 00fb0000 00010000 "firmware"
mtd4: 00ea9283 00010000 "rootfs"
mtd5: 00b30000 00010000 "rootfs_data"

根文件系統的解析

上面 /proc/mtd 的內容中相比 dts 中的描述多了兩個分區 rootfs, rootfs_data。這兩個分區是什麼時候添加的呢?it

看看添加 mtd 分區的函數:io

int add_mtd_partitions(struct mtd_info *master,
               const struct mtd_partition *parts,
               int nbparts)
{
    struct mtd_part *slave;
    uint64_t cur_offset = 0;
    int i;

    printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);

    for (i = 0; i < nbparts; i++) {
        slave = allocate_partition(master, parts + i, i, cur_offset);
        if (IS_ERR(slave))
            return PTR_ERR(slave);

        mutex_lock(&mtd_partitions_mutex);
        list_add(&slave->list, &mtd_partitions);
        mutex_unlock(&mtd_partitions_mutex);

        add_mtd_device(&slave->mtd);
        mtd_partition_split(master, slave);

        cur_offset = slave->offset + slave->mtd.size;
    }

    return 0;
}

最後調用了 mtd_partition_split()。

static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part)
{
    static int rootfs_found = 0;

    if (rootfs_found)
        return;

    if (!strcmp(part->mtd.name, "rootfs")) {
        rootfs_found = 1;

        if (config_enabled(CONFIG_MTD_ROOTFS_SPLIT))
            split_rootfs_data(master, part);
    }

    if (!strcmp(part->mtd.name, SPLIT_FIRMWARE_NAME) &&
        config_enabled(CONFIG_MTD_SPLIT_FIRMWARE))
        split_firmware(master, part);

    arch_split_mtd_part(master, part->mtd.name, part->offset,
                part->mtd.size);
}

若是:

  1. rootfs 尚未被找到
  2. 當前分區名是 "firmware"
  3. 內核配置時開啓了 CONFIG_MTD_SPLIT_FIRMWARE

則調用 split_firmware() 來解析。在該函數中作了如下幾件事:

  1. 找 type 爲 MTD_PARSER_TYPE_FIRMWARE 的分區解析器來分析。
  2. "uimage-fw" 解析器讀出 firmware 分區的頭部,成功找到一個 uImage。
  3. 躍過 uImage,緊接着成功找到 squashfs 的頭信息,因而找到了格式爲 squashfs 的 rootfs。
  4. 解析器在找到一個分區後,會調用 __mtd_add_partition() 將此分區添加到系統中。
  5. __mtd_add_partition() 最後又調用 mtd_partition_split(),由於此時 rootfs 已經找到,因此會調用 split_rootfs_data() 找 rootfs_data 分區。
  6. rootfs 爲 squashfs 分區,該格式的文件系統只讀,且頭信息裏有標記分區大小。因此很容易就能夠找到 rootfs_data 的起始位置。
相關文章
相關標籤/搜索