aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2019-09-05 11:36:09 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2022-10-26 22:46:13 -0700
commit27103fbbe0c396313c7b89742b8dae15cd9d16ea (patch)
treeae5ff71ddb02f0240103004c42b6aa2644a0ef6e
parentd9f252acf1a40c99b5b28f4c885d6a1420c5d3d7 (diff)
downloadinput-tmp-gpiolib-swnode.tar.gz
gpiolib: add support for software nodestmp-gpiolib-swnode
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@gmail.com> Series-to: Linus Walleij <linus.walleij@linaro.org> Series-cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Series-cc: Mika Westerberg <mika.westerberg@linux.intel.com> Series-cc: linux-gpio@vger.kernel.org Series-cc: linux-kernel@vger.kernel.org Cover-letter: Add support for software nodes to gpiolib This series attempts to add support for software nodes to gpiolib, using software node references that were introduced recently. This allows us to convert more drivers to the generic device properties and drop support for custom platform data: static const struct software_node gpio_bank_b_node = { .name = "B", }; static const struct property_entry simone_key_enter_props[] = { PROPERTY_ENTRY_U32("linux,code", KEY_ENTER), PROPERTY_ENTRY_STRING("label", "enter"), PROPERTY_ENTRY_REF("gpios", &gpio_bank_b_node, 123, GPIO_ACTIVE_LOW), { } }; If we agree in principle, I would like to have the very first 3 patches in an immutable branch off maybe -rc8 so that it can be pulled into individual subsystems so that patches switching various drivers to fwnode_gpiod_get_index() could be applied. END
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpiolib-swnode.c102
-rw-r--r--drivers/gpio/gpiolib-swnode.h13
-rw-r--r--drivers/gpio/gpiolib.c24
4 files changed, 139 insertions, 1 deletions
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 84fae267e8ebf..5009e59e7d1e5 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 0000000000000..3c193f47f2988
--- /dev/null
+++ b/drivers/gpio/gpiolib-swnode.c
@@ -0,0 +1,102 @@
+// 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;
+ 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 '%s[%d]'\n",
+ __func__, prop_name, swnode->name ?: "unnamed", 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);
+
+ /* We expect native GPIO flags */
+ *flags = args.args[1];
+
+ return gpiochip_get_desc(chip, args.args[0]);
+}
+
+/**
+ * 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 0000000000000..afd51c9b6e342
--- /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 88cbfa2500569..5c7e1297ab435 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"
@@ -3824,6 +3825,18 @@ static struct gpio_desc *gpiod_find_and_request(struct fwnode_handle *fwnode,
dev_dbg(fwnode->dev, "using ACPI for GPIO lookup\n");
desc = acpi_find_gpio(fwnode,
con_id, idx, &flags, &lookupflags);
+ } else if (is_software_node(fwnode)) {
+ dev_dbg(fwnode->dev, "using software node for GPIO lookup\n");
+ desc = swnode_find_gpio(fwnode, con_id, idx, &lookupflags);
+ }
+
+ if (IS_ERR(desc) &&
+ !IS_ERR_OR_NULL(fwnode) &&
+ is_software_node(fwnode->secondary)) {
+ dev_dbg(fwnode->dev,
+ "using secondary software node for GPIO lookup\n");
+ desc = swnode_find_gpio(fwnode->secondary,
+ con_id, idx, &lookupflags);
}
/*
@@ -3916,13 +3929,22 @@ 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;
+ struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
int count = -ENOENT;
+ int secondary_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);
+
+ if (!IS_ERR_OR_NULL(fwnode) && is_software_node(fwnode->secondary)) {
+ secondary_count = swnode_gpio_count(fwnode, con_id);
+ if (secondary_count > 0)
+ count = (count > 0 ? count : 0) + secondary_count;
+ }
if (count < 0)
count = platform_gpio_count(dev, con_id);