Now that static device properties understand notion of child nodes and references, let's teach gpiolib to handle them: - GPIOs are represented as a references to software nodes representing gpiochip - references must have 2 arguments - GPIO number within the chip and GPIO flags (GPIO_ACTIVE_LOW/GPIO_ACTIVE_HIGH, etc). - name of the software node representing gpiochip must match label of the gpiochip, as we use it to locate gpiochip structure at runtime. const struct software_node gpio_bank_b_node = { .name = "B", }; const struct property_entry simone_key_enter_props[] __initconst = { PROPERTY_ENTRY_U32("linux,code", KEY_ENTER), PROPERTY_ENTRY_STRING("label", "enter"), PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW), { } }; Signed-off-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> --- drivers/gpio/Makefile | 1 + drivers/gpio/gpiolib-swnode.c | 106 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib-swnode.h | 13 ++++++ drivers/gpio/gpiolib.c | 35 +++++++++++++- 4 files changed, 153 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8629e9eaf79e..010587025fc8 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o +obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c new file mode 100644 index 000000000000..d005ce0b986d --- /dev/null +++ b/drivers/gpio/gpiolib-swnode.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Software Node helpers for the GPIO API + * + * Copyright 2022 Google LLC + */ +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gpio/consumer.h> + +#include "gpiolib.h" +#include "gpiolib-swnode.h" + +static int swnode_gpiochip_match_name(struct gpio_chip *chip, void *data) +{ + return !strcmp(chip->label, data); +} + +struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode, + const char *con_id, unsigned int idx, + unsigned long *flags) +{ + const struct software_node *chip_node; + const struct software_node *swnode; + struct fwnode_reference_args args; + struct gpio_chip *chip; + struct gpio_desc *desc; + char prop_name[32]; /* 32 is max size of property name */ + int error; + + swnode = to_software_node(fwnode); + if (!swnode) + return ERR_PTR(-EINVAL); + + /* + * Note we do not need to try both -gpios and -gpio suffixes, + * as, unlike OF and ACPI, we can fix software nodes to conform + * to the proper binding. + */ + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-gpios", con_id); + else + strscpy(prop_name, "gpios", sizeof(prop_name)); + + /* + * We expect all swnode-described GPIOs have GPIO number and + * polarity arguments, hence nargs is set to 2. + */ + error = fwnode_property_get_reference_args(fwnode, prop_name, NULL, + 2, idx, &args); + if (error) { + pr_debug("%s: can't parse '%s' property of node '%pfwP[%d]'\n", + __func__, prop_name, fwnode, idx); + return ERR_PTR(error); + } + + chip_node = to_software_node(args.fwnode); + if (!chip_node || !chip_node->name) + return ERR_PTR(-EINVAL); + + chip = gpiochip_find((void *)chip_node->name, + swnode_gpiochip_match_name); + if (!chip) + return ERR_PTR(-EPROBE_DEFER); + + desc = gpiochip_get_desc(chip, args.args[0]); + *flags = args.args[1]; /* We expect native GPIO flags */ + + pr_debug("%s: parsed '%s' property of node '%pfwP[%d]' - status (%d)\n", + __func__, prop_name, fwnode, idx, PTR_ERR_OR_ZERO(desc)); + + return desc; +} + +/** + * swnode_gpio_count - count the GPIOs associated with a device / function + * @fwnode: firmware node of the GPIO consumer, can be %NULL for + * system-global GPIOs + * @con_id: function within the GPIO consumer + * + * Return: + * The number of GPIOs associated with a device / function or %-ENOENT, + * if no GPIO has been assigned to the requested function. + */ +int swnode_gpio_count(struct fwnode_handle *fwnode, const char *con_id) +{ + struct fwnode_reference_args args; + char prop_name[32]; + int count; + + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-gpios", con_id); + else + strscpy(prop_name, "gpios", sizeof(prop_name)); + + /* + * This is not very efficient, but GPIO lists usually have only + * 1 or 2 entries. + */ + count = 0; + while (fwnode_property_get_reference_args(fwnode, prop_name, NULL, + 0, count, &args) == 0) + count++; + + return count ? count : -ENOENT; +} diff --git a/drivers/gpio/gpiolib-swnode.h b/drivers/gpio/gpiolib-swnode.h new file mode 100644 index 000000000000..afd51c9b6e34 --- /dev/null +++ b/drivers/gpio/gpiolib-swnode.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIOLIB_SWNODE_H +#define GPIOLIB_SWNODE_H + +struct fwnode_handle; + +struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode, + const char *con_id, unsigned int idx, + unsigned long *flags); +int swnode_gpio_count(struct fwnode_handle *fwnode, const char *con_id); + +#endif /* GPIOLIB_SWNODE_H */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 79aaba693c4f..b9976485587d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -26,6 +26,7 @@ #include "gpiolib.h" #include "gpiolib-of.h" #include "gpiolib-acpi.h" +#include "gpiolib-swnode.h" #include "gpiolib-cdev.h" #include "gpiolib-sysfs.h" @@ -3813,6 +3814,19 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode, dev_dbg(consumer, "GPIO lookup for consumer %s in node '%s'\n", con_id, fwnode_get_name(fwnode)); + /* + * First look up GPIO in the secondary software node in case + * it was used to store updated properties. + */ + if (is_software_node(fwnode->secondary)) { + dev_dbg(consumer, + "using secondary software node for GPIO lookup\n"); + desc = swnode_find_gpio(fwnode->secondary, + con_id, idx, lookupflags); + if (!gpiod_not_found(desc)) + return desc; + } + /* Using device tree? */ if (is_of_node(fwnode)) { dev_dbg(consumer, "using device tree for GPIO lookup\n"); @@ -3821,6 +3835,9 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode, } else if (is_acpi_node(fwnode)) { dev_dbg(consumer, "using ACPI for GPIO lookup\n"); desc = acpi_find_gpio(fwnode, con_id, idx, flags, lookupflags); + } else if (is_software_node(fwnode)) { + dev_dbg(consumer, "using software node for GPIO lookup\n"); + desc = swnode_find_gpio(fwnode, con_id, idx, lookupflags); } return desc; @@ -3933,13 +3950,27 @@ EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index); */ int gpiod_count(struct device *dev, const char *con_id) { - const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL; - int count = -ENOENT; + struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL; + int count; + + /* + * First look up GPIO in the secondary software node in case + * it was used to store updated properties. + */ + if (!IS_ERR_OR_NULL(fwnode) && is_software_node(fwnode->secondary)) { + count = swnode_gpio_count(fwnode->secondary, con_id); + if (count > 0) + return count; + } if (is_of_node(fwnode)) count = of_gpio_get_count(dev, con_id); else if (is_acpi_node(fwnode)) count = acpi_gpio_count(dev, con_id); + else if (is_software_node(fwnode)) + count = swnode_gpio_count(fwnode, con_id); + else + count = -ENOENT; if (count < 0) count = platform_gpio_count(dev, con_id); -- b4 0.11.0-dev-28747