162306a36Sopenharmony_ci.. SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci.. include:: ../disclaimer-zh_CN.rst
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci:Original: Documentation/devicetree/usage-model.rst
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci:翻译:
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci 司延腾 Yanteng Si <siyanteng@loongson.cn>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci:校译:
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci===================
1462306a36Sopenharmony_ciLinux 和 Devicetree
1562306a36Sopenharmony_ci===================
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciLinux对设备树数据的使用模型
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci:作者: Grant Likely <grant.likely@secretlab.ca>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci这篇文章描述了Linux如何使用设备树。关于设备树数据格式的概述可以在
2262306a36Sopenharmony_cidevicetree.org\ [1]_ 的设备树使用页面上找到。
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci.. [1] https://www.devicetree.org/specifications/
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci"Open Firmware Device Tree",或简称为Devicetree(DT),是一种用于描述硬
2762306a36Sopenharmony_ci件的数据结构和语言。更确切地说,它是一种操作系统可读的硬件描述,这样操作系统就不
2862306a36Sopenharmony_ci需要对机器的细节进行硬编码。
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci从结构上看,DT是一棵树,或者说是带有命名节点的无环图,节点可以有任意数量的命名
3162306a36Sopenharmony_ci属性来封装任意的数据。还存在一种机制,可以在自然的树状结构之外创建从一个节点到
3262306a36Sopenharmony_ci另一个节点的任意链接。
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci从概念上讲,一套通用的使用惯例,称为 "bindings"(后文译为绑定),被定义为数据
3562306a36Sopenharmony_ci应该如何出现在树中,以描述典型的硬件特性,包括数据总线、中断线、GPIO连接和外围
3662306a36Sopenharmony_ci设备。
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci尽可能使用现有的绑定来描述硬件,以最大限度地利用现有的支持代码,但由于属性和节
3962306a36Sopenharmony_ci点名称是简单的文本字符串,通过定义新的节点和属性来扩展现有的绑定或创建新的绑定
4062306a36Sopenharmony_ci很容易。然而,要警惕的是,在创建一个新的绑定之前,最好先对已经存在的东西做一些
4162306a36Sopenharmony_ci功课。目前有两种不同的、不兼容的i2c总线的绑定,这是因为在创建新的绑定时没有事先
4262306a36Sopenharmony_ci调查i2c设备在现有系统中是如何被枚举的。
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci1. 历史
4562306a36Sopenharmony_ci-------
4662306a36Sopenharmony_ciDT最初是由Open Firmware创建的,作为将数据从Open Firmware传递给客户程序
4762306a36Sopenharmony_ci(如传递给操作系统)的通信方法的一部分。操作系统使用设备树在运行时探测硬件的拓
4862306a36Sopenharmony_ci扑结构,从而在没有硬编码信息的情况下支持大多数可用的硬件(假设所有设备的驱动程
4962306a36Sopenharmony_ci序都可用)。
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci由于Open Firmware通常在PowerPC和SPARC平台上使用,长期以来,对这些架构的
5262306a36Sopenharmony_ciLinux支持一直使用设备树。
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci2005年,当PowerPC Linux开始大规模清理并合并32位和64位支持时,决定在所有
5562306a36Sopenharmony_ciPowerpc平台上要求DT支持,无论它们是否使用Open Firmware。为了做到这一点,
5662306a36Sopenharmony_ci我们创建了一个叫做扁平化设备树(FDT)的DT表示法,它可以作为一个二进制的blob
5762306a36Sopenharmony_ci传递给内核,而不需要真正的Open Firmware实现。U-Boot、kexec和其他引导程序
5862306a36Sopenharmony_ci被修改,以支持传递设备树二进制(dtb)和在引导时修改dtb。DT也被添加到PowerPC
5962306a36Sopenharmony_ci引导包装器(arch/powerpc/boot/\*)中,这样dtb就可以被包裹在内核镜像中,以
6062306a36Sopenharmony_ci支持引导现有的非DT察觉的固件。
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci一段时间后,FDT基础架构被普及到了所有的架构中。在写这篇文章的时候,6个主线架
6362306a36Sopenharmony_ci构(arm、microblaze、mips、powerpc、sparc和x86)和1个非主线架构(ios)
6462306a36Sopenharmony_ci有某种程度的DT支持。
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci1. 数据模型
6762306a36Sopenharmony_ci-----------
6862306a36Sopenharmony_ci如果你还没有读过设备树用法\ [1]_页,那么现在就去读吧。没关系,我等着....
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci2.1 高层次视角
7162306a36Sopenharmony_ci--------------
7262306a36Sopenharmony_ci最重要的是要明白,DT只是一个描述硬件的数据结构。它没有什么神奇之处,也不会神
7362306a36Sopenharmony_ci奇地让所有的硬件配置问题消失。它所做的是提供一种语言,将硬件配置与Linux内核
7462306a36Sopenharmony_ci(或任何其他操作系统)中的板卡和设备驱动支持解耦。使用它可以使板卡和设备支持
7562306a36Sopenharmony_ci变成数据驱动;根据传递到内核的数据做出设置决定,而不是根据每台机器的硬编码选
7662306a36Sopenharmony_ci择。
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci理想情况下,数据驱动的平台设置应该导致更少的代码重复,并使其更容易用一个内核
7962306a36Sopenharmony_ci镜像支持各种硬件。
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciLinux使用DT数据有三个主要目的:
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci1) 平台识别。
8462306a36Sopenharmony_ci2) 运行时配置,以及
8562306a36Sopenharmony_ci3) 设备数量。
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci2.2 平台识别
8862306a36Sopenharmony_ci------------
8962306a36Sopenharmony_ci首先,内核将使用DT中的数据来识别特定的机器。在一个理想的世界里,具体的平台对
9062306a36Sopenharmony_ci内核来说并不重要,因为所有的平台细节都会被设备树以一致和可靠的方式完美描述。
9162306a36Sopenharmony_ci但是,硬件并不完美,所以内核必须在早期启动时识别机器,以便有机会运行特定于机
9262306a36Sopenharmony_ci器的修复程序。
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci在大多数情况下,机器的身份是不相关的,而内核将根据机器的核心CPU或SoC来选择
9562306a36Sopenharmony_ci设置代码。例如,在ARM上,arch/arm/kernel/setup.c中的setup_arch()将调
9662306a36Sopenharmony_ciarch/arm/kernel/devtree.c中的setup_machine_fdt(),它通过
9762306a36Sopenharmony_cimachine_desc表搜索并选择与设备树数据最匹配的machine_desc。它通过查看根
9862306a36Sopenharmony_ci设备树节点中的'compatible'属性,并将其与struct machine_desc中的
9962306a36Sopenharmony_cidt_compat列表(如果你好奇,该列表定义在arch/arm/include/asm/mach/arch.h
10062306a36Sopenharmony_ci中)进行比较,从而确定最佳匹配。
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci“compatible” 属性包含一个排序的字符串列表,以机器的确切名称开始,后面是
10362306a36Sopenharmony_ci一个可选的与之兼容的板子列表,从最兼容到最不兼容排序。例如,TI BeagleBoard
10462306a36Sopenharmony_ci和它的后继者BeagleBoard xM板的根兼容属性可能看起来分别为::
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	compatible = "ti,omap3-beagleboard", "ti,omap3450", "ti,omap3";
10762306a36Sopenharmony_ci	compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci其中 "ti,map3-beagleboard-xm "指定了确切的型号,它还声称它与OMAP 3450 SoC
11062306a36Sopenharmony_ci以及一般的OMP3系列SoC兼容。你会注意到,该列表从最具体的(确切的板子)到最
11162306a36Sopenharmony_ci不具体的(SoC系列)进行排序。
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci聪明的读者可能会指出,Beagle xM也可以声称与原Beagle板兼容。然而,我们应
11462306a36Sopenharmony_ci该当心在板级上这样做,因为通常情况下,即使在同一产品系列中,每块板都有很高
11562306a36Sopenharmony_ci的变化,而且当一块板声称与另一块板兼容时,很难确定到底是什么意思。对于高层
11662306a36Sopenharmony_ci来说,最好是谨慎行事,不要声称一块板子与另一块板子兼容。值得注意的例外是,
11762306a36Sopenharmony_ci当一块板子是另一块板子的载体时,例如CPU模块连接到一个载体板上。
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci关于兼容值还有一个注意事项。在兼容属性中使用的任何字符串都必须有文件说明它
12062306a36Sopenharmony_ci表示什么。在Documentation/devicetree/bindings中添加兼容字符串的文档。
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci同样在ARM上,对于每个machine_desc,内核会查看是否有任何dt_compat列表条
12362306a36Sopenharmony_ci目出现在兼容属性中。如果有,那么该machine_desc就是驱动该机器的候选者。在搜索
12462306a36Sopenharmony_ci了整个machine_descs表之后,setup_machine_fdt()根据每个machine_desc
12562306a36Sopenharmony_ci在兼容属性中匹配的条目,返回 “最兼容” 的machine_desc。如果没有找到匹配
12662306a36Sopenharmony_ci的machine_desc,那么它将返回NULL。
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci这个方案背后的原因是观察到,在大多数情况下,如果它们都使用相同的SoC或相同
12962306a36Sopenharmony_ci系列的SoC,一个machine_desc可以支持大量的电路板。然而,不可避免地会有一些例
13062306a36Sopenharmony_ci外情况,即特定的板子需要特殊的设置代码,这在一般情况下是没有用的。特殊情况
13162306a36Sopenharmony_ci可以通过在通用设置代码中明确检查有问题的板子来处理,但如果超过几个情况下,
13262306a36Sopenharmony_ci这样做很快就会变得很难看和/或无法维护。
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci相反,兼容列表允许通用machine_desc通过在dt_compat列表中指定“不太兼容”的值
13562306a36Sopenharmony_ci来提供对广泛的通用板的支持。在上面的例子中,通用板支持可以声称与“ti,ompa3”
13662306a36Sopenharmony_ci或“ti,ompa3450”兼容。如果在最初的beagleboard上发现了一个bug,需要在
13762306a36Sopenharmony_ci早期启动时使用特殊的变通代码,那么可以添加一个新的machine_desc,实现变通,
13862306a36Sopenharmony_ci并且只在“ti,omap3-beagleboard”上匹配。
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciPowerPC使用了一个稍微不同的方案,它从每个machine_desc中调用.probe()钩子,
14162306a36Sopenharmony_ci并使用第一个返回TRUE的钩子。然而,这种方法没有考虑到兼容列表的优先级,对于
14262306a36Sopenharmony_ci新的架构支持可能应该避免。
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci2.3 运行时配置
14562306a36Sopenharmony_ci--------------
14662306a36Sopenharmony_ci在大多数情况下,DT是将数据从固件传递给内核的唯一方法,所以也被用来传递运行
14762306a36Sopenharmony_ci时和配置数据,如内核参数字符串和initrd镜像的位置。
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci这些数据大部分都包含在/chosen节点中,当启动Linux时,它看起来就像这样::
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	chosen {
15262306a36Sopenharmony_ci		bootargs = "console=ttyS0,115200 loglevel=8";
15362306a36Sopenharmony_ci		initrd-start = <0xc8000000>;
15462306a36Sopenharmony_ci		initrd-end = <0xc8200000>;
15562306a36Sopenharmony_ci	};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cibootargs属性包含内核参数,initrd-\*属性定义initrd blob的地址和大小。注
15862306a36Sopenharmony_ci意initrd-end是initrd映像后的第一个地址,所以这与结构体资源的通常语义不一
15962306a36Sopenharmony_ci致。选择的节点也可以选择包含任意数量的额外属性,用于平台特定的配置数据。
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci在早期启动过程中,架构设置代码通过不同的辅助回调函数多次调用
16262306a36Sopenharmony_ciof_scan_flat_dt()来解析设备树数据,然后进行分页设置。of_scan_flat_dt()
16362306a36Sopenharmony_ci代码扫描设备树,并使用辅助函数来提取早期启动期间所需的信息。通常情况下,
16462306a36Sopenharmony_ciearly_init_dt_scan_chosen()辅助函数用于解析所选节点,包括内核参数,
16562306a36Sopenharmony_ciearly_init_dt_scan_root()用于初始化DT地址空间模型,early_init_dt_scan_memory()
16662306a36Sopenharmony_ci用于确定可用RAM的大小和位置。
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci在ARM上,函数setup_machine_fdt()负责在选择支持板子的正确machine_desc
16962306a36Sopenharmony_ci后,对设备树进行早期扫描。
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci2.4 设备数量
17262306a36Sopenharmony_ci------------
17362306a36Sopenharmony_ci在电路板被识别后,在早期配置数据被解析后,内核初始化可以以正常方式进行。在
17462306a36Sopenharmony_ci这个过程中的某个时刻,unflatten_device_tree()被调用以将数据转换成更有
17562306a36Sopenharmony_ci效的运行时表示。这也是调用机器特定设置钩子的时候,比如ARM上的machine_desc
17662306a36Sopenharmony_ci.init_early()、.init_irq()和.init_machine()钩子。本节的其余部分使用
17762306a36Sopenharmony_ci了ARM实现的例子,但所有架构在使用DT时都会做几乎相同的事情。
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci从名称上可以猜到,.init_early()用于在启动过程早期需要执行的任何机器特定设
18062306a36Sopenharmony_ci置,而.init_irq()则用于设置中断处理。使用DT并不会实质性地改变这两个函数的
18162306a36Sopenharmony_ci行为。如果提供了DT,那么.init_early()和.init_irq()都能调用任何一个DT查
18262306a36Sopenharmony_ci询函数(of_* in include/linux/of*.h),以获得关于平台的额外数据。
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciDT上下文中最有趣的钩子是.init_machine(),它主要负责将平台的数据填充到
18562306a36Sopenharmony_ciLinux设备模型中。历史上,这在嵌入式平台上是通过在板卡support .c文件中定
18662306a36Sopenharmony_ci义一组静态时钟结构、platform_devices和其他数据,并在.init_machine()中
18762306a36Sopenharmony_ci大量注册来实现的。当使用DT时,就不用为每个平台的静态设备进行硬编码,可以通过
18862306a36Sopenharmony_ci解析DT获得设备列表,并动态分配设备结构体。
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci最简单的情况是,.init_machine()只负责注册一个platform_devices。
19162306a36Sopenharmony_ciplatform_device是Linux使用的一个概念,用于不能被硬件检测到的内存或I/O映
19262306a36Sopenharmony_ci射的设备,以及“复合”或 “虚拟”设备(后面会详细介绍)。虽然DT没有“平台设备”的
19362306a36Sopenharmony_ci术语,但平台设备大致对应于树根的设备节点和简单内存映射总线节点的子节点。
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci现在是举例说明的好时机。下面是NVIDIA Tegra板的设备树的一部分::
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci  /{
19862306a36Sopenharmony_ci	compatible = "nvidia,harmony", "nvidia,tegra20";
19962306a36Sopenharmony_ci	#address-cells = <1>;
20062306a36Sopenharmony_ci	#size-cells = <1>;
20162306a36Sopenharmony_ci	interrupt-parent = <&intc>;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	chosen { };
20462306a36Sopenharmony_ci	aliases { };
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	memory {
20762306a36Sopenharmony_ci		device_type = "memory";
20862306a36Sopenharmony_ci		reg = <0x00000000 0x40000000>;
20962306a36Sopenharmony_ci	};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	soc {
21262306a36Sopenharmony_ci		compatible = "nvidia,tegra20-soc", "simple-bus";
21362306a36Sopenharmony_ci		#address-cells = <1>;
21462306a36Sopenharmony_ci		#size-cells = <1>;
21562306a36Sopenharmony_ci		ranges;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		intc: interrupt-controller@50041000 {
21862306a36Sopenharmony_ci			compatible = "nvidia,tegra20-gic";
21962306a36Sopenharmony_ci			interrupt-controller;
22062306a36Sopenharmony_ci			#interrupt-cells = <1>;
22162306a36Sopenharmony_ci			reg = <0x50041000 0x1000>, < 0x50040100 0x0100 >;
22262306a36Sopenharmony_ci		};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		serial@70006300 {
22562306a36Sopenharmony_ci			compatible = "nvidia,tegra20-uart";
22662306a36Sopenharmony_ci			reg = <0x70006300 0x100>;
22762306a36Sopenharmony_ci			interrupts = <122>;
22862306a36Sopenharmony_ci		};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		i2s1: i2s@70002800 {
23162306a36Sopenharmony_ci			compatible = "nvidia,tegra20-i2s";
23262306a36Sopenharmony_ci			reg = <0x70002800 0x100>;
23362306a36Sopenharmony_ci			interrupts = <77>;
23462306a36Sopenharmony_ci			codec = <&wm8903>;
23562306a36Sopenharmony_ci		};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		i2c@7000c000 {
23862306a36Sopenharmony_ci			compatible = "nvidia,tegra20-i2c";
23962306a36Sopenharmony_ci			#address-cells = <1>;
24062306a36Sopenharmony_ci			#size-cells = <0>;
24162306a36Sopenharmony_ci			reg = <0x7000c000 0x100>;
24262306a36Sopenharmony_ci			interrupts = <70>;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			wm8903: codec@1a {
24562306a36Sopenharmony_ci				compatible = "wlf,wm8903";
24662306a36Sopenharmony_ci				reg = <0x1a>;
24762306a36Sopenharmony_ci				interrupts = <347>;
24862306a36Sopenharmony_ci			};
24962306a36Sopenharmony_ci		};
25062306a36Sopenharmony_ci	};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	sound {
25362306a36Sopenharmony_ci		compatible = "nvidia,harmony-sound";
25462306a36Sopenharmony_ci		i2s-controller = <&i2s1>;
25562306a36Sopenharmony_ci		i2s-codec = <&wm8903>;
25662306a36Sopenharmony_ci	};
25762306a36Sopenharmony_ci  };
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci在.init_machine()时,Tegra板支持代码将需要查看这个DT,并决定为哪些节点
26062306a36Sopenharmony_ci创建platform_devices。然而,看一下这个树,并不能立即看出每个节点代表什么
26162306a36Sopenharmony_ci类型的设备,甚至不能看出一个节点是否代表一个设备。/chosen、/aliases和
26262306a36Sopenharmony_ci/memory节点是信息节点,并不描述设备(尽管可以说内存可以被认为是一个设备)。
26362306a36Sopenharmony_ci/soc节点的子节点是内存映射的设备,但是codec@1a是一个i2c设备,而sound节
26462306a36Sopenharmony_ci点代表的不是一个设备,而是其他设备是如何连接在一起以创建音频子系统的。我知
26562306a36Sopenharmony_ci道每个设备是什么,因为我熟悉电路板的设计,但是内核怎么知道每个节点该怎么做?
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci诀窍在于,内核从树的根部开始,寻找具有“兼容”属性的节点。首先,一般认为任何
26862306a36Sopenharmony_ci具有“兼容”属性的节点都代表某种设备;其次,可以认为树根的任何节点要么直接连
26962306a36Sopenharmony_ci接到处理器总线上,要么是无法用其他方式描述的杂项系统设备。对于这些节点中的
27062306a36Sopenharmony_ci每一个,Linux都会分配和注册一个platform_device,它又可能被绑定到一个
27162306a36Sopenharmony_ciplatform_driver。
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci为什么为这些节点使用platform_device是一个安全的假设?嗯,就Linux对设备
27462306a36Sopenharmony_ci的建模方式而言,几乎所有的总线类型都假定其设备是总线控制器的孩子。例如,每
27562306a36Sopenharmony_ci个i2c_client是i2c_master的一个子节点。每个spi_device都是SPI总线的一
27662306a36Sopenharmony_ci个子节点。类似的还有USB、PCI、MDIO等。同样的层次结构也出现在DT中,I2C设
27762306a36Sopenharmony_ci备节点只作为I2C总线节点的子节点出现。同理,SPI、MDIO、USB等等。唯一不需
27862306a36Sopenharmony_ci要特定类型的父设备的设备是platform_devices(和amba_devices,但后面会
27962306a36Sopenharmony_ci详细介绍),它们将愉快地运行在Linux/sys/devices树的底部。因此,如果一个
28062306a36Sopenharmony_ciDT节点位于树的根部,那么它真的可能最好注册为platform_device。
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ciLinux板支持代码调用of_platform_populate(NULL, NULL, NULL, NULL)来
28362306a36Sopenharmony_ci启动树根的设备发现。参数都是NULL,因为当从树的根部开始时,不需要提供一个起
28462306a36Sopenharmony_ci始节点(第一个NULL),一个父结构设备(最后一个NULL),而且我们没有使用匹配
28562306a36Sopenharmony_ci表(尚未)。对于只需要注册设备的板子,除了of_platform_populate()的调用,
28662306a36Sopenharmony_ci.init_machine()可以完全为空。
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci在Tegra的例子中,这说明了/soc和/sound节点,但是SoC节点的子节点呢?它们
28962306a36Sopenharmony_ci不应该也被注册为平台设备吗?对于Linux DT支持,一般的行为是子设备在驱动
29062306a36Sopenharmony_ci.probe()时被父设备驱动注册。因此,一个i2c总线设备驱动程序将为每个子节点
29162306a36Sopenharmony_ci注册一个i2c_client,一个SPI总线驱动程序将注册其spi_device子节点,其他
29262306a36Sopenharmony_ci总线类型也是如此。根据该模型,可以编写一个与SoC节点绑定的驱动程序,并简单
29362306a36Sopenharmony_ci地为其每个子节点注册platform_device。板卡支持代码将分配和注册一个SoC设
29462306a36Sopenharmony_ci备,一个(理论上的)SoC设备驱动程序可以绑定到SoC设备,并在其.probe()钩
29562306a36Sopenharmony_ci中为/soc/interruptcontroller/soc/serial/soc/i2s/soc/i2c29662306a36Sopenharmony_ci册platform_devices。很简单,对吗?
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci实际上,事实证明,将一些platform_device的子设备注册为更多的platform_device
29962306a36Sopenharmony_ci是一种常见的模式,设备树支持代码反映了这一点,并使上述例子更简单。
30062306a36Sopenharmony_ciof_platform_populate()的第二个参数是一个of_device_id表,任何与该表
30162306a36Sopenharmony_ci中的条目相匹配的节点也将获得其子节点的注册。在Tegra的例子中,代码可以是
30262306a36Sopenharmony_ci这样的::
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci  static void __init harmony_init_machine(void)
30562306a36Sopenharmony_ci  {
30662306a36Sopenharmony_ci	/* ... */
30762306a36Sopenharmony_ci	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
30862306a36Sopenharmony_ci  }
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci“simple-bus”在Devicetree规范中被定义为一个属性,意味着一个简单的内存映射
31162306a36Sopenharmony_ci的总线,所以of_platform_populate()代码可以被写成只是假设简单总线兼容的节
31262306a36Sopenharmony_ci点将总是被遍历。然而,我们把它作为一个参数传入,以便电路板支持代码可以随时覆
31362306a36Sopenharmony_ci盖默认行为。
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci[需要添加关于添加i2c/spi/etc子设备的讨论] 。
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci附录A:AMBA设备
31862306a36Sopenharmony_ci---------------
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ciARM Primecell是连接到ARM AMBA总线的某种设备,它包括对硬件检测和电源管理
32162306a36Sopenharmony_ci的一些支持。在Linux中,amba_device和amba_bus_type结构体被用来表示
32262306a36Sopenharmony_ciPrimecell设备。然而,棘手的一点是,AMBA总线上的所有设备并非都是Primecell,
32362306a36Sopenharmony_ci而且对于Linux来说,典型的情况是amba_device和platform_device实例都是同
32462306a36Sopenharmony_ci一总线段的同义词。
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci当使用DT时,这给of_platform_populate()带来了问题,因为它必须决定是否将
32762306a36Sopenharmony_ci每个节点注册为platform_device或amba_device。不幸的是,这使设备创建模型
32862306a36Sopenharmony_ci变得有点复杂,但解决方案原来并不是太具有侵略性。如果一个节点与“arm,primecell”
32962306a36Sopenharmony_ci兼容,那么of_platform_populate()将把它注册为amba_device而不是
33062306a36Sopenharmony_ciplatform_device。
331