在Linux
内核启动时,内核通过of_platform_populate()
函数,将dts
中的device node
创建成platform device
。为后续和各类驱动的platform driver
匹配做准备。
of_platform_populate()
函数在文件drivers/of/platform.c
中实现。下面基于RockPI 4A单板的内核代码介绍其调用流程和实现过程。
一、函数调用流程
在Linux
内核中,可以使用dump_stack()
函数查看函数的调用流程。
/**
* of_platform_populate() - Populate platform_devices from device tree data
... #省略部分注释
*/
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
int rc = 0;
dump_stack(); ### 打印函数调用的堆栈信息
//1.如果root为NULL,则通过of_find_node_by_path()查找
root = root ? of_node_get(root) : of_find_node_by_path("/");
if (!root)
return -EINVAL;
//2.遍历dts中的节点
for_each_child_of_node(root, child) {
//3.为每个节点和子节点创建platform device
rc = of_platform_bus_create(child, matches, lookup, parent, true);
...
}
...
}
EXPORT_SYMBOL_GPL(of_platform_populate);
dump_stack()
堆栈信息如下:
[ 0.311191] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.4.154-00036-gcef30e88a9f5-dirty #36
[ 0.311198] Hardware name: ROCK PI 4A 2 (DT)
[ 0.311206] Call trace:
[ 0.311220] [] dump_backtrace+0x0/0x220
[ 0.311232] [] show_stack+0x24/0x30
[ 0.311244] [] dump_stack+0x98/0xc0
[ 0.311258] [] of_platform_populate+0x30/0xb8
[ 0.311268] [] arm64_device_init+0x30/0x4c
[ 0.311278] [] do_one_initcall+0x18c/0x194
[ 0.311290] [] kernel_init_freeable+0x228/0x22c
[ 0.311301] [] kernel_init+0x18/0x100
[ 0.311311] [] ret_from_fork+0x10/0x20
从堆栈信息中,可以看出:在arm64_device_init()
函数中实现了of_platform_populate()
函数的调用。后续介绍kernel_init()
函数,暂时先留个念想。
注:
arm64_device_init()
函数在arch/arm64/kernel/setup.c
文件中实现。此时,串口驱动尚未加载,串口日志保存在缓冲区中。由于RK3399
是多核,在Linux
内核启动时,堆栈信息或其它日志有可能会丢失。在系统启动时,可以增加nosmp
配置,关闭其他CPU
的加载,保证尽可能多的日志输出。在配置文件/boot/extlinux/extlinux.conf
最后增加:
label kernel-debug
kernel /debug/Image
fdt /debug/rk3399-rock-pi-4a.dtb
append earlyprintk console=ttyFIQ0,1500000n8 init=/sbin/init root=PARTUUID=b921b045-1d rw rootwait rootfstype=ext4 nosmp
二、函数实现过程
of_platform_populate()
函数主要通过of_platform_bus_create()
函数创建platform device
。为了理解其实现过程,通过printk
增加了部分调试日志,代码如下:
/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
* @matches: match table for bus nodes
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent for new device, or NULL for top level.
* @strict: require compatible property
*
* Creates a platform_device for the provided device_node, and optionally
* recursively create devices for all the child nodes.
*/
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)
{
...
printk(KERN_ERR"--- name %s \n",bus->name);
//1.判断是否有compatible属性,没有则返回
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, "compatible", NULL))) {
printk(KERN_ERR"--- %s() - skipping %s, no compatible prop\n",
__func__, bus->full_name);
return 0;
}
...
//2.创建platform device
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus)) {
printk(KERN_ERR"--- no match node\n");
return 0;
}
//3.遍历子节点。如果存在,则创建platform device
for_each_child_of_node(bus, child) {
printk(KERN_ERR"--- 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;
}
}
of_node_set_flag(bus, OF_POPULATED_BUS);
return rc;
}
更新内核映像后,截取了部分内核启动日志,如下:
[ 0.326151] --- name syscon
[ 0.326311] --- create child: /syscon@ff770000/io-domains
[ 0.326318] --- name io-domains
[ 0.326458] --- no match node
[ 0.326466] --- create child: /syscon@ff770000/usb2-phy@e450
[ 0.326472] --- name usb2-phy
[ 0.326627] --- no match node
[ 0.326635] --- create child: /syscon@ff770000/usb2-phy@e460
[ 0.326641] --- name usb2-phy
[ 0.326791] --- no match node
[ 0.326798] --- create child: /syscon@ff770000/phy@f780
[ 0.326804] --- name phy
[ 0.326958] --- no match node
[ 0.326965] --- create child: /syscon@ff770000/mipi-dphy-rx0
[ 0.326972] --- name mipi-dphy-rx0
[ 0.327113] --- no match node
[ 0.327120] --- create child: /syscon@ff770000/pvtm
[ 0.327126] --- name pvtm
[ 0.327291] --- no match node
... ## 省略部分log
[ 0.330604] --- name display-subsystem ## drm
[ 0.330742] --- no match node
...
上述日志中的节点名称bus->name
和子节点名称child->full_name
可在arch/arm64/boot/dts/rockchip/rk3399.dtsi
文件中查到:
grf: syscon@ff770000 { ## syscon对应节点名
compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
reg = <0x0 0xff770000 0x0 0x10000>;
#address-cells = <1>;
#size-cells = <1>;
io_domains: io-domains {
compatible = "rockchip,rk3399-io-voltage-domain";
status = "disabled";
};
u2phy0: usb2-phy@e450 { ## usb2-phy@e450对应子节点名
compatible = "rockchip,rk3399-usb2phy";
reg = <0xe450 0x10>;
clocks = <&cru SCLK_USB2PHY0_REF>;
clock-names = "phyclk";
#clock-cells = <0>;
clock-output-names = "clk_usbphy0_480m";
status = "disabled";
...
}
}
...
display_subsystem: display-subsystem { ## display-subsystem 对应节点名
compatible = "rockchip,display-subsystem";
ports = <&vopl_out>, <&vopb_out>;
clocks = <&cru PLL_VPLL>, <&cru PLL_CPLL>;
clock-names = "hdmi-tmds-pll", "default-vop-pll";
devfreq = <&dmc>;
status = "disabled";
};
在系统启动后,可以在/sys/firmware/devicetree/base
路径下查看dts
文件节点,在/sys/devices/platform
路径下查看platform device
。
root@linaro-alip:/sys/firmware/devicetree/base# ls syscon@ff770000/
#address-cells compatible mipi-dphy-rx0 phandle pvtm usb2-phy@e450
#size-cells io-domains name phy@f780 reg usb2-phy@e460
root@linaro-alip:/sys/firmware/devicetree/base# ls display-subsystem/
clock-names compatible logo-memory-region phandle route
clocks devfreq name ports status
root@linaro-alip:/sys/firmware/devicetree/base#
root@linaro-alip:/sys/devices/platform# ls ff770000.syscon/
driver_override ff770000.syscon:usb2-phy@e460/
ff770000.syscon:io-domains/ modalias
ff770000.syscon:mipi-dphy-rx0/ of_node/
ff770000.syscon:phy@f780/ power/
ff770000.syscon:pvtm/ subsystem/
ff770000.syscon:usb2-phy@e450/ uevent
root@linaro-alip:/sys/devices/platform# ls display-subsystem/
driver drm modalias power uevent
driver_override graphics of_node subsystem