diff options
| -rw-r--r-- | drivers/gpio/gpiolib.c | 38 | ||||
| -rw-r--r-- | include/asm-generic/gpio.h | 5 | 
2 files changed, 41 insertions, 2 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d5f9742d9ac..e468eed261c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -191,6 +191,32 @@ err:  	return ret;  } +/* caller ensures gpio is valid and requested, chip->get_direction may sleep  */ +static int gpio_get_direction(unsigned gpio) +{ +	struct gpio_chip	*chip; +	struct gpio_desc	*desc = &gpio_desc[gpio]; +	int			status = -EINVAL; + +	chip = gpio_to_chip(gpio); +	gpio -= chip->base; + +	if (!chip->get_direction) +		return status; + +	status = chip->get_direction(chip, gpio); +	if (status > 0) { +		/* GPIOF_DIR_IN, or other positive */ +		status = 1; +		clear_bit(FLAG_IS_OUT, &desc->flags); +	} +	if (status == 0) { +		/* GPIOF_DIR_OUT */ +		set_bit(FLAG_IS_OUT, &desc->flags); +	} +	return status; +} +  #ifdef CONFIG_GPIO_SYSFS  /* lock protects against unexport_gpio() being called while @@ -223,6 +249,7 @@ static ssize_t gpio_direction_show(struct device *dev,  		struct device_attribute *attr, char *buf)  {  	const struct gpio_desc	*desc = dev_get_drvdata(dev); +	unsigned		gpio = desc - gpio_desc;  	ssize_t			status;  	mutex_lock(&sysfs_lock); @@ -230,6 +257,7 @@ static ssize_t gpio_direction_show(struct device *dev,  	if (!test_bit(FLAG_EXPORT, &desc->flags))  		status = -EIO;  	else +		gpio_get_direction(gpio);  		status = sprintf(buf, "%s\n",  			test_bit(FLAG_IS_OUT, &desc->flags)  				? "out" : "in"); @@ -1080,6 +1108,7 @@ int gpiochip_add(struct gpio_chip *chip)  			 * inputs (often with pullups enabled) so power  			 * usage is minimized.  Linux code should set the  			 * gpio direction first thing; but until it does, +			 * and in case chip->get_direction is not set,  			 * we may expose the wrong direction in sysfs.  			 */  			gpio_desc[id].flags = !chip->direction_input @@ -1231,9 +1260,15 @@ int gpio_request(unsigned gpio, const char *label)  			desc_set_label(desc, NULL);  			module_put(chip->owner);  			clear_bit(FLAG_REQUESTED, &desc->flags); +			goto done;  		}  	} - +	if (chip->get_direction) { +		/* chip->get_direction may sleep */ +		spin_unlock_irqrestore(&gpio_lock, flags); +		gpio_get_direction(gpio); +		spin_lock_irqsave(&gpio_lock, flags); +	}  done:  	if (status)  		pr_debug("gpio_request: gpio-%d (%s) status %d\n", @@ -1769,6 +1804,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)  		if (!test_bit(FLAG_REQUESTED, &gdesc->flags))  			continue; +		gpio_get_direction(gpio);  		is_out = test_bit(FLAG_IS_OUT, &gdesc->flags);  		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s",  			gpio, gdesc->label, diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index a9432fc6b8b..eb70ca29597 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -56,6 +56,8 @@ struct device_node;   *	enabling module power and clock; may sleep   * @free: optional hook for chip-specific deactivation, such as   *	disabling module power and clock; may sleep + * @get_direction: returns direction for signal "offset", 0=out, 1=in, + *	(same as GPIOF_DIR_XXX), or negative error   * @direction_input: configures signal "offset" as input, or returns error   * @get: returns value for signal "offset"; for output signals this   *	returns either the value actually sensed, or zero @@ -100,7 +102,8 @@ struct gpio_chip {  						unsigned offset);  	void			(*free)(struct gpio_chip *chip,  						unsigned offset); - +	int			(*get_direction)(struct gpio_chip *chip, +						unsigned offset);  	int			(*direction_input)(struct gpio_chip *chip,  						unsigned offset);  	int			(*get)(struct gpio_chip *chip,  |