diff options
Diffstat (limited to 'drivers/gpio/gpio-uclass.c')
| -rw-r--r-- | drivers/gpio/gpio-uclass.c | 266 | 
1 files changed, 266 insertions, 0 deletions
| diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c new file mode 100644 index 000000000..56bfd1146 --- /dev/null +++ b/drivers/gpio/gpio-uclass.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <asm/gpio.h> + +/** + * gpio_to_device() - Convert global GPIO number to device, number + * gpio:	The numeric representation of the GPIO + * + * Convert the GPIO number to an entry in the list of GPIOs + * or GPIO blocks registered with the GPIO controller. Returns + * entry on success, NULL on error. + */ +static int gpio_to_device(unsigned int gpio, struct device **devp, +			  unsigned int *offset) +{ +	struct gpio_dev_priv *uc_priv; +	struct device *dev; +	int ret; + +	for (ret = uclass_first_device(UCLASS_GPIO, &dev); +	     dev; +	     ret = uclass_next_device(&dev)) { +		uc_priv = dev->uclass_priv; +		if (gpio >= uc_priv->gpio_base && +		    gpio < uc_priv->gpio_base + uc_priv->gpio_count) { +			*devp = dev; +			*offset = gpio - uc_priv->gpio_base; +			return 0; +		} +	} + +	/* No such GPIO */ +	return ret ? ret : -EINVAL; +} + +int gpio_lookup_name(const char *name, struct device **devp, +		     unsigned int *offsetp, unsigned int *gpiop) +{ +	struct gpio_dev_priv *uc_priv; +	struct device *dev; +	int ret; + +	if (devp) +		*devp = NULL; +	for (ret = uclass_first_device(UCLASS_GPIO, &dev); +	     dev; +	     ret = uclass_next_device(&dev)) { +		ulong offset; +		int len; + +		uc_priv = dev->uclass_priv; +		len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0; + +		if (!strncmp(name, uc_priv->bank_name, len)) { +			if (strict_strtoul(name + len, 10, &offset)) +				continue; +			if (devp) +				*devp = dev; +			if (offsetp) +				*offsetp = offset; +			if (gpiop) +				*gpiop = uc_priv->gpio_base + offset; +			return 0; +		} +	} + +	return ret ? ret : -EINVAL; +} + +/** + * gpio_request() - [COMPAT] Request GPIO + * gpio:	GPIO number + * label:	Name for the requested GPIO + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_request(unsigned gpio, const char *label) +{ +	unsigned int offset; +	struct device *dev; +	int ret; + +	ret = gpio_to_device(gpio, &dev, &offset); +	if (ret) +		return ret; + +	if (!gpio_get_ops(dev)->request) +		return 0; + +	return gpio_get_ops(dev)->request(dev, offset, label); +} + +/** + * gpio_free() - [COMPAT] Relinquish GPIO + * gpio:	GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_free(unsigned gpio) +{ +	unsigned int offset; +	struct device *dev; +	int ret; + +	ret = gpio_to_device(gpio, &dev, &offset); +	if (ret) +		return ret; + +	if (!gpio_get_ops(dev)->free) +		return 0; +	return gpio_get_ops(dev)->free(dev, offset); +} + +/** + * gpio_direction_input() - [COMPAT] Set GPIO direction to input + * gpio:	GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_input(unsigned gpio) +{ +	unsigned int offset; +	struct device *dev; +	int ret; + +	ret = gpio_to_device(gpio, &dev, &offset); +	if (ret) +		return ret; + +	return gpio_get_ops(dev)->direction_input(dev, offset); +} + +/** + * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value + * gpio:	GPIO number + * value:	Logical value to be set on the GPIO pin + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_output(unsigned gpio, int value) +{ +	unsigned int offset; +	struct device *dev; +	int ret; + +	ret = gpio_to_device(gpio, &dev, &offset); +	if (ret) +		return ret; + +	return gpio_get_ops(dev)->direction_output(dev, offset, value); +} + +/** + * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value + * gpio:	GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns the value of the GPIO pin, or negative value + * on error. + */ +int gpio_get_value(unsigned gpio) +{ +	unsigned int offset; +	struct device *dev; +	int ret; + +	ret = gpio_to_device(gpio, &dev, &offset); +	if (ret) +		return ret; + +	return gpio_get_ops(dev)->get_value(dev, offset); +} + +/** + * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin + * gpio:	GPIO number + * value:	Logical value to be set on the GPIO pin. + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_set_value(unsigned gpio, int value) +{ +	unsigned int offset; +	struct device *dev; +	int ret; + +	ret = gpio_to_device(gpio, &dev, &offset); +	if (ret) +		return ret; + +	return gpio_get_ops(dev)->set_value(dev, offset, value); +} + +const char *gpio_get_bank_info(struct device *dev, int *bit_count) +{ +	struct gpio_dev_priv *priv; + +	/* Must be called on an active device */ +	priv = dev->uclass_priv; +	assert(priv); + +	*bit_count = priv->gpio_count; +	return priv->bank_name; +} + +/* We need to renumber the GPIOs when any driver is probed/removed */ +static int gpio_renumber(void) +{ +	struct gpio_dev_priv *uc_priv; +	struct device *dev; +	struct uclass *uc; +	unsigned base; +	int ret; + +	ret = uclass_get(UCLASS_GPIO, &uc); +	if (ret) +		return ret; + +	/* Ensure that we have a base for each bank */ +	base = 0; +	uclass_foreach_dev(dev, uc) { +		if (device_active(dev)) { +			uc_priv = dev->uclass_priv; +			uc_priv->gpio_base = base; +			base += uc_priv->gpio_count; +		} +	} + +	return 0; +} + +static int gpio_post_probe(struct device *dev) +{ +	return gpio_renumber(); +} + +static int gpio_pre_remove(struct device *dev) +{ +	return gpio_renumber(); +} + +UCLASS_DRIVER(gpio) = { +	.id		= UCLASS_GPIO, +	.name		= "gpio", +	.post_probe	= gpio_post_probe, +	.pre_remove	= gpio_pre_remove, +	.per_device_auto_alloc_size = sizeof(struct gpio_dev_priv), +}; |