公众号欢迎关注本人微信公众号。 公众号分享更多的嵌入式知识和资料,分享个人学习嵌入式的心得。 请大家一起来玩。
一个语句谈到要理解linux设备树的提示:本文介绍了如何使用Rockchip平台在linux下应用设备树。 在其他平台上,虽然有些写法略有不同,但基本上不影响我们的学习。 百通,接下来开始设备树之旅吧。
文章的目录公众号表示,理解linux设备树的序言1、为什么要引用设备树的使用。 2、使用设备树给我们带来了那些好处吗? 另一方面,设备树1、设备树文件2、rk3399-tve1030g.dts3、内核内设备树文件3、总结2、设备树的节点1、设备树的i2c的使用方法总结3、设备树的使用方法总结3 3 .设备树中断的指定方法
前言为什么要引用设备树来使用? 新功能出现时,应该有疑问为什么要使用设备树。 使用设备树给我们带来了那些好处吗? 不使用设备树有不好的地方吗? 让我们逐一回答。
1、为什么要引用使用机器树? 其实,在较早的另一个平台上已经引用了设备树,只是在ARM平台上的引用延迟了,所以很多没有在ARM平台上部署设备树的早期开发人员都很熟悉这样的文件夹mach-xxx 在未浏览设备树之前,kernelarcharm目录下有许多与芯片和开发板相关的文件。 例如,三星芯片s5pv210 a在这个芯片上开发产品,在上面添加文件,b公司也用这个芯片在上面开发产品,在上面添加文件,世界上就有无数的公司在开发无数的产品,在这个目录下不久就会变得非常庞大于是,这时,linux的创始人linux的父亲Linus Torvalds看到这种情况时发了邮件。 thiswholearmthingisaf * ckingpainintheass。 通过这句话,ARM Linux社区现在部署设备树。
kernelarcharm目录中的平台相关文件
2、使用设备树给我们带来了那些好处吗? 设备树是一种编程语言,用于描述芯片与外围设备之间的连接关系,实际上类似于c语言的语法。 主板具有主芯片CPU,如Rochchip的33xx系列,根据CPU的不同,有很多外围控制器,如I2C、SPI、MIPI控制器、DDR控制器、NIC控制器等众所周知,寄存器地址因芯片和控制器而异,但Rochchip和三星芯片相对于I2C控制器的寄存器配置地址一定不同。 实际上,不同的制造商不仅芯片寄存器不同,通过一个制造商不同系列芯片的寄存器地址也可能不同。 即使是同一个芯片,如果这个芯片有两个I2C控制器,我们可能只使用I2C1。 其他控制器需要关闭以节省电力。 这是设备树用来说明与芯片相关的东西。 板上不仅有主控芯片,还有很多外围设备。 例如,一家a公司生产了使用Rochchip相同芯片的平板电脑。 但是这两个平台在硬件上有区别。 例如,a公司使用一个陀螺仪硬件连接到I2C1,而b公司连接到I2C2。 假设linux系统支持两个陀螺仪的驱动,但连接到针脚的I2C控制器不同,所以是否需要修改代码才能支持这两种平板电脑呢? 参照设备树时,实际上不使用,修改设备树即可。 因为设备树描述了这些硬件设备的连接,所以系统启动了,例如,如果a公司的平板陀螺仪安装在I2C1下,则只需在设备树中描述陀螺仪在I2C1下
下图是设备树的树结构
另一方面,设备树1、设备树文件的RK平台的设备树路径: kernelarcharm 64bootdtsrockchip
编译设备树期间生成的中间文件:rk3399-tve1030g.dtb.d.dtc.tmp
开发板设备树文件: rk3399-tve1030g.dts
给定的设备树文件是内核使用的skddp文件: rk3399-tve1030g.dtb
2、查看rk3399-tve1030g.dts下的rk3399-tve1030g.dts,很多东西都被我删除了。 为了简化,请大家通俗易懂
/* *版权(c ) 2018 fuzhourockchipelectronicsco . ltd * * spdx -许可证标识符: ) GPL-2.0ormit )/dttt # includedt-skddpdings/PWM/PWM.h//设备树
#include "rk3399.dtsi" #include "rk3399-android.dtsi"#include "rk3399-opp.dtsi"#include "rk3399-vop-clk-set.dtsi"/ { /* 这个大括号里面表示一个根节点,也是最大的节点,其他的小节点是它的组成成分,什么叫根节点?设备树设备树就是把设备比喻成一棵树,比如一个平板就是一棵树,树上面开枝散叶有很多子节点比如i2c节点,spi节点,下面rk_headset就是我们定义的一个节点 */compatible = "rockchip,rk3399-tve1030g", "rockchip,rk3399";/* 根节点的属性成员,表示这个设备树支持那个板子,比如这个设备树优先支持"rockchip,rk3399-tve1030g"这款板子,其次支持是使用RK3399芯片的板子,内核启动的时候会去匹配这个字符串,匹配上了代表内核能匹配这个设备树*//*rk_headset 根节点下面的一个子节点*/rk_headset {compatible = "rockchip_headset"; /*compatible属性:"rockchip_headset" 后面这串字符需要和驱动匹配,驱动的probe函数才能被执行*/headset_gpio = <&gpio4 RK_PD4 GPIO_ACTIVE_LOW>;/*headset_gpio: 可以理解为一个变量,用来告诉驱动,使用那个GPIO,headset_gpio 可以修改为别的名字,但是对应的驱动也要修改为对应的名字,因为驱动是通过这个名字获取headset_gpio里面的值*/pinctrl-names = "default";pinctrl-0 = <&hp_det>;/*pinctrl-names和pinctrl-0 这个是pinctrl的相关配置,先不管,等说明pinctrl的时候在看*/io-channels = <&saradc 2>;/*io-channels 和headset_gpi0 是一样的,也是可以理解为一个变量,驱动会引用里面的变量*/};}; 3、在内核里面查看设备树文件在内核里面查看设备树文件,设备树的路径是:/sys/firmware/devicetree/base
由上面的图片我们看到在base文件夹就是设备树的根节点,在根节点下又有很多子节点,比如我们可以看到上面例子中的子节点rk_headset,还有别的节点i2c@ff110000/ 、i2c@ff120000/每个节点i2c节点代表芯片的一个i2c控制器。我们还可以进入到子节点rk_headset的目录查看子节点由什么组成。
我们进入子节点rk_headset可以看里面有组成成员:compatible 、headset_gpio 、io-channels、name、 pinctrl-0、 pinctrl-names和上面我们rk3399-tve1030g.dts里面的rk_headset是一样的。
我们初步的看了设备树的组成部分,设备树是由一个根节点组成,根节点下面有许多子节点,子节点下面还可以有子节点的子节点,以此类推形成一个树状的结构。
二、设备树的节点 1.设备树之i2c怎么使用
我们上面说了设备树有个根节点,根节点下面有多个子节点其中I2C是其中的一个子节点,我们知道i2c总线下面可以挂载多个设备,比如下图i2c总线下挂载了触摸屏设备和多个陀螺仪设备,他们通过不同的i2c地址去识别不同的设备,既然i2c下挂载有多个子节点,那我们怎么去描述i2c总线下挂载的数据呢?那就是i2c节点下可以在建立一个子节点。
下面是设备树截取i2c节点的一段代码。
触摸屏代码节选,代码路径:drivers/input/touchscreen/gslx680.c
static const struct i2c_device_id gsl_ts_id[] = {{"gslX680_tve", 0}, //和设备树里面的gslx680节点的 compatible = "gslX680_tve";进行匹配{}};static struct i2c_driver gsl_ts_driver = {.driver = { .name = GSLX680_I2C_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(gsl_ts_ids), },#if 0 //ndef CONFIG_HAS_EARLYSUSPEND.suspend = gsl_ts_suspend,.resume = gsl_ts_resume,#endif.probe = gsl_ts_probe, //匹配上后这个probe函数会被调用,然后初始化触摸屏驱动.remove = gsl_ts_remove,.id_table = gsl_ts_id,}; 总结i2c下面的子节点会在内核起来的时候进行解析,最后会转化为一个struct i2c_client结构体,然后注册到一个链表里面,最后struct i2c_client会和触摸屏驱动里面的struct i2c_driver进行匹配,当这两个匹配上的时候,会调用驱动里面的probe函数,进行驱动的下一步注册。
3.设备树之spi怎么使用
在芯片中spi节点和i2c节点差不多,所以暂时不进行解析。
3.设备树之pinctrl怎么使用 1、pinctrl是用来干什么的?
在设备树里面一定会有pinctrl,在说明白什么是pinctrl之前我们先说一下芯片的Pin脚功能,学过单片机的都知道,芯片的Pin脚一般有两个功能,1、可以当成GPIO进行使用。2、可以当初特殊功能的引脚使用,比如这个Pin脚可以配置为I2c功能,那么这个Pin的寄存器就需要配置为I2c功能,其实内部是通过一个MUX电路进行切换。
既然是配置寄存器那么谁更熟悉如何配置寄存器?当然是芯片的原厂工程师,如果我们自己配置要怎么办?是不是要查询数据手册,然后设置对应的标志位才能切换到i2c功能?这样做是不是效率太低了?专业的事情就让专业的人干吧,我们只要使用就好了,所以pinctrl的作用就是用来管理一组Pin脚的功能,比如这组pin脚想配置为i2c功能,我们在i2c节点里面写出来就好了,内核起来的时候会解析你的配置,然后把引脚配置成i2c的功能。
代码已经被精简,只截取i2c部分拿出来进行说明
/*pinctrl 这个节点是根节点下的一个普通节点*/pinctrl: pinctrl { /* compatible和上面的功能一样主要是用来和芯片原厂的驱动进行匹配,匹配之后调用对于的芯片设置Pinctrl的驱动*/compatible = "rockchip,rk3399-pinctrl";rockchip,grf = <&grf>;rockchip,pmu = <&pmugrf>;#address-cells = <2>;#size-cells = <2>;ranges;i2c6 { /*Pin脚要设置为i2c6功能的配置 */i2c6_xfer: i2c6-xfer {rockchip,pins =<2 10 RK_FUNC_2 &pcfg_pull_none>,<2 9 RK_FUNC_2 &pcfg_pull_none>;};};};上面是pinctrl的要将pin脚配置为i2c的功能的配置,那么既然配置好了谁要进行使用呢?当然就是i2c节点拿过来进行使用,我们查看i2c6的节点代码
i2c6: i2c@ff150000 {compatible = "rockchip,rk3399-i2c";reg = <0x0 0xff150000 0x0 0x1000>;clocks = <&cru SCLK_I2C6>, <&cru PCLK_I2C6>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH 0>;/*pinctrl-names = "default"是什么意思呢,内核去解析这个节点的的时候会调用下面图片里面的函数pinctrl_select_state,将pinctrl-0 = <&i2c6_xfer>;这个组Pin脚设置为i2c6_xfer配置*/pinctrl-names = "default"; pinctrl-0 = <&i2c6_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};代码配置kerneldriversbasepinctrl.c
3.设备树之怎么指定中断