Linux 设备树 DTS 语法
DTS 语法
.dtsi 头文件
设备树支持头文件,设备树的头文件扩展名为.dtsi
//linux-5.5.4\linux-5.5.4\arch\arm\boot\dts\s5pv210-smdkv210.dts
// SPDX-License-Identifier: GPL-2.0
/*
* Samsung's S5PV210 SoC device tree source
*
* Copyright (c) 2013-2014 Samsung Electronics, Co. Ltd.
*
* Mateusz Krawczuk <m.krawczuk@partner.samsung.com>
* Tomasz Figa <t.figa@samsung.com>
*
* Board device tree source for YIC System SMDV210 board.
*
* NOTE: This file is completely based on original board file for mach-smdkv210
* available in Linux 3.15 and intends to provide equivalent level of hardware
* support. Due to lack of hardware, _no_ testing has been performed.
*/
/dts-v1/;
// #include 来引用.h .dtsi .dts 文件
#include <dt-bindings/input/input.h>
#include "s5pv210.dtsi"
//...
.dtsi 文件 描述 SOC 的内部外设信息 (CPU架构 主频 外设寄存器地址范围)
// linux-5.5.4\linux-5.5.4\arch\arm\boot\dts\s5pv210.dtsi
// SPDX-License-Identifier: GPL-2.0
/*
* Samsung's S5PV210 SoC device tree source
*
* Copyright (c) 2013-2014 Samsung Electronics, Co. Ltd.
* 版权所有(c)2013-2014三星电子有限公司
*
* Mateusz Krawczuk <m.krawczuk@partner.samsung.com>
* Tomasz Figa <t.figa@samsung.com>
*
* Samsung's S5PV210 SoC device nodes are listed in this file. S5PV210
* based board files can include this file and provide values for board specfic
* bindings.
* 三星的S5PV210 SoC设备节点列在这个文件中
* 基于S5PV210的板文件可以包含此文件并为板特定绑定提供值
*
* Note: This file does not include device nodes for all the controllers in
* S5PV210 SoC. As device tree coverage for S5PV210 increases, additional
* nodes can be added to this file.
* 注意:此文件不包括S5PV210 SoC中所有控制器的设备节点
* 随着S5PV210的设备树覆盖率的增加,可以向该文件添加额外的节点
*/
#include <dt-bindings/clock/s5pv210.h>
#include <dt-bindings/clock/s5pv210-audss.h>
/ { // 根节点 /
#address-cells = <1>;
#size-cells = <1>;
aliases { //子节点
csis0 = &csis0;
//...
};
cpus { //子节点
#address-cells = <1>;
#size-cells = <0>;
cpu@0 { // cpus 的子节点
device_type = "cpu";
compatible = "arm,cortex-a8"; //架构信息
// reg 属性为 0,数据形式:32 位无符号整数
reg = <0>;
};
};
soc {
// : 前为 节点标签(label)
// :后为 节点名 是 ASCII 字符串
// @ 后为 设备的地址 或 寄存器首地址
// 可 &spi0 访问该节点
spi0: spi@e1300000 {
// compatible 属性为 "samsung,s5pv210-spi",数据形式:字符串
compatible = "samsung,s5pv210-spi";
// reg属性为 一组值
reg = <0xe1300000 0x1000>;
interrupt-parent = <&vic1>;
interrupts = <15>;
dmas = <&pdma0 7>, <&pdma0 6>;
dma-names = "tx", "rx";
clocks = <&clocks SCLK_SPI0>, <&clocks CLK_SPI0>;
clock-names = "spi", "spi_busclk0";
pinctrl-names = "default";
pinctrl-0 = <&spi0_bus>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
};
//...
};
#include "s5pv210-pinctrl.dtsi"
标准属性
节点是很多属性组成,节点是具体的设备
不同的设备有属性不同,用户可自定义属性
compatible 属性
static const struct of_device_id imx_wm8960_dt_ids[] = //匹配表
{
{
// compatible 属性 也叫 “兼容性”属性
// compatible 属性将 设备 和 驱动 绑定起来
// 厂商:fsl
// 驱动名字:imx-dudio-wm8960
.compatible = "fsl, imx-dudio-wm8960", //配置值
},
{
/*sentinel*/
}
};
MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);
static struct platform_driver imx_wm8960_driver =
{
.driver =
{
.name = "imx-wm8960",
.pm = &snd_soc_pm_ops,
.of_match_table = imx_wm8960_dt_ids, //platform_driver驱动模式,使用OF匹配表
},
.probe = imx_wm8960_probe,
.remore = imx_wm8960_remore,
};
model属性
model = "wm8960-audio"; //设备模块信息
status 属性
表示 设备的状态信息
值 | 描述 |
“okay” | 设备可操作 |
"disabled" | 设备不可操作,具体看设备绑定文件 |
"fail" | 设备不可操作,检测一系列的错误 |
"fail-sss" | sss:检测错误内容 |
#address-cells #size-cells 属性
#address-cells 属性值 决定 子节点reg属性的 地址信息 占用字长(32位)
#size-cells 属性值 决定 子节点reg属性的 长度信息 占用字长(32位)
// address length组合表示一个地址的范围
// address 表示 起始地址
// length 表示 地址长度
// #address-cells 表明 address 数据占用的字长
// #size-cells 表明 length 数据占用的字长
reg = <address1 length1 address2 length2 ...>
spi4
{
compatible = "spi-gpio";
#address-cells = <1>; //起始地址为1 字节
#size-cells = <0>; //地址长度为 0 字节
gpio_spi:gpio_spi@0
{
compatible = "fairchild, 74hc595";
reg = <0>; // reg属性为 0
};
};
aips3:aips-bus@02200000
{
comptible = "fsl, aips-bus", "simple-bus";
#address-cells = <1>; // 起始地址长度占用 为 1 字节
#size-cells = <1>; // 地址长度占用 为 1字节
dcp:dcp@02280000
{
compatible = "fsl, imx6sl-dcp";
//起始地址:0x02280000
//地址长度:0x40000
reg = <0x02280000 0x4000>; // 描述设备地址空间资源信息
};
};
ranges 属性
ranges属性值 可为 空 或者 按照(child-bus-address , parent-bus-address , length)格式编写的数字矩阵
ranges :地址映射 / 转换表
ranges 属性每个项目 由 子地址、父地址和 地址空间长度 三部分组成:
child-bus-address:子总线地址空间的物理地址,由父节点 #address-cells 确定 物理地址 的占用字长
parent-bus-address:父总线地址空间的物理地址,由父节点 #address-cells 确定 物理地址 的占用字长
length:子地址空间的长度,由 父节点#size-cells确定 地址长度 的占用字长
ranges 属性值为空,说明 子地址空间 和 父地址空间 一样
soc
{
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
// 子地址空间的物理起始地址为 0x0
// 父地址空间的物理起始地址为 0xe0000000
// 地址范围 0x00100000(1024KB)
ranges = <0x0 0xe0000000 0x00100000>;
serial // 串口设备节点
{
device_type = "serial";
compatible = "ns16550";
// 起始地址 0x4600
// 寄存器长度 0x100
// serial设备从 0xe0004600(0x4600 + 0xe0000000)开始读写
reg = <0x4600 0x100>;
clock-frequency = <0>;
interrupts = <0xA 0x8>;
interrupt-parent = <&ipic>;
};
};
device_type 属性
device_type 描述设备的 FCode
IEEE 1275 会用到 device_type
device_type属性只用于 cpu 节点 或者 memory 节点
cpu0:cpu@0
{
compatible = "arm, cortex-a7";
device_type = "cpu";
reg = <0>;
//...
}
根节点 compatible 属性
/
{
model = "Freescale i.MX6_ULL_14x14_EVK_Board";
compatible = "fsl,imx6ull-14x14-evk","fsl, imx6ull"; //匹配Linux内核中的驱动程序
//...
}
没用设备树时设备匹配方法
// arch/arm/mach-imx/machmx35_3ds.c
MACHINE_START(MX35_3DS, "Freescale MX35PDK")
/* Maintainer: Freescale Semiconductor, Inc */
/* 维护者:飞思卡尔半导体公司 */
.atag_offset = 0x100,
.map_io = mx35_map_io,
.init_early = imx35_init_early,
.init_irq = mx35_init_irq,
.init_time = mx35pdk_timer_init,
.init_machine = mx35_3ds_init,
.init_late = mx35_3ds_late_init,
.restart = mxc_restart,
MACHINE_END
MACHINE_START 和 MACHINE_END 定义
// arch/arm/include/asm/mach/arch.h
/*
* Set of macros to define architecture features.
* This is built into a table by the linker.
* 宏定义体系结构功能
* 这是由链接器内置到表中
*/
#define MACHINE_START(_type, _name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \ // machine id 设备ID
.name = _name,
#define MACHINE_END };
设备树的引入,已经不使用这个方法了
用设备树的设备匹配方法
// arch/arm/include/asm/mach/arch.h
#define DT_MACHINE_START(_name, _namestr) \
static const struct machine_desc __mach_desc_##_name \
__usend \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = ~0, //设备树不靠machine id 检查Linux内核是否支持某设备
.name = _namestr,
// linux-5.5.4/arch/arm/mach-s5pv210/s5pv210.c
static char const *const s5pv210_dt_compat[] __initconst=
{
"samsung, s5pc110",
"samsung, s5pv210",
NULL
};
DT_MACHINE_START(SSPV210_DT, "Samsung SSPC110/SSPV210-based board")
// 保存着本设备兼容属性
// 判断根节点compatible属性值与它是否相等
// 相等,Linux内核支持该设备
.dt_compat = s5pv210_dt_compat,
.map_io = s5pv210_dt_map_io,
.restart = s5pv210_dt_restart,
.init_late = s5pv210_dt_init_late,
MACHINE_END
Linux 内核调用 start_kernel 函数启动内核
// linux-5.5.4/init/main.c
asmlinkage __visible void __init start_kernel(void)
{
char *command_line;
char *after_dashes;
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them.
* 中断禁用 设置启动函数
*/
setup_arch(&command_line);
// ...
}
start_kernel 函数会调用setup_arch 函数来匹配 machine_desc
// arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
const struct machine_desc *mdesc;
setup_processor();
// 获取匹配的machine_desc
// atags的首地址(Linux 内核的 dtb 文件首地址):__atags_pointer
mdesc = setup_machine_fdt(__atags_pointer);
if(!mdesc)
{
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
}
if(!mdesc)
{
early_printf(" \nError:invalid dtb and unrecognized/unsupported machine ID\n");
early_printf(" r1 = 0x%08x, r2 = 0x%08x\n",__machine_arch_type, __atags_pointer);
if(__atags_pointer)
{
early_print("")
}
dump_machine_table();
}
machine_desc = mdesc;
machine_name = madesc->name;
dump_stack_set_arch_desc("%s",mdesc->name);
}
// arch/arm/kernel/devtree.c
/**
* setup_machine_fdt - Machine setup when an dtb was passed to the kernel
* setup_machine_fdt-将dtb传递到内核的 machine 设置
*
* @dt_phys: physical address of dt blob
* @dt_phys: dt blob的物理地址
*
* If a dtb was passed to the kernel in r2, then use it to choose the
* correct machine_desc and to setup the system.
* 如果在r2中将dtb传递给内核,则使用它来选择正确的machine_desc并设置系统
*/
const struct machine_desc *__init setup_machine_fdt(unsigned int dt_phys)
{
const struct machine_desc *__init setup_best = NULL;
if(!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
{
return NULL;
}
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);//获取匹配的machine_desc
if(!mdesc)
{
//...
}
//...
/* Change machine number to match the mdesc we're using */
__machine_arch_type = mdesc->nr;
}
// linux-5.5.4/arch/arm/kernel/devtree.c
// 获取 Linux 内核中下一个 machine_desc 结构体
static const void * __init arch_get_next_mach(const char *const **match)
{
static const struct machine_desc *mdesc = __arch_info_begin;
const struct machine_desc *m = mdesc;
if(m >= __arch_info_end)
{
return NULL;
}
mdesc++;
*match = m->dt_compat;
return m;
}
// linux-5.5.4/drivers/of/fdt.c
/**
* of_flat_dt_match_machine - Iterate match tables to find matching machine.
* 迭代匹配表以找到匹配的 machine
*
* @default_match: A machine specific ptr to return in case of no match.
* 在不匹配的情况下返回的 machine 具体 ptr
* @get_next_compat: callback function to return next compatible match table.
* 返回下一个兼容匹配表的回调函数
*
* Iterate through machine match tables to find the best match for the machine
* compatible string in the FDT.
* 遍历machine 匹配表以在FDT中找到与 machine 兼容的字符串的最佳匹配
*/
const void * __init of_flat_dt_match_machine(const void *default_match,
const void *(*get_next_compat)(const char *const**))
{
const void *data = NULl;
const void *best_data = default_match;
const char *const *compat;
unsigned long dt_root;
unsigned int best_score = ~1,score = 0;
dt_root =of_get_flat_dt_root(); // 获取设备树根节点
while(( data = get_next_compat(&compet) )) // 查找匹配的 machine_desc
{
score = of_flag_dt_match(dt_root,compat); //根节点 compatible 与每个dt_compat 比较
if(score > 0 && score < best_score)
{
best_data = data;
best_score = score;
}
}
if(!best_data)
{
const char *prop;
int size;
if(prop)
{
//...
}
return NULL;
}
pr_info("Machine model: %s\n", of_flat_dt_get_machine_name() );
return best_data;
}
Linux 内核通过根节点 compatible 属性找到对应的设备的函数调用过程
向节点追加或修改内容
设备树是描述板子硬件信息的文件
追加节点不能在 xxx.dtsi
xxx.dtsi 是设备树头文件
// xxx.dts
&i2cl //访问 i2c1
{
clock-frequency = <100000>; // 时钟为 100KHz
pinctrl-names = "defaulft";
pinctrl-0 = <&pinctrl_i2cl>;
status = "okay";
mag31100@0e // 子节点
{
compatible = "fsl, mag3110";
reg = <0x0e>;
position = <2>;
};
fxls8471@1e //子节点
{
compatible = "fsl,fxls8471";
reg = <0x1e>;
position = <0>;
interrupt-parent = <&gpio5>;
interrupts = <0 8>;
};
};