4. 开发环境搭建

设备树,说白了就是Linux内核用来描述硬件的一门"方言"。你在R-Car上做驱动移植,绕不开它。我刚开始接触设备树时也觉得这东西有点玄乎,后来调了几个板子才明白——它就是个硬件配置清单,告诉内核"你有哪些外设、它们怎么连、地址在哪"。

这一章我们聚焦四个核心点:dtsi/dts文件结构、节点与属性、中断与GPIO绑定、reg和ranges的用法。掌握了这些,你在R-Car上改设备树就不会抓瞎。

4.1 dtsi与dts:继承与覆盖

R-Car的BSP里,设备树文件分两类:.dtsi是"头文件",.dts是"板级文件"。你可以把.dtsi理解成芯片原厂提供的通用配置,.dts则是你针对具体板子做的定制。

关键区别

  • dtsi:描述SoC内部模块,比如SCIF、I2C、MMC控制器。由瑞萨提供,一般不改。
  • dts:描述板级外设,比如PHY地址、GPIO按键、LED。由板卡厂商维护。

举个例子,R-Car H3的通用文件叫r8a7795.dtsi,里面定义了所有内置外设的节点。你的板子文件my-board.dts通过#include把它拉进来,然后覆盖或追加节点。

// r8a7795.dtsi(原厂提供)
scif0: serial@e6e60000 {
    compatible = "renesas,scif-r8a7795", "renesas,scif";
    reg = <0 0xe6e60000 0 0x40>;
    interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>;
    status = "disabled";
};

// my-board.dts(板级文件)
#include "r8a7795.dtsi"

&scif0 {
    status = "okay";
    clocks = <&cpg CPG_MOD 310>;
    clock-names = "fck";
};

看到没?my-board.dts里用&scif0引用原节点,只改status和clocks。这种做法我特别喜欢——原厂升级dtsi时,你的板级改动不受影响。

我的习惯:在R-Car上做移植,我通常把板级特有的GPIO、中断、电源管理相关属性全放在dts里,dtsi只动SoC级别的定义。这样BSP升级时冲突最少。

4.2 节点与属性:设备树的"零件"

设备树的每个节点代表一个硬件设备。节点名一般用名称@地址的格式,比如i2c@e6500000。属性就是键值对,描述设备的特征。

常用的属性类型有这些:

属性类型 写法示例 说明
字符串 compatible = "renesas,rcar-gen3-i2c"; 驱动匹配的关键
32位整数 #address-cells = <1>; 地址宽度描述
整数数组 reg = <0 0xe6500000 0 0x1000>; 地址+长度对
布尔值 status = "disabled"; 存在即true
phandle clocks = <&cpg CPG_MOD 310>; 引用另一个节点

这里有个坑——compatible的优先级。内核匹配驱动时,会从左到右尝试。我在R-Car上遇到过一个问题:写"renesas,scif-r8a7795"和"renesas,scif"的顺序反了,结果驱动加载了通用版本,没走优化路径。嗯,顺序很重要。

4.3 中断与GPIO绑定

中断绑定是设备树里最容易出错的环节。R-Car使用GIC(通用中断控制器),中断描述格式是固定的。

interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>;

三个字段的含义:

  • GIC_SPI:共享外设中断,值为0。还有GIC_PPI(私有外设中断,值为1)。
  • 152:中断号,这是硬件线号,不是Linux的IRQ号。
  • IRQ_TYPE_LEVEL_HIGH:触发类型,高电平触发。

我曾经踩过的坑:在R-Car M3上移植一个触摸屏驱动,中断号写成了Linux的IRQ号(比如152+32=184),结果中断死活不触发。后来查手册才发现,设备树里必须用硬件中断号,内核会自动映射。记住:设备树里写的是GIC侧的编号,不是CPU侧的。

GPIO绑定相对简单,但要注意gpio-hog的用法:

gpio-keys {
    compatible = "gpio-keys";
    power-key {
        gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
        linux,code = <KEY_POWER>;
        label = "Power Key";
    };
};

这里&gpio6 11表示GPIO bank 6的第11脚。R-Car的GPIO bank编号从0开始,每个bank有32个引脚。我建议你在调试时先确认bank号和引脚号是否对应原理图。

4.4 reg与ranges:地址映射的"翻译官"

reg描述设备的物理地址范围,ranges描述父总线到子总线的地址映射。这两个属性在R-Car的PCIe、Display Controller等复杂外设中特别重要。

先看reg的格式:

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

地址和长度的宽度由#address-cells和#size-cells决定。R-Car gen3通常用<0 0x...>这种64位地址写法:

#address-cells = <2>;
#size-cells = <2>;

reg = <0 0xe6500000 0 0x1000>;
// 地址:0x0e6500000,长度:0x1000

ranges用于桥接不同地址空间。比如PCIe控制器:

pciec0: pcie@fe000000 {
    compatible = "renesas,pcie-r8a7795";
    reg = <0 0xfe000000 0 0x80000>;
    ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000
              0x02000000 0 0x60000000 0 0x60000000 0 0x20000000>;
    // 子地址(PCI) -> 父地址(CPU)
};

这里ranges的每一行包含:子地址类型、子地址、父地址、映射长度。第一行是IO空间,第二行是32位非预取内存空间。

我的调试技巧:当设备树里的reg和实际硬件对不上时,用devmem2直接读物理地址验证。比如devmem2 0xe6500000,如果读到全0或全F,说明地址错了或者外设没使能。

4.5 知识结构总览

下面这张图把本章的核心逻辑串起来了。你看一遍,心里就有谱了。

设备树基础核心结构 设备树文件 dtsi(SoC通用) dts(板级定制) 节点(硬件设备) 节点(板级外设) 属性(描述设备特征) compatible 驱动匹配 reg / ranges 地址映射 interrupts 中断绑定 dtsi提供SoC通用节点 → dts覆盖板级差异 → 属性描述具体硬件特征

4.6 实战要点总结

最后,我把R-Car设备树移植的几个关键点列出来,你写代码时可以对照检查:

  • compatible:优先使用SoC精确匹配,比如"renesas,scif-r8a7795",再写通用版本。
  • reg:R-Car gen3用2个cell表示地址,别写成1个cell。长度也要用2个cell。
  • interrupts:中断号是GIC硬件号,不是Linux IRQ号。触发类型用宏定义,别写数字。
  • status:原厂dtsi里外设默认disabled,板级文件里改成okay。别忘了这一步。
  • pinctrl:R-Car的引脚复用通过pinctrl节点配置,记得在设备节点里加pinctrl-0和pinctrl-names。

特别提醒:R-Car的某些外设(比如HS-USB、PCIe)在dtsi里可能定义了多个reg区域。你移植时一定要对照硬件手册,确认每个reg段对应哪个功能模块。我曾经因为漏了一个reg段,导致DMA传输失败,查了两天才找到原因。

设备树这东西,说白了就是"写对了就通,写错了就黑"。你只要把dtsi/dts的关系理清,reg和interrupts的格式搞对,R-Car上的驱动移植就成功了一半。剩下的,就是调试和验证了。