U-Boot设备树操作指南:查询与配置FDT节点
在嵌入式系统开发中,设备树(Device Tree,简称DT或FDT - Flattened Device Tree)是描述硬件配置的关键。U-Boot引导加载程序提供了强大的FDT工具,允许开发者在启动阶段查看和修改设备树节点。本文将详细介绍如何在U-Boot控制台中使用fdt命令进行设备树的查询与配置。
FDT命令概览
当系统在U-Boot控制台停下时,输入fdt命令,即可获取其使用帮助信息。
sunxi#fdt
fdt - flattened device tree utility commands
Usage:
fdt addr [-c] <addr> [<length>] - 设置当前操作的FDT地址 [并指定长度]
fdt move <fdt> <newaddr> <length> - 复制FDT到新地址并使其激活
fdt resize - 调整FDT大小至下一个4K对齐地址
fdt print <path> [<prop>] - 从指定路径递归打印节点及属性
fdt list <path> [<prop>] - 打印指定路径下的一级节点或属性
fdt get value <var> <path> <prop> - 获取属性值并存入变量
fdt get name <var> <path> <index> - 获取指定索引节点的名称并存入变量
fdt get addr <var> <path> <prop> - 获取属性起始地址并存入变量
fdt get size <var> <path> [<prop>] - 获取属性大小或节点数量并存入变量
fdt set <path> <prop> [<val>] - 设置指定路径节点的属性值
fdt mknode <path> <node> - 在指定路径下创建新节点
fdt rm <path> [<prop>] - 删除节点或属性
fdt header - 显示FDT头部信息
fdt bootcpu <id> - 设置启动CPU ID
fdt memory <addr> <size> - 添加/更新内存节点
fdt rsvmem print - 显示当前内存保留区域
fdt rsvmem add <addr> <size> - 添加内存保留区域
fdt rsvmem delete <index> - 删除内存保留区域
fdt chosen [<start> <end>] - 添加/更新/chosen分支
<start>/<end> - initrd起始/结束地址
注意: 省略路径前的'/'来解引用别名, 例如 'fdt print ethernet0'.
在这些命令中,fdt list和fdt set是日常调试中最常用的两个命令。fdt list用于查看设备树节点的配置信息,而fdt set则用于修改这些配置。
1. 设备树节点查询
在修改设备树配置之前,首先需要确定目标字段在设备树中的完整路径。如果路径不明确,可以按照以下步骤逐步查询。
1.1 从根节点开始探索
首先可以从设备树的根节点/开始查询,查看其直接子节点及属性。
sunxi#fdt list /
/ {
model = "{LICHEE_CHIP}";
compatible = "arm,{LICHEE_CHIP}", "arm,{LICHEE_CHIP}";
interrupt-parent = <0x00000001>;
#address-cells = <0x00000002>;
#size-cells = <0x00000002>;
......................
cpuscfg {
};
ion {
};
dram {
};
memory@40000000 {
};
interrupt-controller@1c81000 {
};
sunxi-chipid@1c14200 {
};
timer {
};
pmu {
};
dvfs_table {
};
dramfreq {
};
gpu@0x01c40000 {
};
wlan {
};
bt {
};
btlpm {
};
};
如果找到了目标配置,例如无线局域网(WLAN)的配置位于/wlan节点,可以直接使用fdt list命令查看其详细信息。
sunxi#fdt list /wlan
wlan {
compatible = "allwinner,sunxi-wlan";
clocks = <0x00000096>;
wlan_power = "vcc-wifi";
wlan_io_regulator = "vcc-wifi-io";
wlan_busnum = <0x00000001>;
status = "okay";
device_type = "wlan";
wlan_regon = <0x00000077 0x0000000b 0x00000002 0x00000001 0xffffffff 0xffffffff 0x00000000>;
wlan_hostwake = <0x00000077 0x0000000b 0x00000003 0x00000006 0xffffffff 0xffffffff 0x00000000>;
};
1.2 深入特定子节点:以 soc 节点为例
如果在根目录下未能找到所需配置,它可能位于更深层的节点中,例如/soc。
sunxi#fdt list /soc
soc@01c00000 {
compatible = "simple-bus";
#address-cells = <0x00000002>;
#size-cells = <0x00000002>;
ranges;
device_type = "soc";
......................
hdmi@01ee0000 {
};
tr@01000000 {
};
pwm@01c21400 {
};
nand0@01c03000 {
};
thermal_sensor {
};
cpu_budget_cool {
};
.......................
};
例如,要查看nand0设备的配置,可以使用完整的路径:
sunxi#fdt list /soc/nand0
nand0@01c03000 {
compatible = "allwinner,sun50i-nand";
device_type = "nand0";
reg = <0x00000000 0x01c03000 0x00000000 0x00001000>;
interrupts = <0x00000000 0x00000046 0x00000004>;
clocks = <0x00000004 0x0000007e>;
pinctrl-names = "default", "sleep";
pinctrl-1 = <0x00000081>;
nand0_regulator1 = "vcc-nand";
nand0_regulator2 = "none";
nand0_cache_level = <0x55aaaa55>;
nand0_flush_cache_num = <0x55aaaa55>;
nand0_capacity_level = <0x55aaaa55>;
nand0_id_number_ctl = <0x55aaaa55>;
nand0_print_level = <0x55aaaa55>;
nand0_p0 = <0x55aaaa55>;
nand0_p1 = <0x55aaaa55>;
nand0_p2 = <0x55aaaa55>;
nand0_p3 = <0x55aaaa55>;
status = "disabled";
nand0_support_2ch = <0x00000000>;
pinctrl-0 = <0x000000a9 0x000000aa>;
};
1.3 利用设备树别名简化路径
设备树中的/aliases节点定义了设备完整路径的短名称(别名),这可以极大地简化查询操作。我们可以先查看所有定义的别名:
sunxi#fdt list /aliases
aliases {
serial0 = "/soc@01c00000/uart@01c28000";
..............
mmc0 = "/soc@01c00000/sdmmc@01c0f000";
mmc2 = "/soc@01c00000/sdmmc@01C11000";
nand0 = "/soc@01c00000/nand0@01c03000";
disp = "/soc@01c00000/disp@01000000";
lcd0 = "/soc@01c00000/lcd0@01c0c000";
hdmi = "/soc@01c00000/hdmi@01ee0000";
pwm = "/soc@01c00000/pwm@01c21400";
boot_disp = "/soc@01c00000/boot_disp";
};
sunxi#
有了别名,就可以直接使用别名来查询节点,例如查询nand0:
sunxi#fdt list nand0
nand0@01c03000 {
compatible = "allwinner,sun50i-nand";
device_type = "nand0";
reg = <0x00000000 0x01c03000 0x00000000 0x00001000>;
..................
pinctrl-names = "default", "sleep";
pinctrl-1 = <0x00000081>;
};
请注意,在所有的fdt命令中,别名都可以作为path参数使用,例如fdt list <alias>或fdt set <alias> <prop> <val>。
2. 设备树节点修改
fdt set命令用于修改设备树中的属性值。它支持整数、字符串以及它们的数组形式。
2.1 修改整数属性
修改整数属性的命令格式为:fdt set <path> <prop> <value>。请注意,整数值通常需要用尖括号< >包围。
示例: 修改/wlan节点下的wlan_busnum属性。
sunxi#fdt list /wlan
wlan {
compatible = "allwinner,sunxi-wlan";
clocks = <0x00000096>;
wlan_power = "vcc-wifi";
wlan_io_regulator = "vcc-wifi-io";
wlan_busnum = <0x00000001>; // 当前值为1
status = "disable";
device_type = "wlan";
};
sunxi#fdt set /wlan wlan_busnum <0x2>
sunxi#fdt list /wlan
wlan {
compatible = "allwinner,sunxi-wlan";
clocks = <0x00000096>;
wlan_power = "vcc-wifi";
wlan_io_regulator = "vcc-wifi-io";
wlan_busnum = <0x00000002>; // 修改后值为2
status = "disable";
device_type = "wlan";
};
如果需要设置整数数组,只需用空格分隔各个数值,例如:fdt set <path> <prop> <0x1 0x2 0x3>。
2.2 修改字符串属性
修改字符串属性的命令格式为:fdt set <path> <prop> "string_value"。字符串值需要用双引号"包围。
示例: 修改/wlan节点下的status属性。
sunxi#fdt list /wlan
wlan {
compatible = "allwinner,sunxi-wlan";
clocks = <0x00000096>;
wlan_power = "vcc-wifi";
wlan_io_regulator = "vcc-wifi-io";
wlan_busnum = <0x00000001>;
status = "okay"; // 当前状态为 "okay"
device_type = "wlan";
};
sunxi#fdt set /wlan status "disable"
sunxi#fdt list /wlan
wlan {
compatible = "allwinner,sunxi-wlan";
clocks = <0x00000096>;
wlan_power = "vcc-wifi";
wlan_io_regulator = "vcc-wifi-io";
wlan_busnum = <0x00000001>;
status = "disable"; // 修改后状态为 "disable"
device_type = "wlan";
};
sunxi#
与整数数组类似,字符串数组也通过空格分隔实现,例如:fdt set <path> <prop> "string1" "string2"。
3. GPIO与PIN配置的特殊处理
GPIO(通用输入输出)和PIN(引脚)的配置在设备树中通常以特定的数字编码或数组形式表示。理解这些编码对于正确配置至关重要。
3.1 GPIO端口数字编码
在许多Allwinner芯片平台中,GPIO端口被映射为数字编号,如下所示:
#define PA 0
#define PB 1
#define PC 2
#define PD 3
#define PE 4
#define PF 5
#define PG 6
#define PH 7
#define PI 8
#define PJ 9
#define PK 10
#define PL 11
#define PM 12
#define PN 13
#define PO 14
#define PP 15
#define default 0xffffffff // 表示使用默认值
这意味着PH7代表7号端口的第7个引脚(假设从0开始计数)。
3.2 PIN控制(Pinctrl)配置
Pinctrl节点用于管理GPIO引脚的复用功能、上下拉电阻、驱动能力和输出电平。通常存在cpux和cpus两个Pinctrl控制器节点,路径如下:
Cpux:/soc/pinctrl@01c20800Cpus:/soc/pinctrl@01f02c00
PIN配置属性字段说明:
| 属性字段 | 含义 |
|---|---|
allwinner,function |
对应引脚组的功能,如"lcd0" |
allwinner,pins |
关联的物理引脚名称列表,如"PD12", "PD13" |
allwinner,pname |
对应allwinner,pins中每个引脚的逻辑名称 |
allwinner,muxsel |
引脚的复用功能选择,数字编码 |
allwinner,pull |
内部电阻状态(上拉/下拉/无),数字编码 |
allwinner,drive |
驱动能力设置,数字编码 |
allwinner,data |
输出电平状态,数字编码 |
其中0xffffffff表示使用默认配置。
查看PIN配置示例: 查看cpux控制器下lcd0引脚组的配置。
sunxi#fdt list /soc/pinctrl@01c20800/lcd0
lcd0@0 {
linux,phandle = <0x000000ab>;
phandle = <0x000000ab>;
allwinner,pins = "PD12", "PD13", "PD14", "PD15", "PD16", "PD17", "PD18", "PD19", "PD20", "PD21";
allwinner,function = "lcd0";
allwinner,pname = "lcdd0", "lcdd1", "lcdd2", "lcdd3", "lcdd4", "lcdd5", "lcdd6", "lcdd7", "lcdd8", "lcdd9";
allwinner,muxsel = <0x00000003>;
allwinner,pull = <0x00000000>;
allwinner,drive = <0xffffffff>; // 当前驱动能力为默认值
allwinner,data = <0xffffffff>;
};
sunxi#
修改PIN配置示例: 将lcd0引脚组的驱动能力设置为0x1。
sunxi#fdt set /soc/pinctrl@01c20800/lcd0 allwinner,drive <0x1>
sunxi#fdt list /soc/pinctrl@01c20800/lcd0
lcd0@0 {
linux,phandle = <0x000000ab>;
phandle = <0x000000ab>;
allwinner,pins = "PD12", "PD13", "PD14", "PD15", "PD16", "PD17", "PD18", "PD19", "PD20", "PD21";
allwinner,function = "lcd0";
allwinner,pname = "lcdd0", "lcdd1", "lcdd2", "lcdd3", "lcdd4", "lcdd5", "lcdd6", "lcdd7", "lcdd8", "lcdd9";
allwinner,muxsel = <0x00000003>;
allwinner,pull = <0x00000000>;
allwinner,drive = <0x00000001>; // 驱动能力已修改为1
allwinner,data = <0xffffffff>;
};
说明: 对allwinner,drive的修改会影响allwinner,pins中列出的所有相关引脚。同样,修改allwinner,muxsel、allwinner,pull或allwinner,data也会对整个引脚组产生类似的影响。
3.3 GPIO通用配置
在某些设备树节点中,GPIO配置可能以一个整数数组的形式直接定义。以USB控制器中的usb_id_gpio属性为例:
sunxi#fdt list /soc/usbc0
usbc0@0 {
test = <0x00000002 0x00000003 0x12345678>;
device_type = "usbc0";
compatible = "allwinner,sun50i-otg-manager";
........
usb_serial_unique = <0x00000000>;
usb_serial_number = "20080411";
rndis_wceis = <0x00000001>;
status = "okay";
usb_id_gpio = <0x00000030 0x00000007 0x00000009 0x00000000 0x00000001 0xffffffff 0xffffffff>;
};
对usb_id_gpio = <0x00000030 0x00000007 0x00000009 0x00000000 0x00000001 0xffffffff 0xffffffff>这个数组的解释如下:
| 属性数值 | 含义 |
|---|---|
0x00000030 |
设备树内部节点信息 |
0x00000007 |
GPIO端口号(PH) |
0x00000009 |
端口内引脚序号(PH9) |
0x00000000 |
功能分配(输入) |
0x00000001 |
内部电阻状态(上拉) |
0xffffffff |
驱动能力(默认值) |
0xffffffff |
输出电平(默认值) |
修改GPIO配置示例: 假设我们要修改usb_id_gpio的驱动能力和输出电平。
sunxi#fdt set /soc/usbc0 usb_id_gpio <0x00000030 0x00000007 0x00000009 0x00000000 0x00000001 0x2 0x1>
sunxi#fdt list /soc/usbc0
usbc0@0 {
test = <0x00000002 0x00000003 0x12345678>;
device_type = "usbc0";
compatible = "allwinner,sun50i-otg-manager";
........
usb_serial_unique = <0x00000000>;
usb_serial_number = "20080411";
rndis_wceis = <0x00000001>;
status = "okay";
usb_id_gpio = <0x00000030 0x00000007 0x00000009 0x00000000 0x00000001 0x00000002 0x00000001>; // 驱动能力和输出电平已修改
};
sunxi#