Linux设备树详解
设备树小故事
设备树(Device Tree
),将这个词分开就是“设备”和“树”,描述设备树的文件叫做 DTS
(Device Tree Source
),这个 DTS
文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU
数量、 内存基地址、 IIC
接口上接了哪些设备、 SPI
接口上接了哪些设备等等。
在没有使用设备树的时候,有关板级的硬件信息都被硬编码在内核中,这就导致了内核中描述板级硬件的代码过于庞大,不利于阅读。
随着智能手机的发展,每年新出的 ARM 架构芯片少说都在数十、数百款, Linux 内核下板级信息文件将会成指数级增长!这些板级信息文件都会被硬编码进 Linux 内核中,导致 Linux 内核**“虚胖”**。
当 Linux 之父 linus
看到 ARM 社区向 Linux 内核添加了大量无用、冗余的板级信息文件,不禁的发出了一句This whole ARM thing is a fucking pain in the ass
。
从此以后 ARM 社区就引入了 PowerPC
等架构已经采用的设备树(Flattened Device Tree
)
这个就是设备树的由来,简而言之就是, Linux 内核中 ARM 架构下有太多的冗余的垃圾板级信息文件,导致 linus
震怒,然后 ARM 社区引入了设备树。
设备树文件
设备树源文件扩展名为.dts
,另外DTS
是设备树源码文件, DTB
是将DTS
编译以后得到的二进制文件。
对于设备树文件的编译我们可以使用DTC
工具对其进行编译,DTC
工具源码在Linux内核的scripts/dtc
目录下。
DTC
工具依赖于 dtc.c
、 flattree.c
、 fstree.c
等文件,最终编译并链接出 DTC
这
个主机文件。
如果要编译 DTS
文件的话只需要进入到 Linux 源码根目录下,然后执行如下命
令:
make dtbs
Linux设备树文件在内核源码中的 arch/arm/mach-xxx
文件夹和arch/arm/plat-xxx
文件夹中
使用设备树
我们想要使用设备树首先需要修改设备树文件,设备树文件的位置我们上面已经所过了,大家可以去找一下:
修改设备树文件
我们想要使用设备树首先需要修改设备树文件,我们需要首先找到我们需要修改的设备树文件,如果不知道对应模块的设备树文件在哪个位置的话我上面有讲到,设备树文件都在内核源码中的 arch/arm/mach-xxx
文件夹和arch/arm/plat-xxx
文件夹中。
在文件夹中找到我们芯片或者开发板所对应的DTS
文件,例如我使用的开发板是野火公司的I.MX6U
开发板,所以我们需要首先找到这个开发板所对应的设备树,我们所有的设备都需要在这个设备树文件中进行描述。如果没有可能是内核版本太老所致。
找到我们需要修改的设备树文件后打开对应的.dts
文件,在根节“/”
下创建一个名为“alphaled”
的子节点,打开 imx6ull-alientek-emmc.dts
文件,
在根节点“/”
最后面输入如下所示内容:
1 alphaled {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = "atkalpha-led";
5 status = "okay";
6 reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
7 0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
8 0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
9 0X0209C000 0X04 /* GPIO1_DR_BASE */
10 0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
11 };
- 第
2、 3
行:属性#address-cells
和#size-cells
都为 1,表示reg
属性中起始地址占用一个字长(cell
),地址长度也占用一个字长(cell
)。 - 第
4
行:属性compatbile
设置alphaled
节点兼容性为“atkalpha-led”
。 - 第
5
行:属性 status 设置状态为“okay”
。 - 第
6~10
行:reg
属性,非常重要!reg
属性设置了驱动里面所要使用的寄存器物理地址,比如第6
行:“0X020C406C 0X04”
表示I.MX6ULL
的CCM_CCGR1
寄存器,其中寄存器首地址为0X020C406C
,长度为4
个字节。
编译设备树
设备树文件修改之后我们需要首先进行编译以下,正如我们编译C语言一样,DTS文件也是需要编译的,我们使用以下命令对设备树文件DTS进行编译。
make dtbs
编译完成以后得到 imx6ull-alientek-emmc.dtb
,使用新的 imx6ull-alientek-emmc.dtb
启动Linux 内核。 Linux 启动成功以后进入到/proc/device-tree/
目录中查看是否有“alphaled”
这个节点,结果如图:
异常处理
如果没有“alphaled”
节点的话请重点下面两点:
- 检查设备树修改是否成功,也就是
alphaled
节点是否为根节点“/”
的子节点。 - 检查是否使用新的设备树启动的 Linux 内核。
可以进入到图 44.3.1 中的 alphaled 目录中,查看一下都有哪些属性文件,结果如图 所示:
大家可以查看一下 compatible
、 status
等属性值是否和我们设置的一致。如果不一致修改过来即可。
编写驱动文件
在编写驱动文件过程中我们只需要关注一下设备树的改动,其他例如申请设备号啥的和原来是一样的,我们现在不需要关注,如果你对字符设备驱动框架的理解还不是很深,你可以看我写的这篇文章:
回归主题,我们现在开始修改设备树相关代码,我们首先在驱动入口处添加以下代码:
/* 获取设备树中的属性数据 */
/* 1、获取设备节点: alphaled */
dtsled.nd = of_find_node_by_path("/alphaled");
if(dtsled.nd == NULL) {
printk("alphaled node not find!\r\n");
return -EINVAL;
} else {
printk("alphaled node find!\r\n");
}
/* 2、获取 compatible 属性内容 */
proper = of_find_property(dtsled.nd, "compatible", NULL);
if(proper == NULL) {
printk("compatible property find failed\r\n");
} else {
printk("compatible = %s\r\n", (char*)proper->value);
}
/* 3、获取 status 属性内容 */
ret = of_property_read_string(dtsled.nd, "status", &str);
if(ret < 0){
printk("status read failed!\r\n");
} else {
printk("status = %s\r\n",str);
}
/* 4、获取 reg 属性内容 */
ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
if(ret < 0) {
printk("reg property read failed!\r\n");
} else {
u8 i = 0;
printk("reg data:\r\n");
for(i = 0; i < 10; i++)
printk("%#X ", regdata[i]);
printk("\r\n");
}
/* 初始化 LED */
#if 0
/* 1、寄存器地址映射 */
IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
GPIO1_DR = ioremap(regdata[6], regdata[7]);
GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
#else
IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
GPIO1_DR = of_iomap(dtsled.nd, 3);
GPIO1_GDIR = of_iomap(dtsled.nd, 4);
#endif
看到上面的代码是不是感觉加入设备树之后代码结构变得好复杂啊!还要获取那么多设备树属性和数据,这不是妥妥的给自己找不爽吗?
其实设备树的加入主要是为了减少这些板级信息被写入Linux内核中,因为如果不使用设备树的话我们每一个开发板都需要加入到Linux内核中,否者你就无法使用该款开发板,基于此,设备树就此诞生了,让我们每一个开发板都有一棵属于自己的树,板上的硬件相当于树上的分支,这样Linux在启动的时候就会先去找你所对应的设备树,相当于读到了你板子的信息,你就可以使用设备树了!
其实设备树的知识远不止于此,我目前也是囫囵吞枣,也有很多不明白的地方,大家可以多读几篇文章,看看大佬们怎么总结的。
参考资料
- 正点原子 I.MX6U 嵌入式 Linux 驱动开发指南
相关文章
- [Linux] linux awk命令详解
- [linux]Linux下的log
- linux 混杂设备驱动之adc驱动
- linux lcd设备驱动剖析一
- Linux下SVN的一些使用方法总结
- How to install shutter on Linux 20.04
- 【Linux基础】linux下的stdin,stdout和stderr理解
- 大叔经验分享(125)linux系统蓝牙设备频繁断开
- Spark修炼之道(基础篇)——Linux大数据开发基础:第十一节:Shell编程入门(三)
- 【学习总结】快速上手Linux玩转典型应用-第2章-linux简介
- Linux &用法和jobs命令
- linux如deepin manjaro对笔记本电脑电池的伤害解决方案:TLP:一个可以延长 Linux 笔记本电池寿命的高级电源管理工具
- Linux 卸载分区(会格式化分区数据)
- Linux 设备树 DTS 语法
- 【Linux 内核】调度器 ⑧ ( 进程优先级源码 includelinuxschedprio.h | 进程分类 | 实时进程 | 普通进程 | 进程优先级数值 | 0 ~ 99 实时进程 )
- Linux at命令
- L82.linux命令每日一练 -- 第11章 Linux系统管理命令 -- dmidecode和lspci
- L42.linux命令每日一练 -- 第七章 Linux用户管理及用户信息查询命令 -- groupdel和passwd
- L41.linux命令每日一练 -- 第七章 Linux用户管理及用户信息查询命令 -- userdel和groupadd
- Linux设备模型之kobject
- Linux系统之使用apache部署webserver下载站点
- 嵌入式Linux开发,Ubuntu22下交叉编译报错:arch64-linux-gnu-gcc: error while loading shared libraries: libstdc++.so.
- linux 修改最大文件数,进程数
- 第四章 linux字符设备的编写二
- linux 命令c语言代码实现
- Linux设备驱动中的ioctl
- Linux下安装Elasticsearch6.5