mmcblk节点变化原因分析

driver_attach

__driver_attach

async_schedule_dev(__driver_attach_async_helper, dev);

platform_add_devices

device_register / platform_device_register -> platform_device_add

device_add

bus_add_device

bus->p->klist_devices

platform_device_register

./drivers/base/node.c

./drivers/base/platform.c

./drivers/base/soc.c

./drivers/base/core.c

platform_match

of_driver_match_device

of_match_device(drv->of_match_table, dev)

一、linux驱动probe调用的整体流程

1、device的创建

(1)代码流程

start_kernel // init/main.c

setup_arch(&command_line);

setup_machine_fdt(__fdt_pointer);

early_init_dt_scan(dt_virt))

early_init_dt_verify(params)

initial_boot_params = params;

unflatten_device_tree();

__unflatten_device_tree(initial_boot_params, NULL, &of_root,early_init_dt_alloc_memory_arch, false);

unflatten_dt_nodes(blob, mem, dad, mynodes);

struct device_node *root;

for (offset = 0;offset >= 0 && depth >= initial_depth;offset = fdt_next_node(blob, offset, &depth))

populate_node(blob, offset, &mem, nps[depth],&nps[depth+1], dryrun))

pathp = fdt_get_name(blob, offset, &l);

np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node));

populate_properties(blob, offset, mem, np, pathp, dryrun);

np->name = of_get_property(np, "name", NULL);

root = nps[depth+1];

arch_initcall_sync(of_platform_default_populate_init); // drivers/of/platform.c

of_platform_default_populate_init

of_platform_default_populate(NULL, NULL, NULL);

of_platform_populate(root, of_default_bus_match_table, lookup,parent);

root = root ? of_node_get(root) : of_find_node_by_path("/");

of_find_node_by_path

of_find_node_opts_by_path

if (strcmp(path, "/") == 0)

return of_node_get(of_root);

for_each_child_of_node(root, child)

rc = of_platform_bus_create(child, matches, lookup, parent, true);

dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);

struct platform_device *dev;

dev = of_device_alloc(np, bus_id, parent);

of_device_add

device_add

bus_add_device

klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

(2)流程描述

从上面1的代码流程可以看出,在初始化时会解析__fdt_pointer对应地址的dtb,把整个dtb进行扫描,获取对应的数据结构并存放在of_root中

当系统调用arch_initcall_sync时,会执行of_platform_default_populate_init,在这个里面会根据of_root中的数据依次创建platform_device,并且把数据添加到klist_devices中

2、driver的创建

(1)代码流程

start_kernel // init/main.c

arch_call_rest_init

rest_init

pid = kernel_thread(kernel_init, NULL, CLONE_FS)

kernel_init_freeable

do_basic_setup

do_initcalls

for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)

do_initcall_level

parse_args(initcall_level_names[level],command_line, __start___param,__stop___param - __start___param,level, level,NULL, ignore_unknown_bootoption);

for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)

do_one_initcall(initcall_from_entry(fn));

fn(); 【__initcall_sdhci_edge_driver_init6】

module_platform_driver(sdhci_edge_driver);

#define module_platform_driver(__platform_driver) \

module_driver(__platform_driver, platform_driver_register, \

platform_driver_unregister)

#define module_driver(__driver, __register, __unregister, ...) \

static int __init __driver##_init(void) \

{ \

return __register(&(__driver) , ##__VA_ARGS__); \

} \

module_init(__driver##_init); \

static void __exit __driver##_exit(void) \

{ \

__unregister(&(__driver) , ##__VA_ARGS__); \

} \

module_exit(__driver##_exit);

platform_driver_register(sdhci_edge_driver)

__platform_driver_register(drv, THIS_MODULE)

drv->driver.bus = &platform_bus_type;

.match = platform_match,

drv->driver.probe = platform_drv_probe;

driver_register(&drv->driver);

bus_add_driver(drv)

klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

if (drv->bus->p->drivers_autoprobe)

driver_attach(drv)

bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

klist_iter_init_node(&bus->p->klist_devices, &i,(start ? &start->p->knode_bus : NULL));

while (!error && (dev = next_device(&i)))

error = fn(dev, data); 【__driver_attach,data就是drv】

platform_match

if (of_driver_match_device(dev, drv)) return 1

of_match_device(drv->of_match_table, dev) != NULL;

of_match_node(matches, dev->of_node);

match = __of_match_node(matches, node);

for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {

__of_device_is_compatible(node, matches->compatible,matches->type, matches->name);

prop = __of_find_property(device, "compatible", NULL);

if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL;

strcmp(pdev->name, id->name) == 0

return (strcmp(pdev->name, drv->name) == 0);

__driver_attach // drivers/base/dd.c

driver_match_device(drv, dev);

return drv->bus->match ? drv->bus->match(dev, drv) : 1; 【platform_match】

if (driver_allows_async_probing(drv))

async_schedule_dev(__driver_attach_async_helper, dev); 【通过创建工作队列实现异步调用】

device_driver_attach(drv, dev);

driver_probe_device

really_probe(dev, drv);

if (dev->bus->probe)

dev->bus->probe(dev);

else if (drv->probe)

drv->probe(dev); 【platform_drv_probe】

async_schedule_dev

async_schedule_node(func, dev, dev_to_node(dev));

async_schedule_node_domain(func, data, node, &async_dfl_domain);

INIT_WORK(&entry->work, async_run_entry_fn);

queue_work_node(node, system_unbound_wq, &entry->work);

async_run_entry_fn

entry->func(entry->data, entry->cookie); 【__driver_attach_async_helper】

__driver_attach_async_helper // drivers/base/dd.c

driver_probe_device

really_probe(dev, drv);

if (dev->bus->probe)

dev->bus->probe(dev);

else if (drv->probe)

drv->probe(dev); 【platform_drv_probe】

platform_drv_probe // drivers/base/platform.c

if (drv->probe)

drv->probe(dev); 【sdhci_edge_probe】

(2)System.map

6 ffff800010e9e8b0 d __initcall_gpio_ir_recv_driver_init6

5 ffff800010e9e8b4 d __initcall_dw_wdt_driver_init6

4 ffff800010e9e8b8 d __initcall_dt_cpufreq_platdrv_init6

3 ffff800010e9e8bc d __initcall_psci_idle_init6

2 ffff800010e9e8c0 d __initcall_mmc_pwrseq_simple_driver_init6

1 ffff800010e9e8c4 d __initcall_mmc_pwrseq_emmc_driver_init6

78355 ffff800010e9e8c8 d __initcall_mmc_blk_init6

1 ffff800010e9e8cc d __initcall_sdhci_drv_init6

2 ffff800010e9e8d0 d __initcall_sdhci_pltfm_drv_init6

3 ffff800010e9e8d4 d __initcall_sdhci_edge_driver_init6

(3)流程描述

在linux启动是会依次调用__initcall相关等级的数据,对应的就是驱动的module_init中对应的函数。在同一个等级的module_init调用顺序是受链接脚本的影响,可以参考System.map中的顺序。

当调用module_init中对应的函数时,一般会调用platform_driver_register,该接口会把对应的driver结构体挂载到klist_drivers链表中,然后继续扫描klist_devices链表,调用__driver_attach进行依次匹配,找到drv对应的device。通过platform_match进行匹配对比,也就是dtb中的compatible和驱动中的of_match_table进行比较。

如果匹配成功,且支持异步调用,则可以通过创建工作队列的方式执行async_schedule_dev进行调用触发probe函数

二、同一个等级之间的加载顺序

通过上面的”driver创建描述“,可以看出同一个等级的module_init调用顺序在System.map中可以看出来,即受链接脚本影响

三、多个相同ip之间的加载顺序

通过上面的”device创建描述“,emmc和sd卡的调用顺序,是受dtb中的顺序影响的,如果dtb中emmc靠前,则优先加载emmc。可以通过dtc继续出dts进行查看先后顺序

四、为什么mmcblkx会变化

通过上面的”driver创建描述“,在__driver_attach 的时候会判断driver_allows_async_probing,该函数是判断probe_type的类型,目前mmc的配置是PROBE_PREFER_ASYNCHRONOUS,所以支持异步调用。如果屏蔽掉的话可以解决。

五、mmcblkx的生成原理

在mmc驱动的probe函数中会调用:

host = sdhci_pltfm_init(pdev, data, sizeof(*priv));

host = sdhci_alloc_host(&pdev->dev,sizeof(struct sdhci_pltfm_host) + priv_size);

mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);

alias_id = of_alias_get_id(dev->of_node, "mmc");

if (alias_id >= 0) {

min_idx = alias_id;

max_idx = alias_id + 1;

} else {

min_idx = mmc_first_nonreserved_index();

max_idx = 0;

}

err = ida_simple_get(&mmc_host_ida, min_idx, max_idx, GFP_KERNEL);

ida_alloc_range(ida, start, (end) - 1, gfp)

host->index = err;

dev_set_name(&host->class_dev, "mmc%d", host->index);

sdhci_add_host(host)

sdhci_setup_host(host);

__sdhci_add_host(host);

从上面的代码可以看出,host->index就是对应的mmcblkx的编号,如果有别名,则直接使用别名,如果没有别名,则min_idx =0,max_idx =0,由于结果是end-1,也就是从0~(0-1)之间的值进行获取。

六、解决方法

1、通过别名方式绑定节点名

aliases {

mmc0 = &emmc;

mmc1 = &sd;

mmc2 = &sdio;

}

2、不使用异步方式

static struct platform_driver sdhci_edge_driver = {

.driver = {

.name = "sdhci-edge",

//.probe_type = PROBE_PREFER_ASYNCHRONOUS,

.pm = &sdhci_edge_pm_ops,

.of_match_table = sdhci_edge_match,

},

.probe = sdhci_edge_probe,

.remove = sdhci_pltfm_unregister,

}

posted @ 2023-02-08 11:04  drm2017  阅读(208)  评论(0)    收藏  举报