GPIO驱动实践-基于4.18.7内核

本文介绍Linux Kernel 4.18.7中GPIO子系统的更新,包括新的API介绍、编程模式和示例。新API采用gpio_desc代替整数句柄,并提供了一系列函数用于配置和操作GPIO。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录


#1. GPIO子系统的变化
最近在研究最新的Linux kernel 4.18.7时,发现其关于GPIO子系统的发生了比较大的变化。而且在linux/gpio.h中做了关于声明:

 * This is the LEGACY GPIO bulk include file, including legacy APIs. It is
 * used for GPIO drivers still referencing the global GPIO numberspace,
 * and should not be included in new code.
 *
 * If you're implementing a GPIO driver, only include <linux/gpio/driver.h>
 * If you're implementing a GPIO consumer, only include <linux/gpio/consumer.h>
 * 

上述说明的基本含义是:linux/gpio.h已经作为历史API出现,并且建议最新的使用GPIO的驱动程序不要再使用该linux/gpio.h中的API。同时,对于GPIO控制器驱动器程序,应该引用linux/gpio/driver.h文件;对于使用GPIO的一般驱动程序,应该引用linux/gpio/consumer.h文件。

新版GPIO相关的API中,每个API第一个参数都改成了gpio_desc,下面是该数据结构的定义:

struct gpio_desc {
	struct gpio_device	*gdev;
	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;
};

gpio_desc是GPIO的不透明描述符。 gpio_desc是使用gpiod_get()获得的,并且优于旧的基于整数的句柄。与基于gpio num的整数相反,指向gpio_desc的指针保证在GPIO释放之前始终有效。

#2. 编程模式
新版的GPIO APIs同样工作于GPIOLIB框架之下,Linux内核需要启用CONFIG_GPIOLIB配置选项;

##2.1. GPIO APIs

如果使用gpio编写一般的驱动程序,需要包含linux/gpio/consumer.h,其中比较重要的API定义如下:

  1. 获取/释放gpio_desc的接口:

     struct gpio_desc *__must_check gpiod_get(struct device *dev,
     				 const char *con_id,
     				 enum gpiod_flags flags);
    
     struct gpio_desc *__must_check gpiod_get_optional(struct device *dev,
     					  const char *con_id,
     					  enum gpiod_flags flags);
    
     struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
     					const char *con_id,
     					enum gpiod_flags flags);
     struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev,
     						const char *con_id,
     						enum gpiod_flags flags);
    
     void gpiod_put(struct gpio_desc *desc);
     void gpiod_put_array(struct gpio_descs *descs);
    
  2. 基于devm机制的获取/释放gpio_desc的接口:

     struct gpio_desc *__must_check devm_gpiod_get(struct device *dev,
     				  const char *con_id,
     				  enum gpiod_flags flags);
     struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev,
     						const char *con_id,
     						unsigned int idx,
     						enum gpiod_flags flags);
     struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev,
     						   const char *con_id,
     						   enum gpiod_flags flags);
     struct gpio_desc *__must_check
     devm_gpiod_get_index_optional(struct device *dev, const char *con_id,
     			  unsigned int index, enum gpiod_flags flags);
     struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
     						 const char *con_id,
     						 enum gpiod_flags flags);
     struct gpio_descs *__must_check
     devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
     			  enum gpiod_flags flags);
     void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
     void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
    
  3. 配置gpio输入/输出模式的接口:

     int gpiod_get_direction(struct gpio_desc *desc);
     int gpiod_direction_input(struct gpio_desc *desc);
     int gpiod_direction_output(struct gpio_desc *desc, int value);
     int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
    
  4. 获取、设置GPIO I/O值的接口( non-sleeping context):

     int gpiod_get_value(const struct gpio_desc *desc);
     int gpiod_get_array_value(unsigned int array_size,
     			  struct gpio_desc **desc_array, int *value_array);
     void gpiod_set_value(struct gpio_desc *desc, int value);
     void gpiod_set_array_value(unsigned int array_size,
     			   struct gpio_desc **desc_array, int *value_array);
     int gpiod_get_raw_value(const struct gpio_desc *desc);
     int gpiod_get_raw_array_value(unsigned int array_size,
     			  struct gpio_desc **desc_array,
     			  int *value_array);
     void gpiod_set_raw_value(struct gpio_desc *desc, int value);
     int gpiod_set_raw_array_value(unsigned int array_size,
     		   struct gpio_desc **desc_array,
     		   int *value_array);
    

5.获取、设置GPIO I/O值的接口( sleeping context):

	int gpiod_get_value_cansleep(const struct gpio_desc *desc);
	int gpiod_get_array_value_cansleep(unsigned int array_size,
					   struct gpio_desc **desc_array,
					   int *value_array);
	void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
	void gpiod_set_array_value_cansleep(unsigned int array_size,
					    struct gpio_desc **desc_array,
					    int *value_array);
	int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
	int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
					       struct gpio_desc **desc_array,
					       int *value_array);
	void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
	int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
						struct gpio_desc **desc_array,
						int *value_array);

注:
gpiod_xxx_cansleep系列接口与gpiod_xxx之间的区别:

6.新旧gpio描述符之间的转换接口:

	struct gpio_desc *gpio_to_desc(unsigned gpio);
	int desc_to_gpio(const struct gpio_desc *desc);

##2.2 编程规范
使用新GPIO APIs驱动一个普通的GPIO时,基本规范如下:

  1. 使用devm_gpiod_get或者gpiod_get过去gpio_desc描述符;
  2. 使用gpiod_direction_input或者gpiod_direction_output配置gpio的输入/输出模式;
  3. 使用gpiod_get_value或者gpiod_get_value系列函数获取、设置gpio I/O值;
  4. 使用devm_gpiod_put或者gpiod_put释放gpio资源;

#3. 驱动示例
下面列举一个基于最新的gpio驱动编程接口的例子,我们选取driver/input/gpio_mouse.c最为示例。下面为典型的gpio_mouse设备的DTS配置信息:

gpio-mouse { compatible = "gpio-mouse";
    	scan-interval-ms = <50>;
    	up-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
    	down-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
    	left-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
    	right-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
    	button-left-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
    	button-middle-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
    	button-right-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; 
}; 

gpio_mouse.c的gpio_mouse_probe对于各个gpio进行了资源申请:

......

gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN);------------------------------>(1)
if (IS_ERR(gmouse->up))
	return PTR_ERR(gmouse->up);
gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN);
if (IS_ERR(gmouse->down))
	return PTR_ERR(gmouse->down);
gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN);
if (IS_ERR(gmouse->left))
	return PTR_ERR(gmouse->left);
gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN);
if (IS_ERR(gmouse->right))
	return PTR_ERR(gmouse->right);
......

其中,(1)中"up"参数对应于DTS中的"up-gpios",注意,如果devm_gpiod_get的con_id参数在DTS中找不到对应的配置,那么devm_gpiod_get将返回NULL。

x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left);
y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up);

上述代码通过gpiod_get_value获取各个gpio的I/O值。

#4. 总结
虽然,内核对于GPIO相关的APIs做了较大的改动,但是,基本的编程方式并未发生改变,熟悉旧的GPIO APIs的用户可以很容易的使用新版的GPIO APIs进行驱动程序的开发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值