设备树详解+ }$ A' \4 i* p2 K0 E" {0 ^
简介
4 |! u9 ~$ g7 k" c) B在传统Linux内核中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,比如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data,这些板级细节代码对内核来讲只不过是垃圾代码。而采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。导致ARM的merge工作量较大。
; }/ k E7 j/ R" `0 u# q5 N, G4 E5 c$ B
之后经过一些讨论,对ARM平台的相关code做出如下相关规范调整,这个也正是引入DTS的原因。6 Y; I" h4 Y/ w$ F$ s
# J( x" r' |6 K; z; }1、ARM的核心代码仍然保存在arch/arm目录下
+ b' N. S6 k1 A/ A8 T# Q* C8 X/ u) N
& d) H& }5 g9 Q) N0 O2、ARM SoC core architecture code保存在arch/arm目录下" x. M0 F% a2 l3 p$ T- c
; y& |0 a" l4 t$ @( [( g3、ARM SOC的周边外设模块的驱动保存在drivers目录下3 Z5 o) [# z. V: } j6 l8 m
! v C- d. y; `, o/ i6 H5 f1 V
4、ARM SOC的特定代码在arch/arm/mach-xxx目录下
4 A v/ x/ w/ ~$ w5 a* b% l7 l) N* U+ H# Y# L
5、ARM SOC board specific的代码被移除,由DeviceTree机制来负责传递硬件拓扑和硬件资源信息。4 o) n6 Z0 K4 ~ j/ g+ K4 u
& g# M9 w, ~4 m5 a% M本质上,Device Tree改变了原来用code方式将硬件配置信息嵌入到内核代码的方法,改用bootloader传递一个DB的形式。对于嵌入式系统,在系统启动阶段,bootloader会加载内核并将控制权转交给内核,此外,还需要把上述的三个参数信息传递给kernel,以便kernel可以有较大的灵活性。在linux kernel中,Device Tree的设计目标就是如此。" K' M- j; O5 s8 J, v( }( c
: @; X6 _7 ~/ \+ ?7 `在devie tree中,可描述的信息包括:
# j/ Q3 H$ Z9 c- W+ U$ ~
( c2 Y( V) Y" a' u# _6 X' g1、CPU的数量和类别7 K/ o0 \5 Y* J' U" N! \
& Q( Q$ M( T3 @
2、内存基地址和大小3 t- _' m0 U h6 a+ v) C
8 F1 k2 v+ D3 s4 h; c& M3、总线和桥9 P7 N; P# X3 ~1 A
5 p! V5 ? D7 i+ p4、外设连接# P Q$ \# E, W1 W6 I+ L
/ {; U8 \* H$ R1 i+ n
5、中断控制器和中断的使用情况& `+ R2 J, |3 Y; p0 J* B+ Q3 w
1 X9 M; c& K0 X# I- z! J" G4 Q6、GPIO控制器和GPIO使用情况
3 W3 S* V) c+ [+ Z3 P( H r
4 x, v; Q/ L" z' ^8 v7、clock控制器和clock使用情况
^& k) ]: d, E6 o1 c: u9 e$ S1 Z( n @9 ~
它基本就是一棵电路板上的CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核来识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给内核,内核会将这些资源绑定给展开的相应设备。
. u/ w' S2 e# W- z7 R3 H6 _7 X; S3 ?+ D6 e, P0 t0 ~) g: N
Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写。引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码。比如在ARM Linux内,一个.dts(device tree source)文件对应一个ARM的machine,一般放置在内核的"arch/arm/boot/dts/"目录内,比如stmp1a-dk1参考板的板级设备树文件就是"arch/arm/boot/dts/ stm32mp157a-dk1.dts"。这个文件可以通过make dtbs命令编译成二进制的.dtb文件供内核驱动使用。
5 N" b' ]; j4 s, N% ^# {# I2 X8 [! I# U6 Z
基于同样的软件分层设计的思想,由于一个SoC可能对应多个machine,如果每个machine的设备树都写成一个完全独立的.dts文件,那么势必相当一些.dts文件有重复的部分,为了解决这个问题,Linux设备树目录把一个SoC公用的部分或者多个machine共同的部分提炼为相应的.dtsi文件。这样每个.dts就只有自己差异的部分,公有的部分只需要"include"相应的.dtsi文件, 这样就是整个设备树的管理更加有序。我这里用Linux4.19.94源码自带的goodix touchscreen触摸芯片为例来分析设备树的使用和移植。这个触摸芯片的设备树节点信息在"- W3 A3 [9 S1 ?2 J3 n7 g" B( Y
Documentation/devicetree/bindings/input/touchscreen/goodix.txt"有详细说明,其驱动源码是" drivers/input/touchscreen/goodix.c"。$ n' u3 J3 N' Q3 n; E4 m C" R/ D
' W" i, R: W) F1 {7 ^ p7 l
基础知识介绍3 O" i) D8 @. o0 ]0 @( F
dts+ w, I9 E6 j4 q; l8 ~3 }
硬件的相应信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份例如stm32mp157a-dk1.dts,一般在Linux源码中存在大量的dts文件,对于arm架构可以在arch/arm/boot/dts找到相应的dts,一个dts文件对应一个ARM的machie。 b# W. M: q, }" j: |- V
! L: v( E, M% Z
dtsi. C4 ^- g* n! B3 D% {. l
值得一提的是,对于一些相同的dts配置可以抽象到dtsi文件中,然后类似于C语言的方式可以include到dts文件中,对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置。
! v: K L8 ]; d3 m `, i$ }
6 ~5 ~" I- v& w1 @dtc. L# J" O' Q7 v: t
dtc是编译dts的工具,可以在Ubuntu系统上通过指令apt-get install device-tree-compiler安装dtc工具,不过在内核源码scripts/dtc路径下已经包含了dtc工具;
1 k4 q* ~- R5 I7 X! m9 Y# x) B$ x8 G9 P$ L! o' L
dtb D- H6 B1 m& P" T) J; @
dtb(Device Tree Blob),dts经过dtc编译之后会得到dtb文件,dtb通过Bootloader引导程序加载到内核。所以Bootloader需要支持设备树才行;Kernel也需要加入设备树的支持;( u G- U8 Q1 D7 d
- i" u* d& I$ i( F8 f7 }, n
DTS结构, E" U1 R% i( Y% j6 y+ T
! f" L& E3 E3 n# h1 Q
- /dts-v1/;
" M0 ~' \: a9 e9 o - / {
- s# L" N4 T! q, c' }2 P - node1 {; O& Q: i" Y5 X
- a-string-property = "A string";
' L c1 H* B7 F% @5 M( ]% C% J& r - a-string-list-property = "first string", "second string";! {5 _) v; W9 ^5 P/ a
- // hex is implied in byte arrays. no '0x' prefix is required
" ~; `& {2 R% A+ p7 A: r4 v - a-byte-data-property = [01 23 34 56];
E+ t( _' Y( g# o6 P8 L4 b9 U - child-node1 {
- a4 I2 m: [2 `5 Z1 M- { - first-child-property;
- Q" R- x5 \/ a+ |+ a# ` - second-child-property = <1>;
5 I9 v+ e1 Y& @/ h$ `: J - a-string-property = "Hello, world";6 L6 g+ O$ ]% j \# t/ A
- };
( B, h& _/ x0 ]* i) s0 L, X5 ~: S* I - child-node2 {; D' L: w* S- G: M/ G
- };
3 [9 n% Y+ @0 ]+ V6 P - };
, G7 o( Y" @7 u' P0 L& `- u - node2 {8 v- }4 H6 s7 Q& N) x
- an-empty-property;
! {% G! j% F' p3 }/ ?6 _, U - a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
' M+ @2 K# I" p, r4 @ - child-node1 {
7 O& ?% |0 j1 F' t - };/ Q8 Y, n% @3 F4 J9 l }$ U
- };4 K& @/ v" ]/ O9 `# K
- };
8 Y4 ^3 a* O) u6 k5 m
复制代码
& U' Q7 f, N: q/ S) Gdevice tree的基本单元是node。这些node被组织成树状结构,除了root node,每个node都只有一个parent。一个device tree文件中只能有一个root node。每个node中包含了若干的property/value来描述该node的一些特性。每个node用节点名字(node name)标识,节点名字的格式是node-name@unit-address。如果该node没有reg属性(后面会描述这个property),那么该节点名字中必须不能包括@和unit-address。unit-address的具体格式是和设备挂在那个bus上相关。例如对于cpu,其unit-address就是从0开始编址,以此加一。而具体的设备,例如以太网控制器,其unit-address就是寄存器地址。root node的node name是确定的,必须是“/”。
7 a& o* u9 r& i% X# ^3 k1 S" I( k2 |: U! c r. ]
也就是说设备树源文件的结构为:
5 ~0 b& w$ o) {8 p; H; v1个root节点”/”;
' t( a# w% |4 a. M- }7 iroot节点下面含一系列子节点,“node1” and “node2”5 |) {6 T4 q8 S7 u- N$ p
节点node1和下又含有一系列子节点,“child-node1” and “child-node2”
# y$ X9 a6 Q7 m' }6 z各个节点都有一系列属性
( S' s! K/ y. @* h 这些属性可能为空,如an-empty-property9 _. B! V+ Z$ l( e6 x# _
可能为字符串,如a-string-property
1 O8 l' b( _1 T) y( ~8 T# M$ A 可能为字符串树组,如a-string-list-property
- C: j" a; q' d( z% A! D$ j2 d 可能为Cells(由u32整数组成),如second-child-property, ?8 P8 H+ o+ ^+ s
DTS语法介绍* `; |2 t0 j6 @* _; ~& d
了解了基本的device tree的结构后,我们总要把这些结构体现在device tree source code上来。在linux kernel中,扩展名是dts的文件就是描述硬件信息的device tree source file,在dts文件中,一个node被定义成如下格式:' l( Y, f* d9 q+ d9 @( L1 n
) V G _& W4 s/ t' C& M
- [label:] node-name[@unit-address] {
: ?3 }4 A7 R, |! j" Q; K7 S - [properties definitions]; X) Z9 v! |- Q) S6 v
- [child nodes]
c) F9 Q! \* _. Y" q @ - }
复制代码
9 C9 n) `$ x* {/ W% d! Z! Y( ^“[]”表示option,因此可以定义一个只有node name的空节点,label方便在dts文件中引用。
2 U0 T. U8 w8 ?6 a$ y7 L' Y6 u6 g
基本数据类型:
5 i: {* l( d3 s2 Otext string(以null结束),以双引号括起来,如:string-property = “a string”;
- z$ U; G* f6 D1 b2 r) {8 `# xcells 是32位无符号整形数,以尖括号括起来,如:cell-property = <0xbeef 123 0xabcd1234>;
( o- w/ J3 Q% r, q! M0 _5 rbinary data 以方括号括起来,如:binary-property = [0x01 0x23 0x45 0x67];& w9 s! d9 w9 q
不同类型数据可以在同一个属性中存在,以逗号分格,如:mixed-property = “a string”, [0x01 0x23 0x45 0x67],<0x12345678>;& }; _, E: S6 B5 D7 T
多个字符串组成的列表也使用逗号分格,如:string-list = “red fish”,”blue fish”;
* B- L( O# y. I' z$ O3 S
6 f: Q8 q! k- D d2 [7 U! k/ Sdts的组成
' W: v6 _$ u8 o1 A$ tcompatible( ^0 }- A. P+ i' J) y. F3 M3 z9 b8 z
每一个dts文件都是由一个root的根节点组成,内核通过根节点“/”的兼容性即可判断它启动的是什么设备,其代码结构如下
+ e a# @2 k5 A4 d* `, {& K9 j, u; L# Z/ M- P
C++ Code/ {( e7 ]0 w! |$ b
- / {0 F ], O& n7 {' Y: J" J
- model = "HQYJ FS-MP1A Discovery Board";
- v9 t5 u' J7 r - compatible = "st,stm32mp157a-dk1", "st,stm32mp157", "hqyj,fsmp1a";
; `6 ~6 b2 e1 T+ o - aliases {
0 j4 o: M) W! o7 r4 ?0 [ - ethernet0 = ðernet0;, S3 ~2 G8 v: C: B+ e/ o$ H3 Y4 g0 t
- serial0 = &uart4;
9 [7 Z7 h- Y3 L' J# b - serial5 = &usart3;
! i c. @3 O' Z" G/ f* N# w/ x$ ` - };; i0 f l8 `, N& ^: b* O
- chosen {: ~# W1 N. D% f7 y6 t: k. W
- stdout-path = "serial0:115200n8";0 I$ L, U! r5 U/ G" s
- };5 o3 j8 I4 {0 f* P( v& I" ^3 t! |# g
- ... ...' T5 H$ p* z3 b9 U& P1 r: Q
- };. c2 l8 w- ~: H; O
复制代码
+ n5 G4 p" h, y z: Q$ imodel属性值是,它指定制造商的设备型号。推荐的格式是:“manufacturer,model”,其中manufacturer是一个字符串描述制造商的名称,而型号指定型号。
/ S1 ^2 {, J1 G' V+ r, F! G1 S
* g/ q3 V$ m L) E$ I e4 Z; S$ |; kcompatible属性值是,指定了系统的名称,是一个字符串列表,它包含了一个“<制造商>,<型号>”形式的字符串。重要的是要指定一个确切的设备,并且包括制造商的名字,以避免命名空间冲突。
$ f2 v( G Y% w X6 Q7 Z% F" M' n
chosen 节点不代表一个真正的设备,但功能与在固件和操作系统间传递数据的地点一样,如根参数,取代以前bootloader的启动参数,控制台的输入输出参数等。; c$ P9 W/ ^% y( }" R4 m
% P: V+ u1 O8 P$ S% S#address-cells和#size-cells0 G: X$ O8 v9 c& \! @: M* M
( Q$ g9 Y& K4 g: S" p1 b0 x+ ]
#address-cells = <1>: 基地址、片选号等绝对起始地址所占字长,单位uint32。 Z# P1 f/ W& @, s$ {% o' S5 C+ C
! v) @+ B7 i! ^+ d5 p8 ^#size-cells = <1>: 长度所占字长,单位uint32。, V; j2 P8 m$ x ^
9 u1 z+ j* E! P6 f3 {CPU addressing
+ v2 q3 Y& h2 m+ l) t在讨论寻址时,CPU节点代表了最简单的情况。 每个CPU都分配有一个唯一的ID,并且没有与CPU ID相关联的大小。8 g1 f6 t/ S* ^* e
- 5 a6 ~1 S. u9 S! a" ]0 m5 x _0 _+ F7 `
- cpus {+ c5 x- B2 K( Z; K6 |
- #address-cells = <1>;. b* a0 t+ n( G1 a
- #size-cells = <0>;8 x$ z& ?3 ^4 z3 I6 n6 j
- cpu0: cpu@0 {# l2 H; i# U' P' {
- compatible = "arm,cortex-a7";. B1 V+ L( b: B: M: r
- device_type = "cpu";
- L4 h0 V- B- W& f6 _' N/ ]. S4 @% y - reg = <0>; w: O+ G& G9 E( {( J( `
- clocks = <&rcc CK_MPU>;
+ e$ p8 o9 ~% o; y7 b( q - clock-names = "cpu";
. [, M0 n: A) Z0 @1 V - operating-points-v2 = <&cpu0_opp_table>;5 T' D7 [0 J4 _' g6 ^, k0 P$ y6 O7 B& F
- nvmem-cells = <&part_number_otp>;
1 L( @4 j; L, u& M - nvmem-cell-names = "part_number";" U$ V9 Q1 V9 \) t/ f8 i: M
- };
' f7 a. `" k% t, F. z0 F! E0 b - cpu1: cpu@1 {( q6 b0 ^4 ^+ z7 U* `# i1 Y
- compatible = "arm,cortex-a7";
2 P6 z! k: j2 e7 `- [1 T( ^) @ - device_type = "cpu";
4 _( c( `9 m: H - reg = <1>;5 K9 |- T3 k& F9 e: h
- clocks = <&rcc CK_MPU>;+ W4 h2 H3 P, D8 c
- clock-names = "cpu";+ p w' F1 v6 a
- operating-points-v2 = <&cpu0_opp_table>; e5 g: f5 C1 U7 y% H: X: C* P
- };9 C- @6 a& K* L' c& n* S
- };
复制代码 " W$ y9 d7 i& z& c) N% ?# g6 v
在cpus节点,#address-cells被设置成了1,#size-cells被设置成了0。这是说子reg值是单独的uint32,它用无大小字段表示地址。在此情况下,这两个cpu分配到的地址为0和1。cpu节点的#size-cells是0因为每个cpu只分配到了一个单独的地址。. y0 ^/ X2 G3 }
/ h! S1 `1 t2 z
你仍然需要注意reg值班需要与节点名的值相匹配。按照惯例,如果一个节点有一个reg属性,那么这个节点名称必须包括unit-address,这是reg属性的第一个address值。
1 E8 P# Z% P6 h
, \3 ]+ I3 ^0 d8 P5 R0 x# |" vMemory Mapped Devices* A+ E4 z8 N4 f% `# A8 ~$ W
与在cpu节点中单独的address值不同,内存映射设备被分配了一系列将要响应的地址,因此不仅需要包含内存的基地址而且还需要映射地址的长度,因此需要使用#size-cells用来表示在每个子reg元组中长度字段的大小。在以下示例中,每个address值为1 cell(32 bits),每个长度值也是1 cell,这在32 bit系统是比较典型的。64 bit设备也许会为#address-cells和#size-cells使用数值2,在device tree中获取64 bit addressing。( r) u$ d+ R! F1 l
- 1 S* h# y; I1 f% r2 B* R4 c
4 t* p& x' N% N$ E/ e- /dts-v1/;
0 M$ T2 ?6 u, c - / {
; _ y8 l; y6 J: d& C0 t5 m - #address-cells = <1>;
: B5 ~, T8 g, a, a1 K - #size-cells = <1>;" M w* ~. m% Z# H& Q0 P* \
- ...2 S* L8 p- d" ~8 m. p
- timers2: timer@40000000 { U) }. N8 \) L
- compatible = "st,stm32-timers";
# z) y3 j( m9 E/ q5 R - reg = <0x40000000 0x400>;, {2 a* L* i3 D) P7 e
- };- _9 X$ z/ e6 x$ l& j: F5 j3 \3 V
- timers3: timer@40001000 {' W- n" l7 X7 F/ B1 ?' z1 m
- compatible = "st,stm32-timers";, t( O' @: M$ x
- reg = <0x40001000 0x400>;
$ C: d6 T5 b! M8 N - };
* P/ a3 \9 L! ~1 F7 B1 o9 f9 b" Z( I - spi2: spi@4000b000 {
* z2 v$ @) l( M8 l% f _ - compatible = "st,stm32h7-spi";8 l {" f2 ?% Z+ r2 H( d
- reg = <0x4000b000 0x400>;
+ I' m/ G- Y+ j, w' i - };
7 D3 I3 M( c/ T6 |+ I; n. O - uart7: serial@40018000 {
2 ]8 H3 l- }4 f# q$ x4 Z5 S& V, { - compatible = "st,stm32h7-uart";
1 e; d& w f K+ P( i/ }- m) U9 i - reg = <0x40018000 0x400>;
9 Z9 a* \/ M' P- O5 c - };
' N4 y/ g" D3 f( w5 i - sai1: sai@4400a000 {
. d( E! j9 d- T( A/ f1 ^2 K; n - compatible = "st,stm32h7-sai";% L% o2 [1 k9 N0 {. E
- reg = <0x4400a000 0x4>;
, G% M6 N4 ] a - };
" }4 s( c, U, u5 y7 i5 D/ F - ...* S# {; M4 ?% p6 w
- };( G1 y4 O- Y$ @2 w$ x
复制代码
, ^6 ~. I+ a' `. c: gNon Memory Mapped Devices
2 \' ]* E* y5 C6 \! j处理器总线的其它设备为非内存映射设备。他们有地址范围,但不能被CPU直接寻址。母设备的驱动程序将代替CPU进行间接访问。以i2c设备为例,每个设备都分配了一个地址,但没有长度或范围与之相匹配。这与CPU地址分配很相似。
3 t$ Y! J- Q$ o' q4 i/ B1 U i/ ~3 h1 E0 } }. U
C++ Code
" u% \6 c- a" D4 t& c4 b
- ]- g/ H5 o9 A5 m, L- : ^) ?$ o( ^. r' b4 I: Q* R
- etf: etf@50092000 {
9 A' k6 s5 ^) }+ P' C0 i - compatible = "arm,coresight-tmc", "arm,primecell";' Q$ ^' A# o* ]9 g6 y
- reg = <0x50092000 0x1000>;& z4 ?7 h) K' P( M
- clocks = <&rcc CK_TRACE>;
# F; C9 [+ f8 p7 @1 m0 j$ P - clock-names = "apb_pclk"; [6 s3 R. |3 r
- ports {
9 v% Z0 k# a' ?" u+ v - #address-cells = <1>;8 `! T' {/ t# Y8 {. c
- #size-cells = <0>;
. ^; ^5 E9 d. G2 [* \5 b, ~- ]$ Y& ~ - port@0 {
/ {" D1 i% X# }- t _ - reg = <0>;
" ~3 b. I6 W$ A' ~5 X$ L - etf_in_port: endpoint {
' P3 x6 ?- c1 D2 k - slave-mode;
% w" t% I% U; U/ q- e - remote-endpoint = <&funnel_out_port0>;( T8 o. S7 J* t. \ Y" z
- };
6 S0 u7 H0 k/ ~! G0 ] - };, r* m4 H' u: z' X( J% ~* r! c( K
- port@1 {, s* E* z7 f: f# f( l. |
- reg = <0>;: T- O% e0 [! X8 i- |* }0 q
- etf_out_port: endpoint {5 e: w$ H( F* ?* U4 T
- remote-endpoint = <&tpiu_in_port>;9 G& ^ A# ]/ F
- };
9 }4 Q6 u' S7 R# n - };
* l. n. s$ t2 k - };
) ^3 \" [) ~& [# f7 l - };
复制代码
4 i/ p. V" K* W" ?0 Q tRanges (Address Translation)
+ N. r' S9 f, `前面已经讨论过如何向设备分配地址,但此时这些地址只是本地设备节点,还没有说明如何从那些地址里映射到cpu可以使用的地址。根节点经常描述地址空间的CPU视图。根节点的子节点已经使用了CPU的address domain,所以不需要任何明确的映射。例如,serial@101f0000设备被直接分配了地址0x101f0000。; n4 w" c2 P7 D o W) l9 m
* ~. _* y! y0 `; A4 x' G8 c
根节点的非直接子节点是无法使用CPU的address domain的。为了在deivce tree获取内存映射地址必须指定如何从一个域名将地址转换到另一个。Ranges属性就用于此目的。以下是添加了ranges属性的device tree示例。
* I; ^- ]9 U, r* ~! H3 [
! h$ }6 {6 j) y0 x$ ^1 H: X- m4_rproc: m4@0 {
( G/ L" C* o4 @ - compatible = "st,stm32mp1-rproc";
$ x% c) ]7 P. H# R8 {. y - #address-cells = <1>;- C% M+ Q& f# o! A' ]
- #size-cells = <1>;
. u* @& ?/ s; W1 J - ranges = <0x00000000 0x38000000 0x10000>,
g6 X+ W* P* Y1 I" `# e. t - <0x30000000 0x30000000 0x60000>,
" L( M6 b7 i# L: K, @ n - <0x10000000 0x10000000 0x60000>;
3 r" s% T* S* w5 R# p0 w+ b ]. ? - resets = <&rcc MCU_R>;
7 p" F, C( w) B - reset-names = "mcu_rst";3 C, g8 Y% l4 A$ y
- st,syscfg-pdds = <&pwr 0x014 0x1>;
! q" H, a, U5 Z# I - st,syscfg-holdboot = <&rcc 0x10C 0x1>;
" J/ V! ~' G: F$ Q+ _ - st,syscfg-tz = <&rcc 0x000 0x1>;
# z5 }2 M$ f. }5 B0 v - st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
$ G$ v% ~ D" w" ? t& v/ V - status = "disabled";7 ] _+ Q+ Z/ f$ b8 M7 D p; ~) C
- m4_system_resources {
6 {3 h! ^3 ?& g - compatible = "rproc-srm-core";, w' t. n4 j$ F) J8 d
- status = "disabled";4 `* y- H! O6 r: d
- };
- y) Q$ v8 \0 L" S2 { - };
复制代码
) w1 O& s. b, f! v! ?0 Q& M( Iranges是一个地址转换列表。每个输入ranges表格的是包含子地址的元组,母地址和子地址空间的范围大小。每个字段的大小都由获取的子地址的#address-cells值,母地址的#address-cell值和子地址的#size-cells值而定。以外部总线为例,子地址是2 cells,母地址是1 cell,大小也为1 cell。, g5 ?8 z; Y: j/ k+ j8 d7 W
) w3 `! R+ |) p# istatus) B. c# X e$ U& K
device tree中的status标识了设备的状态,使用status可以去禁止设备或者启用设备,看下设备树规范中的status可选值。6 j6 r. N# T& V. p; q- ]( G/ k7 |! T
9 S+ J6 O5 ?6 T% J
, M2 `$ H; M: l5 h5 B% j$ L0 E& {
4 o! f8 i1 U1 R H1 G( p中断映射7 C7 ~2 I' K+ S$ d8 b7 i
与遵循树的自然结构而进行的地址转换不同,机器上的任何设备都可以发起和终止中断信号。另外地址的编址也不同于中断信号,前者是设备树的自然表示,而后者者表现为独立于设备树结构的节点之间的链接。
; U! G- I2 K" S# j1 [* h M5 M
3 B, Q5 }& S& J2 u, }% B1 X
1 R# P: ^, K& L$ Q, `' h下面显示了ADC控制器中断设备片段0 y/ f* H# s) ~/ e" \9 k. a4 O: u
- / w7 @! ?4 g* `1 c/ o
- & M+ h: P2 q+ ~( r2 j9 J9 v" p5 e
- adc: adc@48003000 {
% L* b( Z- K; b$ }' v2 ~3 s# | - compatible = "st,stm32mp1-adc-core";
! V) b: \5 H9 ?% F6 } - reg = <0x48003000 0x400>;) b4 y% k9 E1 D ~8 J
- interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,* j J- h3 G% G) _# R
- <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
) Z x1 j4 J8 U% v7 x, q - clocks = <&rcc ADC12>, <&rcc ADC12_K>;
" b+ {" M s5 ]' O* F7 ?. I' } - clock-names = "bus", "adc";
( N0 W9 V9 a( H' [ - interrupt-controller;2 J8 |9 }+ Y6 l; `( l5 E, N
- st,syscfg-vbooster = <&syscfg 0x4 0x100>;" q% Z" _7 S$ k" q6 K
- st,syscfg-vbooster-clr = <&syscfg 0x44 0x100>;
2 a: |& C1 ^2 i* U7 l3 S' y! G - st,syscfg-anaswvdd = <&syscfg 0x4 0x200>;
v# K/ C. p1 E3 a5 t4 W/ v. c - st,syscfg-anaswvdd-clr = <&syscfg 0x44 0x200>;" }3 Z! V& i7 z! E& y9 {
- #interrupt-cells = <1>;
: C0 h/ c$ M$ ~0 V* \ - #address-cells = <1>;
% U: o* ]8 t1 L- E - #size-cells = <0>;1 k# m, W0 I! E: A* h) ~
- status = "disabled";6 \2 x) N- c8 u5 z8 t8 i
- adc1: adc@0 {2 W- e. u( Y$ R- i$ J
- compatible = "st,stm32mp1-adc";- t8 e' e$ Q g! b- m& z/ d, x
- #io-channel-cells = <1>;
5 [+ ]" ~# C, G7 r9 f - reg = <0x0>;
{) G1 w5 C3 T, A5 s - interrupt-parent = <&adc>;
) B* h: c* K( f - interrupts = <0>;" V9 `2 B, J# ~: b
- dmas = <&dmamux1 9 0x400 0x05>;# V, z* V4 C4 u- u1 Q
- dma-names = "rx";1 ~' _0 o* S6 z1 A2 p4 R" z3 }
- status = "disabled";
- P* d4 t$ _5 O7 n# Y& ? - };
: x! Y/ S; c& }! ?' W - adc2: adc@100 {: j! {; g' T' o8 Q
- compatible = "st,stm32mp1-adc";
, o* Q( M: M8 M0 c - #io-channel-cells = <1>;8 D( p3 I/ B, E- ^
- reg = <0x100>;
6 R* k* A* v' v1 X - interrupt-parent = <&adc>;' {* M' a0 k$ V9 O, y
- interrupts = <1>;. H e1 D+ Q2 m! }' E1 H
- dmas = <&dmamux1 10 0x400 0x05>;' g; @' `3 l9 {9 Q5 d) E8 E
- dma-names = "rx";- {+ _: Y+ J1 P$ n4 k* Y
- /* temperature sensor */
" v3 p1 w0 N' y6 a; }: a - st,adc-channels = <12>;
: L: J, L! l% U* b - st,min-sample-time-nsecs = <10000>;+ L/ R+ b8 i3 y n# |5 c
- status = "disabled";
, y! s6 l9 v# P6 P - };
8 S& W, O+ K" ^+ h" x' v1 W' S - jadc1: jadc@0 {# m6 M- Y; ~( y! _" l
- compatible = "st,stm32mp1-adc";
2 C, q/ E$ z1 n$ L0 G. f9 E - st,injected;+ b b4 R0 v& e2 m2 t' m4 W# r
- #io-channel-cells = <1>;, O) A t" F ~. j
- reg = <0x0>;
) B! Q" j1 h. @- c8 C - interrupt-parent = <&adc>;
, E5 b" i" o- e- E+ b, g2 @$ z7 X - interrupts = <3>;
, f T9 ?, s: U) U d$ s8 U7 q* `; y! K, o - status = "disabled";( q6 u# ]0 b7 Y; Z
- };
4 O) N/ s! g+ i: ` - jadc2: jadc@100 {
% h! r, R [; h- [6 W7 N K - compatible = "st,stm32mp1-adc";
0 N5 s8 u1 D0 o2 s' D - st,injected;# A* d& I6 V9 V" K
- #io-channel-cells = <1>;6 w/ Z$ z& R4 \; Z9 K X
- reg = <0x100>;
& Y5 U, y" z8 S' e - interrupt-parent = <&adc>;5 l6 k3 p, |! M3 J
- interrupts = <4>;+ d" Z, ^! E# _. d) t
- /* temperature sensor */6 @) C. J- o8 `# c. b2 [& [
- st,adc-channels = <12>;8 C4 E4 ]8 A0 f7 M! K- S$ ^
- st,min-sample-time-nsecs = <10000>;, E6 i) g% j& [# u, n0 m; d. w
- status = "disabled";; ]4 C; f" x0 ]
- }; P; _5 p$ U; t$ z! e. X8 C
- adc_temp: temp {4 S! o2 c" _( t+ ?
- compatible = "st,stm32mp1-adc-temp";" n! o* \# E; V
- io-channels = <&adc2 12>;
w9 A3 S5 Q* c m6 M9 c - nvmem-cells = <&ts_cal1>, <&ts_cal2>;+ u8 O, z8 x; o- c$ E' h' m, P. F
- nvmem-cell-names = "ts_cal1", "ts_cal2";! ?" v) o+ u8 d' Y- O3 x0 X
- #io-channel-cells = <0>;
i% ^* t1 H4 r. ]1 e& n7 A9 W - #thermal-sensor-cells = <0>;
. ?& R$ y6 V% V8 c7 E - status = "disabled";# Y& w+ f) ~8 n( _0 q5 g5 H) v7 k
- };' K2 ?# C( M2 [. I; F
- };
复制代码
' i6 j# o+ n& E( n. S下面显示了ethernet控制器中断设备片段/ {& ^+ k8 G( _
! N/ S9 ]' r$ U9 B8 `
- ethernet0: ethernet@5800a000 {
! n+ m! `/ _5 G9 q4 @9 l - compatible = "st,stm32mp1-dwmac", "snps,dwmac-4.20a";2 R' \" p7 e9 s) E
- reg = <0x5800a000 0x2000>;+ Q7 ^6 M& N% z2 @4 z9 K: u
- reg-names = "stmmaceth";: ]' M' K7 m2 |) m; ^5 i9 m- ^
- interrupts-extended = <&intc GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>,+ F* l, y- Z2 A2 s4 Y" Z, s
- <&intc GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>,
3 h* U8 I$ O) a - <&exti 70 1>;0 Z. ]7 C: y$ P0 f! r3 A- K
- interrupt-names = "macirq",
/ Z2 Q4 D# n9 p9 e9 I - "eth_wake_irq",
1 ^" U/ W4 Z* N: o" T - "stm32_pwr_wakeup";
& X9 n0 L7 ?+ g% L/ b4 g3 m - clock-names = "stmmaceth",
+ g; h* a( H# x- Q, b - "mac-clk-tx",# b/ h. s0 C6 f7 Z; g
- "mac-clk-rx",! I. l* o5 f8 F6 I' o& J9 e
- "ethstp";
! \2 d6 e& u! d% n) f& O1 ^' I - clocks = <&rcc ETHMAC>,
0 | v3 x: @0 P3 d, P - <&rcc ETHTX>,
* P# j6 h+ M( l* ~) a( [ - <&rcc ETHRX>,
" u: W5 ]" ?2 K; h, G# r# H& K - <&rcc ETHSTP>;
: G0 W- m; M3 i' w) O- M2 V - st,syscon = <&syscfg 0x4>;! J3 \5 s: S$ `- p% ~ b$ \
- snps,mixed-burst;
! d, n7 h$ s9 r" W! q7 x/ z - snps,pbl = <2>;
/ s- {. S) ]& y, ^# l - snps,en-tx-lpi-clockgating;( d) S0 o: t. g& F5 M" \
- snps,axi-config = <&stmmac_axi_config_0>;
! f/ B! J& v" x- g - snps,tso;
' ~2 E! O ?2 x - power-domains = <&pd_core>;: z' L/ s: [0 C: v6 }1 `7 _
- status = "disabled";
, @; |( o1 }% v' m" Q$ p3 k - };
复制代码 8 Y0 p H9 x y+ f" y
+ b+ ^$ W/ B. ?7 z
$ d7 ]& e& h2 x) F% S! C! o特殊节点# n5 O) i. |, \" v9 o
aliases节点为了解决节点路径名过长的问题,引入了节点别名的概念,可以引用到一个全路径的节点。如/external-bus/ethernet@0,0,但当用户想知道具体内容的时候显得太累赘。
, Z+ r) B! B0 @2 ?
4 \" F" S( L/ a9 A- * x+ G% ~4 c: X8 L" O
- aliases {
5 c h0 U( C3 j) N2 n - ethernet0 = ðernet0;
! R% q2 D+ u: `) i8 W! }& l - serial0 = &uart4;% ~. s4 d, V+ Q4 ]
- serial5 = &usart3;
8 z8 R: w8 e% \3 _% s - };
复制代码 M5 P9 f, }+ A: d; N
当为设备分配一个标识符的时候,操作系统更倾向于使用aliases。
- j$ A- I- b, M" ?( U, @————————————————
+ D. i& X4 R0 X# V- Z i版权声明:华清远见IT开放实验室; Q0 k4 O, q1 f
4 Q6 K' Q1 X( l1 O# w) k
2 ~; _$ C' l3 j6 o& H0 d( \" ` |