linux kernel的设备驱动模型在linux kernel引入统一设备模型之后,bus、driver和device形成了设备模型中的铁三角。在驱动初始化的时候会将代表该driver的一个数据结构挂入bus上的driver链表,device的数据结构挂入bus上的devie链表,那么如何让device遇到“对”的那个driver呢?那么就要靠缘分了,也就是bus的match函数来完成。在传统的方式中,代码中会定义一个static struct platform_device *xxx_devices的静态数组,在初始化的时候调用platform_add_devices。这些静态定义的platform_device往往又需要静态定义各种resource,那么对于设备树,也就是需要根据device_node的树状结构(root是of_allnodes)将一个个的device node挂入到相应的总线device链表中即可。
static int __init customize_machine(void) { /* * customizes platform devices, or adds new ones * On DT based machines, we fall back to populating the * machine from the device tree, if no callback is provided, * otherwise we would always need an init_machine callback. */ if (machine_desc->init_machine) machine_desc->init_machine(); #ifdef CONFIG_OF else of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); #endif return 0; }
新内核中device_node展开为platform_device将device_node展开为platform_device是of_platform_default_populate_init()函数实现的。
arch_initcall_sync(of_platform_default_populate_init);
其具体调用为
static int __init of_platform_default_populate_init(void)
int of_platform_default_populate(struct device_node *root, const struct of_dev_auxdata *lookup,struct device *parent)
int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent)
static int of_platform_bus_create(struct device_node *bus, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent, bool strict)
static struct platform_device *of_platform_device_create_pdata(struct device_node *np, const char *bus_id, void *platform_data, struct device *parent)
of_platform_default_populate_init函数生成platform_device的过程
static int __init of_platform_default_populate_init(void) { struct device_node *node; if (!of_have_populated_dt()) return -ENODEV; /* * Handle certain compatibles explicitly, since we don't want to create * platform_devices for every node in /reserved-memory with a * "compatible", */ // 处理一些保留的节点 for_each_matching_node(node, reserved_mem_matches) of_platform_device_create(node, NULL, NULL); // 处理一些特殊节点下面的子节点 node = of_find_node_by_path("/firmware"); if (node) { of_platform_populate(node, NULL, NULL, NULL); of_node_put(node); } //这个才是我们关心的 /* Populate everything else. */ of_platform_default_populate(NULL, NULL, NULL); return 0; } arch_initcall_sync(of_platform_default_populate_init); #endif
of_platform_default_populate()
int of_platform_default_populate(struct device_node *root, const struct of_dev_auxdata *lookup, struct device *parent) { return of_platform_populate(root, of_default_bus_match_table, lookup, parent); } EXPORT_SYMBOL_GPL(of_platform_default_populate);
传入一个特殊的参数:of_default_bus_match_table
const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", }, { .compatible = "simple-mfd", }, { .compatible = "isa", }, #ifdef CONFIG_ARM_AMBA { .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */ {} /* Empty terminated list */ };
根据我们之前的分析,只要是子节点中的属性含有"simple-bus",“simple-mfd”,“isa”,“arm,amba-bus”,这些子节点就可以转化为platform_device
传递进来的lookup参数为NULL,所以of_dev_lookup这部分也就不去看了
auxdata = of_dev_lookup(lookup, bus); if (auxdata) { bus_id = auxdata->name; platform_data = auxdata->platform_data; }
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
static struct platform_device *of_platform_device_create_pdata( struct device_node *np, const char *bus_id, void *platform_data, struct device *parent) { struct platform_device *dev; if (!of_device_is_available(np) || of_node_test_and_set_flag(np, OF_POPULATED)) return NULL; dev = of_device_alloc(np, bus_id, parent); if (!dev) goto err_clear_flag; dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); if (!dev->dev.dma_mask) dev->dev.dma_mask = &dev->dev.coherent_dma_mask; dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data;