From e1c73a99067ee378e395da89efff40daa7096f42 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 31 Oct 2017 16:21:39 +0200 Subject: gpio: max732x: Remove duplicate NULL check Since i2c_unregister_device() became NULL-aware we may remove duplicate NULL check. Cc: Linus Walleij Cc: linux-gpio@vger.kernel.org Signed-off-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-max732x.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index c04fae1ba32a..9d8bcc69f245 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -709,8 +709,7 @@ static int max732x_probe(struct i2c_client *client, return 0; out_failed: - if (chip->client_dummy) - i2c_unregister_device(chip->client_dummy); + i2c_unregister_device(chip->client_dummy); return ret; } @@ -734,8 +733,7 @@ static int max732x_remove(struct i2c_client *client) gpiochip_remove(&chip->gpio_chip); /* unregister any dummy i2c_client */ - if (chip->client_dummy) - i2c_unregister_device(chip->client_dummy); + i2c_unregister_device(chip->client_dummy); return 0; } -- cgit v1.2.3 From f67a6c11f99a0b7015dea7378b5e82e371425710 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Nov 2017 15:40:28 +0200 Subject: gpiolib: acpi: Assign polarity when call acpi_populate_gpio_lookup() There is no need, since we preserve firmware settings, to override polarity for GpioInt() resources. While Documentation/gpio-properties.txt refers to any from GpioIo() / GpioInt() resources, the active_low flag has been introduced to fill the gap only for GpioIo() which lacks of that information. Moreover, in case of GpioInt() existed solution was broken anyway, it overrides only in one direction, i.e. from 0 to 1, otherwise it would be still 1 as defined in the resource macro. So, move the assignment to a right place and forbid to (semi-)override polarity for GpioInt() type of resources. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index eb4528c87c0b..2a85d27eb028 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -531,8 +531,8 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) lookup->info.triggering = agpio->triggering; } else { lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio); + lookup->info.polarity = lookup->active_low; } - } return 1; @@ -557,11 +557,8 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, if (!lookup->desc) return -ENOENT; - if (info) { + if (info) *info = lookup->info; - if (lookup->active_low) - info->polarity = lookup->active_low; - } return 0; } -- cgit v1.2.3 From 08be1a795de3bf9dab31affed5e495ff91a1744d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Nov 2017 15:40:29 +0200 Subject: gpiolib: acpi: Don't contaminate return parameter in case of error If error occurs, leave lookup parameter untouched. There is no functional change, since all current callers just bail out in case of error without using the assigned pieces. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 2a85d27eb028..f640445099ab 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -585,7 +585,6 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, * The property was found and resolved, so need to lookup the GPIO based * on returned args. */ - lookup->adev = args.adev; if (args.nargs != 3) return -EPROTO; @@ -593,6 +592,7 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, lookup->pin_index = args.args[1]; lookup->active_low = !!args.args[2]; + lookup->adev = args.adev; return 0; } -- cgit v1.2.3 From 5870cff47bb6aeb9f9e073cbfcecd6f0f0d3035b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Nov 2017 15:40:30 +0200 Subject: gpiolib: acpi: Move adev member to struct acpi_gpio_info The further improvements are based on this change since struct acpi_gpio_lookup is not available in some cases. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 10 +++++----- drivers/gpio/gpiolib.h | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index f640445099ab..7d0664a3d1b9 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -494,7 +494,6 @@ struct acpi_gpio_lookup { int index; int pin_index; bool active_low; - struct acpi_device *adev; struct gpio_desc *desc; int n; }; @@ -541,12 +540,13 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, struct acpi_gpio_info *info) { + struct acpi_device *adev = lookup->info.adev; struct list_head res_list; int ret; INIT_LIST_HEAD(&res_list); - ret = acpi_dev_get_resources(lookup->adev, &res_list, + ret = acpi_dev_get_resources(adev, &res_list, acpi_populate_gpio_lookup, lookup); if (ret < 0) @@ -592,7 +592,7 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, lookup->pin_index = args.args[1]; lookup->active_low = !!args.args[2]; - lookup->adev = args.adev; + lookup->info.adev = args.adev; return 0; } @@ -640,11 +640,11 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, return ERR_PTR(ret); dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %d %u\n", - dev_name(&lookup.adev->dev), lookup.index, + dev_name(&lookup.info.adev->dev), lookup.index, lookup.pin_index, lookup.active_low); } else { dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); - lookup.adev = adev; + lookup.info.adev = adev; } ret = acpi_gpio_resource_lookup(&lookup, info); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index af48322839c3..e8d0bfa57288 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -75,12 +75,14 @@ struct gpio_device { /** * struct acpi_gpio_info - ACPI GPIO specific information + * @adev: reference to ACPI device which consumes GPIO resource * @flags: GPIO initialization flags * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI */ struct acpi_gpio_info { + struct acpi_device *adev; enum gpiod_flags flags; bool gpioint; int polarity; -- cgit v1.2.3 From 5c34b6c1468d54df71e8f5b7b3275bac705e2c4c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Nov 2017 15:40:31 +0200 Subject: gpiolib: acpi: Consolidate debug output in acpi_gpio_update_gpiod_flags() We have the duplicated debug strings printed whenever acpi_gpio_update_gpiod_flags() fails. Instead of doing this by callers, move the debug output inside function. In one case convert almost useless pr_debug() to dev_dbg() where actual consumer of GPIO resource is disclosed. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 23 ++++++++++++++++------- drivers/gpio/gpiolib.c | 4 +--- drivers/gpio/gpiolib.h | 4 ++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 7d0664a3d1b9..88518afa26bd 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -461,8 +461,8 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) } } -int -acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +static int +__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) { int ret = 0; @@ -489,6 +489,19 @@ acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) return ret; } +int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) +{ + struct device *dev = &info->adev->dev; + int ret; + + ret = __acpi_gpio_update_gpiod_flags(flags, info->flags); + if (ret) + dev_dbg(dev, "Override GPIO initialization flags\n"); + + return ret; +} + struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; @@ -661,7 +674,6 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, struct acpi_gpio_info info; struct gpio_desc *desc; char propname[32]; - int err; int i; /* Try first from _DSD */ @@ -700,10 +712,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, if (info.polarity == GPIO_ACTIVE_LOW) *lookupflags |= GPIO_ACTIVE_LOW; - err = acpi_gpio_update_gpiod_flags(dflags, info.flags); - if (err) - dev_dbg(dev, "Override GPIO initialization flags\n"); - + acpi_gpio_update_gpiod_flags(dflags, &info); return desc; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index aad84a6306c4..c45dcb0edb12 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3681,9 +3681,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, desc = acpi_node_get_gpiod(fwnode, propname, index, &info); if (!IS_ERR(desc)) { active_low = info.polarity == GPIO_ACTIVE_LOW; - ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags); - if (ret) - pr_debug("Override GPIO initialization flags\n"); + acpi_gpio_update_gpiod_flags(&dflags, &info); } } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index e8d0bfa57288..b23aeaf5ac9e 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -126,7 +126,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, - enum gpiod_flags update); + struct acpi_gpio_info *info); struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, @@ -151,7 +151,7 @@ static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } static inline int -acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) { return 0; } -- cgit v1.2.3 From ce0929d222f8cb18a66611642dc0661d633ce192 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Nov 2017 15:40:32 +0200 Subject: gpiolib: acpi: Add quirks field to struct acpi_gpio_mapping Some broken ACPI tables might require quirks in the OS. Introduce quirks field in struct acpi_gpio_mapping. Propagate them to struct acpi_gpio_info for further use. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 10 ++++++++-- drivers/gpio/gpiolib.h | 2 ++ include/linux/acpi.h | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 88518afa26bd..711f64b9dd30 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -414,7 +414,8 @@ EXPORT_SYMBOL_GPL(devm_acpi_dev_remove_driver_gpios); static bool acpi_get_driver_gpio_data(struct acpi_device *adev, const char *name, int index, - struct acpi_reference_args *args) + struct acpi_reference_args *args, + unsigned int *quirks) { const struct acpi_gpio_mapping *gm; @@ -430,6 +431,8 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev, args->args[1] = par->line_index; args->args[2] = par->active_low; args->nargs = 3; + + *quirks = gm->quirks; return true; } @@ -580,6 +583,7 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, struct acpi_gpio_lookup *lookup) { struct acpi_reference_args args; + unsigned int quirks = 0; int ret; memset(&args, 0, sizeof(args)); @@ -591,7 +595,8 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, if (!adev) return ret; - if (!acpi_get_driver_gpio_data(adev, propname, index, &args)) + if (!acpi_get_driver_gpio_data(adev, propname, index, &args, + &quirks)) return ret; } /* @@ -606,6 +611,7 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, lookup->active_low = !!args.args[2]; lookup->info.adev = args.adev; + lookup->info.quirks = quirks; return 0; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b23aeaf5ac9e..cd4622863fe1 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -80,6 +80,7 @@ struct gpio_device { * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI + * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping */ struct acpi_gpio_info { struct acpi_device *adev; @@ -87,6 +88,7 @@ struct acpi_gpio_info { bool gpioint; int polarity; int triggering; + unsigned int quirks; }; /* gpio suffixes used for ACPI and device tree lookup */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index dc1ebfeeb5ec..25fe77fccea0 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -978,6 +978,7 @@ struct acpi_gpio_mapping { const char *name; const struct acpi_gpio_params *data; unsigned int size; + unsigned int quirks; }; #if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) -- cgit v1.2.3 From 1b2ca32ab0b8311da84fe692522266b32ad4315e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Nov 2017 15:40:33 +0200 Subject: gpiolib: acpi: Introduce NO_RESTRICTION quirk Allow to relax IoRestriction for certain cases. One of the use case is incorrectly cooked ACPI table where interrupt pin is defined with GpioIo() macro with IoRestrictionOutputOnly. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 13 ++++++++++--- include/linux/acpi.h | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 711f64b9dd30..430a1475212d 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -496,11 +496,18 @@ int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) { struct device *dev = &info->adev->dev; + enum gpiod_flags old = *flags; int ret; - ret = __acpi_gpio_update_gpiod_flags(flags, info->flags); - if (ret) - dev_dbg(dev, "Override GPIO initialization flags\n"); + ret = __acpi_gpio_update_gpiod_flags(&old, info->flags); + if (info->quirks & ACPI_GPIO_QUIRK_NO_IO_RESTRICTION) { + if (ret) + dev_warn(dev, FW_BUG "GPIO not in correct mode, fixing\n"); + } else { + if (ret) + dev_dbg(dev, "Override GPIO initialization flags\n"); + *flags = old; + } return ret; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 25fe77fccea0..06b6eb775115 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -978,6 +978,10 @@ struct acpi_gpio_mapping { const char *name; const struct acpi_gpio_params *data; unsigned int size; + +/* Ignore IoRestriction field */ +#define ACPI_GPIO_QUIRK_NO_IO_RESTRICTION BIT(0) + unsigned int quirks; }; -- cgit v1.2.3 From 588fc3bceaf81bbd62e18af6f7bd475e01c2b7e8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 15 Nov 2017 16:47:43 +0100 Subject: gpiolib: don't allow OPEN_DRAIN & OPEN_SOURCE flags simultaneously Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If the hardware actually supports enabling both at the same time the electrical result would be disastrous. Suggested-by: Linus Walleij Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index aad84a6306c4..bdcdcdefdb71 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -459,6 +459,15 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) return -EINVAL; + /* + * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If + * the hardware actually supports enabling both at the same time the + * electrical result would be disastrous. + */ + if ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && + (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ if (!(lflags & GPIOHANDLE_REQUEST_OUTPUT) && ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || -- cgit v1.2.3 From 8193032821d6badd6dc80e805c9e2af48e84ef7c Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 15 Nov 2017 12:36:33 -0600 Subject: gpio: omap: initialize gpioirq chip as part of gpiochip_add_data Hence, series from Thierry Reding [1] merged - the OMAP GPIO driver can be switched to reuse new feature and just fill new struct gpio_irq_chip before calling gpiochip_add_data() instead of using few separate gpioirq chip APIs. gpiochip_add_data() will do the job and create and initialize gpioirq chip [1] https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1531592.html Signed-off-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index e136d666f1e5..05bae9f42f52 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1058,6 +1058,7 @@ static void omap_gpio_mod_init(struct gpio_bank *bank) static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) { + struct gpio_irq_chip *irq; static int gpio; int irq_base = 0; int ret; @@ -1085,16 +1086,6 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) } bank->chip.ngpio = bank->width; - ret = gpiochip_add_data(&bank->chip, bank); - if (ret) { - dev_err(bank->chip.parent, - "Could not register gpio chip %d\n", ret); - return ret; - } - - if (!bank->is_mpuio) - gpio += bank->width; - #ifdef CONFIG_ARCH_OMAP1 /* * REVISIT: Once we have OMAP1 supporting SPARSE_IRQ, we can drop @@ -1115,25 +1106,30 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) irqc->irq_set_wake = NULL; } - ret = gpiochip_irqchip_add(&bank->chip, irqc, - irq_base, handle_bad_irq, - IRQ_TYPE_NONE); + irq = &bank->chip.irq; + irq->chip = irqc; + irq->handler = handle_bad_irq; + irq->default_type = IRQ_TYPE_NONE; + irq->num_parents = 1; + irq->parents = &bank->irq; + irq->first = irq_base; + ret = gpiochip_add_data(&bank->chip, bank); if (ret) { dev_err(bank->chip.parent, - "Couldn't add irqchip to gpiochip %d\n", ret); - gpiochip_remove(&bank->chip); - return -ENODEV; + "Could not register gpio chip %d\n", ret); + return ret; } - gpiochip_set_chained_irqchip(&bank->chip, irqc, bank->irq, NULL); - ret = devm_request_irq(bank->chip.parent, bank->irq, omap_gpio_irq_handler, 0, dev_name(bank->chip.parent), bank); if (ret) gpiochip_remove(&bank->chip); + if (!bank->is_mpuio) + gpio += bank->width; + return ret; } -- cgit v1.2.3 From 56a46b6144e7311e2bf605755a168409ef527fc4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 30 Nov 2017 11:03:05 +0100 Subject: gpio: Clarify that is legacy It should be clear to developers that they should not include this file in new code. Suggested-by: Arnd Bergmann Signed-off-by: Linus Walleij --- include/linux/gpio.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 8ef7fc0ce0f0..91ed23468530 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -1,4 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * + * + * This is the LEGACY GPIO bulk include file, including legacy APIs. It is + * used for GPIO drivers still referencing the global GPIO numberspace, + * and should not be included in new code. + * + * If you're implementing a GPIO driver, only include + * If you're implementing a GPIO consumer, only include + */ #ifndef __LINUX_GPIO_H #define __LINUX_GPIO_H -- cgit v1.2.3 From 97b03136e1b637d7a9d2274c099e44ecf23f1103 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Mon, 20 Nov 2017 12:54:52 -0800 Subject: gpio: iop: add missing MODULE_DESCRIPTION/AUTHOR/LICENSE This change resolves a new compile-time warning when built as a loadable module: WARNING: modpost: missing MODULE_LICENSE() in drivers/gpio/gpio-iop.o see include/linux/module.h for more information This adds the license as "GPL", which matches the header of the file. MODULE_DESCRIPTION and MODULE_AUTHOR are also added. Signed-off-by: Jesse Chan Signed-off-by: Linus Walleij --- drivers/gpio/gpio-iop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 98c7ff2a76e7..8d62db447ec1 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -58,3 +58,7 @@ static int __init iop3xx_gpio_init(void) return platform_driver_register(&iop3xx_gpio_driver); } arch_initcall(iop3xx_gpio_init); + +MODULE_DESCRIPTION("GPIO handling for Intel IOP3xx processors"); +MODULE_AUTHOR("Lennert Buytenhek "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 539340f37e6d6ed4cd93e8e18c9b2e4eafd4b842 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Mon, 20 Nov 2017 12:54:26 -0800 Subject: gpio: ath79: add missing MODULE_DESCRIPTION/LICENSE This change resolves a new compile-time warning when built as a loadable module: WARNING: modpost: missing MODULE_LICENSE() in drivers/gpio/gpio-ath79.o see include/linux/module.h for more information This adds the license as "GPL v2", which matches the header of the file. MODULE_DESCRIPTION is also added. Signed-off-by: Jesse Chan Acked-by: Alban Bedel Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ath79.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index 5fad89dfab7e..3ae7c1876bf4 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -324,3 +324,6 @@ static struct platform_driver ath79_gpio_driver = { }; module_platform_driver(ath79_gpio_driver); + +MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X GPIO API support"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From f3b47170e048478a61063194d74fa72ffad716fc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:37 +0100 Subject: gpio: mockup: add missing prefixes Add the gpio_mockup_ prefix to the remaining symbols that still don't have it, so that the entire driver code is consistent. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 9532d86a82f7..d21c6d651287 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -327,8 +327,9 @@ static struct platform_driver gpio_mockup_driver = { .probe = gpio_mockup_probe, }; -static struct platform_device *pdev; -static int __init mock_device_init(void) +static struct platform_device *gpio_mockup_pdev; + +static int __init gpio_mockup_init(void) { int err; @@ -337,34 +338,34 @@ static int __init mock_device_init(void) pr_err("%s: error creating debugfs directory\n", GPIO_MOCKUP_NAME); - pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1); - if (!pdev) + gpio_mockup_pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1); + if (!gpio_mockup_pdev) return -ENOMEM; - err = platform_device_add(pdev); + err = platform_device_add(gpio_mockup_pdev); if (err) { - platform_device_put(pdev); + platform_device_put(gpio_mockup_pdev); return err; } err = platform_driver_register(&gpio_mockup_driver); if (err) { - platform_device_unregister(pdev); + platform_device_unregister(gpio_mockup_pdev); return err; } return 0; } -static void __exit mock_device_exit(void) +static void __exit gpio_mockup_exit(void) { debugfs_remove_recursive(gpio_mockup_dbg_dir); platform_driver_unregister(&gpio_mockup_driver); - platform_device_unregister(pdev); + platform_device_unregister(gpio_mockup_pdev); } -module_init(mock_device_init); -module_exit(mock_device_exit); +module_init(gpio_mockup_init); +module_exit(gpio_mockup_exit); MODULE_AUTHOR("Kamlakant Patel "); MODULE_AUTHOR("Bamvor Jian Zhang "); -- cgit v1.2.3 From 20c35ac473116101120b3e170f294bc5bc70bbc4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:38 +0100 Subject: gpio: mockup: parse the module params in init, not probe If the module parameters are invalid, we should bail out from the init function instead of detecting it during the device probe. That way we don't even allow the user to load the module if we don't accept the arguments. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index d21c6d651287..b70f3b0c30b1 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -275,9 +275,6 @@ static int gpio_mockup_probe(struct platform_device *pdev) struct gpio_mockup_chip *chips; char *chip_name; - if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2)) - return -EINVAL; - /* Each chip is described by two values. */ num_chips = gpio_mockup_params_nr / 2; @@ -333,6 +330,9 @@ static int __init gpio_mockup_init(void) { int err; + if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2)) + return -EINVAL; + gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); if (!gpio_mockup_dbg_dir) pr_err("%s: error creating debugfs directory\n", -- cgit v1.2.3 From b447a75100a63c114a8aaa02563088f4995c09f3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:39 +0100 Subject: gpio: mockup: verify the number of GPIO chips requested The number of supported mockup chips is limited. Check this limit when parsing the module parameters. Also: make sure that each chip is described with a pair. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index b70f3b0c30b1..0a269cbe197c 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -330,7 +330,9 @@ static int __init gpio_mockup_init(void) { int err; - if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2)) + if ((gpio_mockup_params_nr < 2) || + (gpio_mockup_params_nr % 2) || + (gpio_mockup_params_nr > GPIO_MOCKUP_MAX_RANGES)) return -EINVAL; gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); -- cgit v1.2.3 From 8a39f597bcfde78d566fb61b39215d8897f0f392 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:40 +0100 Subject: gpio: mockup: rework device probing We currently create a single platform device in init and then parse the configuration passed to us via module parameters in probe() before creating GPIO chips and registering them with the gpiolib framework. The relation between platform devices and mockup chips should be 1:1. Create a separate platform device for each mockup chip using convenient helpers (platform_device_register_resndata()). Pass a platform data structure to probe() in which the configuration (GPIO base, number of lines, chip index) extracted from the module params is stored. Make probe() create a single mockup chip for every platform device. This approach has several advantages: - we only parse the module parameters in init() and can bail out before attaching any device if the input is invalid (currently we would have to examine kernel logs), - we'll get notified by the device framework about errors in probe() for specific chips, - probe() gets simplified and only does what it's supposed to. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 122 ++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 0a269cbe197c..5ba8f2089de8 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -62,6 +62,12 @@ struct gpio_mockup_dbgfs_private { int offset; }; +struct gpio_mockup_platform_data { + int base; + int ngpio; + int index; +}; + static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; static int gpio_mockup_params_nr; module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); @@ -70,7 +76,6 @@ static bool gpio_mockup_named_lines; module_param_named(gpio_mockup_named_lines, gpio_mockup_named_lines, bool, 0400); -static const char gpio_mockup_name_start = 'A'; static struct dentry *gpio_mockup_dbg_dir; static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) @@ -270,48 +275,32 @@ static int gpio_mockup_add(struct device *dev, static int gpio_mockup_probe(struct platform_device *pdev) { - int ret, i, base, ngpio, num_chips; - struct device *dev = &pdev->dev; - struct gpio_mockup_chip *chips; - char *chip_name; + struct gpio_mockup_platform_data *pdata; + struct gpio_mockup_chip *chip; + int rv, base, ngpio; + struct device *dev; + char *name; - /* Each chip is described by two values. */ - num_chips = gpio_mockup_params_nr / 2; + dev = &pdev->dev; + pdata = dev_get_platdata(dev); + base = pdata->base; + ngpio = pdata->ngpio; - chips = devm_kcalloc(dev, num_chips, sizeof(*chips), GFP_KERNEL); - if (!chips) + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) return -ENOMEM; - platform_set_drvdata(pdev, chips); - - for (i = 0; i < num_chips; i++) { - base = gpio_mockup_ranges[i * 2]; - - if (base == -1) - ngpio = gpio_mockup_ranges[i * 2 + 1]; - else - ngpio = gpio_mockup_ranges[i * 2 + 1] - base; - - if (ngpio >= 0) { - chip_name = devm_kasprintf(dev, GFP_KERNEL, - "%s-%c", GPIO_MOCKUP_NAME, - gpio_mockup_name_start + i); - if (!chip_name) - return -ENOMEM; - - ret = gpio_mockup_add(dev, &chips[i], - chip_name, base, ngpio); - } else { - ret = -EINVAL; - } - - if (ret) { - dev_err(dev, - "adding gpiochip failed: %d (base: %d, ngpio: %d)\n", - ret, base, base < 0 ? ngpio : base + ngpio); + name = devm_kasprintf(dev, GFP_KERNEL, "%s-%c", + pdev->name, pdata->index); + if (!name) + return -ENOMEM; - return ret; - } + rv = gpio_mockup_add(dev, chip, name, base, ngpio); + if (rv) { + dev_err(dev, + "adding gpiochip failed (base: %d, ngpio: %d)\n", + base, base < 0 ? ngpio : base + ngpio); + return rv; } return 0; @@ -324,36 +313,67 @@ static struct platform_driver gpio_mockup_driver = { .probe = gpio_mockup_probe, }; -static struct platform_device *gpio_mockup_pdev; +static struct platform_device *gpio_mockup_pdevs[GPIO_MOCKUP_MAX_GC]; + +static void gpio_mockup_unregister_pdevs(void) +{ + struct platform_device *pdev; + int i; + + for (i = 0; i < GPIO_MOCKUP_MAX_GC; i++) { + pdev = gpio_mockup_pdevs[i]; + + if (pdev) + platform_device_unregister(pdev); + } +} static int __init gpio_mockup_init(void) { - int err; + int i, num_chips, err = 0, index = 'A'; + struct gpio_mockup_platform_data pdata; + struct platform_device *pdev; if ((gpio_mockup_params_nr < 2) || (gpio_mockup_params_nr % 2) || (gpio_mockup_params_nr > GPIO_MOCKUP_MAX_RANGES)) return -EINVAL; + /* Each chip is described by two values. */ + num_chips = gpio_mockup_params_nr / 2; + gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); if (!gpio_mockup_dbg_dir) pr_err("%s: error creating debugfs directory\n", GPIO_MOCKUP_NAME); - gpio_mockup_pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1); - if (!gpio_mockup_pdev) - return -ENOMEM; - - err = platform_device_add(gpio_mockup_pdev); + err = platform_driver_register(&gpio_mockup_driver); if (err) { - platform_device_put(gpio_mockup_pdev); + pr_err("%s: error registering platform driver\n", + GPIO_MOCKUP_NAME); return err; } - err = platform_driver_register(&gpio_mockup_driver); - if (err) { - platform_device_unregister(gpio_mockup_pdev); - return err; + for (i = 0; i < num_chips; i++) { + pdata.index = index++; + pdata.base = gpio_mockup_ranges[i * 2]; + pdata.ngpio = pdata.base < 0 + ? gpio_mockup_ranges[i * 2 + 1] + : gpio_mockup_ranges[i * 2 + 1] - pdata.base; + + pdev = platform_device_register_resndata(NULL, + GPIO_MOCKUP_NAME, + i, NULL, 0, &pdata, + sizeof(pdata)); + if (!pdev) { + pr_err("%s: error registering device", + GPIO_MOCKUP_NAME); + platform_driver_unregister(&gpio_mockup_driver); + gpio_mockup_unregister_pdevs(); + return -ENOMEM; + } + + gpio_mockup_pdevs[i] = pdev; } return 0; @@ -363,7 +383,7 @@ static void __exit gpio_mockup_exit(void) { debugfs_remove_recursive(gpio_mockup_dbg_dir); platform_driver_unregister(&gpio_mockup_driver); - platform_device_unregister(gpio_mockup_pdev); + gpio_mockup_unregister_pdevs(); } module_init(gpio_mockup_init); -- cgit v1.2.3 From f1bec99ae4fccb35c477f483cb8149f4196faa08 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:41 +0100 Subject: gpio: mockup: implement gpio_mockup_err() Visually shrink the pr_err() calls by encapsulating adding the module name prefix to the message in a macro. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 5ba8f2089de8..0aa68242f33b 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -34,6 +34,8 @@ */ #define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2) +#define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__) + enum { GPIO_MOCKUP_DIR_OUT = 0, GPIO_MOCKUP_DIR_IN = 1, @@ -344,13 +346,11 @@ static int __init gpio_mockup_init(void) gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); if (!gpio_mockup_dbg_dir) - pr_err("%s: error creating debugfs directory\n", - GPIO_MOCKUP_NAME); + gpio_mockup_err("error creating debugfs directory\n"); err = platform_driver_register(&gpio_mockup_driver); if (err) { - pr_err("%s: error registering platform driver\n", - GPIO_MOCKUP_NAME); + gpio_mockup_err("error registering platform driver\n"); return err; } @@ -366,8 +366,7 @@ static int __init gpio_mockup_init(void) i, NULL, 0, &pdata, sizeof(pdata)); if (!pdev) { - pr_err("%s: error registering device", - GPIO_MOCKUP_NAME); + gpio_mockup_err("error registering device"); platform_driver_unregister(&gpio_mockup_driver); gpio_mockup_unregister_pdevs(); return -ENOMEM; -- cgit v1.2.3 From b9576d03d52eb16dce5f6b4b3800e504cafe87e5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:42 +0100 Subject: gpio: mockup: remove a stray tab Minor coding style fix. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 0aa68242f33b..9b2823a33538 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -27,7 +27,7 @@ #include "gpiolib.h" #define GPIO_MOCKUP_NAME "gpio-mockup" -#define GPIO_MOCKUP_MAX_GC 10 +#define GPIO_MOCKUP_MAX_GC 10 /* * We're storing two values per chip: the GPIO base and the number * of GPIO lines. -- cgit v1.2.3 From 6d974d32919501ebac1576fda0ce042f0a58dd8f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:43 +0100 Subject: gpio: mockup: merge gpio_mockup_add() into gpio_mockup_probe() Now that the probe() function only does what is should, there's no need to split the chip adding logic into a separate routine. Merge gpio_mockup_add() into gpio_mockup_probe(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 78 ++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 9b2823a33538..2dbaeb69d2d2 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -231,13 +231,30 @@ err: dev_err(dev, "error creating debugfs directory\n"); } -static int gpio_mockup_add(struct device *dev, - struct gpio_mockup_chip *chip, - const char *name, int base, int ngpio) +static int gpio_mockup_probe(struct platform_device *pdev) { - struct gpio_chip *gc = &chip->gc; - int ret; + struct gpio_mockup_platform_data *pdata; + struct gpio_mockup_chip *chip; + struct gpio_chip *gc; + int rv, base, ngpio; + struct device *dev; + char *name; + + dev = &pdev->dev; + pdata = dev_get_platdata(dev); + base = pdata->base; + ngpio = pdata->ngpio; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-%c", + pdev->name, pdata->index); + if (!name) + return -ENOMEM; + gc = &chip->gc; gc->base = base; gc->ngpio = ngpio; gc->label = name; @@ -256,18 +273,18 @@ static int gpio_mockup_add(struct device *dev, return -ENOMEM; if (gpio_mockup_named_lines) { - ret = gpio_mockup_name_lines(dev, chip); - if (ret) - return ret; + rv = gpio_mockup_name_lines(dev, chip); + if (rv) + return rv; } - ret = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio); - if (ret) - return ret; + rv = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio); + if (rv) + return rv; - ret = devm_gpiochip_add_data(dev, &chip->gc, chip); - if (ret) - return ret; + rv = devm_gpiochip_add_data(dev, &chip->gc, chip); + if (rv) + return rv; if (gpio_mockup_dbg_dir) gpio_mockup_debugfs_setup(dev, chip); @@ -275,39 +292,6 @@ static int gpio_mockup_add(struct device *dev, return 0; } -static int gpio_mockup_probe(struct platform_device *pdev) -{ - struct gpio_mockup_platform_data *pdata; - struct gpio_mockup_chip *chip; - int rv, base, ngpio; - struct device *dev; - char *name; - - dev = &pdev->dev; - pdata = dev_get_platdata(dev); - base = pdata->base; - ngpio = pdata->ngpio; - - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; - - name = devm_kasprintf(dev, GFP_KERNEL, "%s-%c", - pdev->name, pdata->index); - if (!name) - return -ENOMEM; - - rv = gpio_mockup_add(dev, chip, name, base, ngpio); - if (rv) { - dev_err(dev, - "adding gpiochip failed (base: %d, ngpio: %d)\n", - base, base < 0 ? ngpio : base + ngpio); - return rv; - } - - return 0; -} - static struct platform_driver gpio_mockup_driver = { .driver = { .name = GPIO_MOCKUP_NAME, -- cgit v1.2.3 From c3196a78281acf51dc339f1344e19918118b724b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:44 +0100 Subject: gpio: mockup: pass the named_lines parameter over platform_data Move the last bits of code dealing with module parameters to the init function. Add a new variable to platform data, which indicates to the probe function if it should name the GPIO lines. If we ever want to make the line naming more fine-grained (e.g. per chip switch) it will be easier this way. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 2dbaeb69d2d2..9da90ad87030 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -68,6 +68,7 @@ struct gpio_mockup_platform_data { int base; int ngpio; int index; + bool named_lines; }; static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; @@ -272,7 +273,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) if (!chip->lines) return -ENOMEM; - if (gpio_mockup_named_lines) { + if (pdata->named_lines) { rv = gpio_mockup_name_lines(dev, chip); if (rv) return rv; @@ -344,6 +345,7 @@ static int __init gpio_mockup_init(void) pdata.ngpio = pdata.base < 0 ? gpio_mockup_ranges[i * 2 + 1] : gpio_mockup_ranges[i * 2 + 1] - pdata.base; + pdata.named_lines = gpio_mockup_named_lines; pdev = platform_device_register_resndata(NULL, GPIO_MOCKUP_NAME, -- cgit v1.2.3 From ca8792af40afb1e83e6910eac1bc48a91694fba3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:45 +0100 Subject: gpio: mockup: extend the debugfs layout Currently each chip has a dedicated directory in debugfs for event triggers. We use the chip's label for the directory name, but the user can't really associate these directories with chip names without parsing the relevant sysfs entries. Use chip names for directory names. For backward compatibility: create links pointing to the actual directories named using the chip labels. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 9da90ad87030..9a880e15ea0b 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -196,17 +196,23 @@ static void gpio_mockup_debugfs_setup(struct device *dev, struct gpio_mockup_chip *chip) { struct gpio_mockup_dbgfs_private *priv; - struct dentry *evfile; + struct dentry *evfile, *link; struct gpio_chip *gc; + const char *devname; char *name; int i; gc = &chip->gc; + devname = dev_name(&gc->gpiodev->dev); - chip->dbg_dir = debugfs_create_dir(gc->label, gpio_mockup_dbg_dir); + chip->dbg_dir = debugfs_create_dir(devname, gpio_mockup_dbg_dir); if (!chip->dbg_dir) goto err; + link = debugfs_create_symlink(gc->label, gpio_mockup_dbg_dir, devname); + if (!link) + goto err; + for (i = 0; i < gc->ngpio; i++) { name = devm_kasprintf(dev, GFP_KERNEL, "%d", i); if (!name) -- cgit v1.2.3 From 5b7908c024ae6e3f21af077d4841d20f9a9567b9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:46 +0100 Subject: gpio: mockup: change the type of value field in line state struct GPIO values are universally represented as integers. Change the type of the variable storing the current line value to int for consistency. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 9a880e15ea0b..8a61328a255d 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -48,7 +48,7 @@ enum { */ struct gpio_mockup_line_status { int dir; - bool value; + int value; }; struct gpio_mockup_chip { -- cgit v1.2.3 From 1cea4734f954ea66f3c9e89199823f6b917a8e7a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:47 +0100 Subject: gpio: mockup: group code by logic Keep GPIO chip callbacks, event trigger callbacks and mockup chip setup code visibly separated. We're mostly good - just need to move the line naming routine below. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 8a61328a255d..dba5e79e3278 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -123,29 +123,6 @@ static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) return chip->lines[offset].dir; } -static int gpio_mockup_name_lines(struct device *dev, - struct gpio_mockup_chip *chip) -{ - struct gpio_chip *gc = &chip->gc; - char **names; - int i; - - names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL); - if (!names) - return -ENOMEM; - - for (i = 0; i < gc->ngpio; i++) { - names[i] = devm_kasprintf(dev, GFP_KERNEL, - "%s-%d", gc->label, i); - if (!names[i]) - return -ENOMEM; - } - - gc->names = (const char *const *)names; - - return 0; -} - static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); @@ -238,6 +215,29 @@ err: dev_err(dev, "error creating debugfs directory\n"); } +static int gpio_mockup_name_lines(struct device *dev, + struct gpio_mockup_chip *chip) +{ + struct gpio_chip *gc = &chip->gc; + char **names; + int i; + + names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL); + if (!names) + return -ENOMEM; + + for (i = 0; i < gc->ngpio; i++) { + names[i] = devm_kasprintf(dev, GFP_KERNEL, + "%s-%d", gc->label, i); + if (!names[i]) + return -ENOMEM; + } + + gc->names = (const char *const *)names; + + return 0; +} + static int gpio_mockup_probe(struct platform_device *pdev) { struct gpio_mockup_platform_data *pdata; -- cgit v1.2.3 From 46526c15cadcba43c69e22a6dfaf3f6f444c70aa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:48 +0100 Subject: gpio: mockup: fix debugfs handling The debugfs routines returning pointers can return NULL or error codes embedded with ERR_PTR(). Check the return values with IS_ERR_OR_NULL(). While we're at it: make the error message more specific so it's not confused with the one emitted when the top-level gpio-mockup debugfs directory creation fails. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index dba5e79e3278..68a0c1e06a16 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -183,11 +183,11 @@ static void gpio_mockup_debugfs_setup(struct device *dev, devname = dev_name(&gc->gpiodev->dev); chip->dbg_dir = debugfs_create_dir(devname, gpio_mockup_dbg_dir); - if (!chip->dbg_dir) + if (IS_ERR_OR_NULL(chip->dbg_dir)) goto err; link = debugfs_create_symlink(gc->label, gpio_mockup_dbg_dir, devname); - if (!link) + if (IS_ERR_OR_NULL(link)) goto err; for (i = 0; i < gc->ngpio; i++) { @@ -205,14 +205,14 @@ static void gpio_mockup_debugfs_setup(struct device *dev, evfile = debugfs_create_file(name, 0200, chip->dbg_dir, priv, &gpio_mockup_event_ops); - if (!evfile) + if (IS_ERR_OR_NULL(evfile)) goto err; } return; err: - dev_err(dev, "error creating debugfs directory\n"); + dev_err(dev, "error creating debugfs event files\n"); } static int gpio_mockup_name_lines(struct device *dev, @@ -336,7 +336,7 @@ static int __init gpio_mockup_init(void) num_chips = gpio_mockup_params_nr / 2; gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); - if (!gpio_mockup_dbg_dir) + if (IS_ERR_OR_NULL(gpio_mockup_dbg_dir)) gpio_mockup_err("error creating debugfs directory\n"); err = platform_driver_register(&gpio_mockup_driver); -- cgit v1.2.3 From fa86963a7546e08efdb32153522c1a42de069c8b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:49 +0100 Subject: gpio: mockup: verify that ngpio > 0 Improve the module params sanitization: bail out from init if the user tries to pass a non-positive number of GPIO lines for any mockup chip. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 68a0c1e06a16..532f5807390b 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -335,6 +335,15 @@ static int __init gpio_mockup_init(void) /* Each chip is described by two values. */ num_chips = gpio_mockup_params_nr / 2; + /* + * The second value in the pair must + * always be greater than 0. + */ + for (i = 0; i < num_chips; i++) { + if (gpio_mockup_ranges[i * 2 + 1] < 0) + return -EINVAL; + } + gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); if (IS_ERR_OR_NULL(gpio_mockup_dbg_dir)) gpio_mockup_err("error creating debugfs directory\n"); -- cgit v1.2.3 From 2a9d742c2699f7d4a7c23f330520d75872810ade Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:50 +0100 Subject: gpio: mockup: tweak line breaks Minor readability tweak: prefer breaking the lines in a way where the second part is longer than the first. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 532f5807390b..dfb9ee03a2f0 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -88,16 +88,16 @@ static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) return chip->lines[offset].value; } -static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset, - int value) +static void gpio_mockup_set(struct gpio_chip *gc, + unsigned int offset, int value) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); chip->lines[offset].value = !!value; } -static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, - int value) +static int gpio_mockup_dirout(struct gpio_chip *gc, + unsigned int offset, int value) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); -- cgit v1.2.3 From c47bee95f62748baed83ca56c6f8c73c830f986e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:51 +0100 Subject: gpio: mockup: implement gpio_mockup_set_multiple() Implement the set_multiple() callback and register it with the gpiolib framework. This is only meant to also test the internal kernel API. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index dfb9ee03a2f0..cbc823e43151 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -96,6 +96,16 @@ static void gpio_mockup_set(struct gpio_chip *gc, chip->lines[offset].value = !!value; } +static void gpio_mockup_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) +{ + unsigned int bit; + + for_each_set_bit(bit, mask, gc->ngpio) + gpio_mockup_set(gc, bit, test_bit(bit, bits)); + +} + static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, int value) { @@ -269,6 +279,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) gc->parent = dev; gc->get = gpio_mockup_get; gc->set = gpio_mockup_set; + gc->set_multiple = gpio_mockup_set_multiple; gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; -- cgit v1.2.3 From 1e0dca677b9c780e90e708ac3f884c54dba94b18 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:52 +0100 Subject: gpio: mockup: modify the return value check for devm_irq_sim_init() As discussed with Marc Zyngier: irq_sim_init() and its devres variant should return the base of the allocated interrupt range on success rather than 0. This will be modified later - first, change the way users handle the return value of these routines. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index cbc823e43151..0abb53038ba8 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -297,7 +297,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) } rv = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio); - if (rv) + if (rv < 0) return rv; rv = devm_gpiochip_add_data(dev, &chip->gc, chip); -- cgit v1.2.3 From e63a006f704ba11928a8393e07a35a5f7d07290c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:53 +0100 Subject: gpio: mockup: rename gpio_mockup_params_nr to gpio_mockup_num_ranges This variable holds the number of mockup GPIO ranges so rename it accordingly. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 0abb53038ba8..a6bfbe58bc63 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -72,8 +72,8 @@ struct gpio_mockup_platform_data { }; static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; -static int gpio_mockup_params_nr; -module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); +static int gpio_mockup_num_ranges; +module_param_array(gpio_mockup_ranges, int, &gpio_mockup_num_ranges, 0400); static bool gpio_mockup_named_lines; module_param_named(gpio_mockup_named_lines, @@ -338,13 +338,13 @@ static int __init gpio_mockup_init(void) struct gpio_mockup_platform_data pdata; struct platform_device *pdev; - if ((gpio_mockup_params_nr < 2) || - (gpio_mockup_params_nr % 2) || - (gpio_mockup_params_nr > GPIO_MOCKUP_MAX_RANGES)) + if ((gpio_mockup_num_ranges < 2) || + (gpio_mockup_num_ranges % 2) || + (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES)) return -EINVAL; /* Each chip is described by two values. */ - num_chips = gpio_mockup_params_nr / 2; + num_chips = gpio_mockup_num_ranges / 2; /* * The second value in the pair must -- cgit v1.2.3 From cd9835f1c67712412be6781157a811b0f177d545 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:48:54 +0100 Subject: gpio: mockup: add helpers for accessing the gpio ranges In order to avoid repeating the calculations on every access - add helpers for gpio base and ngpio components of the ranges array. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index a6bfbe58bc63..ea8c730d8af1 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -81,6 +81,16 @@ module_param_named(gpio_mockup_named_lines, static struct dentry *gpio_mockup_dbg_dir; +static int gpio_mockup_range_base(unsigned int index) +{ + return gpio_mockup_ranges[index * 2]; +} + +static int gpio_mockup_range_ngpio(unsigned int index) +{ + return gpio_mockup_ranges[index * 2 + 1]; +} + static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); @@ -351,7 +361,7 @@ static int __init gpio_mockup_init(void) * always be greater than 0. */ for (i = 0; i < num_chips; i++) { - if (gpio_mockup_ranges[i * 2 + 1] < 0) + if (gpio_mockup_range_ngpio(i) < 0) return -EINVAL; } @@ -367,10 +377,10 @@ static int __init gpio_mockup_init(void) for (i = 0; i < num_chips; i++) { pdata.index = index++; - pdata.base = gpio_mockup_ranges[i * 2]; + pdata.base = gpio_mockup_range_base(i); pdata.ngpio = pdata.base < 0 - ? gpio_mockup_ranges[i * 2 + 1] - : gpio_mockup_ranges[i * 2 + 1] - pdata.base; + ? gpio_mockup_range_ngpio(i) + : gpio_mockup_range_ngpio(i) - pdata.base; pdata.named_lines = gpio_mockup_named_lines; pdev = platform_device_register_resndata(NULL, -- cgit v1.2.3 From 7965f5df85c6bae48d8d63a0528fe681613fe511 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Nov 2017 11:50:51 +0100 Subject: MAINTAINERS: add myself as reviewer for gpio-mockup The user space libgpiod project is now tightly coupled with the gpio-mockup module, so let me know when any changes to it are being proposed. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index aa71ab52fd76..89e2acf95981 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5964,6 +5964,7 @@ F: drivers/media/rc/gpio-ir-tx.c GPIO MOCKUP DRIVER M: Bamvor Jian Zhang +R: Bartosz Golaszewski L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-mockup.c -- cgit v1.2.3 From deb2e713ba54e87320cca4e3a62d3a88477b4ded Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Tue, 28 Nov 2017 04:21:49 +0100 Subject: gpio: it87: fix mojibake in module metadata It had twice-encoded Unicode. Signed-off-by: Adam Borowski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-it87.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index d43d0a2cc4c5..efb46edff81f 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -414,6 +414,6 @@ static void __exit it87_gpio_exit(void) module_init(it87_gpio_init); module_exit(it87_gpio_exit); -MODULE_AUTHOR("Diego Elio Pettenò "); +MODULE_AUTHOR("Diego Elio Pettenò "); MODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e2843cb6cd6ba433d18d87e3e922b6e49ecccf01 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 28 Nov 2017 18:23:39 +0000 Subject: gpio: gpio-stmpe: make various char arrays static const, shrinks object size Don't populate the read-only arrays edge_det_values, rise_values and fall_values on the stack but instead make them static and constify them. Makes the object code smaller by over 240 bytes: Before: text data bss dec hex filename 9525 2520 192 12237 2fcd drivers/gpio/gpio-stmpe.o After: text data bss dec hex filename 9025 2776 192 11993 2ed9 drivers/gpio/gpio-stmpe.o (gcc version 7.2.0 x86_64) Signed-off-by: Colin Ian King Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index e6e5cca624a7..e3d048e65339 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -273,15 +273,21 @@ static void stmpe_dbg_show_one(struct seq_file *s, u8 fall_reg; u8 irqen_reg; - char *edge_det_values[] = {"edge-inactive", - "edge-asserted", - "not-supported"}; - char *rise_values[] = {"no-rising-edge-detection", - "rising-edge-detection", - "not-supported"}; - char *fall_values[] = {"no-falling-edge-detection", - "falling-edge-detection", - "not-supported"}; + static const char * const edge_det_values[] = { + "edge-inactive", + "edge-asserted", + "not-supported" + }; + static const char * const rise_values[] = { + "no-rising-edge-detection", + "rising-edge-detection", + "not-supported" + }; + static const char * const fall_values[] = { + "no-falling-edge-detection", + "falling-edge-detection", + "not-supported" + }; #define NOT_SUPPORTED_IDX 2 u8 edge_det = NOT_SUPPORTED_IDX; u8 rise = NOT_SUPPORTED_IDX; -- cgit v1.2.3 From e10f72bf4b3e8885c1915a119141481e7fc45ca8 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Thu, 30 Nov 2017 14:25:24 +1030 Subject: gpio: gpiolib: Generalise state persistence beyond sleep General support for state persistence is added to gpiolib with the introduction of a new pinconf parameter to propagate the request to hardware. The existing persistence support for sleep is adapted to include hardware support if the GPIO driver provides it. Persistence continues to be enabled by default; in-kernel consumers can opt out, but userspace (currently) does not have a choice. The *_SLEEP_MAY_LOSE_VALUE and *_SLEEP_MAINTAIN_VALUE symbols are renamed, dropping the SLEEP prefix to reflect that the concept is no longer sleep-specific. I feel that renaming to just *_MAY_LOSE_VALUE could initially be misinterpreted, so I've further changed the symbols to *_TRANSITORY and *_PERSISTENT to address this. The sysfs interface is modified only to keep consistency with the chardev interface in enforcing persistence for userspace exports. Signed-off-by: Andrew Jeffery Reviewed-by: Charles Keepax Acked-by: Rob Herring Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 6 ++-- drivers/gpio/gpiolib-sysfs.c | 14 +++++--- drivers/gpio/gpiolib.c | 61 ++++++++++++++++++++++++++++++--- drivers/gpio/gpiolib.h | 2 +- include/dt-bindings/gpio/gpio.h | 6 ++-- include/linux/gpio/consumer.h | 8 +++++ include/linux/gpio/machine.h | 4 +-- include/linux/of_gpio.h | 2 +- include/linux/pinctrl/pinconf-generic.h | 2 ++ 9 files changed, 87 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index e0d59e61b52f..4a2b8d3397c7 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -153,8 +153,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *flags |= GPIO_OPEN_SOURCE; } - if (of_flags & OF_GPIO_SLEEP_MAY_LOSE_VALUE) - *flags |= GPIO_SLEEP_MAY_LOSE_VALUE; + if (of_flags & OF_GPIO_TRANSITORY) + *flags |= GPIO_TRANSITORY; return desc; } @@ -214,6 +214,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, if (xlate_flags & OF_GPIO_ACTIVE_LOW) *lflags |= GPIO_ACTIVE_LOW; + if (xlate_flags & OF_GPIO_TRANSITORY) + *lflags |= GPIO_TRANSITORY; if (of_property_read_bool(np, "input")) *dflags |= GPIOD_IN; diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 3f454eaf2101..0bd472ffb072 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -474,11 +474,15 @@ static ssize_t export_store(struct class *class, status = -ENODEV; goto done; } - status = gpiod_export(desc, true); - if (status < 0) - gpiod_free(desc); - else - set_bit(FLAG_SYSFS, &desc->flags); + + status = gpiod_set_transitory(desc, false); + if (!status) { + status = gpiod_export(desc, true); + if (status < 0) + gpiod_free(desc); + else + set_bit(FLAG_SYSFS, &desc->flags); + } done: if (status) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ec0fd95bbf35..56eec094184c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -514,6 +514,10 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + ret = gpiod_set_transitory(desc, false); + if (ret < 0) + goto out_free_descs; + /* * Lines have to be requested explicitly for input * or output, else the line will be treated "as is". @@ -2529,6 +2533,49 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) } EXPORT_SYMBOL_GPL(gpiod_set_debounce); +/** + * gpiod_set_transitory - Lose or retain GPIO state on suspend or reset + * @desc: descriptor of the GPIO for which to configure persistence + * @transitory: True to lose state on suspend or reset, false for persistence + * + * Returns: + * 0 on success, otherwise a negative error code. + */ +int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +{ + struct gpio_chip *chip; + unsigned long packed; + int gpio; + int rc; + + /* + * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for + * persistence state. + */ + if (transitory) + set_bit(FLAG_TRANSITORY, &desc->flags); + else + clear_bit(FLAG_TRANSITORY, &desc->flags); + + /* If the driver supports it, set the persistence state now */ + chip = desc->gdev->chip; + if (!chip->set_config) + return 0; + + packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, + !transitory); + gpio = gpio_chip_hwgpio(desc); + rc = chip->set_config(chip, gpio, packed); + if (rc == -ENOTSUPP) { + dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", + gpio); + return 0; + } + + return rc; +} +EXPORT_SYMBOL_GPL(gpiod_set_transitory); + /** * gpiod_is_active_low - test whether a GPIO is active-low or not * @desc: the gpio descriptor to test @@ -3116,8 +3163,7 @@ bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset) if (offset >= chip->ngpio) return false; - return !test_bit(FLAG_SLEEP_MAY_LOSE_VALUE, - &chip->gpiodev->descs[offset].flags); + return !test_bit(FLAG_TRANSITORY, &chip->gpiodev->descs[offset].flags); } EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); @@ -3554,8 +3600,10 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if (lflags & GPIO_SLEEP_MAY_LOSE_VALUE) - set_bit(FLAG_SLEEP_MAY_LOSE_VALUE, &desc->flags); + + status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); + if (status < 0) + return status; /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { @@ -3669,6 +3717,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, bool active_low = false; bool single_ended = false; bool open_drain = false; + bool transitory = false; int ret; if (!fwnode) @@ -3683,6 +3732,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, active_low = flags & OF_GPIO_ACTIVE_LOW; single_ended = flags & OF_GPIO_SINGLE_ENDED; open_drain = flags & OF_GPIO_OPEN_DRAIN; + transitory = flags & OF_GPIO_TRANSITORY; } } else if (is_acpi_node(fwnode)) { struct acpi_gpio_info info; @@ -3711,6 +3761,9 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, lflags |= GPIO_OPEN_SOURCE; } + if (transitory) + lflags |= GPIO_TRANSITORY; + ret = gpiod_configure_flags(desc, propname, lflags, dflags); if (ret < 0) { gpiod_put(desc); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index cd4622863fe1..5e1f7cc6eeb6 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -209,7 +209,7 @@ struct gpio_desc { #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ -#define FLAG_SLEEP_MAY_LOSE_VALUE 12 /* GPIO may lose value in sleep */ +#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ /* Connection label */ const char *label; diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h index dd549ff04295..2cc10ae4bbb7 100644 --- a/include/dt-bindings/gpio/gpio.h +++ b/include/dt-bindings/gpio/gpio.h @@ -29,8 +29,8 @@ #define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) #define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE) -/* Bit 3 express GPIO suspend/resume persistence */ -#define GPIO_SLEEP_MAINTAIN_VALUE 0 -#define GPIO_SLEEP_MAY_LOSE_VALUE 8 +/* Bit 3 express GPIO suspend/resume and reset persistence */ +#define GPIO_PERSISTENT 0 +#define GPIO_TRANSITORY 8 #endif diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 7447d85dbe2f..540b2c142493 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -139,6 +139,7 @@ void gpiod_set_raw_array_value_cansleep(unsigned int array_size, int *value_array); int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); +int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); int gpiod_is_active_low(const struct gpio_desc *desc); int gpiod_cansleep(const struct gpio_desc *desc); @@ -431,6 +432,13 @@ static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) return -ENOSYS; } +static inline int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return -ENOSYS; +} + static inline int gpiod_is_active_low(const struct gpio_desc *desc) { /* GPIO can never have been requested */ diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 846be7c69a52..b2f2dc638463 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -10,8 +10,8 @@ enum gpio_lookup_flags { GPIO_ACTIVE_LOW = (1 << 0), GPIO_OPEN_DRAIN = (1 << 1), GPIO_OPEN_SOURCE = (1 << 2), - GPIO_SLEEP_MAINTAIN_VALUE = (0 << 3), - GPIO_SLEEP_MAY_LOSE_VALUE = (1 << 3), + GPIO_PERSISTENT = (0 << 3), + GPIO_TRANSITORY = (1 << 3), }; /** diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 1fe205582111..18a7f03e1182 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -31,7 +31,7 @@ enum of_gpio_flags { OF_GPIO_ACTIVE_LOW = 0x1, OF_GPIO_SINGLE_ENDED = 0x2, OF_GPIO_OPEN_DRAIN = 0x4, - OF_GPIO_SLEEP_MAY_LOSE_VALUE = 0x8, + OF_GPIO_TRANSITORY = 0x8, }; #ifdef CONFIG_OF_GPIO diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index ec6dadcc1fde..6c0680641108 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -94,6 +94,7 @@ * or latch delay (on outputs) this parameter (in a custom format) * specifies the clock skew or latch delay. It typically controls how * many double inverters are put in front of the line. + * @PIN_CONFIG_PERSIST_STATE: retain pin state across sleep or controller reset * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if * you need to pass in custom configurations to the pin controller, use * PIN_CONFIG_END+1 as the base offset. @@ -122,6 +123,7 @@ enum pin_config_param { PIN_CONFIG_SLEEP_HARDWARE_STATE, PIN_CONFIG_SLEW_RATE, PIN_CONFIG_SKEW_DELAY, + PIN_CONFIG_PERSIST_STATE, PIN_CONFIG_END = 0x7F, PIN_CONFIG_MAX = 0xFF, }; -- cgit v1.2.3 From 1b43d26985745901c87e0dca44c9b57896062306 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Thu, 30 Nov 2017 14:25:25 +1030 Subject: gpio: aspeed: Add support for reset tolerance Use the new pinconf parameter for state persistence to expose the associated capability of the Aspeed GPIO controller. Signed-off-by: Andrew Jeffery Reviewed-by: Joel Stanley Signed-off-by: Linus Walleij --- drivers/gpio/gpio-aspeed.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 6b3ca6601af2..30bc97b81bec 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -60,6 +60,7 @@ struct aspeed_gpio_bank { uint16_t val_regs; uint16_t irq_regs; uint16_t debounce_regs; + uint16_t tolerance_regs; const char names[4][3]; }; @@ -70,48 +71,56 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .val_regs = 0x0000, .irq_regs = 0x0008, .debounce_regs = 0x0040, + .tolerance_regs = 0x001c, .names = { "A", "B", "C", "D" }, }, { .val_regs = 0x0020, .irq_regs = 0x0028, .debounce_regs = 0x0048, + .tolerance_regs = 0x003c, .names = { "E", "F", "G", "H" }, }, { .val_regs = 0x0070, .irq_regs = 0x0098, .debounce_regs = 0x00b0, + .tolerance_regs = 0x00ac, .names = { "I", "J", "K", "L" }, }, { .val_regs = 0x0078, .irq_regs = 0x00e8, .debounce_regs = 0x0100, + .tolerance_regs = 0x00fc, .names = { "M", "N", "O", "P" }, }, { .val_regs = 0x0080, .irq_regs = 0x0118, .debounce_regs = 0x0130, + .tolerance_regs = 0x012c, .names = { "Q", "R", "S", "T" }, }, { .val_regs = 0x0088, .irq_regs = 0x0148, .debounce_regs = 0x0160, + .tolerance_regs = 0x015c, .names = { "U", "V", "W", "X" }, }, { .val_regs = 0x01E0, .irq_regs = 0x0178, .debounce_regs = 0x0190, + .tolerance_regs = 0x018c, .names = { "Y", "Z", "AA", "AB" }, }, { - .val_regs = 0x01E8, - .irq_regs = 0x01A8, + .val_regs = 0x01e8, + .irq_regs = 0x01a8, .debounce_regs = 0x01c0, + .tolerance_regs = 0x01bc, .names = { "AC", "", "", "" }, }, }; @@ -534,6 +543,30 @@ static int aspeed_gpio_setup_irqs(struct aspeed_gpio *gpio, return 0; } +static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, + unsigned int offset, bool enable) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(chip); + const struct aspeed_gpio_bank *bank; + unsigned long flags; + u32 val; + + bank = to_bank(offset); + + spin_lock_irqsave(&gpio->lock, flags); + val = readl(gpio->base + bank->tolerance_regs); + + if (enable) + val |= GPIO_BIT(offset); + else + val &= ~GPIO_BIT(offset); + + writel(val, gpio->base + bank->tolerance_regs); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + static int aspeed_gpio_request(struct gpio_chip *chip, unsigned int offset) { if (!have_gpio(gpiochip_get_data(chip), offset)) @@ -771,6 +804,8 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, param == PIN_CONFIG_DRIVE_OPEN_SOURCE) /* Return -ENOTSUPP to trigger emulation, as per datasheet */ return -ENOTSUPP; + else if (param == PIN_CONFIG_PERSIST_STATE) + return aspeed_gpio_reset_tolerance(chip, offset, arg); return -ENOTSUPP; } -- cgit v1.2.3 From 4070a53496ec0c94fda1da0773eee2e5a5bd0084 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sat, 2 Dec 2017 22:31:01 +0530 Subject: gpio: ftgpio010: Fix platform_get_irq's error checking The platform_get_irq() function returns negative if an error occurs. zero or positive number on success. platform_get_irq() error checking for zero is not correct. Signed-off-by: Arvind Yadav Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ftgpio010.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index 7b3394fdc624..b7a3a2db699b 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -176,8 +176,8 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) return PTR_ERR(g->base); irq = platform_get_irq(pdev, 0); - if (!irq) - return -EINVAL; + if (irq <= 0) + return irq ? irq : -EINVAL; ret = bgpio_init(&g->gc, dev, 4, g->base + GPIO_DATA_IN, -- cgit v1.2.3 From 3cac991e33b1395430c09ae49039702eb6748c7d Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:39 +0100 Subject: gpio: axp209: switch unsigned variables to unsigned int Checkpatch complains with the following message: WARNING: Prefer 'unsigned int' to bare use of 'unsigned' Let's make it happy by switching over to unsigned int. Signed-off-by: Quentin Schulz Signed-off-by: Linus Walleij --- drivers/gpio/gpio-axp209.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-axp209.c b/drivers/gpio/gpio-axp209.c index 4a346b7b4172..6ee7dc1418fa 100644 --- a/drivers/gpio/gpio-axp209.c +++ b/drivers/gpio/gpio-axp209.c @@ -32,7 +32,7 @@ struct axp20x_gpio { struct regmap *regmap; }; -static int axp20x_gpio_get_reg(unsigned offset) +static int axp20x_gpio_get_reg(unsigned int offset) { switch (offset) { case 0: @@ -46,7 +46,7 @@ static int axp20x_gpio_get_reg(unsigned offset) return -EINVAL; } -static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset) +static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset) { struct axp20x_gpio *gpio = gpiochip_get_data(chip); int reg; @@ -60,7 +60,7 @@ static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset) AXP20X_GPIO_FUNCTION_INPUT); } -static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset) +static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct axp20x_gpio *gpio = gpiochip_get_data(chip); unsigned int val; @@ -73,7 +73,8 @@ static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(val & BIT(offset + 4)); } -static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +static int axp20x_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) { struct axp20x_gpio *gpio = gpiochip_get_data(chip); unsigned int val; @@ -102,7 +103,7 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) return val & 2; } -static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset, +static int axp20x_gpio_output(struct gpio_chip *chip, unsigned int offset, int value) { struct axp20x_gpio *gpio = gpiochip_get_data(chip); @@ -118,7 +119,7 @@ static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset, : AXP20X_GPIO_FUNCTION_OUT_LOW); } -static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset, +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { axp20x_gpio_output(chip, offset, value); -- cgit v1.2.3 From 449317a8b4c4ed47c2a51f864c4697ae57195b96 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:40 +0100 Subject: pinctrl: move gpio-axp209 to pinctrl To prepare the driver for the upcoming pinctrl features, move the GPIO driver AXP209 from GPIO to pinctrl subsystem. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 6 -- drivers/gpio/Makefile | 1 - drivers/gpio/gpio-axp209.c | 189 --------------------------------------- drivers/pinctrl/Kconfig | 10 +++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-axp209.c | 189 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 196 deletions(-) delete mode 100644 drivers/gpio/gpio-axp209.c create mode 100644 drivers/pinctrl/pinctrl-axp209.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d6a8e851ad13..395669bfcc26 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -122,12 +122,6 @@ config GPIO_ATH79 Select this option to enable GPIO driver for Atheros AR71XX/AR724X/AR913X SoC devices. -config GPIO_AXP209 - tristate "X-Powers AXP209 PMIC GPIO Support" - depends on MFD_AXP20X - help - Say yes to enable GPIO support for the AXP209 PMIC - config GPIO_BCM_KONA bool "Broadcom Kona GPIO" depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST) diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4bc24febb889..bc5dd673fa11 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -32,7 +32,6 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o -obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o diff --git a/drivers/gpio/gpio-axp209.c b/drivers/gpio/gpio-axp209.c deleted file mode 100644 index 6ee7dc1418fa..000000000000 --- a/drivers/gpio/gpio-axp209.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * AXP20x GPIO driver - * - * Copyright (C) 2016 Maxime Ripard - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AXP20X_GPIO_FUNCTIONS 0x7 -#define AXP20X_GPIO_FUNCTION_OUT_LOW 0 -#define AXP20X_GPIO_FUNCTION_OUT_HIGH 1 -#define AXP20X_GPIO_FUNCTION_INPUT 2 - -struct axp20x_gpio { - struct gpio_chip chip; - struct regmap *regmap; -}; - -static int axp20x_gpio_get_reg(unsigned int offset) -{ - switch (offset) { - case 0: - return AXP20X_GPIO0_CTRL; - case 1: - return AXP20X_GPIO1_CTRL; - case 2: - return AXP20X_GPIO2_CTRL; - } - - return -EINVAL; -} - -static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - int reg; - - reg = axp20x_gpio_get_reg(offset); - if (reg < 0) - return reg; - - return regmap_update_bits(gpio->regmap, reg, - AXP20X_GPIO_FUNCTIONS, - AXP20X_GPIO_FUNCTION_INPUT); -} - -static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - unsigned int val; - int ret; - - ret = regmap_read(gpio->regmap, AXP20X_GPIO20_SS, &val); - if (ret) - return ret; - - return !!(val & BIT(offset + 4)); -} - -static int axp20x_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - unsigned int val; - int reg, ret; - - reg = axp20x_gpio_get_reg(offset); - if (reg < 0) - return reg; - - ret = regmap_read(gpio->regmap, reg, &val); - if (ret) - return ret; - - /* - * This shouldn't really happen if the pin is in use already, - * or if it's not in use yet, it doesn't matter since we're - * going to change the value soon anyway. Default to output. - */ - if ((val & AXP20X_GPIO_FUNCTIONS) > 2) - return 0; - - /* - * The GPIO directions are the three lowest values. - * 2 is input, 0 and 1 are output - */ - return val & 2; -} - -static int axp20x_gpio_output(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - int reg; - - reg = axp20x_gpio_get_reg(offset); - if (reg < 0) - return reg; - - return regmap_update_bits(gpio->regmap, reg, - AXP20X_GPIO_FUNCTIONS, - value ? AXP20X_GPIO_FUNCTION_OUT_HIGH - : AXP20X_GPIO_FUNCTION_OUT_LOW); -} - -static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - axp20x_gpio_output(chip, offset, value); -} - -static int axp20x_gpio_probe(struct platform_device *pdev) -{ - struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); - struct axp20x_gpio *gpio; - int ret; - - if (!of_device_is_available(pdev->dev.of_node)) - return -ENODEV; - - if (!axp20x) { - dev_err(&pdev->dev, "Parent drvdata not set\n"); - return -EINVAL; - } - - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) - return -ENOMEM; - - gpio->chip.base = -1; - gpio->chip.can_sleep = true; - gpio->chip.parent = &pdev->dev; - gpio->chip.label = dev_name(&pdev->dev); - gpio->chip.owner = THIS_MODULE; - gpio->chip.get = axp20x_gpio_get; - gpio->chip.get_direction = axp20x_gpio_get_direction; - gpio->chip.set = axp20x_gpio_set; - gpio->chip.direction_input = axp20x_gpio_input; - gpio->chip.direction_output = axp20x_gpio_output; - gpio->chip.ngpio = 3; - - gpio->regmap = axp20x->regmap; - - ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); - if (ret) { - dev_err(&pdev->dev, "Failed to register GPIO chip\n"); - return ret; - } - - dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n"); - - return 0; -} - -static const struct of_device_id axp20x_gpio_match[] = { - { .compatible = "x-powers,axp209-gpio" }, - { } -}; -MODULE_DEVICE_TABLE(of, axp20x_gpio_match); - -static struct platform_driver axp20x_gpio_driver = { - .probe = axp20x_gpio_probe, - .driver = { - .name = "axp20x-gpio", - .of_match_table = axp20x_gpio_match, - }, -}; - -module_platform_driver(axp20x_gpio_driver); - -MODULE_AUTHOR("Maxime Ripard "); -MODULE_DESCRIPTION("AXP20x PMIC GPIO driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 4571cc098b76..ce126955212c 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -63,6 +63,16 @@ config PINCTRL_AS3722 open drain configuration for the GPIO pins of AS3722 devices. It also supports the GPIO functionality through gpiolib. +config PINCTRL_AXP209 + tristate "X-Powers AXP209 PMIC pinctrl and GPIO Support" + depends on MFD_AXP20X + help + AXP PMICs provides multiple GPIOs that can be muxed for different + functions. This driver bundles a pinctrl driver to select the function + muxing and a GPIO driver to handle the GPIO when the GPIO function is + selected. + Say yes to enable pinctrl and GPIO support for the AXP209 PMIC + config PINCTRL_BF54x def_bool y if BF54x select PINCTRL_ADI2 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index d0d4844f8022..4777f1595ce2 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o obj-$(CONFIG_PINCTRL_ADI2) += pinctrl-adi2.o obj-$(CONFIG_PINCTRL_ARTPEC6) += pinctrl-artpec6.o obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o +obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o obj-$(CONFIG_PINCTRL_BF54x) += pinctrl-adi2-bf54x.o obj-$(CONFIG_PINCTRL_BF60x) += pinctrl-adi2-bf60x.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c new file mode 100644 index 000000000000..6ee7dc1418fa --- /dev/null +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -0,0 +1,189 @@ +/* + * AXP20x GPIO driver + * + * Copyright (C) 2016 Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXP20X_GPIO_FUNCTIONS 0x7 +#define AXP20X_GPIO_FUNCTION_OUT_LOW 0 +#define AXP20X_GPIO_FUNCTION_OUT_HIGH 1 +#define AXP20X_GPIO_FUNCTION_INPUT 2 + +struct axp20x_gpio { + struct gpio_chip chip; + struct regmap *regmap; +}; + +static int axp20x_gpio_get_reg(unsigned int offset) +{ + switch (offset) { + case 0: + return AXP20X_GPIO0_CTRL; + case 1: + return AXP20X_GPIO1_CTRL; + case 2: + return AXP20X_GPIO2_CTRL; + } + + return -EINVAL; +} + +static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + int reg; + + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return reg; + + return regmap_update_bits(gpio->regmap, reg, + AXP20X_GPIO_FUNCTIONS, + AXP20X_GPIO_FUNCTION_INPUT); +} + +static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(gpio->regmap, AXP20X_GPIO20_SS, &val); + if (ret) + return ret; + + return !!(val & BIT(offset + 4)); +} + +static int axp20x_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + unsigned int val; + int reg, ret; + + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return reg; + + ret = regmap_read(gpio->regmap, reg, &val); + if (ret) + return ret; + + /* + * This shouldn't really happen if the pin is in use already, + * or if it's not in use yet, it doesn't matter since we're + * going to change the value soon anyway. Default to output. + */ + if ((val & AXP20X_GPIO_FUNCTIONS) > 2) + return 0; + + /* + * The GPIO directions are the three lowest values. + * 2 is input, 0 and 1 are output + */ + return val & 2; +} + +static int axp20x_gpio_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + int reg; + + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return reg; + + return regmap_update_bits(gpio->regmap, reg, + AXP20X_GPIO_FUNCTIONS, + value ? AXP20X_GPIO_FUNCTION_OUT_HIGH + : AXP20X_GPIO_FUNCTION_OUT_LOW); +} + +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + axp20x_gpio_output(chip, offset, value); +} + +static int axp20x_gpio_probe(struct platform_device *pdev) +{ + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct axp20x_gpio *gpio; + int ret; + + if (!of_device_is_available(pdev->dev.of_node)) + return -ENODEV; + + if (!axp20x) { + dev_err(&pdev->dev, "Parent drvdata not set\n"); + return -EINVAL; + } + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->chip.base = -1; + gpio->chip.can_sleep = true; + gpio->chip.parent = &pdev->dev; + gpio->chip.label = dev_name(&pdev->dev); + gpio->chip.owner = THIS_MODULE; + gpio->chip.get = axp20x_gpio_get; + gpio->chip.get_direction = axp20x_gpio_get_direction; + gpio->chip.set = axp20x_gpio_set; + gpio->chip.direction_input = axp20x_gpio_input; + gpio->chip.direction_output = axp20x_gpio_output; + gpio->chip.ngpio = 3; + + gpio->regmap = axp20x->regmap; + + ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (ret) { + dev_err(&pdev->dev, "Failed to register GPIO chip\n"); + return ret; + } + + dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n"); + + return 0; +} + +static const struct of_device_id axp20x_gpio_match[] = { + { .compatible = "x-powers,axp209-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, axp20x_gpio_match); + +static struct platform_driver axp20x_gpio_driver = { + .probe = axp20x_gpio_probe, + .driver = { + .name = "axp20x-gpio", + .of_match_table = axp20x_gpio_match, + }, +}; + +module_platform_driver(axp20x_gpio_driver); + +MODULE_AUTHOR("Maxime Ripard "); +MODULE_DESCRIPTION("AXP20x PMIC GPIO driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 23f75d7dfa92132e08e31640e0f641dcdea781b9 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:41 +0100 Subject: pinctrl: axp209: add pinctrl features The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO, an ADC or a LDO regulator. GPIO2 can only act as a GPIO. This adds the pinctrl features to the driver so GPIO0/1 can be used as ADC or LDO regulator. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-axp209.c | 304 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 284 insertions(+), 20 deletions(-) diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index 6ee7dc1418fa..48cb57dcc575 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -1,7 +1,8 @@ /* - * AXP20x GPIO driver + * AXP20x pinctrl and GPIO driver * * Copyright (C) 2016 Maxime Ripard + * Copyright (C) 2017 Quentin Schulz * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -18,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -27,9 +31,52 @@ #define AXP20X_GPIO_FUNCTION_OUT_HIGH 1 #define AXP20X_GPIO_FUNCTION_INPUT 2 +#define AXP20X_FUNC_GPIO_OUT 0 +#define AXP20X_FUNC_GPIO_IN 1 +#define AXP20X_FUNC_LDO 2 +#define AXP20X_FUNC_ADC 3 +#define AXP20X_FUNCS_NB 4 + +#define AXP20X_MUX_GPIO_OUT 0 +#define AXP20X_MUX_GPIO_IN BIT(1) +#define AXP20X_MUX_ADC BIT(2) + +struct axp20x_pctrl_desc { + const struct pinctrl_pin_desc *pins; + unsigned int npins; + /* Stores the pins supporting LDO function. Bit offset is pin number. */ + u8 ldo_mask; + /* Stores the pins supporting ADC function. Bit offset is pin number. */ + u8 adc_mask; +}; + +struct axp20x_pinctrl_function { + const char *name; + unsigned int muxval; + const char **groups; + unsigned int ngroups; +}; + struct axp20x_gpio { struct gpio_chip chip; struct regmap *regmap; + struct pinctrl_dev *pctl_dev; + struct device *dev; + const struct axp20x_pctrl_desc *desc; + struct axp20x_pinctrl_function funcs[AXP20X_FUNCS_NB]; +}; + +static const struct pinctrl_pin_desc axp209_pins[] = { + PINCTRL_PIN(0, "GPIO0"), + PINCTRL_PIN(1, "GPIO1"), + PINCTRL_PIN(2, "GPIO2"), +}; + +static const struct axp20x_pctrl_desc axp20x_data = { + .pins = axp209_pins, + .npins = ARRAY_SIZE(axp209_pins), + .ldo_mask = BIT(0) | BIT(1), + .adc_mask = BIT(0) | BIT(1), }; static int axp20x_gpio_get_reg(unsigned int offset) @@ -48,16 +95,7 @@ static int axp20x_gpio_get_reg(unsigned int offset) static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset) { - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - int reg; - - reg = axp20x_gpio_get_reg(offset); - if (reg < 0) - return reg; - - return regmap_update_bits(gpio->regmap, reg, - AXP20X_GPIO_FUNCTIONS, - AXP20X_GPIO_FUNCTION_INPUT); + return pinctrl_gpio_direction_input(chip->base + offset); } static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -105,30 +143,221 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, static int axp20x_gpio_output(struct gpio_chip *chip, unsigned int offset, int value) +{ + chip->set(chip, offset, value); + + return 0; +} + +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct axp20x_gpio *gpio = gpiochip_get_data(chip); int reg; + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return; + + regmap_update_bits(gpio->regmap, reg, + AXP20X_GPIO_FUNCTIONS, + value ? AXP20X_GPIO_FUNCTION_OUT_HIGH : + AXP20X_GPIO_FUNCTION_OUT_LOW); +} + +static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset, + u8 config) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + int reg; + reg = axp20x_gpio_get_reg(offset); if (reg < 0) return reg; - return regmap_update_bits(gpio->regmap, reg, - AXP20X_GPIO_FUNCTIONS, - value ? AXP20X_GPIO_FUNCTION_OUT_HIGH - : AXP20X_GPIO_FUNCTION_OUT_LOW); + return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS, + config); } -static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + + return ARRAY_SIZE(gpio->funcs); +} + +static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + + return gpio->funcs[selector].name; +} + +static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int *num_groups) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + + *groups = gpio->funcs[selector].groups; + *num_groups = gpio->funcs[selector].ngroups; + + return 0; +} + +static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + unsigned int mask; + + /* Every pin supports GPIO_OUT and GPIO_IN functions */ + if (function <= AXP20X_FUNC_GPIO_IN) + return axp20x_pmx_set(pctldev, group, + gpio->funcs[function].muxval); + + if (function == AXP20X_FUNC_LDO) + mask = gpio->desc->ldo_mask; + else + mask = gpio->desc->adc_mask; + + if (!(BIT(group) & mask)) + return -EINVAL; + + /* + * We let the regulator framework handle the LDO muxing as muxing bits + * are basically also regulators on/off bits. It's better not to enforce + * any state of the regulator when selecting LDO mux so that we don't + * interfere with the regulator driver. + */ + if (function == AXP20X_FUNC_LDO) + return 0; + + return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval); +} + +static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, bool input) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + + if (input) + return axp20x_pmx_set(pctldev, offset, + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval); + + return axp20x_pmx_set(pctldev, offset, + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval); +} + +static const struct pinmux_ops axp20x_pmx_ops = { + .get_functions_count = axp20x_pmx_func_cnt, + .get_function_name = axp20x_pmx_func_name, + .get_function_groups = axp20x_pmx_func_groups, + .set_mux = axp20x_pmx_set_mux, + .gpio_set_direction = axp20x_pmx_gpio_set_direction, + .strict = true, +}; + +static int axp20x_groups_cnt(struct pinctrl_dev *pctldev) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + + return gpio->desc->npins; +} + +static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, + const unsigned int **pins, unsigned int *num_pins) +{ + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + + *pins = (unsigned int *)&gpio->desc->pins[selector]; + *num_pins = 1; + + return 0; +} + +static const char *axp20x_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) { - axp20x_gpio_output(chip, offset, value); + struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + + return gpio->desc->pins[selector].name; +} + +static const struct pinctrl_ops axp20x_pctrl_ops = { + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinconf_generic_dt_free_map, + .get_groups_count = axp20x_groups_cnt, + .get_group_name = axp20x_group_name, + .get_group_pins = axp20x_group_pins, +}; + +static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask, + unsigned int mask_len, + struct axp20x_pinctrl_function *func, + const struct pinctrl_pin_desc *pins) +{ + unsigned long int mask_cpy = mask; + const char **group; + unsigned int ngroups = hweight8(mask); + int bit; + + func->ngroups = ngroups; + if (func->ngroups > 0) { + func->groups = devm_kzalloc(dev, ngroups * sizeof(const char *), + GFP_KERNEL); + group = func->groups; + for_each_set_bit(bit, &mask_cpy, mask_len) { + *group = pins[bit].name; + group++; + } + } +} + +static void axp20x_build_funcs_groups(struct platform_device *pdev) +{ + struct axp20x_gpio *gpio = platform_get_drvdata(pdev); + int i, pin, npins = gpio->desc->npins; + + gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out"; + gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = AXP20X_MUX_GPIO_OUT; + gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in"; + gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = AXP20X_MUX_GPIO_IN; + gpio->funcs[AXP20X_FUNC_LDO].name = "ldo"; + /* + * Muxval for LDO is useless as we won't use it. + * See comment in axp20x_pmx_set_mux. + */ + gpio->funcs[AXP20X_FUNC_ADC].name = "adc"; + gpio->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC; + + /* Every pin supports GPIO_OUT and GPIO_IN functions */ + for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) { + gpio->funcs[i].ngroups = npins; + gpio->funcs[i].groups = devm_kzalloc(&pdev->dev, + npins * sizeof(char *), + GFP_KERNEL); + for (pin = 0; pin < npins; pin++) + gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name; + } + + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask, + npins, &gpio->funcs[AXP20X_FUNC_LDO], + gpio->desc->pins); + + axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask, + npins, &gpio->funcs[AXP20X_FUNC_ADC], + gpio->desc->pins); } static int axp20x_gpio_probe(struct platform_device *pdev) { struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct axp20x_gpio *gpio; + struct pinctrl_desc *pctrl_desc; int ret; if (!of_device_is_available(pdev->dev.of_node)) @@ -145,6 +374,8 @@ static int axp20x_gpio_probe(struct platform_device *pdev) gpio->chip.base = -1; gpio->chip.can_sleep = true; + gpio->chip.request = gpiochip_generic_request; + gpio->chip.free = gpiochip_generic_free; gpio->chip.parent = &pdev->dev; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.owner = THIS_MODULE; @@ -155,7 +386,30 @@ static int axp20x_gpio_probe(struct platform_device *pdev) gpio->chip.direction_output = axp20x_gpio_output; gpio->chip.ngpio = 3; + gpio->desc = &axp20x_data; gpio->regmap = axp20x->regmap; + gpio->dev = &pdev->dev; + + platform_set_drvdata(pdev, gpio); + + axp20x_build_funcs_groups(pdev); + + pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL); + if (!pctrl_desc) + return -ENOMEM; + + pctrl_desc->name = dev_name(&pdev->dev); + pctrl_desc->owner = THIS_MODULE; + pctrl_desc->pins = gpio->desc->pins; + pctrl_desc->npins = gpio->desc->npins; + pctrl_desc->pctlops = &axp20x_pctrl_ops; + pctrl_desc->pmxops = &axp20x_pmx_ops; + + gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio); + if (IS_ERR(gpio->pctl_dev)) { + dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); + return PTR_ERR(gpio->pctl_dev); + } ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); if (ret) { @@ -163,7 +417,16 @@ static int axp20x_gpio_probe(struct platform_device *pdev) return ret; } - dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n"); + ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev), + gpio->desc->pins->number, + gpio->desc->pins->number, + gpio->desc->npins); + if (ret) { + dev_err(&pdev->dev, "failed to add pin range\n"); + return ret; + } + + dev_info(&pdev->dev, "AXP209 pinctrl and GPIO driver loaded\n"); return 0; } @@ -185,5 +448,6 @@ static struct platform_driver axp20x_gpio_driver = { module_platform_driver(axp20x_gpio_driver); MODULE_AUTHOR("Maxime Ripard "); -MODULE_DESCRIPTION("AXP20x PMIC GPIO driver"); +MODULE_AUTHOR("Quentin Schulz "); +MODULE_DESCRIPTION("AXP20x PMIC pinctrl and GPIO driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f17aea430fff4b95ac94e8625096cd82544e9054 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:42 +0100 Subject: dt-bindings: gpio: gpio-axp209: add pinctrl features The X-Powers AXP209 has 3 GPIOs. GPIO0/1 can each act either as a GPIO, an ADC or a LDO regulator. GPIO2 can only act as a GPIO. This adds the pinctrl features to the driver so GPIO0/1 can be used as ADC or LDO regulator. Signed-off-by: Quentin Schulz Acked-by: Rob Herring Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/gpio-axp209.txt | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-axp209.txt b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt index a6611304dd3c..0d77597c3f92 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-axp209.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt @@ -1,8 +1,13 @@ -AXP209 GPIO controller +AXP209 GPIO & pinctrl controller This driver follows the usual GPIO bindings found in Documentation/devicetree/bindings/gpio/gpio.txt +This driver follows the usual pinctrl bindings found in +Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + +This driver employs the per-pin muxing pattern. + Required properties: - compatible: Should be "x-powers,axp209-gpio" - #gpio-cells: Should be two. The first cell is the pin number and the @@ -28,3 +33,32 @@ axp209: pmic@34 { #gpio-cells = <2>; }; }; + +The GPIOs can be muxed to other functions and therefore, must be a subnode of +axp_gpio. + +Example: + +&axp_gpio { + gpio0_adc: gpio0-adc { + pins = "GPIO0"; + function = "adc"; + }; +}; + +&example_node { + pinctrl-names = "default"; + pinctrl-0 = <&gpio0_adc>; +}; + +GPIOs and their functions +------------------------- + +Each GPIO is independent from the other (i.e. GPIO0 in gpio_in function does +not force GPIO1 and GPIO2 to be in gpio_in function as well). + +GPIO | Functions +------------------------ +GPIO0 | gpio_in, gpio_out, ldo, adc +GPIO1 | gpio_in, gpio_out, ldo, adc +GPIO2 | gpio_in, gpio_out -- cgit v1.2.3 From d242e60c7d59fb95a9349d1487cfb3ddf6eebdde Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:43 +0100 Subject: pinctrl: axp209: rename everything from gpio to pctl This driver used to do only GPIO features of the GPIOs in X-Powers AXP20X. Now that we have migrated everything to the pinctrl subsystem and added pinctrl features, rename everything related to pinctrl from gpio to pctl to ease the understanding of differences between GPIO and pinctrl features. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-axp209.c | 176 +++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index 48cb57dcc575..17eeb0410a5b 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -57,7 +57,7 @@ struct axp20x_pinctrl_function { unsigned int ngroups; }; -struct axp20x_gpio { +struct axp20x_pctl { struct gpio_chip chip; struct regmap *regmap; struct pinctrl_dev *pctl_dev; @@ -100,11 +100,11 @@ static int axp20x_gpio_input(struct gpio_chip *chip, unsigned int offset) static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) { - struct axp20x_gpio *gpio = gpiochip_get_data(chip); + struct axp20x_pctl *pctl = gpiochip_get_data(chip); unsigned int val; int ret; - ret = regmap_read(gpio->regmap, AXP20X_GPIO20_SS, &val); + ret = regmap_read(pctl->regmap, AXP20X_GPIO20_SS, &val); if (ret) return ret; @@ -114,7 +114,7 @@ static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - struct axp20x_gpio *gpio = gpiochip_get_data(chip); + struct axp20x_pctl *pctl = gpiochip_get_data(chip); unsigned int val; int reg, ret; @@ -122,7 +122,7 @@ static int axp20x_gpio_get_direction(struct gpio_chip *chip, if (reg < 0) return reg; - ret = regmap_read(gpio->regmap, reg, &val); + ret = regmap_read(pctl->regmap, reg, &val); if (ret) return ret; @@ -152,14 +152,14 @@ static int axp20x_gpio_output(struct gpio_chip *chip, unsigned int offset, static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { - struct axp20x_gpio *gpio = gpiochip_get_data(chip); + struct axp20x_pctl *pctl = gpiochip_get_data(chip); int reg; reg = axp20x_gpio_get_reg(offset); if (reg < 0) return; - regmap_update_bits(gpio->regmap, reg, + regmap_update_bits(pctl->regmap, reg, AXP20X_GPIO_FUNCTIONS, value ? AXP20X_GPIO_FUNCTION_OUT_HIGH : AXP20X_GPIO_FUNCTION_OUT_LOW); @@ -168,30 +168,30 @@ static void axp20x_gpio_set(struct gpio_chip *chip, unsigned int offset, static int axp20x_pmx_set(struct pinctrl_dev *pctldev, unsigned int offset, u8 config) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); int reg; reg = axp20x_gpio_get_reg(offset); if (reg < 0) return reg; - return regmap_update_bits(gpio->regmap, reg, AXP20X_GPIO_FUNCTIONS, + return regmap_update_bits(pctl->regmap, reg, AXP20X_GPIO_FUNCTIONS, config); } static int axp20x_pmx_func_cnt(struct pinctrl_dev *pctldev) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); - return ARRAY_SIZE(gpio->funcs); + return ARRAY_SIZE(pctl->funcs); } static const char *axp20x_pmx_func_name(struct pinctrl_dev *pctldev, unsigned int selector) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); - return gpio->funcs[selector].name; + return pctl->funcs[selector].name; } static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev, @@ -199,10 +199,10 @@ static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev, const char * const **groups, unsigned int *num_groups) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); - *groups = gpio->funcs[selector].groups; - *num_groups = gpio->funcs[selector].ngroups; + *groups = pctl->funcs[selector].groups; + *num_groups = pctl->funcs[selector].ngroups; return 0; } @@ -210,18 +210,18 @@ static int axp20x_pmx_func_groups(struct pinctrl_dev *pctldev, static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned int function, unsigned int group) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); unsigned int mask; /* Every pin supports GPIO_OUT and GPIO_IN functions */ if (function <= AXP20X_FUNC_GPIO_IN) return axp20x_pmx_set(pctldev, group, - gpio->funcs[function].muxval); + pctl->funcs[function].muxval); if (function == AXP20X_FUNC_LDO) - mask = gpio->desc->ldo_mask; + mask = pctl->desc->ldo_mask; else - mask = gpio->desc->adc_mask; + mask = pctl->desc->adc_mask; if (!(BIT(group) & mask)) return -EINVAL; @@ -235,21 +235,21 @@ static int axp20x_pmx_set_mux(struct pinctrl_dev *pctldev, if (function == AXP20X_FUNC_LDO) return 0; - return axp20x_pmx_set(pctldev, group, gpio->funcs[function].muxval); + return axp20x_pmx_set(pctldev, group, pctl->funcs[function].muxval); } static int axp20x_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned int offset, bool input) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); if (input) return axp20x_pmx_set(pctldev, offset, - gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval); + pctl->funcs[AXP20X_FUNC_GPIO_IN].muxval); return axp20x_pmx_set(pctldev, offset, - gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval); + pctl->funcs[AXP20X_FUNC_GPIO_OUT].muxval); } static const struct pinmux_ops axp20x_pmx_ops = { @@ -263,17 +263,17 @@ static const struct pinmux_ops axp20x_pmx_ops = { static int axp20x_groups_cnt(struct pinctrl_dev *pctldev) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); - return gpio->desc->npins; + return pctl->desc->npins; } static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, const unsigned int **pins, unsigned int *num_pins) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); - *pins = (unsigned int *)&gpio->desc->pins[selector]; + *pins = (unsigned int *)&pctl->desc->pins[selector]; *num_pins = 1; return 0; @@ -282,9 +282,9 @@ static int axp20x_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, static const char *axp20x_group_name(struct pinctrl_dev *pctldev, unsigned int selector) { - struct axp20x_gpio *gpio = pinctrl_dev_get_drvdata(pctldev); + struct axp20x_pctl *pctl = pinctrl_dev_get_drvdata(pctldev); - return gpio->desc->pins[selector].name; + return pctl->desc->pins[selector].name; } static const struct pinctrl_ops axp20x_pctrl_ops = { @@ -319,44 +319,44 @@ static void axp20x_funcs_groups_from_mask(struct device *dev, unsigned int mask, static void axp20x_build_funcs_groups(struct platform_device *pdev) { - struct axp20x_gpio *gpio = platform_get_drvdata(pdev); - int i, pin, npins = gpio->desc->npins; - - gpio->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out"; - gpio->funcs[AXP20X_FUNC_GPIO_OUT].muxval = AXP20X_MUX_GPIO_OUT; - gpio->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in"; - gpio->funcs[AXP20X_FUNC_GPIO_IN].muxval = AXP20X_MUX_GPIO_IN; - gpio->funcs[AXP20X_FUNC_LDO].name = "ldo"; + struct axp20x_pctl *pctl = platform_get_drvdata(pdev); + int i, pin, npins = pctl->desc->npins; + + pctl->funcs[AXP20X_FUNC_GPIO_OUT].name = "gpio_out"; + pctl->funcs[AXP20X_FUNC_GPIO_OUT].muxval = AXP20X_MUX_GPIO_OUT; + pctl->funcs[AXP20X_FUNC_GPIO_IN].name = "gpio_in"; + pctl->funcs[AXP20X_FUNC_GPIO_IN].muxval = AXP20X_MUX_GPIO_IN; + pctl->funcs[AXP20X_FUNC_LDO].name = "ldo"; /* * Muxval for LDO is useless as we won't use it. * See comment in axp20x_pmx_set_mux. */ - gpio->funcs[AXP20X_FUNC_ADC].name = "adc"; - gpio->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC; + pctl->funcs[AXP20X_FUNC_ADC].name = "adc"; + pctl->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC; /* Every pin supports GPIO_OUT and GPIO_IN functions */ for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) { - gpio->funcs[i].ngroups = npins; - gpio->funcs[i].groups = devm_kzalloc(&pdev->dev, + pctl->funcs[i].ngroups = npins; + pctl->funcs[i].groups = devm_kzalloc(&pdev->dev, npins * sizeof(char *), GFP_KERNEL); for (pin = 0; pin < npins; pin++) - gpio->funcs[i].groups[pin] = gpio->desc->pins[pin].name; + pctl->funcs[i].groups[pin] = pctl->desc->pins[pin].name; } - axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->ldo_mask, - npins, &gpio->funcs[AXP20X_FUNC_LDO], - gpio->desc->pins); + axp20x_funcs_groups_from_mask(&pdev->dev, pctl->desc->ldo_mask, + npins, &pctl->funcs[AXP20X_FUNC_LDO], + pctl->desc->pins); - axp20x_funcs_groups_from_mask(&pdev->dev, gpio->desc->adc_mask, - npins, &gpio->funcs[AXP20X_FUNC_ADC], - gpio->desc->pins); + axp20x_funcs_groups_from_mask(&pdev->dev, pctl->desc->adc_mask, + npins, &pctl->funcs[AXP20X_FUNC_ADC], + pctl->desc->pins); } -static int axp20x_gpio_probe(struct platform_device *pdev) +static int axp20x_pctl_probe(struct platform_device *pdev) { struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); - struct axp20x_gpio *gpio; + struct axp20x_pctl *pctl; struct pinctrl_desc *pctrl_desc; int ret; @@ -368,29 +368,29 @@ static int axp20x_gpio_probe(struct platform_device *pdev) return -EINVAL; } - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) + pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); + if (!pctl) return -ENOMEM; - gpio->chip.base = -1; - gpio->chip.can_sleep = true; - gpio->chip.request = gpiochip_generic_request; - gpio->chip.free = gpiochip_generic_free; - gpio->chip.parent = &pdev->dev; - gpio->chip.label = dev_name(&pdev->dev); - gpio->chip.owner = THIS_MODULE; - gpio->chip.get = axp20x_gpio_get; - gpio->chip.get_direction = axp20x_gpio_get_direction; - gpio->chip.set = axp20x_gpio_set; - gpio->chip.direction_input = axp20x_gpio_input; - gpio->chip.direction_output = axp20x_gpio_output; - gpio->chip.ngpio = 3; - - gpio->desc = &axp20x_data; - gpio->regmap = axp20x->regmap; - gpio->dev = &pdev->dev; - - platform_set_drvdata(pdev, gpio); + pctl->chip.base = -1; + pctl->chip.can_sleep = true; + pctl->chip.request = gpiochip_generic_request; + pctl->chip.free = gpiochip_generic_free; + pctl->chip.parent = &pdev->dev; + pctl->chip.label = dev_name(&pdev->dev); + pctl->chip.owner = THIS_MODULE; + pctl->chip.get = axp20x_gpio_get; + pctl->chip.get_direction = axp20x_gpio_get_direction; + pctl->chip.set = axp20x_gpio_set; + pctl->chip.direction_input = axp20x_gpio_input; + pctl->chip.direction_output = axp20x_gpio_output; + pctl->chip.ngpio = 3; + + pctl->desc = &axp20x_data; + pctl->regmap = axp20x->regmap; + pctl->dev = &pdev->dev; + + platform_set_drvdata(pdev, pctl); axp20x_build_funcs_groups(pdev); @@ -400,27 +400,27 @@ static int axp20x_gpio_probe(struct platform_device *pdev) pctrl_desc->name = dev_name(&pdev->dev); pctrl_desc->owner = THIS_MODULE; - pctrl_desc->pins = gpio->desc->pins; - pctrl_desc->npins = gpio->desc->npins; + pctrl_desc->pins = pctl->desc->pins; + pctrl_desc->npins = pctl->desc->npins; pctrl_desc->pctlops = &axp20x_pctrl_ops; pctrl_desc->pmxops = &axp20x_pmx_ops; - gpio->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, gpio); - if (IS_ERR(gpio->pctl_dev)) { + pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, pctl); + if (IS_ERR(pctl->pctl_dev)) { dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); - return PTR_ERR(gpio->pctl_dev); + return PTR_ERR(pctl->pctl_dev); } - ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + ret = devm_gpiochip_add_data(&pdev->dev, &pctl->chip, pctl); if (ret) { dev_err(&pdev->dev, "Failed to register GPIO chip\n"); return ret; } - ret = gpiochip_add_pin_range(&gpio->chip, dev_name(&pdev->dev), - gpio->desc->pins->number, - gpio->desc->pins->number, - gpio->desc->npins); + ret = gpiochip_add_pin_range(&pctl->chip, dev_name(&pdev->dev), + pctl->desc->pins->number, + pctl->desc->pins->number, + pctl->desc->npins); if (ret) { dev_err(&pdev->dev, "failed to add pin range\n"); return ret; @@ -431,21 +431,21 @@ static int axp20x_gpio_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id axp20x_gpio_match[] = { +static const struct of_device_id axp20x_pctl_match[] = { { .compatible = "x-powers,axp209-gpio" }, { } }; -MODULE_DEVICE_TABLE(of, axp20x_gpio_match); +MODULE_DEVICE_TABLE(of, axp20x_pctl_match); -static struct platform_driver axp20x_gpio_driver = { - .probe = axp20x_gpio_probe, +static struct platform_driver axp20x_pctl_driver = { + .probe = axp20x_pctl_probe, .driver = { .name = "axp20x-gpio", - .of_match_table = axp20x_gpio_match, + .of_match_table = axp20x_pctl_match, }, }; -module_platform_driver(axp20x_gpio_driver); +module_platform_driver(axp20x_pctl_driver); MODULE_AUTHOR("Maxime Ripard "); MODULE_AUTHOR("Quentin Schulz "); -- cgit v1.2.3 From 48e706fbc546d63148a762aadf92f412eefe8b8e Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:44 +0100 Subject: pinctrl: axp209: add programmable gpio_status_offset To prepare for patches that will add support for a new PMIC that has a different GPIO input status register, add a gpio_status_offset within axp20x_pctl structure and use it. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-by: Chen-Yu Tsai Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-axp209.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index 17eeb0410a5b..db8e319b6e11 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -48,6 +48,7 @@ struct axp20x_pctrl_desc { u8 ldo_mask; /* Stores the pins supporting ADC function. Bit offset is pin number. */ u8 adc_mask; + u8 gpio_status_offset; }; struct axp20x_pinctrl_function { @@ -77,6 +78,7 @@ static const struct axp20x_pctrl_desc axp20x_data = { .npins = ARRAY_SIZE(axp209_pins), .ldo_mask = BIT(0) | BIT(1), .adc_mask = BIT(0) | BIT(1), + .gpio_status_offset = 4, }; static int axp20x_gpio_get_reg(unsigned int offset) @@ -108,7 +110,7 @@ static int axp20x_gpio_get(struct gpio_chip *chip, unsigned int offset) if (ret) return ret; - return !!(val & BIT(offset + 4)); + return !!(val & BIT(offset + pctl->desc->gpio_status_offset)); } static int axp20x_gpio_get_direction(struct gpio_chip *chip, -- cgit v1.2.3 From a0a4b4c2420e0af6171a2be1965bdd983d35953c Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:45 +0100 Subject: pinctrl: axp209: add programmable ADC muxing value To prepare for patches that will add support for a new PMIC that has a different GPIO adc muxing value, add an adc_mux within axp20x_pctl structure and use it. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-axp209.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index db8e319b6e11..9bb8722ba90f 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -49,6 +49,7 @@ struct axp20x_pctrl_desc { /* Stores the pins supporting ADC function. Bit offset is pin number. */ u8 adc_mask; u8 gpio_status_offset; + u8 adc_mux; }; struct axp20x_pinctrl_function { @@ -79,6 +80,7 @@ static const struct axp20x_pctrl_desc axp20x_data = { .ldo_mask = BIT(0) | BIT(1), .adc_mask = BIT(0) | BIT(1), .gpio_status_offset = 4, + .adc_mux = AXP20X_MUX_ADC, }; static int axp20x_gpio_get_reg(unsigned int offset) @@ -334,7 +336,7 @@ static void axp20x_build_funcs_groups(struct platform_device *pdev) * See comment in axp20x_pmx_set_mux. */ pctl->funcs[AXP20X_FUNC_ADC].name = "adc"; - pctl->funcs[AXP20X_FUNC_ADC].muxval = AXP20X_MUX_ADC; + pctl->funcs[AXP20X_FUNC_ADC].muxval = pctl->desc->adc_mux; /* Every pin supports GPIO_OUT and GPIO_IN functions */ for (i = 0; i <= AXP20X_FUNC_GPIO_IN; i++) { -- cgit v1.2.3 From e1190083b89bd0d53c83b647ff313d9f004c6772 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 5 Dec 2017 15:46:46 +0100 Subject: pinctrl: axp209: add support for AXP813 GPIOs The AXP813 has only two GPIOs. GPIO0 can either be used as a GPIO, an LDO regulator or an ADC. GPIO1 can be used either as a GPIO or an LDO regulator. Moreover, the status bit of the GPIOs when in input mode is not offset by 4 unlike the AXP209. Signed-off-by: Quentin Schulz Acked-by: Maxime Ripard Acked-by: Rob Herring Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/gpio-axp209.txt | 13 +++++++- drivers/pinctrl/pinctrl-axp209.c | 35 +++++++++++++++++----- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-axp209.txt b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt index 0d77597c3f92..fc42b2caa06d 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-axp209.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt @@ -9,7 +9,9 @@ Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt This driver employs the per-pin muxing pattern. Required properties: -- compatible: Should be "x-powers,axp209-gpio" +- compatible: Should be one of: + - "x-powers,axp209-gpio" + - "x-powers,axp813-gpio" - #gpio-cells: Should be two. The first cell is the pin number and the second is the GPIO flags. - gpio-controller: Marks the device node as a GPIO controller. @@ -57,8 +59,17 @@ GPIOs and their functions Each GPIO is independent from the other (i.e. GPIO0 in gpio_in function does not force GPIO1 and GPIO2 to be in gpio_in function as well). +axp209 +------ GPIO | Functions ------------------------ GPIO0 | gpio_in, gpio_out, ldo, adc GPIO1 | gpio_in, gpio_out, ldo, adc GPIO2 | gpio_in, gpio_out + +axp813 +------ +GPIO | Functions +------------------------ +GPIO0 | gpio_in, gpio_out, ldo, adc +GPIO1 | gpio_in, gpio_out, ldo diff --git a/drivers/pinctrl/pinctrl-axp209.c b/drivers/pinctrl/pinctrl-axp209.c index 9bb8722ba90f..22d3bb0bf927 100644 --- a/drivers/pinctrl/pinctrl-axp209.c +++ b/drivers/pinctrl/pinctrl-axp209.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,8 @@ #define AXP20X_MUX_GPIO_IN BIT(1) #define AXP20X_MUX_ADC BIT(2) +#define AXP813_MUX_ADC (BIT(2) | BIT(0)) + struct axp20x_pctrl_desc { const struct pinctrl_pin_desc *pins; unsigned int npins; @@ -74,6 +77,11 @@ static const struct pinctrl_pin_desc axp209_pins[] = { PINCTRL_PIN(2, "GPIO2"), }; +static const struct pinctrl_pin_desc axp813_pins[] = { + PINCTRL_PIN(0, "GPIO0"), + PINCTRL_PIN(1, "GPIO1"), +}; + static const struct axp20x_pctrl_desc axp20x_data = { .pins = axp209_pins, .npins = ARRAY_SIZE(axp209_pins), @@ -83,6 +91,15 @@ static const struct axp20x_pctrl_desc axp20x_data = { .adc_mux = AXP20X_MUX_ADC, }; +static const struct axp20x_pctrl_desc axp813_data = { + .pins = axp813_pins, + .npins = ARRAY_SIZE(axp813_pins), + .ldo_mask = BIT(0) | BIT(1), + .adc_mask = BIT(0), + .gpio_status_offset = 0, + .adc_mux = AXP813_MUX_ADC, +}; + static int axp20x_gpio_get_reg(unsigned int offset) { switch (offset) { @@ -357,10 +374,18 @@ static void axp20x_build_funcs_groups(struct platform_device *pdev) pctl->desc->pins); } +static const struct of_device_id axp20x_pctl_match[] = { + { .compatible = "x-powers,axp209-gpio", .data = &axp20x_data, }, + { .compatible = "x-powers,axp813-gpio", .data = &axp813_data, }, + { } +}; +MODULE_DEVICE_TABLE(of, axp20x_pctl_match); + static int axp20x_pctl_probe(struct platform_device *pdev) { struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct axp20x_pctl *pctl; + struct device *dev = &pdev->dev; struct pinctrl_desc *pctrl_desc; int ret; @@ -388,9 +413,9 @@ static int axp20x_pctl_probe(struct platform_device *pdev) pctl->chip.set = axp20x_gpio_set; pctl->chip.direction_input = axp20x_gpio_input; pctl->chip.direction_output = axp20x_gpio_output; - pctl->chip.ngpio = 3; + pctl->chip.ngpio = pctl->desc->npins; - pctl->desc = &axp20x_data; + pctl->desc = (struct axp20x_pctrl_desc *)of_device_get_match_data(dev); pctl->regmap = axp20x->regmap; pctl->dev = &pdev->dev; @@ -435,12 +460,6 @@ static int axp20x_pctl_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id axp20x_pctl_match[] = { - { .compatible = "x-powers,axp209-gpio" }, - { } -}; -MODULE_DEVICE_TABLE(of, axp20x_pctl_match); - static struct platform_driver axp20x_pctl_driver = { .probe = axp20x_pctl_probe, .driver = { -- cgit v1.2.3 From e0fc62a6552f3d9c21e73cc65844f9aad1892cf7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 20:27:09 +0200 Subject: w1: w1-gpio: Convert to use GPIO descriptors The w1 master driver includes a complete open drain emulation reimplementation among other things. This converts the driver and all board files using it to use GPIO descriptors associated with the device to look up the GPIO wire, as well ass the optional pull-up GPIO line. When probed from the device tree, the driver will just pick descriptors and use them right off. For the two board files in the kernel, we add descriptor lookups so we do not need to keep any old platform data handling around for the GPIO lines. As the platform data is also a state container for this driver, we augment it to contain the GPIO descriptors. w1_gpio_write_bit_dir() and w1_gpio_write_bit_val() are gone since this pair was a reimplementation of open drain emulation which is now handled by gpiolib. The special "linux,open-drain" flag is a bit of mishap here: it has the same semantic as the same flags in I2C: it means that something in the platform is setting up the line as open drain behind our back. We handle this the same way as in I2C. To drive the pull-up, we need to bypass open drain emulation in gpiolib for the line, and this is done by driving it high using gpiod_set_raw_value() which has been augmented to have the semantic of overriding the open drain emulation. We also augment the documentation to reflect the way to pass GPIO descriptors from the machine. Acked-by: Evgeniy Polyakov Signed-off-by: Linus Walleij --- Documentation/w1/masters/w1-gpio | 17 +++- arch/arm/mach-ixp4xx/vulcan-setup.c | 13 +++- arch/arm/mach-pxa/raumfeld.c | 16 ++-- drivers/w1/masters/w1-gpio.c | 149 +++++++++++++++--------------------- include/linux/w1-gpio.h | 9 +-- 5 files changed, 101 insertions(+), 103 deletions(-) diff --git a/Documentation/w1/masters/w1-gpio b/Documentation/w1/masters/w1-gpio index af5d3b4aa851..623961d9e83f 100644 --- a/Documentation/w1/masters/w1-gpio +++ b/Documentation/w1/masters/w1-gpio @@ -8,17 +8,27 @@ Description ----------- GPIO 1-wire bus master driver. The driver uses the GPIO API to control the -wire and the GPIO pin can be specified using platform data. +wire and the GPIO pin can be specified using GPIO machine descriptor tables. +It is also possible to define the master using device tree, see +Documentation/devicetree/bindings/w1/w1-gpio.txt Example (mach-at91) ------------------- +#include #include +static struct gpiod_lookup_table foo_w1_gpiod_table = { + .dev_id = "w1-gpio", + .table = { + GPIO_LOOKUP_IDX("at91-gpio", AT91_PIN_PB20, NULL, 0, + GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN), + }, +}; + static struct w1_gpio_platform_data foo_w1_gpio_pdata = { - .pin = AT91_PIN_PB20, - .is_open_drain = 1, + .ext_pullup_enable_pin = -EINVAL, }; static struct platform_device foo_w1_device = { @@ -30,4 +40,5 @@ static struct platform_device foo_w1_device = { ... at91_set_GPIO_periph(foo_w1_gpio_pdata.pin, 1); at91_set_multi_drive(foo_w1_gpio_pdata.pin, 1); + gpiod_add_lookup_table(&foo_w1_gpiod_table); platform_device_register(&foo_w1_device); diff --git a/arch/arm/mach-ixp4xx/vulcan-setup.c b/arch/arm/mach-ixp4xx/vulcan-setup.c index 731fb2019ecb..2c03d2f6b647 100644 --- a/arch/arm/mach-ixp4xx/vulcan-setup.c +++ b/arch/arm/mach-ixp4xx/vulcan-setup.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -162,9 +163,16 @@ static struct platform_device vulcan_max6369 = { .num_resources = 1, }; +static struct gpiod_lookup_table vulcan_w1_gpiod_table = { + .dev_id = "w1-gpio", + .table = { + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", 14, NULL, 0, + GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), + }, +}; + static struct w1_gpio_platform_data vulcan_w1_gpio_pdata = { - .pin = 14, - .ext_pullup_enable_pin = -EINVAL, + /* Intentionally left blank */ }; static struct platform_device vulcan_w1_gpio = { @@ -233,6 +241,7 @@ static void __init vulcan_init(void) IXP4XX_EXP_BUS_WR_EN | IXP4XX_EXP_BUS_BYTE_EN; + gpiod_add_lookup_table(&vulcan_w1_gpiod_table); platform_add_devices(vulcan_devices, ARRAY_SIZE(vulcan_devices)); } diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c index 9d662fed03ec..feddca7f3540 100644 --- a/arch/arm/mach-pxa/raumfeld.c +++ b/arch/arm/mach-pxa/raumfeld.c @@ -506,11 +506,16 @@ static void w1_enable_external_pullup(int enable) msleep(100); } +static struct gpiod_lookup_table raumfeld_w1_gpiod_table = { + .dev_id = "w1-gpio", + .table = { + GPIO_LOOKUP_IDX("gpio-pxa", GPIO_ONE_WIRE, NULL, 0, + GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), + }, +}; + static struct w1_gpio_platform_data w1_gpio_platform_data = { - .pin = GPIO_ONE_WIRE, - .is_open_drain = 0, - .enable_external_pullup = w1_enable_external_pullup, - .ext_pullup_enable_pin = -EINVAL, + .enable_external_pullup = w1_enable_external_pullup, }; static struct platform_device raumfeld_w1_gpio_device = { @@ -523,13 +528,14 @@ static struct platform_device raumfeld_w1_gpio_device = { static void __init raumfeld_w1_init(void) { int ret = gpio_request(GPIO_W1_PULLUP_ENABLE, - "W1 external pullup enable"); + "W1 external pullup enable"); if (ret < 0) pr_warn("Unable to request GPIO_W1_PULLUP_ENABLE\n"); else gpio_direction_output(GPIO_W1_PULLUP_ENABLE, 0); + gpiod_add_lookup_table(&raumfeld_w1_gpiod_table); platform_device_register(&raumfeld_w1_gpio_device); } diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index a90728ceec5a..55e11bf8ebaf 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -13,9 +13,8 @@ #include #include #include -#include +#include #include -#include #include #include #include @@ -30,11 +29,17 @@ static u8 w1_gpio_set_pullup(void *data, int delay) pdata->pullup_duration = delay; } else { if (pdata->pullup_duration) { - gpio_direction_output(pdata->pin, 1); - + /* + * This will OVERRIDE open drain emulation and force-pull + * the line high for some time. + */ + gpiod_set_raw_value(pdata->gpiod, 1); msleep(pdata->pullup_duration); - - gpio_direction_input(pdata->pin); + /* + * This will simply set the line as input since we are doing + * open drain emulation in the GPIO library. + */ + gpiod_set_value(pdata->gpiod, 1); } pdata->pullup_duration = 0; } @@ -42,28 +47,18 @@ static u8 w1_gpio_set_pullup(void *data, int delay) return 0; } -static void w1_gpio_write_bit_dir(void *data, u8 bit) -{ - struct w1_gpio_platform_data *pdata = data; - - if (bit) - gpio_direction_input(pdata->pin); - else - gpio_direction_output(pdata->pin, 0); -} - -static void w1_gpio_write_bit_val(void *data, u8 bit) +static void w1_gpio_write_bit(void *data, u8 bit) { struct w1_gpio_platform_data *pdata = data; - gpio_set_value(pdata->pin, bit); + gpiod_set_value(pdata->gpiod, bit); } static u8 w1_gpio_read_bit(void *data) { struct w1_gpio_platform_data *pdata = data; - return gpio_get_value(pdata->pin) ? 1 : 0; + return gpiod_get_value(pdata->gpiod) ? 1 : 0; } #if defined(CONFIG_OF) @@ -74,107 +69,85 @@ static const struct of_device_id w1_gpio_dt_ids[] = { MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids); #endif -static int w1_gpio_probe_dt(struct platform_device *pdev) -{ - struct w1_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; - int gpio; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - if (of_get_property(np, "linux,open-drain", NULL)) - pdata->is_open_drain = 1; - - gpio = of_get_gpio(np, 0); - if (gpio < 0) { - if (gpio != -EPROBE_DEFER) - dev_err(&pdev->dev, - "Failed to parse gpio property for data pin (%d)\n", - gpio); - - return gpio; - } - pdata->pin = gpio; - - gpio = of_get_gpio(np, 1); - if (gpio == -EPROBE_DEFER) - return gpio; - /* ignore other errors as the pullup gpio is optional */ - pdata->ext_pullup_enable_pin = gpio; - - pdev->dev.platform_data = pdata; - - return 0; -} - static int w1_gpio_probe(struct platform_device *pdev) { struct w1_bus_master *master; struct w1_gpio_platform_data *pdata; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + /* Enforce open drain mode by default */ + enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN; int err; if (of_have_populated_dt()) { - err = w1_gpio_probe_dt(pdev); - if (err < 0) - return err; + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* + * This parameter means that something else than the gpiolib has + * already set the line into open drain mode, so we should just + * driver it high/low like we are in full control of the line and + * open drain will happen transparently. + */ + if (of_get_property(np, "linux,open-drain", NULL)) + gflags = GPIOD_OUT_LOW; + + pdev->dev.platform_data = pdata; } - - pdata = dev_get_platdata(&pdev->dev); + pdata = dev_get_platdata(dev); if (!pdata) { - dev_err(&pdev->dev, "No configuration data\n"); + dev_err(dev, "No configuration data\n"); return -ENXIO; } - master = devm_kzalloc(&pdev->dev, sizeof(struct w1_bus_master), + master = devm_kzalloc(dev, sizeof(struct w1_bus_master), GFP_KERNEL); if (!master) { - dev_err(&pdev->dev, "Out of memory\n"); + dev_err(dev, "Out of memory\n"); return -ENOMEM; } - err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); - if (err) { - dev_err(&pdev->dev, "gpio_request (pin) failed\n"); - return err; + pdata->gpiod = devm_gpiod_get_index(dev, NULL, 0, gflags); + if (IS_ERR(pdata->gpiod)) { + dev_err(dev, "gpio_request (pin) failed\n"); + return PTR_ERR(pdata->gpiod); } - if (gpio_is_valid(pdata->ext_pullup_enable_pin)) { - err = devm_gpio_request_one(&pdev->dev, - pdata->ext_pullup_enable_pin, GPIOF_INIT_LOW, - "w1 pullup"); - if (err < 0) { - dev_err(&pdev->dev, "gpio_request_one " - "(ext_pullup_enable_pin) failed\n"); - return err; - } + pdata->pullup_gpiod = + devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_LOW); + if (IS_ERR(pdata->pullup_gpiod)) { + dev_err(dev, "gpio_request_one " + "(ext_pullup_enable_pin) failed\n"); + return PTR_ERR(pdata->pullup_gpiod); } master->data = pdata; master->read_bit = w1_gpio_read_bit; - - if (pdata->is_open_drain) { - gpio_direction_output(pdata->pin, 1); - master->write_bit = w1_gpio_write_bit_val; - } else { - gpio_direction_input(pdata->pin); - master->write_bit = w1_gpio_write_bit_dir; + gpiod_direction_output(pdata->gpiod, 1); + master->write_bit = w1_gpio_write_bit; + + /* + * If we are using open drain emulation from the GPIO library, + * we need to use this pullup function that hammers the line + * high using a raw accessor to provide pull-up for the w1 + * line. + */ + if (gflags == GPIOD_OUT_LOW_OPEN_DRAIN) master->set_pullup = w1_gpio_set_pullup; - } err = w1_add_master_device(master); if (err) { - dev_err(&pdev->dev, "w1_add_master device failed\n"); + dev_err(dev, "w1_add_master device failed\n"); return err; } if (pdata->enable_external_pullup) pdata->enable_external_pullup(1); - if (gpio_is_valid(pdata->ext_pullup_enable_pin)) - gpio_set_value(pdata->ext_pullup_enable_pin, 1); + if (pdata->pullup_gpiod) + gpiod_set_value(pdata->pullup_gpiod, 1); platform_set_drvdata(pdev, master); @@ -189,8 +162,8 @@ static int w1_gpio_remove(struct platform_device *pdev) if (pdata->enable_external_pullup) pdata->enable_external_pullup(0); - if (gpio_is_valid(pdata->ext_pullup_enable_pin)) - gpio_set_value(pdata->ext_pullup_enable_pin, 0); + if (pdata->pullup_gpiod) + gpiod_set_value(pdata->pullup_gpiod, 0); w1_remove_master_device(master); diff --git a/include/linux/w1-gpio.h b/include/linux/w1-gpio.h index d58594a32324..78901ecd2f95 100644 --- a/include/linux/w1-gpio.h +++ b/include/linux/w1-gpio.h @@ -10,16 +10,15 @@ #ifndef _LINUX_W1_GPIO_H #define _LINUX_W1_GPIO_H +struct gpio_desc; + /** * struct w1_gpio_platform_data - Platform-dependent data for w1-gpio - * @pin: GPIO pin to use - * @is_open_drain: GPIO pin is configured as open drain */ struct w1_gpio_platform_data { - unsigned int pin; - unsigned int is_open_drain:1; + struct gpio_desc *gpiod; + struct gpio_desc *pullup_gpiod; void (*enable_external_pullup)(int enable); - unsigned int ext_pullup_enable_pin; unsigned int pullup_duration; }; -- cgit v1.2.3 From c4b54e138a2610b02cd655deddefa793c1871ef8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 6 Dec 2017 17:30:02 +0100 Subject: gpio: mockup: fix a return value check The return value of platform_device_register_resndata() on error is an error code converted to pointer with ERR_PTR(), not NULL. Check the return value correctly. Fixes: 8a39f597bcfd ("gpio: mockup: rework device probing") Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index ea8c730d8af1..3a545ad17817 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -387,11 +387,11 @@ static int __init gpio_mockup_init(void) GPIO_MOCKUP_NAME, i, NULL, 0, &pdata, sizeof(pdata)); - if (!pdev) { + if (IS_ERR(pdev)) { gpio_mockup_err("error registering device"); platform_driver_unregister(&gpio_mockup_driver); gpio_mockup_unregister_pdevs(); - return -ENOMEM; + return PTR_ERR(pdev); } gpio_mockup_pdevs[i] = pdev; -- cgit v1.2.3 From 3b469b0a139e65a7c3da32f5481467f6bdcee837 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 13 Dec 2017 12:25:19 +0100 Subject: gpiolib: constify label in gpio_device This string is never modified. Make it const. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 5e1f7cc6eeb6..6e9228b94437 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -58,7 +58,7 @@ struct gpio_device { struct gpio_desc *descs; int base; u16 ngpio; - char *label; + const char *label; void *data; struct list_head list; -- cgit v1.2.3 From fcf273e5807976009dbb5ffb00bd5b9091843fe6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 14 Dec 2017 15:29:20 +0100 Subject: gpiolib: use kstrdup_const() for gpio_device label Users often pass a pointer to a static string to gpiochip_add_data() family of functions. Avoid unnecessary memory allocations with the provided helper routine. While at it: use a ternary operator instead of an if else for brevity. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 56eec094184c..e9ec44ffaaaf 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1062,7 +1062,7 @@ static void gpiodevice_release(struct device *dev) list_del(&gdev->list); ida_simple_remove(&gpio_ida, gdev->id); - kfree(gdev->label); + kfree_const(gdev->label); kfree(gdev->descs); kfree(gdev); } @@ -1170,10 +1170,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, goto err_free_descs; } - if (chip->label) - gdev->label = kstrdup(chip->label, GFP_KERNEL); - else - gdev->label = kstrdup("unknown", GFP_KERNEL); + gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { status = -ENOMEM; goto err_free_descs; @@ -1294,7 +1291,7 @@ err_remove_from_list: list_del(&gdev->list); spin_unlock_irqrestore(&gpio_lock, flags); err_free_label: - kfree(gdev->label); + kfree_const(gdev->label); err_free_descs: kfree(gdev->descs); err_free_gdev: -- cgit v1.2.3 From 7fda9100bb8258bbdff90f3db5079d28eb9b0013 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 18 Dec 2017 11:08:29 +0100 Subject: gpio: sysfs: change 'value' attribute to prealloc The GPIO 'value' attribute is time critical. A small bench with 'perf record' on the app below shows that 80% of the time spent in sysfs_kf_seq_show() is spent in memset() for zeroising the buffer. |--67.48%--sysfs_kf_seq_show | | | |--54.40%--memset | | | |--11.49%--dev_attr_show | | | | | |--10.06%--value_show | | | | | | | |--4.75%--sprintf | | | | | This patch changes the attribute type to prealloc, eliminating the need to zeroise the buffer at each read. 'perf record' gives the following result. |--42.41%--sysfs_kf_read | | | |--39.73%--dev_attr_show | | | | | |--38.23%--value_show | | | | | | | |--29.22%--sprintf | | | | | Test done with the following small app: int main(int argc, char **argv) { int fd = open(argv[1], O_RDONLY); for (;;) { int buf[512]; read(fd, buf, 512); lseek(fd, 0, SEEK_SET); } exit(0); } Signed-off-by: Christophe Leroy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 2 +- include/linux/device.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 0bd472ffb072..3b2465bbd5e7 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -138,7 +138,7 @@ static ssize_t value_store(struct device *dev, return status; } -static DEVICE_ATTR_RW(value); +static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { diff --git a/include/linux/device.h b/include/linux/device.h index 9d32000725da..46ac622e5c6f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -575,6 +575,9 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr, #define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) +#define DEVICE_ATTR_PREALLOC(_name, _mode, _show, _store) \ + struct device_attribute dev_attr_##_name = \ + __ATTR_PREALLOC(_name, _mode, _show, _store) #define DEVICE_ATTR_RW(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RW(_name) #define DEVICE_ATTR_RO(_name) \ -- cgit v1.2.3 From 9295c01253b4eda5dc0b5a5b2e0fd321fe57010c Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 18 Dec 2017 11:08:31 +0100 Subject: gpio: sysfs: correct error handling on 'value' attribute read. 'value' attribute is supposed to only return 0 or 1 according to the documentation. With today's implementation, if gpiod_get_value_cansleep() fails the printed 'value' is a negative value. This patch ensures that an error is returned on read instead. Signed-off-by: Christophe Leroy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 3b2465bbd5e7..ef34b8f56bd1 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -106,8 +106,12 @@ static ssize_t value_show(struct device *dev, mutex_lock(&data->mutex); - status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); + status = gpiod_get_value_cansleep(desc); + if (status < 0) + goto err; + status = sprintf(buf, "%d\n", status); +err: mutex_unlock(&data->mutex); return status; -- cgit v1.2.3 From 7a94b88cb7087aa3184161bedccfea25e5502c62 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 18 Dec 2017 11:08:33 +0100 Subject: gpio: sysfs: don't use sprintf() for 'value' attribute A bench with 'perf record' shows that most of time spent in value_show() is spent in sprintf() --42.41%--sysfs_kf_read | |--39.73%--dev_attr_show | | | |--38.23%--value_show | | | | | |--29.22%--sprintf | | | | | |--2.94%--gpiod_get_value_cansleep | | | value_show() only returns "0\n" or "1\n", therefore the use of sprintf() can be avoided With this patch we get the following result with 'perf record' --13.89%--sysfs_kf_read | |--10.72%--dev_attr_show | | | |--9.44%--value_show | | | | | |--4.61%--gpiod_get_value_cansleep Signed-off-by: Christophe Leroy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index ef34b8f56bd1..ad7173df72eb 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -110,7 +110,9 @@ static ssize_t value_show(struct device *dev, if (status < 0) goto err; - status = sprintf(buf, "%d\n", status); + buf[0] = '0' + status; + buf[1] = '\n'; + status = 2; err: mutex_unlock(&data->mutex); -- cgit v1.2.3 From 1efba35afae43094d3f26093f5f1ab8a46686a6f Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Mon, 18 Dec 2017 11:08:35 +0100 Subject: gpio: sysfs: avoid using kstrtol() in 'value' attribute write A 'perf record' on an app continuously writing in the 'value' attribute show that most of the time is spent in kstrtol() --17.99%--value_store | |--10.17%--kstrtoint | | | |--8.82%--kstrtoll | |--2.50%--gpiod_set_value_cansleep | |--1.82%--u16_gpio_set | |--1.46%--value_store The normal case is to write 0 or 1 in the attribute, therefore this patch avoids the call to kstrtol() in the most common cases Then 'perf record' shows --7.21%--value_store | |--2.69%--u16_gpio_set | |--1.47%--value_store | |--1.08%--gpiod_set_value_cansleep | |--0.60%--mutex_lock | --0.58%--mutex_unlock Signed-off-by: Christophe Leroy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-sysfs.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index ad7173df72eb..3dbaf489a8a5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "gpiolib.h" @@ -124,7 +125,7 @@ static ssize_t value_store(struct device *dev, { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - ssize_t status; + ssize_t status = 0; mutex_lock(&data->mutex); @@ -133,7 +134,11 @@ static ssize_t value_store(struct device *dev, } else { long value; - status = kstrtol(buf, 0, &value); + if (size <= 2 && isdigit(buf[0]) && + (size == 1 || buf[1] == '\n')) + value = buf[0] - '0'; + else + status = kstrtol(buf, 0, &value); if (status == 0) { gpiod_set_value_cansleep(desc, value); status = size; -- cgit v1.2.3 From 92e70b83c0fef79dea9b8be06944b1eaffa4a9ba Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Thu, 14 Dec 2017 21:26:08 +0100 Subject: tools/gpio: Don't use u_int32_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit u_int32_t is a non-standard version of uint32_t, that was apparently introduced by BSD. Use uint32_t from stdint.h instead. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Linus Walleij --- tools/gpio/gpio-event-mon.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c index 1c14c2595158..be6768e21b09 100644 --- a/tools/gpio/gpio-event-mon.c +++ b/tools/gpio/gpio-event-mon.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -27,8 +28,8 @@ int monitor_device(const char *device_name, unsigned int line, - u_int32_t handleflags, - u_int32_t eventflags, + uint32_t handleflags, + uint32_t eventflags, unsigned int loops) { struct gpioevent_request req; @@ -145,8 +146,8 @@ int main(int argc, char **argv) const char *device_name = NULL; unsigned int line = -1; unsigned int loops = 0; - u_int32_t handleflags = GPIOHANDLE_REQUEST_INPUT; - u_int32_t eventflags = 0; + uint32_t handleflags = GPIOHANDLE_REQUEST_INPUT; + uint32_t eventflags = 0; int c; while ((c = getopt(argc, argv, "c:n:o:dsrf?")) != -1) { -- cgit v1.2.3 From 76bc7f187677e6f0c57e327a689be92d999ed7e8 Mon Sep 17 00:00:00 2001 From: Pravin Shedge Date: Sat, 16 Dec 2017 18:26:52 +0530 Subject: drivers: gpio: remove duplicate includes These duplicate includes have been found with scripts/checkincludes.pl but they have been removed manually to avoid removing false positives. Signed-off-by: Pravin Shedge Acked-by: Gregory Fong Signed-off-by: Linus Walleij --- drivers/gpio/gpio-brcmstb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 545d43a587b7..3dcbeadca8d5 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -19,7 +19,6 @@ #include #include #include -#include enum gio_reg_index { GIO_REG_ODEN = 0, -- cgit v1.2.3 From caa92ee84ba02aa2619b087d81a9e2e54a2235fd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 19 Dec 2017 17:03:42 +0100 Subject: dt-bindings: gpio: rcar: Correct SoC family name for R8A7778 R8A7778 is R-Car (not R-Mobile) M1. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Reviewed-by: Rob Herring Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt index a7ac460ad657..9474138d776e 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt +++ b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt @@ -5,7 +5,7 @@ Required Properties: - compatible: should contain one or more of the following: - "renesas,gpio-r8a7743": for R8A7743 (RZ/G1M) compatible GPIO controller. - "renesas,gpio-r8a7745": for R8A7745 (RZ/G1E) compatible GPIO controller. - - "renesas,gpio-r8a7778": for R8A7778 (R-Mobile M1) compatible GPIO controller. + - "renesas,gpio-r8a7778": for R8A7778 (R-Car M1) compatible GPIO controller. - "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller. - "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller. - "renesas,gpio-r8a7791": for R8A7791 (R-Car M2-W) compatible GPIO controller. -- cgit v1.2.3 From 1ca2a92b2a99323f666f1b669b7484df4bda05e4 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 20 Dec 2017 13:10:31 -0600 Subject: Revert "gpio: set up initial state from .get_direction()" This reverts commit 72d3200061776264941be1b5a9bb8e926b3b30a5. We cannot blindly query the direction of all GPIOs when the pins are first registered. The get_direction callback normally triggers a read/write to hardware, but we shouldn't be touching the hardware for an individual GPIO until after it's been properly claimed. Signed-off-by: Timur Tabi Reviewed-by: Stephen Boyd Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e9ec44ffaaaf..45c20307630a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1217,31 +1217,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, struct gpio_desc *desc = &gdev->descs[i]; desc->gdev = gdev; - /* - * REVISIT: most hardware initializes GPIOs as inputs - * (often with pullups enabled) so power usage is - * minimized. Linux code should set the gpio direction - * first thing; but until it does, and in case - * chip->get_direction is not set, we may expose the - * wrong direction in sysfs. - */ - - if (chip->get_direction) { - /* - * If we have .get_direction, set up the initial - * direction flag from the hardware. - */ - int dir = chip->get_direction(chip, i); - if (!dir) - set_bit(FLAG_IS_OUT, &desc->flags); - } else if (!chip->direction_input) { - /* - * If the chip lacks the .direction_input callback - * we logically assume all lines are outputs. - */ - set_bit(FLAG_IS_OUT, &desc->flags); - } + /* REVISIT: most hardware initializes GPIOs as inputs (often + * with pullups enabled) so power usage is minimized. Linux + * code should set the gpio direction first thing; but until + * it does, and in case chip->get_direction is not set, we may + * expose the wrong direction in sysfs. + */ + desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } #ifdef CONFIG_PINCTRL -- cgit v1.2.3 From 93ebe8636bb0d95e2e711f2a53abbb72a9d9cf8d Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 20 Dec 2017 13:10:32 -0600 Subject: pinctrl: qcom: disable GPIO groups with no pins pinctrl-msm only accepts an array of GPIOs from 0 to n-1, and it expects each group to support have only one pin (npins == 1). We can support "sparse" GPIO maps if we allow for some groups to have zero pins (npins == 0). These pins are "hidden" from the rest of the driver and gpiolib. Access to unavailable GPIOs is blocked via a request callback. If the requested GPIO is unavailable, -EACCES is returned, which prevents further access to that GPIO. Signed-off-by: Timur Tabi Reviewed-by: Stephen Boyd Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-msm.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 7a960590ecaa..d45b4c2b5af1 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -507,6 +507,11 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, }; g = &pctrl->soc->groups[offset]; + + /* If the GPIO group has no pins, then don't show it. */ + if (!g->npins) + return; + ctl_reg = readl(pctrl->regs + g->ctl_reg); is_out = !!(ctl_reg & BIT(g->oe_bit)); @@ -516,7 +521,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func); seq_printf(s, " %dmA", msm_regval_to_drive(drive)); - seq_printf(s, " %s", pulls[pull]); + seq_printf(s, " %s\n", pulls[pull]); } static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -524,23 +529,36 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) unsigned gpio = chip->base; unsigned i; - for (i = 0; i < chip->ngpio; i++, gpio++) { + for (i = 0; i < chip->ngpio; i++, gpio++) msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); - seq_puts(s, "\n"); - } } #else #define msm_gpio_dbg_show NULL #endif +/* + * If the requested GPIO has no pins, then treat it as unavailable. + * Otherwise, call the standard request function. + */ +static int msm_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + const struct msm_pingroup *g = &pctrl->soc->groups[offset]; + + if (!g->npins) + return -EACCES; + + return gpiochip_generic_request(chip, offset); +} + static const struct gpio_chip msm_gpio_template = { .direction_input = msm_gpio_direction_input, .direction_output = msm_gpio_direction_output, .get_direction = msm_gpio_get_direction, .get = msm_gpio_get, .set = msm_gpio_set, - .request = gpiochip_generic_request, + .request = msm_gpio_request, .free = gpiochip_generic_free, .dbg_show = msm_gpio_dbg_show, }; -- cgit v1.2.3 From 1696784eb7b52b13b62d160c028ef2c2c981d4f2 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Thu, 21 Dec 2017 11:11:31 +1030 Subject: tools/gpio: Fix build error with musl libc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GPIO tools build fails when using a buildroot toolchain that uses musl as it's C library: arm-broomstick-linux-musleabi-gcc -Wp,-MD,./.gpio-event-mon.o.d \ -Wp,-MT,gpio-event-mon.o -O2 -Wall -g -D_GNU_SOURCE \ -Iinclude -D"BUILD_STR(s)=#s" -c -o gpio-event-mon.o gpio-event-mon.c gpio-event-mon.c:30:6: error: unknown type name ‘u_int32_t’; did you mean ‘uint32_t’? u_int32_t handleflags, ^~~~~~~~~ uint32_t The glibc headers installed on my laptop include sys/types.h in unistd.h, but it appears that musl does not. Fixes: 97f69747d8b1 ("tools/gpio: add the gpio-event-mon tool") Cc: stable@vger.kernel.org Signed-off-by: Joel Stanley Signed-off-by: Linus Walleij --- tools/gpio/gpio-event-mon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c index be6768e21b09..dac4d4131d9b 100644 --- a/tools/gpio/gpio-event-mon.c +++ b/tools/gpio/gpio-event-mon.c @@ -24,6 +24,7 @@ #include #include #include +#include #include int monitor_device(const char *device_name, -- cgit v1.2.3 From a746a232184822a649d36275edbd031c6550324d Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 21 Dec 2017 01:27:04 +0100 Subject: gpio: reduce descriptor validation code size While we do need macros to be able to return from the "calling" function, we can still factor the checks done by the VALIDATE_DESC* macros into a real helper function. This reduces the backslashtitis, avoids duplicating the logic in the two macros and saves about 1K of generated code: $ scripts/bloat-o-meter drivers/gpio/gpiolib.o.{0,1} add/remove: 1/0 grow/shrink: 0/15 up/down: 104/-1281 (-1177) Function old new delta validate_desc - 104 +104 gpiod_set_value 192 135 -57 gpiod_set_raw_value 125 67 -58 gpiod_direction_output 412 351 -61 gpiod_set_value_cansleep 150 70 -80 gpiod_set_raw_value_cansleep 132 52 -80 gpiod_get_raw_value 139 54 -85 gpiod_set_debounce 226 140 -86 gpiod_direction_output_raw 124 38 -86 gpiod_get_value 161 74 -87 gpiod_cansleep 126 39 -87 gpiod_get_raw_value_cansleep 130 39 -91 gpiod_get_value_cansleep 152 59 -93 gpiod_is_active_low 128 33 -95 gpiod_request 299 184 -115 gpiod_direction_input 386 266 -120 Total: Before=25460, After=24283, chg -4.62% Signed-off-by: Rasmus Villemoes Reviewed-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 57 ++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 45c20307630a..fb1441315bc3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2158,40 +2158,37 @@ done: * macro to avoid endless duplication. If the desc is NULL it is an * optional GPIO and calls should just bail out. */ +static int validate_desc(const struct gpio_desc *desc, const char *func) +{ + if (!desc) + return 0; + if (IS_ERR(desc)) { + pr_warn("%s: invalid GPIO (errorpointer)\n", func); + return PTR_ERR(desc); + } + if (!desc->gdev) { + pr_warn("%s: invalid GPIO (no device)\n", func); + return -EINVAL; + } + if (!desc->gdev->chip) { + dev_warn(&desc->gdev->dev, + "%s: backing chip is gone\n", func); + return 0; + } + return 1; +} + #define VALIDATE_DESC(desc) do { \ - if (!desc) \ - return 0; \ - if (IS_ERR(desc)) { \ - pr_warn("%s: invalid GPIO (errorpointer)\n", __func__); \ - return PTR_ERR(desc); \ - } \ - if (!desc->gdev) { \ - pr_warn("%s: invalid GPIO (no device)\n", __func__); \ - return -EINVAL; \ - } \ - if ( !desc->gdev->chip ) { \ - dev_warn(&desc->gdev->dev, \ - "%s: backing chip is gone\n", __func__); \ - return 0; \ - } } while (0) + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return __valid; \ + } while (0) #define VALIDATE_DESC_VOID(desc) do { \ - if (!desc) \ - return; \ - if (IS_ERR(desc)) { \ - pr_warn("%s: invalid GPIO (errorpointer)\n", __func__); \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ return; \ - } \ - if (!desc->gdev) { \ - pr_warn("%s: invalid GPIO (no device)\n", __func__); \ - return; \ - } \ - if (!desc->gdev->chip) { \ - dev_warn(&desc->gdev->dev, \ - "%s: backing chip is gone\n", __func__); \ - return; \ - } } while (0) - + } while (0) int gpiod_request(struct gpio_desc *desc, const char *label) { -- cgit v1.2.3 From 689fd02e81e3876a91790e4fec2e8c3852c94745 Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Thu, 21 Dec 2017 17:56:34 +0100 Subject: gpiolib: Fix typo in comment ("piochip_add_data") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Neuschäfer Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index fb1441315bc3..0ae49943dfaf 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1374,7 +1374,7 @@ static int devm_gpio_chip_match(struct device *dev, void *res, void *data) } /** - * devm_gpiochip_add_data() - Resource manager piochip_add_data() + * devm_gpiochip_add_data() - Resource manager gpiochip_add_data() * @dev: the device pointer on which irq_chip belongs to. * @chip: the chip to register, with chip->base initialized * @data: driver-private data associated with this chip -- cgit v1.2.3 From bd8ed930af7d3e325d53cb6c976c59abb885c1fc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 28 Dec 2017 13:32:53 +0100 Subject: Revert "pinctrl: qcom: disable GPIO groups with no pins" This reverts commit 93ebe8636bb0d95e2e711f2a53abbb72a9d9cf8d. After discussion and review of the v11 patchset, a new approach was found so that this patch is not needed. Signed-off-by: Linus Walleij --- drivers/pinctrl/qcom/pinctrl-msm.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index d45b4c2b5af1..7a960590ecaa 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -507,11 +507,6 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, }; g = &pctrl->soc->groups[offset]; - - /* If the GPIO group has no pins, then don't show it. */ - if (!g->npins) - return; - ctl_reg = readl(pctrl->regs + g->ctl_reg); is_out = !!(ctl_reg & BIT(g->oe_bit)); @@ -521,7 +516,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func); seq_printf(s, " %dmA", msm_regval_to_drive(drive)); - seq_printf(s, " %s\n", pulls[pull]); + seq_printf(s, " %s", pulls[pull]); } static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -529,36 +524,23 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) unsigned gpio = chip->base; unsigned i; - for (i = 0; i < chip->ngpio; i++, gpio++) + for (i = 0; i < chip->ngpio; i++, gpio++) { msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); + seq_puts(s, "\n"); + } } #else #define msm_gpio_dbg_show NULL #endif -/* - * If the requested GPIO has no pins, then treat it as unavailable. - * Otherwise, call the standard request function. - */ -static int msm_gpio_request(struct gpio_chip *chip, unsigned int offset) -{ - struct msm_pinctrl *pctrl = gpiochip_get_data(chip); - const struct msm_pingroup *g = &pctrl->soc->groups[offset]; - - if (!g->npins) - return -EACCES; - - return gpiochip_generic_request(chip, offset); -} - static const struct gpio_chip msm_gpio_template = { .direction_input = msm_gpio_direction_input, .direction_output = msm_gpio_direction_output, .get_direction = msm_gpio_get_direction, .get = msm_gpio_get, .set = msm_gpio_set, - .request = msm_gpio_request, + .request = gpiochip_generic_request, .free = gpiochip_generic_free, .dbg_show = msm_gpio_dbg_show, }; -- cgit v1.2.3 From fe13862c9cad6f7d7d4faa97f546eb94d4989f44 Mon Sep 17 00:00:00 2001 From: Vasyl Gomonovych Date: Thu, 21 Dec 2017 16:55:10 +0100 Subject: gpio: fix aspeed_gpio_banks array size check The test should be >= ARRAY_SIZE() instead of > ARRAY_SIZE(). Signed-off-by: Vasyl Gomonovych Signed-off-by: Linus Walleij --- drivers/gpio/gpio-aspeed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 30bc97b81bec..77e485557498 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -149,7 +149,7 @@ static const struct aspeed_gpio_bank *to_bank(unsigned int offset) { unsigned int bank = GPIO_BANK(offset); - WARN_ON(bank > ARRAY_SIZE(aspeed_gpio_banks)); + WARN_ON(bank >= ARRAY_SIZE(aspeed_gpio_banks)); return &aspeed_gpio_banks[bank]; } -- cgit v1.2.3 From 30322bcf82d74cad0d6e1cf9ba7fa7fa48c7a026 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 21 Dec 2017 18:37:24 +0200 Subject: gpiolib: don't dereference a desc before validation The fix restores a proper validation of an input gpio desc, which might be needed to deal with optional GPIOs correctly. Fixes: 02e479808b5d ("gpio: Alter semantics of *raw* operations to actually be raw") Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0ae49943dfaf..02cf658eeedd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2437,7 +2437,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw); */ int gpiod_direction_output(struct gpio_desc *desc, int value) { - struct gpio_chip *gc = desc->gdev->chip; + struct gpio_chip *gc; int ret; VALIDATE_DESC(desc); @@ -2454,6 +2454,7 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) return -EIO; } + gc = desc->gdev->chip; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { /* First see if we can enable open drain in hardware */ ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), -- cgit v1.2.3 From dd3b9a44083d3815517a692f8181be8c30c0a1aa Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 21 Dec 2017 18:37:30 +0200 Subject: gpiolib: remove a redundant check in gpiod_to_chip() This non-functional change slightly simplifies the implementation of gpiod_to_chip() function. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 02cf658eeedd..c23cc6b586ba 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -161,7 +161,7 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { - if (!desc || !desc->gdev || !desc->gdev->chip) + if (!desc || !desc->gdev) return NULL; return desc->gdev->chip; } -- cgit v1.2.3 From 156dd39219b159d03762af32f1a0acd05d0cd726 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 21 Dec 2017 18:37:35 +0200 Subject: gpiolib: add desc validation to gpiod_set_transitory() The gpiod_set_transitory() function is publicly exported, and it is expected from it to be ready for usage with optional GPIOs on consumer's side. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c23cc6b586ba..7dde703b6da2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2526,6 +2526,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) int gpio; int rc; + VALIDATE_DESC(desc); /* * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for * persistence state. -- cgit v1.2.3 From 088413bc0bd5f5fb66ca22a19d66a49d7154ba4c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 29 Dec 2017 13:22:58 +0100 Subject: gpio: omap: Give unique labels to each GPIO bank/chip As we need to add GPIO lookup tables to the OMAP platforms, we need to reference each GPIO chip with a unique label. Use the GPIO base to name each chip, "gpio-0-31", "gpio-32-63" etc. Cc: Grygorii Strashko Cc: Santosh Shilimkar Cc: Kevin Hilman Cc: linux-omap@vger.kernel.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 05bae9f42f52..ab5035b96886 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1060,6 +1060,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) { struct gpio_irq_chip *irq; static int gpio; + const char *label; int irq_base = 0; int ret; @@ -1081,7 +1082,11 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) bank->chip.parent = &omap_mpuio_device.dev; bank->chip.base = OMAP_MPUIO(0); } else { - bank->chip.label = "gpio"; + label = devm_kasprintf(bank->chip.parent, GFP_KERNEL, "gpio-%d-%d", + gpio, gpio + bank->width - 1); + if (!label) + return -ENOMEM; + bank->chip.label = label; bank->chip.base = gpio; } bank->chip.ngpio = bank->width; -- cgit v1.2.3 From 24e78079bf2250874e33da2e7cfbb6db72d3caf4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 4 Jan 2018 22:31:11 +0100 Subject: gpio: label descriptors using the device name Some GPIO lines appear named "?" in the lsgpio dump due to their requesting drivers not passing a reasonable label. Most typically this happens if a device tree node just defines gpios = <...> and not foo-gpios = <...>, the former gets named "foo" and the latter gets named "?". However the struct device passed in is always valid so let's just label the GPIO with dev_name() on the device if no proper label was passed. Cc: Reported-by: Jason Kridner Reported-by: Jason Kridner Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 7dde703b6da2..986293143068 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3650,7 +3650,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, return desc; } - status = gpiod_request(desc, con_id); + /* If a connection label was passed use that, else use the device name as label */ + status = gpiod_request(desc, con_id ? con_id : dev_name(dev)); if (status < 0) return ERR_PTR(status); -- cgit v1.2.3 From c85823390215e52d68d3826df92a447ed31e5c80 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 27 Dec 2017 16:37:44 +0100 Subject: gpio: of: Support SPI nonstandard GPIO properties Before it was clearly established that all GPIO properties in the device tree shall be named "foo-gpios" (with the deprecated variant "foo-gpio" for single lines) we unfortunately merged a few bindings which named the lines "gpio-foo" instead. This is most prominent in the GPIO SPI driver in Linux which names the lines "gpio-sck", "gpio-mosi" and "gpio-miso". As we want to switch the GPIO SPI driver to using descriptors, we need devm_gpiod_get() to return something reasonable when looking up these in the device tree. Put in a special #ifdef:ed kludge to do this special lookup only for the SPI case and gets compiled out if we're not enabling SPI. If we have more oddly defined legacy GPIOs like this, they can be handled in a similar manner. Reviewed-by: Rob Herring Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 4a2b8d3397c7..13acd0378884 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -117,6 +117,36 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name, } EXPORT_SYMBOL(of_get_named_gpio_flags); +/* + * The SPI GPIO bindings happened before we managed to establish that GPIO + * properties should be named "foo-gpios" so we have this special kludge for + * them. + */ +static struct gpio_desc *of_find_spi_gpio(struct device *dev, const char *con_id, + enum of_gpio_flags *of_flags) +{ + char prop_name[32]; /* 32 is max size of property name */ + struct device_node *np = dev->of_node; + struct gpio_desc *desc; + + /* + * Hopefully the compiler stubs the rest of the function if this + * is false. + */ + if (!IS_ENABLED(CONFIG_SPI_MASTER)) + return ERR_PTR(-ENOENT); + + /* Allow this specifically for "spi-gpio" devices */ + if (!of_device_is_compatible(np, "spi-gpio") || !con_id) + return ERR_PTR(-ENOENT); + + /* Will be "gpio-sck", "gpio-mosi" or "gpio-miso" */ + snprintf(prop_name, sizeof(prop_name), "%s-%s", "gpio", con_id); + + desc = of_get_named_gpiod_flags(np, prop_name, 0, of_flags); + return desc; +} + struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags) @@ -126,6 +156,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, struct gpio_desc *desc; unsigned int i; + /* Try GPIO property "foo-gpios" and "foo-gpio" */ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id, @@ -140,6 +171,10 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, break; } + /* Special handling for SPI GPIOs if used */ + if (IS_ERR(desc)) + desc = of_find_spi_gpio(dev, con_id, &of_flags); + if (IS_ERR(desc)) return desc; -- cgit v1.2.3 From a0d65009411c620a9e7140d6a83b9d5aeb8f1801 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 6 Jan 2018 00:46:08 +0100 Subject: gpio: winbond: Add driver This commit adds GPIO driver for Winbond Super I/Os. Currently, only W83627UHG model (also known as Nuvoton NCT6627UD) is supported but in the future a support for other Winbond models, too, can be added to the driver. A module parameter "gpios" sets a bitmask of GPIO ports to enable (bit 0 is GPIO1, bit 1 is GPIO2, etc.). One should be careful which ports one tinkers with since some might be managed by the firmware (for functions like powering on and off, sleeping, BIOS recovery, etc.) and some of GPIO port pins are physically shared with other devices included in the Super I/O chip. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 16 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-winbond.c | 732 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 749 insertions(+) create mode 100644 drivers/gpio/gpio-winbond.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 395669bfcc26..8c4018855c3b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -698,6 +698,22 @@ config GPIO_TS5500 blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 LCD port. +config GPIO_WINBOND + tristate "Winbond Super I/O GPIO support" + select ISA_BUS_API + help + This option enables support for GPIOs found on Winbond Super I/O + chips. + Currently, only W83627UHG (also known as Nuvoton NCT6627UD) is + supported. + + You will need to provide a module parameter "gpios", or a + boot-time parameter "gpio_winbond.gpios" with a bitmask of GPIO + ports to enable (bit 0 is GPIO1, bit 1 is GPIO2, etc.). + + To compile this driver as a module, choose M here: the module will + be called gpio-winbond. + config GPIO_WS16C48 tristate "WinSystems WS16C48 GPIO support" depends on ISA_BUS_API diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index bc5dd673fa11..ff3d36d0a443 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -139,6 +139,7 @@ obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o +obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o diff --git a/drivers/gpio/gpio-winbond.c b/drivers/gpio/gpio-winbond.c new file mode 100644 index 000000000000..7f8f5b02e31d --- /dev/null +++ b/drivers/gpio/gpio-winbond.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * GPIO interface for Winbond Super I/O chips + * Currently, only W83627UHG (Nuvoton NCT6627UD) is supported. + * + * Author: Maciej S. Szmigiero + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +#define WB_GPIO_DRIVER_NAME KBUILD_MODNAME + +#define WB_SIO_BASE 0x2e +#define WB_SIO_BASE_HIGH 0x4e + +#define WB_SIO_EXT_ENTER_KEY 0x87 +#define WB_SIO_EXT_EXIT_KEY 0xaa + +/* global chip registers */ + +#define WB_SIO_REG_LOGICAL 0x07 + +#define WB_SIO_REG_CHIP_MSB 0x20 +#define WB_SIO_REG_CHIP_LSB 0x21 + +#define WB_SIO_CHIP_ID_W83627UHG 0xa230 +#define WB_SIO_CHIP_ID_W83627UHG_MASK GENMASK(15, 4) + +#define WB_SIO_REG_DPD 0x22 +#define WB_SIO_REG_DPD_UARTA 4 +#define WB_SIO_REG_DPD_UARTB 5 + +#define WB_SIO_REG_IDPD 0x23 +#define WB_SIO_REG_IDPD_UARTC 4 +#define WB_SIO_REG_IDPD_UARTD 5 +#define WB_SIO_REG_IDPD_UARTE 6 +#define WB_SIO_REG_IDPD_UARTF 7 + +#define WB_SIO_REG_GLOBAL_OPT 0x24 +#define WB_SIO_REG_GO_ENFDC 1 + +#define WB_SIO_REG_OVTGPIO3456 0x29 +#define WB_SIO_REG_OG3456_G3PP 3 +#define WB_SIO_REG_OG3456_G4PP 4 +#define WB_SIO_REG_OG3456_G5PP 5 +#define WB_SIO_REG_OG3456_G6PP 7 + +#define WB_SIO_REG_I2C_PS 0x2a +#define WB_SIO_REG_I2CPS_I2CFS 1 + +#define WB_SIO_REG_GPIO1_MF 0x2c +#define WB_SIO_REG_G1MF_G1PP 6 +#define WB_SIO_REG_G1MF_G2PP 7 +#define WB_SIO_REG_G1MF_FS_MASK GENMASK(1, 0) +#define WB_SIO_REG_G1MF_FS_IR_OFF 0 +#define WB_SIO_REG_G1MF_FS_IR 1 +#define WB_SIO_REG_G1MF_FS_GPIO1 2 +#define WB_SIO_REG_G1MF_FS_UARTB 3 + +/* not an actual device number, just a value meaning 'no device' */ +#define WB_SIO_DEV_NONE 0xff + +/* registers with offsets >= 0x30 are specific for a particular device */ + +/* UART B logical device */ +#define WB_SIO_DEV_UARTB 0x03 +#define WB_SIO_UARTB_REG_ENABLE 0x30 +#define WB_SIO_UARTB_ENABLE_ON 0 + +/* UART C logical device */ +#define WB_SIO_DEV_UARTC 0x06 +#define WB_SIO_UARTC_REG_ENABLE 0x30 +#define WB_SIO_UARTC_ENABLE_ON 0 + +/* GPIO3, GPIO4 logical device */ +#define WB_SIO_DEV_GPIO34 0x07 +#define WB_SIO_GPIO34_REG_ENABLE 0x30 +#define WB_SIO_GPIO34_ENABLE_3 0 +#define WB_SIO_GPIO34_ENABLE_4 1 +#define WB_SIO_GPIO34_REG_IO3 0xe0 +#define WB_SIO_GPIO34_REG_DATA3 0xe1 +#define WB_SIO_GPIO34_REG_INV3 0xe2 +#define WB_SIO_GPIO34_REG_IO4 0xe4 +#define WB_SIO_GPIO34_REG_DATA4 0xe5 +#define WB_SIO_GPIO34_REG_INV4 0xe6 + +/* WDTO, PLED, GPIO5, GPIO6 logical device */ +#define WB_SIO_DEV_WDGPIO56 0x08 +#define WB_SIO_WDGPIO56_REG_ENABLE 0x30 +#define WB_SIO_WDGPIO56_ENABLE_5 1 +#define WB_SIO_WDGPIO56_ENABLE_6 2 +#define WB_SIO_WDGPIO56_REG_IO5 0xe0 +#define WB_SIO_WDGPIO56_REG_DATA5 0xe1 +#define WB_SIO_WDGPIO56_REG_INV5 0xe2 +#define WB_SIO_WDGPIO56_REG_IO6 0xe4 +#define WB_SIO_WDGPIO56_REG_DATA6 0xe5 +#define WB_SIO_WDGPIO56_REG_INV6 0xe6 + +/* GPIO1, GPIO2, SUSLED logical device */ +#define WB_SIO_DEV_GPIO12 0x09 +#define WB_SIO_GPIO12_REG_ENABLE 0x30 +#define WB_SIO_GPIO12_ENABLE_1 0 +#define WB_SIO_GPIO12_ENABLE_2 1 +#define WB_SIO_GPIO12_REG_IO1 0xe0 +#define WB_SIO_GPIO12_REG_DATA1 0xe1 +#define WB_SIO_GPIO12_REG_INV1 0xe2 +#define WB_SIO_GPIO12_REG_IO2 0xe4 +#define WB_SIO_GPIO12_REG_DATA2 0xe5 +#define WB_SIO_GPIO12_REG_INV2 0xe6 + +/* UART D logical device */ +#define WB_SIO_DEV_UARTD 0x0d +#define WB_SIO_UARTD_REG_ENABLE 0x30 +#define WB_SIO_UARTD_ENABLE_ON 0 + +/* UART E logical device */ +#define WB_SIO_DEV_UARTE 0x0e +#define WB_SIO_UARTE_REG_ENABLE 0x30 +#define WB_SIO_UARTE_ENABLE_ON 0 + +/* + * for a description what a particular field of this struct means please see + * a description of the relevant module parameter at the bottom of this file + */ +struct winbond_gpio_params { + unsigned long base; + unsigned long gpios; + unsigned long ppgpios; + unsigned long odgpios; + bool pledgpio; + bool beepgpio; + bool i2cgpio; +}; + +static struct winbond_gpio_params params; + +static int winbond_sio_enter(unsigned long base) +{ + if (!request_muxed_region(base, 2, WB_GPIO_DRIVER_NAME)) + return -EBUSY; + + /* + * datasheet says two successive writes of the "key" value are needed + * in order for chip to enter the "Extended Function Mode" + */ + outb(WB_SIO_EXT_ENTER_KEY, base); + outb(WB_SIO_EXT_ENTER_KEY, base); + + return 0; +} + +static void winbond_sio_select_logical(unsigned long base, u8 dev) +{ + outb(WB_SIO_REG_LOGICAL, base); + outb(dev, base + 1); +} + +static void winbond_sio_leave(unsigned long base) +{ + outb(WB_SIO_EXT_EXIT_KEY, base); + + release_region(base, 2); +} + +static void winbond_sio_reg_write(unsigned long base, u8 reg, u8 data) +{ + outb(reg, base); + outb(data, base + 1); +} + +static u8 winbond_sio_reg_read(unsigned long base, u8 reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static void winbond_sio_reg_bset(unsigned long base, u8 reg, u8 bit) +{ + u8 val; + + val = winbond_sio_reg_read(base, reg); + val |= BIT(bit); + winbond_sio_reg_write(base, reg, val); +} + +static void winbond_sio_reg_bclear(unsigned long base, u8 reg, u8 bit) +{ + u8 val; + + val = winbond_sio_reg_read(base, reg); + val &= ~BIT(bit); + winbond_sio_reg_write(base, reg, val); +} + +static bool winbond_sio_reg_btest(unsigned long base, u8 reg, u8 bit) +{ + return winbond_sio_reg_read(base, reg) & BIT(bit); +} + +/** + * struct winbond_gpio_port_conflict - possibly conflicting device information + * @name: device name (NULL means no conflicting device defined) + * @dev: Super I/O logical device number where the testreg register + * is located (or WB_SIO_DEV_NONE - don't select any + * logical device) + * @testreg: register number where the testbit bit is located + * @testbit: index of a bit to check whether an actual conflict exists + * @warnonly: if set then a conflict isn't fatal (just warn about it), + * otherwise disable the particular GPIO port if a conflict + * is detected + */ +struct winbond_gpio_port_conflict { + const char *name; + u8 dev; + u8 testreg; + u8 testbit; + bool warnonly; +}; + +/** + * struct winbond_gpio_info - information about a particular GPIO port (device) + * @dev: Super I/O logical device number of the registers + * specified below + * @enablereg: port enable bit register number + * @enablebit: index of a port enable bit + * @outputreg: output driver mode bit register number + * @outputppbit: index of a push-pull output driver mode bit + * @ioreg: data direction register number + * @invreg: pin data inversion register number + * @datareg: pin data register number + * @conflict: description of a device that possibly conflicts with + * this port + */ +struct winbond_gpio_info { + u8 dev; + u8 enablereg; + u8 enablebit; + u8 outputreg; + u8 outputppbit; + u8 ioreg; + u8 invreg; + u8 datareg; + struct winbond_gpio_port_conflict conflict; +}; + +static const struct winbond_gpio_info winbond_gpio_infos[6] = { + { /* 0 */ + .dev = WB_SIO_DEV_GPIO12, + .enablereg = WB_SIO_GPIO12_REG_ENABLE, + .enablebit = WB_SIO_GPIO12_ENABLE_1, + .outputreg = WB_SIO_REG_GPIO1_MF, + .outputppbit = WB_SIO_REG_G1MF_G1PP, + .ioreg = WB_SIO_GPIO12_REG_IO1, + .invreg = WB_SIO_GPIO12_REG_INV1, + .datareg = WB_SIO_GPIO12_REG_DATA1, + .conflict = { + .name = "UARTB", + .dev = WB_SIO_DEV_UARTB, + .testreg = WB_SIO_UARTB_REG_ENABLE, + .testbit = WB_SIO_UARTB_ENABLE_ON, + .warnonly = true + } + }, + { /* 1 */ + .dev = WB_SIO_DEV_GPIO12, + .enablereg = WB_SIO_GPIO12_REG_ENABLE, + .enablebit = WB_SIO_GPIO12_ENABLE_2, + .outputreg = WB_SIO_REG_GPIO1_MF, + .outputppbit = WB_SIO_REG_G1MF_G2PP, + .ioreg = WB_SIO_GPIO12_REG_IO2, + .invreg = WB_SIO_GPIO12_REG_INV2, + .datareg = WB_SIO_GPIO12_REG_DATA2 + /* special conflict handling so doesn't use conflict data */ + }, + { /* 2 */ + .dev = WB_SIO_DEV_GPIO34, + .enablereg = WB_SIO_GPIO34_REG_ENABLE, + .enablebit = WB_SIO_GPIO34_ENABLE_3, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G3PP, + .ioreg = WB_SIO_GPIO34_REG_IO3, + .invreg = WB_SIO_GPIO34_REG_INV3, + .datareg = WB_SIO_GPIO34_REG_DATA3, + .conflict = { + .name = "UARTC", + .dev = WB_SIO_DEV_UARTC, + .testreg = WB_SIO_UARTC_REG_ENABLE, + .testbit = WB_SIO_UARTC_ENABLE_ON, + .warnonly = true + } + }, + { /* 3 */ + .dev = WB_SIO_DEV_GPIO34, + .enablereg = WB_SIO_GPIO34_REG_ENABLE, + .enablebit = WB_SIO_GPIO34_ENABLE_4, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G4PP, + .ioreg = WB_SIO_GPIO34_REG_IO4, + .invreg = WB_SIO_GPIO34_REG_INV4, + .datareg = WB_SIO_GPIO34_REG_DATA4, + .conflict = { + .name = "UARTD", + .dev = WB_SIO_DEV_UARTD, + .testreg = WB_SIO_UARTD_REG_ENABLE, + .testbit = WB_SIO_UARTD_ENABLE_ON, + .warnonly = true + } + }, + { /* 4 */ + .dev = WB_SIO_DEV_WDGPIO56, + .enablereg = WB_SIO_WDGPIO56_REG_ENABLE, + .enablebit = WB_SIO_WDGPIO56_ENABLE_5, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G5PP, + .ioreg = WB_SIO_WDGPIO56_REG_IO5, + .invreg = WB_SIO_WDGPIO56_REG_INV5, + .datareg = WB_SIO_WDGPIO56_REG_DATA5, + .conflict = { + .name = "UARTE", + .dev = WB_SIO_DEV_UARTE, + .testreg = WB_SIO_UARTE_REG_ENABLE, + .testbit = WB_SIO_UARTE_ENABLE_ON, + .warnonly = true + } + }, + { /* 5 */ + .dev = WB_SIO_DEV_WDGPIO56, + .enablereg = WB_SIO_WDGPIO56_REG_ENABLE, + .enablebit = WB_SIO_WDGPIO56_ENABLE_6, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G6PP, + .ioreg = WB_SIO_WDGPIO56_REG_IO6, + .invreg = WB_SIO_WDGPIO56_REG_INV6, + .datareg = WB_SIO_WDGPIO56_REG_DATA6, + .conflict = { + .name = "FDC", + .dev = WB_SIO_DEV_NONE, + .testreg = WB_SIO_REG_GLOBAL_OPT, + .testbit = WB_SIO_REG_GO_ENFDC, + .warnonly = false + } + } +}; + +/* returns whether changing a pin is allowed */ +static bool winbond_gpio_get_info(unsigned int *gpio_num, + const struct winbond_gpio_info **info) +{ + bool allow_changing = true; + unsigned long i; + + for_each_set_bit(i, ¶ms.gpios, BITS_PER_LONG) { + if (*gpio_num < 8) + break; + + *gpio_num -= 8; + } + + *info = &winbond_gpio_infos[i]; + + /* + * GPIO2 (the second port) shares some pins with a basic PC + * functionality, which is very likely controlled by the firmware. + * Don't allow changing these pins by default. + */ + if (i == 1) { + if (*gpio_num == 0 && !params.pledgpio) + allow_changing = false; + else if (*gpio_num == 1 && !params.beepgpio) + allow_changing = false; + else if ((*gpio_num == 5 || *gpio_num == 6) && !params.i2cgpio) + allow_changing = false; + } + + return allow_changing; +} + +static int winbond_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + bool val; + + winbond_gpio_get_info(&offset, &info); + + val = winbond_sio_enter(*base); + if (val) + return val; + + winbond_sio_select_logical(*base, info->dev); + + val = winbond_sio_reg_btest(*base, info->datareg, offset); + if (winbond_sio_reg_btest(*base, info->invreg, offset)) + val = !val; + + winbond_sio_leave(*base); + + return val; +} + +static int winbond_gpio_direction_in(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + int ret; + + if (!winbond_gpio_get_info(&offset, &info)) + return -EACCES; + + ret = winbond_sio_enter(*base); + if (ret) + return ret; + + winbond_sio_select_logical(*base, info->dev); + + winbond_sio_reg_bset(*base, info->ioreg, offset); + + winbond_sio_leave(*base); + + return 0; +} + +static int winbond_gpio_direction_out(struct gpio_chip *gc, + unsigned int offset, + int val) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + int ret; + + if (!winbond_gpio_get_info(&offset, &info)) + return -EACCES; + + ret = winbond_sio_enter(*base); + if (ret) + return ret; + + winbond_sio_select_logical(*base, info->dev); + + winbond_sio_reg_bclear(*base, info->ioreg, offset); + + if (winbond_sio_reg_btest(*base, info->invreg, offset)) + val = !val; + + if (val) + winbond_sio_reg_bset(*base, info->datareg, offset); + else + winbond_sio_reg_bclear(*base, info->datareg, offset); + + winbond_sio_leave(*base); + + return 0; +} + +static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, + int val) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + + if (!winbond_gpio_get_info(&offset, &info)) + return; + + if (winbond_sio_enter(*base) != 0) + return; + + winbond_sio_select_logical(*base, info->dev); + + if (winbond_sio_reg_btest(*base, info->invreg, offset)) + val = !val; + + if (val) + winbond_sio_reg_bset(*base, info->datareg, offset); + else + winbond_sio_reg_bclear(*base, info->datareg, offset); + + winbond_sio_leave(*base); +} + +static struct gpio_chip winbond_gpio_chip = { + .base = -1, + .label = WB_GPIO_DRIVER_NAME, + .owner = THIS_MODULE, + .can_sleep = true, + .get = winbond_gpio_get, + .direction_input = winbond_gpio_direction_in, + .set = winbond_gpio_set, + .direction_output = winbond_gpio_direction_out, +}; + +static void winbond_gpio_configure_port0_pins(unsigned long base) +{ + unsigned int val; + + val = winbond_sio_reg_read(base, WB_SIO_REG_GPIO1_MF); + if ((val & WB_SIO_REG_G1MF_FS_MASK) == WB_SIO_REG_G1MF_FS_GPIO1) + return; + + pr_warn("GPIO1 pins were connected to something else (%.2x), fixing\n", + val); + + val &= ~WB_SIO_REG_G1MF_FS_MASK; + val |= WB_SIO_REG_G1MF_FS_GPIO1; + + winbond_sio_reg_write(base, WB_SIO_REG_GPIO1_MF, val); +} + +static void winbond_gpio_configure_port1_check_i2c(unsigned long base) +{ + params.i2cgpio = !winbond_sio_reg_btest(base, WB_SIO_REG_I2C_PS, + WB_SIO_REG_I2CPS_I2CFS); + if (!params.i2cgpio) + pr_warn("disabling GPIO2.5 and GPIO2.6 as I2C is enabled\n"); +} + +static bool winbond_gpio_configure_port(unsigned long base, unsigned int idx) +{ + const struct winbond_gpio_info *info = &winbond_gpio_infos[idx]; + const struct winbond_gpio_port_conflict *conflict = &info->conflict; + + /* is there a possible conflicting device defined? */ + if (conflict->name != NULL) { + if (conflict->dev != WB_SIO_DEV_NONE) + winbond_sio_select_logical(base, conflict->dev); + + if (winbond_sio_reg_btest(base, conflict->testreg, + conflict->testbit)) { + if (conflict->warnonly) + pr_warn("enabled GPIO%u share pins with active %s\n", + idx + 1, conflict->name); + else { + pr_warn("disabling GPIO%u as %s is enabled\n", + idx + 1, conflict->name); + return false; + } + } + } + + /* GPIO1 and GPIO2 need some (additional) special handling */ + if (idx == 0) + winbond_gpio_configure_port0_pins(base); + else if (idx == 1) + winbond_gpio_configure_port1_check_i2c(base); + + winbond_sio_select_logical(base, info->dev); + + winbond_sio_reg_bset(base, info->enablereg, info->enablebit); + + if (params.ppgpios & BIT(idx)) + winbond_sio_reg_bset(base, info->outputreg, + info->outputppbit); + else if (params.odgpios & BIT(idx)) + winbond_sio_reg_bclear(base, info->outputreg, + info->outputppbit); + else + pr_notice("GPIO%u pins are %s\n", idx + 1, + winbond_sio_reg_btest(base, info->outputreg, + info->outputppbit) ? + "push-pull" : + "open drain"); + + return true; +} + +static int winbond_gpio_configure(unsigned long base) +{ + unsigned long i; + + for_each_set_bit(i, ¶ms.gpios, BITS_PER_LONG) + if (!winbond_gpio_configure_port(base, i)) + __clear_bit(i, ¶ms.gpios); + + if (!params.gpios) { + pr_err("please use 'gpios' module parameter to select some active GPIO ports to enable\n"); + return -EINVAL; + } + + return 0; +} + +static int winbond_gpio_check_chip(unsigned long base) +{ + int ret; + unsigned int chip; + + ret = winbond_sio_enter(base); + if (ret) + return ret; + + chip = winbond_sio_reg_read(base, WB_SIO_REG_CHIP_MSB) << 8; + chip |= winbond_sio_reg_read(base, WB_SIO_REG_CHIP_LSB); + + pr_notice("chip ID at %lx is %.4x\n", base, chip); + + if ((chip & WB_SIO_CHIP_ID_W83627UHG_MASK) != + WB_SIO_CHIP_ID_W83627UHG) { + pr_err("not an our chip\n"); + ret = -ENODEV; + } + + winbond_sio_leave(base); + + return ret; +} + +static int winbond_gpio_imatch(struct device *dev, unsigned int id) +{ + unsigned long gpios_rem; + int ret; + + gpios_rem = params.gpios & ~GENMASK(ARRAY_SIZE(winbond_gpio_infos) - 1, + 0); + if (gpios_rem) { + pr_warn("unknown ports (%lx) enabled in GPIO ports bitmask\n", + gpios_rem); + params.gpios &= ~gpios_rem; + } + + if (params.ppgpios & params.odgpios) { + pr_err("some GPIO ports are set both to push-pull and open drain mode at the same time\n"); + return 0; + } + + if (params.base != 0) + return winbond_gpio_check_chip(params.base) == 0; + + /* + * if the 'base' module parameter is unset probe two chip default + * I/O port bases + */ + params.base = WB_SIO_BASE; + ret = winbond_gpio_check_chip(params.base); + if (ret == 0) + return 1; + if (ret != -ENODEV && ret != -EBUSY) + return 0; + + params.base = WB_SIO_BASE_HIGH; + return winbond_gpio_check_chip(params.base) == 0; +} + +static int winbond_gpio_iprobe(struct device *dev, unsigned int id) +{ + int ret; + + if (params.base == 0) + return -EINVAL; + + ret = winbond_sio_enter(params.base); + if (ret) + return ret; + + ret = winbond_gpio_configure(params.base); + + winbond_sio_leave(params.base); + + if (ret) + return ret; + + /* + * Add 8 gpios for every GPIO port that was enabled in gpios + * module parameter (that wasn't disabled earlier in + * winbond_gpio_configure() & co. due to, for example, a pin conflict). + */ + winbond_gpio_chip.ngpio = hweight_long(params.gpios) * 8; + + /* + * GPIO6 port has only 5 pins, so if it is enabled we have to adjust + * the total count appropriately + */ + if (params.gpios & BIT(5)) + winbond_gpio_chip.ngpio -= (8 - 5); + + winbond_gpio_chip.parent = dev; + + return devm_gpiochip_add_data(dev, &winbond_gpio_chip, ¶ms.base); +} + +static struct isa_driver winbond_gpio_idriver = { + .driver = { + .name = WB_GPIO_DRIVER_NAME, + }, + .match = winbond_gpio_imatch, + .probe = winbond_gpio_iprobe, +}; + +module_isa_driver(winbond_gpio_idriver, 1); + +module_param_named(base, params.base, ulong, 0444); +MODULE_PARM_DESC(base, + "I/O port base (when unset - probe chip default ones)"); + +/* This parameter sets which GPIO devices (ports) we enable */ +module_param_named(gpios, params.gpios, ulong, 0444); +MODULE_PARM_DESC(gpios, + "bitmask of GPIO ports to enable (bit 0 - GPIO1, bit 1 - GPIO2, etc."); + +/* + * These two parameters below set how we configure GPIO ports output drivers. + * It can't be a one bitmask since we need three values per port: push-pull, + * open-drain and keep as-is (this is the default). + */ +module_param_named(ppgpios, params.ppgpios, ulong, 0444); +MODULE_PARM_DESC(ppgpios, + "bitmask of GPIO ports to set to push-pull mode (bit 0 - GPIO1, bit 1 - GPIO2, etc."); + +module_param_named(odgpios, params.odgpios, ulong, 0444); +MODULE_PARM_DESC(odgpios, + "bitmask of GPIO ports to set to open drain mode (bit 0 - GPIO1, bit 1 - GPIO2, etc."); + +/* + * GPIO2.0 and GPIO2.1 control a basic PC functionality that we + * don't allow tinkering with by default (it is very likely that the + * firmware owns these pins). + * These two parameters below allow overriding these prohibitions. + */ +module_param_named(pledgpio, params.pledgpio, bool, 0644); +MODULE_PARM_DESC(pledgpio, + "enable changing value of GPIO2.0 bit (Power LED), default no."); + +module_param_named(beepgpio, params.beepgpio, bool, 0644); +MODULE_PARM_DESC(beepgpio, + "enable changing value of GPIO2.1 bit (BEEP), default no."); + +MODULE_AUTHOR("Maciej S. Szmigiero "); +MODULE_DESCRIPTION("GPIO interface for Winbond Super I/O chips"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 94fc73094abe4798be8d4af60227be41a6dc3be8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 9 Jan 2018 12:35:53 +0100 Subject: gpio: correct docs about return value of gpiod_get_direction The use of the GPIOF_* flags is deprecated, so don't advertise them here. Document the plain numbers for now until we have a better solution. Signed-off-by: Wolfram Sang Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 986293143068..0e78a5023cb2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -195,7 +195,7 @@ static int gpiochip_find_base(int ngpio) * gpiod_get_direction - return the current direction of a GPIO * @desc: GPIO to get the direction of * - * Return GPIOF_DIR_IN or GPIOF_DIR_OUT, or an error code in case of error. + * Returns 0 for output, 1 for input, or an error code in case of error. * * This function may sleep if gpiod_cansleep() is true. */ -- cgit v1.2.3 From 9961dbcd228e868224f5f725dfae69de739855dc Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 9 Jan 2018 12:35:54 +0100 Subject: Documentation: gpio: correct return value of gpiod_get_direction The use of the GPIOF_* flags is deprecated, so don't advertise them here. Document the plain numbers for now until we have a better solution. Signed-off-by: Wolfram Sang Signed-off-by: Linus Walleij --- Documentation/gpio/consumer.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt index 63e1bd1d88e3..7aac33081510 100644 --- a/Documentation/gpio/consumer.txt +++ b/Documentation/gpio/consumer.txt @@ -184,7 +184,7 @@ A driver can also query the current direction of a GPIO: int gpiod_get_direction(const struct gpio_desc *desc) -This function will return either GPIOF_DIR_IN or GPIOF_DIR_OUT. +This function returns 0 for output, 1 for input, or an error code in case of error. Be aware that there is no default direction for GPIOs. Therefore, **using a GPIO without setting its direction first is illegal and will result in undefined -- cgit v1.2.3 From 64ff2c8e468ceff3cd678a4fa2edfc77dadc6bfe Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 9 Jan 2018 17:58:46 -0800 Subject: gpiolib: Export gpiochip_irqchip_irq_valid() to drivers Some pinctrl drivers can use the gpiochip irq valid information to figure out if certain gpios are exposed to the kernel for usage or not. Expose this API so we can use it in the pinmux_ops::request ops. Signed-off-by: Stephen Boyd Acked-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 5 +++-- include/linux/gpio/driver.h | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0e78a5023cb2..94a5575d1ebe 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1501,14 +1501,15 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) gpiochip->irq.valid_mask = NULL; } -static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, - unsigned int offset) +bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, + unsigned int offset) { /* No mask means all valid */ if (likely(!gpiochip->irq.valid_mask)) return true; return test_bit(offset, gpiochip->irq.valid_mask); } +EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); /** * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 55e672592fa9..b6a05dd0d10a 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -431,6 +431,9 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, bool threaded, struct lock_class_key *lock_key); +bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, + unsigned int offset); + #ifdef CONFIG_LOCKDEP /* -- cgit v1.2.3 From 58556204662812f4beec1bc8ee7685884037e0a4 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 9 Jan 2018 17:21:45 -0500 Subject: gpio: Add GPIO support for the ACCES PCIe-IDIO-24 family The ACCES PCIe-IDIO-24 device provides 56 lines of digital I/O (24 lines of optically-isolated non-polarized digital inputs for AC and DC control signals, 24 lines of isolated solid state FET digital outputs, and 8 non-isolated TTL/CMOS compatible programmable I/O). An interrupt is generated when any of the inputs change state (low to high or high to low). Input filter control is not supported by this driver, and input filters are deactivated by this driver. These devices are capable of get_multiple and set_multiple functionality, but these functions have not yet been implemented for this driver. Change-Of-State (COS) detection functionality may be configured to fire interrupts on exclusively rising/falling edges, but this driver currently only implements COS detection for either both edges or none. Signed-off-by: William Breathitt Gray Signed-off-by: Linus Walleij --- MAINTAINERS | 6 + drivers/gpio/Kconfig | 10 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-pcie-idio-24.c | 447 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 464 insertions(+) create mode 100644 drivers/gpio/gpio-pcie-idio-24.c diff --git a/MAINTAINERS b/MAINTAINERS index 89e2acf95981..57811dfb3416 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -270,6 +270,12 @@ L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-pci-idio-16.c +ACCES PCIe-IDIO-24 GPIO DRIVER +M: William Breathitt Gray +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-pcie-idio-24.c + ACENIC DRIVER M: Jes Sorensen L: linux-acenic@sunsite.dk diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8c4018855c3b..440af077cc76 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1244,6 +1244,16 @@ config GPIO_PCI_IDIO_16 low). Input filter control is not supported by this driver, and the input filters are deactivated by this driver. +config GPIO_PCIE_IDIO_24 + tristate "ACCES PCIe-IDIO-24 GPIO support" + select GPIOLIB_IRQCHIP + help + Enables GPIO support for the ACCES PCIe-IDIO-24 family (PCIe-IDIO-24, + PCIe-IDI-24, PCIe-IDO-24, PCIe-IDIO-12). An interrupt is generated + when any of the inputs change state (low to high or high to low). + Input filter control is not supported by this driver, and the input + filters are deactivated by this driver. + config GPIO_RDC321X tristate "RDC R-321x GPIO support" select MFD_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ff3d36d0a443..cccb0d40846c 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o +obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c new file mode 100644 index 000000000000..f666e2e69074 --- /dev/null +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driver for the ACCES PCIe-IDIO-24 family + * Copyright (C) 2018 William Breathitt Gray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * This driver supports the following ACCES devices: PCIe-IDIO-24, + * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct idio_24_gpio_reg - GPIO device registers structure + * @out0_7: Read: FET Outputs 0-7 + * Write: FET Outputs 0-7 + * @out8_15: Read: FET Outputs 8-15 + * Write: FET Outputs 8-15 + * @out16_23: Read: FET Outputs 16-23 + * Write: FET Outputs 16-23 + * @ttl_out0_7: Read: TTL/CMOS Outputs 0-7 + * Write: TTL/CMOS Outputs 0-7 + * @in0_7: Read: Isolated Inputs 0-7 + * Write: Reserved + * @in8_15: Read: Isolated Inputs 8-15 + * Write: Reserved + * @in16_23: Read: Isolated Inputs 16-23 + * Write: Reserved + * @ttl_in0_7: Read: TTL/CMOS Inputs 0-7 + * Write: Reserved + * @cos0_7: Read: COS Status Inputs 0-7 + * Write: COS Clear Inputs 0-7 + * @cos8_15: Read: COS Status Inputs 8-15 + * Write: COS Clear Inputs 8-15 + * @cos16_23: Read: COS Status Inputs 16-23 + * Write: COS Clear Inputs 16-23 + * @cos_ttl0_7: Read: COS Status TTL/CMOS 0-7 + * Write: COS Clear TTL/CMOS 0-7 + * @ctl: Read: Control Register + * Write: Control Register + * @reserved: Read: Reserved + * Write: Reserved + * @cos_enable: Read: COS Enable + * Write: COS Enable + * @soft_reset: Read: IRQ Output Pin Status + * Write: Software Board Reset + */ +struct idio_24_gpio_reg { + u8 out0_7; + u8 out8_15; + u8 out16_23; + u8 ttl_out0_7; + u8 in0_7; + u8 in8_15; + u8 in16_23; + u8 ttl_in0_7; + u8 cos0_7; + u8 cos8_15; + u8 cos16_23; + u8 cos_ttl0_7; + u8 ctl; + u8 reserved; + u8 cos_enable; + u8 soft_reset; +}; + +/** + * struct idio_24_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @lock: synchronization lock to prevent I/O race conditions + * @reg: I/O address offset for the GPIO device registers + * @irq_mask: I/O bits affected by interrupts + */ +struct idio_24_gpio { + struct gpio_chip chip; + raw_spinlock_t lock; + struct idio_24_gpio_reg __iomem *reg; + unsigned long irq_mask; +}; + +static int idio_24_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + const unsigned long out_mode_mask = BIT(1); + + /* FET Outputs */ + if (offset < 24) + return 0; + + /* Isolated Inputs */ + if (offset < 48) + return 1; + + /* TTL/CMOS I/O */ + /* OUT MODE = 1 when TTL/CMOS Output Mode is set */ + return !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask); +} + +static int idio_24_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + unsigned int ctl_state; + const unsigned long out_mode_mask = BIT(1); + + /* TTL/CMOS I/O */ + if (offset > 47) { + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + /* Clear TTL/CMOS Output Mode */ + ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask; + iowrite8(ctl_state, &idio24gpio->reg->ctl); + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); + } + + return 0; +} + +static int idio_24_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + unsigned int ctl_state; + const unsigned long out_mode_mask = BIT(1); + + /* TTL/CMOS I/O */ + if (offset > 47) { + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + /* Set TTL/CMOS Output Mode */ + ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask; + iowrite8(ctl_state, &idio24gpio->reg->ctl); + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); + } + + chip->set(chip, offset, value); + return 0; +} + +static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + const unsigned long offset_mask = BIT(offset % 8); + const unsigned long out_mode_mask = BIT(1); + + /* FET Outputs */ + if (offset < 8) + return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask); + + if (offset < 16) + return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask); + + if (offset < 24) + return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask); + + /* Isolated Inputs */ + if (offset < 32) + return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask); + + if (offset < 40) + return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask); + + if (offset < 48) + return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask); + + /* TTL/CMOS Outputs */ + if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) + return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask); + + /* TTL/CMOS Inputs */ + return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask); +} + +static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + const unsigned long out_mode_mask = BIT(1); + void __iomem *base; + const unsigned int mask = BIT(offset % 8); + unsigned long flags; + unsigned int out_state; + + /* Isolated Inputs */ + if (offset > 23 && offset < 48) + return; + + /* TTL/CMOS Inputs */ + if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask)) + return; + + /* TTL/CMOS Outputs */ + if (offset > 47) + base = &idio24gpio->reg->ttl_out0_7; + /* FET Outputs */ + else if (offset > 15) + base = &idio24gpio->reg->out16_23; + else if (offset > 7) + base = &idio24gpio->reg->out8_15; + else + base = &idio24gpio->reg->out0_7; + + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + if (value) + out_state = ioread8(base) | mask; + else + out_state = ioread8(base) & ~mask; + + iowrite8(out_state, base); + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); +} + +static void idio_24_irq_ack(struct irq_data *data) +{ +} + +static void idio_24_irq_mask(struct irq_data *data) +{ + struct gpio_chip *const chip = irq_data_get_irq_chip_data(data); + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + const unsigned long bit_offset = irqd_to_hwirq(data) - 24; + unsigned char new_irq_mask; + const unsigned long bank_offset = bit_offset/8 * 8; + unsigned char cos_enable_state; + + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + idio24gpio->irq_mask &= BIT(bit_offset); + new_irq_mask = idio24gpio->irq_mask >> bank_offset; + + if (!new_irq_mask) { + cos_enable_state = ioread8(&idio24gpio->reg->cos_enable); + + /* Disable Rising Edge detection */ + cos_enable_state &= ~BIT(bank_offset); + /* Disable Falling Edge detection */ + cos_enable_state &= ~BIT(bank_offset + 4); + + iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable); + } + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); +} + +static void idio_24_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *const chip = irq_data_get_irq_chip_data(data); + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + unsigned char prev_irq_mask; + const unsigned long bit_offset = irqd_to_hwirq(data) - 24; + const unsigned long bank_offset = bit_offset/8 * 8; + unsigned char cos_enable_state; + + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + prev_irq_mask = idio24gpio->irq_mask >> bank_offset; + idio24gpio->irq_mask |= BIT(bit_offset); + + if (!prev_irq_mask) { + cos_enable_state = ioread8(&idio24gpio->reg->cos_enable); + + /* Enable Rising Edge detection */ + cos_enable_state |= BIT(bank_offset); + /* Enable Falling Edge detection */ + cos_enable_state |= BIT(bank_offset + 4); + + iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable); + } + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); +} + +static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + /* The only valid irq types are none and both-edges */ + if (flow_type != IRQ_TYPE_NONE && + (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) + return -EINVAL; + + return 0; +} + +static struct irq_chip idio_24_irqchip = { + .name = "pcie-idio-24", + .irq_ack = idio_24_irq_ack, + .irq_mask = idio_24_irq_mask, + .irq_unmask = idio_24_irq_unmask, + .irq_set_type = idio_24_irq_set_type +}; + +static irqreturn_t idio_24_irq_handler(int irq, void *dev_id) +{ + struct idio_24_gpio *const idio24gpio = dev_id; + unsigned long irq_status; + struct gpio_chip *const chip = &idio24gpio->chip; + unsigned long irq_mask; + int gpio; + + raw_spin_lock(&idio24gpio->lock); + + /* Read Change-Of-State status */ + irq_status = ioread32(&idio24gpio->reg->cos0_7); + + raw_spin_unlock(&idio24gpio->lock); + + /* Make sure our device generated IRQ */ + if (!irq_status) + return IRQ_NONE; + + /* Handle only unmasked IRQ */ + irq_mask = idio24gpio->irq_mask & irq_status; + + for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24) + generic_handle_irq(irq_find_mapping(chip->irq.domain, + gpio + 24)); + + raw_spin_lock(&idio24gpio->lock); + + /* Clear Change-Of-State status */ + iowrite32(irq_status, &idio24gpio->reg->cos0_7); + + raw_spin_unlock(&idio24gpio->lock); + + return IRQ_HANDLED; +} + +#define IDIO_24_NGPIO 56 +static const char *idio_24_names[IDIO_24_NGPIO] = { + "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", + "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", + "OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23", + "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", + "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15", + "IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23", + "TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7" +}; + +static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *const dev = &pdev->dev; + struct idio_24_gpio *idio24gpio; + int err; + const size_t pci_bar_index = 2; + const char *const name = pci_name(pdev); + + idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL); + if (!idio24gpio) + return -ENOMEM; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device (%d)\n", err); + return err; + } + + err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name); + if (err) { + dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err); + return err; + } + + idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; + + idio24gpio->chip.label = name; + idio24gpio->chip.parent = dev; + idio24gpio->chip.owner = THIS_MODULE; + idio24gpio->chip.base = -1; + idio24gpio->chip.ngpio = IDIO_24_NGPIO; + idio24gpio->chip.names = idio_24_names; + idio24gpio->chip.get_direction = idio_24_gpio_get_direction; + idio24gpio->chip.direction_input = idio_24_gpio_direction_input; + idio24gpio->chip.direction_output = idio_24_gpio_direction_output; + idio24gpio->chip.get = idio_24_gpio_get; + idio24gpio->chip.set = idio_24_gpio_set; + + raw_spin_lock_init(&idio24gpio->lock); + + /* Software board reset */ + iowrite8(0, &idio24gpio->reg->soft_reset); + + err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0, + handle_edge_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(dev, "Could not add irqchip (%d)\n", err); + return err; + } + + err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED, + name, idio24gpio); + if (err) { + dev_err(dev, "IRQ handler registering failed (%d)\n", err); + return err; + } + + return 0; +} + +static const struct pci_device_id idio_24_pci_dev_id[] = { + { PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) }, + { PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id); + +static struct pci_driver idio_24_driver = { + .name = "pcie-idio-24", + .id_table = idio_24_pci_dev_id, + .probe = idio_24_probe +}; + +module_pci_driver(idio_24_driver); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 92a8046c9d952a2a7d21dfcd3afadc72a0bc0f72 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Jan 2018 09:35:15 +0100 Subject: gpio: winbond: fix ISA_BUS_API dependency The newly added GPIO driver for winbond chipsets causes a circular dependency warning in Kconfig: drivers/gpio/Kconfig:13:error: recursive dependency detected! drivers/gpio/Kconfig:13: symbol GPIOLIB is selected by STX104 drivers/iio/adc/Kconfig:699: symbol STX104 depends on ISA_BUS_API arch/Kconfig:830: symbol ISA_BUS_API is selected by GPIO_WINBOND drivers/gpio/Kconfig:701: symbol GPIO_WINBOND depends on GPIOLIB The underlying problem is that ISA_BUS_API is not meant to be selected by device drivers, instead it is provided by the architectures that support ISA add-on card devices, or in case of x86 have this explicitly enabled. This particular driver appears to be different from the other ISA_BUS_API based drivers, in that it is not normally an add-on card (ISA or PC104) but instead is an LPC-attached component on the mainboard. We already support other functionality provided by this chip, at least drivers/watchdog/w83627hf_wdt.c and drivers/hwmon/w83627ehf.c, plus there is a discovery function for this hardware in drivers/parport/parport_pc.c. If we want to use this driver without having to enable CONFIG_EXPERT, it might be better to not use the isa_bus_type for it, but rather turn it into a platform_driver, acpi_driver or add an MFD for it that is shared with the wdt and hwmon portions and does the probing. For now, this patch fixes the dependency by changing 'select' into 'depends on'. Cc: William Breathitt Gray Cc: Guenter Roeck Cc: Maciej S. Szmigiero Cc: Andy Shevchenko Fixes: a0d65009411c ("gpio: winbond: Add driver") Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 440af077cc76..8dbb2280538d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -700,7 +700,7 @@ config GPIO_TS5500 config GPIO_WINBOND tristate "Winbond Super I/O GPIO support" - select ISA_BUS_API + depends on ISA_BUS_API help This option enables support for GPIOs found on Winbond Super I/O chips. -- cgit v1.2.3 From 76e28f5ffed82b1e81a86c4eb8d0420515765620 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 10 Jan 2018 14:37:13 +0000 Subject: gpio: thunderx: fix error return code in thunderx_gpio_probe() Fix to return error code -ENOMEM from the error handling case instead of 0, as done elsewhere in this function. Fixes: 5a2a30024d8c ("gpio: Add gpio driver support for ThunderX and OCTEON-TX") Signed-off-by: Wei Yongjun Acked-by: David Daney Signed-off-by: Linus Walleij --- drivers/gpio/gpio-thunderx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index b5adb79a631a..d16e9d4a129b 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -553,8 +553,10 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, 0, 0, of_node_to_fwnode(dev->of_node), &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) + if (!txgpio->irqd) { + err = -ENOMEM; goto out; + } /* Push on irq_data and the domain for each line. */ for (i = 0; i < ngpio; i++) { -- cgit v1.2.3 From 6a537d48461deacc57c07ed86d9915e5aa4b3539 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 27 Dec 2017 16:37:44 +0100 Subject: gpio: of: Support regulator nonstandard GPIO properties Before it was clearly established that all GPIO properties in the device tree shall be named "foo-gpios" (with the deprecated variant "foo-gpio" for single lines) we unfortunately merged a few bindings for regulators with random phandle names. As we want to switch the GPIO regulator driver to using descriptors, we need devm_gpiod_get() to return something reasonable when looking up these in the device tree. Put in a special #ifdef:ed kludge to do this special lookup only for the regulator case and gets compiled out if we're not enabling regulators. Supply a whitelist with properties we accept. Cc: Rob Herring Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 13acd0378884..d2a6656b82cb 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -147,6 +147,41 @@ static struct gpio_desc *of_find_spi_gpio(struct device *dev, const char *con_id return desc; } +/* + * Some regulator bindings happened before we managed to establish that GPIO + * properties should be named "foo-gpios" so we have this special kludge for + * them. + */ +static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char *con_id, + enum of_gpio_flags *of_flags) +{ + /* These are the connection IDs we accept as legacy GPIO phandles */ + const char *whitelist[] = { + "wlf,ldoena", /* Arizona */ + "wlf,ldo1ena", /* WM8994 */ + "wlf,ldo2ena", /* WM8994 */ + }; + struct device_node *np = dev->of_node; + struct gpio_desc *desc; + int i; + + if (!IS_ENABLED(CONFIG_REGULATOR)) + return ERR_PTR(-ENOENT); + + if (!con_id) + return ERR_PTR(-ENOENT); + + for (i = 0; i < ARRAY_SIZE(whitelist); i++) + if (!strcmp(con_id, whitelist[i])) + break; + + if (i == ARRAY_SIZE(whitelist)) + return ERR_PTR(-ENOENT); + + desc = of_get_named_gpiod_flags(np, con_id, 0, of_flags); + return desc; +} + struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags) @@ -175,6 +210,10 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, if (IS_ERR(desc)) desc = of_find_spi_gpio(dev, con_id, &of_flags); + /* Special handling for regulator GPIOs if used */ + if (IS_ERR(desc)) + desc = of_find_regulator_gpio(dev, con_id, &of_flags); + if (IS_ERR(desc)) return desc; -- cgit v1.2.3 From 6392cca42de603d49c2d929c838c92801d4ee761 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 29 Dec 2017 02:07:54 +0100 Subject: gpio: Break out code to get a descriptor from a DT node Sometimes a GPIO needs to be taken from a node without a device associated with it. The fwnode accessor does this, let's however break out the DT code for now. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 123 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 94a5575d1ebe..6df94492e93e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3667,18 +3667,88 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, } EXPORT_SYMBOL_GPL(gpiod_get_index); +/** + * gpiod_get_from_of_node() - obtain a GPIO from an OF node + * @node: handle of the OF node + * @propname: name of the DT property representing the GPIO + * @index: index of the GPIO to obtain for the consumer + * @dflags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * Returns: + * On successful request the GPIO pin is configured in accordance with + * provided @dflags. If the node does not have the requested GPIO + * property, NULL is returned. + * + * In case of error an ERR_PTR() is returned. + */ +static struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + struct gpio_desc *desc = ERR_PTR(-ENODEV); + unsigned long lflags = 0; + enum of_gpio_flags flags; + bool active_low = false; + bool single_ended = false; + bool open_drain = false; + bool transitory = false; + int ret; + + desc = of_get_named_gpiod_flags(node, propname, + index, &flags); + + if (!desc || IS_ERR(desc)) { + /* If it is not there, just return NULL */ + if (PTR_ERR(desc) == -ENOENT) + return NULL; + return desc; + } + + active_low = flags & OF_GPIO_ACTIVE_LOW; + single_ended = flags & OF_GPIO_SINGLE_ENDED; + open_drain = flags & OF_GPIO_OPEN_DRAIN; + transitory = flags & OF_GPIO_TRANSITORY; + + ret = gpiod_request(desc, label); + if (ret) + return ERR_PTR(ret); + + if (active_low) + lflags |= GPIO_ACTIVE_LOW; + + if (single_ended) { + if (open_drain) + lflags |= GPIO_OPEN_DRAIN; + else + lflags |= GPIO_OPEN_SOURCE; + } + + if (transitory) + lflags |= GPIO_TRANSITORY; + + ret = gpiod_configure_flags(desc, propname, lflags, dflags); + if (ret < 0) { + gpiod_put(desc); + return ERR_PTR(ret); + } + + return desc; +} + /** * fwnode_get_named_gpiod - obtain a GPIO from firmware node * @fwnode: handle of the firmware node * @propname: name of the firmware property representing the GPIO - * @index: index of the GPIO to obtain in the consumer + * @index: index of the GPIO to obtain for the consumer * @dflags: GPIO initialization flags * @label: label to attach to the requested GPIO * * This function can be used for drivers that get their configuration - * from firmware. + * from opaque firmware. * - * Function properly finds the corresponding GPIO using whatever is the + * The function properly finds the corresponding GPIO using whatever is the * underlying firmware interface and then makes sure that the GPIO * descriptor is requested before it is returned to the caller. * @@ -3695,56 +3765,35 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, { struct gpio_desc *desc = ERR_PTR(-ENODEV); unsigned long lflags = 0; - bool active_low = false; - bool single_ended = false; - bool open_drain = false; - bool transitory = false; int ret; if (!fwnode) return ERR_PTR(-EINVAL); if (is_of_node(fwnode)) { - enum of_gpio_flags flags; - - desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname, - index, &flags); - if (!IS_ERR(desc)) { - active_low = flags & OF_GPIO_ACTIVE_LOW; - single_ended = flags & OF_GPIO_SINGLE_ENDED; - open_drain = flags & OF_GPIO_OPEN_DRAIN; - transitory = flags & OF_GPIO_TRANSITORY; - } + desc = gpiod_get_from_of_node(to_of_node(fwnode), + propname, index, + dflags, + label); + return desc; } else if (is_acpi_node(fwnode)) { struct acpi_gpio_info info; desc = acpi_node_get_gpiod(fwnode, propname, index, &info); - if (!IS_ERR(desc)) { - active_low = info.polarity == GPIO_ACTIVE_LOW; - acpi_gpio_update_gpiod_flags(&dflags, &info); - } - } + if (IS_ERR(desc)) + return desc; - if (IS_ERR(desc)) - return desc; + acpi_gpio_update_gpiod_flags(&dflags, &info); + + if (info.polarity == GPIO_ACTIVE_LOW) + lflags |= GPIO_ACTIVE_LOW; + } + /* Currently only ACPI takes this path */ ret = gpiod_request(desc, label); if (ret) return ERR_PTR(ret); - if (active_low) - lflags |= GPIO_ACTIVE_LOW; - - if (single_ended) { - if (open_drain) - lflags |= GPIO_OPEN_DRAIN; - else - lflags |= GPIO_OPEN_SOURCE; - } - - if (transitory) - lflags |= GPIO_TRANSITORY; - ret = gpiod_configure_flags(desc, propname, lflags, dflags); if (ret < 0) { gpiod_put(desc); -- cgit v1.2.3 From 92542edc42496ac4b8f5ba0ae81ab5776db9473b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 29 Dec 2017 22:52:02 +0100 Subject: gpio: Export devm_gpiod_get_from_of_node() for consumers We have been holding back on adding an API for fetching GPIO handles directly from device nodes, strongly preferring to get it from the spawn devices instead. The fwnode interface however already contains an API for doing this, as it is used for opaque device tree nodes or ACPI nodes for getting handles to LEDs and keys that use GPIO: those are specified as one child per LED/key in the device tree and are not individual devices. However regulators present a special problem as they already have helper functions to traverse the device tree from a regulator node and two levels down to fill in data, and as it already traverses GPIO nodes in its own way, and already holds a pointer to each regulators device tree node, it makes most sense to export an API to fetch the GPIO descriptor directly from the node. We only support the devm_* version for now, hopefully no non-devres version will be needed. Signed-off-by: Linus Walleij --- drivers/gpio/devres.c | 42 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 9 +++++---- drivers/gpio/gpiolib.h | 6 ++++++ include/linux/gpio/consumer.h | 17 +++++++++++++++++ 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index afbff155a0ba..e82cc763633c 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -124,6 +124,48 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, } EXPORT_SYMBOL(devm_gpiod_get_index); +/** + * devm_gpiod_get_from_of_node() - obtain a GPIO from an OF node + * @dev: device for lifecycle management + * @node: handle of the OF node + * @propname: name of the DT property representing the GPIO + * @index: index of the GPIO to obtain for the consumer + * @dflags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * Returns: + * On successful request the GPIO pin is configured in accordance with + * provided @dflags. + * + * In case of error an ERR_PTR() is returned. + */ +struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, + struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + struct gpio_desc **dr; + struct gpio_desc *desc; + + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + desc = gpiod_get_from_of_node(node, propname, index, dflags, label); + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return desc; +} +EXPORT_SYMBOL(devm_gpiod_get_from_of_node); + /** * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a * device's child node diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6df94492e93e..bf083bc58d30 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3682,10 +3682,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_index); * * In case of error an ERR_PTR() is returned. */ -static struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, - const char *propname, int index, - enum gpiod_flags dflags, - const char *label) +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) { struct gpio_desc *desc = ERR_PTR(-ENODEV); unsigned long lflags = 0; @@ -3736,6 +3736,7 @@ static struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, return desc; } +EXPORT_SYMBOL(gpiod_get_from_of_node); /** * fwnode_get_named_gpiod - obtain a GPIO from firmware node diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 6e9228b94437..0ae9f991ede1 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -193,6 +193,12 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep, struct gpio_desc **desc_array, int *value_array); +/* This is just passed between gpiolib and devres */ +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label); + extern struct spinlock gpio_lock; extern struct list_head gpio_devices; diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 540b2c142493..dbd065963296 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -151,8 +151,14 @@ struct gpio_desc *gpio_to_desc(unsigned gpio); int desc_to_gpio(const struct gpio_desc *desc); /* Child properties interface */ +struct device_node; struct fwnode_handle; +struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, + struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label); struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, const char *propname, int index, enum gpiod_flags dflags, @@ -472,8 +478,19 @@ static inline int desc_to_gpio(const struct gpio_desc *desc) } /* Child properties interface */ +struct device_node; struct fwnode_handle; +static inline +struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, + struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + return ERR_PTR(-ENOSYS); +} + static inline struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, const char *propname, int index, -- cgit v1.2.3 From a603a2b8d86ee93ee2107da8ca75fd854fd4ff32 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 30 Dec 2017 16:26:36 +0100 Subject: gpio: of: Add special quirk to parse regulator flags While most GPIOs are indicated to be active low or open drain using their twocell flags, we have legacy regulator bindings to take into account. Add a quirk respecting the special boolean active-high and open drain flags when parsing regulator nodes for GPIOs. This makes it possible to get rid of duplicated inversion semantics handling in the regulator core and any regulator drivers parsing and handling this separately. Unfortunately the old regulator inversion semantics are specified such that the presence or absence of "enable-active-high" solely controls the semantics, so we cannot deprecate this in favor of the phandle-provided inversion flag, instead any such phandle inversion flag provided in the second cell of a GPIO handle must be actively ignored, so we print a warning to contain the situation and make things easy for the users. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index d2a6656b82cb..71ba008b7f46 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -56,6 +56,42 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, return gpiochip_get_desc(chip, ret); } +static void of_gpio_flags_quirks(struct device_node *np, + enum of_gpio_flags *flags) +{ + /* + * Some GPIO fixed regulator quirks. + * Note that active low is the default. + */ + if (IS_ENABLED(CONFIG_REGULATOR) && + (of_device_is_compatible(np, "reg-fixed-voltage") || + of_device_is_compatible(np, "regulator-gpio"))) { + /* + * The regulator GPIO handles are specified such that the + * presence or absence of "enable-active-high" solely controls + * the polarity of the GPIO line. Any phandle flags must + * be actively ignored. + */ + if (*flags & OF_GPIO_ACTIVE_LOW) { + pr_warn("%s GPIO handle specifies active low - ignored\n", + of_node_full_name(np)); + *flags &= ~OF_GPIO_ACTIVE_LOW; + } + if (!of_property_read_bool(np, "enable-active-high")) + *flags |= OF_GPIO_ACTIVE_LOW; + } + /* + * Legacy open drain handling for fixed voltage regulators. + */ + if (IS_ENABLED(CONFIG_REGULATOR) && + of_device_is_compatible(np, "reg-fixed-voltage") && + of_property_read_bool(np, "gpio-open-drain")) { + *flags |= (OF_GPIO_SINGLE_ENDED | OF_GPIO_OPEN_DRAIN); + pr_info("%s uses legacy open drain flag - update the DTS if you can\n", + of_node_full_name(np)); + } +} + /** * of_get_named_gpiod_flags() - Get a GPIO descriptor and flags for GPIO API * @np: device node to get GPIO from @@ -93,6 +129,8 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, if (IS_ERR(desc)) goto out; + of_gpio_flags_quirks(np, flags); + pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n", __func__, propname, np, index, PTR_ERR_OR_ZERO(desc)); -- cgit v1.2.3 From 91f6a4afd1227f3806ca310b0a9c101c021ac6c5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:07:09 +0100 Subject: gpio: 74x174: Include proper headers This driver has no business including or . Cut them and include and which is it they really needs. Signed-off-by: Linus Walleij --- drivers/gpio/gpio-74x164.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 6b535ec858cc..3c25f504a784 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -9,12 +9,11 @@ * published by the Free Software Foundation. */ -#include #include #include #include -#include -#include +#include +#include #include #include -- cgit v1.2.3 From 8a3b4f20d9d9157690dae98a5982c3be20ed97d2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:12:07 +0100 Subject: gpio: adp5520: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-adp5520.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c index abf199609546..21452622d954 100644 --- a/drivers/gpio/gpio-adp5520.c +++ b/drivers/gpio/gpio-adp5520.c @@ -12,8 +12,7 @@ #include #include #include - -#include +#include struct adp5520_gpio { struct device *master; -- cgit v1.2.3 From d543668a6174ddc1f0460cce3737e27a39ccd9df Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:14:56 +0100 Subject: gpio: adp5588: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-adp5588.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index e717f8dc3966..3530ccd17e04 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From 40a1f9b2071c30c6f498b8b83fe03f6bd321e436 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:18:34 +0100 Subject: gpio: altera: Include GPIO driver header This is a GPIO driver so it should definately include . We want to get rid of but that will take a bit longer. Signed-off-by: Linus Walleij --- drivers/gpio/gpio-altera.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 8e76d390e653..8c3ff6e2366f 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -18,7 +18,8 @@ #include #include -#include +#include +#include /* For of_mm_gpio_chip */ #include #define ALTERA_GPIO_MAX_NGPIO 32 -- cgit v1.2.3 From 3708c665a778349072a2c49fbc5da40779a5b07a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:22:49 +0100 Subject: gpio: amd8111: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-amd8111.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 30ad7d7c1678..fdcebe59510d 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From 2e21ec4c40eb9d40d1b2d719fcb40726ae8bf4b2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:47:48 +0100 Subject: gpio: arizona: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-arizona.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index d4e6ba0301bc..ba51ea15f379 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 14ec018e15595d3e5c11160eef3f18c0b9c72e3f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:52:33 +0100 Subject: gpio: bcm-kona: Include proper header This driver has no business including , it is a driver so include . GPIOF_DIR_IN/GPIOF_DIR_OUT are for consumers and should not be used in drivers to use just 1/0 instead. Signed-off-by: Linus Walleij --- drivers/gpio/gpio-bcm-kona.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index dfcf56ee3c61..f4c793e9a92b 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -127,7 +127,7 @@ static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio) u32 val; val = readl(reg_base + GPIO_CONTROL(gpio)) & GPIO_GPCTR0_IOTR_MASK; - return val ? GPIOF_DIR_IN : GPIOF_DIR_OUT; + return !!val; } static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) @@ -144,7 +144,7 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) raw_spin_lock_irqsave(&kona_gpio->lock, flags); /* this function only applies to output pin */ - if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN) + if (bcm_kona_gpio_get_dir(chip, gpio) == 1) goto out; reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); @@ -170,7 +170,7 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) reg_base = kona_gpio->reg_base; raw_spin_lock_irqsave(&kona_gpio->lock, flags); - if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN) + if (bcm_kona_gpio_get_dir(chip, gpio) == 1) reg_offset = GPIO_IN_STATUS(bank_id); else reg_offset = GPIO_OUT_STATUS(bank_id); -- cgit v1.2.3 From e88a2ae2befb98f7c9a3accc18e77f0084d03de6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 13 Jan 2018 22:56:52 +0100 Subject: gpio: bt8xx: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-bt8xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index acefb25e8eca..b8ec75cbd4b5 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include /* Steal the hardware definitions from the bttv driver. */ -- cgit v1.2.3 From 6ba40f875d12a8583a10b1c6996fc87a56ba8bdf Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 14 Jan 2018 01:48:48 +0100 Subject: gpio: crystalcove: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-crystalcove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index b6f0f729656c..58531d8b8c6e 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 326a2926c900237ef2c67c3648ed5d64f4c6f090 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 14 Jan 2018 01:56:24 +0100 Subject: gpio: cs5535: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-cs5535.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c index 90278b19aa0e..8814c8f47e57 100644 --- a/drivers/gpio/gpio-cs5535.c +++ b/drivers/gpio/gpio-cs5535.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 45e8bd9cdc021f4a11eb50e68ff02058e95e8d86 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 14 Jan 2018 02:00:10 +0100 Subject: gpio: da905x: Include proper header These drivers has no business including , they are drivers so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-da9052.c | 2 +- drivers/gpio/gpio-da9055.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c index dd8977cf3e85..b6d3e997eb26 100644 --- a/drivers/gpio/gpio-da9052.c +++ b/drivers/gpio/gpio-da9052.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c index 82053b52cba0..2f1b5d23b10c 100644 --- a/drivers/gpio/gpio-da9055.c +++ b/drivers/gpio/gpio-da9055.c @@ -13,7 +13,7 @@ */ #include #include -#include +#include #include #include -- cgit v1.2.3 From 7220c43a4efa4f5107bc1b99410d2b01df1837ed Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 14 Jan 2018 02:05:38 +0100 Subject: gpio: davinci: Include proper header This driver has no business including , it is a driver so include . Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index f75d8443ecaf..34fc0a167b7c 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -9,7 +9,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ -#include +#include #include #include #include -- cgit v1.2.3 From b888fb6f2a278442933e3bfab70262e9a5365fb3 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 12 Jan 2018 13:16:08 +0100 Subject: gpio: stmpe: i2c transfer are forbiden in atomic context Move the workaround from stmpe_gpio_irq_unmask() which is executed in atomic context to stmpe_gpio_irq_sync_unlock() which is not. It fixes the following issue: [ 1.500000] BUG: scheduling while atomic: swapper/1/0x00000002 [ 1.500000] CPU: 0 PID: 1 Comm: swapper Not tainted 4.15.0-rc2-00020-gbd4301f-dirty #28 [ 1.520000] Hardware name: STM32 (Device Tree Support) [ 1.520000] [<0000bfc9>] (unwind_backtrace) from [<0000b347>] (show_stack+0xb/0xc) [ 1.530000] [<0000b347>] (show_stack) from [<0001fc49>] (__schedule_bug+0x39/0x58) [ 1.530000] [<0001fc49>] (__schedule_bug) from [<00168211>] (__schedule+0x23/0x2b2) [ 1.550000] [<00168211>] (__schedule) from [<001684f7>] (schedule+0x57/0x64) [ 1.550000] [<001684f7>] (schedule) from [<0016a513>] (schedule_timeout+0x137/0x164) [ 1.550000] [<0016a513>] (schedule_timeout) from [<00168b91>] (wait_for_common+0x8d/0xfc) [ 1.570000] [<00168b91>] (wait_for_common) from [<00139753>] (stm32f4_i2c_xfer+0xe9/0xfe) [ 1.580000] [<00139753>] (stm32f4_i2c_xfer) from [<00138545>] (__i2c_transfer+0x111/0x148) [ 1.590000] [<00138545>] (__i2c_transfer) from [<001385cf>] (i2c_transfer+0x53/0x70) [ 1.590000] [<001385cf>] (i2c_transfer) from [<001388a5>] (i2c_smbus_xfer+0x12f/0x36e) [ 1.600000] [<001388a5>] (i2c_smbus_xfer) from [<00138b49>] (i2c_smbus_read_byte_data+0x1f/0x2a) [ 1.610000] [<00138b49>] (i2c_smbus_read_byte_data) from [<00124fdd>] (__stmpe_reg_read+0xd/0x24) [ 1.620000] [<00124fdd>] (__stmpe_reg_read) from [<001252b3>] (stmpe_reg_read+0x19/0x24) [ 1.630000] [<001252b3>] (stmpe_reg_read) from [<0002c4d1>] (unmask_irq+0x17/0x22) [ 1.640000] [<0002c4d1>] (unmask_irq) from [<0002c57f>] (irq_startup+0x6f/0x78) [ 1.650000] [<0002c57f>] (irq_startup) from [<0002b7a1>] (__setup_irq+0x319/0x47c) [ 1.650000] [<0002b7a1>] (__setup_irq) from [<0002bad3>] (request_threaded_irq+0x6b/0xe8) [ 1.660000] [<0002bad3>] (request_threaded_irq) from [<0002d0b9>] (devm_request_threaded_irq+0x3b/0x6a) [ 1.670000] [<0002d0b9>] (devm_request_threaded_irq) from [<001446e7>] (mmc_gpiod_request_cd_irq+0x49/0x8a) [ 1.680000] [<001446e7>] (mmc_gpiod_request_cd_irq) from [<0013d45d>] (mmc_start_host+0x49/0x60) [ 1.690000] [<0013d45d>] (mmc_start_host) from [<0013e40b>] (mmc_add_host+0x3b/0x54) [ 1.700000] [<0013e40b>] (mmc_add_host) from [<00148119>] (mmci_probe+0x4d1/0x60c) [ 1.710000] [<00148119>] (mmci_probe) from [<000f903b>] (amba_probe+0x7b/0xbe) [ 1.720000] [<000f903b>] (amba_probe) from [<001170e5>] (driver_probe_device+0x169/0x1f8) [ 1.730000] [<001170e5>] (driver_probe_device) from [<001171b7>] (__driver_attach+0x43/0x5c) [ 1.740000] [<001171b7>] (__driver_attach) from [<0011618d>] (bus_for_each_dev+0x3d/0x46) [ 1.740000] [<0011618d>] (bus_for_each_dev) from [<001165cd>] (bus_add_driver+0xcd/0x124) [ 1.740000] [<001165cd>] (bus_add_driver) from [<00117713>] (driver_register+0x4d/0x7a) [ 1.760000] [<00117713>] (driver_register) from [<001fc765>] (do_one_initcall+0xbd/0xe8) [ 1.770000] [<001fc765>] (do_one_initcall) from [<001fc88b>] (kernel_init_freeable+0xfb/0x134) [ 1.780000] [<001fc88b>] (kernel_init_freeable) from [<00167ee3>] (kernel_init+0x7/0x9c) [ 1.790000] [<00167ee3>] (kernel_init) from [<00009b65>] (ret_from_fork+0x11/0x2c) Cc: stable@vger.kernel.org Signed-off-by: Alexandre TORGUE Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index e3d048e65339..3b6efce8e681 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -190,6 +190,16 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) }; int i, j; + /* + * STMPE1600: to be able to get IRQ from pins, + * a read must be done on GPMR register, or a write in + * GPSR or GPCR registers + */ + if (stmpe->partnum == STMPE1600) { + stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_LSB]); + stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_CSB]); + } + for (i = 0; i < CACHE_NR_REGS; i++) { /* STMPE801 and STMPE1600 don't have RE and FE registers */ if ((stmpe->partnum == STMPE801 || @@ -227,21 +237,11 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); - struct stmpe *stmpe = stmpe_gpio->stmpe; int offset = d->hwirq; int regoffset = offset / 8; int mask = BIT(offset % 8); stmpe_gpio->regs[REG_IE][regoffset] |= mask; - - /* - * STMPE1600 workaround: to be able to get IRQ from pins, - * a read must be done on GPMR register, or a write in - * GPSR or GPCR registers - */ - if (stmpe->partnum == STMPE1600) - stmpe_reg_read(stmpe, - stmpe->regs[STMPE_IDX_GPMR_LSB + regoffset]); } static void stmpe_dbg_show_one(struct seq_file *s, -- cgit v1.2.3 From 7d18f0a14aa6a0d6bad39111c1fb655f07f71d59 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 16 Jan 2018 08:29:50 +0100 Subject: gpio: No NULL owner Sometimes a GPIO is fetched with NULL as parent device, and that is just fine. So under these circumstances, avoid using dev_name() to provide a name for the GPIO line. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bf083bc58d30..4092b8601c4e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3623,6 +3623,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, struct gpio_desc *desc = NULL; int status; enum gpio_lookup_flags lookupflags = 0; + /* Maybe we have a device name, maybe not */ + const char *devname = dev ? dev_name(dev) : "?"; dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); @@ -3651,8 +3653,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, return desc; } - /* If a connection label was passed use that, else use the device name as label */ - status = gpiod_request(desc, con_id ? con_id : dev_name(dev)); + /* + * If a connection label was passed use that, else attempt to use + * the device name as label + */ + status = gpiod_request(desc, con_id ? con_id : devname); if (status < 0) return ERR_PTR(status); -- cgit v1.2.3 From 0d83a5eb65095b84b5b000684407fc171d7872e4 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 12 Jan 2018 19:30:50 +0100 Subject: gpio: stmpe: Use seq_putc() in stmpe_dbg_show() A single character (line break) should be put into a sequence. Thus use the corresponding function "seq_putc". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 3b6efce8e681..8061c70c4b95 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -350,7 +350,7 @@ static void stmpe_dbg_show(struct seq_file *s, struct gpio_chip *gc) for (i = 0; i < gc->ngpio; i++, gpio++) { stmpe_dbg_show_one(s, gc, i, gpio); - seq_printf(s, "\n"); + seq_putc(s, '\n'); } } -- cgit v1.2.3 From 64fec0bcea0faa148151050bbb2c15da6a1423e0 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 12 Jan 2018 19:36:29 +0100 Subject: gpio: stmpe: Improve a size determination in stmpe_gpio_probe() Replace the specification of a data structure by a pointer dereference as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 8061c70c4b95..b041c1768ecc 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -437,7 +437,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); - stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL); + stmpe_gpio = kzalloc(sizeof(*stmpe_gpio), GFP_KERNEL); if (!stmpe_gpio) return -ENOMEM; -- cgit v1.2.3 From 757ad058da83c800891bffd0bcba232853104892 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 12 Jan 2018 20:44:15 +0100 Subject: gpio: stmpe: Move an assignment in stmpe_gpio_probe() Move the assignment for the local variable "irq" so that its setting will only be performed directly before it is checked by this function. Signed-off-by: Markus Elfring Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index b041c1768ecc..420892ccc316 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -435,8 +435,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev) int ret; int irq = 0; - irq = platform_get_irq(pdev, 0); - stmpe_gpio = kzalloc(sizeof(*stmpe_gpio), GFP_KERNEL); if (!stmpe_gpio) return -ENOMEM; @@ -459,6 +457,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) if (stmpe_gpio->norequest_mask) stmpe_gpio->chip.irq.need_valid_mask = true; + irq = platform_get_irq(pdev, 0); if (irq < 0) dev_info(&pdev->dev, "device configured in no-irq mode: " -- cgit v1.2.3 From 0f7192318bd81b5bfa6b0f12d71f0f00c67acb78 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 12 Jan 2018 20:48:40 +0100 Subject: gpio: stmpe: Delete an unnecessary variable initialisation in stmpe_gpio_probe() The local variable "irq" will eventually be set to an appropriate value a bit later. Thus omit the explicit initialisation at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 420892ccc316..f8d7d1cd8488 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -432,8 +432,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); struct device_node *np = pdev->dev.of_node; struct stmpe_gpio *stmpe_gpio; - int ret; - int irq = 0; + int ret, irq; stmpe_gpio = kzalloc(sizeof(*stmpe_gpio), GFP_KERNEL); if (!stmpe_gpio) -- cgit v1.2.3 From 605f2d34ea52a14744fe3c6b43708bce7e2eb690 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 16 Jan 2018 23:44:46 +0100 Subject: gpio: of: Fix NPE from OF flags Some calls to of_get_named_gpio() calls sets the flags argument to NULL because they are not interested in the flags. This caused a null pointer exception since we were unconditionally using these flags. Fix it. Fixes: 6a537d48461d ("gpio: of: Support regulator nonstandard GPIO properties") Cc: Heiner Kallweit Reported-by: Heiner Kallweit Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 71ba008b7f46..27e8a339310c 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -129,7 +129,8 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, if (IS_ERR(desc)) goto out; - of_gpio_flags_quirks(np, flags); + if (flags) + of_gpio_flags_quirks(np, flags); pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n", __func__, propname, np, index, -- cgit v1.2.3 From 40a3c9db08ad59062c6ca525fca32ca97edcf99e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 16 Jan 2018 11:56:11 +0000 Subject: gpiolib: remove redundant initialization of pointer desc The initialized value stored in pointer desc is never read as it is updated in the first executable statement in the function. This is therefore redundant and can be removed. Cleans up clang warning: drivers/gpio/gpiolib.c:3710:20: warning: Value stored to 'desc' during its initialization is never read Signed-off-by: Colin Ian King Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4092b8601c4e..37e31ba82ca0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3692,7 +3692,7 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, enum gpiod_flags dflags, const char *label) { - struct gpio_desc *desc = ERR_PTR(-ENODEV); + struct gpio_desc *desc; unsigned long lflags = 0; enum of_gpio_flags flags; bool active_low = false; -- cgit v1.2.3 From adbf02998b2e21e3f055f72fe2daa86260f03637 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 18 Jan 2018 10:43:43 +0100 Subject: gpio: Documentation update Update a slew of documentation files with the latest changes in the API/ABI. Again stress that sysfs is deprecated. Add all new flags and clean up and move some text. Signed-off-by: Linus Walleij --- Documentation/gpio/board.txt | 14 ++++-- Documentation/gpio/consumer.txt | 105 ++++++++++++++++++++++++---------------- Documentation/gpio/driver.txt | 4 ++ Documentation/gpio/sysfs.txt | 11 +++++ 4 files changed, 88 insertions(+), 46 deletions(-) diff --git a/Documentation/gpio/board.txt b/Documentation/gpio/board.txt index a0f61898d493..b1d106167baa 100644 --- a/Documentation/gpio/board.txt +++ b/Documentation/gpio/board.txt @@ -2,6 +2,7 @@ GPIO Mappings ============= This document explains how GPIOs can be assigned to given devices and functions. + Note that it only applies to the new descriptor-based interface. For a description of the deprecated integer-based GPIO interface please refer to gpio-legacy.txt (actually, there is no real mapping possible with the old @@ -49,7 +50,7 @@ This property will make GPIOs 15, 16 and 17 available to the driver under the power = gpiod_get(dev, "power", GPIOD_OUT_HIGH); -The led GPIOs will be active-high, while the power GPIO will be active-low (i.e. +The led GPIOs will be active high, while the power GPIO will be active low (i.e. gpiod_is_active_low(power) will be true). The second parameter of the gpiod_get() functions, the con_id string, has to be @@ -122,9 +123,14 @@ where can be NULL, in which case it will match any function. - idx is the index of the GPIO within the function. - flags is defined to specify the following properties: - * GPIOF_ACTIVE_LOW - to configure the GPIO as active-low - * GPIOF_OPEN_DRAIN - GPIO pin is open drain type. - * GPIOF_OPEN_SOURCE - GPIO pin is open source type. + * GPIO_ACTIVE_HIGH - GPIO line is active high + * GPIO_ACTIVE_LOW - GPIO line is active low + * GPIO_OPEN_DRAIN - GPIO line ise set up as open drain + * GPIO_OPEN_SOURCE - GPIO line is set up as open source + * GPIO_PERSISTENT - GPIO line is persistent during + suspend/resume and maintains its value + * GPIO_TRANSITORY - GPIO line is transitory and may loose its + electrical state during suspend/resume In the future, these flags might be extended to support more properties. diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt index 7aac33081510..d53e5b5cfc9c 100644 --- a/Documentation/gpio/consumer.txt +++ b/Documentation/gpio/consumer.txt @@ -66,6 +66,15 @@ for the GPIO. Values can be: * GPIOD_IN to initialize the GPIO as input. * GPIOD_OUT_LOW to initialize the GPIO as output with a value of 0. * GPIOD_OUT_HIGH to initialize the GPIO as output with a value of 1. +* GPIOD_OUT_LOW_OPEN_DRAIN same as GPIOD_OUT_LOW but also enforce the line + to be electrically used with open drain. +* GPIOD_OUT_HIGH_OPEN_DRAIN same as GPIOD_OUT_HIGH but also enforce the line + to be electrically used with open drain. + +The two last flags are used for use cases where open drain is mandatory, such +as I2C: if the line is not already configured as open drain in the mappings +(see board.txt), then open drain will be enforced anyway and a warning will be +printed that the board configuration needs to be updated to match the use case. Both functions return either a valid GPIO descriptor, or an error code checkable with IS_ERR() (they will never return a NULL pointer). -ENOENT will be returned @@ -240,59 +249,71 @@ that can't be accessed from hardIRQ handlers, these calls act the same as the spinlock-safe calls. -Active-low State and Raw GPIO Values ------------------------------------- -Device drivers like to manage the logical state of a GPIO, i.e. the value their -device will actually receive, no matter what lies between it and the GPIO line. -In some cases, it might make sense to control the actual GPIO line value. The -following set of calls ignore the active-low property of a GPIO and work on the -raw line value: - - int gpiod_get_raw_value(const struct gpio_desc *desc) - void gpiod_set_raw_value(struct gpio_desc *desc, int value) - int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) - void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) - int gpiod_direction_output_raw(struct gpio_desc *desc, int value) - -The active-low state of a GPIO can also be queried using the following call: - - int gpiod_is_active_low(const struct gpio_desc *desc) - -Note that these functions should only be used with great moderation ; a driver -should not have to care about the physical line level. - - -The active-low property ------------------------ - -As a driver should not have to care about the physical line level, all of the +The active low and open drain semantics +--------------------------------------- +As a consumer should not have to care about the physical line level, all of the gpiod_set_value_xxx() or gpiod_set_array_value_xxx() functions operate with -the *logical* value. With this they take the active-low property into account. -This means that they check whether the GPIO is configured to be active-low, +the *logical* value. With this they take the active low property into account. +This means that they check whether the GPIO is configured to be active low, and if so, they manipulate the passed value before the physical line level is driven. +The same is applicable for open drain or open source output lines: those do not +actively drive their output high (open drain) or low (open source), they just +switch their output to a high impedance value. The consumer should not need to +care. (For details read about open drain in driver.txt.) + With this, all the gpiod_set_(array)_value_xxx() functions interpret the -parameter "value" as "active" ("1") or "inactive" ("0"). The physical line +parameter "value" as "asserted" ("1") or "de-asserted" ("0"). The physical line level will be driven accordingly. -As an example, if the active-low property for a dedicated GPIO is set, and the -gpiod_set_(array)_value_xxx() passes "active" ("1"), the physical line level +As an example, if the active low property for a dedicated GPIO is set, and the +gpiod_set_(array)_value_xxx() passes "asserted" ("1"), the physical line level will be driven low. To summarize: -Function (example) active-low property physical line -gpiod_set_raw_value(desc, 0); don't care low -gpiod_set_raw_value(desc, 1); don't care high -gpiod_set_value(desc, 0); default (active-high) low -gpiod_set_value(desc, 1); default (active-high) high -gpiod_set_value(desc, 0); active-low high -gpiod_set_value(desc, 1); active-low low - -Please note again that the set_raw/get_raw functions should be avoided as much -as possible, especially by drivers which should not care about the actual -physical line level and worry about the logical value instead. +Function (example) line property physical line +gpiod_set_raw_value(desc, 0); don't care low +gpiod_set_raw_value(desc, 1); don't care high +gpiod_set_value(desc, 0); default (active high) low +gpiod_set_value(desc, 1); default (active high) high +gpiod_set_value(desc, 0); active low high +gpiod_set_value(desc, 1); active low low +gpiod_set_value(desc, 0); default (active high) low +gpiod_set_value(desc, 1); default (active high) high +gpiod_set_value(desc, 0); open drain low +gpiod_set_value(desc, 1); open drain high impedance +gpiod_set_value(desc, 0); open source high impedance +gpiod_set_value(desc, 1); open source high + +It is possible to override these semantics using the *set_raw/'get_raw functions +but it should be avoided as much as possible, especially by system-agnostic drivers +which should not need to care about the actual physical line level and worry about +the logical value instead. + + +Accessing raw GPIO values +------------------------- +Consumers exist that need to manage the logical state of a GPIO line, i.e. the value +their device will actually receive, no matter what lies between it and the GPIO +line. + +The following set of calls ignore the active-low or open drain property of a GPIO and +work on the raw line value: + + int gpiod_get_raw_value(const struct gpio_desc *desc) + void gpiod_set_raw_value(struct gpio_desc *desc, int value) + int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) + void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) + int gpiod_direction_output_raw(struct gpio_desc *desc, int value) + +The active low state of a GPIO can also be queried using the following call: + + int gpiod_is_active_low(const struct gpio_desc *desc) + +Note that these functions should only be used with great moderation; a driver +should not have to care about the physical line level or open drain semantics. Access multiple GPIOs with a single function call diff --git a/Documentation/gpio/driver.txt b/Documentation/gpio/driver.txt index d8de1c7de85a..3392a0fd4c23 100644 --- a/Documentation/gpio/driver.txt +++ b/Documentation/gpio/driver.txt @@ -88,6 +88,10 @@ ending up in the pin control back-end "behind" the GPIO controller, usually closer to the actual pins. This way the pin controller can manage the below listed GPIO configurations. +If a pin controller back-end is used, the GPIO controller or hardware +description needs to provide "GPIO ranges" mapping the GPIO line offsets to pin +numbers on the pin controller so they can properly cross-reference each other. + GPIOs with debounce support --------------------------- diff --git a/Documentation/gpio/sysfs.txt b/Documentation/gpio/sysfs.txt index aeab01aa4d00..6cdeab8650cd 100644 --- a/Documentation/gpio/sysfs.txt +++ b/Documentation/gpio/sysfs.txt @@ -1,6 +1,17 @@ GPIO Sysfs Interface for Userspace ================================== +THIS ABI IS DEPRECATED, THE ABI DOCUMENTATION HAS BEEN MOVED TO +Documentation/ABI/obsolete/sysfs-gpio AND NEW USERSPACE CONSUMERS +ARE SUPPOSED TO USE THE CHARACTER DEVICE ABI. THIS OLD SYSFS ABI WILL +NOT BE DEVELOPED (NO NEW FEATURES), IT WILL JUST BE MAINTAINED. + +Refer to the examples in tools/gpio/* for an introduction to the new +character device ABI. Also see the userspace header in +include/uapi/linux/gpio.h + +The deprecated sysfs ABI +------------------------ Platforms which use the "gpiolib" implementors framework may choose to configure a sysfs user interface to GPIOs. This is different from the debugfs interface, since it provides control over GPIO direction and -- cgit v1.2.3 From 2046362cf11d3dcb0c6904f7f3209c568d7858b3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 21 Jan 2018 16:52:31 +0100 Subject: gpio: Fix a documentation spelling mistake Simple etraneous letter. Reported-by: Alexander Stein Signed-off-by: Linus Walleij --- Documentation/gpio/board.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/gpio/board.txt b/Documentation/gpio/board.txt index b1d106167baa..659bb19f5b3c 100644 --- a/Documentation/gpio/board.txt +++ b/Documentation/gpio/board.txt @@ -125,7 +125,7 @@ where - flags is defined to specify the following properties: * GPIO_ACTIVE_HIGH - GPIO line is active high * GPIO_ACTIVE_LOW - GPIO line is active low - * GPIO_OPEN_DRAIN - GPIO line ise set up as open drain + * GPIO_OPEN_DRAIN - GPIO line is set up as open drain * GPIO_OPEN_SOURCE - GPIO line is set up as open source * GPIO_PERSISTENT - GPIO line is persistent during suspend/resume and maintains its value -- cgit v1.2.3 From 24bd3efc9d1efb5f756a7c6f807a36ddb6adc671 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 22 Jan 2018 13:19:28 +0100 Subject: gpio: Fix kernel stack leak to userspace The GPIO event descriptor was leaking kernel stack to userspace because we don't zero the variable before use. Ooops. Fix this. Cc: stable@vger.kernel.org Reported-by: Arnd Bergmann Reviewed-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 37e31ba82ca0..754836e4ca0e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -744,6 +744,9 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) struct gpioevent_data ge; int ret, level; + /* Do not leak kernel stack to userspace */ + memset(&ge, 0, sizeof(ge)); + ge.timestamp = ktime_get_real_ns(); level = gpiod_get_value_cansleep(le->desc); -- cgit v1.2.3 From d58f2bf261fdf3a3fc916c9999a686f959dcf6b6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 30 Nov 2017 10:23:27 +0100 Subject: gpio: Timestamp events in hardirq handler Add a hardirq handler to the GPIO userspace event loop, making sure to pick up the timestamp there, as close as possible in time relative to the actual event causing the interrupt. Tested with a simple pushbutton GPIO on ux500 and seems to work fine. Cc: Bartosz Golaszewski Reviewed-by: Felipe Balbi Reported-by: Felipe Balbi Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 754836e4ca0e..1eebd69f369e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -600,6 +600,9 @@ out_free_lh: * @events: KFIFO for the GPIO events * @read_lock: mutex lock to protect reads from colliding with adding * new events to the FIFO + * @timestamp: cache for the timestamp storing it between hardirq + * and IRQ thread, used to bring the timestamp close to the actual + * event */ struct lineevent_state { struct gpio_device *gdev; @@ -610,6 +613,7 @@ struct lineevent_state { wait_queue_head_t wait; DECLARE_KFIFO(events, struct gpioevent_data, 16); struct mutex read_lock; + u64 timestamp; }; #define GPIOEVENT_REQUEST_VALID_FLAGS \ @@ -747,7 +751,7 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) /* Do not leak kernel stack to userspace */ memset(&ge, 0, sizeof(ge)); - ge.timestamp = ktime_get_real_ns(); + ge.timestamp = le->timestamp; level = gpiod_get_value_cansleep(le->desc); if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE @@ -775,6 +779,19 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) return IRQ_HANDLED; } +static irqreturn_t lineevent_irq_handler(int irq, void *p) +{ + struct lineevent_state *le = p; + + /* + * Just store the timestamp in hardirq context so we get it as + * close in time as possible to the actual event. + */ + le->timestamp = ktime_get_real_ns(); + + return IRQ_WAKE_THREAD; +} + static int lineevent_create(struct gpio_device *gdev, void __user *ip) { struct gpioevent_request eventreq; @@ -867,7 +884,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) /* Request a thread to read the events */ ret = request_threaded_irq(le->irq, - NULL, + lineevent_irq_handler, lineevent_irq_thread, irqflags, le->label, -- cgit v1.2.3