先分析一个完整的设备树,是怎么表达各种外设信息的。
以imux6ull开发板为例进行说明。
这个文件里就一个设备信息
才这么点内容,是不是出问题了?当然不是,我们知道dts文件是可包含的,所以,最终形成的一个完整文件就是把各文件给整合起来,以根结点为起始的一个文件。 根据前面了解到的知识,这里应该是对已有外设的信息的追加,不过我们暂时不管这里,而是根据包含关系找到起始的地方,然后顺着往下看。
梳理后,有如下包含关系:
imx6ull-14x14-evk-emmc.dts包含了imx6ull-14x14-evk.dts;
imx6ull-14x14-evk.dts又包含了imx6ull.dtsi;
imx6ull.dtsi又包含了skeleton.dtsi;
skeleton:骨骼,骨架;框架;梗概,纲要
再往上就没有了
skeleton.dtsi
先看看skeleton.dtsi文件
skeleton.dtsi 文件是用于设备树的通用头文件,通常被包含在其他设备树源文件(.dts 或 .dtsi)中。它定义了一些基本节点和属性,以满足设备树的基本要求。以下是对 skeleton.dtsi 文件内容的详细解释:
根节点
/ {:这是设备树的根节点,每个设备树文件有且仅有一个根节点。在包含 skeleton.dtsi 的文件中,这个根节点会与其他文件的根节点合并,最终形成一个统一的设备树结构。
地址单元和大小单元
#address-cells = <1>;:指定了该设备树中地址单元的数量。对于大多数简单的设备树节点,地址单元数量为 1 即可满足需求。这意味着在描述设备的地址信息时,使用一个单元(通常是 32 位或 64 位的值)来表示地址。例如,对于内存设备的基地址,就可以用一个这样的地址单元来指定。
#size-cells = <1>;:定义了大小单元的数量。同样,对于很多设备节点,大小单元数量设为 1 就足够了。它用于描述设备所占用的内存或资源的大小,比如内存区域的大小、寄存器块的大小等,以一个单元的值来表示具体的字节数。
chosen 节点
chosen { };:这是一个特殊的节点,通常用于在设备树中标记一些被选择或推荐的设置。在内核引导过程中,引导加载程序可能会根据这个节点中的信息来进行一些特定的配置或初始化操作。不过在 skeleton.dtsi 中,这个节点暂时没有包含任何具体的子节点或属性。
aliases 节点
aliases { };:用于创建设备树中的别名。通过别名,可以给设备树中的节点起一个更易读、更有意义的名字,方便在其他部分的设备树文件中引用。这有助于提高设备树的可读性和可维护性。但在 skeleton.dtsi 中,目前也没有具体的别名定义。
memory 节点
memory { device_type = "memory"; reg = <0 0>; };:定义了一个内存设备节点。device_type = "memory" 明确指出这是一个内存类型的设备。reg = <0 0> 表示该内存设备的起始地址为 0x00000000,大小为 0x00000000(这里的大小为 0 可能是一个占位符,实际的内存大小会在具体的设备树文件中根据硬件情况进行设置)。这个内存节点是设备树中描述系统内存的基础模板,其他具体的内存设备节点可以在继承这个节点的基础上进行修改和扩展。
总的来说,skeleton.dtsi 文件提供了设备树的基本框架和一些常用的节点定义,使得其他的设备树文件可以在此基础上进行扩展和定制,从而减少代码的重复编写,提高设备树的可维护性和可扩展性。 可见,该文件只是一个最基础的框架性模版,没有具体的设备信息。
imx6ull.dtsi
接下来看imx6ull.dtsi这个文件,这个文件的内容比较多。
imx6ull.dtsi 文件是一个设备树包含文件(Device Tree Include File),用于描述 I.MX6ULL SoC(System on Chip)的内部外设信息。可见,是一个描述芯片信息的设备树包含文件。因此,建议不要在该文件中包含片外的外设。 不过虽然看着很长,其实总体上就那么几类信息,大部分的长度都在soc片上外设或者控制器等上面。 imx6ull.dtsi 文件是一个设备树包含文件(Device Tree Include File),用于描述 I.MX6ULL SoC(System on Chip)的内部外设信息。以下是对该文件内容的详细解释:
头文件引用
时钟、GPIO、中断控制器相关头文件:
#include <dt-bindings/clock/imx6ul-clock.h>:
包含了与 I.MX6ULL 时钟配置相关的绑定信息,这些信息定义了时钟的各种属性、频率范围等,以便在设备树中对时钟进行准确的描述和配置。
#include <dt-bindings/gpio/gpio.h>:
提供了 GPIO(通用输入输出端口)的相关信息,如 GPIO 的编号、功能、状态等定义,使得在设备树中可以正确地描述和使用 GPIO 引脚。
#include <dt-bindings/interrupt-controller/arm-gic.h>:
包含了 ARM 通用中断控制器(GIC)的绑定信息,这对于处理 I.MX6ULL 的中断机制非常重要,通过该头文件可以在设备树中准确地设置中断控制器的属性和行为。
针脚功能相关头文件:
#include "imx6ull-pinfunc.h" 和 #include "imx6ull-pinfunc-snvs.h":
这两个头文件定义了 I.MX6ULL 的针脚功能和特殊非易失性存储(SNVS)相关的针脚功能。它们指定了各个针脚的用途、电气特性等,对于连接外部设备以及正确配置芯片的引脚功能至关重要。
#include "skeleton.dtsi":
这是一个基础的设备树框架头文件,通常包含了一些通用的设备树节点定义和基本的结构信息,为后续具体的设备树描述提供了一个基础模板。
根节点及子节点
根节点:
/ {:这是设备树的根节点,所有的其他节点都是直接或间接地挂在这个根节点下面。在设备树的语法中,根节点使用反斜杠“/”表示,它是整个设备树的起点,代表了整个系统的硬件资源。
别名节点(aliases):
别名节点用于为设备树中的节点创建易于理解和使用的别名。比如在这里将flexcan1 这个节点命名为 can0,这样在其他地方引用 can0 时,实际上就是指代 flexcan1 节点。这种别名机制方便了设备树的编写和阅读,尤其是对于复杂的硬件系统,可以通过有意义的别名来快速定位和引用特定的硬件设备。
CPU 节点(cpus):
这段设备树代码定义了一个 CPU 节点(
cpus
)及其子节点cpu0
,主要用于描述 ARM Cortex-A7 处理器的硬件特性和运行参数。下面是对关键属性的详细解析:1. 父节点
cpus
的属性cpus {#address-cells = <1>; // 子节点地址用 1 个 cell 表示#size-cells = <0>; // 子节点地址无需描述大小(0 个 cells)... };
#address-cells = <1>
和#size-cells = <0>
:
由于 CPU 是 “编号型设备”(通过 ID 区分,而非连续地址空间),因此子节点cpu0
的reg
属性仅需 1 个 cell 表示 CPU 编号(无需大小)。2. 子节点
cpu0: cpu@0
的核心属性(1)基本标识属性
cpu0: cpu@0 {compatible = "arm,cortex-a7"; // 兼容的 CPU 型号(Cortex-A7)device_type = "cpu"; // 设备类型为 CPU(标准属性)reg = <0>; // CPU 编号为 0(对应父节点的 #address-cells 配置)... };
compatible
:用于匹配 CPU 驱动(如 ARM 架构的 CPU 核心驱动)。device_type = "cpu"
:内核识别 CPU 节点的标准属性(固定值)。reg = <0>
:表示这是第 0 号 CPU(多核系统中会有cpu@1
、cpu@2
等)。(2)时钟与电源管理属性
clock-latency = <61036>; // 时钟切换延迟(单位:纳秒) operating-points = </* kHz uV */996000 1275000792000 1225000528000 1175000396000 1025000198000 950000 >; fsl,soc-operating-points = <...>; // 飞思卡尔平台的 SoC 级电压配置 fsl,low-power-run; // 支持低功耗运行模式(飞思卡尔自定义属性)
clock-latency
:CPU 频率切换所需的延迟时间(此处为 61036ns),用于内核动态调频(DVFS)决策。operating-points
:定义 CPU 支持的 “频率 - 电压” 组合(动态调频的关键参数):
例如,当 CPU 运行在 996MHz 时,核心电压需为 1275mV;降频到 198MHz 时,电压可降至 950mV(平衡性能与功耗)。fsl,soc-operating-points
:飞思卡尔(Freescale)平台特有的 SoC 级电压配置(可能用于外围总线与 CPU 电压协同)。(3)时钟源配置
clocks = <&clks IMX6UL_CLK_ARM>,<&clks IMX6UL_CLK_PLL2_BUS>,... // 共 11 个时钟源 clock-names = "arm", "pll2_bus", ...; // 时钟源的名称(与 clocks 一一对应)
clocks
:指定 CPU 相关的时钟源(通过 phandle<&clks>
引用时钟控制器节点clks
,并通过宏定义指定具体时钟)。
例如IMX6UL_CLK_ARM
表示 CPU 核心时钟,IMX6UL_CLK_PLL1
表示主 PLL 时钟。clock-names
:为每个时钟源命名,方便驱动程序按名称引用(如驱动中通过"arm"
找到 CPU 核心时钟)。总结
这段设备树代码完整描述了 i.MX6UL 芯片中 Cortex-A7 处理器(cpu0)的基本信息、频率 / 电压配置、时钟源及电源管理特性,是内核初始化 CPU、实现动态调频和电源管理的关键依据。其中:
- 标准属性(
compatible
、reg
、operating-points
)确保内核通用驱动能识别 CPU;- 厂商自定义属性(
fsl,soc-operating-points
、fsl,low-power-run
)用于适配飞思卡尔平台的特殊硬件逻辑;- 时钟相关属性则为内核提供了 CPU 时钟管理的具体参数。
中断控制器节点(intc):
这段设备树代码定义了一个 中断控制器节点(
intc
),对应 ARM Cortex-A7 处理器的通用中断控制器(GIC),用于管理系统中所有硬件设备的中断请求。下面对每个关键属性进行详细解析,帮助理解中断控制器在设备树中的配置逻辑:1. 节点基本信息:
intc: interrupt-controller@00a01000
- 节点名称:
interrupt-controller@00a01000
- 前缀
interrupt-controller
明确标识该节点是 “中断控制器”(符合设备树规范的命名习惯);@00a01000
是中断控制器的基地址,与后续reg
属性中第一个地址段一致,用于直观关联硬件地址。- 节点标签:
intc
为节点定义的别名,方便设备树其他节点通过&intc
快速引用该中断控制器(例如外设节点声明 “使用intc
管理中断”)。2. 核心属性解析
(1)
compatible = "arm,cortex-a7-gic"
- 作用:声明中断控制器的兼容性,用于内核匹配对应的 GIC 驱动程序。
- 含义:
"arm,cortex-a7-gic"
表示该中断控制器是 ARM Cortex-A7 处理器配套的 GIC(通常为 GIC-400 或 GICv2 架构);- 内核中专门的 GIC 驱动(如
drivers/irqchip/irq-gic.c
)会通过of_match_table
匹配该字符串,从而初始化中断控制器硬件。(2)
#interrupt-cells = <3>
作用:定义 “引用该中断控制器时,需要用几个
cell
(32 位整数)描述一个中断请求”,是中断控制器节点的核心配置。为什么是 3 个
cell
?
ARM GIC 架构下,一个完整的中断描述需要 3 个参数,格式和含义固定为:<中断类型 中断号 触发方式>
- 第 1 个
cell
:中断类型(如0
表示 SPI 中断,1
表示 PPI 中断 ——SPI 是外设共享中断,PPI 是 CPU 私有中断);- 第 2 个
cell
:中断号(GIC 分配给该中断的唯一编号,如23
对应某 UART 的中断号);- 第 3 个
cell
:触发方式(如0x04
表示上升沿触发,0x08
表示下降沿触发,0x10
表示高电平触发)。示例:某 UART 节点引用该中断控制器时,会这样声明中断:
interrupts = <0 23 0x04>; // 类型=SPI(0),中断号=23,触发方式=上升沿(0x04)
(3)
interrupt-controller
- 作用:空属性(无值),仅用于标识该节点是中断控制器,内核解析时会将其归类为 “中断管理设备”,允许其他节点通过
interrupt-parent
属性关联它。- 必要性:所有中断控制器节点必须包含该属性,否则内核无法识别其功能,后续外设无法绑定中断。
(4)
reg = <0x00a01000 0x1000>, <0x00a02000 0x100>
- 作用:描述中断控制器的物理地址空间(寄存器基地址和占用大小),内核驱动通过这些地址访问 GIC 寄存器,实现中断的使能、屏蔽、优先级配置等操作。
- 两个地址段的含义(GIC 架构特性):
- 第一个地址段
<0x00a01000 0x1000>
:对应 GIC 的 Distributor(分发器) 寄存器区域;
- 功能:管理全局中断(如中断分配、优先级设置、向 CPU 分发中断请求);
- 大小
0x1000
(4KB):覆盖 Distributor 的所有控制寄存器。- 第二个地址段
<0x00a02000 0x100>
:对应 GIC 的 CPU Interface(CPU 接口) 寄存器区域;
- 功能:CPU 与 GIC 交互的接口(如 CPU 确认中断、清除中断、屏蔽当前 CPU 的中断);
- 大小
0x100
(256B):覆盖 CPU Interface 的核心寄存器。3. 该节点的核心作用
整个
intc
节点的目的是:
- 告诉内核 “在
0x00a01000
和0x00a02000
地址有一个 GIC 中断控制器”;- 定义 “其他外设引用中断时,需用 3 个参数描述中断信息”;
- 让内核加载对应的 GIC 驱动,初始化硬件后,接管整个系统的中断管理(如响应外设中断请求、分发到对应 CPU 处理)。
时钟节点(clocks):
这段设备树代码定义了一个
clocks
节点,包含了多个子节点,用于描述系统中的固定时钟源(fixed-clock
)。这些节点主要用于向内核提供时钟频率信息,是系统时钟管理的基础配置。下面详细解析每个部分的作用:1. 父节点
clocks
的属性clocks {#address-cells = <1>; // 子节点地址用 1 个 cell 表示#size-cells = <0>; // 子节点地址无需描述大小(0 个 cells)... };
#address-cells = <1>
和#size-cells = <0>
:
由于时钟源是 “编号型设备”(通过 ID 区分,而非连续地址空间),因此子节点的reg
属性仅需 1 个 cell 表示时钟编号(无需大小)。2. 子节点:固定时钟源(
fixed-clock
)的配置每个子节点(如
ckil: clock@0
)都描述了一个固定时钟源,核心属性如下:(1)
ckil: clock@0
(32.768kHz 低频时钟)ckil: clock@0 {compatible = "fixed-clock"; // 标识为固定频率时钟reg = <0>; // 时钟编号为 0(对应父节点配置)#clock-cells = <0>; // 引用该时钟时无需额外参数clock-frequency = <32768>; // 时钟频率为 32768Hz(32.768kHz)clock-output-names = "ckil"; // 时钟输出名称(供驱动引用) };
- 作用:通常作为系统的实时时钟(RTC)或低功耗定时器的基准时钟(32.768kHz 是常见的低频时钟频率,适合长周期计时)。
(2)
osc: clock@1
(24MHz 高频振荡器)osc: clock@1 {compatible = "fixed-clock";reg = <1>; // 时钟编号为 1#clock-cells = <0>;clock-frequency = <24000000>;// 时钟频率为 24MHzclock-output-names = "osc"; // 输出名称为 "osc" };
- 作用:通常作为系统主振荡器(Oscillator),为 PLL(锁相环)提供基础时钟,进而生成 CPU、总线等高频时钟(24MHz 是嵌入式系统中常见的高频基准频率)。
(3)
ipp_di0: clock@2
和ipp_di1: clock@3
(未启用的时钟)
ipp_di0: clock@2 {compatible = "fixed-clock";reg = <2>;#clock-cells = <0>;clock-frequency = <0>; // 频率为 0,表示未启用或无效clock-output-names = "ipp_di0"; };
- 作用:
clock-frequency = <0>
通常表示该时钟源当前未使用(可能是预留的接口,或依赖外部硬件触发后才有效)。ipp_di
可能对应图像处理器(IPP)的输入时钟,需根据具体硬件场景启用。3. 关键属性解析
(1)
compatible = "fixed-clock"
- 标识该节点是固定频率时钟(频率不可动态调整),内核会匹配通用的
fixed-clock
驱动(无需针对特定硬件编写驱动)。(2)
#clock-cells = <0>
- 定义 “引用该时钟时需要多少个 cell 作为参数”。此处为
0
表示:引用该时钟时无需额外参数(仅需通过 phandle 指向该节点即可)。
示例:某外设需要使用osc
时钟时,配置如下:some_device {clocks = <&osc>; // 直接引用 &osc 即可,无需额外参数clock-names = "osc_clk"; };
(3)
clock-frequency
- 声明时钟的固定频率(单位:Hz),是该节点的核心参数。内核和驱动通过读取该值获取时钟频率,用于计算定时器周期、波特率等(如 UART 波特率需基于时钟频率配置)。
(4)
clock-output-names
- 为时钟输出命名,方便调试和驱动中按名称识别时钟(如内核日志中会显示该名称,便于定位时钟来源)。
4. 整体作用
这些
fixed-clock
节点是系统时钟树的 “根节点”,为后续的时钟控制器(如 PLL、分频器)提供初始基准时钟。例如:
osc
(24MHz)可能输入到 PLL,通过倍频生成 CPU 核心时钟(如 1GHz);ckil
(32.768kHz)直接供给 RTC 模块,用于记录系统时间。内核通过这些节点获取基础时钟频率后,才能进一步配置复杂的时钟树(分频、倍频、多路切换),确保所有外设(UART、SPI、ADC 等)在正确的时钟频率下工作。
总结
这段代码定义了系统的基础固定时钟源,核心作用是向内核提供 “不可调整的基准频率” 信息。其中:
ckil
和osc
是活跃的基准时钟,为系统和外设提供基础计时信号;ipp_di0
/ipp_di1
是预留的时钟接口,可能用于扩展硬件;- 所有节点通过
fixed-clock
兼容属性,被内核通用驱动识别和管理,无需额外开发驱动。这些时钟是整个系统时钟管理的起点,后续复杂的时钟配置(如动态调频、外设时钟开关)都依赖这些基础频率。
系统节点(soc):
这是个非常大的节点,里面包含了基本的soc外设
先从整体上看看
这段设备树代码是 i.MX6UL 芯片的 SoC 核心节点配置,通过层级化结构(
soc
父节点 + 子总线 / 外设节点)完整描述了芯片内部的总线架构、核心外设(如 GPIO、UART、SPI)、时钟 / 电源管理模块等硬件资源。它是内核识别和初始化 SoC 硬件的 “蓝图”,下面从 架构逻辑、核心模块、关键属性 三方面展开解析:一、整体架构:SoC 节点的层级逻辑
soc
节点是整个芯片硬件描述的 “根容器”,采用 “总线 - 外设” 分层设计,符合嵌入式系统的硬件拓扑(类似电脑的 “主板 - PCIe 总线 - 显卡 / 硬盘” 结构),核心层级如下:soc(根节点) ├─ 核心功能模块(总线频率管理、电源管理、SRAM等) │ ├─ busfreq(总线频率动态调整) │ ├─ pmu(CPU性能监控单元) │ ├─ ocrams/ocram(片上SRAM,用于低功耗场景或临时数据存储) │ └─ dma_apbh(APBH总线DMA控制器,用于外设数据传输) ├─ 三大 AIPS 总线节点(AIPS1/AIPS2/AIPS3,管理不同外设组) │ ├─ AIPS1(0x02000000 地址段):管理 SPI、UART、PWM、GPIO、CAN 等常用外设 │ ├─ AIPS2(0x02100000 地址段):管理 USB、以太网、SDHC(SD卡)、I2C、CSI(摄像头)等高速/扩展外设 │ └─ AIPS3(0x02200000 地址段):管理加密(DCP)、随机数(RNG)、低功耗相关外设(如 SNVS) └─ 系统控制模块(时钟、中断、电源、引脚复用)├─ clks(CCM 时钟控制器,管理全芯片时钟)├─ gpc(通用电源控制器,管理电源域和中断分发)├─ anatop(模拟拓扑控制器,管理电源 regulators)├─ iomuxc(引脚复用控制器,配置GPIO功能)└─ snvs(安全非易失性存储,管理RTC、电源键)
这种分层设计的核心目的是:按地址段和功能归类外设,让内核能高效遍历和初始化硬件(例如通过
ranges
属性映射物理地址,通过compatible
匹配驱动)。二、核心模块解析:关键子节点的功能与属性
1.
soc
根节点的基础属性soc {#address-cells = <1>; // 子节点地址用 1 个 cell(32位整数)表示#size-cells = <1>; // 子节点地址空间大小用 1 个 cell 表示compatible = "simple-bus"; // 标识为“简单总线”,内核按总线逻辑管理子节点interrupt-parent = <&gpc>; // 默认中断父节点为 GPC(电源与中断控制器)ranges; // 地址无偏移(子节点物理地址 = 父节点地址空间内的地址)... };
#address-cells
/#size-cells
:由于 SoC 外设地址是连续的 32 位物理地址,用 1 个 cell 即可描述地址和大小(例如reg = <0x0209c000 0x4000>
表示地址0x0209c000
,大小16KB
)。ranges
:空属性表示 “子节点地址直接映射到物理地址”,无需地址转换(常见于 SoC 内部外设)。2. 核心功能模块(非总线类节点)
这类节点是 SoC 运行的 “基础支撑”,负责频率、电源、数据传输等核心能力:
(1)
busfreq
:总线频率动态管理busfreq {compatible = "fsl,imx_busfreq"; // 飞思卡尔总线频率驱动匹配clocks = <&clks IMX6UL_CLK_PLL2_PFD2>, ...; // 关联总线相关时钟源clock-names = "pll2_pfd2_396m", ...; // 时钟源命名,方便驱动引用fsl,max_ddr_freq = <400000000>; // DDR 最大频率(400MHz) };
- 功能:动态调整 AHB/AXI 总线频率,平衡系统性能与功耗(例如高负载时提频,低负载时降频)。
- 关键属性:
fsl,max_ddr_freq
限制 DDR 内存的最高工作频率,避免硬件过载。(2)
ocram
/ocrams
:片上 SRAMocram: sram@00905000 {compatible = "mmio-sram"; // 内存映射型 SRAMreg = <0x00905000 0x1B000>; // 地址 0x00905000,大小 108KB };
- 功能:片上静态内存,比 DDR 内存速度更快、功耗更低,常用于:
- 低功耗模式下的数据暂存(如休眠时保存上下文);
- DMA 传输的临时缓冲区;
- 实时性要求高的代码(如中断服务函数)。
(3)
dma_apbh
:APBH 总线 DMA 控制器dma_apbh: dma-apbh@01804000 {compatible = "fsl,imx6ul-dma-apbh", "fsl,imx28-dma-apbh";reg = <0x01804000 0x2000>; // DMA 控制器寄存器地址interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>, ...; // DMA 通道中断#dma-cells = <1>; // 引用 DMA 时需 1 个 cell(通道号)dma-channels = <4>; // 支持 4 个 DMA 通道 };
- 功能:卸载 CPU 数据传输压力,实现外设(如 NAND Flash、UART)与内存的直接数据交互(DMA 传输)。
- 示例引用:
gpmi-nand
(NAND 控制器)通过dmas = <&dma_apbh 0>
绑定 DMA 第 0 通道,实现 NAND 数据的高速读写。3. AIPS 总线节点:外设的 “管理中心”
AIPS
(Advanced Intelligent Peripheral Subsystem)是 i.MX6UL 的外设总线架构,按地址段分为 AIPS1/AIPS2/AIPS3,每个总线节点管理一组功能相近的外设,以 AIPS1 为例:aips1: aips-bus@02000000 {compatible = "fsl,aips-bus", "simple-bus";reg = <0x02000000 0x100000>; // 总线地址范围:0x02000000 ~ 0x020FFFFF(1MB)ranges; // 地址无偏移spba-bus@02000000 { // SPBA 子总线(低速外设总线)compatible = "fsl,spba-bus", "simple-bus";reg = <0x02000000 0x40000>; // 子总线地址范围:0x02000000 ~ 0x0203FFFFranges;ecspi1: ecspi@02008000 { ... }; // SPI1 外设uart1: serial@02020000 { ... }; // UART1 外设};gpio1: gpio@0209c000 { ... }; // GPIO1 外设pwm1: pwm@02080000 { ... }; // PWM1 外设 };
- 总线分工:
- AIPS1:管理低速 / 常用外设(SPI、UART、GPIO、PWM、CAN),地址段
0x02000000 ~ 0x020FFFFF
;- AIPS2:管理高速 / 扩展外设(USB、以太网、SDHC、I2C、CSI),地址段
0x02100000 ~ 0x021FFFFF
;- AIPS3:管理安全 / 低功耗外设(DCP 加密、RNG 随机数、SNVS 低功耗模块),地址段
0x02200000 ~ 0x022FFFFF
。spba-bus
:AIPS 下的子总线,专门管理更低速的外设(如早期 SPI、UART),减少对主总线的占用。4. 系统控制模块:SoC 的 “大脑”
这类节点负责 SoC 的全局控制(时钟、电源、中断、引脚),是硬件初始化的核心:
(1)
clks
:CCM 时钟控制器clks: ccm@020c4000 {compatible = "fsl,imx6ul-ccm"; // 匹配 i.MX6UL 的 CCM 驱动reg = <0x020c4000 0x4000>; // CCM 寄存器地址(16KB)#clock-cells = <1>; // 引用时钟时需 1 个 cell(时钟 ID,如 IMX6UL_CLK_ARM)clocks = <&ckil>, <&osc>; // 输入基准时钟(32.768kHz 低频钟、24MHz 晶振)clock-names = "ckil", "osc"; };
- 功能:生成和分配全芯片的时钟信号(如 CPU 核心时钟、外设时钟),支持动态调频(DVFS)。
- 关键属性:
#clock-cells = <1>
表示其他外设引用时钟时,需传入 “时钟 ID”(如clocks = <&clks IMX6UL_CLK_UART1_SERIAL>
表示 UART1 的串行时钟)。(2)
gpc
:电源与中断控制器gpc: gpc@020dc000 {compatible = "fsl,imx6ul-gpc", "fsl,imx6q-gpc";reg = <0x020dc000 0x4000>; // GPC 寄存器地址interrupt-controller; // 标识为中断控制器#interrupt-cells = <3>; // 中断描述需 3 个 cell(类型、编号、触发方式)interrupt-parent = <&intc>; // 最终中断父节点为 GIC(全局中断控制器) };
- 功能:
- 电源管理:控制 SoC 各电源域(如 CPU 核心域、外设域)的上电 / 下电;
- 中断分发:将外设中断请求(如 UART 接收中断)转发给 GIC,再分配给 CPU 处理。
(3)
iomuxc
:引脚复用控制器iomuxc: iomuxc@020e0000 {compatible = "fsl,imx6ul-iomuxc";reg = <0x020e0000 0x4000>; // IOMUXC 寄存器地址 };
- 功能:i.MX6UL 的 GPIO 引脚支持 “复用功能”(如同一引脚可作为 UART_TX、SPI_SCK 或普通 GPIO),
iomuxc
节点通过寄存器配置引脚功能,例如:
- 将引脚
GPIO1_IO03
复用为UART1_TX
;- 配置引脚的上下拉电阻、驱动能力。
(4)
anatop
:模拟电源控制器anatop: anatop@020c8000 {compatible = "fsl,imx6ul-anatop", "syscon";reg = <0x020c8000 0x1000>; // ANATOP 寄存器地址reg_arm: regulator-vddcore@140 { // CPU 核心电源 regulatorcompatible = "fsl,anatop-regulator";regulator-name = "cpu";regulator-min-microvolt = <725000>; // 最小 725mVregulator-max-microvolt = <1450000>; // 最大 1450mVregulator-always-on; // 始终上电(CPU 核心不可掉电)}; };
- 功能:管理 SoC 的模拟电源(如 CPU 核心电压、外设电压),通过
regulator
子节点配置电压范围,支持动态调压(配合 CPU 调频,如高频时提压、低频时降压)。三、外设节点共性:如何识别和初始化外设
所有外设节点(如
uart1
、ecspi1
、gpio1
)都遵循统一的配置逻辑,核心属性包括:以
属性名 作用 compatible
匹配驱动(如 fsl,imx6ul-uart
匹配 i.MX6UL 的 UART 驱动)reg
外设寄存器的物理地址和大小(如 0x02020000 0x4000
表示 UART1 地址)interrupts
外设中断配置(如 GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH
表示 SPI 中断)clocks
/clock-names
外设依赖的时钟源(如 UART 需要 ipg
总线时钟和serial
串行时钟)dmas
/dma-names
外设绑定的 DMA 通道(如高速 UART 用 DMA 实现数据收发) status
外设使能状态( "okay"
表示启用,"disabled"
表示禁用)uart1
为例,其配置逻辑为:uart1: serial@02020000 {compatible = "fsl,imx6ul-uart"; // 匹配 UART 驱动reg = <0x02020000 0x4000>; // UART1 寄存器地址(16KB)interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>; // 中断:SPI类型,编号26,高电平触发clocks = <&clks IMX6UL_CLK_UART1_IPG>, <&clks IMX6UL_CLK_UART1_SERIAL>; // 依赖的两个时钟clock-names = "ipg", "per"; // 时钟命名(总线时钟、串行时钟)status = "disabled"; // 默认禁用,需根据硬件启用 };
四、总结:这段设备树的核心作用
- 硬件抽象:将 i.MX6UL 的复杂硬件(数百个寄存器、数十个外设)抽象为结构化的节点,让内核无需硬编码地址即可访问硬件;
- 驱动匹配:通过
compatible
属性将外设与驱动关联(如fsl,imx6ul-gpio
匹配 GPIO 驱动),实现 “硬件无关” 的驱动设计;- 参数配置:为外设提供初始化参数(如时钟频率、中断触发方式、DMA 通道),内核加载驱动时直接读取这些参数,无需修改代码;
- 灵活性:通过
status
属性启用 / 禁用外设,通过ranges
调整地址映射,适配不同的硬件版型(如有的板子用 UART1,有的用 UART2)。简单来说,这段设备树是 “内核与硬件之间的翻译官”,没有它,内核无法识别 i.MX6UL 的任何外设,系统也无法启动。
imx6ull-14x14-evk.dts
imx6ull-14x14-evk.dts和imx6ull.dtsi有啥区别?
在嵌入式 Linux 开发中,
imx6ull-14x14-evk.dts
和imx6ull.dtsi
都是设备树文件,用于描述硬件信息,但它们的作用和适用范围有明显区别:
imx6ull.dtsi(设备树包含文件)
- 这是 i.MX6ULL 处理器的通用基础设备树文件,包含了该芯片共有的硬件描述(如 CPU 核心、内部外设控制器、内存接口等)
- 它不针对特定开发板,而是所有基于 i.MX6ULL 芯片的板卡都可以复用的通用部分
- 通常以
.dtsi
为扩展名,表示 "device tree include",用于被其他设备树文件引用imx6ull-14x14-evk.dts(具体板卡设备树)
- 这是 NXP 官方 i.MX6ULL 14x14 EVK 开发板的专用设备树文件
- 它基于
imx6ull.dtsi
扩展,添加了该特定开发板的硬件信息(如板载外设、引脚配置、外部设备等)- 是最终会被编译为
.dtb
(设备树二进制)文件并被内核使用的文件简单来说:
imx6ull.dtsi
是芯片级的通用描述,imx6ull-14x14-evk.dts
是基于特定开发板的具体实现,前者被后者通过#include
语句引用,形成完整的硬件描述。这种分层设计的好处是:不同板卡可以共享芯片级的通用描述,只需关注各自板级硬件的差异部分,减少重复代码。
再来看看imx6ull-14x14-evk.dts文件,这个文件的内容也比较长,其实就是描述特定开发板的设备信息的,也有对芯片信息的进一步补充。 imx6ull-14x14-evk.dts 文件是用于描述 i.MX6ULL 14x14 EVK(Evaluation Kit,评估套件)硬件的 Device Tree Source(设备树源)文件。以下是对该文件内容的详细解释:
头文件引用
#include <dt-bindings>:引入与输入设备相关的绑定定义,为后续描述输入设备节点提供基础信息和属性绑定。
#include "imx6ull.dtsi":包含 i.MX6ULL 芯片通用的设备树包含文件,其中定义了 i.MX6ULL 芯片相关的外设描述、时钟、中断控制器等基本信息,可在当前文件中直接使用这些预定义的节点和属性。
根节点及基本属性
/ {:根节点,代表整个设备树的根,所有其他的节点都是直接或间接挂在这个根节点下面。
model = "Freescale i.MX6 ULL 14x14 EVK Board";:定义了该设备树所对应的开发板型号,即 Freescale(现 NXP)的 i.MX6ULL 14x14 EVK 评估板。
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";:指定了设备的兼容属性,表明该设备与 “fsl,imx6ull-14x14-evk” 和 “fsl,imx6ull” 兼容,这样内核在加载设备树时可以根据这些兼容字符串来匹配相应的驱动程序。
chosen { stdout-path = &uart1; };:选择了默认的控制台输出设备,这里将 uart1 设置为系统启动时的默认控制台输出端口,以便在系统启动过程中输出日志信息。
内存节点
memory { reg = <0x80000000 0x20000000>; };:描述了内存区域的起始地址和大小,0x80000000 是内存的起始物理地址,0x20000000 是内存的大小,表示该评估板上有 512MB 的内存可供使用。
保留内存节点
reserved-memory {:定义了一些需要保留的内存区域,这些区域通常用于特定的硬件功能或未来的扩展,不允许常规的内存访问。
#address-cells = <1>; #size-cells = <1>; ranges;:指定了地址单元和大小单元的数量,以及使用范围来描述保留内存的区域。
linux,cma { compatible = "shared-dma-pool"; reusable; size = <0x14000000>; linux,cma-default; };:保留了一块用于 Contiguous Memory Allocator(CMA)的内存区域,大小为 0x14000000 字节(约 36MB),compatible 属性为 "shared-dma-pool",表示这是一个共享的 DMA 池,reusable 表示该内存区域可重复使用,linux,cma-default 表示这是默认的 CMA 区域。
背光节点
backlight { compatible = "pwm-backlight"; pwms = <&pwm1 0 5000000>; brightness-levels = <0 4 8 16 32 64 128 255>; default-brightness-level = <6>; status = "okay"; };:描述了背光设备的相关信息。compatible 属性为 "pwm-backlight",表示该背光设备使用 PWM(脉宽调制)控制。pwms 属性指定了用于控制背光的 PWM 控制器及其参数,这里使用了 pwm1 通道 0,频率为 5MHz。brightness-levels 定义了背光的亮度级别,从 0 到 255 共 256 级亮度可调。default-brightness-level 设置了默认的亮度级别为 6。status = "okay" 表示该背光设备工作正常。
摄像头节点
pxp_v4l2 { compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2"; status = "okay"; };:定义了一个 V4L2(Video for Linux Two)接口的摄像头设备节点。compatible 属性列出了该摄像头设备所兼容的驱动类型,包括 "fsl,imx6ul-pxp-v4l2"、"fsl,imx6sx-pxp-v4l2" 和 "fsl,imx6sl-pxp-v4l2",表示该摄像头可以在使用这些 SoC(System on Chip)的开发板上通过相应的驱动程序进行视频采集等工作。status = "okay" 表示该摄像头设备工作正常。
电源管理节点
regulators { compatible = "simple-bus"; //省略... };:描述了电源管理相关的信息。compatible 属性为 "simple-bus",表示这是一个简单的电源总线。此处省略了具体的电压调节器等电源组件的定义,实际的文件中可能会详细列出各个电源域、电压调节器的类型、输出电压等信息,用于内核对电源的管理。
CPU 供电相关节点
&cpu0 { arm-supply = <®_arm>; soc-supply = <®_soc>; dc-supply = <®_gpio_dvfs>; };:针对 CPU0 的供电配置节点。arm-supply、soc-supply 和 dc-supply 分别指定了 CPU 的不同供电域所使用的电源控制器或电源轨,如 ®_arm、®_soc 和 ®_gpio_dvfs,这些是在其他部分定义好的电源相关的节点,用于为 CPU 提供稳定的电源供应。
时钟节点
&clks { assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; assigned-clock-rates = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; };:描述了时钟相关的配置信息。assigned-clocks 和 assigned-clock-rates 属性分别指定了分配给设备的时钟源和时钟频率,这里使用了 &clks IMX6UL_CLK_PLL4_AUDIO_DIV,表示使用的是 i.MX6ULL 的 PLL4 音频时钟分频后的时钟源作为设备的时钟输入,具体的时钟频率值也在其他地方定义。
其他节点
总的来说,imx6ull-14x14-evk.dts 文件通过上述各种节点和属性的定义,详细地描述了 i.MX6ULL 14x14 EVK 开发板的硬件资源和配置信息,包括内存、背光、摄像头、电源管理、CPU 供电、时钟等方面,使得操作系统内核能够正确地识别和使用这些硬件资源,从而实现对开发板的支持和管理。在实际的嵌入式系统开发中,根据具体的硬件设计和需求,还可能需要对该文件进行进一步的修改和扩展。
总体框架暂时就了解这么多,接下来,看一些重点细节。
重点其实是在imx6ull.dtsi和imx6ull-14x14-evk.dts这两个文件上,因此我们也着眼于这两个文件。
其中,板级相关的在这个imx6ull-14x14-evk.dts文件里,芯片级相关的在这个imx6ull.dtsi文件里,我们以板级imx6ull-14x14-evk.dts作为进一步的着手点来分析。
model、compatible、chosen、memory等属性如果作为一级属性,通常是放在板级的dts文件中,而不是放在dtsi文件中,因为这些信息基本都是用来匹配开发板的。
设备树中的reserved-memory节点是用于预留一段内存区域的特殊节点。以下是关于该节点的详细解释:
定义与用途
在设备树中,reserved-memory节点用于指定需要预留的内存区域。这些区域通常不会被操作系统或驱动程序用于常规的内存分配,而是保留给特定的硬件设备、驱动程序或系统功能使用。
典型应用场景
DMA和其他硬件加速功能:许多硬件设备(如网络接口卡、音频设备等)可能需要直接访问内存而不经过CPU,这种技术称为DMA(Direct Memory Access)。为了确保这些设备能够安全地访问内存,通常会在设备树中使用reserved-memory节点来预留所需的内存区域。
避免内存冲突:通过预留特定的内存区域,可以防止操作系统或其他驱动程序错误地将这些内存分配给其他用途,从而避免潜在的内存冲突和系统不稳定。
节点属性
reg:表示要预留的物理内存区域的起始地址和大小,格式为<address>, <size>。
compatible:可选属性,用于指定预留内存区域的兼容类型,如"shared-dma-pool"表示该内存区域可用作一组设备的DMA缓冲区共享池。
no-map:可选属性,如果存在,表示操作系统不得创建该区域的虚拟映射,也不能将其作为系统内存标准映射的一部分。
reusable:可选属性,如果存在,表示操作系统可以使用该区域中的内存,但前提是拥有该区域的设备驱动程序能够回收它。
等等。 看下这个
这里除了对pwm1的引用,后面还加了些其他数据,不要觉得这样不行,无非就是后面按照这种规则去解析罢了,只要能解析到数据,就没问题。 在设备树(Device Tree)中,pwms = <&pwm1 0 5000000> 是一种描述 PWM(脉宽调制)控制器和其通道配置的语法。具体来说,这行代码的含义如下:
pwms: 这是设备树中的一个属性,表示该设备支持 PWM 功能。
<&pwm1 0 5000000>: 这是属性的值,描述了具体的 PWM 控制器及其配置。
详细解释如下:
&pwm1: 这是一个引用,指向一个名为 pwm1 的 PWM 控制器节点。& 符号表示这是一个对其他节点的引用。
0: 这是 PWM 通道号。在这个例子中,它表示使用 pwm1 控制器的第 0 个通道。
5000000: 这是 PWM 的频率,单位是纳秒(ns)。在这个例子中,PWM 的频率为 5,000,000 纳秒,即 5 MHz。
综合起来,这行代码的意思是:
该设备使用 pwm1 控制器的第 0 个通道。
该通道的 PWM 频率设置为 5 MHz。
这种配置通常用于需要精确控制信号频率的设备,如电机控制、LED 调光等应用。 这个被引用的pwm1,是在imx6ull.dtsi文件中定义的
并且在imx6ull-14x14-evk.dts的后面进行了追加
接着看电源稳压器、声卡、spi、iic等外设信息
…… 更多自行查看。 看下iomuxc节点
这里是个追加,其定义是在imx6ull.dtsi文件里,属于片上soc的外设子节点
可以对子节点直接追加,可见,如果名称是唯一的,那么是可以直接追加的。 iomuxc其实就是引脚复用控制器,这个节点里面是对引脚基本功能比如复用、驱动能力、上下拉等的描述,按照外设所用到的引脚集合在一起,比如i2c1用到的引脚
比如pwm1用到的所有引脚
等等。
pinctrl-names 属性
pinctrl-names 属性是一个字符串列表,它为引脚的不同状态或功能模式提供了名称标识。这些名称可以代表引脚的不同复用功能、电气特性配置或其他特定的工作状态等,方便在设备树中对引脚的配置进行描述和管理。通过为不同的引脚配置或功能模式指定独特的名称,如 “default”、“sleep”、“init” (自定义)等,使得设备树中的其他节点可以引用这些名称来快速定位和使用相应的引脚配置。
pinctrl-0通常用于指定设备或引脚组在默认状态下的引脚配置。这种状态通常是设备上电后或复位后的初始状态。它引用了一个在设备树的其他节点中定义的引脚组,这个引脚组包含了一组引脚及其相关的配置信息,如引脚复用功能、上下拉电阻、驱动能力等。 这两个属性貌似经常一起使用,是不是可以理解成,默认状态下所使用的引脚?
当 pinctrl-names = "default" 和 pinctrl-0 = <&pinctrl_uart1> 一起出现在设备树的一个节点中时,意味着当前节点所代表的设备或引脚组在默认状态下,将采用 pinctrl_uart1 节点所定义的引脚配置。 考虑某种应用场景,在驱动里获取pinctrl-names和pinctrl-0的属性值,然后判断pinctrl-names是否处于“default”状态,如果是的话,那就使用pinctrl-0提供的引脚信息来进行初始化。具体的行为还得看驱动是怎么去使用这些信息的。
为了进一步理解这个问题,看下这个节点,里面pinctrl-names有三个值,然后下面就分别有pinctrl-0和pinctrl-1和pinctrl-2,和pinctrl-names的三个名称是一一对应的,在驱动程序里肯定是根据不同的状态判断,去使用不同的引脚组。
iomuxc中pinctrl-names和pinctrl-0是什么属性 在设备树中,iomuxc 节点用于描述 IOMUXC(Input/Output Multiplexer Complex)的配置信息。该节点通常包含以下属性: pinctrl-names 含义:定义引脚状态,一种状态对应一个 pinctrl-x。它为引脚的不同功能或模式提供了一种命名方式,方便在设备树中对引脚配置进行分类和管理。例如,可以定义 “default”、”init”、”sleep” 等状态,来分别表示引脚在默认情况下、初始化过程中以及低功耗状态下的不同配置。 作用:通过指定不同的引脚状态,可以在不同的场景下灵活地配置引脚的功能和属性,满足系统在不同运行阶段的需求。比如在设备启动时,将引脚配置为初始化状态;在设备进入低功耗模式时,将引脚配置为睡眠状态,以降低功耗。 pinctrl-0 含义:定义第 0 种状态需要使用到的引脚组,可引用其他节点标识。它是一个引用属性,通过引用其他定义了具体引脚配置的节点,来确定在该状态下所使用的引脚设置。 作用:将引脚的实际配置信息与具体的引脚状态相关联。当设备的引脚状态为 “default” 时,pinctrl-0 所引用的节点中的引脚配置信息就会被应用到相应的引脚上,从而实现对该状态下引脚功能的设置。这样可以方便地对不同状态下的引脚配置进行集中管理和修改,提高代码的可维护性和可读性。 总的来说,pinctrl-names 和 pinctrl-0 属性在设备树的 iomuxc 节点中扮演着关键角色,它们共同协作以实现对引脚配置的灵活管理与精确控制。
iomuxc的最后一级子节点里面只有一个fls,pins属性,这个名字也是厂家自定义的,只要能跟驱动里引用节点时的名称对应起来即可。 fsl,pins 属性是一个特定于 Freescale(现为 NXP)SoC 的属性,用于描述引脚的配置信息。它通常包含一个或多个引脚配置项,每个配置项由引脚功能标识符和配置值组成。通过设置 fsl,pins 属性,可以指定引脚的复用功能以及相关的电气特性,如上下拉电阻、驱动能力等。这对于正确配置 SoC 的引脚以适应不同的外设功能至关重要。
继续
iomuxc_snvs是设备树中的一个属性,主要用于配置与安全非易失性存储(SNVS)子系统相关的引脚复用功能。以下是对该属性的详细解释:
基本概念
iomuxc_snvs属性通常出现在设备树节点中,用于描述某个或某些引脚与SNVS子系统之间的关联和配置信息。这些引脚可能具有特定的复用功能,通过该属性进行设置和说明。
作用范围
主要针对与SNVS子系统相关的引脚,这些引脚在系统中通常用于电源管理、安全存储或其他与安全相关的功能。
典型用途
电源管理配置:可以指定某些引脚作为电源管理引脚,如开关机控制引脚等。通过iomuxc_snvs属性的配置,可以确定这些引脚在系统中的具体功能和行为。
安全功能设置:对于涉及安全功能的引脚,如加密算法的硬件加速引脚、安全认证引脚等,使用iomuxc_snvs属性进行配置,以确保系统的安全运行。
特点
引脚复用选项有限:与一般的IOMUXC寄存器相比,iomuxc_snvs属性所涉及的引脚复用选项相对较少,通常只与特定的电源管理和安全功能相关。
安全性要求高:由于涉及到安全相关的功能,对iomuxc_snvs属性的配置和使用需要特别小心,以确保系统的安全性和稳定性。
综上所述,iomuxc_snvs属性在设备树中扮演着重要的角色,它用于配置与SNVS子系统相关的引脚复用功能,确保这些引脚能够正确地实现电源管理和安全存储等功能。
注意,设备树中的节点名称都是可以自己定义的,只要在同一层级下保持唯一即可。
设备树中写在前面的节点可以引用后面的节点吗?
在设备树中,前面的节点可以引用后面的节点。 设备树中的节点引用通常使用phandle(物理句柄)和label(标签)两种方式来表示。phandle是一个32位的整数值,用于唯一标识一个节点;label是一个字符串,用于标识一个节点。通过这些方式,一个节点可以引用另一个节点,无论它们在设备树中的位置如何。例如,可以在前面的节点中使用“&”符号和后面节点的phandle值或label来引用该节点。 设备树中引用节点是引用节点的某个属性还是引用整个节点,如何理解? 在设备树中,引用节点既可以是引用节点的某个具体属性,也可以理解为引用整个节点及其所有属性。以下是对这两种情况的具体解释:
引用节点的某个属性:
在某些情况下,一个节点可能会引用另一个节点的特定属性值。例如,一个设备节点可能需要配置其电源管理参数,而它本身没有直接定义这些参数,而是引用了另一个专门用于电源管理的节点中的相关属性。
这种引用方式使得设备树能够更加灵活地组织和管理硬件信息,避免重复定义相同的属性,同时也方便了设备的初始化和配置过程。
引用整个节点及其所有属性:
更常见的情况是,一个节点引用另一个节点时,实际上是将整个被引用节点的所有属性都纳入考虑范围。
例如,在一个复杂的嵌入式系统中,可能存在多个不同功能模块的节点,其中一个模块节点需要使用另一个模块节点的资源或配置信息。通过引用整个节点,可以方便地获取和使用该节点的所有属性,而不仅仅是其中的某个特定属性。
综上所述,设备树中的节点引用既可以指代对单个属性的引用,也可以指代对整个节点及其所有属性的引用。这种灵活性使得设备树成为一种强大的工具,能够有效地描述和管理复杂的硬件系统结构。 设备树中如果某个属性的属性值是一个节点,那么如果进一步获取属性节点的属性值? 在设备树中,如果某个属性的属性值是一个节点,要进一步获取该属性节点的属性值,可以通过以下步骤实现:
一、理解设备树结构
节点与属性:设备树由节点(Node)和属性(Property)组成。节点代表硬件设备或组件,属性用于描述节点的特性或配置参数。
属性值类型:属性的值可以是多种类型,包括整数、字符串、数组、甚至是另一个节点(这种情况较少见但确实存在)。
二、定位属性节点
查找节点:首先,需要在设备树中找到包含目标属性的节点。这通常通过遍历设备树或使用特定的查找函数(如of_find_node_by_path或of_find_node_by_name)来实现。
读取属性:一旦找到目标节点,就可以读取其属性值。如果属性值是一个节点,那么这个属性值实际上就是另一个节点的引用(可能是phandle、label或其他形式的引用)。
三、解析属性节点
获取节点引用:如果属性值是一个节点,需要解析这个引用以获取实际的节点对象。这通常涉及到将引用转换为实际的节点标识符(如phandle转换为节点指针)。
读取节点属性:一旦获得了实际的节点对象,就可以像访问普通节点一样读取其属性值。这通常通过设备树库提供的接口函数(如of_get_property)来实现。
其实就很像json数据的键的值又是一个json数据。
设备树中节点属性的值可以是另一个节点。
以下是对这一行为的详细解释:
一、设备树中的属性值类型
属性值的多样性:在设备树中,节点的属性值可以是多种数据类型,包括整数、字符串、数组(包括字节数组和字符串数组)、以及布尔值等。
节点作为属性值:除了上述基本数据类型外,设备树还支持一种特殊类型的属性值,即节点本身。这意味着一个节点的属性值可以引用或指向另一个节点。
二、实现方式
使用phandle:在设备树中,当一个节点的属性值是另一个节点时,通常使用物理句柄(phandle)来表示这种引用关系。phandle是一个32位的整数值,用于唯一标识一个节点。通过指定phandle,设备树能够清晰地表达节点之间的层次和依赖关系。
语法规则:为了在设备树中表示phandle,需要使用特定的语法。例如,在设备树源码中,可以使用“&”符号后跟节点标签名或phandle来引用其他节点。同时,为了将phandle与属性相关联,可能需要使用“<”和“>”符号来包含phandle值。
三、应用场景
硬件复用:在嵌入式系统中,多个设备或组件可能会共享相同的硬件资源(如中断控制器、时钟源等)。通过在设备树中使用节点作为属性值,可以方便地实现硬件资源的共享和复用。
模块化设计:设备树中的节点引用也有助于实现模块化设计。通过将相关的节点组织在一起,并使用属性值进行引用,可以构建出更加清晰、易于维护的设备树结构。
四、注意事项
循环引用:虽然设备树允许节点属性的值是另一个节点,但需要注意避免循环引用的情况。循环引用可能导致设备树解析器进入无限循环,从而无法正确解析设备树。
语义清晰性:当使用节点作为属性值时,应确保这种引用关系具有明确的语义意义。这有助于提高设备树的可读性和可维护性。
综上所述,设备树中节点属性的值确实可以是另一个节点,并且这种特性在嵌入式系统的硬件描述和配置中发挥着重要作用。通过合理使用节点引用和phandle机制,可以构建出更加灵活、可扩展和可维护的设备树结构。
更多补充
中断结点
#interrupt-cells
是一个整数类型的属性,用于指定在设备树的中断描述中使用多少个单元来表示一个中断。例如,如果#interrupt-cells
的值为2,那么在描述每个中断时,需要使用2个单元的数据来表示相关信息。注意,不是对应的reg,具体对应着什么,暂时我也不知道,后面再看。在设备树中,
interrupt-controller
属性通常是一个空属性,其作用是声明当前节点是一个中断控制器。通过设置interrupt-controller
属性(即使为空),可以让设备树和系统清晰地知道该节点代表的是一个中断控制器设备。这有助于在复杂的硬件架构中快速识别和定位负责中断处理的设备,方便系统对其进行正确的配置和管理。在设备树中,存在各种不同类型的设备节点,如CPU、内存、I/O设备等。interrupt-controller
属性为空可以作为一种简单的标识方式,将中断控制器与其他普通设备区分开来,避免混淆,确保系统能够准确地识别和处理中断相关的设备。上面的#interrupt-cells应该对应的是子结点的interrupts属性的表示个数。
interrupts
属性是一个整数数组,用于指定与该设备相关联的中断请求信号的信息。每个元素通常包含两个主要部分信息,一是中断控制器的引用(通过 phandle 或设备树节点路径表示),二是中断编号或中断名称(具体取决于设备和平台的表示方式)。例如,
interrupts = <&intc 23 0x04>;
这个属性值中,&intc
是中断控制器的设备树节点标签,23
是中断编号,0x04
表示中断触发类型(如上升沿触发等)。作用与意义
硬件中断配置:它告诉操作系统或硬件抽象层(HAL)当设备需要产生中断时,应该向哪个中断控制器发送信号,以及使用哪个中断号。这样,当设备发生特定事件(如数据接收、错误发生等)需要通知处理器时,处理器能够正确地响应并执行相应的中断服务程序。
系统资源分配和管理:系统可以根据
interrupts
属性的信息,为设备分配合适的中断处理资源,确保不同设备的中断请求能够得到及时、准确的处理,避免中断冲突和混乱。驱动程序开发:对于设备驱动开发者来说,
interrupts
属性提供了关键的信息,以便编写正确的中断处理代码。驱动程序需要根据这个属性的值来注册相应的中断处理函数,使设备能够正常工作。常见属性值的含义
中断编号:如果
interrupts
属性值中只包含一个数字,那么这个数字通常代表中断编号。例如,在一些简单的系统中,interrupts = <5>;
表示设备使用中断编号为 5 的中断线。中断名称:在某些情况下,可能会使用中断名称来代替中断编号。例如,
interrupts = <"irq_name">;
这里的"irq_name"
是在设备树中定义的一个中断名称标识符。这种方式可以使设备树更加易读和可维护,尤其是当中断编号较多且不易理解时。复合值:有些复杂的设备可能会有更复杂的
interrupts
属性值,包含多个单元的复合值。这些复合值可能包括中断域、触发方式、CPU 接口信息等。例如,interrupts = <gic_ppi>;
这个属性值中,GIC_PPI
表示这是一个与通用中断控制器(GIC)相关的私有外设中断(PPI),37
是中断号,1
表示中断触发类型等信息。总之,设备树中的
interrupts
属性至关重要,它不仅明确了设备的中断请求信号细节,还为系统资源分配、驱动程序开发提供了关键依据,确保了硬件中断的有效管理和设备的正常运行。具体还要看程序里是怎么获取的。
#clock-cells
通过指定
#clock-cells
的值,可以明确子节点的时钟信息在设备树中应该如何被表示和解析。例如,如果#clock-cells = <2>
,则表示该节点下的子节点时钟信息将使用 2 个单元的数据来描述。这有助于系统准确地理解和处理不同节点的时钟配置信息,确保硬件设备的时钟设置能够被正确地识别和应用。
芯片上的外设通常都作为soc结点的子结点。
设备树的语法规定了属性值可以是整数、字符串、数组等多种形式。当属性值为数组时,尖括号用于标识数组的开始和结束。分开写尖括号并不影响设备树编译器对属性值的解析,因为编译器会按照设备树的语法规则正确处理这种写法。例如,对于一个包含多个数值的数组属性
reg = <0x1000 0x2000 0x3000>;
,无论是连续写还是分开写尖括号,如reg = <0x1000>,<0x2000>,<0x3000>;
,中间以逗号或者空格隔开应该都可以,设备树编译器都能识别并将其解析为一个包含三个元素的数组。也可以分行写reg = <0x1000>,
<0x2000>,
<0x3000>;
比如:
gpio-controller
在设备树(Device Tree)中,
gpio-controller
属性通常用于标识某个节点代表一个 GPIO(通用输入输出)控制器。这个属性告诉系统该节点下的硬件资源是与 GPIO 控制相关的,使得操作系统能够正确地识别和使用这些 GPIO 引脚。
使用pinctrl子系统时,我们通常都会将所有的pin引脚信息都放在一起;然后具体被哪个外设使用时,需要再在对应外设节点中引用pin信息。
目前还是没太搞明白,pin,gpio和具体外设之间的设备树的关系,所有的引脚都是pin,需要先设置好pin的复用功能、驱动能力以及上下拉等基本属性;如果是复用为gpio,那么就作为gpio这种外设功能;但是,gpio也需要依附其他外设来使用的。这里要搞清楚,gpio是一种片上外设,一般都定义在soc节点中吧?然后片外的外设,就不放在soc里面?这块还得再研究研究,捋一捋。
clock-latency
在设备树(Device Tree)中,
clock-latency
是一个用于描述时钟切换延迟的属性,主要用于告知内核时钟在不同频率或源之间切换时所需的时间(通常以纳秒为单位)。这一属性帮助内核更精确地管理时钟资源,尤其是在涉及动态调频(DVFS)或电源管理的场景中。1. 基本作用
- 定义时钟切换延迟:
clock-latency
的值表示时钟从一种状态(如当前频率)切换到另一种状态(如目标频率)所需的最小时间(单位:纳秒)。- 辅助电源管理决策:内核的电源管理模块(如 CPUFreq)会根据此延迟值,判断是否进行时钟频率切换。例如,若切换延迟过大,频繁切换可能导致功耗或性能损失,内核会据此调整策略。
2. 典型使用场景
clock-latency
通常出现在与时钟相关的设备节点中,尤其是需要动态调整频率的硬件(如 CPU、GPU、总线控制器等):cpu0: cpu@0 {compatible = "arm,cortex-a53";reg = <0x0>;clocks = <&cpu_clk>;clock-latency = <100000>; // 时钟切换延迟为 100,000 纳秒(0.1 毫秒)operating-points-v2 = <&cpu_opp_table>; };
在上述示例中:
clock-latency = <100000>
表示该 CPU 的时钟在不同频率间切换时,至少需要 100,000 纳秒才能完成。- 内核在进行动态调频时,会参考此值避免过于频繁的切换(例如,短暂的负载波动可能不足以抵消切换带来的延迟开销)。
3. 注意事项
- 单位是纳秒:值的单位固定为纳秒(ns),需根据硬件手册填写实际测量的延迟时间。
- 与其他属性配合使用:通常与
operating-points-v2
(定义不同工作点的频率 / 电压)或clocks
(指定时钟源)等属性结合,共同实现动态时钟管理。- 可选属性:并非所有时钟相关节点都需要此属性,仅在设备支持动态时钟切换且需要内核优化切换策略时使用。
总结
clock-latency
是设备树中用于描述时钟切换延迟的属性,其核心作用是帮助内核在电源管理和动态调频中做出更合理的决策,平衡性能与功耗。它的值需根据硬件特性填写,直接影响时钟切换策略的有效性。