arch/arm/plat-xxx和arch/arm/mach-xxx中存在大量的板级信息,注册platform_device,绑定resource等,随着时间的推移,越来越多的板级信息加入,影响维护,这些具体的硬件信息应该和内核代码解耦。有了Device Tree后,大量的板级信息不再需要,都可以通过Device Tree api来做处理。Device Tree相当于一个硬件配置文件,kernel读取这个配置文件做对应的驱动初始化相关工作
内核目录
arch/arm/boot/dts/ arch/arm64/boot/dts/
.dts文件是Device Tree源文件,相当于配置文件,一个.dts文件对应一个具体的板级信息,公用的一部分提炼为.dtsi, 类似于C语言的头文件.
/dts-v1/; #include "rk3328.dtsi"
DTC (device tree compiler)
将.dts编译为.dtb的工具.
.dtb是.dts被DTC编译后的二进制格式的Device Tree描述,可由uboot和Linux内核解析。
Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。
- Node节点。在DTS中使用一对花括号”node-name{}”来定义;
- Property属性。在Node中使用”property-name=value”字符串来定义;
int u32 u64 string phandle(int)
compatible
“compatible”属性用来device和driver的适配,
推荐的格式为”manufacturer,model”,
fsl,mpc8641为特指的设备, ns16550类型更广泛
compatible = "fsl,mpc8641", "ns16550";
phandle
“phandle”属性通用一个唯一的id来标识一个Node,在property可以使用这个id来引用Node
pic@10000000 { // 将处理值定义为 1 phandle = <1>; interrupt-controller; };
another-device-node { // 引用phandle值为1的pic节点 interrupt-parent = <1>; };
定义一个“label:”来引用Node,在编译是系统会自动为node生成一个phandle属性。”backlight”是一个label,用来引用node”backlight”:
backlight: backlight { compatible = "pwm-backlight"; enable-gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>; pwms = <&pwm0 0 25000 0>; }
使用”&”来引用“label”,即是引用phandle
edp-panel { compatible = "boe,nv101wxmn51", "simple-panel"; backlight = <&backlight>; power-supply = <&pp3300_disp>; }
#address-cells 、 #size-cells
“#address-cells, #size-cells”属性用来定义当前node的子node中”reg”属性的解析格式。
- address-cells, 用几个数描述地址
- size-cells, 用几个数描述地址长度
soc { // 1个值表示地址 #address-cells = <1>; // 1个值表示长度 #size-cells = <1>; serial { compatible = "ns16550"; reg = <0x4600 0x100>; clock-frequency = <0>; interrupts = <0xA 0x8>; interrupt-parent = <&ipic>; }; };
reg
“reg”属性解析出”address,length”数字,解析格式依据父节点的”#address-cells、#size-cells”定义
soc { // 1个值表示地址 #address-cells = <1>; // 1个值表示长度 #size-cells = <1>; serial { compatible = "ns16550"; reg = <0x4600 0x100>; clock-frequency = <0>; interrupts = <0xA 0x8>; interrupt-parent = <&ipic>; }; };
0x4600表示对应的基地址,0x100表示地址对应的大小为0x100
i2c@1,0 { compatible = "acme,a1234-i2c-bus"; #address-cells = <1>; #size-cells = <0>; rtc@58 { compatible = "maxim,ds1338"; reg = <58>; }; };
address-cells = <1>;size-cells = <0>;58表示i2c对应从设备地址,映射长度为0
节点的别名
aliases { ethernet0 = &gmac; i2c0 = &i2c0; serial0 = &uart0; spi0 = &spi0; };
memory 节点
用来传递内存布局
memory { device_type = "memory"; reg = <0x0 0x0 0x0 0x40000000>; };
chosen 节点
- “bootargs”属性用来传递cmdline参数
- “stdout-path”属性用来指定标准输出设备
- “stdin-path”属性用来指定标准输入设备
chosen { bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200"; };
常用OF API
判断设备结点的compatible属性是否包含compat指定的字符串
int of_device_is_compatible(const struct device_node *device,const char *compat);
根据compatible属性,获得设备结点。遍历Device Tree中所有的设备结点,看看哪个结点的类型、compatible属性与本函数的输入参数匹配,大多数情况下,from、type为NULL。
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible);
读取设备结点np的属性名为propname,类型为8、16、32、64位整型数组的属性
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz); int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz); int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz); int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value);
读取字符串属性
int of_property_read_string(struct device_node *np, const char *propname, const char **out_string);
读取字符串数组属性中的第index个字符串
int of_property_read_string_index(struct device_node *np, const char *propname, int index, const char **output);
如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在
static inline bool of_property_read_bool(const struct device_node *np, const char *propname);
博客推荐
http://kernel.meizu.com/device-tree.html
https://blog.csdn.net/21cnbao/article/details/8457546
https://elinux.org/Device_Tree_Usage
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)