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.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpio/gpiolib.h') 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(-) (limited to 'drivers/gpio/gpiolib.h') 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(-) (limited to 'drivers/gpio/gpiolib.h') 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 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(-) (limited to 'drivers/gpio/gpiolib.h') 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 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(-) (limited to 'drivers/gpio/gpiolib.h') 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 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(-) (limited to 'drivers/gpio/gpiolib.h') 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