Linux加载DTS设备节点的过程(以高通8974平台为例) .

2024-04-18 14:18

本文主要是介绍Linux加载DTS设备节点的过程(以高通8974平台为例) .,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

DTS是Device Tree Source的缩写,用来描述设备的硬件细节。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。为了去掉这些垃圾代码,Linux采用DTS这种新的数据结构来描述硬件设备。采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。

       有关DTS的语法格式,网上有很多资料,这里我就不再赘述了。本文主要讲Linux是怎样加载DTS设备节点的流程。下面以高通QCT8974平台(Linux内核)为例进行讲解:

(1)使用DTS注册平台总线的过程

       在讲注册过程之前,我们先看看DTS是怎样描述平台总线结构的,以i2c总线为例:

[c] view plain copy print ?
  1. / {  
  2.     model = "Qualcomm MSM 8974";  
  3.     compatible = "qcom,msm8974";  
  4.     interrupt-parent = <&intc>;  
  5.   
  6.     aliases {  
  7.         spi0 = &spi_0;  
  8.         spi7 = &spi_7;  
  9.         sdhc1 = &sdhc_1; /* SDC1 eMMC slot */  
  10.         sdhc2 = &sdhc_2; /* SDC2 SD card slot */  
  11.         sdhc3 = &sdhc_3; /* SDC3 SDIO slot */  
  12.         sdhc4 = &sdhc_4; /* SDC4 SDIO slot */  
  13.     };  
  14.   
  15.     memory {  
  16.   
  17.         secure_mem: secure_region {  
  18.             linux,contiguous-region;  
  19.             reg = <0 0x7800000="">;  
  20.             label = "secure_mem";  
  21.         };  
  22.   
  23.         adsp_mem: adsp_region {  
  24.             linux,contiguous-region;  
  25.             reg = <0 0x2000000="">;  
  26.             label = "adsp_mem";  
  27.         };  
  28.     };  
  29.   
  30.     intc: interrupt-controller@F9000000 {  
  31.         compatible = "qcom,msm-qgic2";  
  32.         interrupt-controller;  
  33.         #interrupt-cells = <3>;   
  34.         reg = <0xf9000000 0x1000="">,  
  35.               <0xf9002000 0x1000="">;  
  36.     };  
  37.   
  38.     msmgpio: gpio@fd510000 {  
  39.         compatible = "qcom,msm-gpio";  
  40.         gpio-controller;  
  41.         #gpio-cells = <2>;   
  42.         interrupt-controller;  
  43.         #interrupt-cells = <2>;   
  44.         reg = <0xfd510000 0x4000="">;  
  45.         ngpio = <146>;  
  46.         interrupts = <0 208="" 0="">;  
  47.         qcom,direct-connect-irqs = <8>;  
  48.     };  
  49.   
  50.     wcd9xxx_intc: wcd9xxx-irq {  
  51.         compatible = "qcom,wcd9xxx-irq";  
  52.         interrupt-controller;  
  53.         #interrupt-cells = <1>;   
  54.         interrupt-parent = <&msmgpio>;  
  55.         interrupts = <72 0="">;  
  56.         interrupt-names = "cdc-int";  
  57.     };  
  58.   
  59.     timer {  
  60.         compatible = "arm,armv7-timer";  
  61.         interrupts = <1 2="" 0="" 1="" 3="" 0="">;  
  62.         clock-frequency = <19200000>;  
  63.     };  
  64.   
  65.     i2c_0: i2c@f9967000 { /* BLSP#11 */  
  66.         cell-index = <0>;  
  67.         compatible = "qcom,i2c-qup";  
  68.         reg = <0xf9967000 0x1000="">;  
  69.         #address-cells = <1>;   
  70.         #size-cells = <0>;   
  71.         reg-names = "qup_phys_addr";  
  72.         interrupts = <0 105="" 0="">;  
  73.         interrupt-names = "qup_err_intr";  
  74.         qcom,i2c-bus-freq = <100000>;  
  75.         qcom,i2c-src-freq = <50000000>;  
  76.     };  
  77.   
  78.     i2c_2: i2c@f9924000 {  
  79.         cell-index = <2>;  
  80.         compatible = "qcom,i2c-qup";  
  81.         reg = <0xf9924000 0x1000="">;  
  82.         #address-cells = <1>;   
  83.         #size-cells = <0>;   
  84.         reg-names = "qup_phys_addr";  
  85.         interrupts = <0 96="" 0="">;  
  86.         interrupt-names = "qup_err_intr";  
  87.         qcom,i2c-bus-freq = <100000>;  
  88.         qcom,i2c-src-freq = <50000000>;  
  89.     };  
  90.   
  91.     spi_0: spi@f9923000 {  
  92.         compatible = "qcom,spi-qup-v2";  
  93.         reg = <0xf9923000 0x1000="">;  
  94.         interrupts = <0 95="" 0="">;  
  95.         spi-max-frequency = <19200000>;  
  96.         #address-cells = <1>;   
  97.         #size-cells = <0>;   
  98.         gpios = <&msmgpio 3 0>, /* CLK  */  
  99.             <&msmgpio 1 0>, /* MISO */  
  100.             <&msmgpio 0 0>; /* MOSI */  
  101.         cs-gpios = <&msmgpio 9 0>;  
  102.     };  
  103.   
  104. };  

从上面可知,系统平台上挂载了很多总线,如i2c、spi、uart等等,每一个总线分别被描述为一个节点。Linux在启动后,到C入口时,会执行以下操作,加载系统平台上的总线和设备:

start_kernel() --> setup_arch() --> unflatten_device_tree() 

unflatten_device_tree()的代码如下:

[c] view plain copy print ?
  1. void __init unflatten_device_tree(void)  
  2. {  
  3.     __unflatten_device_tree(initial_boot_params, &allnodes,  
  4.                 early_init_dt_alloc_memory_arch);  
  5.   
  6.     /* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */  
  7.     of_alias_scan(early_init_dt_alloc_memory_arch);  
  8. }  

 在执行完unflatten_device_tree()后,DTS节点信息被解析出来,保存到allnodes链表中,allnodes会在后面被用到。

随后,当系统启动到board文件时,会调用.init_machine,高通8974平台对应的是msm8974_init()。接着调用of_platform_populate(....)接口,加载平台总线和平台设备。至此,系统平台上的所有已配置的总线和设备将被注册到系统中。注意:不是dtsi文件中所有的节点都会被注册,在注册总线和设备时,会对dts节点的状态作一个判断,如果节点里面的status属性没有被定义,或者status属性被定义了并且值被设为“ok”或者“okay”,其他情况则不被注册到系统中。


(2)使用DTS注册总线设备的过程

       上面讲了Linux怎样使用DTS注册平台总线和平台设备到系统中,那么其他设备,例如i2c、spi设备是怎样注册到系统中的呢?下面我们就以i2c设备为例,讲解Linux怎样注册i2c设备到系统中。

 以高通8974平台为例,在注册i2c总线时,会调用到qup_i2c_probe()接口,该接口用于申请总线资源和添加i2c适配器。在成功添加i2c适配器后,会调用of_i2c_register_devices()接口。此接口会解析i2c总线节点的子节点(挂载在该总线上的i2c设备节点),获取i2c设备的地址、中断号等硬件信息。然后调用request_module()加载设备对应的驱动文件,调用i2c_new_device(),生成i2c设备。此时设备和驱动都已加载,于是drvier里面的probe方法将被调用。后面流程就和之前一样了。


       简而言之,Linux采用DTS描述设备硬件信息后,省去了大量板文件垃圾信息。Linux在开机启动阶段,会解析DTS文件,保存到全局链表allnodes中,在掉用.init_machine时,会跟据allnodes中的信息注册平台总线和设备。值得注意的是,加载流程并不是按找从树根到树叶的方式递归注册,而是只注册根节点下的第一级子节点,第二级及之后的子节点暂不注册。Linux系统下的设备大多都是挂载在平台总线下的,因此在平台总线被注册后,会根据allnodes节点的树结构,去寻找该总线的子节点,所有的子节点将被作为设备注册到该总线上。


这篇关于Linux加载DTS设备节点的过程(以高通8974平台为例) .的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/915021

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

Linux云服务器手动配置DNS的方法步骤

《Linux云服务器手动配置DNS的方法步骤》在Linux云服务器上手动配置DNS(域名系统)是确保服务器能够正常解析域名的重要步骤,以下是详细的配置方法,包括系统文件的修改和常见问题的解决方案,需要... 目录1. 为什么需要手动配置 DNS?2. 手动配置 DNS 的方法方法 1:修改 /etc/res

MyBatis延迟加载与多级缓存全解析

《MyBatis延迟加载与多级缓存全解析》文章介绍MyBatis的延迟加载与多级缓存机制,延迟加载按需加载关联数据提升性能,一级缓存会话级默认开启,二级缓存工厂级支持跨会话共享,增删改操作会清空对应缓... 目录MyBATis延迟加载策略一对多示例一对多示例MyBatis框架的缓存一级缓存二级缓存MyBat

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配