mtdparts中的mtd-id为什么常是spi32766.0?


嵌入式开发一个常见的任务是要调通新的设备,前期有许多工作,比如调通uboot,添加flash驱动支持,配置MTD分区,调通网络等等。其中MTD分区信息多通过cmdline的形式传递给内核,有两种途径,一是通过uboot参数传递,二是内核配置CONFIG_CMDLINE参数。
在cmdline中,MTD分区信息有固定的格式,如下:

mtdparts=<mtddef>[;<mtddef]
<mtddef>  := <mtd-id>:<partdef>[,<partdef>]
<partdef> := <size>[@<offset>][<name>][ro][lk]
<mtd-id>  := unique name used in mapping driver/device (mtd->name)
<size>    := standard linux memsize OR "-" to denote all remaining space
             size is automatically truncated at end of device
             if specified or truncated size is 0 the part is skipped
<offset>  := standard linux memsize
             if omitted the part will immediately follow the previous part
             or 0 if the first part
<name>    := '(' NAME ')'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

举一个例子,mtdparts=spi32766.0:256K(uboot),2M(kernel),6M(rootfs).-(rootfsdata)
这其中mtd-id=spi32766.0,我接触过的多个项目均是采用这一个mtd-id,为什么是这个值?32766是怎么得来的?和flash芯片有关系吗?下面来分析。

1、32766=0x7FFE,起初觉得应该和flash型号有关系,但搜索flash驱动代码,没有发现两者有关联,并且使用不同flash芯片的设备均采用这一值,说明它与flash没有关系。
2、那么就分析代码,首先要知道内核中哪个变量代表了mtd-id,这里我不准备按照代码执行顺序来分析,而是按照我思考问题的方式来分析。既然mtdparts是cmdline参数,那我首先要看cmdline的解析函数。

static int parse_cmdline_partitions(struct mtd_info *master,
                    struct mtd_partition **pparts,
                    struct mtd_part_parser_data *data)
{
    ...
    const char *mtd_id = master->name;//mtd-id是由matser->name定义的
    ...
    for (part = partitions; part; part = part->next) {
        if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
            break;
    } //去匹配cmdline中的mtd-id
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

上面代码可知内核中mtd-id由master->name来定义,那我们就从这个函数起往前跟踪。调用关系如下:

m25p_probe
   -->mtd_device_parse_register
         -->parse_mtd_partitions
               -->parse_cmdline_partitions
  • 1
  • 2
  • 3
  • 4

跟踪到源头m25p_probe,上面函数中的master为下面的flash->mtd

static int m25p_probe(struct spi_device *spi)
{
    ...
    struct m25p *flash;
    struct spi_nor *nor;
    ...
    flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
    ...
    nor->dev = &spi->dev;// spi设备device
    nor->mtd = &flash->mtd; // master源头
    ...
    ret = spi_nor_scan(nor, spi_get_device_id(spi), mode);//这里对nor处理
    ...
    return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
            data ? data->parts : NULL,
            data ? data->nr_parts : 0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

继续跟踪spi_nor_scan

int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
            enum read_mode mode)
{
    ...
    struct device *dev = nor->dev;
    struct mtd_info *mtd = nor->mtd;
    ...
    mtd->name = dev_name(dev);//mtd-id是设备dev的name
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可以得出内核中mtd-id是spi设备名(spi->dev->name)
3、接下来我们去跟踪spi设备名是怎么得来的
查找spi设备驱动(platform_driver),一般是通过设备树dts信息去内核查找,跟踪platform_driver中相应的probe函数。这里的驱动函数就不列举了,不同的spi设备有不同的驱动,一般是由原厂商提供。驱动probe函数中一般的执行流程如下:

spi_alloc_master
   -->spi_register_master
         -->of_register_spi_devices
               -->spi_add_device
                     -->device_add
  • 1
  • 2
  • 3
  • 4
  • 5

1) spi_alloc_master

struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
    struct spi_master   *master;
    ...
    master = kzalloc(size + sizeof(*master), GFP_KERNEL);
    ...
    master->bus_num = -1;//注意这里初始化为-1
    master->num_chipselect = 1;
    master->dev.class = &spi_master_class;
    master->dev.parent = get_device(dev);
    spi_master_set_devdata(master, &master[1]);

    return master;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2)spi_register_master

int spi_register_master(struct spi_master *master)
{
    static atomic_t     dyn_bus_id = ATOMIC_INIT((1<<15) - 1); // 重点,dyn_bus_id初始化为0x7FFF
    struct device       *dev = master->dev.parent;
    ...
    if (master->num_chipselect == 0)
        return -EINVAL;

    if ((master->bus_num < 0) && master->dev.of_node)
        master->bus_num = of_alias_get_id(master->dev.of_node, "spi");

    /* convention:  dynamically assigned bus IDs count down from the max */
    if (master->bus_num < 0) { // 初始化时bus_num=-1
        /* FIXME switch to an IDR based scheme, something like
         * I2C now uses, so we can't run out of "dynamic" IDs
         */
        master->bus_num = atomic_dec_return(&dyn_bus_id); //计算后bus_num=0x7FFE=32766
        dynamic = 1;
    }
    ...
    /* register the device, then userspace will see it.
     * registration fails if the bus ID is in use.
     */
    dev_set_name(&master->dev, "spi%u", master->bus_num);// master->dev->name="spi32766"
    status = device_add(&master->dev);
    if (status < 0)
        goto done;
    dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
            dynamic ? " (dynamic)" : "");

    ...

    /* Register devices from the device tree and ACPI */
    of_register_spi_devices(master);
    acpi_register_spi_devices(master);
done:
    return status;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

现在已经得到spi_master设备名为spi32766
3)of_register_spi_devices

static void of_register_spi_devices(struct spi_master *master)
{
    struct spi_device *spi;
    struct device_node *nc;
    int rc;
    u32 value;

    if (!master->dev.of_node)
        return;

    for_each_available_child_of_node(master->dev.of_node, nc) {
        /* Alloc an spi_device */
        spi = spi_alloc_device(master); // 这里会创建我们需要的spi设备
        if (!spi) {
            dev_err(&master->dev, "spi_device alloc error for %s\n",
                nc->full_name);
            spi_dev_put(spi);
            continue;
        }

        /* Select device driver */
        if (of_modalias_node(nc, spi->modalias,
                     sizeof(spi->modalias)) < 0) {
            dev_err(&master->dev, "cannot find modalias for %s\n",
                nc->full_name);
            spi_dev_put(spi);
            continue;
        }

        /* Device address */
        // 获取设备树里spi节点下的reg参数,赋值给spi->chip_select,代表片选ID,即第几片flash,0代表第一片flash
        rc = of_property_read_u32(nc, "reg", &value);
        if (rc) {
            dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
                nc->full_name, rc);
            spi_dev_put(spi);
            continue;
        }
        spi->chip_select = value;//一般为0,第一片flash
        ...
    }
    ...
    rc = spi_add_device(spi);//将spi设备(spi->dev)添加入linux device hierarchy,也可以说是devices bus中
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

4)spi_add_device

int spi_add_device(struct spi_device *spi)
{
    ...
    spi_dev_set_name(spi);//确定spi->dev->name
    ...
    status = device_add(&spi->dev);//这是linux设备模型中一个很关键的函数,这里不详细讲,函数除了添加device到bus外,还会probe drivers for this device and attach device to driver.
    ...
}
static void spi_dev_set_name(struct spi_device *spi)
{
    struct acpi_device *adev = ACPI_COMPANION(&spi->dev);

    if (adev) {
        dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
        return;
    }

    //最终确定spi设备名为"spi32766.0",dev_name(&spi->master->dev)为"spi32766", spi->chip_select为0
    dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
             spi->chip_select);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

至此,确定spi设备名spi32766.0,按代码流程分析,已经清楚为什么mtd-id是spi32766.0了。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值