* [PATCH v2 00/10] gpio: improve support for shared GPIOs
@ 2025-10-22 13:10 Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 01/10] string: provide strends() Bartosz Golaszewski
` (10 more replies)
0 siblings, 11 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
Problem statement: GPIOs are implemented as a strictly exclusive
resource in the kernel but there are lots of platforms on which single
pin is shared by multiple devices which don't communicate so need some
way of properly sharing access to a GPIO. What we have now is the
GPIOD_FLAGS_BIT_NONEXCLUSIVE flag which was introduced as a hack and
doesn't do any locking or arbitration of access - it literally just hand
the same GPIO descriptor to all interested users.
The proposed solution is composed of three major parts: the high-level,
shared GPIO proxy driver that arbitrates access to the shared pin and
exposes a regular GPIO chip interface to consumers, a low-level shared
GPIOLIB module that scans firmware nodes and creates auxiliary devices
that attach to the proxy driver and finally a set of core GPIOLIB
changes that plug the former into the GPIO lookup path.
The changes are implemented in a way that allows to seamlessly compile
out any code related to sharing GPIOs for systems that don't need it.
The practical use-case for this are the powerdown GPIOs shared by
speakers on Qualcomm db845c platform, however I have also extensively
tested it using gpio-virtuser on arm64 qemu with various DT
configurations.
I'm Cc'ing some people that may help with reviewing/be interested in
this: OF maintainers (because the main target are OF systems initially),
Mark Brown because most users of GPIOD_FLAGS_BIT_NONEXCLUSIVE live
in audio or regulator drivers and one of the goals of this series is
dropping the hand-crafted GPIO enable counting via struct
regulator_enable_gpio in regulator core), Andy and Mika because I'd like
to also cover ACPI (even though I don't know about any ACPI platform that
would need this at the moment, I think it makes sense to make the
solution complete), Dmitry (same thing but for software nodes), Mani
(because you have a somewhat related use-case for the PERST# signal and
I'd like to hear your input on whether this is something you can use or
maybe it needs a separate, implicit gpio-perst driver similar to what
Krzysztof did for reset-gpios) and Greg (because I mentioned this to you
last week in person and I also use the auxiliary bus for the proxy
devices).
Merging strategy: patches 1-6 should go through the GPIO tree and then
ARM-SoC, ASoC and regulator trees can pull these changes from an
immutable branch and apply the remaining patches.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Changes in v2:
- Fix a memory leak in error path in gpiolib-shared
- Drop the gpio-wcd934x fix that already went upstream
- Free resources used during scanning by GPIOs that turned out to be
unique
- Rework the OF property scanning
- Add patches making the regulator subsystem aware of shared GPIOs
managed by GPIOLIB
- Link to v1: https://lore.kernel.org/r/20250924-gpio-shared-v1-0-775e7efeb1a3@linaro.org
---
Bartosz Golaszewski (10):
string: provide strends()
gpiolib: define GPIOD_FLAG_SHARED
gpiolib: implement low-level, shared GPIO support
gpio: shared-proxy: implement the shared GPIO proxy driver
gpiolib: support shared GPIOs in core subsystem code
gpio: provide gpiod_is_shared()
arm64: select HAVE_SHARED_GPIOS for ARCH_QCOM
ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
ASoC: wsa883x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
regulator: make the subsystem aware of shared GPIOs
arch/arm64/Kconfig.platforms | 1 +
drivers/gpio/Kconfig | 17 ++
drivers/gpio/Makefile | 2 +
drivers/gpio/gpio-shared-proxy.c | 329 ++++++++++++++++++++++++
drivers/gpio/gpiolib-shared.c | 530 +++++++++++++++++++++++++++++++++++++++
drivers/gpio/gpiolib-shared.h | 71 ++++++
drivers/gpio/gpiolib.c | 70 +++++-
drivers/gpio/gpiolib.h | 2 +
drivers/regulator/core.c | 8 +
include/linux/gpio/consumer.h | 9 +
include/linux/string.h | 2 +
lib/string.c | 19 ++
lib/tests/string_kunit.c | 13 +
sound/soc/codecs/wsa881x.c | 3 +-
sound/soc/codecs/wsa883x.c | 7 +-
15 files changed, 1067 insertions(+), 16 deletions(-)
---
base-commit: 304d18863e6e62a8f2d0350ce0a59596e2e42768
change-id: 20250908-gpio-shared-67ec352884b6
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v2 01/10] string: provide strends()
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-22 13:33 ` Andy Shevchenko
2025-10-22 15:25 ` Andy Shevchenko
2025-10-22 13:10 ` [PATCH v2 02/10] gpiolib: define GPIOD_FLAG_SHARED Bartosz Golaszewski
` (9 subsequent siblings)
10 siblings, 2 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Implement a function for checking if a string ends with a different
string and add its kunit test cases.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
include/linux/string.h | 2 ++
lib/string.c | 19 +++++++++++++++++++
lib/tests/string_kunit.c | 13 +++++++++++++
3 files changed, 34 insertions(+)
diff --git a/include/linux/string.h b/include/linux/string.h
index fdd3442c6bcbd786e177b6e87358e1065a0ffafc..16149404fc3aed535c094b9550f7bd7c9b44a0c9 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -562,4 +562,6 @@ static inline bool strstarts(const char *str, const char *prefix)
return strncmp(str, prefix, strlen(prefix)) == 0;
}
+bool strends(const char *str, const char *suffix);
+
#endif /* _LINUX_STRING_H_ */
diff --git a/lib/string.c b/lib/string.c
index b632c71df1a5061256f556c40a30587cf45dce18..ac18313d9c52b38132f0bfdd952cdec6b1354d76 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -880,3 +880,22 @@ void *memchr_inv(const void *start, int c, size_t bytes)
return check_bytes8(start, value, bytes % 8);
}
EXPORT_SYMBOL(memchr_inv);
+
+/**
+ * strends - Check if a string ends with another string.
+ * @str - NULL-terminated string to check against @suffix
+ * @suffix - NULL-terminated string defining the suffix to look for in @str
+ *
+ * Returns:
+ * True if @str ends with @suffix. False in all other cases.
+ */
+bool strends(const char *str, const char *suffix)
+{
+ unsigned int str_len = strlen(str), suffix_len = strlen(suffix);
+
+ if (str_len < suffix_len)
+ return false;
+
+ return !(strcmp(str + str_len - suffix_len, suffix));
+}
+EXPORT_SYMBOL(strends);
diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c
index 0ed7448a26d3aa0fe9e2a6a894d4c49c2c0b86e0..f9a8e557ba7734c9848d58ff986407d8000f52ee 100644
--- a/lib/tests/string_kunit.c
+++ b/lib/tests/string_kunit.c
@@ -602,6 +602,18 @@ static void string_test_memtostr(struct kunit *test)
KUNIT_EXPECT_EQ(test, dest[7], '\0');
}
+static void string_test_strends(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, strends("foo-bar", "bar"));
+ KUNIT_EXPECT_TRUE(test, strends("foo-bar", "-bar"));
+ KUNIT_EXPECT_TRUE(test, strends("foobar", "foobar"));
+ KUNIT_EXPECT_TRUE(test, strends("foobar", ""));
+ KUNIT_EXPECT_FALSE(test, strends("bar", "foobar"));
+ KUNIT_EXPECT_FALSE(test, strends("", "foo"));
+ KUNIT_EXPECT_FALSE(test, strends("foobar", "ba"));
+ KUNIT_EXPECT_TRUE(test, strends("", ""));
+}
+
static struct kunit_case string_test_cases[] = {
KUNIT_CASE(string_test_memset16),
KUNIT_CASE(string_test_memset32),
@@ -623,6 +635,7 @@ static struct kunit_case string_test_cases[] = {
KUNIT_CASE(string_test_strlcat),
KUNIT_CASE(string_test_strtomem),
KUNIT_CASE(string_test_memtostr),
+ KUNIT_CASE(string_test_strends),
{}
};
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 02/10] gpiolib: define GPIOD_FLAG_SHARED
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 01/10] string: provide strends() Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support Bartosz Golaszewski
` (8 subsequent siblings)
10 siblings, 0 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Define a new GPIO descriptor flag for marking pins that are shared by
multiple consumer. This flag will be used in several places so we need
to do it in advance and separately from other changes.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b4c5369f8a3314244424d0c90ba006f7568b314e..ae9aa145ca055c08dad55a537567a27de57f7066 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -204,6 +204,7 @@ struct gpio_desc {
#define GPIOD_FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
#define GPIOD_FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
#define GPIOD_FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
+#define GPIOD_FLAG_SHARED 20 /* GPIO is shared by multiple consumers */
/* Connection label */
struct gpio_desc_label __rcu *label;
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 01/10] string: provide strends() Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 02/10] gpiolib: define GPIOD_FLAG_SHARED Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-22 17:34 ` Andy Shevchenko
2025-10-22 13:10 ` [PATCH v2 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver Bartosz Golaszewski
` (7 subsequent siblings)
10 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
This module scans the device tree (for now only OF nodes are supported
but care is taken to make other fwnode implementations easy to
integrate) and determines which GPIO lines are shared by multiple users.
It stores that information in memory. When the GPIO chip exposing shared
lines is registered, the shared GPIO descriptors it exposes are marked
as shared and virtual "proxy" devices that mediate access to the shared
lines are created. When a consumer of a shared GPIO looks it up, its
fwnode lookup is redirected to a just-in-time machine lookup that points
to this proxy device.
This code can be compiled out on platforms which don't use shared GPIOs.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/Kconfig | 8 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpiolib-shared.c | 512 ++++++++++++++++++++++++++++++++++++++++++
drivers/gpio/gpiolib-shared.h | 71 ++++++
4 files changed, 592 insertions(+)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7ee3afbc2b05da4d7470e609a0311ecc38727b00..679a7385a9776eef96a86ca4f429ee83ac939362 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -6,6 +6,9 @@
config GPIOLIB_LEGACY
def_bool y
+config HAVE_SHARED_GPIOS
+ bool
+
menuconfig GPIOLIB
bool "GPIO Support"
help
@@ -42,6 +45,11 @@ config GPIOLIB_IRQCHIP
select IRQ_DOMAIN
bool
+config GPIO_SHARED
+ def_bool y
+ depends on HAVE_SHARED_GPIOS || COMPILE_TEST
+ select AUXILIARY_BUS
+
config OF_GPIO_MM_GPIOCHIP
bool
help
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ec296fa14bfdb360c27b4578826354762f01f074..f702f7e27e5b4017e7eab3019dae4ec912d534f8 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
+obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2087d0df4ab7bcd23d3736e341c0f57cd748af4
--- /dev/null
+++ b/drivers/gpio/gpiolib-shared.c
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/idr.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/overflow.h>
+#include <linux/printk.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "gpiolib.h"
+#include "gpiolib-shared.h"
+
+/* Represents a single reference to a GPIO pin. */
+struct gpio_shared_ref {
+ struct list_head list;
+ /* Firmware node associated with this GPIO's consumer. */
+ struct fwnode_handle *fwnode;
+ /* GPIO flags this consumer uses for the request. */
+ enum gpiod_flags flags;
+ char *con_id;
+ int dev_id;
+ struct auxiliary_device adev;
+ struct gpiod_lookup_table *lookup;
+};
+
+/* Represents a single GPIO pin. */
+struct gpio_shared_entry {
+ struct list_head list;
+ /* Firmware node associated with the GPIO controller. */
+ struct fwnode_handle *fwnode;
+ /* Hardware offset of the GPIO within its chip. */
+ unsigned int offset;
+ /* Index in the property value array. */
+ size_t index;
+ struct gpio_shared_desc *shared_desc;
+ struct kref ref;
+ struct list_head refs;
+};
+
+static LIST_HEAD(gpio_shared_list);
+static DEFINE_MUTEX(gpio_shared_lock);
+static DEFINE_IDA(gpio_shared_ida);
+
+static struct gpio_shared_entry *
+gpio_shared_find_entry(struct fwnode_handle *controller_node,
+ unsigned int offset)
+{
+ struct gpio_shared_entry *entry;
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ if (entry->fwnode == controller_node && entry->offset == offset)
+ return entry;
+ }
+
+ return NULL;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static int gpio_shared_of_traverse(struct device_node *curr)
+{
+ struct gpio_shared_entry *entry;
+ size_t con_id_len, suffix_len;
+ struct fwnode_handle *fwnode;
+ struct of_phandle_args args;
+ struct property *prop;
+ unsigned int offset;
+ const char *suffix;
+ int ret, count, i;
+
+ for_each_property_of_node(curr, prop) {
+ /*
+ * The standard name for a GPIO property is "foo-gpios"
+ * or "foo-gpio". Some bindings also use "gpios" or "gpio".
+ * There are some legacy device-trees which have a different
+ * naming convention and for which we have rename quirks in
+ * place in gpiolib-of.c. I don't think any of them require
+ * support for shared GPIOs so for now let's just ignore
+ * them. We can always just export the quirk list and
+ * iterate over it here.
+ */
+ if (!strends(prop->name, "-gpios") &&
+ !strends(prop->name, "-gpio") &&
+ strcmp(prop->name, "gpios") != 0 &&
+ strcmp(prop->name, "gpio") != 0)
+ continue;
+
+ count = of_count_phandle_with_args(curr, prop->name,
+ "#gpio-cells");
+ if (count <= 0)
+ continue;
+
+ for (i = 0; i < count; i++) {
+ struct device_node *np __free(device_node) = NULL;
+
+ ret = of_parse_phandle_with_args(curr, prop->name,
+ "#gpio-cells", i,
+ &args);
+ if (ret)
+ continue;
+
+ np = args.np;
+
+ if (!of_property_present(np, "gpio-controller"))
+ continue;
+
+ /*
+ * We support 1, 2 and 3 cell GPIO bindings in the
+ * kernel currently. There's only one old MIPS dts that
+ * has a one-cell binding but there's no associated
+ * consumer so it may as well be an error. There don't
+ * seem to be any 3-cell users of non-exclusive GPIOs,
+ * so we can skip this as well. Let's occupy ourselves
+ * with the predominant 2-cell binding with the first
+ * cell indicating the hardware offset of the GPIO and
+ * the second defining the GPIO flags of the request.
+ */
+ if (args.args_count != 2)
+ continue;
+
+ fwnode = of_fwnode_handle(args.np);
+ offset = args.args[0];
+
+ entry = gpio_shared_find_entry(fwnode, offset);
+ if (!entry) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->fwnode = fwnode_handle_get(fwnode);
+ entry->offset = offset;
+ entry->index = count;
+ INIT_LIST_HEAD(&entry->refs);
+
+ list_add_tail(&entry->list, &gpio_shared_list);
+ }
+
+ struct gpio_shared_ref *ref __free(kfree) =
+ kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return -ENOMEM;
+
+ ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr));
+ ref->flags = args.args[1];
+
+ if (strends(prop->name, "gpios"))
+ suffix = "-gpios";
+ else if (strends(prop->name, "gpio"))
+ suffix = "-gpio";
+ else
+ suffix = NULL;
+ if (!suffix)
+ continue;
+
+ /* We only set con_id if there's actually one. */
+ if (strcmp(prop->name, "gpios") && strcmp(prop->name, "gpio")) {
+ ref->con_id = kstrdup(prop->name, GFP_KERNEL);
+ if (!ref->con_id)
+ return -ENOMEM;
+
+ con_id_len = strlen(ref->con_id);
+ suffix_len = strlen(suffix);
+
+ ref->con_id[con_id_len - suffix_len] = '\0';
+ }
+
+ ref->dev_id = ida_alloc(&gpio_shared_ida, GFP_KERNEL);
+ if (ref->dev_id < 0) {
+ kfree(ref->con_id);
+ return -ENOMEM;
+ }
+
+ if (!list_empty(&entry->refs))
+ pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n",
+ entry->offset, fwnode_get_name(entry->fwnode));
+
+ list_add_tail(&no_free_ptr(ref)->list, &entry->refs);
+ }
+ }
+
+ for_each_child_of_node_scoped(curr, child) {
+ ret = gpio_shared_of_traverse(child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gpio_shared_of_scan(void)
+{
+ return gpio_shared_of_traverse(of_root);
+}
+#else
+static int gpio_shared_of_scan(void)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
+static int gpio_shared_make_adev(struct gpio_device *gdev,
+ struct gpio_shared_ref *ref)
+{
+ struct auxiliary_device *adev = &ref->adev;
+ int ret;
+
+ lockdep_assert_held(&gpio_shared_lock);
+
+ memset(adev, 0, sizeof(*adev));
+
+ adev->id = ref->dev_id;
+ adev->name = "proxy";
+ adev->dev.parent = gdev->dev.parent;
+ /* No need to dev->release() anything. */
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n",
+ dev_name(&adev->dev), gpio_device_get_label(gdev));
+
+ return 0;
+}
+
+int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
+{
+ const char *dev_id = dev_name(consumer);
+ struct gpio_shared_entry *entry;
+ struct gpio_shared_ref *ref;
+
+ struct gpiod_lookup_table *lookup __free(kfree) =
+ kzalloc(struct_size(lookup, table, 2), GFP_KERNEL);
+ if (!lookup)
+ return -ENOMEM;
+
+ guard(mutex)(&gpio_shared_lock);
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ list_for_each_entry(ref, &entry->refs, list) {
+ if (!device_match_fwnode(consumer, ref->fwnode))
+ continue;
+
+ /* We've already done that on a previous request. */
+ if (ref->lookup)
+ return 0;
+
+ char *key __free(kfree) =
+ kasprintf(GFP_KERNEL,
+ KBUILD_MODNAME ".proxy.%u",
+ ref->adev.id);
+ if (!key)
+ return -ENOMEM;
+
+ pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n",
+ dev_id, key, ref->con_id ?: "none");
+
+ lookup->dev_id = dev_id;
+ lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0,
+ ref->con_id, lflags);
+
+ gpiod_add_lookup_table(no_free_ptr(lookup));
+
+ return 0;
+ }
+ }
+
+ /* We warn here because this can only happen if the programmer borked. */
+ WARN_ON(1);
+ return -ENOENT;
+}
+
+static void gpio_shared_remove_adev(struct auxiliary_device *adev)
+{
+ lockdep_assert_held(&gpio_shared_lock);
+
+ auxiliary_device_uninit(adev);
+ auxiliary_device_delete(adev);
+}
+
+int gpio_device_setup_shared(struct gpio_device *gdev)
+{
+ struct gpio_shared_entry *entry;
+ struct gpio_shared_ref *ref;
+ unsigned long *flags;
+ int ret;
+
+ guard(mutex)(&gpio_shared_lock);
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ if (!device_match_fwnode(&gdev->dev, entry->fwnode))
+ continue;
+
+ if (list_count_nodes(&entry->refs) <= 1)
+ continue;
+
+ flags = &gdev->descs[entry->offset].flags;
+
+ set_bit(GPIOD_FLAG_SHARED, flags);
+ /*
+ * Shared GPIOs are not requested via the normal path. Make
+ * them inaccessible to anyone even before we register the
+ * chip.
+ */
+ set_bit(GPIOD_FLAG_REQUESTED, flags);
+
+ pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
+ entry->offset, gpio_device_get_label(gdev));
+
+ list_for_each_entry(ref, &entry->refs, list) {
+ pr_debug("Setting up a shared GPIO entry for %s\n",
+ fwnode_get_name(ref->fwnode));
+
+ ret = gpio_shared_make_adev(gdev, ref);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void gpio_device_teardown_shared(struct gpio_device *gdev)
+{
+ struct gpio_shared_entry *entry;
+ struct gpio_shared_ref *ref;
+
+ guard(mutex)(&gpio_shared_lock);
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ if (!device_match_fwnode(&gdev->dev, entry->fwnode))
+ continue;
+
+ list_for_each_entry(ref, &entry->refs, list) {
+ gpiod_remove_lookup_table(ref->lookup);
+ kfree(ref->lookup->table[0].key);
+ kfree(ref->lookup);
+ ref->lookup = NULL;
+ gpio_shared_remove_adev(&ref->adev);
+ }
+ }
+}
+
+static void gpio_shared_release(struct kref *kref)
+{
+ struct gpio_shared_entry *entry =
+ container_of(kref, struct gpio_shared_entry, ref);
+ struct gpio_shared_desc *shared_desc = entry->shared_desc;
+
+ guard(mutex)(&gpio_shared_lock);
+
+ gpio_device_put(shared_desc->desc->gdev);
+ if (shared_desc->can_sleep)
+ mutex_destroy(&shared_desc->mutex);
+ kfree(shared_desc);
+ entry->shared_desc = NULL;
+}
+
+static void gpiod_shared_put(void *data)
+{
+ struct gpio_shared_entry *entry = data;
+
+ lockdep_assert_not_held(&gpio_shared_lock);
+
+ kref_put(&entry->ref, gpio_shared_release);
+}
+
+struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+ struct gpio_shared_desc *shared_desc;
+ struct gpio_shared_entry *entry;
+ struct gpio_shared_ref *ref;
+ struct gpio_device *gdev;
+ int ret;
+
+ scoped_guard(mutex, &gpio_shared_lock) {
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ list_for_each_entry(ref, &entry->refs, list) {
+ if (adev != &ref->adev)
+ continue;
+
+ if (entry->shared_desc) {
+ kref_get(&entry->ref);
+ shared_desc = entry->shared_desc;
+ break;
+ }
+
+ shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
+ if (!shared_desc)
+ return ERR_PTR(-ENOMEM);
+
+ gdev = gpio_device_find_by_fwnode(entry->fwnode);
+ if (!gdev) {
+ kfree(shared_desc);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ shared_desc->desc = &gdev->descs[entry->offset];
+ shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
+ if (shared_desc->can_sleep)
+ mutex_init(&shared_desc->mutex);
+ else
+ spin_lock_init(&shared_desc->spinlock);
+
+ kref_init(&entry->ref);
+ entry->shared_desc = shared_desc;
+
+ pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
+ dev_name(dev), desc_to_gpio(shared_desc->desc),
+ gpio_device_get_label(gdev));
+ break;
+ }
+ }
+ }
+
+ ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return shared_desc;
+}
+EXPORT_SYMBOL_GPL(devm_gpiod_shared_get);
+
+static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
+{
+ list_del(&ref->list);
+ kfree(ref->con_id);
+ ida_free(&gpio_shared_ida, ref->dev_id);
+ fwnode_handle_put(ref->fwnode);
+ kfree(ref);
+}
+
+static void gpio_shared_drop_entry(struct gpio_shared_entry *entry)
+{
+ list_del(&entry->list);
+ fwnode_handle_put(entry->fwnode);
+ kfree(entry);
+}
+
+/*
+ * This is only called if gpio_shared_init() fails so it's in fact __init and
+ * not __exit.
+ */
+static void __init gpio_shared_teardown(void)
+{
+ struct gpio_shared_entry *entry, *epos;
+ struct gpio_shared_ref *ref, *rpos;
+
+ list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) {
+ list_for_each_entry_safe(ref, rpos, &entry->refs, list)
+ gpio_shared_drop_ref(ref);
+
+ gpio_shared_drop_entry(entry);
+ }
+}
+
+static void gpio_shared_free_exclusive(void)
+{
+ struct gpio_shared_entry *entry, *epos;
+
+ list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) {
+ if (list_count_nodes(&entry->refs) > 1)
+ continue;
+
+ gpio_shared_drop_ref(list_first_entry(&entry->refs,
+ struct gpio_shared_ref,
+ list));
+ gpio_shared_drop_entry(entry);
+ }
+}
+
+static int __init gpio_shared_init(void)
+{
+ int ret;
+
+ /* Right now, we only support OF-based systems. */
+ ret = gpio_shared_of_scan();
+ if (ret) {
+ gpio_shared_teardown();
+ pr_err("Failed to scan OF nodes for shared GPIOs: %d\n", ret);
+ return ret;
+ }
+
+ gpio_shared_free_exclusive();
+
+ pr_debug("Finished scanning firmware nodes for shared GPIOs\n");
+ return 0;
+}
+postcore_initcall(gpio_shared_init);
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
new file mode 100644
index 0000000000000000000000000000000000000000..667dbdff3585066b7cbe2ebe476725fe7d683d84
--- /dev/null
+++ b/drivers/gpio/gpiolib-shared.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_GPIO_SHARED_H
+#define __LINUX_GPIO_SHARED_H
+
+#include <linux/cleanup.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+struct gpio_device;
+struct gpio_desc;
+struct device;
+
+#if IS_ENABLED(CONFIG_GPIO_SHARED)
+
+int gpio_device_setup_shared(struct gpio_device *gdev);
+void gpio_device_teardown_shared(struct gpio_device *gdev);
+int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags);
+
+#else
+
+static inline int gpio_device_setup_shared(struct gpio_device *gdev)
+{
+ return 0;
+}
+
+static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { }
+
+static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
+ unsigned long lflags)
+{
+ return 0;
+}
+
+#endif /* CONFIG_GPIO_SHARED */
+
+struct gpio_shared_desc {
+ struct gpio_desc *desc;
+ bool can_sleep;
+ unsigned long cfg;
+ unsigned int usecnt;
+ unsigned int highcnt;
+ union {
+ struct mutex mutex;
+ spinlock_t spinlock;
+ };
+};
+
+struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev);
+
+DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc,
+ if (_T->lock->can_sleep)
+ mutex_lock(&_T->lock->mutex);
+ else
+ spin_lock_irqsave(&_T->lock->spinlock, _T->flags),
+ if (_T->lock->can_sleep)
+ mutex_unlock(&_T->lock->mutex);
+ else
+ spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags),
+ unsigned long flags)
+
+static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc)
+{
+ if (shared_desc->can_sleep)
+ lockdep_assert_held(&shared_desc->mutex);
+ else
+ lockdep_assert_held(&shared_desc->spinlock);
+}
+
+#endif /* __LINUX_GPIO_SHARED_H */
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (2 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-22 18:04 ` Andy Shevchenko
2025-10-22 13:10 ` [PATCH v2 05/10] gpiolib: support shared GPIOs in core subsystem code Bartosz Golaszewski
` (6 subsequent siblings)
10 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Add a virtual GPIO proxy driver which arbitrates access to a single
shared GPIO by multiple users. It works together with the core shared
GPIO support from GPIOLIB and functions by acquiring a reference to a
shared GPIO descriptor exposed by gpiolib-shared and making sure that
the state of the GPIO stays consistent.
In general: if there's only one user at the moment: allow it to do
anything as if this was a normal GPIO (in essence: just propagate calls
to the underlying real hardware driver). If there are more users: don't
allow to change the direction set by the initial user, allow to change
configuration options but warn about possible conflicts and finally:
treat the output-high value as a reference counted, logical "GPIO
enabled" setting, meaning: the GPIO value is set to high when the first
user requests it to be high and back to low once the last user stops
"voting" for high.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/Kconfig | 9 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-shared-proxy.c | 329 +++++++++++++++++++++++++++++++++++++++
3 files changed, 339 insertions(+)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 679a7385a9776eef96a86ca4f429ee83ac939362..de8e4febf344fc19a633cd7efe8412fe12166fb8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -2025,6 +2025,15 @@ config GPIO_SIM
This enables the GPIO simulator - a configfs-based GPIO testing
driver.
+config GPIO_SHARED_PROXY
+ tristate "Proxy driver for non-exclusive GPIOs"
+ default m
+ depends on GPIO_SHARED || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ This enables the GPIO shared proxy driver - an abstraction layer
+ for GPIO pins that are shared by multiple devices.
+
endmenu
menu "GPIO Debugging utilities"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index f702f7e27e5b4017e7eab3019dae4ec912d534f8..d0020bc70b84f6fb9949165070c21725a60f47e2 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -160,6 +160,7 @@ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+obj-$(CONFIG_GPIO_SHARED_PROXY) += gpio-shared-proxy.o
obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b3dd875a5ad9f56ca9d27f6f2246eb854829f1f
--- /dev/null
+++ b/drivers/gpio/gpio-shared-proxy.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+
+#include "gpiolib-shared.h"
+
+struct gpio_shared_proxy_data {
+ struct gpio_chip gc;
+ struct gpio_shared_desc *shared_desc;
+ struct device *dev;
+ bool voted_high;
+};
+
+static int
+gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy,
+ int (*set_func)(struct gpio_desc *desc, int value),
+ int value)
+{
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int ret = 0;
+
+ gpio_shared_lockdep_assert(shared_desc);
+
+ if (value) {
+ /* User wants to set value to high. */
+ if (proxy->voted_high)
+ /* Already voted for high, nothing to do. */
+ goto out;
+
+ /* Haven't voted for high yet. */
+ if (!shared_desc->highcnt) {
+ /*
+ * Current value is low, need to actually set value
+ * to high.
+ */
+ ret = set_func(desc, 1);
+ if (ret)
+ goto out;
+ }
+
+ shared_desc->highcnt++;
+ proxy->voted_high = true;
+
+ goto out;
+ }
+
+ /* Desired value is low. */
+ if (!proxy->voted_high)
+ /* We didn't vote for high, nothing to do. */
+ goto out;
+
+ /* We previously voted for high. */
+ if (shared_desc->highcnt == 1) {
+ /* This is the last remaining vote for high, set value to low. */
+ ret = set_func(desc, 0);
+ if (ret)
+ goto out;
+ }
+
+ shared_desc->highcnt--;
+ proxy->voted_high = false;
+
+out:
+ if (shared_desc->highcnt)
+ dev_dbg(proxy->dev,
+ "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n",
+ value ? "high" : "low", shared_desc->highcnt);
+ else
+ dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n");
+
+ return ret;
+}
+
+static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ proxy->shared_desc->usecnt++;
+
+ dev_dbg(proxy->dev, "Shared GPIO requested, number of users: %u\n",
+ proxy->shared_desc->usecnt);
+
+ return 0;
+}
+
+static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ proxy->shared_desc->usecnt--;
+
+ dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n",
+ proxy->shared_desc->usecnt);
+}
+
+static int gpio_shared_proxy_set_config(struct gpio_chip *gc,
+ unsigned int offset, unsigned long cfg)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int ret;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ if (shared_desc->usecnt > 1) {
+ if (shared_desc->cfg != cfg) {
+ dev_dbg(proxy->dev,
+ "Shared GPIO's configuration already set, accepting changes but users may conflict!!\n");
+ } else {
+ dev_dbg(proxy->dev, "Equal config requested, nothing to do\n");
+ return 0;
+ }
+ }
+
+ ret = gpiod_set_config(desc, cfg);
+ if (ret && ret != -ENOTSUPP)
+ return ret;
+
+ shared_desc->cfg = cfg;
+ return 0;
+}
+
+static int gpio_shared_proxy_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int dir;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ if (shared_desc->usecnt == 1) {
+ dev_dbg(proxy->dev,
+ "Only one user of this shared GPIO, allowing to set direction to input\n");
+
+ return gpiod_direction_input(desc);
+ }
+
+ dir = gpiod_get_direction(desc);
+ if (dir < 0)
+ return dir;
+
+ if (dir == GPIO_LINE_DIRECTION_OUT) {
+ dev_dbg(proxy->dev,
+ "Shared GPIO's direction already set to output, refusing to change\n");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int ret, dir;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ if (shared_desc->usecnt == 1) {
+ dev_dbg(proxy->dev,
+ "Only one user of this shared GPIO, allowing to set direction to output with value '%s'\n",
+ value ? "high" : "low");
+
+ ret = gpiod_direction_output(desc, value);
+ if (ret)
+ return ret;
+
+ if (value) {
+ proxy->voted_high = true;
+ shared_desc->highcnt = 1;
+ } else {
+ proxy->voted_high = false;
+ shared_desc->highcnt = 0;
+ }
+
+ return 0;
+ }
+
+ dir = gpiod_get_direction(desc);
+ if (dir < 0)
+ return dir;
+
+ if (dir == GPIO_LINE_DIRECTION_IN) {
+ dev_dbg(proxy->dev,
+ "Shared GPIO's direction already set to input, refusing to change\n");
+ return -EPERM;
+ }
+
+ return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value);
+}
+
+static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_get_value(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_get_value_cansleep(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy,
+ int (*set_func)(struct gpio_desc *desc, int value),
+ int value)
+{
+ guard(gpio_shared_desc_lock)(proxy->shared_desc);
+
+ return gpio_shared_proxy_set_unlocked(proxy, set_func, value);
+}
+
+static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value);
+}
+
+static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value);
+}
+
+static int gpio_shared_proxy_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_get_direction(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_to_irq(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct gpio_shared_proxy_data *proxy;
+ struct gpio_shared_desc *shared_desc;
+ struct device *dev = &adev->dev;
+ struct gpio_chip *gc;
+
+ shared_desc = devm_gpiod_shared_get(dev);
+ if (IS_ERR(shared_desc))
+ return PTR_ERR(shared_desc);
+
+ proxy = devm_kzalloc(dev, sizeof(*proxy), GFP_KERNEL);
+ if (!proxy)
+ return -ENOMEM;
+
+ proxy->shared_desc = shared_desc;
+ proxy->dev = dev;
+
+ gc = &proxy->gc;
+ gc->base = -1;
+ gc->ngpio = 1;
+ gc->label = dev_name(dev);
+ gc->parent = dev;
+ gc->owner = THIS_MODULE;
+ gc->can_sleep = shared_desc->can_sleep;
+
+ gc->request = gpio_shared_proxy_request;
+ gc->free = gpio_shared_proxy_free;
+ gc->set_config = gpio_shared_proxy_set_config;
+ gc->direction_input = gpio_shared_proxy_direction_input;
+ gc->direction_output = gpio_shared_proxy_direction_output;
+ if (gc->can_sleep) {
+ gc->set = gpio_shared_proxy_set_cansleep;
+ gc->get = gpio_shared_proxy_get_cansleep;
+ } else {
+ gc->set = gpio_shared_proxy_set;
+ gc->get = gpio_shared_proxy_get;
+ }
+ gc->get_direction = gpio_shared_proxy_get_direction;
+ gc->to_irq = gpio_shared_proxy_to_irq;
+
+ return devm_gpiochip_add_data(dev, &proxy->gc, proxy);
+}
+
+static const struct auxiliary_device_id gpio_shared_proxy_id_table[] = {
+ { .name = "gpiolib_shared.proxy" },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table);
+
+static struct auxiliary_driver gpio_shared_proxy_driver = {
+ .driver = {
+ .name = "gpio-shared-proxy",
+ },
+ .probe = gpio_shared_proxy_probe,
+ .id_table = gpio_shared_proxy_id_table,
+};
+module_auxiliary_driver(gpio_shared_proxy_driver);
+
+MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
+MODULE_DESCRIPTION("Shared GPIO mux driver.");
+MODULE_LICENSE("GPL");
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 05/10] gpiolib: support shared GPIOs in core subsystem code
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (3 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 06/10] gpio: provide gpiod_is_shared() Bartosz Golaszewski
` (5 subsequent siblings)
10 siblings, 0 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
As the final step in adding official support for shared GPIOs, enable
the previously added elements in core GPIO subsystem code. Set-up shared
GPIOs when adding a GPIO chip, tear it down on removal and check if a
GPIO descriptor looked up during the firmware-node stage is shared and
fall-back to machine lookup in this case.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 41 insertions(+), 9 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index ba5df8a233fe75e16bae615f7f7c8591066c056e..03fd60e787fcf5846519fb2db9a701770ed15d6b 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -37,6 +37,7 @@
#include "gpiolib-acpi.h"
#include "gpiolib-cdev.h"
#include "gpiolib-of.h"
+#include "gpiolib-shared.h"
#include "gpiolib-swnode.h"
#include "gpiolib-sysfs.h"
#include "gpiolib.h"
@@ -1200,6 +1201,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret)
goto err_remove_irqchip_mask;
+ ret = gpio_device_setup_shared(gdev);
+ if (ret)
+ goto err_remove_irqchip;
+
/*
* By first adding the chardev, and then adding the device,
* we get a device node entry in sysfs under
@@ -1211,10 +1216,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (gpiolib_initialized) {
ret = gpiochip_setup_dev(gdev);
if (ret)
- goto err_remove_irqchip;
+ goto err_teardown_shared;
}
+
return 0;
+err_teardown_shared:
+ gpio_device_teardown_shared(gdev);
err_remove_irqchip:
gpiochip_irqchip_remove(gc);
err_remove_irqchip_mask:
@@ -1283,6 +1291,7 @@ void gpiochip_remove(struct gpio_chip *gc)
/* Numb the device, cancelling all outstanding operations */
rcu_assign_pointer(gdev->chip, NULL);
synchronize_srcu(&gdev->srcu);
+ gpio_device_teardown_shared(gdev);
gpiochip_irqchip_remove(gc);
acpi_gpiochip_remove(gc);
of_gpiochip_remove(gc);
@@ -4646,11 +4655,29 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
scoped_guard(srcu, &gpio_devices_srcu) {
desc = gpiod_fwnode_lookup(fwnode, consumer, con_id, idx,
&flags, &lookupflags);
+ if (!IS_ERR_OR_NULL(desc) &&
+ test_bit(GPIOD_FLAG_SHARED, &desc->flags)) {
+ /*
+ * We're dealing with a GPIO shared by multiple
+ * consumers. This is the moment to add the machine
+ * lookup table for the proxy device as previously
+ * we only knew the consumer's fwnode.
+ */
+ ret = gpio_shared_add_proxy_lookup(consumer, lookupflags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Trigger platform lookup for shared GPIO proxy. */
+ desc = ERR_PTR(-ENOENT);
+ /* Trigger it even for fwnode-only gpiod_get(). */
+ platform_lookup_allowed = true;
+ }
+
if (gpiod_not_found(desc) && platform_lookup_allowed) {
/*
* Either we are not using DT or ACPI, or their lookup
- * did not return a result. In that case, use platform
- * lookup as a fallback.
+ * did not return a result or this is a shared GPIO. In
+ * that case, use platform lookup as a fallback.
*/
dev_dbg(consumer,
"using lookup tables for GPIO lookup\n");
@@ -4673,14 +4700,19 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
return ERR_PTR(ret);
/*
- * This happens when there are several consumers for
- * the same GPIO line: we just return here without
- * further initialization. It is a bit of a hack.
- * This is necessary to support fixed regulators.
+ * This happens when there are several consumers for the same
+ * GPIO line: we just return here without further
+ * initialization. It's a hack introduced long ago to support
+ * fixed regulators. We now have a better solution with
+ * automated scanning where affected platforms just need to
+ * select the provided Kconfig option.
*
- * FIXME: Make this more sane and safe.
+ * FIXME: Remove the GPIOD_FLAGS_BIT_NONEXCLUSIVE flag after
+ * making sure all platforms use the new mechanism.
*/
- dev_info(consumer, "nonexclusive access to GPIO for %s\n", name);
+ dev_info(consumer,
+ "nonexclusive access to GPIO for %s, consider updating your code to using gpio-shared-proxy\n",
+ name);
return desc;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 06/10] gpio: provide gpiod_is_shared()
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (4 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 05/10] gpiolib: support shared GPIOs in core subsystem code Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 07/10] arm64: select HAVE_SHARED_GPIOS for ARCH_QCOM Bartosz Golaszewski
` (4 subsequent siblings)
10 siblings, 0 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Provide an interface allowing consumers to check if a GPIO descriptor
represents a GPIO that can potentially be shared by multiple consumers
at the same time. This is exposed to allow subsystems that already
work around the limitations of the current non-exclusive GPIO handling
in some ways, to gradually convert to relying on the new shared GPIO
feature of GPIOLIB.
Extend the gpiolib-shared module to mark the GPIO shared proxy
descriptors with a flag checked by the new interface.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-shared.c | 18 ++++++++++++++++++
drivers/gpio/gpiolib.c | 20 ++++++++++++++++++++
drivers/gpio/gpiolib.h | 1 +
include/linux/gpio/consumer.h | 9 +++++++++
4 files changed, 48 insertions(+)
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index d2087d0df4ab7bcd23d3736e341c0f57cd748af4..b5c33bccff0765eb940fedd5d678c47a05f1a25d 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -309,6 +309,24 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
guard(mutex)(&gpio_shared_lock);
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ list_for_each_entry(ref, &entry->refs, list) {
+ if (gdev->dev.parent == &ref->adev.dev) {
+ /*
+ * This is a shared GPIO proxy. Mark its
+ * descriptor as such and return here.
+ */
+ set_bit(GPIOD_FLAG_SHARED_PROXY,
+ &gdev->descs[0].flags);
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * This is not a shared GPIO proxy but it still may be the device
+ * exposing shared pins. Find them and create the proxy devices.
+ */
list_for_each_entry(entry, &gpio_shared_list, list) {
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 03fd60e787fcf5846519fb2db9a701770ed15d6b..d689065471a1269ac0671b964c67670df5c95dd7 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -3984,6 +3984,26 @@ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
}
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
+/**
+ * gpiod_is_shared() - check if this GPIO can be shared by multiple consumers
+ * @desc: GPIO to inspect
+ *
+ * Returns:
+ * True if this GPIO can be shared by multiple consumers at once. False if it's
+ * a regular, exclusive GPIO.
+ *
+ * Note:
+ * This function returning true does not mean that this GPIO is currently being
+ * shared. It means the GPIO core has registered the fact that the firmware
+ * configuration indicates that it can be shared by multiple consumers and is
+ * in charge of arbitrating the access.
+ */
+bool gpiod_is_shared(const struct gpio_desc *desc)
+{
+ return test_bit(GPIOD_FLAG_SHARED_PROXY, &desc->flags);
+}
+EXPORT_SYMBOL_GPL(gpiod_is_shared);
+
/**
* gpiod_to_irq() - return the IRQ corresponding to a GPIO
* @desc: gpio whose IRQ will be returned (already requested)
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index ae9aa145ca055c08dad55a537567a27de57f7066..8deed8a7d1128f01f0696af040d06f5fe140c7f8 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -205,6 +205,7 @@ struct gpio_desc {
#define GPIOD_FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
#define GPIOD_FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
#define GPIOD_FLAG_SHARED 20 /* GPIO is shared by multiple consumers */
+#define GPIOD_FLAG_SHARED_PROXY 21 /* GPIO is a virtual proxy to a physically shared pin. */
/* Connection label */
struct gpio_desc_label __rcu *label;
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 00df68c514051434e6fa67dc2307c6a8ce4ce3df..a8acb7c0b5af5066fd05e533468fc28616c68d78 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -167,6 +167,8 @@ int gpiod_cansleep(const struct gpio_desc *desc);
int gpiod_to_irq(const struct gpio_desc *desc);
int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name);
+bool gpiod_is_shared(const struct gpio_desc *desc);
+
/* Convert between the old gpio_ and new gpiod_ interfaces */
struct gpio_desc *gpio_to_desc(unsigned gpio);
int desc_to_gpio(const struct gpio_desc *desc);
@@ -520,6 +522,13 @@ static inline int gpiod_set_consumer_name(struct gpio_desc *desc,
return -EINVAL;
}
+static inline bool gpiod_is_shared(const struct gpio_desc *desc)
+{
+ /* GPIO can never have been requested */
+ WARN_ON(desc);
+ return false;
+}
+
static inline struct gpio_desc *gpio_to_desc(unsigned gpio)
{
return NULL;
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 07/10] arm64: select HAVE_SHARED_GPIOS for ARCH_QCOM
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (5 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 06/10] gpio: provide gpiod_is_shared() Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup Bartosz Golaszewski
` (3 subsequent siblings)
10 siblings, 0 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Some qualcomm platforms use shared GPIOs. Enable support for them by
selecting the Kconfig switch provided by GPIOLIB.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
arch/arm64/Kconfig.platforms | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 13173795c43d4f28e2d47acc700f80a165d44671..3dbff0261f0add0516d8cb3fd0f29e277af94f20 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -316,6 +316,7 @@ config ARCH_QCOM
select GPIOLIB
select PINCTRL
select HAVE_PWRCTRL if PCI
+ select HAVE_SHARED_GPIOS
help
This enables support for the ARMv8 based Qualcomm chipsets.
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (6 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 07/10] arm64: select HAVE_SHARED_GPIOS for ARCH_QCOM Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-24 15:46 ` Mark Brown
2025-10-24 23:32 ` Alexey Klimov
2025-10-22 13:10 ` [PATCH v2 09/10] ASoC: wsa883x: " Bartosz Golaszewski
` (2 subsequent siblings)
10 siblings, 2 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
This driver is only used on Qualcomm platforms which now select
HAVE_SHARED_GPIOS so this flag can be dropped.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
sound/soc/codecs/wsa881x.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 636e59abc3772fc0b333873a329b65f4213c3ef3..92a1e3bb8371e178571a6c1ed6f1185fe6c2e757 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -1112,8 +1112,7 @@ static int wsa881x_probe(struct sdw_slave *pdev,
if (!wsa881x)
return -ENOMEM;
- wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
- GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", 0);
if (IS_ERR(wsa881x->sd_n))
return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n),
"Shutdown Control GPIO not found\n");
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 09/10] ASoC: wsa883x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (7 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-24 15:46 ` Mark Brown
2025-10-22 13:10 ` [PATCH v2 10/10] regulator: make the subsystem aware of shared GPIOs Bartosz Golaszewski
2025-10-24 7:17 ` [PATCH v2 00/10] gpio: improve support for " Péter Ujfalusi
10 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
This driver is only used on Qualcomm platforms which now select
HAVE_SHARED_GPIOS so this flag can be dropped.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
sound/soc/codecs/wsa883x.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index ca4520ade79aa2c9377449e29cdd33e9e8dd28c5..ba60b4de6cab7740cd8aa70c89f6e03e1dc2dd12 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -1572,13 +1572,10 @@ static int wsa883x_get_reset(struct device *dev, struct wsa883x_priv *wsa883x)
if (IS_ERR(wsa883x->sd_reset))
return dev_err_probe(dev, PTR_ERR(wsa883x->sd_reset),
"Failed to get reset\n");
- /*
- * if sd_reset: NULL, so use the backwards compatible way for powerdown-gpios,
- * which does not handle sharing GPIO properly.
- */
+
+ /* if sd_reset: NULL, so use the backwards compatible way for powerdown-gpios */
if (!wsa883x->sd_reset) {
wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
- GPIOD_FLAGS_BIT_NONEXCLUSIVE |
GPIOD_OUT_HIGH);
if (IS_ERR(wsa883x->sd_n))
return dev_err_probe(dev, PTR_ERR(wsa883x->sd_n),
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v2 10/10] regulator: make the subsystem aware of shared GPIOs
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (8 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 09/10] ASoC: wsa883x: " Bartosz Golaszewski
@ 2025-10-22 13:10 ` Bartosz Golaszewski
2025-10-24 15:57 ` Mark Brown
2025-10-24 7:17 ` [PATCH v2 00/10] gpio: improve support for " Péter Ujfalusi
10 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:10 UTC (permalink / raw)
To: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Bartosz Golaszewski,
Catalin Marinas, Will Deacon, Srinivas Kandagatla, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
GPIOLIB is now aware of shared GPIOs and - for platforms where access to
such pins is managed internally - we don't need to keep track of the
enable count.
Once all users in the kernel switch to using the new mechanism, we'll be
able to drop the internal counting of users from the regulator code.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/regulator/core.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index dd7b10e768c06c83d2a4fb5dfd0fce8d796c9185..f9690f0689fda85123d07967f3c87105829e330c 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2616,6 +2616,13 @@ static int regulator_ena_gpio_request(struct regulator_dev *rdev,
mutex_lock(®ulator_list_mutex);
+ if (gpiod_is_shared(gpiod))
+ /*
+ * The sharing of this GPIO pin is managed internally by
+ * GPIOLIB. We don't need to keep track of its enable count.
+ */
+ goto skip_compare;
+
list_for_each_entry(pin, ®ulator_ena_gpio_list, list) {
if (gpiod_is_equal(pin->gpiod, gpiod)) {
rdev_dbg(rdev, "GPIO is already used\n");
@@ -2628,6 +2635,7 @@ static int regulator_ena_gpio_request(struct regulator_dev *rdev,
return -ENOMEM;
}
+skip_compare:
pin = new_pin;
new_pin = NULL;
--
2.48.1
^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [PATCH v2 01/10] string: provide strends()
2025-10-22 13:10 ` [PATCH v2 01/10] string: provide strends() Bartosz Golaszewski
@ 2025-10-22 13:33 ` Andy Shevchenko
2025-10-22 13:40 ` Bartosz Golaszewski
2025-10-22 15:25 ` Andy Shevchenko
1 sibling, 1 reply; 30+ messages in thread
From: Andy Shevchenko @ 2025-10-22 13:33 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 4:11 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
>
> Implement a function for checking if a string ends with a different
> string and add its kunit test cases.
...
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -562,4 +562,6 @@ static inline bool strstarts(const char *str, const char *prefix)
> return strncmp(str, prefix, strlen(prefix)) == 0;
> }
>
> +bool strends(const char *str, const char *suffix);
Why not static inline as strstarts()?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 01/10] string: provide strends()
2025-10-22 13:33 ` Andy Shevchenko
@ 2025-10-22 13:40 ` Bartosz Golaszewski
2025-10-22 15:23 ` Andy Shevchenko
0 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 13:40 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 3:34 PM Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
>
> On Wed, Oct 22, 2025 at 4:11 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> >
> > Implement a function for checking if a string ends with a different
> > string and add its kunit test cases.
>
> ...
>
> > --- a/include/linux/string.h
> > +++ b/include/linux/string.h
> > @@ -562,4 +562,6 @@ static inline bool strstarts(const char *str, const char *prefix)
> > return strncmp(str, prefix, strlen(prefix)) == 0;
> > }
> >
> > +bool strends(const char *str, const char *suffix);
>
> Why not static inline as strstarts()?
>
Because it's not a oneliner.
Bartosz
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 01/10] string: provide strends()
2025-10-22 13:40 ` Bartosz Golaszewski
@ 2025-10-22 15:23 ` Andy Shevchenko
0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2025-10-22 15:23 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Andy Shevchenko, Kees Cook, Mika Westerberg, Dmitry Torokhov,
Andrew Morton, Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 03:40:00PM +0200, Bartosz Golaszewski wrote:
> On Wed, Oct 22, 2025 at 3:34 PM Andy Shevchenko
> <andy.shevchenko@gmail.com> wrote:
> > On Wed, Oct 22, 2025 at 4:11 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
...
> > > static inline bool strstarts(const char *str, const char *prefix)
> > > return strncmp(str, prefix, strlen(prefix)) == 0;
> > > }
> > >
> > > +bool strends(const char *str, const char *suffix);
> >
> > Why not static inline as strstarts()?
>
> Because it's not a oneliner.
So, and how does it answer the question? What is the obstacle here that it may
not be a static inline few-liner?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 01/10] string: provide strends()
2025-10-22 13:10 ` [PATCH v2 01/10] string: provide strends() Bartosz Golaszewski
2025-10-22 13:33 ` Andy Shevchenko
@ 2025-10-22 15:25 ` Andy Shevchenko
2025-10-22 15:36 ` Bartosz Golaszewski
1 sibling, 1 reply; 30+ messages in thread
From: Andy Shevchenko @ 2025-10-22 15:25 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 03:10:40PM +0200, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> Implement a function for checking if a string ends with a different
> string and add its kunit test cases.
...
> +static void string_test_strends(struct kunit *test)
> +{
> + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "bar"));
> + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "-bar"));
> + KUNIT_EXPECT_TRUE(test, strends("foobar", "foobar"));
> + KUNIT_EXPECT_TRUE(test, strends("foobar", ""));
> + KUNIT_EXPECT_FALSE(test, strends("bar", "foobar"));
> + KUNIT_EXPECT_FALSE(test, strends("", "foo"));
> + KUNIT_EXPECT_FALSE(test, strends("foobar", "ba"));
> + KUNIT_EXPECT_TRUE(test, strends("", ""));
> +}
Have you checked the binary file? If you want this to be properly implemented,
generate the suffix. (Actually making the function static inline makes my point
really visible)
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 01/10] string: provide strends()
2025-10-22 15:25 ` Andy Shevchenko
@ 2025-10-22 15:36 ` Bartosz Golaszewski
2025-10-22 17:12 ` Andy Shevchenko
0 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-22 15:36 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 5:25 PM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Wed, Oct 22, 2025 at 03:10:40PM +0200, Bartosz Golaszewski wrote:
> > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> >
> > Implement a function for checking if a string ends with a different
> > string and add its kunit test cases.
>
> ...
>
> > +static void string_test_strends(struct kunit *test)
> > +{
> > + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "bar"));
> > + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "-bar"));
> > + KUNIT_EXPECT_TRUE(test, strends("foobar", "foobar"));
> > + KUNIT_EXPECT_TRUE(test, strends("foobar", ""));
> > + KUNIT_EXPECT_FALSE(test, strends("bar", "foobar"));
> > + KUNIT_EXPECT_FALSE(test, strends("", "foo"));
> > + KUNIT_EXPECT_FALSE(test, strends("foobar", "ba"));
> > + KUNIT_EXPECT_TRUE(test, strends("", ""));
> > +}
>
> Have you checked the binary file? If you want this to be properly implemented,
> generate the suffix. (Actually making the function static inline makes my point
> really visible)
>
Andy, this is bikeshedding. This is literally the least important
piece of this series. It doesn't matter for the big picture whether
this is inlined or not.
Bartosz
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 01/10] string: provide strends()
2025-10-22 15:36 ` Bartosz Golaszewski
@ 2025-10-22 17:12 ` Andy Shevchenko
2025-10-23 18:43 ` Bartosz Golaszewski
0 siblings, 1 reply; 30+ messages in thread
From: Andy Shevchenko @ 2025-10-22 17:12 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 05:36:33PM +0200, Bartosz Golaszewski wrote:
> On Wed, Oct 22, 2025 at 5:25 PM Andy Shevchenko
> <andriy.shevchenko@intel.com> wrote:
> > On Wed, Oct 22, 2025 at 03:10:40PM +0200, Bartosz Golaszewski wrote:
...
> > > +static void string_test_strends(struct kunit *test)
> > > +{
> > > + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "bar"));
> > > + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "-bar"));
> > > + KUNIT_EXPECT_TRUE(test, strends("foobar", "foobar"));
> > > + KUNIT_EXPECT_TRUE(test, strends("foobar", ""));
> > > + KUNIT_EXPECT_FALSE(test, strends("bar", "foobar"));
> > > + KUNIT_EXPECT_FALSE(test, strends("", "foo"));
> > > + KUNIT_EXPECT_FALSE(test, strends("foobar", "ba"));
> > > + KUNIT_EXPECT_TRUE(test, strends("", ""));
> > > +}
> >
> > Have you checked the binary file? If you want this to be properly implemented,
> > generate the suffix. (Actually making the function static inline makes my point
> > really visible)
>
> Andy, this is bikeshedding. This is literally the least important
> piece of this series. It doesn't matter for the big picture whether
> this is inlined or not.
It's definitely not a bikeshedding. I try to keep a bit consistency here and
I don't see the point of bloating a kernel (binary as well) for the function
that just a couple of lines with simple basic calls.
Also note that with inlined version strlen() for string literals will be
calculated at _compile-time_! This is clear benefit.
Really, library code is not as simple as dropping something to somewhere...
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support
2025-10-22 13:10 ` [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support Bartosz Golaszewski
@ 2025-10-22 17:34 ` Andy Shevchenko
2025-10-23 18:55 ` Bartosz Golaszewski
0 siblings, 1 reply; 30+ messages in thread
From: Andy Shevchenko @ 2025-10-22 17:34 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 03:10:42PM +0200, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> This module scans the device tree (for now only OF nodes are supported
> but care is taken to make other fwnode implementations easy to
> integrate) and determines which GPIO lines are shared by multiple users.
> It stores that information in memory. When the GPIO chip exposing shared
> lines is registered, the shared GPIO descriptors it exposes are marked
> as shared and virtual "proxy" devices that mediate access to the shared
> lines are created. When a consumer of a shared GPIO looks it up, its
> fwnode lookup is redirected to a just-in-time machine lookup that points
> to this proxy device.
>
> This code can be compiled out on platforms which don't use shared GPIOs.
...
> + if (!strends(prop->name, "-gpios") &&
> + !strends(prop->name, "-gpio") &&
> + strcmp(prop->name, "gpios") != 0 &&
> + strcmp(prop->name, "gpio") != 0)
We have gpio_suffixes for a reason (also refer to for_each_gpio_property_name()
implementation, and yes I understand the difference, this is just a reference
for an example of use of the existing list of suffixes).
> + continue;
...
> + if (strends(prop->name, "gpios"))
> + suffix = "-gpios";
> + else if (strends(prop->name, "gpio"))
> + suffix = "-gpio";
> + else
> + suffix = NULL;
> + if (!suffix)
> + continue;
In a similar way.
> + /* We only set con_id if there's actually one. */
> + if (strcmp(prop->name, "gpios") && strcmp(prop->name, "gpio")) {
And again...
> + ref->con_id = kstrdup(prop->name, GFP_KERNEL);
> + if (!ref->con_id)
> + return -ENOMEM;
> +
> + con_id_len = strlen(ref->con_id);
> + suffix_len = strlen(suffix);
> +
> + ref->con_id[con_id_len - suffix_len] = '\0';
> + }
...
> + adev->dev.parent = gdev->dev.parent;
> + /* No need to dev->release() anything. */
And is it okay?
See drivers/base/core.c:2567
WARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",
...
> + pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n",
> + dev_name(&adev->dev), gpio_device_get_label(gdev));
Are you expecting dev_name() to be NULL in some cases? Otherwise why is this
not a dev_dbg() call?
> + return 0;
> +}
...
> + char *key __free(kfree) =
> + kasprintf(GFP_KERNEL,
> + KBUILD_MODNAME ".proxy.%u",
> + ref->adev.id);
This looks awful. Just allow a bit longer line
> + if (!key)
> + return -ENOMEM;
...
> +static void gpio_shared_remove_adev(struct auxiliary_device *adev)
> +{
> + lockdep_assert_held(&gpio_shared_lock);
> +
> + auxiliary_device_uninit(adev);
> + auxiliary_device_delete(adev);
_destroy() ? Esp. taking into account the (wrong?) ordering.
> +}
...
> + set_bit(GPIOD_FLAG_SHARED, flags);
Do you need this to be atomic?
> + /*
> + * Shared GPIOs are not requested via the normal path. Make
> + * them inaccessible to anyone even before we register the
> + * chip.
> + */
> + set_bit(GPIOD_FLAG_REQUESTED, flags);
Ditto.
...
> +struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
> +{
> + struct auxiliary_device *adev = to_auxiliary_dev(dev);
> + struct gpio_shared_desc *shared_desc;
> + struct gpio_shared_entry *entry;
> + struct gpio_shared_ref *ref;
> + struct gpio_device *gdev;
> + int ret;
> + + scoped_guard(mutex, &gpio_shared_lock) {
Much better to split the below to a helper and make it run under a
scoped_guard() here or call a guard()() there.
> + list_for_each_entry(entry, &gpio_shared_list, list) {
> + list_for_each_entry(ref, &entry->refs, list) {
> + if (adev != &ref->adev)
> + continue;
> +
> + if (entry->shared_desc) {
> + kref_get(&entry->ref);
> + shared_desc = entry->shared_desc;
> + break;
> + }
> +
> + shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
> + if (!shared_desc)
> + return ERR_PTR(-ENOMEM);
> +
> + gdev = gpio_device_find_by_fwnode(entry->fwnode);
> + if (!gdev) {
> + kfree(shared_desc);
> + return ERR_PTR(-EPROBE_DEFER);
> + }
> +
> + shared_desc->desc = &gdev->descs[entry->offset];
> + shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
> + if (shared_desc->can_sleep)
> + mutex_init(&shared_desc->mutex);
> + else
> + spin_lock_init(&shared_desc->spinlock);
> +
> + kref_init(&entry->ref);
> + entry->shared_desc = shared_desc;
> +
> + pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
> + dev_name(dev), desc_to_gpio(shared_desc->desc),
> + gpio_device_get_label(gdev));
> + break;
> + }
> + }
> + }
> +
> + ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + return shared_desc;
> +}
...
> +/*
> + * This is only called if gpio_shared_init() fails so it's in fact __init and
> + * not __exit.
> + */
Fine. Have you checked if there are any section mismatch warnings during kernel
(post)build?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver
2025-10-22 13:10 ` [PATCH v2 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver Bartosz Golaszewski
@ 2025-10-22 18:04 ` Andy Shevchenko
2025-10-24 7:03 ` Bartosz Golaszewski
0 siblings, 1 reply; 30+ messages in thread
From: Andy Shevchenko @ 2025-10-22 18:04 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 03:10:43PM +0200, Bartosz Golaszewski wrote:
>
> Add a virtual GPIO proxy driver which arbitrates access to a single
> shared GPIO by multiple users. It works together with the core shared
> GPIO support from GPIOLIB and functions by acquiring a reference to a
> shared GPIO descriptor exposed by gpiolib-shared and making sure that
> the state of the GPIO stays consistent.
>
> In general: if there's only one user at the moment: allow it to do
> anything as if this was a normal GPIO (in essence: just propagate calls
> to the underlying real hardware driver). If there are more users: don't
> allow to change the direction set by the initial user, allow to change
> configuration options but warn about possible conflicts and finally:
> treat the output-high value as a reference counted, logical "GPIO
> enabled" setting, meaning: the GPIO value is set to high when the first
> user requests it to be high and back to low once the last user stops
> "voting" for high.
I have two Q:s about the design:
1) why can't the value be counted on the struct gpio_desc level?
2) can gpio-aggregator facilities be reused (to some extent)?
...
> +#include <linux/auxiliary_bus.h>
> +#include <linux/cleanup.h>
> +#include <linux/device.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/module.h>
+ types.h
> +#include "gpiolib-shared.h"
...
> +out:
> + if (shared_desc->highcnt)
> + dev_dbg(proxy->dev,
> + "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n",
> + value ? "high" : "low", shared_desc->highcnt);
> + else
> + dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n");
You can unify and maybe save a few bytes here and there by doing something like
this:
const char *tmp; // name is a placeholder
tmp = str_high_low(shared_desc->highcnt);
dev_dbg(proxy->dev,
"Voted for value '%s', effective value is '%s', number of votes for '%s': %u\n",
str_high_low(value), tmp, tmp, shared_desc->highcnt);
...
> + dev_dbg(proxy->dev,
> + "Only one user of this shared GPIO, allowing to set direction to output with value '%s'\n",
> + value ? "high" : "low");
str_high_low() ?
> + ret = gpiod_direction_output(desc, value);
> + if (ret)
> + return ret;
> +
> + if (value) {
> + proxy->voted_high = true;
> + shared_desc->highcnt = 1;
> + } else {
> + proxy->voted_high = false;
> + shared_desc->highcnt = 0;
> + }
> +
> + return 0;
> + }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 01/10] string: provide strends()
2025-10-22 17:12 ` Andy Shevchenko
@ 2025-10-23 18:43 ` Bartosz Golaszewski
0 siblings, 0 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-23 18:43 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 7:12 PM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Wed, Oct 22, 2025 at 05:36:33PM +0200, Bartosz Golaszewski wrote:
> > On Wed, Oct 22, 2025 at 5:25 PM Andy Shevchenko
> > <andriy.shevchenko@intel.com> wrote:
> > > On Wed, Oct 22, 2025 at 03:10:40PM +0200, Bartosz Golaszewski wrote:
>
> ...
>
> > > > +static void string_test_strends(struct kunit *test)
> > > > +{
> > > > + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "bar"));
> > > > + KUNIT_EXPECT_TRUE(test, strends("foo-bar", "-bar"));
> > > > + KUNIT_EXPECT_TRUE(test, strends("foobar", "foobar"));
> > > > + KUNIT_EXPECT_TRUE(test, strends("foobar", ""));
> > > > + KUNIT_EXPECT_FALSE(test, strends("bar", "foobar"));
> > > > + KUNIT_EXPECT_FALSE(test, strends("", "foo"));
> > > > + KUNIT_EXPECT_FALSE(test, strends("foobar", "ba"));
> > > > + KUNIT_EXPECT_TRUE(test, strends("", ""));
> > > > +}
> > >
> > > Have you checked the binary file? If you want this to be properly implemented,
> > > generate the suffix. (Actually making the function static inline makes my point
> > > really visible)
> >
> > Andy, this is bikeshedding. This is literally the least important
> > piece of this series. It doesn't matter for the big picture whether
> > this is inlined or not.
>
> It's definitely not a bikeshedding. I try to keep a bit consistency here and
> I don't see the point of bloating a kernel (binary as well) for the function
> that just a couple of lines with simple basic calls.
>
> Also note that with inlined version strlen() for string literals will be
> calculated at _compile-time_! This is clear benefit.
>
> Really, library code is not as simple as dropping something to somewhere...
>
Ok, whatever I'll make it static inline.
Bart
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support
2025-10-22 17:34 ` Andy Shevchenko
@ 2025-10-23 18:55 ` Bartosz Golaszewski
2025-10-24 7:09 ` Andy Shevchenko
0 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-23 18:55 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 7:34 PM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Wed, Oct 22, 2025 at 03:10:42PM +0200, Bartosz Golaszewski wrote:
> > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> >
> > This module scans the device tree (for now only OF nodes are supported
> > but care is taken to make other fwnode implementations easy to
> > integrate) and determines which GPIO lines are shared by multiple users.
> > It stores that information in memory. When the GPIO chip exposing shared
> > lines is registered, the shared GPIO descriptors it exposes are marked
> > as shared and virtual "proxy" devices that mediate access to the shared
> > lines are created. When a consumer of a shared GPIO looks it up, its
> > fwnode lookup is redirected to a just-in-time machine lookup that points
> > to this proxy device.
> >
> > This code can be compiled out on platforms which don't use shared GPIOs.
>
> ...
>
> > + if (!strends(prop->name, "-gpios") &&
> > + !strends(prop->name, "-gpio") &&
>
> > + strcmp(prop->name, "gpios") != 0 &&
> > + strcmp(prop->name, "gpio") != 0)
>
> We have gpio_suffixes for a reason (also refer to for_each_gpio_property_name()
> implementation, and yes I understand the difference, this is just a reference
> for an example of use of the existing list of suffixes).
>
And how would you use them here - when you also need the hyphen -
without multiple dynamic allocations instead of static strings?
>
> > + adev->dev.parent = gdev->dev.parent;
> > + /* No need to dev->release() anything. */
>
> And is it okay?
>
> See drivers/base/core.c:2567
>
> WARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",
>
Huh... you're not wrong but I haven't seen this warning. Do people
just use empty functions in this case?
> ...
>
> > + pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n",
> > + dev_name(&adev->dev), gpio_device_get_label(gdev));
>
> Are you expecting dev_name() to be NULL in some cases? Otherwise why is this
> not a dev_dbg() call?
>
It's the low-level code saying: "I created device X for Y", not "Hey,
here's X, I'm here for Y".
>
> > + return 0;
> > +}
>
> ...
>
> > + char *key __free(kfree) =
> > + kasprintf(GFP_KERNEL,
> > + KBUILD_MODNAME ".proxy.%u",
> > + ref->adev.id);
>
> This looks awful. Just allow a bit longer line
>
Ok
> > + if (!key)
> > + return -ENOMEM;
>
> ...
>
> > +static void gpio_shared_remove_adev(struct auxiliary_device *adev)
> > +{
> > + lockdep_assert_held(&gpio_shared_lock);
> > +
> > + auxiliary_device_uninit(adev);
> > + auxiliary_device_delete(adev);
>
> _destroy() ? Esp. taking into account the (wrong?) ordering.
>
You're right about the order but if you _add() then you should
_delete(). You typically _destroy() if you earlier _create().
> > +}
>
> ...
>
> > + set_bit(GPIOD_FLAG_SHARED, flags);
>
> Do you need this to be atomic?
>
> > + /*
> > + * Shared GPIOs are not requested via the normal path. Make
> > + * them inaccessible to anyone even before we register the
> > + * chip.
> > + */
> > + set_bit(GPIOD_FLAG_REQUESTED, flags);
>
> Ditto.
>
Ok
> ...
>
> > +struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
> > +{
> > + struct auxiliary_device *adev = to_auxiliary_dev(dev);
> > + struct gpio_shared_desc *shared_desc;
> > + struct gpio_shared_entry *entry;
> > + struct gpio_shared_ref *ref;
> > + struct gpio_device *gdev;
> > + int ret;
>
> > + + scoped_guard(mutex, &gpio_shared_lock) {
>
> Much better to split the below to a helper and make it run under a
> scoped_guard() here or call a guard()() there.
>
I'm not following, please rephrase.
> > + list_for_each_entry(entry, &gpio_shared_list, list) {
> > + list_for_each_entry(ref, &entry->refs, list) {
> > + if (adev != &ref->adev)
> > + continue;
> > +
> > + if (entry->shared_desc) {
> > + kref_get(&entry->ref);
> > + shared_desc = entry->shared_desc;
> > + break;
> > + }
> > +
> > + shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
> > + if (!shared_desc)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + gdev = gpio_device_find_by_fwnode(entry->fwnode);
> > + if (!gdev) {
> > + kfree(shared_desc);
> > + return ERR_PTR(-EPROBE_DEFER);
> > + }
> > +
> > + shared_desc->desc = &gdev->descs[entry->offset];
> > + shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
> > + if (shared_desc->can_sleep)
> > + mutex_init(&shared_desc->mutex);
> > + else
> > + spin_lock_init(&shared_desc->spinlock);
> > +
> > + kref_init(&entry->ref);
> > + entry->shared_desc = shared_desc;
> > +
> > + pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
> > + dev_name(dev), desc_to_gpio(shared_desc->desc),
> > + gpio_device_get_label(gdev));
> > + break;
> > + }
> > + }
> > + }
> > +
> > + ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
> > + if (ret)
> > + return ERR_PTR(ret);
> > +
> > + return shared_desc;
> > +}
>
> ...
>
> > +/*
> > + * This is only called if gpio_shared_init() fails so it's in fact __init and
> > + * not __exit.
> > + */
>
> Fine. Have you checked if there are any section mismatch warnings during kernel
> (post)build?
>
Yes, there are none.
Bartosz
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver
2025-10-22 18:04 ` Andy Shevchenko
@ 2025-10-24 7:03 ` Bartosz Golaszewski
0 siblings, 0 replies; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-24 7:03 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed, Oct 22, 2025 at 8:04 PM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Wed, Oct 22, 2025 at 03:10:43PM +0200, Bartosz Golaszewski wrote:
> >
> > Add a virtual GPIO proxy driver which arbitrates access to a single
> > shared GPIO by multiple users. It works together with the core shared
> > GPIO support from GPIOLIB and functions by acquiring a reference to a
> > shared GPIO descriptor exposed by gpiolib-shared and making sure that
> > the state of the GPIO stays consistent.
> >
> > In general: if there's only one user at the moment: allow it to do
> > anything as if this was a normal GPIO (in essence: just propagate calls
> > to the underlying real hardware driver). If there are more users: don't
> > allow to change the direction set by the initial user, allow to change
> > configuration options but warn about possible conflicts and finally:
> > treat the output-high value as a reference counted, logical "GPIO
> > enabled" setting, meaning: the GPIO value is set to high when the first
> > user requests it to be high and back to low once the last user stops
> > "voting" for high.
>
> I have two Q:s about the design:
> 1) why can't the value be counted on the struct gpio_desc level?
No, I specifically tried to limit the impact on users not needing this
to a minimum.
> 2) can gpio-aggregator facilities be reused (to some extent)?
I don't see how but if you have a precise idea, let me know.
>
> ...
>
> > +#include <linux/auxiliary_bus.h>
> > +#include <linux/cleanup.h>
> > +#include <linux/device.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/gpio/driver.h>
> > +#include <linux/module.h>
>
> + types.h
>
> > +#include "gpiolib-shared.h"
>
> ...
>
> > +out:
> > + if (shared_desc->highcnt)
> > + dev_dbg(proxy->dev,
> > + "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n",
> > + value ? "high" : "low", shared_desc->highcnt);
> > + else
> > + dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n");
>
> You can unify and maybe save a few bytes here and there by doing something like
> this:
>
> const char *tmp; // name is a placeholder
>
> tmp = str_high_low(shared_desc->highcnt);
> dev_dbg(proxy->dev,
> "Voted for value '%s', effective value is '%s', number of votes for '%s': %u\n",
> str_high_low(value), tmp, tmp, shared_desc->highcnt);
>
This doesn't make sense, we don't "vote for low". We only vote for
high. It's not: which option gets the most votes, it's: if there's at
least one vote for high, then we go high.
Bart
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support
2025-10-23 18:55 ` Bartosz Golaszewski
@ 2025-10-24 7:09 ` Andy Shevchenko
0 siblings, 0 replies; 30+ messages in thread
From: Andy Shevchenko @ 2025-10-24 7:09 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Thu, Oct 23, 2025 at 08:55:27PM +0200, Bartosz Golaszewski wrote:
> On Wed, Oct 22, 2025 at 7:34 PM Andy Shevchenko
> <andriy.shevchenko@intel.com> wrote:
> > On Wed, Oct 22, 2025 at 03:10:42PM +0200, Bartosz Golaszewski wrote:
...
> > > + if (!strends(prop->name, "-gpios") &&
> > > + !strends(prop->name, "-gpio") &&
> >
> > > + strcmp(prop->name, "gpios") != 0 &&
> > > + strcmp(prop->name, "gpio") != 0)
> >
> > We have gpio_suffixes for a reason (also refer to for_each_gpio_property_name()
> > implementation, and yes I understand the difference, this is just a reference
> > for an example of use of the existing list of suffixes).
>
> And how would you use them here - when you also need the hyphen -
> without multiple dynamic allocations instead of static strings?
Something like
char suffix[6];
bool found = false;
for_each_gpio_property_name(suffix, "")
found = found || strends();
for_each_gpio_property_name(suffix, NULL)
found = found || (strcmp() == 0);
if (!found)
continue;
Of course with more thinking this may be optimized to avoid snprintf()
(probably with a new helper macro or so).
But see my next reply, I found something more interesting.
...
> > > + /* No need to dev->release() anything. */
> >
> > And is it okay?
> >
> > See drivers/base/core.c:2567
> >
> > WARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",
>
> Huh... you're not wrong but I haven't seen this warning. Do people
> just use empty functions in this case?
I dunno. Maybe something applies a default release in you case? Can you
investigate that?
...
> > > + pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n",
> > > + dev_name(&adev->dev), gpio_device_get_label(gdev));
> >
> > Are you expecting dev_name() to be NULL in some cases? Otherwise why is this
> > not a dev_dbg() call?
>
> It's the low-level code saying: "I created device X for Y", not "Hey,
> here's X, I'm here for Y".
OK.
> > > + return 0;
> > > +}
...
> > > +static void gpio_shared_remove_adev(struct auxiliary_device *adev)
> > > +{
> > > + lockdep_assert_held(&gpio_shared_lock);
> > > +
> > > + auxiliary_device_uninit(adev);
> > > + auxiliary_device_delete(adev);
> >
> > _destroy() ? Esp. taking into account the (wrong?) ordering.
> >
>
> You're right about the order but if you _add() then you should
> _delete(). You typically _destroy() if you earlier _create().
Maybe, but as we noticed above my suggestion would make the code less error
prone to begin with.
> > > +}
...
> > > + struct auxiliary_device *adev = to_auxiliary_dev(dev);
> > > + struct gpio_shared_desc *shared_desc;
> > > + struct gpio_shared_entry *entry;
> > > + struct gpio_shared_ref *ref;
> > > + struct gpio_device *gdev;
> > > + int ret;
> >
> > > + + scoped_guard(mutex, &gpio_shared_lock) {
> >
> > Much better to split the below to a helper and make it run under a
> > scoped_guard() here or call a guard()() there.
>
> I'm not following, please rephrase.
scoped_guard() {
call_my_new_helper_which_is_easier to read();
}
ret = devm_add_...
OR
call_my_new_helper_which_is_easier to read()
{
guard()()
...
}
call_my_new_helper_which_is_easier to read();
ret = devm_add_...
> > > + list_for_each_entry(entry, &gpio_shared_list, list) {
> > > + list_for_each_entry(ref, &entry->refs, list) {
> > > + if (adev != &ref->adev)
> > > + continue;
> > > +
> > > + if (entry->shared_desc) {
> > > + kref_get(&entry->ref);
> > > + shared_desc = entry->shared_desc;
> > > + break;
> > > + }
> > > +
> > > + shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
> > > + if (!shared_desc)
> > > + return ERR_PTR(-ENOMEM);
> > > +
> > > + gdev = gpio_device_find_by_fwnode(entry->fwnode);
> > > + if (!gdev) {
> > > + kfree(shared_desc);
> > > + return ERR_PTR(-EPROBE_DEFER);
> > > + }
> > > +
> > > + shared_desc->desc = &gdev->descs[entry->offset];
> > > + shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
> > > + if (shared_desc->can_sleep)
> > > + mutex_init(&shared_desc->mutex);
> > > + else
> > > + spin_lock_init(&shared_desc->spinlock);
> > > +
> > > + kref_init(&entry->ref);
> > > + entry->shared_desc = shared_desc;
> > > +
> > > + pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
> > > + dev_name(dev), desc_to_gpio(shared_desc->desc),
> > > + gpio_device_get_label(gdev));
> > > + break;
> > > + }
> > > + }
> > > + }
> > > +
> > > + ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
> > > + if (ret)
> > > + return ERR_PTR(ret);
> > > +
> > > + return shared_desc;
> > > +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 00/10] gpio: improve support for shared GPIOs
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
` (9 preceding siblings ...)
2025-10-22 13:10 ` [PATCH v2 10/10] regulator: make the subsystem aware of shared GPIOs Bartosz Golaszewski
@ 2025-10-24 7:17 ` Péter Ujfalusi
2025-10-24 7:20 ` Bartosz Golaszewski
10 siblings, 1 reply; 30+ messages in thread
From: Péter Ujfalusi @ 2025-10-24 7:17 UTC (permalink / raw)
To: Bartosz Golaszewski, Kees Cook, Mika Westerberg, Dmitry Torokhov,
Andrew Morton, Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai
Cc: linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
On 22/10/2025 16:10, Bartosz Golaszewski wrote:
> Problem statement: GPIOs are implemented as a strictly exclusive
> resource in the kernel but there are lots of platforms on which single
> pin is shared by multiple devices which don't communicate so need some
> way of properly sharing access to a GPIO. What we have now is the
> GPIOD_FLAGS_BIT_NONEXCLUSIVE flag which was introduced as a hack and
> doesn't do any locking or arbitration of access - it literally just hand
> the same GPIO descriptor to all interested users.
I had few stabs on this in the past, all got somehow derailed, one
example was:
https://lkml.org/lkml/2019/10/30/311
> The proposed solution is composed of three major parts: the high-level,
> shared GPIO proxy driver that arbitrates access to the shared pin and
> exposes a regular GPIO chip interface to consumers, a low-level shared
> GPIOLIB module that scans firmware nodes and creates auxiliary devices
> that attach to the proxy driver and finally a set of core GPIOLIB
> changes that plug the former into the GPIO lookup path.
>
> The changes are implemented in a way that allows to seamlessly compile
> out any code related to sharing GPIOs for systems that don't need it.
>
> The practical use-case for this are the powerdown GPIOs shared by
> speakers on Qualcomm db845c platform, however I have also extensively
> tested it using gpio-virtuser on arm64 qemu with various DT
> configurations.
>
> I'm Cc'ing some people that may help with reviewing/be interested in
> this: OF maintainers (because the main target are OF systems initially),
> Mark Brown because most users of GPIOD_FLAGS_BIT_NONEXCLUSIVE live
> in audio or regulator drivers and one of the goals of this series is
> dropping the hand-crafted GPIO enable counting via struct
> regulator_enable_gpio in regulator core), Andy and Mika because I'd like
> to also cover ACPI (even though I don't know about any ACPI platform that
> would need this at the moment, I think it makes sense to make the
> solution complete), Dmitry (same thing but for software nodes), Mani
> (because you have a somewhat related use-case for the PERST# signal and
> I'd like to hear your input on whether this is something you can use or
> maybe it needs a separate, implicit gpio-perst driver similar to what
> Krzysztof did for reset-gpios) and Greg (because I mentioned this to you
> last week in person and I also use the auxiliary bus for the proxy
> devices).
>
> Merging strategy: patches 1-6 should go through the GPIO tree and then
> ARM-SoC, ASoC and regulator trees can pull these changes from an
> immutable branch and apply the remaining patches.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> ---
> Changes in v2:
> - Fix a memory leak in error path in gpiolib-shared
> - Drop the gpio-wcd934x fix that already went upstream
> - Free resources used during scanning by GPIOs that turned out to be
> unique
> - Rework the OF property scanning
> - Add patches making the regulator subsystem aware of shared GPIOs
> managed by GPIOLIB
> - Link to v1: https://lore.kernel.org/r/20250924-gpio-shared-v1-0-775e7efeb1a3@linaro.org
>
> ---
> Bartosz Golaszewski (10):
> string: provide strends()
> gpiolib: define GPIOD_FLAG_SHARED
> gpiolib: implement low-level, shared GPIO support
> gpio: shared-proxy: implement the shared GPIO proxy driver
> gpiolib: support shared GPIOs in core subsystem code
> gpio: provide gpiod_is_shared()
> arm64: select HAVE_SHARED_GPIOS for ARCH_QCOM
> ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
> ASoC: wsa883x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
> regulator: make the subsystem aware of shared GPIOs
>
> arch/arm64/Kconfig.platforms | 1 +
> drivers/gpio/Kconfig | 17 ++
> drivers/gpio/Makefile | 2 +
> drivers/gpio/gpio-shared-proxy.c | 329 ++++++++++++++++++++++++
> drivers/gpio/gpiolib-shared.c | 530 +++++++++++++++++++++++++++++++++++++++
> drivers/gpio/gpiolib-shared.h | 71 ++++++
> drivers/gpio/gpiolib.c | 70 +++++-
> drivers/gpio/gpiolib.h | 2 +
> drivers/regulator/core.c | 8 +
> include/linux/gpio/consumer.h | 9 +
> include/linux/string.h | 2 +
> lib/string.c | 19 ++
> lib/tests/string_kunit.c | 13 +
> sound/soc/codecs/wsa881x.c | 3 +-
> sound/soc/codecs/wsa883x.c | 7 +-
> 15 files changed, 1067 insertions(+), 16 deletions(-)
> ---
> base-commit: 304d18863e6e62a8f2d0350ce0a59596e2e42768
> change-id: 20250908-gpio-shared-67ec352884b6
>
> Best regards,
--
Péter
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 00/10] gpio: improve support for shared GPIOs
2025-10-24 7:17 ` [PATCH v2 00/10] gpio: improve support for " Péter Ujfalusi
@ 2025-10-24 7:20 ` Bartosz Golaszewski
2025-10-24 7:32 ` Péter Ujfalusi
0 siblings, 1 reply; 30+ messages in thread
From: Bartosz Golaszewski @ 2025-10-24 7:20 UTC (permalink / raw)
To: Péter Ujfalusi
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Fri, Oct 24, 2025 at 9:17 AM Péter Ujfalusi
<peter.ujfalusi@linux.intel.com> wrote:
>
>
>
> On 22/10/2025 16:10, Bartosz Golaszewski wrote:
> > Problem statement: GPIOs are implemented as a strictly exclusive
> > resource in the kernel but there are lots of platforms on which single
> > pin is shared by multiple devices which don't communicate so need some
> > way of properly sharing access to a GPIO. What we have now is the
> > GPIOD_FLAGS_BIT_NONEXCLUSIVE flag which was introduced as a hack and
> > doesn't do any locking or arbitration of access - it literally just hand
> > the same GPIO descriptor to all interested users.
>
> I had few stabs on this in the past, all got somehow derailed, one
> example was:
> https://lkml.org/lkml/2019/10/30/311
>
The main issue I see with this approach is adding an actual device
node for the shared GPIO which is now not accepted in DT bindings. We
only create nodes for actual HW components. All the information is
already in the device-tree, we just need to scan it which is what I'm
trying to do here.
Bartosz
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 00/10] gpio: improve support for shared GPIOs
2025-10-24 7:20 ` Bartosz Golaszewski
@ 2025-10-24 7:32 ` Péter Ujfalusi
0 siblings, 0 replies; 30+ messages in thread
From: Péter Ujfalusi @ 2025-10-24 7:32 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On 24/10/2025 10:20, Bartosz Golaszewski wrote:
> On Fri, Oct 24, 2025 at 9:17 AM Péter Ujfalusi
> <peter.ujfalusi@linux.intel.com> wrote:
>>
>>
>>
>> On 22/10/2025 16:10, Bartosz Golaszewski wrote:
>>> Problem statement: GPIOs are implemented as a strictly exclusive
>>> resource in the kernel but there are lots of platforms on which single
>>> pin is shared by multiple devices which don't communicate so need some
>>> way of properly sharing access to a GPIO. What we have now is the
>>> GPIOD_FLAGS_BIT_NONEXCLUSIVE flag which was introduced as a hack and
>>> doesn't do any locking or arbitration of access - it literally just hand
>>> the same GPIO descriptor to all interested users.
>>
>> I had few stabs on this in the past, all got somehow derailed, one
>> example was:
>> https://lkml.org/lkml/2019/10/30/311
>>
>
> The main issue I see with this approach is adding an actual device
> node for the shared GPIO which is now not accepted in DT bindings. We
> only create nodes for actual HW components.
Right, that policy came later, true.
All the information is
> already in the device-tree, we just need to scan it which is what I'm
> trying to do here.
I had a prototype later without the sofware-node which worked for the
use case I had, but over the years I dropped it, it was a bit of hassle
to roll it for nothing.
One can argue that the shared-gpio node is describing the solder blob
where the GPIO line is split and routed to two different component.
With the approach one can handle cases where the level is inverted by a
passive component for one of the device for example - unfortunately I
have seen such a board marvel as well.
The device's binding will tell _how_ it expects the GPIO's active level,
which might or might not match with the real level of the GPIO line and
if one of the device's branch have an inverter, that is going to be
interesting to work out in conjunction with the other devices non
inverted 'direct' line to the same GPIO.
Never the less, it is great that someone is trying to get this supported!
>
> Bartosz
--
Péter
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
2025-10-22 13:10 ` [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup Bartosz Golaszewski
@ 2025-10-24 15:46 ` Mark Brown
2025-10-24 23:32 ` Alexey Klimov
1 sibling, 0 replies; 30+ messages in thread
From: Mark Brown @ 2025-10-24 15:46 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
[-- Attachment #1: Type: text/plain, Size: 298 bytes --]
On Wed, Oct 22, 2025 at 03:10:47PM +0200, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> This driver is only used on Qualcomm platforms which now select
> HAVE_SHARED_GPIOS so this flag can be dropped.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 09/10] ASoC: wsa883x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
2025-10-22 13:10 ` [PATCH v2 09/10] ASoC: wsa883x: " Bartosz Golaszewski
@ 2025-10-24 15:46 ` Mark Brown
0 siblings, 0 replies; 30+ messages in thread
From: Mark Brown @ 2025-10-24 15:46 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
[-- Attachment #1: Type: text/plain, Size: 298 bytes --]
On Wed, Oct 22, 2025 at 03:10:48PM +0200, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> This driver is only used on Qualcomm platforms which now select
> HAVE_SHARED_GPIOS so this flag can be dropped.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 10/10] regulator: make the subsystem aware of shared GPIOs
2025-10-22 13:10 ` [PATCH v2 10/10] regulator: make the subsystem aware of shared GPIOs Bartosz Golaszewski
@ 2025-10-24 15:57 ` Mark Brown
0 siblings, 0 replies; 30+ messages in thread
From: Mark Brown @ 2025-10-24 15:57 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
linux-hardening, linux-kernel, linux-gpio, linux-arm-kernel,
linux-sound, linux-arm-msm, Bartosz Golaszewski
[-- Attachment #1: Type: text/plain, Size: 498 bytes --]
On Wed, Oct 22, 2025 at 03:10:49PM +0200, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> GPIOLIB is now aware of shared GPIOs and - for platforms where access to
> such pins is managed internally - we don't need to keep track of the
> enable count.
>
> Once all users in the kernel switch to using the new mechanism, we'll be
> able to drop the internal counting of users from the regulator code.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup
2025-10-22 13:10 ` [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup Bartosz Golaszewski
2025-10-24 15:46 ` Mark Brown
@ 2025-10-24 23:32 ` Alexey Klimov
1 sibling, 0 replies; 30+ messages in thread
From: Alexey Klimov @ 2025-10-24 23:32 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Kees Cook, Mika Westerberg, Dmitry Torokhov, Andrew Morton,
Linus Walleij, Manivannan Sadhasivam, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
Greg Kroah-Hartman, Andy Shevchenko, Catalin Marinas, Will Deacon,
Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, linux-hardening, linux-kernel, linux-gpio,
linux-arm-kernel, linux-sound, linux-arm-msm, Bartosz Golaszewski
On Wed Oct 22, 2025 at 2:10 PM BST, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> This driver is only used on Qualcomm platforms which now select
> HAVE_SHARED_GPIOS so this flag can be dropped.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
I tested the whole series on db845 RB3 board, looks ok.
Reviewed-and-tested-by: Alexey Klimov <alexey.klimov@linaro.org> # RB3
Thanks,
Alexey
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2025-10-24 23:32 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-22 13:10 [PATCH v2 00/10] gpio: improve support for shared GPIOs Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 01/10] string: provide strends() Bartosz Golaszewski
2025-10-22 13:33 ` Andy Shevchenko
2025-10-22 13:40 ` Bartosz Golaszewski
2025-10-22 15:23 ` Andy Shevchenko
2025-10-22 15:25 ` Andy Shevchenko
2025-10-22 15:36 ` Bartosz Golaszewski
2025-10-22 17:12 ` Andy Shevchenko
2025-10-23 18:43 ` Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 02/10] gpiolib: define GPIOD_FLAG_SHARED Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 03/10] gpiolib: implement low-level, shared GPIO support Bartosz Golaszewski
2025-10-22 17:34 ` Andy Shevchenko
2025-10-23 18:55 ` Bartosz Golaszewski
2025-10-24 7:09 ` Andy Shevchenko
2025-10-22 13:10 ` [PATCH v2 04/10] gpio: shared-proxy: implement the shared GPIO proxy driver Bartosz Golaszewski
2025-10-22 18:04 ` Andy Shevchenko
2025-10-24 7:03 ` Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 05/10] gpiolib: support shared GPIOs in core subsystem code Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 06/10] gpio: provide gpiod_is_shared() Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 07/10] arm64: select HAVE_SHARED_GPIOS for ARCH_QCOM Bartosz Golaszewski
2025-10-22 13:10 ` [PATCH v2 08/10] ASoC: wsa881x: drop GPIOD_FLAGS_BIT_NONEXCLUSIVE flag from GPIO lookup Bartosz Golaszewski
2025-10-24 15:46 ` Mark Brown
2025-10-24 23:32 ` Alexey Klimov
2025-10-22 13:10 ` [PATCH v2 09/10] ASoC: wsa883x: " Bartosz Golaszewski
2025-10-24 15:46 ` Mark Brown
2025-10-22 13:10 ` [PATCH v2 10/10] regulator: make the subsystem aware of shared GPIOs Bartosz Golaszewski
2025-10-24 15:57 ` Mark Brown
2025-10-24 7:17 ` [PATCH v2 00/10] gpio: improve support for " Péter Ujfalusi
2025-10-24 7:20 ` Bartosz Golaszewski
2025-10-24 7:32 ` Péter Ujfalusi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).