4、设备树基础语法(三):reg属性的含义与用法,ranges属性实现地址映射,中断控制器与interrupt-parent

好,咱们接着聊设备树语法。前两章我们把基础骨架搭起来了,这一章要啃几个硬骨头——regranges,还有中断相关的属性。这几个东西,说白了就是设备树里描述「硬件资源怎么分配、地址怎么转换、中断怎么连」的核心手段。

我刚开始学设备树时,最头疼的就是regranges。明明都是地址,为什么有的写两个数,有的写四个?为什么父节点和子节点的地址范围不一样?嗯,别急,咱们一个一个拆开看。

4.1 reg属性:描述设备占用的地址空间

reg属性,全称是registers,但它描述的不只是寄存器。它描述的是设备在总线上占用的地址空间——包括寄存器、内存、IO端口等。

它的格式很简单:

reg = <address1 length1 address2 length2 ...>;

每个地址-长度对,描述一段连续的地址空间。地址和长度的位数,由父节点的#address-cells#size-cells决定。

举个例子:

uart@101f0000 {
    compatible = "arm,pl011";
    reg = <0x101f0000 0x1000>;
};

这里#address-cells = <1>#size-cells = <1>。所以0x101f0000是起始地址,0x1000是长度。这个UART设备占用了从0x101f0000开始的4KB空间。

关键点reg中的地址是相对于父节点的地址空间。如果父节点是根节点,那就是物理地址。如果父节点是桥或总线,那就是总线地址。

我在项目中遇到过一个问题:一个网卡设备,reg里写了三个地址段——控制寄存器、DMA描述符区、数据缓冲区。当时调试时发现DMA总是访问错误,后来才发现是#size-cells写错了,导致长度解析不对。嗯,这种错误很难查,因为编译不报错,运行才出问题。

4.2 多地址段的情况

有些设备有多个地址空间,比如一个LCD控制器,可能有控制寄存器区和帧缓冲区区:

lcdc@f8000000 {
    compatible = "vendor,lcdc";
    reg = <0xf8000000 0x1000    /* 控制寄存器 */
           0xf8001000 0x800000>; /* 帧缓冲区 */
};

驱动里通过of_iomap()platform_get_resource()按索引获取。索引0是控制寄存器,索引1是帧缓冲区。

个人习惯:我写设备树时,喜欢在reg后面加注释,标明每个段是什么用途。这样半年后自己回来看,不用再翻手册。

4.3 ranges属性:实现地址映射

ranges属性,是设备树里最容易让人懵圈的东西之一。它的作用,说白了就是「地址翻译」——把子节点的地址空间,映射到父节点的地址空间。

格式如下:

ranges = <child-bus-address parent-bus-address length>;

每个三元组表示:子总线上的child-bus-address,对应父总线上的parent-bus-address,映射长度为length

举个例子,一个PCIe桥:

pcie@40000000 {
    #address-cells = <3>;
    #size-cells = <2>;
    ranges = <0x02000000 0x00 0x40000000
              0x00 0x40000000
              0x00 0x20000000>;
};

这里子地址是PCIe的配置空间地址(0x02000000类型),父地址是CPU物理地址0x40000000,映射长度0x20000000(512MB)。

如果ranges属性为空(ranges;),表示子地址和父地址是1:1映射。如果ranges不存在,表示子地址空间和父地址空间没有直接映射关系——这种情况常见于非内存映射的设备,比如I2C总线上的设备。

我曾经踩过的坑:有一次写一个FPGA桥接设备,ranges写错了映射关系,导致驱动里读到的寄存器地址全是错的。查了两天才发现,是#address-cells#size-cells的位数没对齐。记住:ranges里每个字段的位数,由父节点和子节点的#address-cells共同决定,千万别搞混。

4.4 中断控制器与interrupt-parent

中断是嵌入式系统里最常用的异步机制。设备树里描述中断,需要三个要素:中断控制器、中断连接关系、中断触发类型。

中断控制器:就是能接收中断信号并转发给CPU的硬件模块。比如GIC(通用中断控制器)、GPIO控制器等。它在设备树里用interrupt-controller属性标记:

gic: interrupt-controller@f0001000 {
    compatible = "arm,gic-400";
    interrupt-controller;
    #interrupt-cells = <3>;
    reg = <0xf0001000 0x1000>;
};

#interrupt-cells表示每个中断描述需要几个32位整数。GIC-400需要3个:中断类型(0=SPI,1=PPI)、中断号、触发类型(1=上升沿,4=高电平)。

interrupt-parent:指定设备连接到哪个中断控制器。如果整个系统只有一个中断控制器,可以在根节点设置interrupt-parent,所有子节点默认继承。

/ {
    interrupt-parent = <&gic>;
    
    uart@101f0000 {
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000>;
        interrupts = <0 33 4>;  /* SPI 33, 高电平触发 */
    };
};

如果设备连接到多个中断控制器,或者需要覆盖父节点的设置,就在设备节点里显式指定interrupt-parent

核心逻辑interrupts属性里的数值含义,完全由interrupt-parent指向的中断控制器的#interrupt-cells决定。不同中断控制器,同样的数值可能代表完全不同的中断。

4.5 中断级联与多级中断

实际系统中,经常有多个中断控制器级联。比如一个GPIO控制器作为二级中断控制器,挂在GIC上:

gpio2: gpio@f0002000 {
    compatible = "vendor,gpio-controller";
    reg = <0xf0002000 0x1000>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    interrupt-parent = <&gic>;
    interrupts = <0 45 4>;  /* GPIO控制器本身的中断 */
};

然后一个外设挂在GPIO2上:

keypad {
    compatible = "vendor,keypad";
    interrupt-parent = <&gpio2>;
    interrupts = <5 2>;  /* GPIO2的第5号引脚,下降沿触发 */
};

这里keypad的中断先到GPIO2,GPIO2再通过自己的中断线通知GIC。驱动里处理时,需要先处理GPIO2的中断,再找到具体是哪个引脚触发的。

我记得有一次调试一个触摸屏,中断一直不触发。查了半天,发现是interrupt-parent写错了,指向了GIC而不是GPIO控制器。内核在解析中断时,用GIC的#interrupt-cells去解析GPIO的中断描述,结果当然对不上。

4.6 本章知识体系

下面这张图,把本章的核心逻辑串起来了:

设备树核心属性逻辑图 设备节点 reg 属性 描述设备占用的 地址空间(寄存器/内存) ranges 属性 子地址 → 父地址 地址映射/翻译 中断相关属性 interrupt-parent 指定中断控制器 interrupts 中断号 + 触发类型 interrupt-controller 标记中断控制器节点 #address-cells / #size-cells 决定地址位数 #interrupt-cells 决定中断描述格式 核心原则 子节点属性含义由父节点属性决定(#address-cells / #interrupt-cells) 中断连接关系由 interrupt-parent 显式指定或继承

4.7 小结

这一章的内容,说实话是设备树里最绕的部分。我自己的经验是:先理解地址空间的概念,再理解中断的层级关系。不要试图一次性记住所有格式,而是记住「每个属性的含义由谁决定」这个核心原则。

你想想看,reg里的地址是几位,由父节点的#address-cells决定;interrupts里的数值怎么解释,由interrupt-parent指向的节点的#interrupt-cells决定。这个「子节点属性含义由父节点决定」的规则,贯穿了整个设备树的设计思想。

嗯,下一章我们会把这些属性组合起来,写一个完整的设备树实例。到时候你会发现,理解了这些基础语法,写设备树其实就是搭积木。


公众号:蓝海资料掘金营,微信deep3321