【linux】驱动-8-一文解决设备树

时间:2021-04-06 15:03:26   收藏:0   阅读:17


前言

8. Linux设备树

Linux3.x 以后引入了设备树,用于描述一个硬件平台的板级细节。

8.1 设备树简介

设备树可以被 bootloader(uboot)传递到内核,内核从中获取设备树中的硬件信息。

设备树的两个特点

几个常用的缩写

8.2 设备树框架

设备树是由 一个根节点 和 多个子节点 组成。

8.2.1 设备树格式

8.2.1.1 DTS 文件布局
/dts-v1/; // 表示版本
[memory reservations] // 格式为: /memreserve/ <address> <length>;
/ {
[property definitions]
[child nodes]
};
8.2.1.2 node 格式

node为设备树中的基本单元。格式为:

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

就是 naem = value

[label:] property-name;
[label:] property-name = value;
clock-frequency = <0x00000001 0x00000000>;
compatible = "lzm-bus";
local-mac-address = [00 00 12 34 56 78]; // 每个 byte 使用 2 个 16 进制数来表示
local-mac-address = [000012345678]; // 每个 byte 使用 2 个 16 进制数来表示
example = <0x84218421 23>, "hello world";
8.2.1.4 包含 dtsi

一般设备树都不需要从零开始写,只需要包含芯片厂商提供的设备树模板,然后再添加,修改即可。
dts 可以包含 dtsi 文件,也可以包含 .h 文件。.h 文件可以定义一些宏。

/dts-v1/;

#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"

/ {
……
};

8.2.2 修改、追加设备树节点

修改、追加设备树节点都可在文件末尾或新文件修改或追加。

而修改节点,可参考以下两种方法:标签法全路径法
标签法

// 方法一:在根节点之外使用标签引用节点
&red_led
{
    status = "okay";
}

// 方法二:使用全路径引用节点
&{/led@0x020C406C}
{
    status = "okay";
}

全路径法

8.2.3 常用属性

在节点 {} 中包含的内容时节点属性。这些属性信息就是板级硬件描述的信息,驱动会通过内核提供的 API 去获取这些资源信息。

注意:节点属性可分为 标准属性自定义属性,即是可以自行添加属性。

8.2.3.1 常用标准属性

compatible 属性:

model 属性:

status 属性:

value description
okay 设备正常
disabled 设备不可操作,但后面可恢复正常
fail 发生严重错误,需要修复
fail-sss 发生严重错误,需要修复。sss 表示错误信息

#address-cells、#size-cells 属性:

reg 属性:

/dts-v1/; 
/ { 
    #address-cells = <1>; 
    #size-cells = <1>;  
    memory { 
        reg = <0x80000000 0x20000000>; 
    }; 
}; 

ranges 属性:

name、ldevice_type 属性:

8.2.3.2 自定义属性

名称及内容可自定义,但是名称不能与标准属性重名。获取方式,后述。

8.2.4 常用节点

根节点

CPU

memory

chosen

chosen 
{ 
    bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200"; 
};

aliases

aliases {
    can0 = &flexcan1;
    gpio0 = &gpio1;
}

8.3 编译、更换设备树

一般的程序猿会修改设备树即可,不必从零开始。

8.3.1 在内核中编译设备树(推荐)

编译时需要设置一下三个环境变量 ARCH、CROSS_COMPILE、PATH

在开发环境中进入板子对应的内核源码目录,使用内核中的 makefile 即可,执行如下命令来编译 dtb 文件:

make dtbs V=1

上述命令是单独编译设备树。
会编译以下设备树
arch/arm/Makefile 或 arch/arm/boot/Makefile 或 arch/arm/boot/dts/Makefile 等相关 Makefile 中找到 dtb-$(xxx) ,该值包含的就是要编译的 dtb
如该文件中宏 dtb-$(CONFIG_SOC_XXX) 包含的 .dtb 就会被编译出来。
如果想编译自己的设备树,添加该值内容,并把自己的设备树放在 arch/arm/boot/dts 下即可。
具体查看该 arch/arm/boot/Makefile 内容

8.3.2 人工编译(不推荐)

意思是手工使用 dtc 工具直接编译。

dtc 工具存放于内核目录 scripts/dtc 下。
若直接使用 dtc 工具手工编译的话,包含其它文件时不能使用 #include,而必须使用 /include

编译、反编译的示例命令如下,-I 指定输入格式,-O 指定输出格式,-o 指定输出文件:

./scripts/dtc/dtc -I dts -O dtb -o tmp.dtb arch/arm/boot/dts/xxx.dts  // 编译 dts 为 dtb 
./scripts/dtc/dtc -I dtb -O dts -o tmp.dts arch/arm/boot/dts/xxx.dtb  // 反编译 dtb 为 dts

8.3.3 更换设备树

一般步骤:

8.3.4 查看设备树

目录 /sys/firmware/devicetree 下以目录结构呈现的 dtb 文件。

目录 /sys/firmware/fdt 文件,就是 dtb 格式的设备树文件。

8.4 内核处理设备树

8.4.1 设备树过程

设备树生命过程
DTS --(PC)--> DTB --(内核)--> device_node -·(内核)·-> platform_device

流程

  1. dts 在 PC 机上被编译为 dtb 文件。
  2. u-bootdtb 文件传给内核。
  3. 内核解析 dtb 文件,把每一个节点都转换为 device_node 结构体。
  4. 对于某些 device_node 结构体,会被转换为 platform_device 结构体。

对于 device_node 和 platform_device,建议去内核源码看看它们的成员。

8.4.2 转换为 platform_device 的条件

8.5 获取节点函数

在驱动程序中,内核加载设备树后。可以通过以下函数获取到设备树节点中的资源信息。
获取节点函数及获取节点内容函数称为 of 函数。

8.5.1 重要结构体内容

8.5.1.1 device_node

device_node 结构体如下:

struct device_node 
{
    const char *name;
    const char *type;
    phandle phandle;
    const char *full_name;
    struct fwnode_handle fwnode;

    struct  property *properties;
    struct  property *deadprops;    /* removed properties */
    struct  device_node *parent;
    struct  device_node *child;
    struct  device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
    struct  kobject kobj;
#endif
    unsigned long _flags;
    void    *data;
#if defined(CONFIG_SPARC)
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};
8.5.1.2 of_device_id

of_device_id 结构体如下:

/* Struct used for matching a device  */
struct of_device_id 
{
    char    name[32];
    char    type[32];
    char    compatible[128];
    const void *data;
};

8.5.2 据节点路径寻找节点

of_find_node_by_path()

8.5.3 据节点类型寻找节点

of_find_node_by_type()

8.5.4 据节点类型和compatible属性寻找节点

of_find_compatible_node()

8.5.5 据匹配表寻找节点

of_find_matching_node_and_match()

8.5.6 寻找父节点

of_get_parent()

8.5.7 寻找子节点

of_get_child()

8.6 提取节点中的属性值

8.6.1 重要结构体内容

8.6.1.1 property 结构体

property

struct property 
{
    char    *name;
    int     length;
    void    *value;
    struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
    unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
    unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
    struct bin_attribute attr;
#endif
};
8.6.1.2 resource 结构体

resource 结构体:

struct resource 
{
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    unsigned long desc;
    struct resource *parent, *sibling, *child;
};

8.6.2 查找节点属性值

of_find_property()

8.6.3 获取整型属性

of_property_read_u8_array


//8位整数读取函数
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz)

//16位整数读取函数
int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz)

//32位整数读取函数
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)

//64位整数读取函数
int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)

8.6.4 简化后的读取整型属性函数

of_property_read_u8

//8位整数读取函数
int of_property_read_u8 (const struct device_node *np, const char *propname,u8 *out_values)

//16位整数读取函数
int of_property_read_u16 (const struct device_node *np, const char *propname,u16 *out_values)

//32位整数读取函数
int of_property_read_u32 (const struct device_node *np, const char *propname,u32 *out_values)

//64位整数读取函数
int of_property_read_u64 (const struct device_node *np, const char *propname,u64 *out_values)

8.6.5 读取字符串属性

of_property_read_string_index:(推荐

of_property_read_string:(不推荐

8.6.6 读取 bool 型属性函数

of_property_read_bool()

8.6.7 内存映射相关 of 函数

设备树提供寄存器的地址段,但是一般情况下都会使用 ioremap 映射为虚拟地址使用。
of_address_to_resource 只是获取 reg 的值,也就是寄存器值。
of_iomap 函数就是获取 reg 属性值&指定哪一段内存&映射为虚拟地址。

of_address_to_resource

of_iomap

原文:https://www.cnblogs.com/lizhuming/p/14621305.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!