一、gpiod子系统是新版的linux内核引入的控制gpio的子系统,这个子系统的功能更为强大,在很多地方都会碰到,在工作过程中尽量使用gpiod子系统,很有必要学习一下。
二、Linux 内核GPIOD介绍文档 kernel\Documentation\gpio\consumer.txt,kernel/include/linux/gpio/consumer.h 包含下面的函数
#ifndef __LINUX_GPIO_CONSUMER_H
#define __LINUX_GPIO_CONSUMER_H
#include
#include
#include
struct device;
/**
* Opaque descriptor for a GPIO. These are obtained using gpiod_get() and are
* preferable to the old integer-based handles.
*
* Contrary to integers, a pointer to a gpio_desc is guaranteed to be valid
* until the GPIO is released.
*/
struct gpio_desc;
/**
* Struct containing an array of descriptors that can be obtained using
* gpiod_get_array().
*/
struct gpio_descs {
unsigned int ndescs;
struct gpio_desc *desc[];
};
#define GPIOD_FLAGS_BIT_DIR_SET BIT(0)
#define GPIOD_FLAGS_BIT_DIR_OUT BIT(1)
#define GPIOD_FLAGS_BIT_DIR_VAL BIT(2)
/**
* Optional flags that can be passed to one of gpiod_* to configure direction
* and output value. These values cannot be OR'd.
*/
enum gpiod_flags {
GPIOD_ASIS = 0,
GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET,
GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,
GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT |
GPIOD_FLAGS_BIT_DIR_VAL,
};
#ifdef CONFIG_GPIOLIB
/* Return the number of GPIOs associated with a device / function */
int gpiod_count(struct device *dev, const char *con_id);
/* Acquire and dispose GPIOs */
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_index(struct device *dev,
const char *con_id,
unsigned int idx,
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_desc *__must_check gpiod_get_index_optional(struct device *dev,
const char *con_id,
unsigned int index,
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);
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);
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);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
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);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
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);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_is_active_low(const struct gpio_desc *desc);
int gpiod_cansleep(const struct gpio_desc *desc);
int gpiod_to_irq(const struct gpio_desc *desc);
/* Convert between the old gpio_ and new gpiod_ interfaces */
struct gpio_desc *gpio_to_desc(unsigned gpio);
int desc_to_gpio(const struct gpio_desc *desc);
/* Child properties interface */
struct fwnode_handle;
struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
const char *propname);
struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
const char *con_id,
struct fwnode_handle *child);
#else /* CONFIG_GPIOLIB */
static inline int gpiod_count(struct device *dev, const char *con_id)
{
return 0;
}
static inline struct gpio_desc *__must_check gpiod_get(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_desc *__must_check
gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_desc *__must_check
gpiod_get_optional(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_desc *__must_check
gpiod_get_index_optional(struct device *dev, const char *con_id,
unsigned int index, enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_descs *__must_check
gpiod_get_array(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_descs *__must_check
gpiod_get_array_optional(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline void gpiod_put(struct gpio_desc *desc)
{
might_sleep();
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline void gpiod_put_array(struct gpio_descs *descs)
{
might_sleep();
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline struct gpio_desc *__must_check
devm_gpiod_get(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline
struct gpio_desc *__must_check
devm_gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_desc *__must_check
devm_gpiod_get_optional(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_desc *__must_check
devm_gpiod_get_index_optional(struct device *dev, const char *con_id,
unsigned int index, enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_descs *__must_check
devm_gpiod_get_array(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_descs *__must_check
devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return ERR_PTR(-ENOSYS);
}
static inline void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
{
might_sleep();
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline void devm_gpiod_put_array(struct device *dev,
struct gpio_descs *descs)
{
might_sleep();
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline int gpiod_get_direction(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return -ENOSYS;
}
static inline int gpiod_direction_input(struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return -ENOSYS;
}
static inline int gpiod_direction_output(struct gpio_desc *desc, int value)
{
/* GPIO can never have been requested */
WARN_ON(1);
return -ENOSYS;
}
static inline int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
/* GPIO can never have been requested */
WARN_ON(1);
return -ENOSYS;
}
static inline int gpiod_get_value(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return 0;
}
static inline void gpiod_set_value(struct gpio_desc *desc, int value)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline int gpiod_get_raw_value(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return 0;
}
static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return 0;
}
static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return 0;
}
static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc,
int value)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
{
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
{
/* GPIO can never have been requested */
WARN_ON(1);
return -ENOSYS;
}
static inline int gpiod_is_active_low(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return 0;
}
static inline int gpiod_cansleep(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return 0;
}
static inline int gpiod_to_irq(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return -EINVAL;
}
static inline struct gpio_desc *gpio_to_desc(unsigned gpio)
{
return ERR_PTR(-EINVAL);
}
static inline int desc_to_gpio(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
WARN_ON(1);
return -EINVAL;
}
/* Child properties interface */
struct fwnode_handle;
static inline struct gpio_desc *fwnode_get_named_gpiod(
struct fwnode_handle *fwnode, const char *propname)
{
return ERR_PTR(-ENOSYS);
}
static inline struct gpio_desc *devm_get_gpiod_from_child(
struct device *dev, const char *con_id, struct fwnode_handle *child)
{
return ERR_PTR(-ENOSYS);
}
#endif /* CONFIG_GPIOLIB */
#if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
int gpiod_export_link(struct device *dev, const char *name,
struct gpio_desc *desc);
void gpiod_unexport(struct gpio_desc *desc);
#else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
static inline int gpiod_export(struct gpio_desc *desc,
bool direction_may_change)
{
return -ENOSYS;
}
static inline int gpiod_export_link(struct device *dev, const char *name,
struct gpio_desc *desc)
{
return -ENOSYS;
}
static inline void gpiod_unexport(struct gpio_desc *desc)
{
}
#endif /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
#endif
三、实例一:三个gpio都是做为输入。
hardware_version: hardware-version{
compatible = "xxx,hardwareversion";
hwversion-gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>,
<&gpio3 RK_PB3 GPIO_ACTIVE_HIGH>,
<&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>;
status = "okay";
};
#include
#include
#include
#include
struct gpio_desc *data[3];
unsigned char hw_version = 0;
static ssize_t hardware_version_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%03d\n", hw_version);
}
static ssize_t hardware_version_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
printk("[%s]buf=%s\r\n",__func__,buf);
return count;
}
/*
***********************************************************
cat /sys/devices/platform/hardware-version/hardware_version
************************************************************
*/
static DEVICE_ATTR(hardware_version, 0644, hardware_version_show, hardware_version_store);
int power(int num,int index)
{
if(index == 0)
{
return 1;
}
else
return num * power(num,index-1);
}
int hardwareversion_probe(struct platform_device *pdev){
int i,n,ret;
n = gpiod_count(&pdev->dev, "hwversion");
for(i = 0;i < n; i++)
{
data[i] = gpiod_get_index(&pdev->dev, "hwversion", i, GPIOD_IN);
if (IS_ERR(data[i]))
printk("Cannot find hwversion-gpios! \n");
else
hw_version += gpiod_get_value(data[i]) * power(10,i);
}
ret = device_create_file(&pdev->dev, &dev_attr_hardware_version);
if (ret)
printk("[%s] hardware_version device_create_file is error \n",__func__);
else
printk("[%s] count:%d,hw_version:%d\n",__func__,n,hw_version);
return 0;
}
int hardwareversion_remove(struct platform_device *pdev){
printk("[%s]\n",__func__);
return 0;
}
static const struct of_device_id hardwareversion_id[] = {
{.compatible = "xxx,hardwareversion", 0},
{}};
struct platform_driver hardwareversion_driver = {
.probe = hardwareversion_probe,
.remove = hardwareversion_remove,
.driver={
.owner = THIS_MODULE,
.name = "hardwareversion",
.of_match_table = hardwareversion_id,
},
};
static int hardwareversion_driver_init(void){
int ret = 0;
ret = platform_driver_register(&hardwareversion_driver);
if(ret < 0)
printk("[%s] error\n",__func__);
else
printk("[%s] ok\n",__func__);
return 0;
}
static void hardwareversion_driver_exit(void){
platform_driver_unregister(&hardwareversion_driver);
printk("[%s]\n",__func__);
}
module_init(hardwareversion_driver_init);
module_exit(hardwareversion_driver_exit);
MODULE_LICENSE("GPL");
四、实例二:有的gpio做in,有的gpio做out。
inoutput_gpio_ctr: inoutput-gpio-ctr{
compatible = "xxx,inoutputgpioctr";
pinctrl-names = "default";
pinctrl-0 =<&inoutput_gpio>;
optocouplerctroutput-gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_HIGH>;
optocouplerctrinput-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>;
gpioenable-gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>;
gpio3b5-gpios = <&gpio3 RK_PB5 GPIO_ACTIVE_HIGH>;
gpio3b7-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>;
status = "okay";
};
#include
#include
#include
#include
struct gpio_desc *optocouplerctroutput;
struct gpio_desc *optocouplerctrinput;
struct gpio_desc *gpioenable;
struct gpio_desc *gpio3b5,*gpio3b7;
static ssize_t optocouplerctroutput_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = gpiod_get_value(optocouplerctroutput);
return sprintf(buf, "%d\n",ret);
}
static ssize_t optocouplerctroutput_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
if(buf[0] =='1')
gpiod_direction_output(optocouplerctroutput, 1);
else
gpiod_direction_output(optocouplerctroutput, 0);
return count;
}
/*
***********************************************************
echo 1 > /sys/devices/platform/inoutput-gpio-ctr/optocouplerctroutput
echo 0 > /sys/devices/platform/inoutput-gpio-ctr/optocouplerctroutput
************************************************************
*/
static DEVICE_ATTR(optocouplerctroutput, 0644, optocouplerctroutput_show, optocouplerctroutput_store);
static ssize_t optocouplerctrinput_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = gpiod_get_value(optocouplerctrinput);
return sprintf(buf, "%d\n",ret);
}
static ssize_t optocouplerctrinput_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
return count;
}
/*
***********************************************************
cat /sys/devices/platform/inoutput-gpio-ctr/optocouplerctrinput
************************************************************
*/
static DEVICE_ATTR(optocouplerctrinput, 0644, optocouplerctrinput_show, optocouplerctrinput_store);
static ssize_t gpio3b5_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = gpiod_get_value(gpio3b5);
return sprintf(buf, "%d\n",ret);
}
static ssize_t gpio3b5_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
if(buf[0] =='1')
gpiod_direction_output(gpio3b5, 1);
else
gpiod_direction_output(gpio3b5, 0);
return count;
}
/*
***********************************************************
cat /sys/devices/platform/inoutput-gpio-ctr/gpio3b5
************************************************************
*/
static DEVICE_ATTR(gpio3b5, 0644, gpio3b5_show, gpio3b5_store);
int inoutputgpioctr_probe(struct platform_device *pdev){
int err;
gpioenable = devm_gpiod_get_optional(&pdev->dev, "gpioenable", GPIOD_OUT_HIGH);
optocouplerctroutput = devm_gpiod_get_optional(&pdev->dev, "optocouplerctroutput", GPIOD_OUT_LOW);
if (IS_ERR(optocouplerctroutput)) {
err = PTR_ERR(optocouplerctroutput);
dev_err(&pdev->dev, "failed to request optocouplerctroutput GPIO: %d\n", err);
return err;
}
optocouplerctrinput = devm_gpiod_get_optional(&pdev->dev, "optocouplerctrinput", GPIOD_IN);
if (IS_ERR(optocouplerctrinput)) {
err = PTR_ERR(optocouplerctrinput);
dev_err(&pdev->dev, "failed to request optocouplerctrinput GPIO: %d\n", err);
return err;
}
gpio3b5 = devm_gpiod_get_optional(&pdev->dev, "gpio3b5", GPIOD_OUT_HIGH);
gpio3b7 = devm_gpiod_get_optional(&pdev->dev, "gpio3b7", GPIOD_IN);
err = device_create_file(&pdev->dev, &dev_attr_optocouplerctroutput);
if (err)
printk("[%s] optocouplerctroutput device_create_file is error \n",__func__);
err = device_create_file(&pdev->dev, &dev_attr_optocouplerctrinput);
if (err)
printk("[%s] optocouplerctrinput device_create_file is error \n",__func__);
err = device_create_file(&pdev->dev, &dev_attr_gpio3b5);
if (err)
printk("[%s] gpio3b5 device_create_file is error \n",__func__);
printk("[%s] \n",__func__);
return 0;
}
int inoutputgpioctr_remove(struct platform_device *pdev){
printk("[%s]\n",__func__);
return 0;
}
static const struct of_device_id inoutputgpioctr_id[] = {
{.compatible = "xxx,inoutputgpioctr", 0},
{}};
struct platform_driver inoutputgpioctr_driver = {
.probe = inoutputgpioctr_probe,
.remove = inoutputgpioctr_remove,
.driver={
.owner = THIS_MODULE,
.name = "inoutputgpioctr",
.of_match_table = inoutputgpioctr_id,
},
};
static int inoutputgpioctr_driver_init(void){
int ret = 0;
ret = platform_driver_register(&inoutputgpioctr_driver);
if(ret < 0)
printk("[%s] error\n",__func__);
else
printk("[%s] ok\n",__func__);
return 0;
}
static void inoutputgpioctr_driver_exit(void){
platform_driver_unregister(&inoutputgpioctr_driver);
printk("[%s]\n",__func__);
}
module_init(inoutputgpioctr_driver_init);
module_exit(inoutputgpioctr_driver_exit);
MODULE_LICENSE("GPL");
五、参考文章
GPIO系列(2)——Linux的GPIO控制“gpiod_”和“gpio_”浅析_杨涂涂的博客-CSDN博客
linux驱动开发学习笔记十六:gpio相关OF函数和子系统API函数_of_gpio_count_耐心的小黑的博客-CSDN博客
linux设备树之gpio_操作系统架构的博客-CSDN博客
Linux下的gpio,gpiod_嵌入式Linux,的博客-CSDN博客
Linux子系统之GPIO_linux gpio子系统_niuiniuiniu的博客-CSDN博客