Linux驱动开发笔记(四)设备树进阶及GPIO、Pinctrl子系统

news2024/12/27 4:03:29

文章目录

  • 前言
  • 一、设备树的进阶知识
    • 1. 追加/修改节点内容
    • 2.chosen子节点
    • 3. 获取设备树节点信息
      • 3.1 of_find_node_by_path( )函数
      • 3.2 of_find_node_by_name( )函数
      • 3.3 of_find_node_by_type( )函数
      • 3.4 of_find_compatible_node( )函数
      • 3.5 of_find_matching_node_and_match( )函数
      • 3.6 of_get_parent( )函数
      • 3.7 of_get_next_child( )函数
    • 4. 提取属性值的of函数
      • 4.1 of_find_property( )函数
      • 4.2 of_property_read_uX_array( )函数
      • 4.3 of_property_read_uX( )函数
      • 4.4 of_property_read_string( )函数
      • 4.5 of_property_read_string_index( )函数
      • 4.6 of_property_read_bool( )函数
      • 4.7 of_iomap( )函数
  • 二、Pinctrl子系统
    • 2.1 使用pinctrl设置复用关系
      • 2.1.1 客户端(Client)
      • 2.1.2 服务端(Server)
    • 2.2 pinctrl相关结构体
  • 三、GPIO子系统
    • 1. 相关API
      • 1.1 of_get_named_gpio( )函数
      • 1.2 gpio_request( )函数
      • 1.3 gpio_direction_input( )函数
      • 1.4 gpio_direction_output( )函数
      • 1.5 gpio_set_value( )函数
      • 1.6 gpio_get_value( )函数
      • 1.7 gpio_free( )函数
    • 2.相关结构体
      • 2.1 gpio_device结构体
      • 2.2 gpio_chip结构体
      • 2.3 gpio_desc结构体
    • 3. GPIO设备树分析
      • 3.1 gpio-controller属性
      • 3.2 gpio-cells属性
      • 3.3 gpio-ranges属性


前言

  在早期笔者已经简单介绍过设备树的相关信息,本章将详细展开学习一下这部分内容。


一、设备树的进阶知识

1. 追加/修改节点内容

&cpu0 {
    cpu-supply = <&vdd_cpu>;
};

  这些源码并不包含在根节点“/{…}”内,它们不是一个新的节点,而是向原有节点追加内容。 以上方源码为例,“&cpu0”表示向“节点标签”为“cpu0”的节点追加数据, 这个节点可能定义在本文件也可能定义在本文件所包含的设备树文件中。

2.chosen子节点

chosen {
   bootargs = "earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTUUID=614e0000-0000 rw rootwait";
};

  chosen子节点不代表实际硬件,它主要用于给内核传递参数。 此外这个节点还用作uboot向linux内核传递配置参数的“通道”, 我们在Uboot中设置的参数就是通过这个节点传递到内核的, 这部分内容是uboot和内核自动完成的,作为初学者我们不必深究。

3. 获取设备树节点信息

  这一小节我们就开始学习如何从设备树的设备节点获取我们想要的数据。 内核提供了一组函数用于从设备节点获取资源(设备节点中定义的属性)的函数,这些函数以of_开头,称为OF操作函数。 常用的OF函数介绍如下:

名称描述
of_find_node_by_path( )根据节点路径寻找节点函数
of_find_node_by_name( )根据节点名字寻找节点函数
of_find_node_by_type( )根据节点类型寻找节点函数
of_find_compatible_node( )根据节点类型和compatible属性寻找节点函数
of_find_matching_node_and_match( )根据匹配表寻找节点函数
of_get_parent( )寻找父节点函数
of_get_next_child( )寻找子节点函数

3.1 of_find_node_by_path( )函数

//根据节点路径寻找节点
struct device_node *of_find_node_by_path(const char *path)
  • 参数:
    • path: 指定节点在设备树中的路径。
  • 返回值:
    • device_node: 结构体指针,如果查找失败则返回NULL,否则返回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;//唯一ID
    #struct of_irq_controller *irq_trans;//中断控制器结构体指针
#endif
};

3.2 of_find_node_by_name( )函数

//根据节点名字寻找节点
struct device_node *of_find_node_by_name(struct device_node *from,const char *name);
  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • name: 要寻找的节点名。
  • 返回值:
    • device_node: 结构体指针,如果查找失败则返回NULL,否则返回device_node类型的结构体指针,它保存着设备节点的信息。

3.3 of_find_node_by_type( )函数

//根据节点类型寻找节点
struct device_node *of_find_node_by_type(struct device_node *from,const char *type)
  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • type: 要查找节点的类型,这个类型就是device_node-> type。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.4 of_find_compatible_node( )函数

// 根据节点类型和compatible属性寻找节点
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

  相比of_find_node_by_name函数增加了一个compatible属性作为筛选条件。

  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • type: 要查找节点的类型,这个类型就是device_node-> type。
    • compatible: 要查找节点的compatible属性。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.5 of_find_matching_node_and_match( )函数

//根据匹配表寻找节点
static inline 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)

  可以看到,该结构体包含了更多的匹配参数,也就是说相比前三个寻找节点函数,这个函数匹配的参数更多,对节点的筛选更细。参数match,查找得到的结果。

  • 参数:
    • from: 指定从哪个节点开始查找,它本身并不在查找行列中,只查找它后面的节点,如果设置为NULL表示从根节点开始查找。
    • matches: 源匹配表,查找与该匹配表想匹配的设备节点。
    • of_device_id: 结构体如下:
struct of_device_id {
    char    name[32]; 		//节点中属性为name的值
    char    type[32];		//节点中属性为device_type的值
    char    compatible[128];//节点的名字,在device_node结构体后面放一个字符串,full_name指向它
    const void *data;		//链表,连接该节点的所有属性
};
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.6 of_get_parent( )函数

//寻找父节点
struct device_node *of_get_parent(const struct device_node *node)
  • 参数:
    • node: 指定谁(节点)要查找父节点。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

3.7 of_get_next_child( )函数

//寻找子节点
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
  • 参数:
    • node: 指定谁(节点)要查找它的子节点。
    • prev: 前一个子节点,寻找的是prev节点之后的节点。这是一个迭代寻找过程,例如寻找第二个子节点,这里就要填第一个子节点。参数为NULL 表示寻找第一个子节点。
  • 返回值:
    • device_node: device_node类型的结构体指针,保存获取得到的节点。同样,如果失败返回NULL。

4. 提取属性值的of函数

名称描述
of_find_property( )查找节点属性函数
of_property_read_uX_array( )读取整型属性函数
of_property_read_uX( )简化后的读取整型属性函数,对读取整型属性函数的简单封装
of_property_read_string_index( )它用于指定读取属性值中第几个字符串,上位替代
of_property_read_bool( )读取布尔型属性函数
of_iomap( )自动完成物理地址到虚拟地址的转换
of_address_to_resource( )得到在设备树中设置的地址值

4.1 of_find_property( )函数

struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • name: 属性名。
    • lenp: 获取得到的属性值的大小,这个指针作为输出参数,这个参数“带回”的值是实际获取得到的属性大小。
  • 返回值:
    • property: 获取得到的属性。property结构体,我们把它称为节点属性结构体,如下所示。失败返回NULL。从这个结构体中我们就可以得到想要的属性值了。
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
};

4.2 of_property_read_uX_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)
  • 参数:
    • np: 指定要读取那个设备节点结构体,也就是说读取那个设备节点的数据。
    • propname: 指定要获取设备节点的哪个属性。
    • out_values: 这是一个输出参数,是函数的“返回值”,保存读取得到的数据。
    • sz: 这是一个输入参数,它用于设置读取的长度。
  • 返回值:
    • 返回值,成功返回0,错误返回错误状态码(非零值),-EINVAL(属性不存在),-ENODATA(没有要读取的数据),-EOVERFLOW(属性值列表太小)。

4.3 of_property_read_uX( )函数

//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)

4.4 of_property_read_string( )函数

&wmsp; 在设备节点中存在很多字符串属性,例如compatible、status、type等等,这些属性可以使用查找节点属性函数of_find_property来获取,但是这样比较繁琐。内核提供了一组用于读取字符串属性的函数,介绍如下:

//读取字符串属性函数
int of_property_read_string(const struct device_node *np,const char *propname,const char **out_string)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • propname: 属性名。
    • out_string: 获取得到字符串指针,这是一个“输出”参数,带回一个字符串指针。也就是字符串属性值的首地址。这个地址是“属性值”在内存中的真实位置,也就是说我们可以通过对地址操作获取整个字符串属性(一个字符串属性可能包含多个字符串,这些字符串在内存中连续存储,使用’0’分隔)。
  • 返回值:
    • 返回值:成功返回0,失败返回错误状态码。

4.5 of_property_read_string_index( )函数

int of_property_read_string_index(const struct device_node *np,const char *propname, int index,const char **out_string)

&wmsp; 相比前面的函数增加了参数index,它用于指定读取属性值中第几个字符串,index从零开始计数。 上一个函数只能得到属性值所在地址,也就是第一个字符串的地址,其他字符串需要我们手动修改移动地址,非常麻烦,推荐使用第本函数。

4.6 of_property_read_bool( )函数

&wmsp; 在设备节点中一些属性是BOOL型,当然内核会提供读取BOOL型属性的函数,介绍如下:

//读取布尔型属性
static inline bool of_property_read_bool(const struct device_node *np, const char *propname);
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • propname: 属性名。
  • 返回值:
    • 这个函数不按套路出牌,它不是读取某个布尔型属性的值,仅仅是读取这个属性存在或者不存在。如果想要或取值,可以使用之前讲解的“全能”函数查找节点属性函数of_find_property。

4.7 of_iomap( )函数

&wmsp; 在设备树的设备节点中大多会包含一些内存相关的属性,比如常用的reg属性。通常情况下,得到寄存器地址之后我们还要通过ioremap函数将物理地址转化为虚拟地址。现在内核提供了of函数,自动完成物理地址到虚拟地址的转换。介绍如下:

void __iomem *of_iomap(struct device_node *np, int index)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • index: 通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。
  • 返回值:
    • 成功,得到转换得到的地址。失败返回NULL。

  内核也提供了常规获取地址的of函数,这些函数得到的值就是我们在设备树中设置的地址值。介绍如下:

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)
  • 参数:
    • np: 指定要获取那个设备节点的属性信息。
    • index: 通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。
    • r: 这是一个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;
};
  • 返回值:
    • 成功返回0,失败返回错误状态码。

  这里介绍了三类常用的of函数,这些基本满足我们的需求,其他of函数后续如果使用到我们在详细介绍。

二、Pinctrl子系统

  在之前我们进行字符设备编辑的时候已经简单提到过引脚复用功能及其修改方法,接下来将讲述一下引脚复用相关的pinctrl子系统,其主要用于管理芯片的引脚,比如引脚的复用,引脚上下拉,驱动能力等。pinctrl核心层是内核抽象出来,向下为个SoC pin controler drvier提供底层通信接口的能力, 向上为其他驱动提供了控制pin的能力,比如pin复用、配置引脚的电气特性,同时也为GPIO子系统提供pin操作。而pin控制器驱动层,主要提供了操作pin的方法。
在这里插入图片描述

2.1 使用pinctrl设置复用关系

  在设备树中,pinctrl(引脚控制)使用客户端和服务端的概念来描述引脚控制的关系和配置,以便在系统启动时正确地初始化和管理硬件设备的引脚。这种划分提供了对引脚功能的抽象,使得设备驱动程序能够以一种标准化的方式使用这些引脚。

2.1.1 客户端(Client)

  pinctrl客户端可以指定引脚描述、引脚组描述和配置描述,以满足其特定的功能和需求。客户端通常与某个具体的硬件设备相关联,例如一个LED灯或者一个传感器。

node {
    pinctrl-names = "default", "wake up";
    pinctrl-0 = <&pinctrl_hog_1>;
    pinctrl-1 = <&pinctrl_hog_2>;
}

pinctrl-names 属性定义了两个状态名称:default 和 wake up。
pinctrl-0 属性指定了第一个状态 default 对应的引脚配置,引用了 pinctrl_hog_1 节点。
pinctrl-1 属性指定了第二个状态 wake up 对应的引脚配置,引用了 pinctrl_hog_2 节点。这意味着设备可以处于两个不同的状态之一,每个状态分别使用不同的引脚配置。

2.1.2 服务端(Server)

  服务端是设备树中定义引脚配置的部分。它包含引脚组和引脚描述符,为客户端提供引脚配置选择。服务端在设备树中定义了 pinctrl 节点,其中包含引脚组和引脚描述符的定义。以下是pinctrl节点下的描述形式:

&pinctrl {
    /*----------新添加的内容--------------*/
    led_test {
        led_test_pin: led_test_pin { //led_test_pin”节点标签
        	//“rockchip,pins”是固定的格式,后面的内容自定义的,我们将通过这个标签引用这个节点
            rockchip,pins = <0 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };
};

  如上述的一个外设xxx,由其使用的引脚为GPIO0_C7,”RK_FUNC_GPIO”设置复用功能为GPIO,每个引脚可以复用的功能具体参考下手册,”&pcfg_pull_none”指定上下拉,这里的没有设置不用上下拉。
  之后我们再去编写leds下的设备节点:

my_led: led {
    compatible = "topeet,led";
    gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&rk_led_gpio>;
    };   

  此时我们就完成了GPIO的复用功能设置。

2.2 pinctrl相关结构体

  内核将pinctrl驱动抽象为pinctrl_desc对象,具体到soc厂商的pinctrl驱动便是该对象一个实例, 在驱动所有的pin信息以及对于pin的控制接口实例化成pinctrl_desc,并将pinctrl_desc注册到内核中,如下

struct pinctrl_desc {
   const char *name;
   const struct pinctrl_pin_desc *pins;   //描述一个pin控制器的引脚,
   unsigned int npins;                    //描述该控制器有多少个引脚
   const struct pinctrl_ops *pctlops;     //引脚操作函数,有描述引脚,获取引脚等,全局控制函数
   const struct pinmux_ops *pmxops;       //引脚复用相关的操作函数
   const struct pinconf_ops *confops;     //引脚配置相关
   struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
   unsigned int num_custom_params;
   const struct pinconf_generic_params *custom_params;
   const struct pin_config_item *custom_conf_items;
#endif
};

  一般控制器驱动匹配设备,调用probe,最后会调用pinctrl_register函数,向内核注册pinctrl,产生pinctrl_dev,该函数如下:

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
                                 struct device *dev, void *driver_data);

  描述一个引脚的结构体 struct pinctrl_pin_desc:

pinctrl_pin_desc
struct pinctrl_pin_desc {
   unsigned number;
   const char *name;
   void *drv_data;
};

  很多pin组合在一起,实现特定功能,使用struct group_desc:

group_desc
struct group_desc {
   const char *name;
   int *pins;
   int num_pins;
   void *data;
};

三、GPIO子系统

  在Linux系统中所有的设备树已经设置好了,我们只需要将GPIO口和控制器对接即可。GPIO子系统结构简单描述如下图:
在这里插入图片描述

1. 相关API

函数名描述
of_get_named_gpio( )获取GPIO编号
gpio_request( )申请GPIO
gpio_direction_input( )将GPIO配置为输入
gpio_direction_output( )将GPIO配置为输出
gpio_set_value( )GPIO电平输出
gpio_get_value( )获取电平状态
gpio_free( )释放GPIO资源

1.1 of_get_named_gpio( )函数

//获取gpio编号
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)
  • 参数:
    - np:结构体指针
    - propname:属性名
    - index:编号
  • 返回值:
    - 成功:返回gpio编号
    - 失败:返回错误码

1.2 gpio_request( )函数

//申请gpio
static inline int gpio_request(unsigned gpio, const char *label)
  • 参数:
    - gpio:GPIO编号
    - label: 标签 NULL
  • 返回值:
    - 成功: 返回0
    - 失败: 返回错误码

1.3 gpio_direction_input( )函数

//设置gpio为输入
static inline int gpio_direction_input(unsigned gpio)
  • 参数:
    - gpio:GPIO编号
  • 返回值:
    - 成功:返回0
    - 失败:返回错误码

1.4 gpio_direction_output( )函数

//设置gpio为输出
static inline int gpio_direction_output(unsigned gpio, int value)
  • 参数:
    - gpio:GPIO编号
    - value:输出电平(1高电平;0是低电平)
  • 返回值:
    - 成功:返回0
    - 失败:返回错误码

1.5 gpio_set_value( )函数

//设置gpio输出电平
gpio_set_value()static inline void gpio_set_value(unsigned int gpio, int value)
  • 参数:
    - gpio:GPIO编号
    - value:输出电平
  • 返回值:
    - 成功:返回0
    - 失败:返回错误码

1.6 gpio_get_value( )函数

//获取gpio电平状态
static inline int gpio_get_value(unsigned int gpio)
  • 参数:
    - gpio:GPIO编号
  • 返回值:
    - 1: 是高电平
    - 0: 是低电平

1.7 gpio_free( )函数

//释放gpio
static inline void gpio_free(unsigned gpio)
  • 参数:
    - gpio GPIO编号
  • 返回值:无

2.相关结构体

2.1 gpio_device结构体

  gpio_device结构体在Linux内核中用于表示一个GPIO控制器,它管理一个或多个gpio_chip和该组下的所有引脚(pin)。多个gpio_device结构体在内核中通常通过链表来组织和管理

struct gpio_device {
   int                       id;           //gpio控制器的id,也就是第几个
   struct device             dev;
   struct cdev               chrdev;
   struct device             *mockdev;
   struct module             *owner;
   struct gpio_chip  *chip;
   struct gpio_desc  *descs;
   int                       base;         gpio在内核中的编号,申请gpio口时就是根据这个编号来查找
   u16                       ngpio;        //该gpio控制器有多少个引脚
   const char                *label;    //标签
   void                      *data;
   struct list_head        list;

#ifdef CONFIG_PINCTRL
   /*
   * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
   * describe the actual pin range which they serve in an SoC. This
   * information would be used by pinctrl subsystem to configure
   * corresponding pins for gpio usage.
   */
   struct list_head pin_ranges;
#endif
};

id:表示这是系统中第几个GPIO控制器。
dev:通用设备结构体,包含了该设备的基本信息和操作函数。
chip:指向关联的gpio_chip结构体的指针,gpio_chip描述了该组GPIO引脚的具体操作方式。
descs:指向一个gpio_desc结构体的数组,用于描述该组GPIO控制器下的所有引脚。每个引脚都有一个对应的gpio_desc。
base:表示该GPIO控制器中引脚的起始编号,加上偏移量可以得到每个引脚的编号。
ngpio:表示该GPIO控制器管理的引脚数量。
label:用于标识该GPIO控制器的标签或名称。
data:私有数据指针,可以用于存储与该GPIO控制器相关的任何额外信息。
list:链表节点,用于将多个gpio_device结构体链接在一起。

2.2 gpio_chip结构体

  gpio_chip结构体是Linux GPIO子系统中描述GPIO控制器功能和操作的核心数据结构,它包含了控制GPIO引脚所需的所有信息和接口,包括相关操作函数和中断相关。

struct gpio_chip {
   const char                *label;       //GPIO端口的名字,标签
   struct device             *dev;
   struct module             *owner;
   
	//gpio在内核中的编号,申请gpio口时就是根据这个编号来查找
   int                       base;      
       
   u16                       ngpio;         //该控制器的GPIO数目
   const char                *const *names;
   unsigned         		 can_sleep;
   
   /*操作gpio口的方法*/
   int (*request)(struct gpio_chip *chip, unsigned offset);
   void (*free)(struct gpio_chip *chip, unsigned offset);
   int (*direction_input)(struct gpio_chip *chip, unsigned offset);
   int (*get)(struct gpio_chip *chip, unsigned offset);
   int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
   int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce);
   void (*set)(struct gpio_chip *chip, unsigned offset, int value);
   int (*to_irq)(struct gpio_chip *chip, unsigned offset);
   /*......*/
};

label:一个字符串,用于标识GPIO控制器的名称或标签。
base:表示该GPIO控制器管理的第一个GPIO引脚的编号。
ngpio:表示该GPIO控制器管理的GPIO引脚数量。
descs:指向gpio_desc结构体的数组,详细描述了该GPIO控制器下的每一个引脚的状态和信息。

2.3 gpio_desc结构体

  GPIO Controller中每一个引脚用gpio_desc表示。

struct gpio_desc {
   struct gpio_device        *gdev;      //gpio_device里面描述了gpio信息,
   unsigned long             flags;
   /* flag symbols are bit numbers */
   #define FLAG_REQUESTED    0
   #define FLAG_IS_OUT       1
   #define FLAG_EXPORT       2       /* protected by sysfs_lock */
   #define FLAG_SYSFS        3       /* exported via /sys/class/gpio/control */
   #define FLAG_ACTIVE_LOW   6       /* value has active low */
   #define FLAG_OPEN_DRAIN   7       /* Gpio is open drain type */
   #define FLAG_OPEN_SOURCE 8        /* Gpio is open source type */
   #define FLAG_USED_AS_IRQ 9        /* GPIO is connected to an IRQ */
   #define FLAG_IS_HOGGED    11      /* GPIO is hogged */
   #define FLAG_TRANSITORY 12        /* GPIO may lose value in sleep or reset */

   /* Connection label */
   const char                *label;
   /* Name of the GPIO */
   const char                *name;
};

gdev:指向gpio_device结构体的指针。
flags:标志位字段,用于指示当前GPIO引脚的状态。例如,当使用gpio_request函数申请GPIO资源时,这个字段会被置位;当使用gpio_free函数释放GPIO资源时,这个字段会被清零。
name:GPIO引脚的名称,通常用于调试和日志输出。
label:GPIO引脚的标签,用于在用户空间或其他内核模块中标识这个引脚。

3. GPIO设备树分析

  这里以GPIO3为例:

gpio3: gpio@fe760000 {//节点名
    compatible = "rockchip,gpio-bank"; //厂商名  设备名
    reg = <0x0 0xfe760000 0x0 0x100>; //地址  0x0 0xfe760000控制器地址   0x0 0x100地址范围
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;//中断
    clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;//时钟
 
    gpio-controller;//标识
    #gpio-cells = <2>;//描述子节点个数
    gpio-ranges = <&pinctrl 0 96 32>; //引脚范围 &pinctrl 引用pinctrl节点 0    96 GPIO起始序号  32引脚范围
    interrupt-controller;
    #interrupt-cells = <2>;//描述子节点个数
};

  在这个实例中,大部分的内容我们已经提前讲到过了,这里主要介绍一下gpio-controller、gpio-cells和gpio-ranges这两个部分:

3.1 gpio-controller属性

  gpio-controller用于标识一个设备节点作为GPIO控制器(GPIO控制器是负责管理和控制GPIO引脚的硬件模块或驱动程序),其通常作为设备节点的一个属性出现,位于设备节点的属性列表中。当一个设备节点被标识为GPIO控制器时,它通常会定义一组GPIO引脚,并提供相关的GPIO控制和配置功能。其他设备节点可以使用该GPIO控制器来控制和管理其GPIO引脚。
  通过使用gpio-controller属性,设备树可以明确标识出GPIO控制器设备节点,使系统可以正确识别和管理GPIO引脚的配置和控制。

3.2 gpio-cells属性

  gpio-cells用于指定GPIO引脚描述符的编码方式(GPIO引脚描述符是用于标识和配置GPIO引脚的一组值,例如引脚编号、引脚属性等),该属性的属性值是一个整数,表示用于编码GPIO引脚描述符的单元数。通常,这个值为2。
gpio-ranges属性是设备树中一个用于描述GPIO范围映射的属性。它通常用于描述具有大量GPIO引脚的GPIO控制器,以简化GPIO引脚的编码和访问。

3.3 gpio-ranges属性

  在设备树中,GPIO控制器的每个引脚都有一个本地编号,用于在控制器内部进行引脚寻址。然而,这些本地编号并不一定与外部引脚的物理编号或其他系统中使用的编号一致。为了解决这个问题,可以使用gpio-ranges属性将本地编号映射到实际的引脚编号。
  gpio-ranges属性是一个包含一系列整数值的列表,每个整数值对应于设备树中的一个GPIO控制器。列表中的每个整数值按照特定的顺序提供以下信息:
(1)外部引脚编号的起始值。
(2)GPIO控制器内部本地编号的起始值。
(3)引脚范围的大小(引脚数量)。

  例如将gpio-ranges属性的值设置为<&pinctrl 0 0 32>,其中<&pinctrl>表示引用了名为pinctrl的引脚控制器节点,0 0 32表示外部引脚从0开始,控制器本地编号从0开始,共映射了32个引脚。这样,gpio-ranges属性将GPIO控制器的本地编号直接映射到外部引脚编号,使得GPIO引脚的编码和访问更加简洁和直观。

免责声明:本文参考了野火和讯为的部分资料,仅供学习参考使用,若有侵权或勘误请联系笔者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1798234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何将照片从Android传输到笔记本电脑?

目前全球大部分照片都是由手机拍摄的。唯一的问题是这些照片会占用您的内部存储或 SD 卡上的大量空间。如果您的Android设备存储空间不足&#xff0c;您可能会被迫将照片从Android手机传输到笔记本电脑。您访问此网站只是因为您想了解如何将图片从Android传输到笔记本电脑。 如…

Ubuntu24.04开发环境配置

目录 0. 前言1. 宇宙最强编辑器&#xff08;暂定&#xff09;vscode的安装与配置1.1 下载安装1.2 用户配置和常用插件 2. C/C环境配置3. git配置4. MySQL配置5. Java环境配置 0. 前言 本篇博客主要介绍Ubuntu24.04中的开发环境等配置。 1. 宇宙最强编辑器&#xff08;暂定&am…

在大模型AI的下一个战场,为中小创新企业重构竞争格局

麦肯锡预计到2030年前&#xff0c;AI有望为全球经济贡献25.6万亿美元的价值&#xff0c;其中生成式AI的贡献高达7.6万亿美元。自2023年“百模大战”以来&#xff0c;基础大模型的“战事”已经进入尾声&#xff0c;大模型正在“卷向”产业和行业&#xff0c;越来越多的创新企业正…

【每日刷题】Day58

【每日刷题】Day58 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c;​​​​​​​ 1. 3038. 相同分数的最大操作数目 I - 力扣&#xff08;LeetCode&#xff09; 2. 868. …

24年北京网安大会是AI驱动安全?还是驱动安全股票全员下跌?

AI驱动安全&#xff0c;网安一哥疯狂转发 吉祥学安全知识星球&#x1f517;除了包含技术干货&#xff1a;Java代码审计、web安全、应急响应等&#xff0c;还包含了安全中常见的售前护网案例、售前方案、ppt等&#xff0c;同时也有面向学生的网络安全面试、护网面试等。 2024年6…

Vue --关于传递参数

多参数传递的两种方法&#xff1a; 第一种&#xff1a;params方法&#xff08;此方法传递不会在URL路径中显示拼接&#xff09; 传递参数&#xff1a; this.$router.push({name: "home",params:{key:1} })接收参数&#xff1a; created() {// 获取参数console.log…

whistle手机抓包

环境&#xff1a;whistle&#xff1a;2.9.59 whistle手机抓包&#xff08;ios可以抓小程序的包&#xff1b;安卓机不能抓小程序的包&#xff0c;但是小程序的有开发者工具就够用了&#xff09; 以安卓手机为例&#xff08;手机跟电脑要连同一个wifi&#xff09; 1.电脑安装w…

香港优才计划线上申请10大步骤,2024年流程截图,diy照做就可以

我是糖爸&#xff0c;已获批香港优才。10个步骤申请香港优才真的很简单&#xff0c;因为现在入境处只接受线上申请啦&#xff0c;你自己上传资料就可以&#xff0c;找中介也是你自己准备资料给他帮忙上传&#xff0c;何不自己动手上传呢&#xff0c;省个几万。 10大步骤分别是&…

2024年【起重机司机(限桥式起重机)】考试试卷及起重机司机(限桥式起重机)证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【起重机司机(限桥式起重机)】考试试卷及起重机司机(限桥式起重机)证考试&#xff0c;包含起重机司机(限桥式起重机)考试试卷答案和解析及起重机司机(限桥式起重机)证考试练习。安全生产模拟考试一点通结合国家…

fastadmin按钮级别权限控制实现

1.菜单规则得存在。 2.html代码增加对应控制 3.js代码增加对应路由标志 <div class"panel panel-default panel-intro"><div class"panel-heading">{:build_heading(null,FALSE)}<ul class"nav nav-tabs" data-field"sta…

numpy入门笔记

学习参考&#xff1a; 菜鸟教程 numpy入门博客 numpy入门视频 NumPy安装 默认情况使用国外线路&#xff0c;国外太慢&#xff0c;我们使用清华的镜像 pip3 install numpy scipy matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple一、创建数组 numpy.array(object, dt…

【K8s】专题四(7):Kubernetes 控制器之 CronJob

以下内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01;如果对您有帮助&#xff0c;烦请点赞、关注、转发&#xff01;欢迎扫码关注个人公众号&#xff01; 目录 一、基本介绍 二、工作原理 三、相关特性 四、资源清单&#xff08;示例&#xff09; 五…

一图文看懂oracle数据库的安装与卸载

oracle数据库安装与卸载 1、卸载 对于已经安装过oracle数据库的主机&#xff0c;一般卸载起来比较麻烦&#xff0c;需要卸载大致四个地方&#xff0c;分别是关闭应用的服务、删除相关注册表、删除路径以及删除安装的位置&#xff0c;最后就需要重启主机。 前提&#xff0c;在…

从零开始:如何在直播应用中集成美颜SDK和美颜插件

本篇文章&#xff0c;小编将详细介绍如何从零开始&#xff0c;在直播应用中集成美颜SDK和美颜插件。 一、准备工作 确定需求 在开始集成美颜SDK之前&#xff0c;首先需要明确需求。考虑以下几个问题&#xff1a; 直播应用的目标用户是谁&#xff1f; 需要集成哪些美颜功能&…

图的相关种类

目录 数据类型 存储结构 邻接矩阵表示法 无向图 邻接矩阵表示 有向图 网 实现 邻接矩阵表示 存储结构 创建无向图 优点 缺点 邻接表法表示 表示无向图 表示有向图 存储结构 无向网 特点 十字链表与多重表 十字链表 存储结构 多重表 存储结构 数据类型 存…

IDEA使用阿里通义灵码插件

在这个AI火热的时代&#xff0c;纯手工写代码已经有点out了&#xff0c;使用AI插件可以帮我们快速写代码&#xff0c;起码能省去写那些简单、重复性的代码&#xff0c;大大提高编码效率&#xff0c;在这里我推荐使用阿里的通义灵码 注册安装 安装注册好后&#xff0c;打开我们…

室内外融合定位是如何做到成为定位领域的新宠

在信息化高速发展的今天&#xff0c;定位技术已成为人们生活和工作中不可或缺的一部分。随着物联网、智慧城市等领域的蓬勃发展&#xff0c;传统的单一定位方式已无法满足复杂多变的环境需求。在这样的背景下&#xff0c;室内外融合定位技术应运而生&#xff0c;以其独特的优势…

海南聚广众达电子商务咨询有限公司正规吗?

在数字经济的浪潮下&#xff0c;海南聚广众达电子商务咨询有限公司凭借其对抖音电商领域的深刻洞察和专业服务&#xff0c;成为引领行业新风尚的佼佼者。公司不仅具备丰富的电商运营经验&#xff0c;更有一支高效、创新的团队&#xff0c;致力于为品牌商家提供全方位的电商解决…

《数学学习与研究》投稿难度大吗?

《数学学习与研究》杂志的投稿难度相对适中。 一方面&#xff0c;它作为一本有一定影响力的数学专业期刊&#xff0c;对稿件的质量有一定要求。论文需要具备一定的创新性、科学性和逻辑性&#xff0c;研究内容要具有一定的价值和深度。 另一方面&#xff0c;与一些核心期刊相…

小红书笔记怎么发浏览量高?建议收藏

如何让自己的小红书笔记脱颖而出&#xff0c;赢得高浏览量&#xff1f;本文伯乐网络传媒将从内容编辑、提高曝光度方面&#xff0c;为你揭秘小红书笔记创作的技巧。 一、文字内容编辑技巧 1.优化笔记标题和封面 &#xff08;1&#xff09;标题关键词设置 一个好的标题决定了…