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

文章目录

  • 前言
  • 一、设备树的进阶知识
    • 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_PINCONFunsigned 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.mzph.cn/bicheng/23593.shtml

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

相关文章

Day30 登录界面设计

​ 本章节,实现了登录界面窗口设计 一.准备登录界面图片素材(透明背景图片) 把准备好的图片放在 Images 文件夹下面,格式分别是 .png和 .icoico 图片,右键属性,生成操作选 内容 png 图片,右键属性,生成操作选 资源 选中 login.png图片鼠标右键,选择属性。生成的操作选…

多目标应用:MOHHO多目标哈里斯鹰优化算法求解无人机三维路径规划(MATLAB代码)

详细介绍 多目标应用&#xff1a;MOHHO多目标哈里斯鹰优化算法求解无人机三维路径规划&#xff08;MATLAB代码&#xff09;-CSDN博客 一次运行结果 完整MATLAB代码

CentOS6系统因目录有隐含i权限属性致下属文件无法删除的故障一例

CentOS6服务器在升级openssh时因系统目录权限异常&#xff08;有隐含i权限属性&#xff09;&#xff0c;下属文件无法删除&#xff0c;导致系统问题的故障一例。 一、问题现象 CentOS6在升级openssh时&#xff0c;提示如下问题&#xff1a; warning: /etc/ssh/sshd_config c…

springboot vue 开源 会员收银系统 (6) 收银台的搭建

前言 完整版演示 前面我们对会员系统 分类和商品的开发 完成了收银所需的基础信息 下面我们开始完成收银台的开发 简单画了一个收银的流程图大家参考下 从这张图我们可以分析一下几点 可以选择会员或散客收银选择会员使用相应的会员价结算使用会员卡则在价格基础根据卡折扣…

function和bind使用实践

文章目录 1.functional 接受全局函数2.functional 接受lambda表达式3.functional 接收静态成员函数4.functional 接收成员函数5.bind 绑定全局函数6.bind 绑定成员函数7.使用 placeholders占位 1.functional 接受全局函数 2.functional 接受lambda表达式 3.functional 接收静…

node.js漏洞——

一.什么是node.js 简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。 Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境&#xff0c;基于 Google 的 V8 引擎&#xff0c;V8 引擎执行 Javascript 的速度非常…

神经网络搭建(1)----nn.Sequential

神经网络模型构建 采用CIFAR10中的数据&#xff0c;并对其进行简单的分类。以下图为例 输入&#xff1a;3通道&#xff0c;3232 ( 经过一个55的卷积) → 变成32通道&#xff0c;3232的图像 (经过22的最大池化) → 变成32通道&#xff0c;1616的图像 ( 经过一个55的卷积) → 变…

Sass混合宏(Mixins)使用

Sass是一个强大的CSS预处理器&#xff0c;它允许你使用变量、嵌套规则、函数等特性&#xff0c;使得CSS开发更加高效和规范。在这篇教程中&#xff0c;我将为你详细介绍一个非常有用的Sass功能——混合宏(mixins)。 1. 基础&#xff1a;混合宏的定义和使用 混合宏是一个可以复…

linux虚拟机免密登录配置

1、假设A服务器要免密登录B服务器 2、在A服务器上执行命令&#xff1a; cd /root/.ssh/ ssh-keygen -t rsa #这里会生成两个文件 一个是id_rsa私钥和公钥rsa.pub2、我们把公钥的内容复制粘贴到B服务器的/root/.ssh/authorized_keys文件下 #在A服务器上执行命令记录内容 cat …

ArkTs-TaskPool和Worker的使用

TaskPool和Worker的区别 实现TaskPoolWorker内存模型线程间隔离&#xff0c;内存不共享。线程间隔离&#xff0c;内存不共享。参数传递机制 采用标准的结构化克隆算法&#xff08;Structured Clone&#xff09;进行序列化、反序列化&#xff0c;完成参数传递。 支持ArrayBuffe…

python调用excel的demo

在本地安装Pycharm之后&#xff0c;新建工程&#xff0c;在main.py中键入如下代码,即可实现Python调用excel&#xff1a; import pandas as pd sheet pd.read_excel(test.xlsx) data sheet.loc[0].values print("读取指定行的数据:\n{0}".format(data)) 第一次编…

IT学习笔记--Flink

概况&#xff1a; Flink 是 Apache 基金会旗下的一个开源大数据处理框架。目前&#xff0c;Flink 已经成为各大公司大数据实时处理的发力重点&#xff0c;特别是国内以阿里为代表的一众互联网大厂都在全力投入&#xff0c;为 Flink 社区贡献了大量源码。 Apache Flink 是一个…

Vscode发生鼠标悬停正在加载、无法跳转和提示词的问题

Vscode发生鼠标悬停正在加载、无法跳转和提示词的问题 查看python语言服务器的日志&#xff0c;确定问题。 我的问题是加载的vscode 目录下存在一个很大的数据集目录&#xff0c;导致无法正常工作。 解决办法&#xff1a; 在vscode的pylance设置中&#xff0c;排除对应的目…

深入理解 Spring 容器:原始 Bean 的生成过程

引言&#xff1a; Spring 框架的核心功能之一是它的 IoC&#xff08;控制反转&#xff09;容器&#xff0c;它负责创建、配置和组装 Bean。在 Spring 应用程序中&#xff0c;Bean 是对象实例&#xff0c;由 Spring 容器负责其生命周期和依赖关系。本文将深入探讨 Spring 容器中…

使用 PlayCanvas 创建带有后处理效果的 3D 场景

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 PlayCanvas 创建带有后处理效果的 3D 场景 应用场景介绍 本代码演示了如何使用 PlayCanvas 创建一个带有后处理效果的 3D 场景。用户可以在场景中查看一个棋盘模型&#xff0c;并使用后处理效果为场景添…

达梦 执行查询语句时报[-544]:Out of sort buf space

达梦数据库有时执行SQL中有时报[-544]:Out of sort buf space, try to adjust SORT_BUF_GLOBAL_SIZE, SORT_BUF_SIZE, SORT_BLK_SIZE. 第一反应是这条语句占用排序区太大。但真实原因是前面执行的语句耗光了全局排序区&#xff0c;后面SQL任何小的排序操作都会报这个错误从而执…

freeRTOS中使用cJSON死机的问题

问题描述 在freeRTOS中使用cJSON来处理PC通过串口发送的信息&#xff0c;但是在串口接收处理任务中调用cJSON处理的函数后会出现死机的问题 // 处理PC的信息 void ProcessPCData(uint8_t* data, uint32_t len) {int functionCode 0;cJSON* json NULL;printf("Start Pr…

天润融通助力浪鲸卫浴,智能化革新引领客户服务新高度

头部家装品牌如何用优质服务抓住客户&#xff1f; 每年初春&#xff0c;万物复苏的同时&#xff0c;家装市场也正式进入旺季。 因为春天气温回升&#xff0c;潮气逐渐散去&#xff0c;开始进入最适合施工的季节&#xff0c;木材不易变形、油漆不易起皮&#xff0c;再加上春季…

算法金 | 再见!!!KNN

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 KNN算法的工作原理简单直观&#xff0c;易于理解和实现&#xff0c;这使得它在各种应用场景中备受青睐。 我们将深入探讨KNN算法&…

Easy 同学:AI 时代将加速计算机专业和程序员职业的分化

一、原贴 2024 年 6 月 5 日 拥有 60多万粉丝的方糖气球&#xff08;ftqq.com&#xff09;博主 、独立开发者&#xff1a;Easy 发表了一篇 AI 对计算机专业和程序员行业影响的新浪博客&#xff0c;看后很有启发&#xff0c;故而将原文摘录于此&#xff1a; 单独开个贴说一下吧…