你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32MP157 Linux系统移植开发篇3:设备树详解

[复制链接]
STMCU小助手 发布时间:2022-10-1 19:16
设备树详解+ }$ 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
  1. /dts-v1/;
    " M0 ~' \: a9 e9 o
  2. / {
    - s# L" N4 T! q, c' }2 P
  3. node1 {; O& Q: i" Y5 X
  4. a-string-property = "A string";
    ' L  c1 H* B7 F% @5 M( ]% C% J& r
  5. a-string-list-property = "first string", "second string";! {5 _) v; W9 ^5 P/ a
  6. // hex is implied in byte arrays. no '0x' prefix is required
    " ~; `& {2 R% A+ p7 A: r4 v
  7. a-byte-data-property = [01 23 34 56];
      E+ t( _' Y( g# o6 P8 L4 b9 U
  8. child-node1 {
    - a4 I2 m: [2 `5 Z1 M- {
  9. first-child-property;
    - Q" R- x5 \/ a+ |+ a# `
  10. second-child-property = <1>;
    5 I9 v+ e1 Y& @/ h$ `: J
  11. a-string-property = "Hello, world";6 L6 g+ O$ ]% j  \# t/ A
  12. };
    ( B, h& _/ x0 ]* i) s0 L, X5 ~: S* I
  13. child-node2 {; D' L: w* S- G: M/ G
  14. };
    3 [9 n% Y+ @0 ]+ V6 P
  15. };
    , G7 o( Y" @7 u' P0 L& `- u
  16. node2 {8 v- }4 H6 s7 Q& N) x
  17. an-empty-property;
    ! {% G! j% F' p3 }/ ?6 _, U
  18. a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
    ' M+ @2 K# I" p, r4 @
  19. child-node1 {
    7 O& ?% |0 j1 F' t
  20. };/ Q8 Y, n% @3 F4 J9 l  }$ U
  21. };4 K& @/ v" ]/ O9 `# K
  22. };
    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
  1. [label:] node-name[@unit-address] {
    : ?3 }4 A7 R, |! j" Q; K7 S
  2. [properties definitions]; X) Z9 v! |- Q) S6 v
  3. [child nodes]
      c) F9 Q! \* _. Y" q  @
  4. }
复制代码

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
  1. / {0 F  ], O& n7 {' Y: J" J
  2. model = "HQYJ FS-MP1A Discovery Board";
    - v9 t5 u' J7 r
  3. compatible = "st,stm32mp157a-dk1", "st,stm32mp157", "hqyj,fsmp1a";
    ; `6 ~6 b2 e1 T+ o
  4. aliases {
    0 j4 o: M) W! o7 r4 ?0 [
  5. ethernet0 = ðernet0;, S3 ~2 G8 v: C: B+ e/ o$ H3 Y4 g0 t
  6. serial0 = &uart4;
    9 [7 Z7 h- Y3 L' J# b
  7. serial5 = &usart3;
    ! i  c. @3 O' Z" G/ f* N# w/ x$ `
  8. };; i0 f  l8 `, N& ^: b* O
  9. chosen {: ~# W1 N. D% f7 y6 t: k. W
  10. stdout-path = "serial0:115200n8";0 I$ L, U! r5 U/ G" s
  11. };5 o3 j8 I4 {0 f* P( v& I" ^3 t! |# g
  12. ... ...' T5 H$ p* z3 b9 U& P1 r: Q
  13. };. 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
  1. . H" S! y8 N* L
复制代码
( 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
  1. 5 a6 ~1 S. u9 S! a" ]0 m5 x  _0 _+ F7 `
  2. cpus {+ c5 x- B2 K( Z; K6 |
  3. #address-cells = <1>;. b* a0 t+ n( G1 a
  4. #size-cells = <0>;8 x$ z& ?3 ^4 z3 I6 n6 j
  5. cpu0: cpu@0 {# l2 H; i# U' P' {
  6. compatible = "arm,cortex-a7";. B1 V+ L( b: B: M: r
  7. device_type = "cpu";
    - L4 h0 V- B- W& f6 _' N/ ]. S4 @% y
  8. reg = <0>;  w: O+ G& G9 E( {( J( `
  9. clocks = <&rcc CK_MPU>;
    + e$ p8 o9 ~% o; y7 b( q
  10. clock-names = "cpu";
    . [, M0 n: A) Z0 @1 V
  11. operating-points-v2 = <&cpu0_opp_table>;5 T' D7 [0 J4 _' g6 ^, k0 P$ y6 O7 B& F
  12. nvmem-cells = <&part_number_otp>;
    1 L( @4 j; L, u& M
  13. nvmem-cell-names = "part_number";" U$ V9 Q1 V9 \) t/ f8 i: M
  14. };
    ' f7 a. `" k% t, F. z0 F! E0 b
  15. cpu1: cpu@1 {( q6 b0 ^4 ^+ z7 U* `# i1 Y
  16. compatible = "arm,cortex-a7";
    2 P6 z! k: j2 e7 `- [1 T( ^) @
  17. device_type = "cpu";
    4 _( c( `9 m: H
  18. reg = <1>;5 K9 |- T3 k& F9 e: h
  19. clocks = <&rcc CK_MPU>;+ W4 h2 H3 P, D8 c
  20. clock-names = "cpu";+ p  w' F1 v6 a
  21. operating-points-v2 = <&cpu0_opp_table>;  e5 g: f5 C1 U7 y% H: X: C* P
  22. };9 C- @6 a& K* L' c& n* S
  23. };
复制代码
" 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. 1 S* h# y; I1 f% r2 B* R4 c

  2. 4 t* p& x' N% N$ E/ e
  3. /dts-v1/;
    0 M$ T2 ?6 u, c
  4. / {
    ; _  y8 l; y6 J: d& C0 t5 m
  5. #address-cells = <1>;
    : B5 ~, T8 g, a, a1 K
  6. #size-cells = <1>;" M  w* ~. m% Z# H& Q0 P* \
  7. ...2 S* L8 p- d" ~8 m. p
  8. timers2: timer@40000000 {  U) }. N8 \) L
  9. compatible = "st,stm32-timers";
    # z) y3 j( m9 E/ q5 R
  10. reg = <0x40000000 0x400>;, {2 a* L* i3 D) P7 e
  11. };- _9 X$ z/ e6 x$ l& j: F5 j3 \3 V
  12. timers3: timer@40001000 {' W- n" l7 X7 F/ B1 ?' z1 m
  13. compatible = "st,stm32-timers";, t( O' @: M$ x
  14. reg = <0x40001000 0x400>;
    $ C: d6 T5 b! M8 N
  15. };
    * P/ a3 \9 L! ~1 F7 B1 o9 f9 b" Z( I
  16. spi2: spi@4000b000 {
    * z2 v$ @) l( M8 l% f  _
  17. compatible = "st,stm32h7-spi";8 l  {" f2 ?% Z+ r2 H( d
  18. reg = <0x4000b000 0x400>;
    + I' m/ G- Y+ j, w' i
  19. };
    7 D3 I3 M( c/ T6 |+ I; n. O
  20. uart7: serial@40018000 {
    2 ]8 H3 l- }4 f# q$ x4 Z5 S& V, {
  21. compatible = "st,stm32h7-uart";
    1 e; d& w  f  K+ P( i/ }- m) U9 i
  22. reg = <0x40018000 0x400>;
    9 Z9 a* \/ M' P- O5 c
  23. };
    ' N4 y/ g" D3 f( w5 i
  24. sai1: sai@4400a000 {
    . d( E! j9 d- T( A/ f1 ^2 K; n
  25. compatible = "st,stm32h7-sai";% L% o2 [1 k9 N0 {. E
  26. reg = <0x4400a000 0x4>;
    , G% M6 N4 ]  a
  27. };
    " }4 s( c, U, u5 y7 i5 D/ F
  28. ...* S# {; M4 ?% p6 w
  29. };( 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

  1. - ]- g/ H5 o9 A5 m, L
  2. : ^) ?$ o( ^. r' b4 I: Q* R
  3. etf: etf@50092000 {
    9 A' k6 s5 ^) }+ P' C0 i
  4. compatible = "arm,coresight-tmc", "arm,primecell";' Q$ ^' A# o* ]9 g6 y
  5. reg = <0x50092000 0x1000>;& z4 ?7 h) K' P( M
  6. clocks = <&rcc CK_TRACE>;
    # F; C9 [+ f8 p7 @1 m0 j$ P
  7. clock-names = "apb_pclk";  [6 s3 R. |3 r
  8. ports {
    9 v% Z0 k# a' ?" u+ v
  9. #address-cells = <1>;8 `! T' {/ t# Y8 {. c
  10. #size-cells = <0>;
    . ^; ^5 E9 d. G2 [* \5 b, ~- ]$ Y& ~
  11. port@0 {
    / {" D1 i% X# }- t  _
  12. reg = <0>;
    " ~3 b. I6 W$ A' ~5 X$ L
  13. etf_in_port: endpoint {
    ' P3 x6 ?- c1 D2 k
  14. slave-mode;
    % w" t% I% U; U/ q- e
  15. remote-endpoint = <&funnel_out_port0>;( T8 o. S7 J* t. \  Y" z
  16. };
    6 S0 u7 H0 k/ ~! G0 ]
  17. };, r* m4 H' u: z' X( J% ~* r! c( K
  18. port@1 {, s* E* z7 f: f# f( l. |
  19. reg = <0>;: T- O% e0 [! X8 i- |* }0 q
  20. etf_out_port: endpoint {5 e: w$ H( F* ?* U4 T
  21. remote-endpoint = <&tpiu_in_port>;9 G& ^  A# ]/ F
  22. };
    9 }4 Q6 u' S7 R# n
  23. };
    * l. n. s$ t2 k
  24. };
    ) ^3 \" [) ~& [# f7 l
  25. };
复制代码

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 [

  1. ! h$ }6 {6 j) y0 x$ ^1 H: X
  2. m4_rproc: m4@0 {
    ( G/ L" C* o4 @
  3. compatible = "st,stm32mp1-rproc";
    $ x% c) ]7 P. H# R8 {. y
  4. #address-cells = <1>;- C% M+ Q& f# o! A' ]
  5. #size-cells = <1>;
    . u* @& ?/ s; W1 J
  6. ranges = <0x00000000 0x38000000 0x10000>,
      g6 X+ W* P* Y1 I" `# e. t
  7. <0x30000000 0x30000000 0x60000>,
    " L( M6 b7 i# L: K, @  n
  8. <0x10000000 0x10000000 0x60000>;
    3 r" s% T* S* w5 R# p0 w+ b  ]. ?
  9. resets = <&rcc MCU_R>;
    7 p" F, C( w) B
  10. reset-names = "mcu_rst";3 C, g8 Y% l4 A$ y
  11. st,syscfg-pdds = <&pwr 0x014 0x1>;
    ! q" H, a, U5 Z# I
  12. st,syscfg-holdboot = <&rcc 0x10C 0x1>;
    " J/ V! ~' G: F$ Q+ _
  13. st,syscfg-tz = <&rcc 0x000 0x1>;
    # z5 }2 M$ f. }5 B0 v
  14. st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
    $ G$ v% ~  D" w" ?  t& v/ V
  15. status = "disabled";7 ]  _+ Q+ Z/ f$ b8 M7 D  p; ~) C
  16. m4_system_resources {
    6 {3 h! ^3 ?& g
  17. compatible = "rproc-srm-core";, w' t. n4 j$ F) J8 d
  18. status = "disabled";4 `* y- H! O6 r: d
  19. };
    - y) Q$ v8 \0 L" S2 {
  20. };
复制代码

) 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
6XU[}`QMTGZ`)EUVJJ])K.png
, 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
60122283f84bd489d66d5a1ce6fb06ef.png
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
  1. / w7 @! ?4 g* `1 c/ o
  2. & M+ h: P2 q+ ~( r2 j9 J9 v" p5 e
  3. adc: adc@48003000 {
    % L* b( Z- K; b$ }' v2 ~3 s# |
  4. compatible = "st,stm32mp1-adc-core";
    ! V) b: \5 H9 ?% F6 }
  5. reg = <0x48003000 0x400>;) b4 y% k9 E1 D  ~8 J
  6. interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,* j  J- h3 G% G) _# R
  7. <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
    ) Z  x1 j4 J8 U% v7 x, q
  8. clocks = <&rcc ADC12>, <&rcc ADC12_K>;
    " b+ {" M  s5 ]' O* F7 ?. I' }
  9. clock-names = "bus", "adc";
    ( N0 W9 V9 a( H' [
  10. interrupt-controller;2 J8 |9 }+ Y6 l; `( l5 E, N
  11. st,syscfg-vbooster = <&syscfg 0x4 0x100>;" q% Z" _7 S$ k" q6 K
  12. st,syscfg-vbooster-clr = <&syscfg 0x44 0x100>;
    2 a: |& C1 ^2 i* U7 l3 S' y! G
  13. st,syscfg-anaswvdd = <&syscfg 0x4 0x200>;
      v# K/ C. p1 E3 a5 t4 W/ v. c
  14. st,syscfg-anaswvdd-clr = <&syscfg 0x44 0x200>;" }3 Z! V& i7 z! E& y9 {
  15. #interrupt-cells = <1>;
    : C0 h/ c$ M$ ~0 V* \
  16. #address-cells = <1>;
    % U: o* ]8 t1 L- E
  17. #size-cells = <0>;1 k# m, W0 I! E: A* h) ~
  18. status = "disabled";6 \2 x) N- c8 u5 z8 t8 i
  19. adc1: adc@0 {2 W- e. u( Y$ R- i$ J
  20. compatible = "st,stm32mp1-adc";- t8 e' e$ Q  g! b- m& z/ d, x
  21. #io-channel-cells = <1>;
    5 [+ ]" ~# C, G7 r9 f
  22. reg = <0x0>;
      {) G1 w5 C3 T, A5 s
  23. interrupt-parent = <&adc>;
    ) B* h: c* K( f
  24. interrupts = <0>;" V9 `2 B, J# ~: b
  25. dmas = <&dmamux1 9 0x400 0x05>;# V, z* V4 C4 u- u1 Q
  26. dma-names = "rx";1 ~' _0 o* S6 z1 A2 p4 R" z3 }
  27. status = "disabled";
    - P* d4 t$ _5 O7 n# Y& ?
  28. };
    : x! Y/ S; c& }! ?' W
  29. adc2: adc@100 {: j! {; g' T' o8 Q
  30. compatible = "st,stm32mp1-adc";
    , o* Q( M: M8 M0 c
  31. #io-channel-cells = <1>;8 D( p3 I/ B, E- ^
  32. reg = <0x100>;
    6 R* k* A* v' v1 X
  33. interrupt-parent = <&adc>;' {* M' a0 k$ V9 O, y
  34. interrupts = <1>;. H  e1 D+ Q2 m! }' E1 H
  35. dmas = <&dmamux1 10 0x400 0x05>;' g; @' `3 l9 {9 Q5 d) E8 E
  36. dma-names = "rx";- {+ _: Y+ J1 P$ n4 k* Y
  37. /* temperature sensor */
    " v3 p1 w0 N' y6 a; }: a
  38. st,adc-channels = <12>;
    : L: J, L! l% U* b
  39. st,min-sample-time-nsecs = <10000>;+ L/ R+ b8 i3 y  n# |5 c
  40. status = "disabled";
    , y! s6 l9 v# P6 P
  41. };
    8 S& W, O+ K" ^+ h" x' v1 W' S
  42. jadc1: jadc@0 {# m6 M- Y; ~( y! _" l
  43. compatible = "st,stm32mp1-adc";
    2 C, q/ E$ z1 n$ L0 G. f9 E
  44. st,injected;+ b  b4 R0 v& e2 m2 t' m4 W# r
  45. #io-channel-cells = <1>;, O) A  t" F  ~. j
  46. reg = <0x0>;
    ) B! Q" j1 h. @- c8 C
  47. interrupt-parent = <&adc>;
    , E5 b" i" o- e- E+ b, g2 @$ z7 X
  48. interrupts = <3>;
    , f  T9 ?, s: U) U  d$ s8 U7 q* `; y! K, o
  49. status = "disabled";( q6 u# ]0 b7 Y; Z
  50. };
    4 O) N/ s! g+ i: `
  51. jadc2: jadc@100 {
    % h! r, R  [; h- [6 W7 N  K
  52. compatible = "st,stm32mp1-adc";
    0 N5 s8 u1 D0 o2 s' D
  53. st,injected;# A* d& I6 V9 V" K
  54. #io-channel-cells = <1>;6 w/ Z$ z& R4 \; Z9 K  X
  55. reg = <0x100>;
    & Y5 U, y" z8 S' e
  56. interrupt-parent = <&adc>;5 l6 k3 p, |! M3 J
  57. interrupts = <4>;+ d" Z, ^! E# _. d) t
  58. /* temperature sensor */6 @) C. J- o8 `# c. b2 [& [
  59. st,adc-channels = <12>;8 C4 E4 ]8 A0 f7 M! K- S$ ^
  60. st,min-sample-time-nsecs = <10000>;, E6 i) g% j& [# u, n0 m; d. w
  61. status = "disabled";; ]4 C; f" x0 ]
  62. };  P; _5 p$ U; t$ z! e. X8 C
  63. adc_temp: temp {4 S! o2 c" _( t+ ?
  64. compatible = "st,stm32mp1-adc-temp";" n! o* \# E; V
  65. io-channels = <&adc2 12>;
      w9 A3 S5 Q* c  m6 M9 c
  66. nvmem-cells = <&ts_cal1>, <&ts_cal2>;+ u8 O, z8 x; o- c$ E' h' m, P. F
  67. nvmem-cell-names = "ts_cal1", "ts_cal2";! ?" v) o+ u8 d' Y- O3 x0 X
  68. #io-channel-cells = <0>;
      i% ^* t1 H4 r. ]1 e& n7 A9 W
  69. #thermal-sensor-cells = <0>;
    . ?& R$ y6 V% V8 c7 E
  70. status = "disabled";# Y& w+ f) ~8 n( _0 q5 g5 H) v7 k
  71. };' K2 ?# C( M2 [. I; F
  72. };
复制代码

' i6 j# o+ n& E( n. S下面显示了ethernet控制器中断设备片段/ {& ^+ k8 G( _
! N/ S9 ]' r$ U9 B8 `
  1. ethernet0: ethernet@5800a000 {
    ! n+ m! `/ _5 G9 q4 @9 l
  2. compatible = "st,stm32mp1-dwmac", "snps,dwmac-4.20a";2 R' \" p7 e9 s) E
  3. reg = <0x5800a000 0x2000>;+ Q7 ^6 M& N% z2 @4 z9 K: u
  4. reg-names = "stmmaceth";: ]' M' K7 m2 |) m; ^5 i9 m- ^
  5. interrupts-extended = <&intc GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>,+ F* l, y- Z2 A2 s4 Y" Z, s
  6. <&intc GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>,
    3 h* U8 I$ O) a
  7. <&exti 70 1>;0 Z. ]7 C: y$ P0 f! r3 A- K
  8. interrupt-names = "macirq",
    / Z2 Q4 D# n9 p9 e9 I
  9. "eth_wake_irq",
    1 ^" U/ W4 Z* N: o" T
  10. "stm32_pwr_wakeup";
    & X9 n0 L7 ?+ g% L/ b4 g3 m
  11. clock-names = "stmmaceth",
    + g; h* a( H# x- Q, b
  12. "mac-clk-tx",# b/ h. s0 C6 f7 Z; g
  13. "mac-clk-rx",! I. l* o5 f8 F6 I' o& J9 e
  14. "ethstp";
    ! \2 d6 e& u! d% n) f& O1 ^' I
  15. clocks = <&rcc ETHMAC>,
    0 |  v3 x: @0 P3 d, P
  16. <&rcc ETHTX>,
    * P# j6 h+ M( l* ~) a( [
  17. <&rcc ETHRX>,
    " u: W5 ]" ?2 K; h, G# r# H& K
  18. <&rcc ETHSTP>;
    : G0 W- m; M3 i' w) O- M2 V
  19. st,syscon = <&syscfg 0x4>;! J3 \5 s: S$ `- p% ~  b$ \
  20. snps,mixed-burst;
    ! d, n7 h$ s9 r" W! q7 x/ z
  21. snps,pbl = <2>;
    / s- {. S) ]& y, ^# l
  22. snps,en-tx-lpi-clockgating;( d) S0 o: t. g& F5 M" \
  23. snps,axi-config = <&stmmac_axi_config_0>;
    ! f/ B! J& v" x- g
  24. snps,tso;
    ' ~2 E! O  ?2 x
  25. power-domains = <&pd_core>;: z' L/ s: [0 C: v6 }1 `7 _
  26. status = "disabled";
    , @; |( o1 }% v' m" Q$ p3 k
  27. };
复制代码
8 Y0 p  H9 x  y+ f" y
}SHWZXG8[SX1)L0Z%9`[8W5.png + 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
  1. * x+ G% ~4 c: X8 L" O
  2. aliases {
    5 c  h0 U( C3 j) N2 n
  3. ethernet0 = ðernet0;
    ! R% q2 D+ u: `) i8 W! }& l
  4. serial0 = &uart4;% ~. s4 d, V+ Q4 ]
  5. serial5 = &usart3;
    8 z8 R: w8 e% \3 _% s
  6. };
复制代码
  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( \" `
赞 收藏 评论0 发布时间:2022-10-1 19:16

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版