From b892d127ffd448e8adf21e9703c0c5107e2a7814 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 4 Oct 2014 11:29:42 -0600 Subject: dm: gpio: Implement GPIO reservation in the uclass We have several GPIO drivers now and all are doing similar things to record which GPIOs are reserved. Move this logic into the uclass to make the drivers similar. We retain the request()/free() methods since currently one driver does use these for setting up the pin. Signed-off-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 92 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 14 deletions(-) (limited to 'drivers/gpio/gpio-uclass.c') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index a5ffd85..e11c5a6 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -92,24 +93,41 @@ int gpio_lookup_name(const char *name, struct udevice **devp, * gpio: GPIO number * label: Name for the requested GPIO * + * The label is copied and allocated so the caller does not need to keep + * the pointer around. + * * 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) { + struct gpio_dev_priv *uc_priv; unsigned int offset; struct udevice *dev; + char *str; int ret; ret = gpio_to_device(gpio, &dev, &offset); if (ret) return ret; - if (!gpio_get_ops(dev)->request) - return 0; + uc_priv = dev->uclass_priv; + if (uc_priv->name[offset]) + return -EBUSY; + str = strdup(label); + if (!str) + return -ENOMEM; + if (gpio_get_ops(dev)->request) { + ret = gpio_get_ops(dev)->request(dev, offset, label); + if (ret) { + free(str); + return ret; + } + } + uc_priv->name[offset] = str; - return gpio_get_ops(dev)->request(dev, offset, label); + return 0; } /** @@ -122,6 +140,7 @@ int gpio_request(unsigned gpio, const char *label) */ int gpio_free(unsigned gpio) { + struct gpio_dev_priv *uc_priv; unsigned int offset; struct udevice *dev; int ret; @@ -130,9 +149,34 @@ int gpio_free(unsigned gpio) if (ret) return ret; - if (!gpio_get_ops(dev)->free) - return 0; - return gpio_get_ops(dev)->free(dev, offset); + uc_priv = dev->uclass_priv; + if (!uc_priv->name[offset]) + return -ENXIO; + if (gpio_get_ops(dev)->free) { + ret = gpio_get_ops(dev)->free(dev, offset); + if (ret) + return ret; + } + + free(uc_priv->name[offset]); + uc_priv->name[offset] = NULL; + + return 0; +} + +static int check_reserved(struct udevice *dev, unsigned offset, + const char *func) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + if (!uc_priv->name[offset]) { + printf("%s: %s: error: gpio %s%d not reserved\n", + dev->name, func, + uc_priv->bank_name ? uc_priv->bank_name : "", offset); + return -EBUSY; + } + + return 0; } /** @@ -152,8 +196,9 @@ int gpio_direction_input(unsigned gpio) ret = gpio_to_device(gpio, &dev, &offset); if (ret) return ret; + ret = check_reserved(dev, offset, "dir_input"); - return gpio_get_ops(dev)->direction_input(dev, offset); + return ret ? ret : gpio_get_ops(dev)->direction_input(dev, offset); } /** @@ -174,8 +219,10 @@ int gpio_direction_output(unsigned gpio, int value) ret = gpio_to_device(gpio, &dev, &offset); if (ret) return ret; + ret = check_reserved(dev, offset, "dir_output"); - return gpio_get_ops(dev)->direction_output(dev, offset, value); + return ret ? ret : + gpio_get_ops(dev)->direction_output(dev, offset, value); } /** @@ -196,8 +243,9 @@ int gpio_get_value(unsigned gpio) ret = gpio_to_device(gpio, &dev, &offset); if (ret) return ret; + ret = check_reserved(dev, offset, "get_value"); - return gpio_get_ops(dev)->get_value(dev, offset); + return ret ? ret : gpio_get_ops(dev)->get_value(dev, offset); } /** @@ -218,8 +266,9 @@ int gpio_set_value(unsigned gpio, int value) ret = gpio_to_device(gpio, &dev, &offset); if (ret) return ret; + ret = check_reserved(dev, offset, "set_value"); - return gpio_get_ops(dev)->set_value(dev, offset, value); + return ret ? ret : gpio_get_ops(dev)->set_value(dev, offset, value); } const char *gpio_get_bank_info(struct udevice *dev, int *bit_count) @@ -235,7 +284,7 @@ const char *gpio_get_bank_info(struct udevice *dev, int *bit_count) } /* We need to renumber the GPIOs when any driver is probed/removed */ -static int gpio_renumber(void) +static int gpio_renumber(struct udevice *removed_dev) { struct gpio_dev_priv *uc_priv; struct udevice *dev; @@ -250,7 +299,7 @@ static int gpio_renumber(void) /* Ensure that we have a base for each bank */ base = 0; uclass_foreach_dev(dev, uc) { - if (device_active(dev)) { + if (device_active(dev) && dev != removed_dev) { uc_priv = dev->uclass_priv; uc_priv->gpio_base = base; base += uc_priv->gpio_count; @@ -262,12 +311,27 @@ static int gpio_renumber(void) static int gpio_post_probe(struct udevice *dev) { - return gpio_renumber(); + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *)); + if (!uc_priv->name) + return -ENOMEM; + + return gpio_renumber(NULL); } static int gpio_pre_remove(struct udevice *dev) { - return gpio_renumber(); + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + int i; + + for (i = 0; i < uc_priv->gpio_count; i++) { + if (uc_priv->name[i]) + free(uc_priv->name[i]); + } + free(uc_priv->name); + + return gpio_renumber(dev); } UCLASS_DRIVER(gpio) = { -- cgit v1.1