作者信息

作者: 彭东林

邮箱:pengdonglin137@163.com

QQ:405728433

平台简介

开发板:tiny4412ADK + S700 + 4GB Flash

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

u-boot版本:友善之臂自带的 U-Boot 2010.12 (为支持uImage启动,做了少许改动)

busybox版本:busybox 1.25

交叉编译工具链: 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”可以理解为一个意思):

一、根据设备树创建device node链表

start_kernel

---> setup_arch

---> unflatten_device_tree

在u-boot引导内核的时候,会将设备树在物理内存中的物理起始地址(存放在寄存器r2中)传递给Linux内核,然后Linux内核在函数unflatten_device_tree中会解析设备树镜像,并利用扫描到的信息创建由device node构成的链表,全局变量of_root指向链表的根节点,设备树的每个节点都会有一个struct device_node与之对应。

二、遍历device node链表,创建并注册platform_device

start_kernel

---> rest_init

---> kernel_init

---> kernel_init_freeable

---> do_basic_setup

---> do_initcalls

在do_initcalls函数中,kernel会依次执行各个initcall函数,在这个过程中,会调用 customize_machine,具体如下:

  1. static int __init customize_machine(void)

  1. {

  1. /*

  1. * customizes platform devices, or adds new ones

  1. * On DT based machines, we fall back to populating the

  1. * machine from the device tree, if no callback is provided,

  1. * otherwise we would always need an init_machine callback.

  1. */

  1. of_iommu_init();

  1. if (machine_desc->init_machine)

  1. machine_desc->init_machine();

  1. #ifdef CONFIG_OF

  1. else

  1. of_platform_populate(NULL, of_default_bus_match_table,

  1. NULL, NULL);

  1. #endif

  1. return 0;

  1. }

  1. arch_initcall(customize_machine);

这样就可调用到exynos_dt_machine_init:

  1. static void __init exynos_dt_machine_init(void)

  1. {

  1. ......

  1.  

  1. of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

  1. }

在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,它的定义如下:

  1. const struct of_device_id of_default_bus_match_table[] = {

  1. { .compatible = "simple-bus", },

  1. { .compatible = "simple-mfd", },

  1. #ifdef CONFIG_ARM_AMBA

  1. { .compatible = "arm,amba-bus", },

  1. #endif /* CONFIG_ARM_AMBA */

  1. {} /* Empty terminated list */

  1. };

是这个意思:如果某个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. 1: int of_platform_populate(struct device_node *root,

  1. 2: const struct of_device_id *matches,

  1. 3: const struct of_dev_auxdata *lookup,

  1. 4: struct device *parent)

  1. 5: {

  1. 6: struct device_node *child;

  1. 7: int rc = 0;

  1. 8: 

  1. 9: root = root ? of_node_get(root) : of_find_node_by_path("/"); // 找到root device node

  1. 10: if (!root)

  1. 11: return -EINVAL;

  1. 12: 

  1. 13: for_each_child_of_node(root, child) { // 遍历root device node的child device node

  1. 14: rc = of_platform_bus_create(child, matches, lookup, parent, true);

  1. 15: if (rc) {

  1. 16: of_node_put(child);

  1. 17: break;

  1. 18: }

  1. 19: }

  1. 20: of_node_set_flag(root, OF_POPULATED_BUS);

  1. 21: 

  1. 22: of_node_put(root);

  1. 23: return rc;

  1. 24: }

of_platform_bus_create :

  1. 1: static int of_platform_bus_create(struct device_node *bus,

  1. 2: const struct of_device_id *matches,

  1. 3: const struct of_dev_auxdata *lookup,

  1. 4: struct device *parent, bool strict)

  1. 5: {

  1. 6: const struct of_dev_auxdata *auxdata;

  1. 7: struct device_node *child;

  1. 8: struct platform_device *dev;

  1. 9: const char *bus_id = NULL;

  1. 10: void *platform_data = NULL;

  1. 11: int rc = 0;

  1. 12: 

  1. 13: /* Make sure it has a compatible property */

  1. 14: if (strict && (!of_get_property(bus, "compatible", NULL))) { // 这样可以把chosen、aliases、memory等没有compatible属性的节点排除在外

  1. 15: pr_debug("%s() - skipping %s, no compatible prop\n",

  1. 16: __func__, bus->full_name);

  1. 17: return 0;

  1. 18: }

  1. 19: 

  1. 20: auxdata = of_dev_lookup(lookup, bus); // tiny4412给lookup传递的是NULL

  1. 21: if (auxdata) {

  1. 22: bus_id = auxdata->name;

  1. 23: platform_data = auxdata->platform_data;

  1. 24: }

  1. 25: 

  1. 26: if (of_device_is_compatible(bus, "arm,primecell")) {

  1. 27: /*

  1. 28: * Don't return an error here to keep compatibility with older

  1. 29: * device tree files.

  1. 30: */

  1. 31: of_amba_device_create(bus, bus_id, platform_data, parent);

  1. 32: return 0;

  1. 33: }

  1. 34: 

  1. 35: dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); // 根据device node创建 platform_device并注册

  1. 36: if (!dev || !of_match_node(matches, bus)) // 判断是不是需要继续遍历这个device node下的child device node

  1. 37: return 0;

  1. 38: 

  1. 39: for_each_child_of_node(bus, child) { // 遍历这个device node下的child device node,将child device node也注册为platform_device

  1. 40: pr_debug(" create child: %s\n", child->full_name);

  1. 41: rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);

  1. 42: if (rc) {

  1. 43: of_node_put(child);

  1. 44: break;

  1. 45: }

  1. 46: }

  1. 47: of_node_set_flag(bus, OF_POPULATED_BUS);

  1. 48: return rc;

  1. 49: }

三、注册其他设备

I2C设备的注册

下面说一下i2c_client是如何注册的。先看下面一张图(来自蜗窝科技):

下面是从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 ;
  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系统起来后,会将解析完成的设备树导出到用户空间。

一、/proc/device-tree

这个目录下的目录和文件是根据device node的结构组织的,顶层目录是root device node,其他的子目录是root device node 的 child device node,同时子目录又可以再嵌套子目录,以此表示这些device node的父子关系。

  1. [root@tiny4412 root]# cd /proc/device-tree/

  1. [root@tiny4412 base]# ls

  1. #address-cells pinctrl@106E0000

  1. #size-cells pinctrl@11000000

  1. adc@126C0000 pinctrl@11400000

  1. aliases pmu

  1. amba ppmu_acp@10ae0000

  1. backlight ppmu_camif@11ac0000

  1. cam-power-domain@10023C00 ppmu_cpu@106c0000

  1. camera ppmu_dmc0@106a0000

  1. chipid@10000000 ppmu_dmc1@106b0000

  1. chosen ppmu_g3d@12630000

  1. clock-controller@03810000 ppmu_g3d@13220000

  1. clock-controller@10030000 ppmu_image@12aa0000

  1. codec@13400000 ppmu_lcd0@11e40000

  1. compatible ppmu_leftbus0@116a0000

  1. cpus ppmu_mfc_left@13660000

  1. dsi@11C80000 ppmu_mfc_right@13670000

  1. ehci@12580000 ppmu_rightbus@112a0000

  1. exynos-usbphy@125B0000 ppmu_tv@12e40000

  1. fimd@11c00000 pwm@139D0000

  1. fixed-rate-clocks regulators

  1. g2d@10800000 rtc@10070000

  1. g3d-power-domain@10023C60 sdhci@12510000

  1. gps-alive-power-domain@10023D00 sdhci@12520000

  1. gps-power-domain@10023CE0 sdhci@12530000

  1. hdmi@12D00000 sdhci@12540000

  1. hsotg@12480000 serial@13800000

  1. i2c-gpio-0 serial@13810000

  1. i2c@13860000 serial@13820000

  1. i2c@13870000 serial@13830000

  1. i2c@13880000 spi@13920000

  1.  

  1. ......

可以看看上一篇博文中的用软件I2C控制MMA7660的设备树的结构:

  1. [root@tiny4412 base]# cd i2c-gpio-0

  1. [root@tiny4412 i2c-gpio-0]# ls

  1. #address-cells compatible i2c-gpio,delay-us name

  1. #size-cells gpios mma7660@4c status

  1. [root@tiny4412 i2c-gpio-0]# cd mma7660@4c/

  1. [root@tiny4412 mma7660@4c]# ls

  1. compatible interrupt-parent poll_interval

  1. input_flat interrupts reg

  1. input_fuzz name status

  1. [root@tiny4412 mma7660@4c]#

可以看到,mma7660@4c确实是i2c-gpio-0的子目录,而且我们也知道mma7660对应的device node确实是i2c-gpio-0对应的device node的child device node。

可以看看platform device的注册情况:

  1. [root@tiny4412 root]# cd /sys/bus/platform/

  1. [root@tiny4412 platform]# ls

  1. devices drivers_autoprobe uevent

  1. drivers drivers_probe

  1. [root@tiny4412 platform]# cd devices/

  1. [root@tiny4412 devices]# ls

  1. 10000000.chipid 12530000.sdhci

  1. 10010000.syscon 12550000.mmc

  1. 10020000.system-controller 12580000.ehci

  1. 10023c00.cam-power-domain 12590000.ohci

  1. 10023c20.tv-power-domain 125b0000.exynos-usbphy

  1. 10023c40.mfc-power-domain 12a30000.sysmmu

  1. 10023c60.g3d-power-domain 12e20000.sysmmu

  1. 10023c80.lcd0-power-domain 13620000.sysmmu

  1. 10023ca0.isp-power-domain 13630000.sysmmu

  1. 10023ce0.gps-power-domain 13800000.serial

  1. 10023d00.gps-alive-power-domain 13810000.serial

  1. 10030000.clock-controller 13820000.serial

  1. 10050000.mct 13830000.serial

  1. 10070000.rtc 139d0000.pwm

  1. 10440000.interrupt-controller 2020000.sysram

  1. 10490000.interrupt-controller 3810000.clock-controller

  1. 10502000.l2-cache-controller 3860000.pinctrl

  1. 106e0000.pinctrl alarmtimer

  1. 10a40000.sysmmu amba

  1. 11000000.pinctrl backlight

  1. 11400000.pinctrl cpufreq-dt

  1. 11840000.jpeg-codec exynos-drm

  1. 11a20000.sysmmu i2c-gpio-0

  1. 11a30000.sysmmu leds

  1. 11a40000.sysmmu opp_table0

  1. 11a50000.sysmmu pmu

  1. 11a60000.sysmmu reg-dummy

  1. 11e20000.sysmmu regulators

  1. 12260000.sysmmu regulators:regulator@0

  1. 12270000.sysmmu regulatory.0

  1. 122a0000.sysmmu serial8250

  1. 122b0000.sysmmu snd-soc-dummy

  1. 123b0000.sysmmu usb-hub

  1. 123c0000.sysmmu video-phy@10020710

  1. 12480000.hsotg

可以看到,在设备树中:

  1. regulators {

  1. compatible = "simple-bus";

  1. #address-cells = <0x1>;

  1. #size-cells = <0x0>;

  1.  

  1. regulator@0 {

  1. compatible = "regulator-fixed";

  1. reg = <0x0>;

  1. regulator-name = "VMEM_VDD_2.8V";

  1. regulator-min-microvolt = <0x2ab980>;

  1. regulator-max-microvolt = <0x2ab980>;

  1. linux,phandle = <0x19>;

  1. phandle = <0x19>;

  1. };

  1. };

regulator@0虽然是regulator的child device node,而在/proc/device-tree(用于呈现device node的父子关系)中却看不到regulator@0对应的目录(其实是放在了regulator目录的下面),但是在/sys/bus/platform/devices/下却可以看得到(说明regulator@0这个device node也被注册为了platform_device)。

二、/sys/firmware

在/sys/firmware下也可以看到devicetree的导出信息:

  1. [root@tiny4412 root]# cd /sys/firmware/

  1. [root@tiny4412 firmware]# ls -F

  1. devicetree/ fdt

其中fdt是一个二进制文件,其中是完整的设备树镜像,也就是bootloader最终传给kernel的设备树镜像文件,如果是在Andriod系统上,可以用adb pull将该文件导出到开发机上,然后使用dtc对导出的文件进行反编译:

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

这样就可以用编辑器查看fdt.dts文件了。

此外,这个文件可以用hexdump查看:

  1. [root@tiny4412 root]# hexdump -C /sys/firmware/fdt | head -n 100

  1. 00000000 d0 0d fe ed 00 00 dc 2d 00 00 00 48 00 00 a3 ec |.......-...H....|

  1. 00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............|

  1. 00000020 00 00 08 ad 00 00 a3 a4 00 00 00 00 43 a7 f0 00 |............C...|

  1. 00000030 00 00 00 00 00 27 bb 09 00 00 00 00 00 00 00 00 |.....'..........|

  1. 00000040 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................|

  1. 00000050 00 00 00 03 00 00 00 04 00 00 00 00 00 00 00 01 |................|

  1. 00000060 00 00 00 03 00 00 00 04 00 00 00 0f 00 00 00 01 |................|

  1. 00000070 00 00 00 03 00 00 00 04 00 00 00 1b 00 00 00 01 |................|

  1. 00000080 00 00 00 03 00 00 00 38 00 00 00 2c 66 72 69 65 |.......8...,frie|

  1. 00000090 6e 64 6c 79 61 72 6d 2c 74 69 6e 79 34 34 31 32 |ndlyarm,tiny4412|

  1. 000000a0 00 73 61 6d 73 75 6e 67 2c 65 78 79 6e 6f 73 34 |.samsung,exynos4|

  1. 000000b0 34 31 32 00 73 61 6d 73 75 6e 67 2c 65 78 79 6e |412.samsung,exyn|

  1. 000000c0 6f 73 34 00 00 00 00 03 00 00 00 2f 00 00 00 37 |os4......../...7|

  1. 000000d0 46 72 69 65 6e 64 6c 79 41 52 4d 20 54 49 4e 59 |FriendlyARM TINY|

  1. 000000e0 34 34 31 32 20 62 6f 61 72 64 20 62 61 73 65 64 |4412 board based|

  1. 000000f0 20 6f 6e 20 45 78 79 6e 6f 73 34 34 31 32 00 00 | on Exynos4412..|

  1. 00000100 00 00 00 01 63 68 6f 73 65 6e 00 00 00 00 00 03 |....chosen......|

  1. 00000110 00 00 00 04 00 00 08 9c 43 cf ab 08 00 00 00 03 |........C.......|

  1. 00000120 00 00 00 04 00 00 08 89 43 a7 f0 00 00 00 00 03 |........C.......|

  1. 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根据当前的环境对设备树镜像内容进行修改,下面是不同的地方:

我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/:

  1. [root@tiny4412 root]# ls /proc/device-tree -l

  1. lrwxrwxrwx 1 0 0 29 Jan 1 06:11 /proc/device-tree -> /sys/firmware/devicetree/base

  1. [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):

  1. void __init of_core_init(void)
  2. {
  3. struct device_node *np;
  4.  
  5. /* Create the kset, and register existing nodes */
  6. mutex_lock(&of_mutex);
  7. of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
  8. if (!of_kset) {
  9. mutex_unlock(&of_mutex);
  10. pr_err("devicetree: failed to register existing nodes\n");
  11. return;
  12. }
  13. for_each_of_allnodes(np)
  14. __of_attach_node_sysfs(np);
  15. mutex_unlock(&of_mutex);
  16.  
  17. /* Symlink in /proc as required by userspace ABI */
  18. if (of_root)
  19. proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
  20. }

 

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

  1. #ifdef CONFIG_SYSFS
  2. static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
  3. struct bin_attribute *bin_attr,
  4. char *buf, loff_t off, size_t count)
  5. {
  6. memcpy(buf, initial_boot_params + off, count);
  7. return count;
  8. }
  9.  
  10. static int __init of_fdt_raw_init(void)
  11. {
  12. static struct bin_attribute of_fdt_raw_attr =
  13. __BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, );
  14.  
  15. if (!initial_boot_params)
  16. return ;
  17.  
  18. if (of_fdt_crc32 != crc32_be(~, initial_boot_params,
  19. fdt_totalsize(initial_boot_params))) {
  20. pr_warn("fdt: not creating '/sys/firmware/fdt': CRC check failed\n");
  21. return ;
  22. }
  23. of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params);
  24. return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr);
  25. }
  26. late_initcall(of_fdt_raw_init);

三、测试

下面测试一下根据parent device node的compatible的不同,在populate的时候会把不同的device node注册为不同的device。

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

  1. 1: diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts

  1. 2: index 579a507..ae29aa8 100644

  1. 3: --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts

  1. 4: +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts

  1. 5: @@ -129,6 +129,30 @@

  1. 6: };

  1. 7: };

  1. 8: #endif

  1. 9: +

  1. 10: + demo_parent0 {

  1. 11: + compatible = "simple-bus";

  1. 12: +

  1. 13: + child0{

  1. 14: + compatible = "child0";

  1. 15: + };

  1. 16: +

  1. 17: + child1{

  1. 18: + compatible = "child1";

  1. 19: + };

  1. 20: + };

  1. 21: +

  1. 22: + demo_parent1 {

  1. 23: + compatible = "demo_parent1";

  1. 24: +

  1. 25: + child3{

  1. 26: + compatible = "child3";

  1. 27: + };

  1. 28: +

  1. 29: + child4{

  1. 30: + compatible = "child4";

  1. 31: + };

  1. 32: + };

  1. 33: };

  1. 34:

  1. 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. 1: 

  1. 2: const struct of_device_id of_default_bus_match_table[] = {

  1. 3: { .compatible = "simple-bus", },

  1. 4: { .compatible = "simple-mfd", },

  1. 5: #ifdef CONFIG_ARM_AMBA

  1. 6: { .compatible = "arm,amba-bus", },

  1. 7: #endif /* CONFIG_ARM_AMBA */

  1. 8: {} /* Empty terminated list */

  1. 9: };

重新编译设备树(make dtbs),启动内核可以发现:

在/proc/device-tree/下(仅表示device node之间的父子逻辑关系):

  1. 1: [root@tiny4412 root]# cd /proc/device-tree/

  1. 2: [root@tiny4412 base]# ls

  1. 3: ......

  1. 4: demo_parent0 ppmu_mfc_left@13660000

  1. 5: demo_parent1 ppmu_mfc_right@13670000

  1. 6: ......

  1. 7: [root@tiny4412 base]# ls -R demo_parent*

  1. 8: demo_parent0:

  1. 9: child0 child1 compatible name

  1. 10: 

  1. 11: demo_parent0/child0:

  1. 12: compatible name

  1. 13: 

  1. 14: demo_parent0/child1:

  1. 15: compatible name

  1. 16: 

  1. 17: demo_parent1:

  1. 18: child3 child4 compatible name

  1. 19: 

  1. 20: demo_parent1/child3:

  1. 21: compatible name

  1. 22: 

  1. 23: demo_parent1/child4:

  1. 24: compatible name

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

  1. 1: [root@tiny4412 root]# cd /sys/bus/platform/devices/

  1. 2: [root@tiny4412 devices]# ls

  1. 3: ......

  1. 4: 11000000.pinctrl demo_parent0

  1. 5: 11400000.pinctrl demo_parent0:child0

  1. 6: 11840000.jpeg-codec demo_parent0:child1

  1. 7: 11a20000.sysmmu demo_parent1

  1. 8: 11a30000.sysmmu exynos-drm

  1. 9: ......

可以看到demo_parent0、child0和child1都被注册为了platform_device,demo_parent1也被注册为了platform_device,而child3和child4却没有。child3和child4的具体被注册为什么设备需要由demo_parent1对应的platform_device的驱动程序决定。

未完待续……

基于tiny4412的Linux内核移植 -- 设备树的展开的更多相关文章

  1. 基于tiny4412的Linux内核移植 -- 设备树的展开【转】

    转自:https://www.cnblogs.com/pengdonglin137/p/5248114.html#_lab2_3_1 阅读目录(Content) 作者信息 平台简介 摘要 正文 一.根 ...

  2. 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九-2)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  3. 基于tiny4412的Linux内核移植 -- MMA7660驱动移植(九)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  4. 基于tiny4412的Linux内核移植 -- PWM子系统学习(八)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  5. 基于tiny4412的Linux内核移植 -- SD卡驱动移植(五)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  6. 基于tiny4412的Linux内核移植(支持device tree)(三)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  7. 基于tiny4412的Linux内核移植(支持device tree)(二)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  8. 基于tiny4412的Linux内核移植(支持device tree)(一)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  9. 基于tiny4412的Linux内核移植 -- PWM子系统学习(七)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

随机推荐

  1. BeginInvoke与EndInvoke方法解决多线程接收委托返回值问题

    BeginInvoke与EndInvoke方法解决多线程接收委托返回值问题 原文:http://www.sufeinet.com/thread-3707-1-1.html      大家可以先看看我上 ...

  2. VS2015 Apache Cordova第一个Android和IOS应用

    前言 本人个人博客原文链接地址为http://aehyok.com/Blog/Detail/75.html. 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehy ...

  3. 開啟apache的日誌功能,但是不記錄.js;.css;.jpg;.ico;.png等訪問記錄

    維護web伺服器最重要的就是要每天都關注網站的訪問日誌,但是每天面對幾百兆的日誌文件實在是非常頭大,所以可以從根源上給日誌減肥一下,讓日誌只記錄對自己有用的內容就變得非常重了. Nginx伺服器要修改 ...

  4. [GO编程]GO编程环境

    GO是一个开源项目,由Google大神发明的,他主要是用于应用程序级开放,可以编译成机器码,和C++一样不需要.NET或JAVA那样的运行框架,因此是个不错的编程语言.更何况发明者都是高手中的高手,而 ...

  5. 深入学习golang(4)—new与make

    Go语言中的内建函数new和make是两个用于内存分配的原语(allocation primitives).对于初学者,这两者的区别也挺容易让人迷糊的.简单的说,new只分配内存,make用于slic ...

  6. 【转】javascript运行机制之this详解

    this是面向对象语言中一个重要的关键字,理解并掌握该关键字的使用对于我们代码的健壮性及优美性至关重要.而javascript的this又有区别于Java.C#等纯面向对象的语言,这使得this更加扑 ...

  7. libev代码

    就是贴上来: ev.c: /* * libev event processing core, watcher management */ /* this big block deduces confi ...

  8. 【Vegas原创】SVN的搭建及安装使用

    中文手册:http://tortoisesvn.net/docs/nightly/TortoiseSVN_zh_CN/index.html 所需程序: 1,TortoiseSVN  http://so ...

  9. Scala 深入浅出实战经典 第40讲:Set、Map、TreeSet、TreeMap操作代码实战

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  10. Codeforces Round #384 (Div. 2)D - Chloe and pleasant prizes 树形dp

    D - Chloe and pleasant prizes 链接 http://codeforces.com/contest/743/problem/D 题面 Generous sponsors of ...