❤️作者主页:凉开水白菜
❤️作者简介:共同学习,互相监督,热于分享,多加讨论,一起进步!
❤️点赞 👍 收藏 ⭐再看,养成习惯
订阅的粉丝可通过PC端文末加我微信,可对文章的内容进行一对一答疑!
文章目录
- 一、什么是设备树,为什么叫设备树?
- 二、如何编译设备树?
- 三、设备树基本语法
-
- 3.1 根节点
- 3.2 子节点
- 3.3 节点名称规则
- 四、设备树基本语法与属性
-
- 4.1 reg属性
- 4.2 #address-cells和#size-cells属性
- 4.3 model属性
- 4.4 status属性
- 4.5 phandle属性
- 4.6 compatible属性
- 4.7 virtual-reg
- 五、设备树特殊节点
-
- 5.1 aliases 批量取别名属性
- 5.2 chosen节点
- 5.3 memory设备节点
- 5.4 device_type属性
- 5.5 自定义属性
- 5.6 ranges属性
- 5.7 .dtsi头文件
- 5.8 向设备树结点追加数据,&label
- 六、中断节点
-
- 6.1 #interrupt-cells 、interrupt-parent 、interrupts
- 6.2 RK瑞星微
- 6.3 imx恩智浦
- 6.4 三星平台
- 6.5 全志平台
- 6.6 其他写法:引用父节点属性
- 6.7 其他写法:级联中断控制器
- 6.8 其他写法:使用多个中断控制器
- 6.9 总结
- 七、时钟节点
-
- 7.1 生产者属性
-
- 7.1.1 #clock-cells
- 7.1.2 clock-output-names
- 7.1.3 clock-frequency
- 7.1.4 assigned-clocks和assigned-clock-rates
- 7.1.5 clock-indices
- 7.1.6 assigned-clock-parents
- 7.2 消费者属性
-
- 7.2.1 clocks和clock-names
- 八、 CPU节点
-
- 8.1 cpus节点
- 8.2 cpu-map节点
- 8.3 socket节点
- 8.4 core节点
- 8.5 thread节点
- 8.6 示例分析
- 九、GPIO节点
-
- 9.1 gpio-controller、#gpio-cells
- 9.2 gpio-ranges
- 十、Pinctrl子系统
-
- 10.1 pinctrl-names
- 10.2 pinctrl-N
- 10.3 全志平台
- 10.4 IMX平台
- 10.5 三星平台
- 10.6 RK平台
- 10.7 总结
- 十一、dtb文件
- 十二、device\_node*转化为*platform\_device
-
- 12.1 转换条件
- 12.2 代码示例
- 十三、设备树下platform\_device与platform\_driver匹配
- 十四、设备树常用of函数
-
- 14.1查找节点的 OF 函数
-
- 14.1.1 of\_find\_node\_by\_name 函数
- 14.1.2 of\_find\_node\_by\_type 函数
- 14.1.3 of\_find\_compatible\_node 函数
- 14.1.4 of\_find\_matching\_node\_and\_match 函数
- 14.1.5 of\_find\_node\_by\_path 函数
- 14.2 查找父/子节点的 OF 函数
-
- 14.2.1 of\_get\_parent 函数
- 14.2.2 of\_get\_next\_child
- 14.3 提取属性值的 OF 函数
-
- 14.3.1 of\_find\_property 函数
- 14.3.2 of\_property\_count\_elems\_of\_size 函数
- 14.3.3 of\_property\_read\_u32\_index 函数
- 14.3.4 of\_property\_read\_u32\_array 函数
- 14.3.5 of\_property\_read\_u32 函数
- 14.3.6 of\_property\_read\_string 函数
- 14.3.7 of\_n\_addr\_cells 函数
- 14.3.8 of\_n\_size\_cells 函数
- 14.4 中断获取的OF函数
-
- 14.4.1 of\_parse\_and\_map函数
- 14.5 其他常用的 OF 函数
-
- 14.5.1 of\_iomap 函数
- 14.5.2 of\_get\_address 函数
- 14.5.3 of\_translate\_address 函数
- 14.5.4 of\_address\_to\_resource 函数
- 十五、设备树与驱动结合实战
-
- 15.1 如何知道我们kernel使用的是那个设备树文件?
- 15.2 不使用设备树点亮LED
- 15.3 杂项设备获取设备树属性点亮LED
- 15.4 platform总线匹配设备树
- 15.5 获取中断资源并使用pinctrl驱动案件
-
- 15.5.1 设备树代码
- 15.5.2 驱动代码
- 十六、结尾
一、什么是设备树,为什么叫设备树?
描述硬件得文本文件,因为语法结构像树所以叫设备树
DT:device tree:设备树(arm下表示)
FDT: flattened device tree:开放设备树、扁平设备树(powerpc使用的设备树,起源的设备又称openFirmware)因为源于openFirmware所以设备树很多函数带有of字词
dts:device tree source : 设备树源码
dtsi: device tree source include:通用设备树源码
dtb:device tree blob:编译设备树源码得到得文件,镜像文件
dtc:device tree compiler:设备树编译器
通过DTC编译dts、dtsi文件编译出dtb文件
所有的设备树文件都存在与下面的路径
// arm32位架构设备树源码位置
板子kernel/arch/arm/boot/dts
// arm64位架构设备树源码位置
板子kernel/arch/arm64/boot/dts/品牌/
二、如何编译设备树?
DTC编译器的源码路径,dtc文件夹下面的dtc执行文件就是我们的设备树编译器(要保证源码已经编译过一次)
如果编译完成还是没有可能是内核版本太低没有引进设备树,或者是配置选项未勾选,可以在.confg文件中查看该选项
板子kernel/scripts/dtc
最简单的设备树源码
/dts-v1/;
/ {
};
编译器编译指令
编译设备树:dtc -I dts -0 dtb -o xxx.dtb xxx.dts
反编译设备树: dtc -I dtb -0 dts -o xxx.dts xxx.dtb
/home/book/licheepi/kernel/scripts/dtc/dtc需要根据我们内核实际路径来找
编译
/home/book/licheepi/kernel/scripts/dtc/dtc -I dts -O dtb -o test.dtb test.dts
反编译
/home/book/licheepi/kernel/scripts/dtc/dtc -I dtb -O dts -o test.dts test.dtb
遇到错误:
Error: test.dts:1.1-2 syntax error
FATAL ERROR: Unable to parse input tree
语法错误如果把/dts-v1/;设备树版本写错或者不写就会出现该问题
编译设备树第二种方法是在内核路径下执行ARCH是指定arm位数,CROSS_COMPILE指定交叉编译器
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
此时编译的是“板子kernel/arch/arm/boot/dts”该路径下的设备树文件,在该路径编译可能会出现错误,因为他编译的是所有的设备树文件
三、设备树基本语法
3.1 根节点
设备树的组成是一棵树,而根节点是树干,是必须存在树的组成还有子节点和子子节点组成
/dts-v1/;
/ {
};
3.2 子节点
以上为根节点为固定格式,下面是子节点格式
[lable:]node-name[@unit-address]{
[properties definitions]
[child nodes]
};
// 需要注意在统一级节点名称节点名称不能相同不同级节点名称可以相同
// 子节点不能与子节点名称重复,但子子节点可以和子节点相同
// 例如子节点为node1 子子节点也可以有node1但子节点不能再有一个node1
举例
/dts-v1/;
/ {
node1{
//子节点,子节点名称为node1
node1_child{
//子节点的子节点,节点名称为node1_child
};
};
node2{
node1_child{
};
};
};
3.3 节点名称规则
对于节点名称,在对节点进行命名的时候一般要体现设备的类型,比如网口一般命名为ethernet,串口一般命名为uart,对于名称一般要遵循下面的命令格式。
[标签]:<名称>[@<设备地址>]
其中,[标签]和[@<设备地址>]是可选项,<名称>是必选项。另外,这里的<设备地址>没有实际意义,只是让节点名称更加人性化,更方便阅读使用
例如
uart: serial@02288000
其中uart就是这个节点标签也叫别名,serial@2288000就是节点名称
这里如果子节点内容为空编译会产生警告,这个reg属性后面会讲到
test.dtb:警告(unit_address_vs_reg):节点/gpio@02288000有单元名称,但没有reg属性
test.dtb:警告(unit_address_format):节点/gpio@02288000单元名称不应以0开头
在设备树中命名使用字符或数字需要根据以下表格约束,如同C语言中的变量命名规则不能使用某某符号不能数字开头
四、设备树基本语法与属性
4.1 reg属性
reg属性可以用来描述地址信息,比如寄存器地址,我们的内核驱动也是通过这些属性来获取信息的,reg属性的格式如下:
reg = <address1 length1 address2 length 2 address 3 length3....>
例如
reg = <0x2200000 0x4000>
// 寄存器的起始地址0x2200000 寄存器的长度为0x4000
reg = <0x02200000 0x4000
0x2205000 0x4000
>
4.2 #address-cells和#size-cells属性
#address-cells和#size-cells用来描述子节点中的reg信息中的地址和长度信息,他们决定了用多少个 32 位的数来表示 address 和 size,需要注意如果是在根节点中插入此属性需要位于子节点之前
例如
node1{
#address-cells=<1>; //地址信息只能有一个u32记录
#size-cells=<0>; // 长度信息为0也就是没有长度
node1-child{
reg=<0>; // 地址为0无长度
};
};
node1{
#address-cells=<1>; //地址信息只能有一个u32记录
#size-cells=<1>; // 长度信息一个
node1-child{
reg=<0x02200000 0x4000>; //存储一个地址和一个长度信息
};
};
node1{
#address-cells=<2>; //地址信息两个u32
#size-cells=<0>; // 长度信息无
node1-child{
reg=<0x02200000 0x02205000>; //存储两个地址
};
};
如果属性插入在子节点之后会报错
Error: test.dts:22.5-24 Properties must precede subnodes
FATAL ERROR: Unable to parse input tree
4.3 model属性
model属性的值是一个字符串,一般用model描述一些信息,比如设备的名称名字等
例如
model = "lan8720a-eth";
model = "this is eth device";
4.4 status属性
status属性是和设备的状态有关系的,status的属性值是字符串,属性值为下面四个状态中的其中一个表示
4.5 phandle属性
phandle属性为devicetree中唯一的节点指定一个数字标识符,节点中的phandle属性,它的取值必须是唯一的(不要跟其他的phandle值一样),例如:
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
another-device-node {
interrupt-parent = <1>; // 使用phandle值为1来引用上述节点
};
注:DTS中的大多数设备树将不包含显式的phandle属性,当DTS被编译成二进制DTB格式时,DTC工具会自动插入phandle属性。
4.6 compatible属性
该属性是非常重要的一个属性,compatible是用来和驱动进行匹配的,匹配成功后会执行probe函数,推荐的格式是“制造商,型号”
例如
compatible = "i2c-oled", "i2c-display";
compatible = "fsl,mpc8641", "ns16550";
在匹配的时候会先使用第一个值进行匹配“i2c-oled”,如果没有就使用第二个值进行匹配“i2c-display”。
技巧用法:对于某个 LED,内核中可能有 A、B、C 三个驱动都支持它,那可以这样
led {
compatible = "A", "B", "C";
};
4.7 virtual-reg
虚拟reg属性指定了一个映射到设备节点的reg属性中指定的第一个物理地址的有效地址。此属性使引导程序能够为客户端程序提供已设置的虚拟到物理映射。他的值由一个32位数字组成也就是物理地址
小节示例
/dts-v1/;
/ {
model = "this is linux board";
#address-cells=<1>; // 地址信息一个
#size-cells=<1>; // 长度信息一个
node1{
//子节点,子节点名称为node1
// 这里重新赋予reg属性的address-cells限制和size-cells限制
#address-cells=<1>; // 地址信息一个
#size-cells=<0>; // 长度信息0
node1_child@2280000{
//子节点的子节点,节点名称为node1_child
reg = <0x02280000>;
};
};
node2{
node1_child{
};
};
rgb:gpio@2288000{
compatible = "rgb-led";
// 这里采用根节点赋予的address-cells限制和size-cells限制
reg = <0x02288000 0x40>;
status = "okay";
};
};
表示reg寄存器的值超过了32bit的限制
Error: test.dts:13.20-32 Value out of range for 32-bit array element
节点/node1/node1_child具有reg或range属性,但没有单元名称
test.dtb: Warning (unit_address_vs_reg): Node /node1/node1_child has a reg or ranges property, but no unit name
这个字节点的名称需要根据前面的格式来填写
// 修改前
node1_child{
//子节点的子节点,节点名称为node1_child
reg = <0x02280000>; // reg值的大小不能超过32位,否则会产生错误
};
// 修改后
node1_child@2280000{
//子节点的子节点,节点名称为node1_child
reg = <0x02280000>;
};
// 也可以取个别名,注意别名不能使用-这个字符可以test_node不能test-node
test_node:node1_child@2280000{
//子节点的子节点,节点名称为node1_child
reg = <0x02280000>;
};
五、设备树特殊节点
5.1 aliases 批量取别名属性
特殊节点aliases用来定义别名。定义别名的目的就是为了方便引用节点,当然除了使用aliases来命名别名也可以在对节点命名的时候添加标签来命名别名
例如
aliases{
mmc0 = &sdmmc0;
mmc1 = &sdmmc1;
mmc2 = &sdhc1;
serial0 = "/simple@fe000000/serial@11c500";
};
示例
/home/book/licheepi/kernel/scripts/dtc/dtc -I dts -O dtb -o test.dtb test.dts
/dts-v1/;
/ {
model = "this is linux board";
#address-cells=<1>; // 地址信息一个
#size-cells=<1>; // 长度信息一个
aliases{
gpiotest = "/nodel/node1_child@2280000";
gpiotest1 = "/gpio@2288000";
gpiotest2 = &test;
};
node1{
//子节点,子节点名称为node1
// 这里重新赋予reg属性的address-cells限制和size-cells限制
#address-cells=<1>; // 地址信息一个
#size-cells=<0>; // 长度信息0
node1_child@2280000{
//子节点的子节点,节点名称为node1_child
reg = <0x02280000>;
};
};
test:node2{
node1_child{
};
};
gpio@2288000{
compatible = "rgb-led";
// 这里采用根节点赋予的address-cells限制和size-cells限制
reg = <0x02288000 0x40>;
status = "okay";
};
};
反编译后得到
/home/book/licheepi/kernel/scripts/dtc/dtc -I dtb -O dts -o test.dts test.dtb
/dts-v1/;
/ {
model = "this is linux board";
#address-cells = <0x1>;
#size-cells = <0x1>;
aliases {
gpiotest = "/nodel/node1_child@2280000";
gpiotest1 = "/gpio@2288000";
gpiotest2 = "/node2";
};
node1 {
#address-cells = <0x1>;
#size-cells = <0x0>;
node1_child@2280000 {
reg = <0x2280000>;
};
};
node2 {
node1_child {
};
};
gpio@2288000 {
compatible = "rgb-led";
reg = <0x2288000 0x40>;
status = "okay";
};
};
5.2 chosen节点
chosen节点用来给uboot给内核传递参数,重点是bootargs参数,chose节点必须是根节点的子节点。
例如
chosen{
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};
5.3 memory设备节点
所有设备树都需要一个memory设备节点,它描述了系统的物理内存布局。如果系统有多个内存块,可以创建多个memory节点,或者可以在单个memory节点的reg属性中指定这些地址范围和内存空间大小。
例如:一个64位的系统有两块内存空间:RAM1:起始地址是0x0,地址空间是 0x80000000;RAM2:起始地址是0x10000000,地址空间也是0x80000000;同时根节点下的 #address-cells = <2>和#size-cells = <2>,这个memory节点描述为:
memory@0 {
device_type = "memory";
reg = <0x00000000 0x00000000 0x00000000 0x80000000
0x00000000 0x10000000 0x00000000 0x80000000>;
};
或者:
memory@0 {
device_type = "memory";
reg = <0x00000000 0x00000000 0x00000000 0x80000000>;
};
memory@10000000 {
device_type = "memory";
reg = <0x00000000 0x10000000 0x00000000 0x80000000>;
};
这里的#address-cells = <2>所以由两个u32表示一个地址,#size-cells = <2>表示一个reg有两个参数一个地址一个大小,
0x00000000 0x10000000 表示RAM2的起始地址
0x00000000 0x80000000表示地址空间为0x80000000
5.4 device_type属性
device_type 属性值为字符串, IEEE 1275 会用到此属性,用于描述设备的 FCode,但是设备树没有 FCode,所以此属性也被抛弃了。某些设备树文件中可以看到device_type属性,device_type的值是字符串,只用于cpu节点或者memmory节点进行描述
例如
memory@30000000{
device_type = "memory";
reg = <0x30000000 0x4000000>;
};
例如2
cpu1: cpu@1{
device_type = "cpu";
compatible = "arm, cortex-a35", "arm, arnv8";
reg = <0x0 0x1>;
};
5.5 自定义属性
设备树中规定的属性有时候并不能满足我们的需求,这时候我们可以自定义属性。
例如我们这里自定义一个管脚标号的属性pinnum
pinnum= <0 1 2 3 4 5>;
也就是因为这个特性所以我们在看有些设备树文件的时候经常不能理解有一些节点的意思的原因,从根本来说设备树只是一个文件记录文件,这个记录里面的数据或则说设备怎么去使用要不要使用都需要根据我们的驱动去决定;
5.6 ranges属性
ranges属性值可以为空或者按照 (child-bus-address,parent-bus-address,length) 格式编写的数字矩阵, ranges 是一个地址映射/转换表, ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成:
- child-bus-address:子总线地址空间的物理地址,由父节点的 #address-cells 确定此物理地址所占用的字长。
- parent-bus-address:父总线地址空间的物理地址,同样由父节点的 #address-cells 确定此物理地址所占用的字长。
- length:子地址空间的长度,由父节点的 #size-cells 确定此地址长度所占用的字长。
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0xe0000000 0x00100000>;
serial {
device_type = "serial";
compatible = "ns16550";
reg = <0x4600 0x100>;
clock-frequency = <0>;
interrupts = <0xA 0x8>;
interrupt-parent = <&ipic>;
};
};
节点 soc 定义的 ranges 属性,值为 <0x0 0xe0000000 0x00100000>,此属性值指定了一个 1024KB(0x00100000) 的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物理起始地址为 0xe0000000。
serial 是串口设备节点,
reg 属性定义了 serial 设备寄存器的起始地址为 0x4600,寄存器长度为 0x100。
经过地址转换, serial 设备可以从 0xe0004600 开始进行读写操作,0xe0004600=0x4600+0xe0000000。
5.7 .dtsi头文件
和 C 语言一样,设备树也支持头文件,设备树的头文件扩展名为 .dtsi;同时也可以像C 语言一样包含 .h头文件;在设备树中包含了一些原厂提供的头文件信息包含其路径在:kernel/scripts/dtc/include-prefixes/dt-bindings或者/kernel/include/dt-bindings他们的内容都是一样的
/include/ "sun8i-v3s.dtsi" // 包含bsp原厂提供的设备树
/include/ "dt-bindings/interrupt-controller/arm-gic.h"
注:.dtsi 文件一般用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、 IIC 等等。
5.8 向设备树结点追加数据,&label
假设需要往 i2c1 节点中添加 fxls8471 子节点,i2c1 节点是定义在 imx6ull.dtsi 文件中的,如果直接在 i2c1 节点中添加 fxls8471 相当于在其他的所有板子上都添加了 fxls8471 这个设备,其他所有使用到I.MX6ULL这颗 SOC 的板子都会引用 imx6ull.dtsi 这个文件,但是其他的板子可能并没有这个设备!所以这样操作是不行的。
按照以下方式在.dts文件中添加 fxls8471子节点即可:
&i2c1 {
/* 要追加或修改的内容 */
};
通过&label 来访问节点,然后直接在里面编写要追加或者修改的内容,不会对其他使用这颗SOC的板子造成任何影响。
六、中断节点
6.1 #interrupt-cells 、interrupt-parent 、interrupts
#interrupt-cells <2>; //中断有两个值,一般一个值是管脚一个是触发方式
interrupt-parent = <&gpio0>;
interrupts = <RK_PB5 IRQ_TYPE_LEVEL_LOW>;
interrupts第一个参数表示gpio组是gpio0,管脚号为rk_pb5也就是使用gpio0-pb5这个引脚,IRQ_TYPE_LEVEL_LOW低电平触发中断,interrupts有两个值是由#interrupt-cells规定的;
6.2 RK瑞星微
gpio控制器
gpio-controller;
表示这个gpio的参数有两个
#gpio-cells = <2>;
中断控制器
interrupt-controller;
中断有两个值,一般一个值是管脚一个是触发方式
#interrupt-cells <2>;
第一个参数表示gpio组是gpio0,管脚号为rk_pb5也就是使用gpio0-pb5这个引脚,IRQ_TYPE_LEVEL_LOW低电平触发中断,interrupts有两个值是由#interrupt-cells规定的;
interrupt-parent = <&gpio0>;
interrupts = <RK_PB5 IRQ_TYPE_LEVEL_LOW>;
中断触发方式包括:
#define IRQ_TYPE_NONE 0
#define IRQ_TYPE_EDGE_RISING 1
#define IRQ_TYPE_EDGE_FALLING 2
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
#define IRQ_TYPE_LEVEL_HIGH 4
#define IRQ_TYPE_LEVEL_LOW 8
6.3 imx恩智浦
一图为BSP原厂编写的节点同样包含gpio控制器(gic控制器)和终端控制器,#interrupt-cells <2>;同样限制为两个参数,interrupt-parent指定了我们使用的gpio组为gpio1,使用的引脚号为9,0一般表示低电平触发;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
6.4 三星平台
可以看到原厂编写的子节点只有核心的几个参数和上面俩芯片的内容相同,下方的节点中我们可以看到使用了一个宏定义判断,其实就是判断使用的屏幕类型来实现不同的定义,如果是rgb屏中断引脚为gpioc_26下降沿触发,如果是使用lvds屏幕中断引脚为gpiob_29下降沿触发;
6.5 全志平台
timer@01c20c00 {
compatible = "allwinner,sun4i-a10-timer";
reg = <0x01c20c00 0xa0>;
#interrupt-cells = <3>;
interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&osc24M>;
};
在上面例子中 interrupts 属性指定了中断控制器、中断号 及 中断触发方式,
GIC 是一个通用中断控制器(Generic Interrupt Controller),SPI 是中断类型,IRQ_TYPE_LEVEL_HIGH 表示中断触发方式。
中断类型包括:
SGI(Software-generated interrupt):范围0 - 15,软件触发的中断,一般用于核间通讯,在内核中通常叫做 IPI (Inter processer interrupt)
PPI(Private processer interrupt): 范围16 - 31,私有外设中断,只对指定的core有效
SPI(Shared processer interrupt):范围32 - 1019,共享中断,不限定特定的core
该控制器的 interrupts 属性之所以有三个参数,是因为 gic 中断控制器中 #interrupt-cells 属性值为 3
6.6 其他写法:引用父节点属性
/ {
interrupt-parent = <&gic>;
gic:interrupt-controller@228000{
compatible = "arm, gic";
interrupt-controller;
#interrupt-cells = <2>;
};
ft5x@38{
compatible = "ft5x06";
interrupt = <26 0>;
};
在这段描述中可以看到我们的ft5x@38节点中和我们之前总结的写法有差别他没有interrupt-parent属性,这是一种特殊的写法,当子节点中没有包含interrupt-parent节点来指定我们的中断控制器的时候就会去父节点中找该属性;
6.7 其他写法:级联中断控制器
/ {
interrupt-parent = <&gic>;
gic:interrupt-controller@228000{
compatible = "arm, gic";
interrupt-controller;
#interrupt-cells = <2>;
};
gic2:interrupt-controller@228200{
compatible = "arm, gic2";
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
};
ft5x@38{
compatible = "ft5x06";
interrupt = <26 0>;
};
可以看到上方描述的有两个gic节点,两个gic都具备 interrupt-controller属性说明他们都是属于中断控制器,但是在gic2节点中interrupt-parent = <&gic>;引用了gic节点,这这种写法表示gic2控制器与gic控制器是属于级联状态;在内核驱动中我们可以通过某一级中断控制器获取另外一级中断控制器这就是级联;
6.8 其他写法:使用多个中断控制器
/ {
gic:interrupt-controller@228000{
compatible = "arm, gic";
interrupt-controller;
#interrupt-cells = <2>;</