| /* |
| * 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 udevice **devp, |
| unsigned int *offset) |
| { |
| struct gpio_dev_priv *uc_priv; |
| struct udevice *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 udevice **devp, |
| unsigned int *offsetp, unsigned int *gpiop) |
| { |
| struct gpio_dev_priv *uc_priv; |
| struct udevice *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 udevice *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 udevice *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 udevice *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 udevice *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 udevice *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 udevice *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 udevice *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 udevice *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 udevice *dev) |
| { |
| return gpio_renumber(); |
| } |
| |
| static int gpio_pre_remove(struct udevice *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), |
| }; |