@@ -206,8 +206,8 @@ struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev,
snprintf(prop_name, sizeof(prop_name), "%s",
gpio_suffixes[i]);
- desc = fwnode_get_named_gpiod(child, prop_name, index, flags,
- label);
+ desc = fwnode_get_named_gpiod_dev(dev, child, prop_name, index,
+ flags, label);
if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
break;
}
@@ -3994,7 +3994,8 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
EXPORT_SYMBOL(gpiod_get_from_of_node);
/**
- * fwnode_get_named_gpiod - obtain a GPIO from firmware node
+ * fwnode_get_named_gpiod_dev - obtain a GPIO from firmware node
+ * @dev: corresponding device, may be NULL for DT and ACPI
* @fwnode: handle of the firmware node
* @propname: name of the firmware property representing the GPIO
* @index: index of the GPIO to obtain for the consumer
@@ -4002,7 +4003,7 @@ EXPORT_SYMBOL(gpiod_get_from_of_node);
* @label: label to attach to the requested GPIO
*
* This function can be used for drivers that get their configuration
- * from opaque firmware.
+ * from opaque firmware or machine descriptors in board files.
*
* The function properly finds the corresponding GPIO using whatever is the
* underlying firmware interface and then makes sure that the GPIO
@@ -4012,20 +4013,19 @@ EXPORT_SYMBOL(gpiod_get_from_of_node);
* On successful request the GPIO pin is configured in accordance with
* provided @dflags.
*
- * In case of error an ERR_PTR() is returned.
+ * In case of error an ERR_PTR() is returned. -ENOENT is returned if
+ * no GPIO descriptor can be located in DT, ACPI or board file tables.
*/
-struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
- const char *propname, int index,
- enum gpiod_flags dflags,
- const char *label)
+struct gpio_desc *fwnode_get_named_gpiod_dev(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *propname, int index,
+ enum gpiod_flags dflags,
+ const char *label)
{
struct gpio_desc *desc = ERR_PTR(-ENODEV);
- unsigned long lflags = 0;
+ enum gpio_lookup_flags lflags = 0;
int ret;
- if (!fwnode)
- return ERR_PTR(-EINVAL);
-
if (is_of_node(fwnode)) {
desc = gpiod_get_from_of_node(to_of_node(fwnode),
propname, index,
@@ -4043,9 +4043,30 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
if (info.polarity == GPIO_ACTIVE_LOW)
lflags |= GPIO_ACTIVE_LOW;
+ } else {
+ desc = gpiod_find(dev, propname, index, &lflags);
+ if (IS_ERR(desc))
+ return desc;
+ /*
+ * This is a special case. This happens when you try
+ * to look up more than one child node from
+ * fwnode_get_*_from_child(): the boardfile tables
+ * do not have hierarchy or children, they are flat.
+ * To get out of this without having to reinvent a
+ * complex datastructure for board files, we convert
+ * the child to an index by simply trying the next
+ * descriptor index until we get something not busy
+ * (already requested) or -ENOENT.
+ */
+ while (test_bit(FLAG_REQUESTED, &desc->flags)) {
+ index++;
+ desc = gpiod_find(dev, propname, index,
+ &lflags);
+ if (IS_ERR(desc))
+ return desc;
+ }
}
- /* Currently only ACPI takes this path */
ret = gpiod_request(desc, label);
if (ret)
return ERR_PTR(ret);
@@ -4058,7 +4079,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
return desc;
}
-EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
+EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod_dev);
/**
* gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO
@@ -157,10 +157,21 @@ struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev,
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,
- const char *label);
+struct gpio_desc *fwnode_get_named_gpiod_dev(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *propname, int index,
+ enum gpiod_flags dflags,
+ const char *label);
+static inline struct gpio_desc *fwnode_get_named_gpiod(
+ struct fwnode_handle *fwnode,
+ const char *propname, int index,
+ enum gpiod_flags dflags,
+ const char *label)
+{
+ /* Just a wrapper calling the former for device NULL */
+ return fwnode_get_named_gpiod_dev(NULL, fwnode, propname, index,
+ dflags, label);
+}
struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev,
const char *con_id, int index,
struct fwnode_handle *child,
In some cases, such as the input gpio_keys code, we want to just call devm_fwnode_get_gpiod_from_child() and not worry about whether the GPIO descriptor is coming from DT or ACPI. To get rid of legacy hard-coded global GPIO numbers we also need to be able to look up GPIOs from machine GPIO descriptor tables. This patch fixes this by using machine tables as a fallback. Since the machine tables are flat and have no concept such as "children" as DT or ACPI has, we simply convert the child to an index in request order. In these few legacy use cases, only the children have GPIO descriptors assigned, the parent does not, so it should be safe to assume that we can represent the children as index 0..N of the parent "gpios" property in machine tables. As these pass the rest of the per-key configuration as an array in the struct gpio_keys_platform_data buttons array, it is natural for users to define the descriptor with an index when converting to machine GPIO descriptor tables. Since the board files have no concept of any "firmware node" we need to pass the device down from the devm_fwnode_*() calls so we can use the device name to locate the descriptor look-up tables, so we wrap fwnode_get_named_gpiod into fwnode_get_named_gpiod_dev() where current users that are DT-only or ACPI-only can pass NULL if they like. Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Andy Shevchenko <andy@infradead.org> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Cc: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> --- drivers/gpio/devres.c | 4 +-- drivers/gpio/gpiolib.c | 47 +++++++++++++++++++++++++---------- include/linux/gpio/consumer.h | 19 +++++++++++--- 3 files changed, 51 insertions(+), 19 deletions(-)