Linux 内核学习(4)---- DeviceTree

DTS简介

在Linux内核早期的时候,每个嵌入式系统的板载信息(总线,设备的寄存器地址等)都是Hardcode在arch/<cpu>/match-xxx/board-xxx.c之类的文件中,即必须存在这样的C文件,里面定义了板子上的硬件的地址等信息,这样做会造成很多很多的大量相似的没多少用的重复代码;为了解决这个问题,而引入了DTS(device tree source)
DTS即Device Tree Source 设备树源码, Device Tree是一种描述硬件的数据结构,它起源于 OpenFirmware (OF)
本质上,Device Tree改变了原来用hardcode方式将HW 配置信息嵌入到内核代码的方法,改用Bootloader传递一个DB的形式。
每个嵌入式系统的描述硬件信息的DTS文件存放在arch/<cpu>/boot/dts/目录下,比如mediatek 的mt7622-rfb1.dts位于linux-5.4.6/arch/arm64/boot/dts/mediatek/ 目录下

#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>

#include "mt7622.dtsi"
#include "mt6380.dtsi"

/ {
    model = "MediaTek MT7622 RFB1 board";
    compatible = "mediatek,mt7622-rfb1", "mediatek,mt7622";

    aliases {
        serial0 = &uart0;
    };

    chosen {
        stdout-path = "serial0:115200n8";
        bootargs = "earlycon=uart8250,mmio32,0x11002000 swiotlb=512";
    };

    cpus {
        cpu@0 {
            proc-supply = <&mt6380_vcpu_reg>;
            sram-supply = <&mt6380_vm_reg>;
        };

        cpu@1 {
            proc-supply = <&mt6380_vcpu_reg>;
            sram-supply = <&mt6380_vm_reg>;
        };
    };

DTS文件编译加载流程

如果要使用Device Tree,首先用户要了解自己的硬件配置和系统运行参数,并把这些信息组织成Device Tree source file;通过DTC(Device Tree Compiler),可以将的Device Tree Source file变成适合机器处理的Device Tree Binary File(DTB,Device Tree Blob);在系统启动的时候,Bootloader可以将保存在FLASH中的DTB copy到内存,并把DTB的起始地址传递给kernel
dts文件编译加载流程如下图所示:

dts编译加载流程.png

Device Tree可以描述的信息包括CPU的数量和类别、内存基地址和大小、总线和桥、外设连接、中断控制器和中断使用情况、GPIO控制器和GPIO使用情况、Clock控制器和Clock使用情况等等
DeviceTree 是一种描述了电路板上CPU、总线、设备组成的树形数据结构,Bootloader会将这些信息传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_Device、I2c_Client、Spi_Device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备

DeviceTree 不需要描述所有的硬件信息,那些可以动态探测到的设备是不需要描述的,例如USB device,不过对于SOC上的Usb Hostcontroller,它是无法动态识别的,需要在Device Tree中描述;同样的道理,在Computer System中,PCI Device可以被动态探测到,不需要在Device Tree中描述,但是PCI Bridge如果不能被探测,所以需要描述

DeviceTree 相关文件描述:
dts:DT源文件称为dts文件,是ASCII格式文本文件,一般一个dts文件对应一个Machine,ARM架构下dts文件存放于arch/arm/boot/dts/目录下

dtsi:多个Machine/SoC公用的dt文件,i代表includedtc;
dtsi和C语言的头文件类似,.dtsi也可以include其他的.dtsi,譬如几乎所有的ARM SoC的.dtsi都引用了skeleton.dtsi,即#include"skeleton.dtsi“或者 /include/ "skeleton.dtsi"

dtb:DeviceTree Blob,由dtc编译dts文件生成的二进制目标文件

dt.img:多个dtb文件打包形成dt.img,以适配多个Machine,dts/dtb的结构是标准化的,dt.img有头信息和多个dtb组成,因为没有统一的标准,不同的厂商头信息可能是不同的

DTS文件构成

dts_struct.jpg

Device Tree的基本单元是node,这些node被组织成树状结构,除了root node,每个node都只有一个parent;一个device tree文件中只能有一个root node

  • root node的node name是确定的,必须是“/”
  • 每个node中包含了若干的 property/value 来描述该node的一些特性。
  • 每个node用节点名字(nodename)标识,节点名字的格式是node-name@unit-address。
    如果该node没有reg属性(后面会描述这个property),那么该节点名字中必须不能包括@和unit-address;unit-address的具体格式是和设备挂在那个bus上相关,例如对于cpu,其unit-address就是从0开始编址,以此加一;而具体的设备,例如以太网控制器,其unit-address就是寄存器地址
    正常情况下所有的dts文件以及dtsi文件都含有一个根节点”/”,Device Tree Compiler会对DTS的node进行合并,最终生成的DTB中只有一个 root node.

DTS 常见Node和常见属性

Chosen Node
chosen {
    stdout-path = "serial0:115200n8";
    bootargs = "earlycon=uart8250,mmio32,0x11002000 swiotlb=512";
};

chosen node 主要用来描述由系统指定的runtime parameter,它并没有描述任何硬件设备节点信息。原先通过tag list传递的一些linux kernel运行的参数,可以通过chosen节点来传递。如command line可以通过bootargs这个property来传递。如果存在chosen node,它的parent节点必须为“/”根节点。

Aliases Node
aliases {
    serial0 = &uart0;
};
uart0: serial@11002000 {
    compatible = "mediatek,mt7622-uart",
             "mediatek,mt6577-uart";
    reg = <0 0x11002000 0 0x400>;
    interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
    clocks = <&topckgen CLK_TOP_UART_SEL>,
         <&pericfg CLK_PERI_UART0_PD>;
    clock-names = "baud", "bus";
    status = "disabled";
};
&uart0 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_pins>;
    status = "okay";
};

aliases node用来定义别名,类似C++中引用。上面是一个在.dtsi中的典型应用,当使用uart0时,也即使用serial@11002000,使得引用节点变得简单方便。例:当.dts include 该.dtsi时,将uart0的status属性赋值为okay,则表明该主板上的serial@11002000处于enable状态;反之,status赋值为disabled,则表明该主板上的serial@11002000处于disable状态。

Memory Node
memory {
    reg = <0 0x40000000 0 0x20000000>;
};

对于memory node,device_type必须为memory,由之前的描述可以知道该memory node是以0x00000000为起始地址,以0x80000000为结束地址的1GB的空间。
其中的reg 属性:
reg的组织形式为reg = <address1 length1 [address2 length2][address3 length3] ... >,其中的每一组address length表明了设备使用的一个地址范围。address为1个或多个32位的整型(即cell),而length则为cell的列表或者为空(若#size-cells = 0)。address和length字段是可变长的,父结点的#address-cells和#size-cells分别决定了子结点的reg属性的address和length字段的长度。

Compatible 属性
/ {
    model = "MediaTek MT7622 RFB1 board";
    compatible = "mediatek,mt7622-rfb1", "mediatek,mt7622";

    aliases {
        serial0 = &uart0;
    };

compatible属性为string list,用来将设备匹配对应的driver驱动,优先级为从左向右。即compatible实现了原先内核版本3.x之前,platform_device中.name的功能
上述.dts文件中,root结点"/"的compatible 属性mediatek,mt7622-rfb1", "mediatek,mt7622;定义了系统的名称,它的组织形式为:<manufacturer>,<model>,Linux内核透过root结点"/"的compatible 属性即可判断它启动的是什么machine

Interrupts 属性
scpsys: scpsys@10006000 {
    compatible = "mediatek,mt7622-scpsys",
             "syscon";
    #power-domain-cells = <1>;
    reg = <0 0x10006000 0 0x1000>;
    interrupts = <GIC_SPI 165 IRQ_TYPE_LEVEL_LOW>,
             <GIC_SPI 166 IRQ_TYPE_LEVEL_LOW>,
             <GIC_SPI 167 IRQ_TYPE_LEVEL_LOW>,
             <GIC_SPI 168 IRQ_TYPE_LEVEL_LOW>;
    infracfg = <&infracfg>;
    clocks = <&topckgen CLK_TOP_HIF_SEL>;
    clock-names = "hif_sel";
};

<>中第一个u32表示中断类型,第二个是中断号,第三个是中断触发条件
若子节点使用到中断(中断号、触发方法等等),则需用interrupt属性来指定,该属性的数值长度受中断控制器中#inrerrupt-controller值③控制,即interrupt属性<>中数值的个数为#inrerrupt-controller的值;本例中#inrerrupt-controller=<2>,因而④中interrupts的值为<0x3d 0>形式,具体每个数值的含义由驱动实现决定。

Ranges属性
 power-domains = <&scpsys MT7622_POWER_DOMAIN_HIF0>;
 bus-range = <0x00 0xff>;
 ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>;
 status = "disabled";

 pcie0: pcie@0,0 {
     reg = <0x0000 0 0 0 0>;
     #address-cells = <3>;
     #size-cells = <2>;
     #interrupt-cells = <1>;
     ranges;
     status = "disabled";

ranges属性为地址转换表,这在pcie中使用较为常见,它表明了该设备在到parent节点中所对用的地址映射关系。ranges格式长度受当前节点#address-cell、parent节点#address-cells、当前节点#size-cell所控制。顺序为ranges=<前节点#address-cell, parent节点#address-cells , 当前节点#size-cell。
注:对于相同名称的节点,dtc会根据定义的先后顺序进行合并,其相同属性,取后定义的那个。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容