From ae7123f876357a81fc4c393239a378f1058cad5e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:27 -0700 Subject: dm: gpio: Add a native driver model API So far driver model's GPIO uclass just implements the existing GPIO API. This has some limitations: - it requires manual device tree munging to support GPIOs in device tree (fdtdec_get_gpio() and friends) - it does not understand polarity - it is somewhat slower since we must scan for the GPIO device each time - Global GPIO numbering can change if other GPIO drivers are probed - it requires extra steps to set the GPIO direction and value The new functions have a dm_ prefix where necessary to avoid name conflicts but we can remove that when it is no-longer needed. The new struct gpio_desc holds all required information about the GPIO. For now this is intended to be stored by the client requesting the GPIO, but in future it might be brought into the uclass in some way. With these changes the old GPIO API still works, and uses the driver model API underneath. Signed-off-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 218 ++++++++++++++++++++++++++++++--------------- 1 file changed, 147 insertions(+), 71 deletions(-) (limited to 'drivers/gpio/gpio-uclass.c') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 255700ab18d..2f3c36b48ff 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -13,14 +13,16 @@ /** * 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. + * + * @gpio: The numeric representation of the GPIO + * @desc: Returns description (desc->flags will always be 0) + * @return 0 if found, -ENOENT if not found */ -static int gpio_to_device(unsigned int gpio, struct udevice **devp, - unsigned int *offset) +static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc) { struct gpio_dev_priv *uc_priv; struct udevice *dev; @@ -32,14 +34,15 @@ static int gpio_to_device(unsigned int gpio, struct udevice **devp, 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; + desc->dev = dev; + desc->offset = gpio - uc_priv->gpio_base; + desc->flags = 0; return 0; } } /* No such GPIO */ - return ret ? ret : -EINVAL; + return ret ? ret : -ENOENT; } int gpio_lookup_name(const char *name, struct udevice **devp, @@ -88,6 +91,31 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; } +static int dm_gpio_request(struct gpio_desc *desc, const char *label) +{ + struct udevice *dev = desc->dev; + struct gpio_dev_priv *uc_priv; + char *str; + int ret; + + uc_priv = dev->uclass_priv; + if (uc_priv->name[desc->offset]) + return -EBUSY; + str = strdup(label); + if (!str) + return -ENOMEM; + if (gpio_get_ops(dev)->request) { + ret = gpio_get_ops(dev)->request(dev, desc->offset, label); + if (ret) { + free(str); + return ret; + } + } + uc_priv->name[desc->offset] = str; + + return 0; +} + /** * gpio_request() - [COMPAT] Request GPIO * gpio: GPIO number @@ -102,32 +130,14 @@ int gpio_lookup_name(const char *name, struct udevice **devp, */ int gpio_request(unsigned gpio, const char *label) { - struct gpio_dev_priv *uc_priv; - unsigned int offset; - struct udevice *dev; - char *str; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - 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 0; + return dm_gpio_request(&desc, label); } /** @@ -151,25 +161,11 @@ int gpio_requestf(unsigned gpio, const char *fmt, ...) return gpio_request(gpio, buf); } -/** - * 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) +int _dm_gpio_free(struct udevice *dev, uint offset) { struct gpio_dev_priv *uc_priv; - unsigned int offset; - struct udevice *dev; int ret; - ret = gpio_to_device(gpio, &dev, &offset); - if (ret) - return ret; - uc_priv = dev->uclass_priv; if (!uc_priv->name[offset]) return -ENXIO; @@ -185,15 +181,35 @@ int gpio_free(unsigned gpio) return 0; } -static int check_reserved(struct udevice *dev, unsigned offset, - const char *func) +/** + * 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) { - struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_desc desc; + int ret; + + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + + return _dm_gpio_free(desc.dev, desc.offset); +} - if (!uc_priv->name[offset]) { +static int check_reserved(struct gpio_desc *desc, const char *func) +{ + struct gpio_dev_priv *uc_priv = desc->dev->uclass_priv; + + if (!uc_priv->name[desc->offset]) { printf("%s: %s: error: gpio %s%d not reserved\n", - dev->name, func, - uc_priv->bank_name ? uc_priv->bank_name : "", offset); + desc->dev->name, func, + uc_priv->bank_name ? uc_priv->bank_name : "", + desc->offset); return -EBUSY; } @@ -210,16 +226,17 @@ static int check_reserved(struct udevice *dev, unsigned offset, */ int gpio_direction_input(unsigned gpio) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + ret = check_reserved(&desc, "dir_input"); if (ret) return ret; - ret = check_reserved(dev, offset, "dir_input"); - return ret ? ret : gpio_get_ops(dev)->direction_input(dev, offset); + return gpio_get_ops(desc.dev)->direction_input(desc.dev, desc.offset); } /** @@ -233,17 +250,81 @@ int gpio_direction_input(unsigned gpio) */ int gpio_direction_output(unsigned gpio, int value) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); + if (ret) + return ret; + ret = check_reserved(&desc, "dir_output"); + if (ret) + return ret; + + return gpio_get_ops(desc.dev)->direction_output(desc.dev, + desc.offset, value); +} + +int dm_gpio_get_value(struct gpio_desc *desc) +{ + int value; + int ret; + + ret = check_reserved(desc, "get_value"); if (ret) return ret; - ret = check_reserved(dev, offset, "dir_output"); - return ret ? ret : - gpio_get_ops(dev)->direction_output(dev, offset, value); + value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset); + + return desc->flags & GPIOD_ACTIVE_LOW ? !value : value; +} + +int dm_gpio_set_value(struct gpio_desc *desc, int value) +{ + int ret; + + ret = check_reserved(desc, "set_value"); + if (ret) + return ret; + + if (desc->flags & GPIOD_ACTIVE_LOW) + value = !value; + gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value); + return 0; +} + +int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags) +{ + struct udevice *dev = desc->dev; + struct dm_gpio_ops *ops = gpio_get_ops(dev); + int ret; + + ret = check_reserved(desc, "set_dir"); + if (ret) + return ret; + + if (flags & GPIOD_IS_OUT) { + int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0; + + if (flags & GPIOD_ACTIVE_LOW) + value = !value; + ret = ops->direction_output(dev, desc->offset, value); + } else if (flags & GPIOD_IS_IN) { + ret = ops->direction_input(dev, desc->offset); + } + if (ret) + return ret; + /* + * Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in + * futures + */ + desc->flags = flags; + + return 0; +} + +int dm_gpio_set_dir(struct gpio_desc *desc) +{ + return dm_gpio_set_dir_flags(desc, desc->flags); } /** @@ -257,16 +338,14 @@ int gpio_direction_output(unsigned gpio, int value) */ int gpio_get_value(unsigned gpio) { - unsigned int offset; - struct udevice *dev; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + struct gpio_desc desc; + + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - ret = check_reserved(dev, offset, "get_value"); - - return ret ? ret : gpio_get_ops(dev)->get_value(dev, offset); + return dm_gpio_get_value(&desc); } /** @@ -280,16 +359,13 @@ int gpio_get_value(unsigned gpio) */ int gpio_set_value(unsigned gpio, int value) { - unsigned int offset; - struct udevice *dev; + struct gpio_desc desc; int ret; - ret = gpio_to_device(gpio, &dev, &offset); + ret = gpio_to_device(gpio, &desc); if (ret) return ret; - ret = check_reserved(dev, offset, "set_value"); - - return ret ? ret : gpio_get_ops(dev)->set_value(dev, offset, value); + return dm_gpio_set_value(&desc, value); } const char *gpio_get_bank_info(struct udevice *dev, int *bit_count) -- cgit v1.2.3 From 0dac4d51f50e9252dbc00075cf65eeba57017926 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:28 -0700 Subject: dm: gpio: Add a driver GPIO translation method Only the GPIO driver knows about the full GPIO device tree binding used by a device. Add a method to allow the driver to provide this information to the uclass, including the GPIO offset within the device and flags such as the polarity. Signed-off-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/gpio/gpio-uclass.c') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 2f3c36b48ff..0a4d9e42178 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,21 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; } +int gpio_find_and_xlate(struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + struct dm_gpio_ops *ops = gpio_get_ops(desc->dev); + + /* Use the first argument as the offset by default */ + if (args->args_count > 0) + desc->offset = args->args[0]; + else + desc->offset = -1; + desc->flags = 0; + + return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0; +} + static int dm_gpio_request(struct gpio_desc *desc, const char *label) { struct udevice *dev = desc->dev; -- cgit v1.2.3 From 3669e0e759118fed3d371e2427b4b47d3969bfd0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Jan 2015 20:05:29 -0700 Subject: dm: gpio: Add better functions to request GPIOs At present U-Boot sort-of supports the standard way of reading GPIOs from device tree nodes, but the support is incomplete, a bit clunky and only works for GPIO bindings where #gpio-cells is 2. Add new functions to request GPIOs, taking full account of the device tree binding. These permit requesting a GPIO with a simple call like: gpio_request_by_name(dev, "cd-gpios", 0, &desc, GPIOD_IS_IN); This will request the GPIO, looking at the device's node which might be this, for example: cd-gpios = <&gpio TEGRA_GPIO(B, 3) GPIO_ACTIVE_LOW>; The GPIO will be set to input mode in this case and polarity will be honoured by the GPIO calls. It is also possible to request and free a list of GPIOs. Signed-off-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 166 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) (limited to 'drivers/gpio/gpio-uclass.c') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 0a4d9e42178..a69bbd2002e 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -12,6 +12,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + /** * gpio_to_device() - Convert global GPIO number to device, number * @@ -92,8 +94,8 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; } -int gpio_find_and_xlate(struct gpio_desc *desc, - struct fdtdec_phandle_args *args) +static int gpio_find_and_xlate(struct gpio_desc *desc, + struct fdtdec_phandle_args *args) { struct dm_gpio_ops *ops = gpio_get_ops(desc->dev); @@ -132,6 +134,17 @@ static int dm_gpio_request(struct gpio_desc *desc, const char *label) return 0; } +static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...) +{ + va_list args; + char buf[40]; + + va_start(args, fmt); + vscnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + return dm_gpio_request(desc, buf); +} + /** * gpio_request() - [COMPAT] Request GPIO * gpio: GPIO number @@ -501,6 +514,155 @@ unsigned gpio_get_values_as_int(const int *gpio_num_array) return vector; } +static int _gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, int index, + struct gpio_desc *desc, int flags, + bool add_index) +{ + struct fdtdec_phandle_args args; + int ret; + + desc->dev = NULL; + desc->offset = 0; + ret = fdtdec_parse_phandle_with_args(blob, node, list_name, + "#gpio-cells", 0, index, &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed\n", __func__); + goto err; + } + + ret = uclass_get_device_by_of_offset(UCLASS_GPIO, args.node, + &desc->dev); + if (ret) { + debug("%s: uclass_get_device_by_of_offset failed\n", __func__); + goto err; + } + ret = gpio_find_and_xlate(desc, &args); + if (ret) { + debug("%s: gpio_find_and_xlate failed\n", __func__); + goto err; + } + ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s", + fdt_get_name(blob, node, NULL), + list_name, index); + if (ret) { + debug("%s: dm_gpio_requestf failed\n", __func__); + goto err; + } + ret = dm_gpio_set_dir_flags(desc, flags | desc->flags); + if (ret) { + debug("%s: dm_gpio_set_dir failed\n", __func__); + goto err; + } + + return 0; +err: + debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n", + __func__, fdt_get_name(blob, node, NULL), list_name, index, ret); + return ret; +} + +int gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, int index, + struct gpio_desc *desc, int flags) +{ + return _gpio_request_by_name_nodev(blob, node, list_name, index, desc, + flags, index > 0); +} + +int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, + struct gpio_desc *desc, int flags) +{ + /* + * This isn't ideal since we don't use dev->name in the debug() + * calls in gpio_request_by_name(), but we can do this until + * gpio_request_by_name_nodev() can be dropped. + */ + return gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset, + list_name, index, desc, flags); +} + +int gpio_request_list_by_name_nodev(const void *blob, int node, + const char *list_name, + struct gpio_desc *desc, int max_count, + int flags) +{ + int count; + int ret; + + for (count = 0; ; count++) { + if (count >= max_count) { + ret = -ENOSPC; + goto err; + } + ret = _gpio_request_by_name_nodev(blob, node, list_name, count, + &desc[count], flags, true); + if (ret == -ENOENT) + break; + else if (ret) + goto err; + } + + /* We ran out of GPIOs in the list */ + return count; + +err: + gpio_free_list_nodev(desc, count - 1); + + return ret; +} + +int gpio_request_list_by_name(struct udevice *dev, const char *list_name, + struct gpio_desc *desc, int max_count, + int flags) +{ + /* + * This isn't ideal since we don't use dev->name in the debug() + * calls in gpio_request_by_name(), but we can do this until + * gpio_request_list_by_name_nodev() can be dropped. + */ + return gpio_request_list_by_name_nodev(gd->fdt_blob, dev->of_offset, + list_name, desc, max_count, + flags); +} + +int gpio_get_list_count(struct udevice *dev, const char *list_name) +{ + int ret; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset, + list_name, "#gpio-cells", 0, -1, + NULL); + if (ret) { + debug("%s: Node '%s', property '%s', GPIO count failed: %d\n", + __func__, dev->name, list_name, ret); + } + + return ret; +} + +int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc) +{ + /* For now, we don't do any checking of dev */ + return _dm_gpio_free(desc->dev, desc->offset); +} + +int gpio_free_list(struct udevice *dev, struct gpio_desc *desc, int count) +{ + int i; + + /* For now, we don't do any checking of dev */ + for (i = 0; i < count; i++) + dm_gpio_free(dev, &desc[i]); + + return 0; +} + +int gpio_free_list_nodev(struct gpio_desc *desc, int count) +{ + return gpio_free_list(NULL, desc, count); +} + /* We need to renumber the GPIOs when any driver is probed/removed */ static int gpio_renumber(struct udevice *removed_dev) { -- cgit v1.2.3