今天和大家分享的依然是设备树,上一节里主要是介绍了设备树文件的基本格式、语法规则等,今天介绍一下如何使用设备树,以及如何动态加载设备树。
设备树里记录的是“资源”,比如我们要点亮led,就可以增加一个led的节点,把led相关的寄存器放在这个节点里。
rgb_led{
#address-cells = <1>;
#size-cells = <1>;
compatible = "fire,rgb_led";
/*红灯节点*/
ranges;
rgb_led_red@0x020C406C{
compatible = "fire,rgb_led_red";
reg = <0x020C406C 0x00000004
0x020E006C 0x00000004
0x020E02F8 0x00000004
0x0209C000 0x00000004
0x0209C004 0x00000004>;
status = "okay";
};
/*绿灯节点*/
rgb_led_green@0x020C4074{
compatible = "fire,rgb_led_green";
reg = <0x020C4074 0x00000004
0x020E01E0 0x00000004
0x020E046C 0x00000004
0x020A8000 0x00000004
0x020A8004 0x00000004>;
status = "okay";
};
/*蓝灯节点*/
rgb_led_blue@0x020C4074{
compatible = "fire,rgb_led_blue";
reg = <0x020C4074 0x00000004
0x020E01DC 0x00000004
0x020E0468 0x00000004
0x020A8000 0x00000004
0x020A8004 0x00000004>;
status = "okay";
};
};
rgb_led就是在根节点下的一个节点,在这个节点里面又有3个子节点,代表3颗led,里面用#address-cells = <1>和#size-cells = <1>用来表示寄存器(子节点的reg属性)的地址宽度是32位的,长度是一个字长(也是32位)。
这样我们的设备树文件就写好了,参照上一节的做法,我们修改完设备树文件,然后进行编译,将生成的dtb文件替换开发板原来的dtb文件,然后重启开发板即可。
这里需要注意的一点就是,我们使用cp命令进行拷贝的时候,拷贝完最好使用sync命令进行同步,sync的作用就是将缓冲区的内容写到磁盘上,如果没有使用sync就直接给开发板断电,可能会造成数据的丢失,到时可能因为无效的设备树文件导致系统启动不了。
因为我为了确保开发板的dtb文件是我新生成的,所以在拷贝之前,我把原来的删掉了,再进行拷贝,而如果拷贝失败,就会导致dtb文件缺失。如果不把原来的删除,而直接使用cp命令拷贝,即使拷贝失败,也不会因为dtb文件缺失而启动不了。另外,重启开发板可以断电重启,也可以使用reboot命令重启,使用reboot命令重启应该会更安全一些,不容易数据丢失。如果非要断电重启,最好在断电之前敲几次sync命令。
设备树文件写好了,接下来就是写驱动文件了。其实驱动文件和我们之前在Linux笔记(21)| platform总线驱动分析介绍的基本是一样的,唯一的不同就是资源获取方式不一样,之前是在设备文件中获取,现在是在设备树文件上获取。所以重点说一下这个部分,其他的就不赘述了。
我们先定义一个led_resource类型的结构体,用来存放led相关的资源,在这个结构体里有led相关的寄存器地址(注意这个是虚拟地址),还有设备节点device_node。
struct led_resource
{
struct device_node *device_node; //rgb_led_red的设备树节点
void __iomem *virtual_CCM_CCGR;
void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD;
void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD;
void __iomem *virtual_DR;
void __iomem *virtual_GDIR;
};
我们实际上要调用of_find_node_by_path等一系列函数从设备树上获取资源,返回值用上面的device_node来接收。然后将device_node里面拿到的真实的物理地址进行虚拟地址映射,得到虚拟地址放在上面的结构体成员里面。然后就像裸机里面一样了,进行硬件初始化工作,给应用层提供一些接口。
因为这里是操作led,本身比较简单,所以不需要提供什么接口,只需要把对硬件的操作写好就行了。约定好从应用层接收到什么数据就进行什么操作。
整个的操作流程可以简单表示如下:
具体的说明可以参照平台总线那一节。这样基本上就完成了。
但是这样还不够好,因为每次修改设备树文件,都要修改内核源码,然后编译、拷贝、重启开发板。这样还是挺不方便的。尤其像内核源码,不应该随随便便去修改,这样子是不太安全的。
所以可以使用动态加载的方法。
动态加载的方法,首先也是写一个设备树文件,不过这个不是去内核源码修改,而是单独的一个文件,然后编译生成.dtbo文件。最后修改/boot/目录下的uEnv.txt文件,把dtbo文件加进去,最后reboot重启即可。关于这部分的详细操作,我们以后再介绍。