本文以smsc911x驱动为例
platform_device注册过程
该设备被定义在dts里面了
参考文章设备树节点转换为设备节点device_node、和平台设备资源platform_device_设备树节点转换成平台设备-CSDN博客
dts里面的节点会被转换为device_node和platform_device(并不是所有节点都会被转换为platform_device)
ethernet@3,02000000 {compatible = "smsc,lan9118", "smsc,lan9115";reg = <3 0x02000000 0x10000>;interrupts = <15>;phy-mode = "mii";reg-io-width = <4>;smsc,irq-active-high;smsc,irq-push-pull;vdd33a-supply = <&v2m_fixed_3v3>;vddvario-supply = <&v2m_fixed_3v3>;};
内核初始化时,回去解析dts文件,然后去注册各个设备,大致的流程如下图
(我也忘了哪里听到了这个说法,不知道对不对。platform总线,设备和驱动。设备往platform总线上注册,注册的时候回去probe,匹配驱动。同样注册驱动的时候,也会去匹配设备)
下图是之间调用platform_device_register注册设备的过程
在下列函数中加入了打印,看看究竟有哪些device_node
int of_platform_populate(struct device_node *root,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent)
{
......................printk("\r\n %s,%d, device_node %s, parnet %s\n", __FUNCTION__, root->name, parent ? parent->init_name : "null");for_each_child_of_node(root, child) {printk("\r\n%s,%d child %s\n", __FUNCTION__, __LINE__, child->name);rc = of_platform_bus_create(child, matches, lookup, parent, true);if (rc)break;}
....................
}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)
{
.........................for_each_child_of_node(bus, child) {printk(" create child: %s\n", child->full_name);rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
................}return rc;
}
打印结构如下图。比较奇怪的是virtio_mmio dts里面没有,不知道是从哪里出来的
dts文件名vexpress-v2p-ca9.dts和vexpress-v2m.dtsi
最后根据dts或者是打印信息可以看到device_node的关系如下图。
跟节点上挂了chosen...smb(simple bus),然后vexpress-v2m.dtsi里面的所有节点有全部挂到了smb下面。
这些节点是为什么会挂到smb下面呢?怀疑是smb节点包含了该头文件导致
/{
chosen { };
..........................
smb {
compatible = "simple-bus";..................................................
/include/ "vexpress-v2m.dtsi"
};
};
b. 并非所有的device_node都会转换为platform_device只有以下的device_node会转换:
- 该节点必须含有compatible属性
- 根节点的子节点(节点必须含有compatible属性)
- 含有特殊compatible属性的节点的子节点(子节点必须含有compatible属性)这些特殊的compatilbe属性为: “simple-bus”,“simple- mfd”,“isa”,"arm,amba-bus "
- 根节点是例外的,生成platfrom_device时,即使有compatible属性也不会处理
注意:i2c, spi等总线节点会转换为platform_device,但是,spi、i2c下的子节点无论compatilbe是否为: “simple-bus”,“simple- mfd”,“isa”,"arm,amba-bus "都应该交给对应的总线驱动程序来处理而不会被转换为platform_device
device_node并不等同于platform_device,感觉转换规则就是这个函数里面确定的
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)
{const struct of_dev_auxdata *auxdata;struct device_node *child;struct platform_device *dev;const char *bus_id = NULL;void *platform_data = NULL;int rc = 0;/* Make sure it has a compatible property */if (strict && (!of_get_property(bus, "compatible", NULL))) {pr_debug("%s() - skipping %s, no compatible prop\n",__func__, bus->full_name);return 0;}auxdata = of_dev_lookup(lookup, bus);if (auxdata) {bus_id = auxdata->name;platform_data = auxdata->platform_data;}if (of_device_is_compatible(bus, "arm,primecell")) {/** Don't return an error here to keep compatibility with older* device tree files.*/of_amba_device_create(bus, bus_id, platform_data, parent);return 0;}/* 怀疑上面的代码就是引用里面说的转换规则 */dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);if (!dev || !of_match_node(matches, bus))return 0;if (g_v2m == 1)printk("platform_device: %s, device node: %s\n", dev->name, bus->full_name);for_each_child_of_node(bus, child) {//if (g_v2m == 1)//printk(" create child: %s\n", child->full_name);rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);if (rc) {of_node_put(child);break;}}return rc;
}
可以看到这么多device_node最后转换为platform_device的节点只有这三个 。compatible属性也满足条件
platform_device: smb, device node: /smb
platform_device: smb:motherboard, device node: /smb/motherboard
platform_device: smb:motherboard:iofpga@7,00000000, device node: /smb/motherboard/iofpga@7,00000000motherboard {
model = "V2M-P1";
arm,hbi = <0x190>;
arm,vexpress,site = <0>;
compatible = "arm,vexpress,v2m-p1", "simple-bus";
#address-cells = <2>; /* SMB chipselect number and offset */
#size-cells = <1>;
#interrupt-cells = <1>;
ranges;
那其他的节点如果不转换为platform_device,那是如何与platform_driver匹配的呢?比如Ethernet节点
更正一下上面的说法,后面经过加打印,确认Ethernet节点其实是被转换为了platform_device了的
b. 并非所有的device_node都会转换为platform_device只有以下的device_node会转换:
那这句话还是对的吗?后面研究研究
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)
{
........................if (g_v2m == 1)printk("%s,%d device node: %s\n", __FUNCTION__, __LINE__, bus->full_name);
........................dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);if (!dev || !of_match_node(matches, bus)){if (g_v2m == 1)printk("fail create platform_device ??, dev %p, device node: %s\n",dev, bus->full_name);return 0;}if (g_v2m == 1)printk("%s,%d platform_device: %s, device node: %s\n", __FUNCTION__, __LINE__, dev->name, bus->full_name);for_each_child_of_node(bus, child) {if (g_v2m == 1)printk(" create child: %s\n", child->full_name);rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);if (rc) {of_node_put(child);break;}}return rc;
}
打印信息如下。暂时不知道of_match_node没有满足是什么意思。但是看样子device是创建成功了的。
platform driver注册
关于platform_driver是在驱动代码里面通过module init,在初始化阶段通过do init call调用注册函数
大致流程如下
1 设备向内核注册platform_device(platform_device_register、或者是通过dts),把设备挂在虚拟的platform bus下
2 驱动注册的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev()。对每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device()->drv->bus->match()==platform_match(),有几种匹配规则,如果相符就调用platform_drv_probe()->driver->probe(),如果probe(本例中则是smsc911x_drv_probe)成功则绑定该设备到该驱动
本例是通过驱动代码里面的of_match_table进行匹配。名字要和dts里面的相同
感觉1和2没有先后顺序,看代码不论是设备注册或者是驱动注册的时候都会去调用driver probe device尝试发现对方
dts中尝试新增设备
1 dts中新增节点
2、重新编译dtb文件。这样加载dts的时候就会注册该设备
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabi-
make dtbs
3、添加驱动代码,注册platform_driver
static int my_drv_probe(struct platform_device *pdev)
{printk("%s,%d platform_device name: %s, device_node full name %s\n", \__FUNCTION__, __LINE__, pdev->name, pdev->dev.of_node->full_name);return 0;
}
static int my_drv_remove(struct platform_device *pdev)
{printk("%s,%d platform_device name: %s, device_node full name %s\n", \__FUNCTION__, __LINE__, pdev->name, pdev->dev.of_node->full_name);return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id new_node_test[] = {{ .compatible = "arm,newnodetest", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, new_node_test);
#endif#define MY_DRV_NAME "newnodetest"
static struct platform_driver my_driver = {.probe = my_drv_probe,.remove = my_drv_remove,.driver = {.name = MY_DRV_NAME,.owner = THIS_MODULE,.pm = NULL,.of_match_table = of_match_ptr(new_node_test),},
};/* Entry point for loading the module */
static int __init my_init_module(void)
{dump_stack();SMSC_INITIALIZE();return platform_driver_register(&my_driver);
}/* entry point for unloading the module */
static void __exit my_cleanup_module(void)
{platform_driver_unregister(&my_driver);
}module_init(my_init_module);
module_exit(my_cleanup_module);
运行信息打印
解析device node能看到有新增的节点
通过module init注册的初始化函数,也调用了,从而注册了platform_drive,最后也走到了我们写的probe函数
dts语法规则不清楚,后面看一下dts语法