2023-03-29 linux 驱动用gpiod来控制gpio,devm_gpiod_get_optional gpiod_get_index等的使用,带实例

一、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博客

你可能感兴趣的:(linux,运维,服务器)