4. 设备树在Layerscape BSP中的组织方式
Layerscape 系列 SoC(如 LS1046A、LS1028A、LX2160A)的 BSP 中,设备树不是单个文件,而是一套按“SoC 公共部分 → SoC 封装/系列 → 板级差异 → 最终编译目标”逐层叠加的组织体系。理解这套分层,是后续修改网络控制器节点、调整时钟和引脚复用的前提。NXP 官方 BSP 通常把设备树源码放在 arch/arm64/boot/dts/freescale/ 目录下,核心文件类型包括 .dtsi(被包含的公共片段)和 .dts(可独立编译的板级顶层文件)。
分层结构:从 SoC 到板级
以 LS1046A 为例,典型的分层关系是:fsl-ls1046a.dtsi 描述 SoC 内部所有控制器(CPU、GIC、串口、I2C、SPI、PCIe、DPAA 相关节点等)的寄存器基址、中断号和默认时钟;fsl-ls1046a-post.dtsi 或系列级 dtsi 补充 SoC 封装层面的公共配置;板级 .dts(如 fsl-ls1046a-rdb.dts)通过 #include 引入上述 dtsi,再覆盖或新增板级专属内容,例如具体的内存容量、PHY 地址、MDIO 总线上的设备、引脚复用选择。最终编译时,一个 .dts 对应一个 .dtb,由 U-Boot 在启动时传递给内核。
这种“dtsi 提供骨架、dts 填充差异”的方式,使得同一 SoC 的多块板卡可以共享绝大部分描述,只在板级文件中体现硬件差异。修改自定义板卡时,正确做法是新建一个板级 .dts,而不是直接改动 SoC 级 dtsi,否则会影响所有引用该 dtsi 的板卡。
时钟、引脚与内存映射的节点描述
Layerscape 的时钟由 clockgen 节点统一描述,内部包含 PLL、分频器和门控,其他控制器通过 clocks = <&clockgen 2 0> 这类句柄加索引的方式引用。修改网络控制器时,如果 MAC 需要独立时钟源,就要确认其 clocks 属性指向正确的 clockgen 输出,否则会出现 MAC 无法初始化或速率异常。
引脚复用(Pin Mux)在 Layerscape 上通过 pinctrl 子节点描述。以 LS1046A 的 fsl-ls1046a.dtsi 为例,scfg 节点下挂 pinctrl,每个外设组用 fsl,pins = <...> 指定引脚编号、复用功能和电气属性。板级 dts 中通过 &fman0_rgmii_pins 之类的标签引用并覆盖,从而把某组引脚从默认功能切换到 RGMII 或 SGMII。
内存映射方面,SoC 级 dtsi 已用 reg = <0x0 0x1_0000_0000> 描述 DDR 控制器范围,板级 dts 只需在 memory 节点中声明实际容量,例如 reg = <0x0 0x8000_0000 0x0 0x8000_0000> 表示 2 GB。网络控制器节点通常包含 reg(寄存器基址)、interrupts、phy-handle、phy-mode、status 等关键属性。
网络控制器节点的描述方法
Layerscape 的以太网由 DPAA(Data Path Acceleration Architecture)管理,设备树中常见 fsl,dpaa 容器节点,下面挂 ethernet@0 到 ethernet@N 以及对应的 mdio 节点。每个 MAC 节点通过 fsl,fman-mac 或 fsl,fman-ports 与 FMan 端口关联,通过 phy-handle = <&phy0> 指向 MDIO 总线上的 PHY。板级 dts 中需要根据实际硬件填写 PHY 地址、phy-mode(如 "rgmii-id"、"sgmii"、"xgmii")以及 status = "okay" 或 "disabled"。
下面是一个自定义板卡网络节点的示例片段,用于说明板级 dts 中如何覆盖 SoC 默认值:
&fman0 {
status = "okay";
};
&fman0_rgmii_pins {
/* 将默认引脚组切换到 RGMII 模式 */
fsl,pins = <0x00000001 0x00000002 0x00000003>;
};
&mdio0 {
status = "okay";
phy0: ethernet-phy@1 {
reg = <0x1>;
compatible = "ethernet-phy-ieee802.3-c22";
};
};
&enet0 {
status = "okay";
phy-handle = <&phy0>;
phy-mode = "rgmii-id";
phy-connection-type = "rgmii-id";
};
其中 reg = <0x1> 表示 PHY 在 MDIO 总线上的地址为 1,phy-mode 决定 MAC 与 PHY 之间的接口时序。若实际硬件使用 SGMII,则应改为 "sgmii",并确认 SerDes 通道已在系列级 dtsi 中正确配置。
自定义板级 dts 的修改与编译
假设我们有一块基于 LS1046A 的自定义板卡,硬件差异是:DDR 容量 2 GB、两个 RGMII 网口、PHY 地址分别为 1 和 2、使用 I2C1 连接一颗 EEPROM。操作步骤如下。
第一步,在 arch/arm64/boot/dts/freescale/ 下新建 fsl-ls1046a-custom.dts,内容以官方 RDB 板级 dts 为模板,保留 #include "fsl-ls1046a.dtsi" 和 #include "fsl-ls1046a-post.dtsi",然后覆盖差异部分:
/dts-v1/;
#include "fsl-ls1046a.dtsi"
#include "fsl-ls1046a-post.dtsi"
/ {
model = "Custom LS1046A Board";
compatible = "fsl,ls1046a-custom", "fsl,ls1046a";
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x80000000>;
};
};
&i2c1 {
status = "okay";
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
};
&mdio0 {
status = "okay";
phy0: ethernet-phy@1 { reg = <0x1>; };
phy1: ethernet-phy@2 { reg = <0x2>; };
};
&enet0 {
status = "okay";
phy-handle = <&phy0>;
phy-mode = "rgmii-id";
};
&enet1 {
status = "okay";
phy-handle = <&phy1>;
phy-mode = "rgmii-id";
};
第二步,在 arch/arm64/boot/dts/freescale/Makefile 中追加一行 dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-custom.dtb,使编译系统识别新目标。
第三步,编译设备树。在内核源码根目录执行:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- fsl-ls1046a-custom.dtb
若编译成功,会在 arch/arm64/boot/dts/freescale/ 下生成 fsl-ls1046a-custom.dtb。可以用 dtc -I dtb -O dts fsl-ls1046a-custom.dtb 反编译回文本,检查 enet0、enet1 的 status 是否为 "okay"、phy-handle 是否指向正确节点、memory 容量是否为 2 GB。
&label 引用覆盖,而不是复制整个节点。这样 SoC 级 dtsi 更新后,板级文件仍能自动继承新属性,减少维护成本。fsl-ls1046a.dtsi 来适配单块板卡。该文件被多块板卡共享,改动会波及所有引用者,导致其他板卡启动异常。所有板级差异应集中在自定义 .dts 中。编译完成后,将 dtb 放入启动介质,在 U-Boot 中通过 fdt addr 和 fdt print /soc/ethernet@0 可以查看实际生效的节点属性。若网络接口未出现,首先检查 status 是否为 "okay",其次确认 phy-handle 指向的 PHY 节点地址与硬件 MDIO 扫描结果一致,最后核对 phy-mode 与原理图上的接口类型是否匹配。这三项是 Layerscape 网络设备树适配中最常见的排查点。