Linux核心Device Tree-建立platform device
在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] [<ffffff80080888d8>] dump_backtrace+0x0/0x220 [ 0.311232] [<ffffff8008088b1c>] show_stack+0x24/0x30 [ 0.311244] [<ffffff800856ebec>] dump_stack+0x98/0xc0 [ 0.311258] [<ffffff80089a1000>] of_platform_populate+0x30/0xb8 [ 0.311268] [<ffffff8009113b68>] arm64_device_init+0x30/0x4c [ 0.311278] [<ffffff80080831cc>] do_one_initcall+0x18c/0x194 [ 0.311290] [<ffffff8009110e10>] kernel_init_freeable+0x228/0x22c [ 0.311301] [<ffffff8008c75080>] kernel_init+0x18/0x100 [ 0.311311] [<ffffff8008082ef0>] 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 subsyste
- 使用Ubuntu製作ext4檔案系統
- Linux核心__setup()巨集介紹
- Linux核心notifier機制
- Linux Kernel Makefiles編譯標誌
- Linux Kernel Makefiles賦值
- 基於Ubuntu 18.04 安裝perf工具
- Linux核心睡眠喚醒流程
- GDB常用除錯命令(一)
- Linux核心睡眠喚醒狀態
- Linux核心睡眠喚醒除錯
- Linux核心編譯失敗
- Linux DRM那些事-HDMI介面DTS配置
- Linux DRM那些事-HDMI介面EDID獲取
- 顯示技術之HDMI介面介紹
- Linux核心中斷之中斷呼叫流程
- Linux核心中斷之中斷初始化
- Linux核心中斷之中斷申請介面
- Linux核心中斷之獲取中斷號
- Linux核心程式入口地址
- GNU Binutils之objcopy命令