linux设备驱动(24)dts补充

1 可以转换成设备的节点

首先,要说明的是设备树最初是为了解决大量重复的platform_device在mach-xx目录下,但在实现的过程重,设备树体现的是一个电路板的信息,添加了bootargs,memory,clock,interrupt等非platform_device的节点,这样就不能对所有的device_node转换成platform_device。memory,interrupt等虽然是硬件,但是不是platform_device。

内核的Documentation/devicetree/usage-model.txt目录中,有设备树使用的一个例子:

 1 /{
 2     compatible = "nvidia,harmony", "nvidia,tegra20";
 3     #address-cells = <1>;
 4     #size-cells = <1>;
 5     interrupt-parent = <&intc>;
 6 
 7     chosen { };
 8     aliases { };
 9 
10     memory {
11         device_type = "memory";
12         reg = <0x00000000 0x40000000>;
13     };
14 
15     soc {
16         compatible = "nvidia,tegra20-soc", "simple-bus";
17         #address-cells = <1>;
18         #size-cells = <1>;
19         ranges;
20 
21         intc: interrupt-controller@50041000 {
22             compatible = "nvidia,tegra20-gic";
23             interrupt-controller;
24             #interrupt-cells = <1>;
25             reg = <0x50041000 0x1000>, < 0x50040100 0x0100 >;
26         };
27 
28         serial@70006300 {
29             compatible = "nvidia,tegra20-uart";
30             reg = <0x70006300 0x100>;
31             interrupts = <122>;
32         };
33 
34         i2s1: i2s@70002800 {
35             compatible = "nvidia,tegra20-i2s";
36             reg = <0x70002800 0x100>;
37             interrupts = <77>;
38             codec = <&wm8903>;
39         };
40 
41         i2c@7000c000 {
42             compatible = "nvidia,tegra20-i2c";
43             #address-cells = <1>;
44             #size-cells = <0>;
45             reg = <0x7000c000 0x100>;
46             interrupts = <70>;
47 
48             wm8903: codec@1a {
49                 compatible = "wlf,wm8903";
50                 reg = <0x1a>;
51                 interrupts = <347>;
52             };
53         };
54     };
55 
56     sound {
57         compatible = "nvidia,harmony-sound";
58         i2s-controller = <&i2s1>;
59         i2s-codec = <&wm8903>;
60     };
61 };

基本的我们看到,根节点肯定不是一个设备。aliases,aliases ,memory 也都不是一个platform_device。
以上面的i2c控制器的节点为例,一般来说i2c控制器下的子节点都是外设,但例子中的i2c节点不是根节点子节点。而是跟节点的子子节点。这样就不复合根节点下的有compatible 属性的节点,一般是子节点的特点了吗?
这里要说的是,对于这种情况,设备树做了特殊处理。即一般在一个节点的compatible 属性中函数一下的几个bus标识的话,它的子节点都会转换成平台设备platform_device。

1 const struct of_device_id of_default_bus_match_table[] = {
2     { .compatible = "simple-bus", },
3     { .compatible = "simple-mfd", },
4     { .compatible = "isa", },
5 #ifdef CONFIG_ARM_AMBA
6     { .compatible = "arm,amba-bus", },
7 #endif /* CONFIG_ARM_AMBA */
8     {} /* Empty terminated list */
9 };

既然i2c控制器被注册为了一个子节点,那么挂在i2c下面的设备呢?
通用行为是由父设备驱动程序在驱动程序.probe()时注册子设备。因此,i2c总线设备驱动程序将为每个子节点注册i2c_client,SPI总线驱动程序将注册其spi_device子节点,对于其他bus_types也是如此。
总结:
(1)除根节点以外,要能能被转换为platform_device,节点必须有compatible 属性。
(2)根节点下含有compatible 属性的子节点,一般被转换成platform_device?。
(3)函有compatible 属性的节点属性中函有基本的bus,其子节点中若也函有compatible 属性也会被转换成platform_device 。

2 device node到platform device的转换

在2.6的内核的时候,我们通常是在mach-xxx.c的一个文件中定义一个平台设备。

接下来说一下如何确定一个device_node是不是能被转换成一个platform_device。就是通过下面这个函数来分析的。

 1 static const struct of_device_id reserved_mem_matches[] = {
 2     { .compatible = "qcom,rmtfs-mem" },
 3     { .compatible = "qcom,cmd-db" },
 4     { .compatible = "ramoops" },
 5     {}
 6 };
 7  
 8 static int __init of_platform_default_populate_init(void)
 9 {
10     struct device_node *node;
11  
12     if (!of_have_populated_dt())
13         return -ENODEV;
14  
15     /*
16      * Handle certain compatibles explicitly, since we don't want to create
17      * platform_devices for every node in /reserved-memory with a
18      * "compatible",
19      */
20     for_each_matching_node(node, reserved_mem_matches)
21         of_platform_device_create(node, NULL, NULL);
22  
23     node = of_find_node_by_path("/firmware");
24     if (node) {
25         of_platform_populate(node, NULL, NULL, NULL);
26         of_node_put(node);
27     }
28  
29     /* Populate everything else. */
30     of_platform_default_populate(NULL, NULL, NULL);
31  
32     return 0;
33 }
34 arch_initcall_sync(of_platform_default_populate_init);

arch_initcall_sync函数是内核分阶段调用的一个例子。关于分阶段调用在内核中很常见,比如我们常用的module_init就是在某一阶段调用的。

看函数of_platform_default_populate_init

 1 static inline bool of_have_populated_dt(void)
 2 {
 3     return of_root != NULL;
 4 }
 5  
 6 static int __init of_platform_default_populate_init(void)
 7 {
 8     struct device_node *node;
 9  
10     if (!of_have_populated_dt())        //判断根节点是否存在,存在才有继续分析的意义
11         return -ENODEV;
12  
13     /*
14      * Handle certain compatibles explicitly, since we don't want to create
15      * platform_devices for every node in /reserved-memory with a
16      * "compatible",   //处理高通对保留的内存节点的处理
17      */
18     for_each_matching_node(node, reserved_mem_matches)
19         of_platform_device_create(node, NULL, NULL);
20  
21     node = of_find_node_by_path("/firmware");    //获取根节点下的firmware
22     if (node) {
23         of_platform_populate(node, NULL, NULL, NULL);    //处理这个节点下的节点
24         of_node_put(node);
25     }
26  
27     /* Populate everything else.处理其他所有的节点*/
28     of_platform_default_populate(NULL, NULL, NULL);
29  
30     return 0;
31 }

函数of_platform_default_populate

 1 const struct of_device_id of_default_bus_match_table[] = {
 2     { .compatible = "simple-bus", },
 3     { .compatible = "simple-mfd", },
 4     { .compatible = "isa", },
 5 #ifdef CONFIG_ARM_AMBA
 6     { .compatible = "arm,amba-bus", },
 7 #endif /* CONFIG_ARM_AMBA */
 8     {} /* Empty terminated list */
 9 };
10  
11 //of_platform_default_populate函数的参数都是NULL
12 int of_platform_default_populate(struct device_node *root,
13                  const struct of_dev_auxdata *lookup,
14                  struct device *parent)
15 {
16     //这里是检测根节点下的所有节点,有of_default_bus_match_table表代表是cpu能访问的总线,即下面的子节点可以被转换成platform_device
17     return of_platform_populate(root, of_default_bus_match_table, lookup,
18                     parent);
19 }

函数of_platform_populate的详解见linux设备驱动(20)设备树详解4-kernel解析dts

3 设备树命名规则

DeviceTree发源于PowerPC架构,目的是为了为了消除代码中冗余的各种device注册代码而产生的,现在已经成为了linux的通用机制。设备树是一种用树状结构的方式来描述硬件信息,由Node(节点)、Property(属性)两种元素组成:

Node节点。在DTS中使用一对花括号”node-name{}”来定义;
Property属性。在Node中使用”property-name=value”字符串来定义;

3.1 设备树结构与规则

3.1.1 节点名(Node Names):node-name@unit-address

节点名称组件指定节点的名称。它的长度应为1到31个字符,且仅由下列总的字符集中的字符组成:

数字:0-9;大小写字母A(a)-Z(z);逗号 "," ;句号“.” ;下划线"_" ;加减号

3.1.2 路径名(Path Names)

通过指定从根节点到子节点再到所需节点的完整路径,可以唯一标识设备树中的一个节点。规则如下:

/node-name-1/node-name-2/node-name-N

其中,/是根节点路径。

3.1.3 属性(Properties)

设备树中的每个节点都有描述其节点特征的属性,属性由名称和值组成。

属性名:属性名称1到31个字符的字符串,字符规则如下表:

属性值:属性值是一个零个或多个字节的数组,其中包含与该属性关联的信息。

属性值详解:linux设备驱动(18)设备树详解2-基础知识

3.2 标准属性(Standard Properties)

3.2.1 compatible属性

Property name: compatible
Value type: <stringlist>

compatible属性值包含一个或多个字符串,这些字符串定义了设备的明确的编程模型。该字符串列表被用来匹配设备驱动程序。该属性的值由空终止字符串的串联列表组成,从最具体到最一般。它允许单个设备驱动程序与多个设备进行匹配。“compatible”属性通常用来device和driver的适配,推荐的格式为”manufacturer,model”。其中manufacturer是描述厂商名称的字符串,model值用于指定设备型号。

例如:

compatible = "fsl,mpc8641", "ns16550";

例中,操作系统将首先尝试找到支持fsl,mpc8641的设备驱动程序。如果找不到驱动程序,它将尝试找到支持更通用的ns16550设备类型的驱动程序。

3.2.2 phandle属性

Property name: phandle
Value type: <u32>

“phandle”属性通用一个唯一的id来标识一个Node,在property可以使用这个id来引用Node。

例如:

1 pic@10000000 {
2     phandle = <1>;
3     interrupt-controller;
4 };
5 
6 another-device-node {
7     interrupt-parent = <1>;
8 };

定义phandle值为1另一个设备节点可以使用一个虚拟值1引用pic节点。

3.2.3 status属性

Property name: status
Value type: <string>

status属性指示设备的运行状态,下面列出了各状态值的定义。

"okay":指示设备正在运行

"disabled":表示该设备当前无法运行,但是将来可能会运行(比如未插入或关闭的某些设备)。

"reserved":表示设备可运行,但不应使用。通常,它用于由另一个软件组件(例如平台固件)控制的设备。

"fail":表示设备无法运行。在设备中检测到严重错误,且不进行修复就无法运行。

"fail-sss":表示设备无法运行。在设备中检测到严重错误,且如果不进行修复就无法开始运行。sss的部分指定设备,并指示侦测到的错误情况。

3.2.4 #address-cells, #size-cells

Property name: #address-cells, #size-cells
Value type: <u32>

#address-cells和 #size-cells属性可作用于设备树结构中具有子节点的任何设备节点,并定义子节点设备的该属性规则。#address-cells属性定义<u32>用于在子节点的 reg 属性中对地址字段进行编码的单元格数。 #size-cells属性定义<u32>用于在子节点的 reg 属性中对大小字段进行编码的单元数。#address-cells和#size-cells属性不会从设备树中的父节点继承。

例如:

 1 soc {
 2     #address-cells = <1>;
 3     #size-cells = <1>;
 4     serial@4600 {
 5         compatible = "ns16550";
 6         reg = <0x4600 0x100>;
 7         clock-frequency = <0>;
 8         interrupts = <0xA 0x8>;
 9         interrupt-parent = <&ipic>;
10     };
11 };

例中,soc 节点的#address-cells数和#size-cells属性都设置为 1。此设置指定需要一个单元来表示地址,并且需要一个单元来表示此节点的子节点大小。串行设备 reg 属性必须遵循父 (soc) 节点中的此规范,地址由1个单元格 (0x4600) 表示,大小由1个单元格 (0x100) 表示。

3.2.5 reg 属性

Property name: reg
Property value: <prop-encoded-array>

reg 属性描述设备资源在其父总线定义的地址空间内的地址。最常见的情况是,表示内存映射 IO 寄存器块的偏移量和长度,但在某些总线类型上可能具有不同的含义。根节点定义的地址空间中的地址是 CPU 的真实地址。它的<prop-encoded-array>值,由任意数目的地址和长度对组成[地址长度]。<u32>指定特定于总线上的地址和长度所需的单元数,由设备节点的父节点中#address-cells和#size-cells属性指定。如果父节点#size-cells值 为0,则应省略 reg 值中的长度字段。

例如:

假设片上系统内的设备有两个寄存器块,SOC 中的偏移 0x3000 的 32 字节块和偏移 0xFE00 的 256 字节块。reg 属性的编码方式如下(假设#address-cells和#size-cells值为 1)

reg = <0x3000 0x20 0xFE00 0x100>;

3.2.6 ranges属性

Property name: ranges
Value type: <empty> or <prop-encoded-array>(编码为任意数量的(子总线地址、父总线地址、长度)三阵列)

ranges提供了一种在总线的地址空间(子地址空间)和总线节点的父节点(父地址空间)的地址空间之间定义映射或转换的方法。

child-bus-address是子总线地址空间内的物理地址。表示地址的单元数与总线相关,可以从该节点的#address-cells(显示范围属性的节点)确定。

parent-bus-address是父总线地址空间中的物理地址。表示父地址的单元数与总线相关,可以从定义父地址空间的节点的#address-cells属性确定。

length指定子地址空间中范围的大小。表示大小的单元数可以从该节点的#size-cells(显示范围属性的节点)确定。

如果属性使用<empty>值定义,则指定父地址空间和子地址空间相同,并且不需要地址转换。如果该属性不存在于总线节点中,则假定节点的子节点和父地址空间之间不存在映射。

例如:

 1 soc {
 2     compatible = "simple-bus";
 3     #address-cells = <1>;
 4     #size-cells = <1>;
 5     ranges = <0x0 0xe0000000 0x00100000>;
 6     serial@4600 {
 7         device_type = "serial";
 8         compatible = "ns16550";
 9         reg = <0x4600 0x100>;
10         clock-frequency = <0>;
11         interrupts = <0xA 0x8>;
12         interrupt-parent = <&ipic>;
13     };
14 };

ranges = <0x0 0xe0000000 0x00100000>;

此属性值指定在1024 KB 地址空间范围,以物理 0x0 地址寻址的子节点映射到物理父地址 0xe000000 ,可以映射到父地址的长度为0x00100000。通过此映射,串行设备节点在父地址 的位置0xe0004600,长度0x100范围内。

为了对ranges属性更深入的理解,后面有详细实例详解。

3.3 中断属性

在设备树中,存在逻辑中断树,该逻辑中断树表示平台硬件中中断的层次结构和路由。在设备树中,使用interruptparent属性表示中断源与中断控制器的物理连线。产生中断的设备节点包含一个中断父级属性,该属性具有一个虚拟值,该值指向该设备的中断所路由到的设备(通常是中断控制器)。每个中断产生设备都包含一个中断属性,该属性的值描述该设备的一个或多个中断源。每个源都在中断说明符填充相关信息。中断说明符的格式和含义是由中断域所确定的,即它取决于中断域根节点上节点的属性。中断域的根使用#interrupt-cells属性定义中断说明符进行编码所需值的数量。如果产生中断的设备不具有父中断属性,则假定其父中断为其设备树父节点。

和中断相关的node可以分成3种:

1 “Interrupt Generating Devices”,中断发生设备,这种设备可以发生中断。
2 “Interrupt Controllers”,中断控制器,处理中断。
3 “Interrupt Nexus”,中断联结,路由中断给中断控制器。

3.3.1 中断产生设备属性(Properties for Interrupt Generating Devices)

Property:interrupts
Value type: <prop-encoded-array>(编码为任意数量的中断说明符)

interrupts属性的值由任意数量的中断说明符组成。中断说明符的格式由绑定的中断域根定义。“interrupts”属性定义了设备的中断解析,定义设备生成的一个或多个中断。根据其”interrupt-parent”node中定义的“#interrupt-cells”来解析。比如#interrupt-cells=2,表示用2个单元解析“interrupts”属性。

例如:

interrupts = <0xA 8>;

在open PIC–compatible中断域中,中断说明符的通用定义由两个单元组成;第二个单元由两个单元组成。中断号和级别/检测信息。例子中定义了一个中断说明符,中断号为0xA,级别/检测编码为8。

Property: interrupt-parent
Value type: <phandle>

“interrupt-parent”属性用来制定当前设备的Interrupt Controllers/Interrupt Nexus,phandle指向对应的node。

3.3.2 中断控制器属性(Properties for Interrupt Controllers)

Property: #interrupt-cells
Value type: <u32>

“#interrupt-cells”属性用来规定连接到该中断控制器上的设备节点里"interrupts"属性的解析长度(所需单元数)。

Property: interrupt-controller
Value type: <empty>

“interrupt-controller”属性用来声明当前node为中断控制器。

3.3.3 中断联结属性(Interrupt Nexus Properties)

Property: #interrupt-cells
Value type: <u32>

“#interrupt-cells”属性用来规定连接到该中断控制器上的设备的”interrupts”属性的解析长度。

Property: interrupt-map
Value type: <prop-encoded-array>(编码为任意数量的中断映射项)

“interrupt-map”属性用来描述interrupt nexus设备对中断的路由。解析格式为5个元素序列:

child unit address:被映射的子节点的单元地址。该属性的cells长度由其所在的总线节点的#address-cells属性描述。

child interrupt specifier:被映射的子节点的中断说明符。该节点所需的单元数由#interrupt-cells属性(包含中断映射属性的关系节点)指定。

interrupt-parent:单个值指向子域要映射到的父中断。

parent unit address:父中断域中的单元地址。指定此地址所需的单元数由父中断域指向节点的#address-cells属性描述。

parent interrupt specifier:父域中的中断说明符。所需的单元数由父中断域指向节点的#interrupt-cells属性描述。

例如:

 1 soc {
 2     compatible = "simple-bus";
 3     #address-cells = <1>;
 4     #size-cells = <1>;
 5     open-pic {
 6         clock-frequency = <0>;
 7         interrupt-controller;
 8         #address-cells = <0>;
 9         #interrupt-cells = <2>;
10     };
11     pci {
12         #interrupt-cells = <1>;
13         #size-cells = <2>;
14         #address-cells = <3>;
15         interrupt-map-mask = <0xf800 0 0 7>;
16         interrupt-map = <
17         /* IDSEL 0x11 - PCI slot 1 */
18         0x8800 0 0 1 &open-pic 2 1 /* INTA */
19         0x8800 0 0 2 &open-pic 3 1 /* INTB */
20         0x8800 0 0 3 &open-pic 4 1 /* INTC */
21         0x8800 0 0 4 &open-pic 1 1 /* INTD */
22         /* IDSEL 0x12 - PCI slot 2 */
23         0x9000 0 0 1 &open-pic 3 1 /* INTA */
24         0x9000 0 0 2 &open-pic 4 1 /* INTB */
25         0x9000 0 0 3 &open-pic 1 1 /* INTC */
26         0x9000 0 0 4 &open-pic 2 1 /* INTD */
27         >;
28     };
29 };

中断映射表的第一行指定插槽1的INTA的映射:

1 child unit address: 0x8800 0 0
2 child interrupt specifier: 1
3 interrupt parent: &open-pic
4 parent unit address: (empty because #address-cells = <0> in the open-pic node)
5 parent interrupt specifier: 2 1

4 ranges的实例详解

在设备树中有时会看到ranges属性,这个ranges属性可以达到什么效果?下面结合一个实际的例子来看看。

4.1 设备树

下面是我们将要实验的设备树的例子:

  1 / {
  2     #address-cells = <1>;
  3     #size-cells = <1>;
  4 
  5     demo_level0 {
  6         compatible = "simple-bus";
  7         ranges = <0x0 0x3000000 0x3000>;
  8         #address-cells = <1>;
  9         #size-cells = <1>;
 10 
 11         range@0 {
 12             compatible = "range";
 13             reg = <0x100 0x200>;
 14             reg-names = "range0";
 15         };
 16 
 17         range@1 {
 18             compatible = "range";
 19             reg = <0x300 0x200>;
 20             reg-names = "range1";
 21         };
 22 
 23         range@2 {
 24             compatible = "range";
 25             reg = <0x600 0x200>;
 26             reg-names = "range2";
 27         };
 28 
 29         demo_level1 {
 30             compatible = "simple-bus";
 31             ranges = <0x0 0x1000 0x1000>;
 32             #address-cells = <1>;
 33             #size-cells = <1>;
 34 
 35             range@3 {
 36                 compatible = "range";
 37                 reg = <0x100 0x200>;
 38                 reg-names = "range3";
 39             };
 40 
 41             demo_level1-1 {
 42                 compatible = "simple-bus";
 43                 ranges = <0x0 0x300 0x500>;
 44                 #address-cells = <1>;
 45                 #size-cells = <1>;
 46 
 47                 range@4 {
 48                     compatible = "range";
 49                     reg = <0x100 0x200>;
 50                     reg-names = "range4";
 51                 };
 52 
 53                 range@5 {
 54                     compatible = "range";
 55                     reg = <0x300 0x100>;
 56                     reg-names = "range5";
 57                 };
 58 
 59                 demo_level1-1-1 {
 60                     compatible = "simple-bus";
 61                     ranges = <0x0 0x400 0x100>;
 62                     #address-cells = <1>;
 63                     #size-cells = <1>;
 64 
 65                     range@6 {
 66                         compatible = "range";
 67                         reg = <0x50 0x30>;
 68                         reg-names = "range6";
 69                     };
 70 
 71                     demo_level1-1-1-1 {
 72                         compatible = "simple-bus";
 73                         ranges = <0x0 0x20 0x20>;
 74                         #address-cells = <1>;
 75                         #size-cells = <1>;
 76 
 77                         range@7 {
 78                             compatible = "range";
 79                             reg = <0x10 0x10>;
 80                             reg-names = "range7";
 81                         };
 82 
 83                         range@8 {
 84                             compatible = "range";
 85                             reg = <0x0 0x10>;
 86                             reg-names = "range8";
 87                         };
 88                     };
 89                 };
 90             };
 91 
 92             range@9 {
 93                 compatible = "range";
 94                 reg = <0x800 0x50>;
 95                 reg-names = "range9";
 96             };
 97 
 98             demo_level1-2 {
 99                 compatible = "simple-bus";
100                 ranges = <0x0 0x900 0x100>;
101                 #address-cells = <1>;
102                 #size-cells = <1>;
103 
104                 range@10 {
105                     compatible = "range";
106                     reg = <0x0 0x50>;
107                     reg-names = "range10";
108                 };
109 
110                 demo_level1-2-1 {
111                     compatible = "simple-bus";
112                     ranges;
113                     #address-cells = <1>;
114                     #size-cells = <1>;
115 
116                     range@11 {
117                         compatible = "range";
118                         reg = <0x50 0x30>;
119                         reg-names = "range11";
120                     };
121                 };
122             };
123         };
124 
125         demo_level2 {
126             compatible = "simple-bus";
127             ranges;
128             #address-cells = <1>;
129             #size-cells = <1>;
130 
131             range@12 {
132                 compatible = "range";
133                 reg = <0x2000 0x1000>;
134                 reg-names = "range12";
135             };
136         };
137     }
138 };

4.2 驱动

下面是一个简单的驱动,功能很简单,只是在probe函数中将memory资源的start和(end+1)打印出来.。demo_range.c:
 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/platform_device.h>
 4 #include <linux/of.h>
 5 
 6 static int demo_range_probe(struct platform_device *pdev)
 7 {
 8     struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 9 
10     printk(KERN_INFO "%s start: 0x%x, end: 0x%x\n",
11         res->name, res->start, res->end + 1);
12 
13     return 0;
14 }
15 
16 static int demo_range_remove(struct platform_device *pdev)
17 {
18     return 0;
19 }
20 
21 static const struct of_device_id demo_range_of_match[]  = {
22     { .compatible = "range"},
23     {},
24 };
25 
26 static struct platform_driver demo_range_driver = {
27     .driver = {
28         .name = "demo_range",
29         .owner = THIS_MODULE,
30         .of_match_table = demo_range_of_match,
31     },
32     .probe = demo_range_probe,
33     .remove = demo_range_remove,
34 };
35 module_platform_driver(demo_range_driver);
36 
37 MODULE_LICENSE("GPL v2");

 在驱动中会获得memory资源,然后将start和(end+1)打印出来,之所以这里用(end+1),仅仅是为了便于理解下面的kernel log。

4.3 验证

编译驱动,然后加载,可以看到下面的打印信息:

[root@vexpress mnt]# insmod demo_range.ko 
[  382.940402] range0 start: 0x3000100, end: 0x3000300
[  382.940697] range1 start: 0x3000300, end: 0x3000500
[  382.941448] range2 start: 0x3000600, end: 0x3000800
[  382.941657] range3 start: 0x3001100, end: 0x3001300
[  382.941855] range4 start: 0x3001400, end: 0x3001600
[  382.942057] range5 start: 0x3001600, end: 0x3001700
[  382.942262] range6 start: 0x3001750, end: 0x3001780
[  382.942470] range7 start: 0x3001730, end: 0x3001740
[  382.942684] range8 start: 0x3001720, end: 0x3001730
[  382.949796] range9 start: 0x3001800, end: 0x3001850
[  382.950023] range10 start: 0x3001900, end: 0x3001950
[  382.950603] range11 start: 0x3001950, end: 0x3001980
[  382.950805] range12 start: 0x3002000, end: 0x3003000

总结:

(1)ranges属性值的格式 <local地址, parent地址, size>, 表示将local地址向parent地址的转换。

比如对于#address-cells和#size-cells都为1的话,以<0x0  0x10 0x20>为例,表示将local的从0x0~(0x0 + 0x20)的地址空间映射到parent的0x10~(0x10 + 0x20)

其中,local地址的个数取决于当前含有ranges属性的节点的#address-cells属性的值,size取决于当前含有ranges属性的节点的#size-cells属性的值。

而parent地址的个数取决于当前含有ranges属性的节点的parent节点的#address-cells的值。

(2)对于含有ranges属性的节点的子节点来说,其reg都是基于local地址的。

(3)ranges属性值为空的话,表示1:1映射。

(4)对于没有ranges属性的节点,代表不是memory map区域。

对照上面的log理解下面的框图:

参考博文:https://blog.csdn.net/qq_16777851/java/article/details/87870567

https://zhuanlan.zhihu.com/p/143167176

https://www.cnblogs.com/pengdonglin137/p/7401049.html

posted @ 2020-07-05 16:40  Action_er  阅读(943)  评论(0编辑  收藏  举报