> 文章列表 > Linux 内核设备树(DTS)简述

Linux 内核设备树(DTS)简述

Linux 内核设备树(DTS)简述

Linux 内核 设备树(DTS)简述

Linux设备树(Device Tree)是一种描述硬件设备的数据结构,它将硬件设备的信息以树形结构的方式组织起来,方便内核和驱动程序的访问和管理。在Linux系统中,设备树通常被用于描述嵌入式系统中的硬件设备,如处理器、内存、外设等。
在Linux内核中,设备树通常被用来描述嵌入式系统中的硬件设备。由于嵌入式系统中的硬件设备种类繁多,而且每个设备的配置都可能不同,因此使用设备树可以方便地描述和配置这些设备。


提示:内核设备树遵循-c99标准,先声明再使用。

文章目录

  • Linux 内核 设备树(DTS)简述
  • 圈重点 看想学
  • 1. DTS结构描述
    • 1.1 dts
    • 1.2 dtsi
    • 1.3 dts/dtsi 再编辑
  • 2 设备树生命周期
    • 2.1 设备树的结构
    • 2.2 设备树的编写
    • 2.3 设备树的编译
    • 2.4 设备树的加载
    • 2.5 设备树的使用
  • 总结

圈重点 看想学

a) 设备树声明属性
b) 设备树巧用


1. DTS结构描述

1.1 dts

设备树的结构由三部分组成:头部信息、设备树节点和属性。其中,头部信息包括设备树的版本号、厂商名称、机器型号等信息;设备树节点则是设备树的核心部分,它描述了硬件设备的层次结构和属性信息;属性则是设备树节点的附加信息,用于描述设备的特性和配置信息。
接下来看一个简单的设备树示例:

/dts-v1/;#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include "rk3568.dtsi"
#include "rk3568-evb.dtsi"
#include "rk3568-android.dtsi"/ {model = "Rockchip RK3568 EVB5 DDR4 V10 Board";compatible = "rockchip,rk3568-evb5-ddr4-v10", "rockchip,rk3568";rk_headset: rk-headset {compatible = "rockchip_headset"headset_gpio = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>;pinctrl-names = "default";pinctrl-0 = <&hp_det>;io-channels = <&saradc 2>;};vcc2v5_sys: vcc2v5-ddr {compatible = "regulator-fixed";regulator-name = "vcc2v5-sys";regulator-always-on;regulator-boot-on;regulator-min-microvolt = <2500000>;regulator-max-microvolt = <2500000>;vin-supply = <&vcc3v3_sys>;};
};&pinctrl {headphone {hp_det: hp-det {rockchip,pins = <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_down>;};};
};

这个设备树描述了一个嵌入式系统,包括一个CPU和一个UART串口。设备树的版本号是“/dts-v1/”,“#include” 包含头文件和子设备树"dtsi"等多重引用。设备树的根节点是“/”,它包含了系统的基本信息,型号和兼容性。内部包含了两个子节点:rk-headset和vcc2v5-ddr。其中,rk-headset 节点描述了系统的耳机,vcc2v5-ddr节点描述了DDR的电源属性,包括开关状态、电压范围和该输入电压源。另外,设备树中还包含了一个rk-headset节点,它描述了耳机检测的信息,包括中断GPIO、GPIO 设定状态和hook检测ADC通道。

1.2 dtsi

仔细浏览DTS会发现#include <dt-bindings/gpio/gpio.h>#include <dt-bindings/pinctrl/rockchip.h> 是标准头文件引用。#include "rk3568.dtsi"#include "rk3568-evb.dtsi"#include "rk3568-android.dtsi"是子设备树dtsi引用。
rk3568-android.dtsi为例,这个子设备树描述基础功能嵌入式系统,设备树的根节点是“/”,它包含了系统的别名定义和其它功能详细描述。

/ {chosen: chosen {bootargs = "earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0";};aliases {mmc0 = &sdmmc0;mmc1 = &sdmmc1;mmc2 = &sdhci;mmc3 = &sdmmc2;};fiq-debugger {compatible = "rockchip,fiq-debugger";rockchip,serial-id = <2>;rockchip,wake-irq = <0>;/* If enable uart uses irq instead of fiq */rockchip,irq-mode-enable = <1>;rockchip,baudrate = <1500000>;  /* Only 115200 and 1500000 */interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>;pinctrl-names = "default";pinctrl-0 = <&uart2m0_xfer>;status = "okay";};debug: debug@fd904000 {compatible = "rockchip,debug";reg = <0x0 0xfd904000 0x0 0x1000>,<0x0 0xfd905000 0x0 0x1000>,<0x0 0xfd906000 0x0 0x1000>,<0x0 0xfd907000 0x0 0x1000>;};cspmu: cspmu@fd90c000 {compatible = "rockchip,cspmu";reg = <0x0 0xfd90c000 0x0 0x1000>,<0x0 0xfd90d000 0x0 0x1000>,<0x0 0xfd90e000 0x0 0x1000>,<0x0 0xfd90f000 0x0 0x1000>;};
};&reserved_memory {linux,cma {compatible = "shared-dma-pool";inactive;reusable;reg = <0x0 0x10000000 0x0 0x00800000>;linux,cma-default;};ramoops: ramoops@110000 {compatible = "ramoops";reg = <0x0 0x110000 0x0 0xf0000>;record-size = <0x20000>;console-size = <0x80000>;ftrace-size = <0x00000>;pmsg-size = <0x50000>;};
};&rng {status = "okay";
};&rockchip_suspend {status = "okay";
};&vop {support-multi-area;
};

内部包含了多个子节点:chosen、aliases、debug@fd904000和cspmu@fd90c000等。其中,chosen 描述了内核启动参数,fiq-debugger描述了系统日志串口,debug@fd904000描述内核日志环的地址和大小,cspmu@fd90c000描述内存管理的地址。另外还有对 reserved_memory的重新定义,包含保留分区的工作方式,ramoops@110000 则是额外增加的功能描述;rng随机数生成功能开启;“rockchip_suspend" 休眠功能开启动,以及 vop 工作方式定义–支持多图块功能。

1.3 dts/dtsi 再编辑

内核设备树在被引用后,可以进程覆写或者删减原有的设备描述,还可以在根节点声明新的设备描述。

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/ Copyright (c) 2020 Rockchip Electronics Co., Ltd.*/#include "rk3568.dtsi"/ {aliases {/delete-property/ ethernet0;};
};&cpu0_opp_table {/delete-node/ opp-1992000000;
};&lpddr4_params {/* freq info, freq_0 is final frequency, unit: MHz */freq_0 = <1056>;
};&lpddr4x_params {/* freq info, freq_0 is final frequency, unit: MHz */freq_0 = <1056>;
};&power {pd_pipe@RK3568_PD_PIPE {reg = <RK3568_PD_PIPE>;clocks = <&cru PCLK_PIPE>;pm_qos = <&qos_pcie2x1>,<&qos_sata1>,<&qos_sata2>,<&qos_usb3_0>,<&qos_usb3_1>;};
};&rkisp {rockchip,iq-feature = /bits/ 64 <0x3FBF7FE67FF>;
};&usbdrd_dwc3 {phys = <&u2phy0_otg>;phy-names = "usb2-phy";extcon = <&usb2phy0>;maximum-speed = "high-speed";snps,dis_u2_susphy_quirk;
};/delete-node/ &combphy0_us;
/delete-node/ &gmac0_clkin;
/delete-node/ &gmac0_xpcsclk;
/delete-node/ &gmac0;
/delete-node/ &pcie30_phy_grf;
/delete-node/ &pcie30phy;
/delete-node/ &pcie3x1;
/delete-node/ &pcie3x2;
/delete-node/ &qos_pcie3x1;
/delete-node/ &qos_pcie3x2;
/delete-node/ &qos_sata0;
/delete-node/ &sata0;

在设备树中同样声明aliases,与上一个不同的此处是删除ethernet0;rk3566 芯片设计原因不支持1992MHz工作频点,&cpu0_opp_table中增加 删除相关频点;与此类似还有DDR4最大频点只支持1056MHz&lpddr4_params&lpddr4x_params则需要将工作频率改为freq_0 = <1056>;;rk3566 不支持gmac0,与匹配的删除描述是/delete-node/ &gmac0; 当前设备树类似操作还有很多,就不一一讲述。
设备树编译规则遵循,优先处理 dts,然后处理被引用的 dtsi,最终生成 dtb。dts 文件的编译优先级高于 dtsi 文件。如果 dts 文件和 dtsi 文件中有相同的节点或属性,dts 文件中的会覆盖 dtsi 文件中的。如果 dts 文件中引用了多个 dtsi 文件,后面引用的 dtsi 文件中的节点或属性会覆盖前面引用的 dtsi 文件中的。

  1. 覆写原有属性
# 覆写前rkisp: rkisp@fdff0000 {compatible = "rockchip,rk3568-rkisp";......iommus = <&rkisp_mmu>;rockchip,iq-feature = /bits/ 64 <0x3FBFFFE67FF>;status = "disabled";};
# 覆写后
&rkisp {rockchip,iq-feature = /bits/ 64 <0x3FBF7FE67FF>;
};
  1. 删除原有属性
	aliases {/delete-property/ ethernet0;};
  1. 删除原有子节点
# 根节点内删除
/ {/delete-node/ rockchip-system-monitor;
};# 根节点外删除,如 stata0
/delete-node/ &sata0;

2 设备树生命周期

在Linux系统中,设备树通常被用于描述硬件设备的信息,以便内核和驱动程序能够正确地访问和管理这些设备。设备树的编写需要一定的技术和经验,但是它可以大大简化系统的开发和维护工作,提高系统的可移植性和可扩展性。

2.1 设备树的结构

设备树的结构是一个树形结构,每个节点都有一个名称和一些属性。节点可以有子节点和兄弟节点,形成一个完整的树形结构。
设备树的根节点是一个特殊的节点,它没有名称和属性,只有子节点。根节点的子节点是整个设备树的顶层节点,通常是处理器节点。
设备树中的每个节点都有一个唯一的路径,路径是由节点名称和父节点路径组成的。例如,一个节点的路径可能是“/soc/uart@12340000”。

2.2 设备树的编写

设备树通常是以.dts或.dtsi文件的形式存在的。.dts文件是设备树的源文件,.dtsi文件是设备树的包含文件,可以在多个.dts文件中共享。
设备树的编写语言是一种类似于C语言的语言,但是语法和C语言有所不同。下面是一个简单的设备树示例:

/dts-v1/;
/ {model = "My Device";compatible = "my,device";memory {reg = <0x10000000 0x10000000>;};uart@12340000 {compatible = "my,uart";reg = <0x12340000 0x100>;interrupts = <0 10 0>;};
};

这个设备树描述了一个名为“My Device”的设备,包含一个内存节点和一个UART节点。UART节点的名称是“uart@12340000”,它的地址是0x12340000,大小是0x100,中断号是10。

2.3 设备树的编译

设备树需要编译成二进制格式才能被内核使用。编译设备树需要使用dtc工具,dtc是Device Tree Compiler的缩写。
编译设备树的命令如下:

dtc -I dts -O dtb -o mydevice.dtb mydevice.dts

这个命令将mydevice.dts文件编译成mydevice.dtb文件,mydevice.dtb文件是设备树的二进制格式。

2.4 设备树的加载

设备树在内核启动时被加载。内核会在启动时查找设备树,并将设备树的信息存储在内存中。设备树的信息可以通过/sys/firmware/devicetree/base目录下的文件来访问。
设备树的加载可以通过bootloader来完成,也可以通过内核模块来完成。如果使用内核模块加载设备树,需要使用of_platform_populate函数来注册设备树中的设备。

2.5 设备树的使用

设备树中的设备可以通过设备树绑定来使用。设备树绑定是一种将设备树中的设备和内核中的驱动程序绑定起来的方法。
设备树绑定需要在驱动程序中定义一个of_device_id结构体,该结构体包含设备树中设备的名称和属性。驱动程序在初始化时会遍历设备树,查找与of_device_id结构体匹配的设备,并将设备和驱动程序绑定起来。
下面是一个设备树绑定的示例:

static const struct of_device_id my_uart_of_match[] = {{ .compatible = "my,uart" },{ },
};
MODULE_DEVICE_TABLE(of, my_uart_of_match);static int my_uart_probe(struct platform_device *pdev)
{struct resource *res;void __iomem *regs;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);regs = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(regs))return PTR_ERR(regs);// do something with the UARTreturn 0;
}static struct platform_driver my_uart_driver = {.probe = my_uart_probe,.driver = {.name = "my_uart",.of_match_table = my_uart_of_match,.owner = THIS_MODULE,},
};module_platform_driver(my_uart_driver);

这个示例定义了一个名为“my_uart”的设备树绑定,它匹配的设备是“my,uart”。当设备树中出现一个名称为“uart”的节点,并且它的compatible属性为“my,uart”时,该设备就会被绑定到my_uart_driver驱动程序上。
在my_uart_probe函数中,可以通过platform_get_resource和devm_ioremap_resource函数来获取设备的资源,并将其映射到内存中。然后就可以对设备进行操作了。
以上就是Linux设备树的使用方法和示例介绍。设备树是嵌入式系统中描述硬件设备的重要工具,掌握设备树的使用方法对于嵌入式系统开发非常重要。

总结

活学活用,做个合格的搬运工。