linux设备树-基础介绍

发布时间 2023-03-29 00:02:39作者: 大奥特曼打小怪兽

一、介绍

1.1 为什么引入设备树

我们首先回顾一下我们之前学习过的驱动程序。比如:

linux驱动移植-lcd驱动基础;在arch/arm/plat-samsung/devs.c文件中定义了platform设备s3c_device_lcd,在arch/arm/mach-s3c24xx/mach-smdk2440.c文件定义了platform数据smdk2440_fb_info。

linux驱动移植-linux块设备驱动Nand Flash;在arch/arm/plat-samsung/devs.c文件中定义了platform设备s3c_device_nand ,在 arch/arm/mach-s3c24xx/common-smdk.c文件定义了platform数据smdk_nand_info

linux驱动移植-linux块设备驱动Nand Flash;在arch/arm/plat-samsung/devs.c文件中定义了platform设备s3c_device_nand ,在 arch/arm/mach-s3c24xx/common-smdk.c文件定义了platform数据smdk_nand_info。

linux驱动移植-linux块设备驱动Nor Flash;在drivers/mtd/maps/physmap-core.c文件中定义了platform设备physmap_flash,同时在文件内定义了platform数据physmap_flash_data。

linux驱动移植-DM9000网卡驱动;在arch/arm/mach-s3c24xx/mach-smdk2440.c文件中定义了platform设备smdk2440_device_eth,同时在文件内定义了platform数据smdk2440_dm9k_pdata 。

linux驱动移植-I2C适配器驱动移植;在arch/arm/mach-s3c24xx/mach-smdk2440.c文件中定义了platform设备s3c_device_i2c0,同时在文件内定义了platform数据default_i2c_data 。

linux驱动移植-I2C设备驱动移植(AT24C08);在arch/arm/mach-s3c24xx/mach-smdk2440.c文件中定义了I2C从设备信息i2c_board_info 。

linux驱动移植-SPI控制器驱动;在arch/arm/plat-samsung/devs.c文件中定义了platform设备s3c_device_spi1,同时在arch/arm/mach-s3c24xx/mach-smdk2440.c文件内定义了platform数据s3c2440_spi1_data 。

linux驱动移植-SPI驱动移植(OLED SSD1306);在arch/arm/mach-s3c24xx/mach-smdk2440.c文件中定义了SPI从设备信息spi_board_info 。

linux驱动移植-UART设备驱动;在arch/arm/plat-samsung/init.c文件中定义了platform设备s3c24xx_uart_device0,同时在该文件内定义了platform数据uart_cfgs。

linux驱动移植-RTC驱动;在arch/arm/plat-samsung/devs.c文件中定义了platform设备s3c_device_rtc ,同时在drivers/rtc/rtc-s3c.c文件内定义了设置platform数据类型为s3c_rtc 。

在之前我们介绍的驱动程序中,Mini2440开发板板极硬件资源都是被硬编码在arch/arm/plat-samsung/devs.c和arch/arm/mach-s3c24xx/mach-smdk2440.c文件,比如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data,这些板级细节代码基本都是和开发外围设备相关的,开发板不同这些信息也会因此不同,与之而来就带来了诸多问题。

比如ARM的merge工作量较大,代码目录结构混乱,为此linux还对ARM平台相关代码做出了相关的规范调整:

  • ARM的核心代码保存在arch/arm目录下;
  • ARM SoC core architecture code保存在arch/arm目录下;
  • ARM SoC的周边外设模块的驱动保存在drivers目录下;
  • ARM SoC的特定machine代码在arch/arm/mach-xxx目录下;
  • ARM SoC board specific的代码(arch/arm/plat-xxx)被移除,由Device Tree机制来负责传递硬件拓扑和硬件资源信息;硬件的细节可以直接通过Device Tree传递给linux驱动程序,而不再需要在kernel中进行大量的冗余编码。

1.2 设备树(Device Tree)

linux内核从3.x开始引入设备树的概念,用于将设备信息与驱动代码分离开来。在设备树出现之前,所有关于设备的硬件信息都要编写在驱动程序里,一旦外围设备变化,驱动代码就要重写。

引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码。

比如在ARM Linux内,一个.dts(device tree source)文件对应一个ARM的machine,一般放置在内核的"arch/arm/boot/dts/"目录内,比如firefly  rk3288参考板的板级设备树文件就是"arch/arm/boot/dts/rk3288-firefly.dts"。这个文件可以通过$make dtbs命令编译成二进制的.dtb文件供内核驱动使用。

基于同样的软件分层设计的思想,由于一个SoC可能对应多个machine,如果每个machine的设备树都写成一个完全独立的.dts文件,那么势必相当一些.dts文件有重复的部分,为了解决这个问题,Linux设备树目录把一个SoC公用的部分或者多个machine共同的部分提炼为相应的.dtsi文件。这样每个.dts就只有自己差异的部分,公有的部分只需要"include"相应的.dtsi文件, 这样就是整个设备树的管理更加有序。比如"arch/arm/boot/dts/rk3288-firefly.dtsi"文件:

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2014, 2015 FUKAUMI Naoki <naobsd@gmail.com>
 */

/dts-v1/;
#include "rk3288-firefly.dtsi"

/ {
        model = "Firefly-RK3288";
        compatible = "firefly,firefly-rk3288", "rockchip,rk3288";
};

&ir {
        gpios = <&gpio7 RK_PA0 GPIO_ACTIVE_LOW>;
};

&pinctrl {
        act8846 {
                pmic_vsel: pmic-vsel {
                        rockchip,pins = <7 RK_PB6 RK_FUNC_GPIO &pcfg_output_low>;
                };
        };

        ir {
                ir_int: ir-int {
                        rockchip,pins = <7 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
                };
        };
};

&pwm1 {
        status = "okay";
};

1.3 设备树文件

设备树涉及到的文件格式有dts、dtsi、dtb:

  • dts:硬件的相关信息都会写在.dts为后缀的文件中,每一款硬件可以单独写一份xxxx.dts,一般在linux源码中存在大量的dts文件,对于arm架构可以在arch/arm/boot/dts找到相应的dts,一个dts文件对应一个ARM的machie。
  • dtsi:对于一些相同的dts配置可以抽象到dtsi文件中,然后类似于C语言的方式可以include到dts文件中,对于同一个节点的设置情况,dts中的配置会覆盖dtsi中的配置;
  • dtb:dtb(Device Tree Blob),dts经过dtc编译之后会得到dtb文件,dtb通过bootloader引导程序加载到内核。所以bootloader需要支持设备树才行;linux kernel也需要加入设备树的支持;

dts文件使用工具dtc进行编译,可以在ubuntu系统上通过指令apt-get install device-tree-compiler安装dtc工具,不过在内核源码scripts/dtc路径下已经包含了dtc工具。

uboot从v1.1.3开始支持设备树,其对ARM的支持则是和ARM内核支持设备树同期完成。

为了使能设备树,需要在编译uboot的时候在config文件中加入:

#define CONfiG_OF_LIBFDT·

在uboot中,可以从NAND、SD或者TFTP等任意介质中将.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在uboot中运行fdt addr命令设置.dtb的地址,如:

fdt addr 0x71000000·

fdt的其他命令就变得可以使用,如fdt resize、fdt print等。
然后通过以下命令来启动内核:

bootz kernel_addr initrd_address dtb_address

其中:

  • 第一个参数为内核映像的地址;
  • 第二个参数为initrd的地址,若不存在initrd,可以用“-”符号代替;
  • 第三个参数dtb_address为.dtb文件在内存的地址

二、设备树结构

设备树用树状结构描述设备信息,其结构如下:

/dts-v1/;
/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        child-node1 {
        };
    };
};

设备树文件具有以下几种特性:

  • 每个设备树文件都有一个根节点,除了根节点,每个节点都只有一个父节点;
  • 每个节点用节点名字标识,节点名字的格式是node-name@unit-address;如果该节点没有reg属性,那么该节点名字中必须不能包括@和unit-address;
  • 根节点的名字是确定的,必须是"/";
  • 每个节点都包含了若干个key-value对(属性)来描述该节点的一些特性,每个属性的描述用;结束;

2.1 语法介绍

了解了基本的device tree的结构后,我们总要把这些结构体现在device tree source code上来。在linux kernel中,扩展名是dts的文件就是描述硬件信息的device tree source file,在dts文件中,一个节点被定义成:

[label:] node-name[@unit-address] {
   [properties definitions]
   [child nodes]
} 

上图中:

  • label:标签,方便在dts文件中引用;
  • node-name:标签名;
  • unit-address:地址;
  • properties:属性定义;
  • child nodes:子节点;
2.1.1 标签

 

2.1.2 节点名

[label:] node-name[@unit-address] 

2.1.2 key
2.1.3 节点值

节点属性(property)值标识了设备的特性,它的值(value)是多种多样的:

  • 可能是空,也就是没有值的定义,如an-empty-property;
  • 可能为字符串,如a-string-property;可能为字符串树组,如a-string-list-property;
  • 可能是一个u32、u64的数值(值得一提的是cell这个术语,在Device Tree表示32bit的信息单位),如second-child-property;可能是一个数值数组,例如<0x00000000 0x00000000 0x00000000 0x20000000>;

三、设备树语法

 

 

 

 

 

 

参考文章

[1]linux设备驱动(17)设备树详解1-概论

[2]Linux设备树语法详解

[3]Linux设备树详解(一) 基础知识

[4]linux设备驱动(18)设备树详解2-基础知识