• Linux 设备树语法(.dts)及如何从设备树获取节点信息


    设备树简介

    一个设备信息用树形结构表示如下:


    from http://www.100ask.org/

    如何用设备树进行描述呢?

    /{ // 表示root节点
        cpu{ // cpu节点
            name = val; // 属性名name,val是属性值。val形式:1)"string"(双引号括起来);2)<u32 u32 u32> (尖括号,有多少个32位就放多少个,空格间隔);3) [12 34 56](16进制单字节)。val可以是这3种形式组合,如<0x123>,"abcd",[34]
        };
    
        memory{ // 内存节点
            
        };
    
        I2C{ // I2C控制器节点
            
        };
    };
    

    设备树中基本单元,称为“node”(节点)。基本格式:

    [label:]node-name[@unit-address]{ // node-name:节点名,unit-address:节点地址;label是标号,方便引用节点
        [properties definitions] // 各种属性
        [child nodes] // 子节点
    };
    

    label是标号,方便引用节点。例如,定义一个label uart0,

    /dts-v1/; // 表示版本
    /{
        uart0:uart@fe001000{
            compatible="ns16550"
            reg=<0xfe001000 0x100>
        };
    };
    

    有两种办法可以修改node uart@fe001000:

    // 1. 在根节点外使用label引用node
    &uart0{ // 通过label
        status="disabled";
    };
    
    // 2. 在根节点外使用全路径
    &{/uart@fe0020000} {
        status="disabled";
    };
    

    常用节点

    1)根节点

    dts文件必须有一个根节点

    /dts-v1/;
    / {
    model = "SMDK24440";
    compatible = "samsung,smdk2440";
    
    #address-cells = <1>;
    #size-celss = <1>;
    };
    

    根节点中必须有这些属性:

    #address-cells // 在其子节点reg属性中,用多少个u32整数来描述地址(address)
    #size-celss    // 在其子节点reg属性中,用多少个u32整数来描述大小(size)
    compatible     // 定义一系列的字符串,用来指定内核中哪个machine_desc可以支持本设备,即这个板子兼容哪些平台,可以直接用这个平台的驱动来初始化当前板子
                   // uImage: smdk2410 smdk2440 mini2440 ==> machine_desc
    model          // 我们这个板子是什么名字?
                   // 如有2款板子配置基于一致,compatible一样,就通过model来区分这两款板子
    

    2)CPU节点

    一个设备肯定有CPU。一般不需要我们设置,dtsi文件中定义好了

    cpus{
        #address-cells=<1>;
        #size-cells=<0>;
    
        cpu0:cpu@0{ //多核, 可能还要cpu1, cpu2, cpu3, ...
            ...
        };
    };
    

    3)memory节点

    芯片厂家不可能事先确定你的板子用多大内存,所以memory节点需要板厂设置。

    memory{
        reg=<0x80000000 0x20000000>; // 起始地址0x80000000,大小500MB
    };
    

    4)chosen节点

    可通过设备树文件给内核传入一些参数,在chosen节点中设置bootargs属性。chosen节点是虚拟节点,不对应实际设备。

    chosen{
        bootargs="noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200"; // “bootargs”就是传递给内核的参数,“root=”告诉内核其哪个分区找到根文件系统,“init=:”启动哪个应用程序,“console=”使用哪个串口
    };
    

    常用属性

    1)#address-cells,#size-celss

    cell指定一个32位的数值,
    address-cells:address要用多少个32位数来表示;
    size-cells:size要用多少个32位数来表示。

    例如,一段内存,怎么描述它的起始地址 + 大小?
    下例中,address-cells为1,所以reg中用1个数(u32)表示地址,即用0x80000000来表示地址;size-cells为1,所以reg用1个数来表示大小,即用0x20000000表示大小:

    /{
        #address-cells=<1>;
        #size-cells=<1>;
        memory{
            reg=<0x80000000 0x20000000>; // 起始地址0x80000000,大小0x20000000 byte
        };
    };
    

    2)compatible

    “compatible”表示兼容,对于某个LED,内核可能有3个驱动A、B、C都支持它,那么可以这样写:

    led{
        compatible="A","B","C";
    };
    

    内核启动时,就会为这个LED按这样的优先顺序为它找到驱动程序A、B、C。

    根节点下面也有compatible属性,用来选择哪个“machine desc”:一个内核可以支持machine A,也支持machine B,内核启动后会根据节点的compatible属性找到对应的machine desc结构体,执行其中的初始化函数。

    compatible的值,建议取这样的形式:"manufacturer,model",即“厂家名,模块名”。

    注:machine desc意为“机器描述”,到内核启动流程才会涉及。

    3)model

    model属性与compatible类似,区别在于:
    compatible属性是一个字符串列表,表示你的硬件可以兼容A、B、C等驱动;
    model用来准确定义这个硬件是什么。

    例如,根节点中可以这样写:

    /{
        compatible = "samsung,smdk2440","samsung,mini2440"; // 代表我的板子兼容samsung的smdk2440和mini2440,优先使用内核里面它们的驱动
        model = "jz2440_v3"; // 代表我这个板子就是jz2440
    };
    

    4)status

    dtsi文件中定义了很多设备,但在你的板子上某些设备是没有的。这是你可以给这个设备添加一个status属性,设置为"disabled"。

    &uart1{
        status = "disabled";
    };
    

    status可能取值:

    • "okay":表示设备正常运行;
    • "disabled":表示设备不可操作,但后面可以恢复工作;
    • "fail":表示发生了严重错误,需要修复;
    • "fail-sss":表示发生了严重错误,需修复;sss表示错误信息。

    常用"okay"和"disabled"。

    dtsi文件

    dtsi文件是专门给别的dts文件包含(include)用的,本身语法和dts文件一样。

    一个板子boardA,对应设备树文件A.dts文件,如何包含xxx.dtsi文件?

    // A.dts
    #include xxx.dtsi
    
    // 可以在A.dts中引用特点节点,为其专门指定属性
    // 比如禁用uart0
    &uart0{ // 通过label引用uart0
        status = "disabled"; // // 禁用uart0
    };
    

    5)reg

    reg本意register,表示寄存器地址。

    设备树里,可用来描述一段空间。对于ARM系统,寄存器和内存是统一编址的,即访问寄存器时用某块地址,访问内存时用某块地址,在访问方法上没有区别。

    reg属性只,是一系列"address size",表示用多少个32bit数来表示address和size,由其父节点的#address-cells、#size-cells决定。

    /dts-v1/;
    /{
        #address-cells = <1>; // 下面的node用1个u32整数表示地址
        #size-cells = <1>;    // 下面的node用1个u32整数表示大小
        memory{
            reg = <0x80000000 0x20000000>; // 起始地址0x80000000,大小0x20000000
        };
    };
    

    6)name

    过时,不建议用。
    值是字符串,用来表示节点名字。跟platform_driver匹配时,优先级最低。compatible属性在匹配过程中,优先级最高。

    7)device_type

    过时,不建议用。
    值是字符串,用来表示节点类型。跟platform_driver匹配时,优先级为中。compatible属性在匹配过程中,优先级最高。


    编译、更换设备树

    通常不会从零开始写dts文件,而是修改。

    在内核中直接make

    对于64bit CPU,dts文件位于

    # 相对于Linux源码根目录
    arch/arm64/boot/dts
    
    对于32bit CPU,dts文件位于
    
    # 相对于Linux源码根目录
    arch/arm/boot/dts/
    

    以100ASK IMX6ULL Pro板子为例,进入ubuntu上板子Linux内核源码目录,执行命令编译dtb文件

    $ touch arch/arm/boot/dts/100ask_imx6ull-14x14.dts # 假装修改了dts文件
    $ make dtbs V=1
    

    注:需要先设置环境变量ARCH、CROSS_COMPILE、PATH。执行make目录不是dts文件所在目录,而是Linux源码根目录。

    可以看到如下输出

    $ make dtbs V=1
    ...
    
      mkdir -p arch/arm/boot/dts/ ; arm-linux-gnueabihf-gcc -E -Wp,-MD,arch/arm/boot/dts/.100ask_imx6ull-14x14.dtb.d.pre.tmp -nostdinc -I./arch/arm/boot/dts -I./arch/arm/boot/dts/include -I./drivers/of/testcase-data -undef -D__DTS__ -x assembler-with-cpp -o arch/arm/boot/dts/.100ask_imx6ull-14x14.dtb.dts.tmp arch/arm/boot/dts/100ask_imx6ull-14x14.dts ; 
    
    ./scripts/dtc/dtc -O dtb -o arch/arm/boot/dts/100ask_imx6ull-14x14.dtb -b 0 -i arch/arm/boot/dts/ -Wno-unit_address_vs_reg -d arch/arm/boot/dts/.100ask_imx6ull-14x14.dtb.d.dtc.tmp arch/arm/boot/dts/.100ask_imx6ull-14x14.dtb.dts.tmp ; cat arch/arm/boot/dts/.100ask_imx6ull-14x14.dtb.d.pre.tmp arch/arm/boot/dts/.100ask_imx6ull-14x14.dtb.d.dtc.tmp > arch/arm/boot/dts/.100ask_imx6ull-14x14.dtb.d
    

    可以看到,利用了arm-linux-gnueabihf-gcc 进行编译,"-E"是指预编译,将dts文件先预编译为临时文件"dts.tmp"。预编译为临时文件后,才会使用设备树的编译器dtc,把临时文件编译为dtb文件。

    dts之所以能使用include语法,就是因为会先用gcc(arm-linux-gnueabihf-gcc)命令进行预编译。如果不用gcc预编译,那么dts中的#include "imx6ull.dtsi",就应该改成/include/imx6ull.dtsi。

    修改设备树文件

    修改100ASK IMX6ULL PRO评估版的设备树文件100ask_imx6ull-14x14.dts

    /dts-v1/;
    
    #include <dt-bindings/input/input.h>
    #include "imx6ull.dtsi"
    
    / {
        model = "Freescale i.MX6 ULL 14x14 EVK Board";
        compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
    
        /* add node by martin */ // 添加内容
        100ask_test {
            xxx_led = "100ask_led_for_test";
        };
    
        chosen {
            stdout-path = &uart1;
        };
    
        ...
    };
    

    通过vim/vi编辑完dts文件后,可以直接编译。

    $ make dtbs
      CHK     include/config/kernel.release
      CHK     include/generated/uapi/linux/version.h
      CHK     include/generated/utsrelease.h
      CHK     include/generated/bounds.h
      CHK     include/generated/timeconst.h
      CHK     include/generated/asm-offsets.h
      CALL    scripts/checksyscalls.sh
      DTC     arch/arm/boot/dts/100ask_imx6ull-14x14.dtb
    

    可以看到生成了100ask_imx6ull-14x14.dtb文件,拷贝到nfs目录(~/nfs_rootfs/)

    $ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/
    

    因为设备树文件(.dtb)是通过bootloader(uboot)传给内核的。可以在启动板子的时候,按空格进入uboot。

    此时输入print命令,也可以看到uboot使用的设备树文件

    => print
    baudrate=115200
    board_name=EVK
    board_rev=14X14
    boot_fdt=try
    ...
    fdt_addr=0x83000000
    fdt_file=100ask_imx6ull-14x14.dtb
    fdt_high=0xffffffff
    fdtcontroladdr=9ef40478
    findfdt=if test $fdt_file = undefined; then if test $board_name = EVK && test $board_rev = 9X9; then setenv fdt_file imx6ull-9x9-evk.dtb; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv fdt_file imx6ull-14x14-evk.dtb; fi; if test $fdt_file = undefined; then setenv fdt_file imx6ull-14x14-alpha.dtb; fi; fi;
    ...
    

    这里fdt_file为 100ask_imx6ull-14x14.dtb,就是所使用的设备树文件。

    • 从uboot进入app,启动板子
    => boot
    
    • 进入boot目录
    # cd /boot
    # ls
    100ask_imx6ull-14x14.dtb      100ask_myir_imx6ull_mini.dtb  zImage
    

    里面有dtb文件。

    • 挂载nfs文件系统
    # mount -t nfs -o nolock,vers=3 192.168.1.4:/home/martin/nfs_rootfs /mnt
    

    注意:这里远程主机的nfs目录需要根据实际情况配置

    • 拷贝设备树文件到boot目录
    # cp 100ask_imx6ull-14x14.dtb 100ask_imx6ull-14x14.dtb_bak # 拷贝前先备份一下旧的dtb文件
    # cp /mnt/100ask_imx6ull-14x14.dtb .
    
    • 板子启动后查看设备树
    # reboot # 重启板子
    # ls /sys/firmware/
    devicetree fdt
    

    /sys/firmware/devicetree 目录下是以目录结构呈现的dtb文件,根节点对应base目录,每个节点对应一个目录,每个属性对应一个文件。
    这些属性的值如果是字符串,可以使用cat命令打印;如果是数值,可以用hexdump命令打印出来。

    而/sys/firmware/fdt文件,就是dtb格式的设备树文件,可以把它复制出来放到ubuntu上,执行下面命令反编译出来(-I dtb:输入格式dtb,-O dts:输出格式dts)

    $ cd /home/martin/100ask_imx6ull-sdk/Linux-4.9.88/ # 板子所用内核源码目录, 根据实际情况决定
    $ ./scripts/dtc/dtc -I dtb -O dts # 从板子上复制出来的ftd文件(dtb), 反编译为dts文件
    

    查看我们之前修改的dts文件,板子是否在运行

    # cd /sys/firmware/devicetree/base/
    # ls -l
    total 0
    -r--r--r--    1 root     root             4 Jan  1  1970 #address-cells
    -r--r--r--    1 root     root             4 Jan  1  1970 #size-cells
    drwxr-xr-x    2 root     root             0 Jan  1  1970 100ask_test
    drwxr-xr-x    2 root     root             0 Jan  1  1970 aliases
    drwxr-xr-x    2 root     root             0 Jan  1  1970 backlight
    drwxr-xr-x    2 root     root             0 Jan  1  1970 chosen
    drwxr-xr-x    6 root     root             0 Jan  1  1970 clocks
    -r--r--r--    1 root     root            34 Jan  1  1970 compatible
    drwxr-xr-x    3 root     root             0 Jan  1  1970 cpus
    drwxr-xr-x    3 root     root             0 Jan  1  1970 gpio-keys
    drwxr-xr-x    2 root     root             0 Jan  1  1970 interrupt-controller@00a01000
    drwxr-xr-x    3 root     root             0 Jan  1  1970 leds
    drwxr-xr-x    2 root     root             0 Jan  1  1970 memory
    -r--r--r--    1 root     root            36 Jan  1  1970 model
    -r--r--r--    1 root     root             1 Jan  1  1970 name
    drwxr-xr-x    2 root     root             0 Jan  1  1970 pxp_v4l2
    drwxr-xr-x    5 root     root             0 Jan  1  1970 regulators
    drwxr-xr-x    3 root     root             0 Jan  1  1970 reserved-memory
    drwxr-xr-x    2 root     root             0 Jan  1  1970 sii902x-reset
    drwxr-xr-x   13 root     root             0 Jan  1  1970 soc
    drwxr-xr-x    2 root     root             0 Jan  1  1970 sound
    drwxr-xr-x    3 root     root             0 Jan  1  1970 spi4
    

    可以看到目录100ask_test,就是我们之前修改dts文件,而创建的节点。
    之前修改的dts:

    /dts-v1/;
    
    #include <dt-bindings/input/input.h>
    #include "imx6ull.dtsi"
    
    / {
        model = "Freescale i.MX6 ULL 14x14 EVK Board";
        compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
    
        /* add node by martin */
        100ask_test {
            xxx_led = "100ask_led_for_test";
        };
    ...
    }
    

    进入100ask_test目录,可以看到下面几个文件:

    # ls -l
    total 0
    -r--r--r--    1 root     root            12 Jan  1  1970 name
    -r--r--r--    1 root     root            20 Jan  1  1970 xxx_led
    

    name和xxx_led 刚好是我们之前添加的100ask_test的属性,其值是"100ask_led_for_test"。可以用cat命令查看其值:

    # cat name
    100ask_test
    # cat xxx_led
    100ask_led_for_test
    

    反编译fdt

    # cd /sys/firmware
    # ls
    devicetree  fdt
    # mount -t nfs -o nolock,vers=3 192.168.1.4:/home/martin/nfs_rootfs /mnt
    # cp fdt /mnt/100ask_imx6ull_fdt
    

    怎么转换为platform_device

    内核处理设备树文件,我们知道所有node会生成device_node,会有部分转换为platform_device,但是如何转换为platform_device呢?

    1)platform_device中包含resourc数组,它来自device_node的reg,interrupt属性;
    2)platform_device.dev.of_node 指向device_node,可以通过它获得其他属性;

    总线设备驱动模型中,有一个platform_bus_type(bus_type 类型),它有platform设备列表、platform driver列表,通过.match成员(函数指针)判断是否匹配,如果匹配,就调用driver的.probe。

    回顾下platform_device与platform_driver的名字匹配顺序:
    1)platform_device的.driver_override 与 platform_driver.driver中的.name;
    2)platform_device的.of_node数组的某个.name与 与 platform_driver.driver.of_match_table数组的某个.name(来源于设备树);
    3)platform_device的.name 与 platform_driver.id_table数组中的某个.name;(常用)
    4)platform_deivce的.name 与 platform_driver.driver中的.name;(常用)


    platform_get_resource从设备树获取资源

    platform_get_resource 跟设备树没什么关系,但设备树的节点被转换为platform_device后,设备树中的reg属性、interrupts属性也会被转换为“resource”。

    此时,可以使用该函数取出这些资源。

    函数原型:

    /**
    * platform_get_resource - get a resource for a device
    * @dev: platform device 
    * @type: resource type 支持取这几类资源:IORESOURCE_MEM, IORESOURCE_REG, IORESOURCE_IRQ等
    * @num: resource index 代表这类资源中的哪个
    */
    struct resource *platform_get_resource(struct platform_device *dev,
                           unsigned int type, unsigned int num);
    

    对于设备树节点中的reg属性(地址),通过type=IORESOURCE_MEM来获得;
    对于设备树节点中的interrupts属性(中断),通过type=IORESOURCE_IRQ来获得;

    怎么获取转换为platform_device的设备树节点中的非标准属性,比如pin(pin=xxx)?

    对于根节点"/ { };",会保存在一个全局变量of_root里面( device_node类型)。可以通过访问该全局变量(代表根节点),得到任意一个节点,得到节点后可以读出属性。

    于是,可以使用内核提供的函数,直接访问device_node,从而读出这些属性。
    这些函数可以分为3来:找到节点,找到属性,获取属性的值。这些函数位于内核源码include/linux/of.h。

    1)找到节点

    a. of_find_node_by_path

    根据路径找到节点,比如"/"就对应根节点,"/memory"对应memory节点。
    函数原型:

    static inline struct device_node *of_find_node_by_path(const char *path);
    

    b. of_find_node_by_name

    根据名字找到节点,节点如果定义了name属性,我们可以根据名字找到它。
    函数原型:

    static inline struct device_node *of_find_node_by_name(struct device_node *from,
        const char *name);
    

    c. of_find_node_by_type

    根据类型找到节点,节点如果定义了device_type属性,我们可以根据类型找到它。
    函数原型:

    extern struct device_node *of_find_node_by_type(struct device_node *from,
        const char *type);
    

    d. of_find_compatible_node

    根据compatible找到节点,节点如果定义了compatible属性,那我们可以根据compatible属性找到它。
    函数原型:

    /*
     * from: 表示从哪个节点开始寻找, NULL表示从根节点开始寻找
     * type: 字符串, 用来指定device_type属性的值, 可以传入NULL
     * compatible: 字符串, 用来指定compatile属性的值
     */
    struct device_node *of_find_compatible_node(struct device_node *from,
        const char *type, const char *compatible);
    

    e. of_find_node_by_phandle

    根据phandle找到节点。
    dts文件被编译为dtb文件时,每个节点都有一个数字ID,这些ID彼此不同。可以用数字ID来找到device_node。这些数字ID就是phandle。
    函数原型:

    /*
     * handle: 要找的phandle (数字ID)
     */
    struct device_node *of_find_node_by_phandle(phandle handle);
    

    f. of_get_parent

    找到device_node的父节点。
    函数原型:

    struct device_node *of_get_parent(const struct device_node *node);
    

    g. of_get_next_parent

    实际上也是找到device_node的父节点,跟of_get_parent返回结果一样。区别在于它多调用了下列函数,把node节点的引用计数-1。这意味着调用of_get_next_parent后,你不再需要调用of_node_put释放node节点。

    of_node_put(node);
    

    h. of_get_next_child

    取出下一个子节点。
    函数原型:

    /*
     * node: 父节点
     * prev: 上一个子节点, NULL表示想找到第1个子节点
     */
    struct device_node *of_get_next_child(const struct device_node *node,
        struct device_node *prev);
    

    不断调用of_get_next_child,更新prev参数,就能得到所有子节点。

    i. of_get_next_available_child

    取出下一个“可用”的子节点,有些节点的status是"disabled",那就会跳过这些节点。
    函数原型:

    /*
     * node: 父节点
     * prev: 上一个子节点, NULL表示想找到第1个子节点
     */
    struct device_node *of_get_next_available_child(const struct device_node *node, struct device_node *prev);
    

    j. of_get_child_by_name

    根据名字取出子节点。
    函数原型:

    /*
    * node: 父节点
    * name: 要取出的子节点名字 
    */
    struct device_node *of_get_child_by_name(const struct device_node *node,
                    const char *name);
    

    2)找到属性

    a. of_find_property

    找到节点中的属性。
    函数原型:

    /*
     * np: 表示节点, 我们要在这个阶段中找到名为name的属性
     * lenp: 用来保存这个属性的长度, 即它的值的长度
     */
    struct property *of_find_property(const struct device_node *np,
                      const char *name,
                      int *lenp);
    

    设备树中,节点大概长这样:

    xxx_node {
        xxx_pp_name = "hello";
    };
    

    上面节点中,"xxx_pp_name" 就是属性的名字,值为"hello",值长度6。

    3)获取属性的值

    a. of_get_property

    根据名字找到节点的属性,并且返回它的值。
    函数原型:

    /*
     * np表示节点, 我们要在这个节点中找到名为name的属性, 然后返回其值
     * lenp: 保存这个属性的长度, 即值的长度
     */
    const void *of_get_property(const struct device_node *np, const char *name,
                    int *lenp);
    

    不论属性保存的是数字,还是字符串,保存的都是字节流。

    属性property结构体:

    struct property {
        char    *name;  // 属性的名字
        int    length;  // 属性名字的长度
        void    *value; // 属性值对应字节流
        struct property *next;
        unsigned long _flags;
        unsigned int unique_id;
        struct bin_attribute attr;
    };
    

    b. of_property_count_elems_of_size

    根据名字找到节点的属性,确定它的值有多少个元素(elem)。
    函数原型:

    /**
    * of_property_count_elems_of_size - Count the number of elements in a property
    *
    * @np:        device node from which the property value is to be read. // 节点
    * @propname:    name of the property to be searched. // 名为propname的属性
    * @elem_size:    size of the individual element // 每个元素尺寸
    *
    * Search for a property in a device node and count the number of elements of
    * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
    * property does not exist or its length does not match a multiple of elem_size
    * and -ENODATA if the property does not have a value.
    */
    int of_property_count_elems_of_size(const struct device_node *np,
                    const char *propname, int elem_size);
    

    返回指定名字的节点属性的元素个数:

    return prop->length / elem_size; // 元素总大小 / 元素尺寸
    

    在设备树中,节点大概长这样:

    xxx_node {
        xxx_pp_name = <0x50000000 1024> <0x60000000 2048>;
    };
    

    调用of_property_count_elems_of_size(np, "xxx_np_name", 8),返回值2;
    调用of_property_count_elems_of_size(np, "xxx_np_name", 4),返回值4;

    c. 读整数u32/u64

    函数原型:

    static inline int of_property_read_u32(const struct device_node *np,
                           const char *propname,
                           u32 *out_value);
    static inline int of_property_read_u64(const struct device_node *np,
                           const char *propname, u64 *out_value);
    

    在设备树中,节点大概长这样:

    xxx_node {
        name1 = <0x50000000>;
        name2 = <0x50000000 0x60000000>;
    };
    

    调用of_property_read_u32(np, "name1", &val),val得到值0x50000000;
    调用of_property_read_u64(np, "name2", &val),val得到值0x60000000 50000000;

    d. 读某个整数u32/u64

    函数原型:

    extern int of_property_read_u32_index(const struct device_node *np,
                           const char *propname,
                           u32 index, u32 *out_value);
    

    在设备树中,节点大概长这样:

    xxx_node {
        name2 = <0x50000000 0x60000000>;
    };
    

    调用of_property_read_u32_index(np, "name2", 1, &val),val得到值0x60000000。

    e. 读数组

    函数原型:

    extern int of_property_read_variable_u8_array(const struct device_node *np,
                        const char *propname, u8 *out_values,
                        size_t sz_min, size_t sz_max);
    extern int of_property_read_variable_u16_array(const struct device_node *np,
                        const char *propname, u16 *out_values,
                        size_t sz_min, size_t sz_max);
    extern int of_property_read_variable_u32_array(const struct device_node *np,
                        const char *propname,
                        u32 *out_values,
                        size_t sz_min,
                        size_t sz_max);
    extern int of_property_read_variable_u64_array(const struct device_node *np,
                        const char *propname,
                        u64 *out_values,
                        size_t sz_min,
                        size_t sz_max);
    

    在设备树中,节点大概长这样:

    xxx_node {
        name2 = <0x50000012 0x60000000>;
    };
    

    上面例子中属性name2的值,长度8(byte)。

    调用of_property_read_variable_u8_array(np, "name2", out_values, 1, 10),out_values将会保存这8个8位数:0x12, 0x00, 0x00, 0x50, 0x34, 0x00, 0x00, 0x60。

    调用of_property_read_variable_u16_array(np, "name2", out_values, 1, 10),out_values将会保存这4个16位数:0x0012, 0x5000, 0x0034, 0x6000。

    总之,这些函数要么能取到全部的数值,要么一个数值都取不到。

    如果值长度在sz_min和sz_max之间,就返回全部的数值;否则,一个数值都不返回。

    f. 读字符串

    函数原型:

    /*
     * 返回np的属性(名为propname)的值, (*out_string)指向这个值, 把它当做字符串.
     */
    extern int of_property_read_string(const struct device_node *np,
                       const char *propname,
                       const char **out_string);
    

    如何修改设备树文件?

    之前已经讲过修改dts文件、编译并烧录的方法,这里要讲的是怎么确定设备树与驱动程序的职责,设备树内容来源等。

    一个写得好的驱动程序,会尽量确定所用资源。把那些不确定度资源,留个设备树,让设备树来指定。

    根据原理图确定“驱动程序无法确定的硬件资源”,再在设备树文件中填写对应内容。
    那么,所填写内容的格式是什么?
    可以这样确定:

    1)看绑定文档
    内核文档Documentation/devicetree/bindings/
    做得好的厂家一般也会提供设备树的说明文档。

    2)参考同类型单板的设备树文件

    3)网上搜索

    4)实在没办法时,只能研究驱动源码


    参考

    http://www.100ask.org/

  • 相关阅读:
    冒泡排序
    Bootstrap fileinput v1.0(ssm版)
    Bootstrap table后端分页(ssm版)
    Bootstrap table前端分页(ssm版)
    北京鱼乐贝贝面试题
    通过前端控制器源码分析springmvc的执行过程
    java Pattern(正则)类
    ssm所需要的pom(jre8、tomcat8、spring4)
    Java 流(Stream)、文件(File)和IO
    idea构建一个简单的maven_web项目
  • 原文地址:https://www.cnblogs.com/fortunely/p/16405592.html
Copyright © 2020-2023  润新知