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