文章目录
#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定义如下:
-
获取/释放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);
-
基于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);
-
配置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);
-
获取、设置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时,基本规范如下:
- 使用devm_gpiod_get或者gpiod_get过去gpio_desc描述符;
- 使用gpiod_direction_input或者gpiod_direction_output配置gpio的输入/输出模式;
- 使用gpiod_get_value或者gpiod_get_value系列函数获取、设置gpio I/O值;
- 使用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进行驱动程序的开发。