Skip to content

Latest commit

 

History

History
551 lines (491 loc) · 29 KB

设备树.md

File metadata and controls

551 lines (491 loc) · 29 KB

设备树

  • 包含头文件

    • .h .dts .dtsi , 建议是自己写头文件,使用后缀 .dtsi
    • .dtsi用于描述SOC 的内部外设的消息 ,CPU架构 ,主频,外设寄存器地址范围等
  • .dtsi文件

    • / 根节点,在被包含的dtsi文件中,/目录下的节点同样能在系统中体现,和自己建立的.dts文件中的/目录是并行的关系
    • 节点命名
      label : node-name@unit-address ==> 标签 :节点名字@设备地址或寄存器首地址 ( label: 可要可不要 节点没有寄存器地址或设备地址,unit-address可不要 )
          demo:
          cpu0:cpu@0
      
    • 节点属性 属性都是键值对 ,值可为空,或为任意的字节流
      1. 字符串
            compatible = "arm , cortex-a7";
        
      2. 32位无符号整数
            reg=<0>;
            reg=<0 0x0123 100>;
        
      3. 字符串列表
            compatible = "arm , cortex-a7","fsl , imx6ull-gpmi-emmc";
        
    • 标准属性
      1. compatible 属性 兼容性属性, 值是字符串。该属性用于将设备和驱动绑定起来的。 demo:
            compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
        
            fsl表示厂商,"imx6ul-evk-wm8960" 和 "imx-audio-wm8960"表示驱动模块名字。 该设备在linux内核先查找第一个兼容值"imx6ul-evk-wm8960",找不到再找第二兼容值"imx-audio-wm8960"
        
        一般驱动程序文件 .c 都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动,demo
            static const struct of_device_id imx_wm8960_dt_ids[] = 
            {
                { .compatible = "fsl,imx-audio-wm8960", }  ,
                { .compatible = "fsl,imx6ul-evk-wm8960", } ,
            };
        
        如果在设备树中有哪个节点的 compatible 属性值与此相等,那么这个节点就会使用此驱动.c文件
      2. model 属性 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的。
            model = "wm8960-audio";
        
      3. status 属性 设备状态有关的, status 属性值也是 字符串 ,字符串是设备的状态信息
        描述
        “okay” 设备是可操作
        “disable” 设备当前是不可操作
        “fail” 备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作
        “fail-sss” 含义和“fail”相同
      4. #address-cells 和 #size-cells属性 用于描述子节点的地址信息
            reg = < address1 length1 address2 length2 .... >
        即: 
            reg = < address-cells1  size-cells1  address-cells2   size-cells2 .... >
            address1是起始地址 ,length1表示地址长度字长(32b) ,假如lengthx设置为0 ,相当于只设置了起始地址
        
      5. 描述设备地址空间资源信息
            reg = <address length>
        
      6. ranges 属性 ranges 是一个地址映射/转换表。ranges属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字矩阵
      7. name 属性 记录节点名字,已弃用。
  • 属性设置的套路

    1. 第一种是抄类似的dts
    2. 第二种是查询内核中的文档,比如Documentation/devicetree/bindings/i2c/i2c-imx.txt就描述了imx平台的i2c属性设置方法;Documentation/devicetree/bindings/fb就描述了lcd、lvds这类属性设置方法
  • linux内核通过根节点 compatible 属性找到的对应设备函数调用过程 I.MX6UL嵌入式linux驱动开发指南pdf ==> 1089页

  • 内核设备树的路径 arch/arm/boot/dts

    • 文件的包含关系:imx6ull芯片平台的原始 头文件: imx6ul.dtsi
    • 官方出的demo板配的设备树: imx6ull-14x14-evk.dts ==> #include <imx6ull.dtsi>
    • 正点原子根据官方demo的设备树文件:追加自己的设备树文件 imx6ull_emmc_cxn.dts ==> #include<imx6ull-14x14-evk.dts> 追加设备节点的格式
          &i2c1{                  //&i2c1 表示要访问i2c1这个label所对应的节点
              /*追加或修改的内容*/
          };
      
          demo:
          @i2c1{                                          //向i2c1这个节点添加内容
              clock-frequency = <100000>;
              pinctrl-names = "default";
              pinctrl-0 = <&pinctrl_i2c1>;
              status = "okay";
      
              mag3110@0e {                                //i2c1总线上挂载的设备器件1
              compatible = "fsl,mag3110";
              reg = <0x0e>;
              position = <2>;
              };
      
              fxls8471@1e {                               //i2c1总线上挂载的设备器件2
              compatible = "fsl,fxls8471";
              reg = <0x1e>;
              position = <0>;
              interrupt-parent = <&gpio5>;
              interrupts = <0 8>;
          }
      
  • 设备树在系统中的体现

    • Linux 内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的/proc/device/tree目录下根据节点名字创建不同文件夹
    • 进入不同节点的文件夹,又可以查看节点内的节点
  • 特殊节点

  1. aliases 别名 , 格式: 别名 = &节点label
        demo:
            can0 = &flexcan1;
    	    can1 = &flexcan2;
    
  2. chosen 子节点 并非一个真实的设备,,是uboot向linux内核传递的数据。进入 /proc/device-tree/chosen 文件夹里面,ls,可以看到 bootargs 节点。该节点是uboot在启动内核的时候,在chosen节点创建的
  • 内核查找节点的OF函数

    • 数据结构
      1. 描述设备节点的数据结构体 device_node , 文件路径: include/linux/of.h
          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;
                                  struct	kobject kobj;
                                  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
                              };
      
      
      1. 描述设备资源信息的 数据结构体 resource ,文件路径: include/linux/ioport.h
          struct resource {
                              resource_size_t start;      //开始地址
                              resource_size_t end;        //结束地址
                              const char *name;           //资源的名字
                              unsigned long flags;        //资源的标志位,资源的类型(宏表示,相关宏在include/linux/ioport.h的头文件中)
                              struct resource *parent, *sibling, *child;
                          };
      
    • 查找节点的of函数
      1. 通过节点名字查找指定的节点 struct device_node * of_find_node_by_name(struct device_node *from , const char *name) from 是开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树 , name 要查找的节点名字 返回: 找到的节点。如果为NULL,表示查找失败
      2. 通过device_type属性查找指定的节点 struct device_node *of_find_node_by_type(struct device_node *from, const char *type) from 是开始查找的节点,如果为NULL,表示从根节点开始查找整个设备树 , type 要查找的节点device_type属性值,字符串 返回: 找到的节点。如果为NULL,表示查找失败
      3. 通过device_type和compatible这两个属性查找指定的节点 struct device_node *of_find_compatible_node(struct device_node *from,const char *type,const char *compatible) from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为 NULL,表示忽略掉 device_type 属性。 compatible: 要查找的节点所对应的 compatible 属性列表。 返回: 找到的节点。如果为NULL,表示查找失败
      4. 通过of_device_id匹配表来查找指定的节点 struct device_node *of_find_matching_node_and_match(struct device_node *from,const struct of_device_id *matches,const struct of_device_id **match) from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 matches: of_device_id 匹配表,也就是在此匹配表里面查找节点。 match: 要找的匹配的 of_device_id。 返回: 找到的节点。如果为NULL,表示查找失败
      5. 通过路径来查找指定的节点 inline struct device_node *of_find_node_by_path(const char *path) path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。 返回: 找到的节点。如果为NULL,表示查找失败。
    • 查找属性值得of函数
      1. 查找指定的属性 property *of_find_property(const struct device_node *np,const char *name,int *lenp) np:设备节点。 name: 属性名字。 lenp[out]:属性值的字节数 返回值: 找到的属性。
      2. 获取属性中元素的数量 int of_property_count_elems_of_size(const struct device_node *np,const char *propname,int elem_size) np: 设备节点。 propname: 需要统计元素数量的属性名字 elem_size: 元素的长度 返回值: 得到的属性元素数量。
      3. 从属性中获取指定标号的u32类型数据值 int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index,u32 *out_value) np: 设备节点 propname: 要读属性的名字 elem_size: 要读取的值标号 out_value[out]: 读取到的值 返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。
      4. 读取属性中 u8、 u16、 u32 和 u64 类型的数组数据 int of_property_read_u8_array(const struct device_node *np,const char *propname,u8 *out_values,size_t sz) int of_property_read_u16_array(const struct device_node *np,const char *propname,u16 *out_values,size_t sz) int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz) int of_property_read_u64_array(const struct device_node *np,const char *propname,u64 *out_values,size_t sz) np:设备节点。 proname: 要读取的属性名字。 out_value:读取到的数组值,分别为 u8、 u16、 u32 和 u64。 sz: 要读取的数组元素数量。 返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。
      5. 读取只有一个整型值得属性 int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value) int of_property_read_u16(const struct device_node *np,const char *propname,u16 *out_value) int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value) int of_property_read_u64(const struct device_node *np,const char *propname,u64 *out_value) np:设备节点。 proname: 要读取的属性名字。 out_value:读取到的数组值。 返回值: 0,读取成功,负值,读取失败, -EINVAL 表示属性不存在, -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小。
      6. 读取属性中的字符串值 int of_property_read_string(struct device_node *np,const char *propname,const char **out_string) np:设备节点。 proname: 要读取的属性名字。 out_string:读取到的字符串值。 返回值: 0,读取成功,负值,读取失败。
      7. 用于获取#address-cells int of_n_addr_cells(struct device_node *np) np:设备节点。 返回值: 获取到的#address-cells 属性值
      8. 用于获取#size-cells 属性值 int of_n_size_cells(struct device_node *np) np:设备节点。 返回值: 获取到的#size-cells 属性值。
    • 其他常用的OF函数
      1. 函数用于查看节点的 compatible 属性是否有包含 compat 指定的字符串,也就是检查设备节点的兼容性 int of_device_is_compatible(const struct device_node *device,const char *compat) device:设备节点。 compat:要查看的字符串。 返回值: 0,节点的 compatible 属性中不包含 compat 指定的字符串; 正数,节点的 compatible属性中包含 compat 指定的字符串
      2. 用于获取地址相关属性。主要是“reg”和“assigned-addresser” const __be32 *of_get_address(struct device_node *dev,int index,u64 *size,unsigned int *flags) dev:设备节点 index:要读取的地址标号。 size:地址长度 flags:参数,比如 IORESOURCE_IO、 IORESOURCE_MEM 等 返回值: 读取到的地址数据首地址,为 NULL 的话表示读取失败。
      3. 负责将从设备树读取到的地址转换为物理地址 u64 of_translate_address(struct device_node *dev,const __be32 *in_addr) dev:设备节点。 in_addr:要转换的地址。 返回值: 得到的物理地址,如果为 OF_BAD_ADDR 的话表示转换失败。
      4. IIC、 SPI、 GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间, Linux内核使用 resource 结构体来描述一段内存空间 int of_address_to_resource(struct device_node *dev,int index,struct resource *r) dev:设备节点。 index:地址资源标号。 r:得到的 resource 类型的资源值。 返回值: 0,成功;负值,失败。
      5. 用于直接内存映射.采用设备树以后就可以直接通过 of_iomap 函数来获取内存地址所对应的虚拟地址,of_iomap 函数本质上也是将 reg 属性中地址信息转换为虚拟地址,如果 reg 属性有多段的话,可以通过 index 参数指定要完成内存映射的是哪一段 void __iomem *of_iomap(struct device_node *np,int index) np:设备节点。 index: reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置为 0。 返回值: 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况, index为0。采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap。 demo 设备树: cxnled { #address-cells = <1>; #size-cells = <1>; name = "cxnled"; compatible = "atkalpha-led","cxnled"; status = "okay"; reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */ 0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */ 0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */ 0X0209C000 0X04 /* GPIO1_DR_BASE */ 0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */ }; 驱动: virtual_ccm_ccgr1 = of_iomap(m_dts_chr_dev.node , 0); virtual_sw_mux_gpio1_io03 = of_iomap(m_dts_chr_dev.node , 1); virtual_sw_pad_gpio1_io03 = of_iomap(m_dts_chr_dev.node , 2); virtual_gpio1_dr = of_iomap(m_dts_chr_dev.node , 3); virtual_gpio1_gdir = of_iomap(m_dts_chr_dev.node , 4);
  • 模板分析

#include <dt-bindings/clock/imx6ul-clock.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "imx6ul-pinfunc.h"
#include "skeleton.dtsi"
    / {                                                     //根节点
        compatible="fsl,imx6ull-alientek-evk" , "fsl,imx6ull";

        aliases {
            can0 = &flexcan1;
            can1 = &flexcan2;
            ethernet0 = &fec1;
            ethernet1 = &fec2;
            gpio0 = &gpio1;
            gpio1 = &gpio2;
            gpio2 = &gpio3;
            gpio3 = &gpio4;
            gpio4 = &gpio5;
            i2c0 = &i2c1;
            i2c1 = &i2c2;
            i2c2 = &i2c3;
            i2c3 = &i2c4;
            mmc0 = &usdhc1;
            mmc1 = &usdhc2;
            serial0 = &uart1;
            serial1 = &uart2;
            serial2 = &uart3;
            serial3 = &uart4;
            serial4 = &uart5;
            serial5 = &uart6;
            serial6 = &uart7;
            serial7 = &uart8;
            spi0 = &ecspi1;
            spi1 = &ecspi2;
            spi2 = &ecspi3;
            spi3 = &ecspi4;
            usbphy0 = &usbphy1;
            usbphy1 = &usbphy2;
        };

        cpus {                                                 //imx6ull是cortex-A7架构,只有一个cpu
            #address-cells = <1>;
            #size-cells = <0>;

            cpu0: cpu@0 {
                compatible = "arm,cortex-a7";
                device_type = "cpu";
                reg = <0>;
            };
        };

        soc {                                                   //管理 SOC内部外设                      
            #address-cells = <1>;
            #size-cells = <1>;
            compatible = "simple-bus";
            interrupt-parent = <&gpc>;
            ranges;

            ocrams: sram@00900000 {                             //内部RAM
                compatible = "fsl,lpm-sram";
                reg = <0x00900000 0x4000>;
            };

        
            aips1: aips-bus@02000000 {                          //外设控制寄存器1
                compatible = "fsl,aips-bus", "simple-bus";
                #address-cells = <1>;
                #size-cells = <1>;
                reg = <0x02000000 0x100000>;
                ranges;

                
            };

            aips2: aips-bus@02100000 {                          //外设控制寄存器2
                compatible = "fsl,aips-bus", "simple-bus";
                #address-cells = <1>;
                #size-cells = <1>;
                reg = <0x02100000 0x100000>;
                ranges;

            };

            aips3: aips-bus@02200000 {                          //外设控制寄存器3
                compatible = "fsl,aips-bus", "simple-bus";
                #address-cells = <1>;
                #size-cells = <1>;
                reg = <0x02200000 0x100000>;
                ranges;

            };

        };
    };


/*imx6dl.dtsi中gpio1控制器的定义节点*/
gpio1: gpio@0209c000 {
    compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
    reg = <0x0209c000 0x4000>;
    interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
             <0 67 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
};
 
/*imx6qdl-sabreauto.dtsi中某个设备节点*/
max7310_reset: max7310-reset {
    compatible = "gpio-reset";
    reset-gpios = <&gpio1 15 1>;            //&gpio1 15 表示引用了gpio1节点 的15号引脚 ,最后一个参数  1 ,表示常规状态 ,其有效状态是低电平有效 ; 因为为gpio1节点中设置了#gpio-cells = <2>,所以带有引脚号15,常规电平1共两个参数,假如#gpio-cells = <1>,则最后一个常规电平参数1可以不要
    reset-delay-us = <1>;
    #reset-cells = <0>;
};

应用:
    gpio = of_get_named_gpio(node, "reset-gpios", index);
        node : 节点
        "reset-gpios" : gpio属性的名字,一定要和节点属性中的xxx-gpios相同
        index : 最后一个是编号index,当节点中有n个同名的xxx-gpios时,可以通过它来获取特定的那个gpio,同一节点中gpio同名情况很少存在,所以我们都把index设为0

个人理解 && 步骤

  • 在设备树.dts文件中,添加了自己设定的节点,make dtbs编译生成.dtb之后,在系统加载设备树的时候,自己设定的节点已挂载到设备树上(但未挂载在/dev/目录下)
  • 替换掉原网络挂载的设备树文件,重启开发板
  • 写驱动设备,因为自己在设备树节点上带有名称,寄存器等参数,可以通过一些列的of函数,在设备树中提取节点的相关信息
  • 提取了寄存器等参数,利用of_iomap函数,把设备树节点上的物理寄存器地址映射到虚拟地址
  • 接下来的操作和标准字符设备一样

pinctrl子系统 (pin-control)

  • 配置信息:形式 宏 + 值
    => 展开 <mux_reg conf_reg input_reg mux_reg_value input_reg_value> conf_reg_val

        demo:
        &iomuxc {
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_hog_1>;                                                   ---------------------表示引用pinctrl的子pin_XX
                imx6ul-evk {
                    pinctrl_hog_1: hoggrp-1 {
                        fsl,pins = <
                            MX6UL_PAD_UART1_RTS_B__GPIO1_IO19		0x17059 /* SD1 CD */        ---------------------pinctrl子系统的表达形式
                            MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT	0x17059 /* SD1 VSELECT */   ---------------------pinctrl子系统的表达形式
                            MX6UL_PAD_GPIO1_IO09__GPIO1_IO09        0x17059 /* SD1 RESET */     ---------------------pinctrl子系统的表达形式
                        >;
                };
    
    #define     MX6UL_PAD_UART1_RTS_B__GPIO1_IO19   0x0090           0x031C               0x0000                                0x5             0x0
                                                    mux_reg偏移地址   conf_reg偏移地址     input_reg偏移地址,00表示无输入寄存器    mux_reg_value   input_reg_value
    
    在参考手册里找到 :
        IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B   对应的是 mux_reg 的地址   
        IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B   对应的是 conf_reg 的地址  
        IOMUXC_UART1_RTS_B_SELECT_INPUT     对应的是 input_reg 的地址                                       
    
  • 在设备树中添加pinctrl节点

  1. 标签名称一定要“pinctrl_”作为前缀
  2. 添加顾定属性,用来获取PIN的配置信息。 I.MX系列的SOC,属性名字一定为“fsl,pins”,不同厂商都不同
  3. 在顾定属性中添加具体的PIN配置信息
  4. demo
        pinctrl_test: testgrp {
            fsl,pins = <
                MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config     /*config 是具体设置值*/
            >;
        };
    

gpio系统

  • API

    1. int gpio_request(unsigned gpio, const char *label) ,申请一个IO, gpio: 要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。 label: 给申请的gpio口设置名字 返回值: 0:申请成功 其他:失败

    2. void gpio_free(unsigned gpio),释放gpio gpio: 要释放的 gpio 标号

    3. int gpio_direction_input(unsigned gpio),函数用于设置某个 GPIO 为输入 gpio:要设置为输入的 GPIO 标号。 返回值: 0,设置成功;负值,设置失败

    4. int gpio_direction_output(unsigned gpio, int value),此函数用于设置某个 GPIO 为输出,并且设置默认输出值 gpio:要设置为输出的 GPIO 标号。 value: GPIO 默认输出值。 返回值: 0,设置成功;负值,设置失败。

    5. int gpio_get_value(unsigned int gpio)获取某个GPIO的值

    6. void gpio_set_value(unsigned gpio, int value),设置某个GPIO的输出值

      设备树中有节点, &usdhc1 { pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc1>; pinctrl-1 = <&pinctrl_usdhc1_100mhz>; pinctrl-2 = <&pinctrl_usdhc1_200mhz>; cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; //在这绑定了引脚号和功能引脚 keep-power-in-suspend; enable-sdio-wakeup; vmmc-supply = <&reg_sd1_vmmc>; status = "okay"; };

  • 与gpio相关的OF函数

    1. int of_gpio_named_count(struct device_node *np, const char *propname),设备树某个属性里面定义了几个GPIO信息,空的GPIO也会被统计 np: 设备节点 propname: 要统计的GPIO属性 返回值:正值,统计到的 GPIO 数量;负值,失败。

          demo:
              某个节点里面:
                          gpios = <  0
                                     &gpio1 1 2
                                     0 &
                                     gpio2 3 4>;
      
          使用该函数时
      
          gpio_cnt = of_gpio_named_count(&dev_np, "gpios" );
      
    2. int of_gpio_count(struct device_node *np),与上面的函数int of_gpio_named_count(struct device_node *np, const char *propname)功能类似,但该函数只针对"gpios"属性

    3. int of_get_named_gpio(struct device_node *np,const char *propname,int index),此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号,即获取7这个编号 np: 设备节点 propname: 要统计的GPIO属性 index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。 返回值:正值,统计到的 GPIO 编号;负值,失败。

    4. 包含的头文件 #include <linux/of_gpio.h>

  • gpio 扩展

  • pinctrl && gpio 混合使用的操作流程

    1. 先在自己的设备树源代码(.dts)文件中,在非 /根目录节点下的 pinctrl 节点中(即&xx节点),添加自己的 pinctrl 信息

    2. 在根节点(或根节点下的成员节点中添加自定义的节点)

    3. 在官方(因为我们的设备树源代码是拷贝官方的)设备树源代码中,查找IO是有有重复使用的情况。

    4. 最后 make dtbs 命令重新编译设备树