summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2014-10-04 11:29:42 -0600
committerSimon Glass <sjg@chromium.org>2014-10-23 19:29:51 -0600
commitb892d127ffd448e8adf21e9703c0c5107e2a7814 (patch)
tree60d7b9932cde9d5eaa3ecc0e557f171d39d0f695
parentb3f4ca1135edd66d14254089bbeb8077c6d0bb72 (diff)
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 <sjg@chromium.org>
-rw-r--r--drivers/gpio/gpio-uclass.c92
-rw-r--r--include/asm-generic/gpio.h6
2 files changed, 84 insertions, 14 deletions
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index a5ffd85c42..e11c5a69c5 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -7,6 +7,7 @@
#include <common.h>
#include <dm.h>
#include <errno.h>
+#include <malloc.h>
#include <asm/gpio.h>
#include <linux/ctype.h>
@@ -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) = {
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 1ebb9c724d..128249ef89 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -29,6 +29,9 @@
* Request a GPIO. This should be called before any of the other functions
* are used on this GPIO.
*
+ * Note: With driver model, the label is allocated so there is no need for
+ * the caller to preserve it.
+ *
* @param gp GPIO number
* @param label User label for this GPIO
* @return 0 if ok, -1 on error
@@ -157,11 +160,14 @@ struct dm_gpio_ops {
* @gpio_base: Base GPIO number for this device. For the first active device
* this will be 0; the numbering for others will follow sequentially so that
* @gpio_base for device 1 will equal the number of GPIOs in device 0.
+ * @name: Array of pointers to the name for each GPIO in this bank. The
+ * value of the pointer will be NULL if the GPIO has not been claimed.
*/
struct gpio_dev_priv {
const char *bank_name;
unsigned gpio_count;
unsigned gpio_base;
+ char **name;
};
/* Access the GPIO operations for a device */