基於tiny4412的Linux內核移植 -- 設備樹的展開【轉】

轉自:https://www.cnblogs.com/pengdonglin137/p/5248114.html#_lab2_3_1html

做者信息

做者: 彭東林linux

郵箱:pengdonglin137@163.comios

QQ:405728433git

平臺簡介

開發板:tiny4412ADK + S700 + 4GB Flash數組

要移植的內核版本:Linux-4.4.0 (支持device tree)dom

u-boot版本:友善之臂自帶的 U-Boot 2010.12 (爲支持uImage啓動,作了少量改動)編輯器

busybox版本:busybox 1.25ide

交叉編譯工具鏈: arm-none-linux-gnueabi-gcc函數

      (gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29))

摘要

    在Linux引入設備樹以後,將原來寫在代碼中的大量的硬件信息所有移到了設備樹中,而後在Linux啓動的時候會解析設備樹,利用解析到的硬件信息構造device,而後註冊到相應的bus上,若是有對應的driver,則會調用driver的probe函數。那麼這個過程是怎麼進行的?Linux系統有各類device,如對於platform子系統來講有platform_device、對於I2C子系統有i2c_client、對於SPI子系統來講有spi_device,那麼這些device是怎麼跟設備樹關聯起來的呢?

   在分析的過程當中參考了下面的幾篇博文:

    http://www.wowotech.net/device_model/dt-code-analysis.html

       http://www.wowotech.net/comm/i2c_overview.html

    http://www.wowotech.net/comm/i2c_provider.html

這幾篇博文講的很是好,下面一篇是以前總結的:

         http://www.cnblogs.com/pengdonglin137/p/4495056.html

內核文檔:

     Documentation/devicetree/booting-without-of.txt

     Documentation/devicetree/usage-model.txt

官方文檔:

     Power_ePAPR_APPROVED_v1.1.pdf

正文

  設備樹的populate過程大體有以下幾個階段(下文中「節點」與「device node」能夠理解爲一個意思):

1、根據設備樹建立device node鏈表

start_kernel

    ---> setup_arch

            ---> unflatten_device_tree

在u-boot引導內核的時候,會將設備樹在物理內存中的物理起始地址(存放在寄存器r2中)傳遞給Linux內核,而後Linux內核在函數unflatten_device_tree中會解析設備樹鏡像,並利用掃描到的信息建立由device node構成的鏈表,全局變量of_root指向鏈表的根節點,設備樹的每一個節點都會有一個struct device_node與之對應。

2、遍歷device node鏈表,建立並註冊platform_device

start_kernel

    ---> rest_init

            ---> kernel_init

                    ---> kernel_init_freeable

                            ---> do_basic_setup

                                    ---> do_initcalls

在do_initcalls函數中,kernel會依次執行各個initcall函數,在這個過程當中,會調用 customize_machine,具體以下:

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.
*/
of_iommu_init();
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;
}
arch_initcall(customize_machine);

這樣就可調用到exynos_dt_machine_init:

static void __init exynos_dt_machine_init(void)
{
......
 
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}

    在of_platform_populate中會調用of_platform_bus_create ---> of_platform_device_create_pdata,完成platform_device的建立和註冊。那麼Linux系統是怎麼知道哪些device node要註冊爲platform_device,哪些是用於i2c_client,哪些是用於spi_device?不知道你有沒有注意到調用of_platform_populate的時候給它傳遞了一個參數of_default_bus_match_table,它的定義以下:

const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
{ .compatible = "simple-mfd", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};

    是這個意思:若是某個device node的compatible屬性的值與數組of_default_bus_match_table中的任意一個元素的compatible的值match(可是對於compatible屬性的值是arm,primecell的節點有些特殊,它是單獨處理的),那麼這個device node的child device node(device_node的child成員變量指向的是這個device node的子節點,也是一個鏈表)仍舊會被註冊爲platform_device。

of_platform_populate:

 1: int of_platform_populate(struct device_node *root,
 2: const struct of_device_id *matches,
 3: const struct of_dev_auxdata *lookup,
 4: struct device *parent)
 5: {
 6: struct device_node *child;
 7: int rc = 0;
 8:  
 9: root = root ? of_node_get(root) : of_find_node_by_path("/"); // 找到root device node
 10: if (!root)
 11: return -EINVAL;
 12:  
 13: for_each_child_of_node(root, child) { // 遍歷root device node的child device node
 14: rc = of_platform_bus_create(child, matches, lookup, parent, true);
 15: if (rc) {
 16: of_node_put(child);
 17: break;
 18: }
 19: }
 20: of_node_set_flag(root, OF_POPULATED_BUS);
 21:  
 22: of_node_put(root);
 23: return rc;
 24: }

of_platform_bus_create :

 1: static int of_platform_bus_create(struct device_node *bus,
 2: const struct of_device_id *matches,
 3: const struct of_dev_auxdata *lookup,
 4: struct device *parent, bool strict)
 5: {
 6: const struct of_dev_auxdata *auxdata;
 7: struct device_node *child;
 8: struct platform_device *dev;
 9: const char *bus_id = NULL;
 10: void *platform_data = NULL;
 11: int rc = 0;
 12:  
 13: /* Make sure it has a compatible property */
 14: if (strict && (!of_get_property(bus, "compatible", NULL))) { // 這樣能夠把chosen、aliases、memory等沒有compatible屬性的節點排除在外
 15: pr_debug("%s() - skipping %s, no compatible prop\n",
 16: __func__, bus->full_name);
 17: return 0;
 18: }
 19:  
 20: auxdata = of_dev_lookup(lookup, bus); // tiny4412給lookup傳遞的是NULL
 21: if (auxdata) {
 22: bus_id = auxdata->name;
 23: platform_data = auxdata->platform_data;
 24: }
 25:  
 26: if (of_device_is_compatible(bus, "arm,primecell")) {
 27: /*
 28: * Don't return an error here to keep compatibility with older
 29: * device tree files.
 30: */
 31: of_amba_device_create(bus, bus_id, platform_data, parent);
 32: return 0;
 33: }
 34:  
 35: dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); // 根據device node建立 platform_device並註冊
 36: if (!dev || !of_match_node(matches, bus)) // 判斷是否是須要繼續遍歷這個device node下的child device node
 37: return 0;
 38:  
 39: for_each_child_of_node(bus, child) { // 遍歷這個device node下的child device node,將child device node也註冊爲platform_device
 40: pr_debug(" create child: %s\n", child->full_name);
 41: rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
 42: if (rc) {
 43: of_node_put(child);
 44: break;
 45: }
 46: }
 47: of_node_set_flag(bus, OF_POPULATED_BUS);
 48: return rc;
 49: }

3、註冊其餘設備

I2C設備的註冊

下面說一下i2c_client是如何註冊的。先看下面一張圖(來自蝸窩科技):

e37c4da1dcb2d9b5a190002b60b3923620160214140115

下面是從http://www.wowotech.net/comm/i2c_overview.html摘抄的一段話:

1)platform bus(/sys/bus/platform)是驅動工程師常見的bus,用於掛載和CPU經過系統總線鏈接的各種外設。在I2C framework中,I2C控制器直接從屬於platform bus,咱們在linux kernel中常說的I2C driver,都是指I2C controller driver,都是以platform driver的形式存在,固然,對應的控制器是platform device。

2)與此同時,kernel抽象出I2C bus(/sys/bus/i2c),用於掛載和I2C controller經過I2C總線鏈接的各個I2C slave device。

3)比較特殊的地方是,I2C core使用一個虛擬實體----I2C adapter,抽象I2C controller有關的功能(主要是數據的收發),I2C adapter也掛載在I2C bus上。

4)I2C adapter和I2C slave device都掛載在I2C bus上,就能夠方便的進行Master(I2C adapter)和Slave之間的匹配操做,並經過I2C core提供的統一接口,訪問I2C salve device,進行數據的收發。

    咱們知道,i2c控制器在i2c驅動模型中被抽象爲i2c_adapter,可是i2c控制器驅動其實是在platform_bus上,因此i2c控制器對應的是platform_device,所以會在上面調用of_platform_populate時註冊,而後i2c控制器驅動的probe函數會被調用。以tiny4412開發板爲例,在drivers/i2c/busses/i2c-s3c2410.c的probe函數中調用註冊adapter的函數接口:i2c_add_numbered_adapter ---> i2c_add_adapter ---> i2c_register_adapter ---> of_i2c_register_devices,在函數of_i2c_register_devices中會遍歷這個adapter對應的device node的child device node,這些child device node對應的就是掛載i2c bus上的板級外設的硬件信息(這些板級外設使用I2C接口跟SOC通訊),如 MMA7660。而後調用of_i2c_register_device,這個函數根據每一個child device node的信息構造i2c_board_info,並調用i2c_new_device,在i2c_new_device中會建立並註冊i2c_client,註冊i2c_client的時候若是找到了對應的設備驅動程序(如 MMA7660的驅動程序),設備驅動程序的probe函數就會被調動。

SPI設備的註冊

    因爲SPI驅動模型跟I2C相似,spi_device的註冊過程也跟i2c_client的很相似。spi控制器在spi子系統中被抽象爲spi_master,spi控制器驅動實際上也在platform_bus上,因此spi控制器對應的是platform_device。當調用of_platform_populate註冊spi控制器對應的platform_device的時候,spi控制器驅動的probe函數會被執行,在probe函數中會向spi子系統註冊spi_master。以tiny4412爲例,在drivers/spi/spi-s3c64xx.c的s3c64xx_spi_probe函數中調用devm_spi_register_master ---> spi_register_master ---> of_register_spi_devices,在of_register_spi_devices中會遍歷與這個spi_master對應的device node的child device node,這些child device node就是掛在spi bus上的板級外設,如spi接口的存儲器等等。而後調用of_register_spi_device,根據每一個child device node的信息建立spi_device,並調用spi_add_device完成註冊,註冊spi_device的時候若是找到了對應的設備驅動程序(如 SPI接口的存儲器的驅動程序),設備驅動程序的probe函數就會被調動。

 

其餘platform device的註冊

在上面說若是在of_platform_populate的時候若是給matches傳遞了of_default_bus_match_table,那麼跟matches匹配的device_node的直接child device node會也會自動被註冊爲platform_device。假如跟matches不匹配的話,這個device_node的直接child device node不會被再被處理了。好比像下面的設備樹結構:

複製代碼
1 / {
 2     #address-cells = <0x2>;
 3     #size-cells = <0x2>;
 4     model = "Qualcomm Technologies";
 5     compatible = "qcom,msm8996";
 6     interrupt-parent = <0x1>;
 7 
 8     soc {
 9         compatible = "simple-bus";
10 
11         qcom,msm-dai-mi2s {
12             compatible = "qcom,msm-dai-mi2s";
13 
14             qcom,msm-dai-q6-mi2s-quat {
15                 compatible = "qcom,msm-dai-q6-mi2s";
16             };
17         };
18     };
19 };
複製代碼

  如上,節點"qcom,msm-dai-mi2s"會被註冊爲platform_device,而其child device node是"qcom,msm-dai-q6-mi2s-quat",並不會被註冊爲platform_device。若是此時須要把"qcom,msm-dai-q6-mi2s-quat"也註冊爲 platform_device的話,也能夠在"qcom,msm-dai-mi2s"對應的platform device_driver在被probe的時候從新調用of_platform_populate。以下:

複製代碼
1 static int msm_dai_mi2s_q6_probe(struct platform_device *pdev)
 2 {
 3     int rc;
 4     rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
 5     if (rc) {
 6         dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
 7             __func__, rc);
 8     } else
 9         dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
10     return rc;
11 }
12 
13 static int msm_dai_mi2s_q6_remove(struct platform_device *pdev)
14 {
15     return 0;
16 }
17 
18 static const struct of_device_id msm_dai_mi2s_dt_match[] = {
19     { .compatible = "qcom,msm-dai-mi2s", },
20     { }
21 };
22 
23 MODULE_DEVICE_TABLE(of, msm_dai_mi2s_dt_match);
24 
25 static struct platform_driver msm_dai_mi2s_q6 = {
26     .probe  = msm_dai_mi2s_q6_probe,
27     .remove = msm_dai_mi2s_q6_remove,
28     .driver = {
29         .name = "msm-dai-mi2s",
30         .owner = THIS_MODULE,
31         .of_match_table = msm_dai_mi2s_dt_match,
32     },
33 };
複製代碼

 其中,在第4行又從新調用了of_platform_populate,它的第一個參數是"qcom,msm-dai-mi2s"的device node,經過這個就能夠遍歷其child device node,並將其註冊爲platform device。

 

其餘

在Linux系統起來後,會將解析完成的設備樹導出到用戶空間。

1、/proc/device-tree

這個目錄下的目錄和文件是根據device node的結構組織的,頂層目錄是root device node,其餘的子目錄是root device node 的 child device node,同時子目錄又能夠再嵌套子目錄,以此表示這些device node的父子關係。

[root@tiny4412 root]# cd /proc/device-tree/
[root@tiny4412 base]# ls
#address-cells                   pinctrl@106E0000
#size-cells                      pinctrl@11000000
adc@126C0000                     pinctrl@11400000
aliases                          pmu
amba                             ppmu_acp@10ae0000
backlight                        ppmu_camif@11ac0000
cam-power-domain@10023C00        ppmu_cpu@106c0000
camera                           ppmu_dmc0@106a0000
chipid@10000000                  ppmu_dmc1@106b0000
chosen                           ppmu_g3d@12630000
clock-controller@03810000        ppmu_g3d@13220000
clock-controller@10030000        ppmu_image@12aa0000
codec@13400000                   ppmu_lcd0@11e40000
compatible                       ppmu_leftbus0@116a0000
cpus                             ppmu_mfc_left@13660000
dsi@11C80000                     ppmu_mfc_right@13670000
ehci@12580000                    ppmu_rightbus@112a0000
exynos-usbphy@125B0000           ppmu_tv@12e40000
fimd@11c00000                    pwm@139D0000
fixed-rate-clocks                regulators
g2d@10800000                     rtc@10070000
g3d-power-domain@10023C60        sdhci@12510000
gps-alive-power-domain@10023D00  sdhci@12520000
gps-power-domain@10023CE0        sdhci@12530000
hdmi@12D00000                    sdhci@12540000
hsotg@12480000                   serial@13800000
i2c-gpio-0                       serial@13810000
i2c@13860000                     serial@13820000
i2c@13870000                     serial@13830000
i2c@13880000                     spi@13920000
 
......

能夠看看上一篇博文中的用軟件I2C控制MMA7660的設備樹的結構:

[root@tiny4412 base]# cd i2c-gpio-0
[root@tiny4412 i2c-gpio-0]# ls
#address-cells     compatible         i2c-gpio,delay-us  name
#size-cells        gpios              mma7660@4c         status
[root@tiny4412 i2c-gpio-0]# cd mma7660@4c/
[root@tiny4412 mma7660@4c]# ls
compatible        interrupt-parent  poll_interval
input_flat        interrupts        reg
input_fuzz        name              status
[root@tiny4412 mma7660@4c]#

能夠看到,mma7660@4c確實是i2c-gpio-0的子目錄,並且咱們也知道mma7660對應的device node確實是i2c-gpio-0對應的device node的child device node。

能夠看看platform device的註冊狀況:

[root@tiny4412 root]# cd /sys/bus/platform/
[root@tiny4412 platform]# ls
devices            drivers_autoprobe  uevent
drivers            drivers_probe
[root@tiny4412 platform]# cd devices/
[root@tiny4412 devices]# ls
10000000.chipid                  12530000.sdhci
10010000.syscon                  12550000.mmc
10020000.system-controller       12580000.ehci
10023c00.cam-power-domain        12590000.ohci
10023c20.tv-power-domain         125b0000.exynos-usbphy
10023c40.mfc-power-domain        12a30000.sysmmu
10023c60.g3d-power-domain        12e20000.sysmmu
10023c80.lcd0-power-domain       13620000.sysmmu
10023ca0.isp-power-domain        13630000.sysmmu
10023ce0.gps-power-domain        13800000.serial
10023d00.gps-alive-power-domain  13810000.serial
10030000.clock-controller        13820000.serial
10050000.mct                     13830000.serial
10070000.rtc                     139d0000.pwm
10440000.interrupt-controller    2020000.sysram
10490000.interrupt-controller    3810000.clock-controller
10502000.l2-cache-controller     3860000.pinctrl
106e0000.pinctrl                 alarmtimer
10a40000.sysmmu                  amba
11000000.pinctrl                 backlight
11400000.pinctrl                 cpufreq-dt
11840000.jpeg-codec              exynos-drm
11a20000.sysmmu                  i2c-gpio-0
11a30000.sysmmu                  leds
11a40000.sysmmu                  opp_table0
11a50000.sysmmu                  pmu
11a60000.sysmmu                  reg-dummy
11e20000.sysmmu                  regulators
12260000.sysmmu                  regulators:regulator@0
12270000.sysmmu                  regulatory.0
122a0000.sysmmu                  serial8250
122b0000.sysmmu                  snd-soc-dummy
123b0000.sysmmu                  usb-hub
123c0000.sysmmu                  video-phy@10020710
12480000.hsotg

能夠看到,在設備樹中:

regulators {
compatible = "simple-bus";
#address-cells = <0x1>;
#size-cells = <0x0>;
 
regulator@0 {
compatible = "regulator-fixed";
reg = <0x0>;
regulator-name = "VMEM_VDD_2.8V";
regulator-min-microvolt = <0x2ab980>;
regulator-max-microvolt = <0x2ab980>;
linux,phandle = <0x19>;
phandle = <0x19>;
};
};

regulator@0雖然是regulator的child device node,而在/proc/device-tree(用於呈現device node的父子關係)中卻看不到regulator@0對應的目錄(實際上是放在了regulator目錄的下面),可是在/sys/bus/platform/devices/下卻能夠看獲得(說明regulator@0這個device node也被註冊爲了platform_device)。

2、/sys/firmware

在/sys/firmware下也能夠看到devicetree的導出信息:

[root@tiny4412 root]# cd /sys/firmware/
[root@tiny4412 firmware]# ls -F
devicetree/ fdt

其中fdt是一個二進制文件,其中是完整的設備樹鏡像,也就是bootloader最終傳給kernel的設備樹鏡像文件,若是是在Andriod系統上,能夠用adb pull將該文件導出到開發機上,而後使用dtc對導出的文件進行反編譯:

adb pull /sys/firmware/fdt ./fdt
dtc -I dtb -O dts -o fdt.dts ./fdt

 這樣就能夠用編輯器查看fdt.dts文件了。

此外,這個文件能夠用hexdump查看:

[root@tiny4412 root]# hexdump -C  /sys/firmware/fdt | head -n 100
00000000  d0 0d fe ed 00 00 dc 2d  00 00 00 48 00 00 a3 ec  |.......-...H....|
00000010  00 00 00 28 00 00 00 11  00 00 00 10 00 00 00 00  |...(............|
00000020  00 00 08 ad 00 00 a3 a4  00 00 00 00 43 a7 f0 00  |............C...|
00000030  00 00 00 00 00 27 bb 09  00 00 00 00 00 00 00 00  |.....'..........|
00000040  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
00000050  00 00 00 03 00 00 00 04  00 00 00 00 00 00 00 01  |................|
00000060  00 00 00 03 00 00 00 04  00 00 00 0f 00 00 00 01  |................|
00000070  00 00 00 03 00 00 00 04  00 00 00 1b 00 00 00 01  |................|
00000080  00 00 00 03 00 00 00 38  00 00 00 2c 66 72 69 65  |.......8...,frie|
00000090  6e 64 6c 79 61 72 6d 2c  74 69 6e 79 34 34 31 32  |ndlyarm,tiny4412|
000000a0  00 73 61 6d 73 75 6e 67  2c 65 78 79 6e 6f 73 34  |.samsung,exynos4|
000000b0  34 31 32 00 73 61 6d 73  75 6e 67 2c 65 78 79 6e  |412.samsung,exyn|
000000c0  6f 73 34 00 00 00 00 03  00 00 00 2f 00 00 00 37  |os4......../...7|
000000d0  46 72 69 65 6e 64 6c 79  41 52 4d 20 54 49 4e 59  |FriendlyARM TINY|
000000e0  34 34 31 32 20 62 6f 61  72 64 20 62 61 73 65 64  |4412 board based|
000000f0  20 6f 6e 20 45 78 79 6e  6f 73 34 34 31 32 00 00  | on Exynos4412..|
00000100  00 00 00 01 63 68 6f 73  65 6e 00 00 00 00 00 03  |....chosen......|
00000110  00 00 00 04 00 00 08 9c  43 cf ab 08 00 00 00 03  |........C.......|
00000120  00 00 00 04 00 00 08 89  43 a7 f0 00 00 00 00 03  |........C.......|
00000130  00 00 00 11 00 00 00 3d  2f 73 65 72 69 61 6c 40  |.......=/serial@|

能夠看到開頭的四個字節正好是d00dfeed,這個文件跟原始的設備樹文件仍是有些不一樣的,如chosen節點和memory節點。由於在用u-boot引導的時候,u-boot根據當前的環境對設備樹鏡像內容進行修改,下面是不一樣的地方:

image

我dump的方法是將fdt的內容用上面的命令重定向到一個文件中(hexdump –C /sys/firmware/fdt > /mnt/fdt.txt),而後經過U盤拷貝到電腦上,複製其中的部分信息,利用winhex文件建立一個二進制文件。再用fdtdump工具(fdtdump dtb文件)將dtb的文件信息導出到一個文本文件中,最後再作比較。

在/sys/firmware/devicetree/base/下也是以device node的父子關係建立的文件和目錄,其實會發現,/proc/device-tree是一個軟鏈接,指向的就是/sys/firmware/devicetree/base/:

[root@tiny4412 root]# ls /proc/device-tree -l
lrwxrwxrwx    1 0        0               29 Jan  1 06:11 /proc/device-tree -> /sys/firmware/devicetree/base
[root@tiny4412 root]#

那麼/sys/firmware/fdt以及/sys/firmware/devicetree是在什麼地方建立的呢?

/sys/firmware/devicetree的建立:

start_kernel
    ---> rest_init
            ---> kernel_init
                    ---> kernel_init_freeable
                            ---> do_basic_setup
                                    ---> driver_init
                                            ---> of_core_init

在of_core_init函數中(drivers/of/base.c):

複製代碼
void __init of_core_init(void)
{
    struct device_node *np;

    /* Create the kset, and register existing nodes */
    mutex_lock(&of_mutex);
    of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
    if (!of_kset) {
        mutex_unlock(&of_mutex);
        pr_err("devicetree: failed to register existing nodes\n");
        return;
    }
    for_each_of_allnodes(np)
        __of_attach_node_sysfs(np);
    mutex_unlock(&of_mutex);

    /* Symlink in /proc as required by userspace ABI */
    if (of_root)
        proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
}
複製代碼

 

/sys/firmware/fdt的建立(drivers/of/fdt.c):

複製代碼
#ifdef CONFIG_SYSFS
static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
                   struct bin_attribute *bin_attr,
                   char *buf, loff_t off, size_t count)
{
    memcpy(buf, initial_boot_params + off, count);
    return count;
}

static int __init of_fdt_raw_init(void)
{
    static struct bin_attribute of_fdt_raw_attr =
        __BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0);

    if (!initial_boot_params)
        return 0;

    if (of_fdt_crc32 != crc32_be(~0, initial_boot_params,
                     fdt_totalsize(initial_boot_params))) {
        pr_warn("fdt: not creating '/sys/firmware/fdt': CRC check failed\n");
        return 0;
    }
    of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params);
    return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr);
}
late_initcall(of_fdt_raw_init);
複製代碼

 

3、測試

下面測試一下根據parent device node的compatible的不一樣,在populate的時候會把不一樣的device node註冊爲不一樣的device。

修改arch/arm/boot/dts/exynos4412-tiny4412.dts:

 1: diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts
 2: index 579a507..ae29aa8 100644
 3: --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts
 4: +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts
 5: @@ -129,6 +129,30 @@
 6: };
 7: };
 8: #endif
 9: +
 10: + demo_parent0 {
 11: + compatible = "simple-bus";
 12: +
 13: + child0{
 14: + compatible = "child0";
 15: + };
 16: +
 17: + child1{
 18: + compatible = "child1";
 19: + };
 20: + };
 21: +
 22: + demo_parent1 {
 23: + compatible = "demo_parent1";
 24: +
 25: + child3{
 26: + compatible = "child3";
 27: + };
 28: +
 29: + child4{
 30: + compatible = "child4";
 31: + };
 32: + };
 33: };
 34: 
 35: &rtc {

其中第11行表示demo_parent0對應的device node未來會在of_platform_populate時遞歸註冊它的的child device node爲platform_device。其實只要demo_parent1的compatible字段含有字符串"simple-bug"便可(如:compatible = "demo_parent1","simple-bus"),字符串"simple-bus"來自數組of_default_bus_match_table:

 1:  
 2: const struct of_device_id of_default_bus_match_table[] = {
 3: { .compatible = "simple-bus", },
 4: { .compatible = "simple-mfd", },
 5: #ifdef CONFIG_ARM_AMBA
 6: { .compatible = "arm,amba-bus", },
 7: #endif /* CONFIG_ARM_AMBA */
 8: {} /* Empty terminated list */
 9: };

從新編譯設備樹(make dtbs),啓動內核能夠發現:

在/proc/device-tree/下(僅表示device node之間的父子邏輯關係):

 1: [root@tiny4412 root]# cd /proc/device-tree/
 2: [root@tiny4412 base]# ls
 3: ......
 4: demo_parent0 ppmu_mfc_left@13660000
 5: demo_parent1 ppmu_mfc_right@13670000
 6: ......
 7: [root@tiny4412 base]# ls -R demo_parent* 
 8: demo_parent0:
 9: child0 child1 compatible name
 10:  
 11: demo_parent0/child0:
 12: compatible name
 13:  
 14: demo_parent0/child1:
 15: compatible name
 16:  
 17: demo_parent1:
 18: child3 child4 compatible name
 19:  
 20: demo_parent1/child3:
 21: compatible name
 22:  
 23: demo_parent1/child4:
 24: compatible name

在/sys/bus/platform/devices/下:

 1: [root@tiny4412 root]# cd /sys/bus/platform/devices/
 2: [root@tiny4412 devices]# ls
 3: ......
 4: 11000000.pinctrl demo_parent0
 5: 11400000.pinctrl demo_parent0:child0
 6: 11840000.jpeg-codec demo_parent0:child1
 7: 11a20000.sysmmu demo_parent1
 8: 11a30000.sysmmu exynos-drm
 9: ......

能夠看到demo_parent0、child0和child1都被註冊爲了platform_device,demo_parent1也被註冊爲了platform_device,而child3和child4卻沒有。child3和child4的具體被註冊爲何設備須要由demo_parent1對應的platform_device的驅動程序決定。

 

未完待續……

相關文章
相關標籤/搜索