* [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
@ 2025-06-10 14:38 Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 01/15] Documentation: gpio: undocument removed behavior Bartosz Golaszewski
` (18 more replies)
0 siblings, 19 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
Following our discussion[1], here's a proposal for extending the sysfs
interface with attributes not referring to GPIO lines by their global
numbers in a backward compatible way.
Long story short: there is now a new class device for each GPIO chip.
It's called chipX where X is the ID of the device as per the driver
model and it lives next to the old gpiochipABC where ABC is the GPIO
base. Each new chip class device has a pair of export/unexport
attributes which work similarly to the global ones under /sys/class/gpio
but take hardware offsets within the chip as input, instead of the
global numbers. Finally, each exported line appears at the same time as
the global /sys/class/gpio/gpioABC as well as per-chip
/sys/class/gpio/chipX/gpioY sysfs group.
First, there are some documentation updates, followed by a set of
updates to the sysfs code that's useful even without the new
functionality. Then the actual implementation of a parallel GPIO chip
entry not containing the base GPIO number in the name and the
corresponding sysfs attribute group for each exported line that lives
under the new chip class device. Finally: also allow to compile out the
legacy parts leaving only the new elements of the sysfs ABI.
This series passes the compatibility tests I wrote while working on the
user-space compatibility layer for sysfs[2].
[1] https://lore.kernel.org/all/CAMRc=McUCeZcU6co1aN54rTudo+JfPjjForu4iKQ5npwXk6GXA@mail.gmail.com/
[2] https://github.com/brgl/gpio-sysfs-compat-tests
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Bartosz Golaszewski (15):
Documentation: gpio: undocument removed behavior
Documentation: gpio: document the active_low field in the sysfs ABI
gpio: sysfs: call mutex_destroy() in gpiod_unexport()
gpio: sysfs: refactor the coding style
gpio: sysfs: remove unneeded headers
gpio: sysfs: remove the mockdev pointer from struct gpio_device
gpio: sysfs: add a parallel class device for each GPIO chip using device IDs
gpio: sysfs: only get the dirent reference for the value attr once
gpio: sysfs: pass gpiod_data directly to internal GPIO sysfs functions
gpio: sysfs: don't use driver data in sysfs callbacks for line attributes
gpio: sysfs: rename the data variable in gpiod_(un)export()
gpio: sysfs: don't look up exported lines as class devices
gpio: sysfs: export the GPIO directory locally in the gpiochip<id> directory
gpio: sysfs: allow disabling the legacy parts of the GPIO sysfs interface
gpio: TODO: remove the task for the sysfs rework
Documentation/ABI/obsolete/sysfs-gpio | 12 +-
drivers/gpio/Kconfig | 8 +
drivers/gpio/TODO | 13 -
drivers/gpio/gpiolib-sysfs.c | 650 ++++++++++++++++++++++++----------
drivers/gpio/gpiolib.h | 3 -
5 files changed, 474 insertions(+), 212 deletions(-)
---
base-commit: 19272b37aa4f83ca52bdf9c16d5d81bdd1354494
change-id: 20250402-gpio-sysfs-chip-export-84ac424b61c5
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 01/15] Documentation: gpio: undocument removed behavior
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-11 8:15 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 02/15] Documentation: gpio: document the active_low field in the sysfs ABI Bartosz Golaszewski
` (17 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Since commit 700cdf7ed00f ("gpio: sysfs: make the sysfs export behavior
consistent"), named GPIO lines are no longer exported in sysfs as links
named after the them. Drop the misleading bit from the ABI docs.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Documentation/ABI/obsolete/sysfs-gpio | 1 -
1 file changed, 1 deletion(-)
diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio
index da1345d854b4ad40ddd99af090597574fbc07565..8203bc2128db7eb9ea6724884e680ed4b669853c 100644
--- a/Documentation/ABI/obsolete/sysfs-gpio
+++ b/Documentation/ABI/obsolete/sysfs-gpio
@@ -19,7 +19,6 @@ Description:
/export ... asks the kernel to export a GPIO to userspace
/unexport ... to return a GPIO to the kernel
/gpioN ... for each exported GPIO #N OR
- /<LINE-NAME> ... for a properly named GPIO line
/value ... always readable, writes fail for input GPIOs
/direction ... r/w as: in, out (default low); write: high, low
/edge ... r/w as: none, falling, rising, both
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 02/15] Documentation: gpio: document the active_low field in the sysfs ABI
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 01/15] Documentation: gpio: undocument removed behavior Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-11 8:15 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 03/15] gpio: sysfs: call mutex_destroy() in gpiod_unexport() Bartosz Golaszewski
` (16 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Exported GPIO lines also have the active_low attribute which is not
documented. Add a short mention for it.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Documentation/ABI/obsolete/sysfs-gpio | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio
index 8203bc2128db7eb9ea6724884e680ed4b669853c..480316fee6d80fb7a0ed61706559838591ec0932 100644
--- a/Documentation/ABI/obsolete/sysfs-gpio
+++ b/Documentation/ABI/obsolete/sysfs-gpio
@@ -22,6 +22,7 @@ Description:
/value ... always readable, writes fail for input GPIOs
/direction ... r/w as: in, out (default low); write: high, low
/edge ... r/w as: none, falling, rising, both
+ /active_low ... r/w as: 0, 1
/gpiochipN ... for each gpiochip; #N is its first GPIO
/base ... (r/o) same as N
/label ... (r/o) descriptive, not necessarily unique
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 03/15] gpio: sysfs: call mutex_destroy() in gpiod_unexport()
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 01/15] Documentation: gpio: undocument removed behavior Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 02/15] Documentation: gpio: document the active_low field in the sysfs ABI Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-11 8:16 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 04/15] gpio: sysfs: refactor the coding style Bartosz Golaszewski
` (15 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
While not critical, it's useful to have the corresponding call to
mutex_destroy() whenever we use mutex_init(). Add the call right before
kfreeing the GPIO data.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 4a3aa09dad9d54dc77f28d596723fd5546cb3ae8..cd3381a4bc93a94b9a975248ae3e4bd8c2a3eb4b 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -713,6 +713,7 @@ void gpiod_unexport(struct gpio_desc *desc)
}
put_device(dev);
+ mutex_destroy(&data->mutex);
kfree(data);
}
EXPORT_SYMBOL_GPL(gpiod_unexport);
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 04/15] gpio: sysfs: refactor the coding style
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (2 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 03/15] gpio: sysfs: call mutex_destroy() in gpiod_unexport() Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-11 8:16 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 05/15] gpio: sysfs: remove unneeded headers Bartosz Golaszewski
` (14 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Update the code to be more consistent with the rest of the codebase.
Mostly correctly align line-breaks, remove unneeded tabs, stray newlines
& spaces and tweak the comment style.
No functional change.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 67 ++++++++++++++++++++++----------------------
1 file changed, 34 insertions(+), 33 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index cd3381a4bc93a94b9a975248ae3e4bd8c2a3eb4b..88f97018fc7995c1e1195c0da4b6a8377af62e0b 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -73,7 +73,7 @@ static DEFINE_MUTEX(sysfs_lock);
*/
static ssize_t direction_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
@@ -88,11 +88,12 @@ static ssize_t direction_show(struct device *dev,
}
static ssize_t direction_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+ struct device_attribute *attr, const char *buf,
+ size_t size)
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
- ssize_t status;
+ ssize_t status;
guard(mutex)(&data->mutex);
@@ -109,12 +110,12 @@ static ssize_t direction_store(struct device *dev,
}
static DEVICE_ATTR_RW(direction);
-static ssize_t value_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t value_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
- ssize_t status;
+ ssize_t status;
scoped_guard(mutex, &data->mutex)
status = gpiod_get_value_cansleep(desc);
@@ -125,8 +126,8 @@ static ssize_t value_show(struct device *dev,
return sysfs_emit(buf, "%zd\n", status);
}
-static ssize_t value_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t value_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
@@ -179,22 +180,22 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
irq_flags = IRQF_SHARED;
if (flags & GPIO_IRQF_TRIGGER_FALLING) {
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
- IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
set_bit(FLAG_EDGE_FALLING, &desc->flags);
}
if (flags & GPIO_IRQF_TRIGGER_RISING) {
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
- IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
set_bit(FLAG_EDGE_RISING, &desc->flags);
}
/*
* FIXME: This should be done in the irq_request_resources callback
- * when the irq is requested, but a few drivers currently fail
- * to do so.
+ * when the irq is requested, but a few drivers currently fail to do
+ * so.
*
- * Remove this redundant call (along with the corresponding
- * unlock) when those drivers have been fixed.
+ * Remove this redundant call (along with the corresponding unlock)
+ * when those drivers have been fixed.
*/
ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
if (ret < 0)
@@ -240,15 +241,15 @@ static void gpio_sysfs_free_irq(struct device *dev)
sysfs_put(data->value_kn);
}
-static const char * const trigger_names[] = {
+static const char *const trigger_names[] = {
[GPIO_IRQF_TRIGGER_NONE] = "none",
[GPIO_IRQF_TRIGGER_FALLING] = "falling",
[GPIO_IRQF_TRIGGER_RISING] = "rising",
[GPIO_IRQF_TRIGGER_BOTH] = "both",
};
-static ssize_t edge_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t edge_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct gpiod_data *data = dev_get_drvdata(dev);
int flags;
@@ -262,8 +263,8 @@ static ssize_t edge_show(struct device *dev,
return sysfs_emit(buf, "%s\n", trigger_names[flags]);
}
-static ssize_t edge_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t edge_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
{
struct gpiod_data *data = dev_get_drvdata(dev);
ssize_t status = size;
@@ -302,7 +303,6 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value)
struct gpio_desc *desc = data->desc;
int status = 0;
-
if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
return 0;
@@ -310,7 +310,7 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value)
/* reconfigure poll(2) support if enabled on one edge only */
if (flags == GPIO_IRQF_TRIGGER_FALLING ||
- flags == GPIO_IRQF_TRIGGER_RISING) {
+ flags == GPIO_IRQF_TRIGGER_RISING) {
gpio_sysfs_free_irq(dev);
status = gpio_sysfs_request_irq(dev, flags);
}
@@ -321,7 +321,7 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value)
}
static ssize_t active_low_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
@@ -334,7 +334,8 @@ static ssize_t active_low_show(struct device *dev,
}
static ssize_t active_low_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+ struct device_attribute *attr,
+ const char *buf, size_t size)
{
struct gpiod_data *data = dev_get_drvdata(dev);
ssize_t status;
@@ -397,8 +398,8 @@ static const struct attribute_group *gpio_groups[] = {
* /ngpio ... matching gpio_chip.ngpio
*/
-static ssize_t base_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t base_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
const struct gpio_device *gdev = dev_get_drvdata(dev);
@@ -406,8 +407,8 @@ static ssize_t base_show(struct device *dev,
}
static DEVICE_ATTR_RO(base);
-static ssize_t label_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t label_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
const struct gpio_device *gdev = dev_get_drvdata(dev);
@@ -415,8 +416,8 @@ static ssize_t label_show(struct device *dev,
}
static DEVICE_ATTR_RO(label);
-static ssize_t ngpio_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
const struct gpio_device *gdev = dev_get_drvdata(dev);
@@ -439,8 +440,8 @@ ATTRIBUTE_GROUPS(gpiochip);
* integer N ... number of GPIO to unexport
*/
static ssize_t export_store(const struct class *class,
- const struct class_attribute *attr,
- const char *buf, size_t len)
+ const struct class_attribute *attr,
+ const char *buf, size_t len)
{
struct gpio_desc *desc;
int status, offset;
@@ -498,8 +499,8 @@ static ssize_t export_store(const struct class *class,
static CLASS_ATTR_WO(export);
static ssize_t unexport_store(const struct class *class,
- const struct class_attribute *attr,
- const char *buf, size_t len)
+ const struct class_attribute *attr,
+ const char *buf, size_t len)
{
struct gpio_desc *desc;
int status;
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 05/15] gpio: sysfs: remove unneeded headers
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (3 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 04/15] gpio: sysfs: refactor the coding style Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-11 8:17 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 06/15] gpio: sysfs: remove the mockdev pointer from struct gpio_device Bartosz Golaszewski
` (13 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
No symbols from the linux/idr.h or linux/spinlock.h headers are used in
this file so remove them. We also don't technically need linux/list.h
currently but one of the follow-up commits will start using it so let's
leave it.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 88f97018fc7995c1e1195c0da4b6a8377af62e0b..f23b4efea5905a9eab51ed9e50b5159135a8e26c 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -3,7 +3,6 @@
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/device.h>
-#include <linux/idr.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kdev_t.h>
@@ -12,7 +11,6 @@
#include <linux/mutex.h>
#include <linux/printk.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/srcu.h>
#include <linux/sysfs.h>
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 06/15] gpio: sysfs: remove the mockdev pointer from struct gpio_device
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (4 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 05/15] gpio: sysfs: remove unneeded headers Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-11 8:19 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 07/15] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs Bartosz Golaszewski
` (12 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
The usage of the mockdev pointer in struct gpio_device is limited to the
GPIO sysfs code. There's no reason to keep it in this top-level
structure. Create a separate structure containing the reference to the
GPIO device and the dummy class device that will be passed to
device_create_with_groups(). The !gdev->mockdev checks can be removed as
long as we make sure that all operations on the GPIO class are protected
with the sysfs lock.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 81 +++++++++++++++++++++++++++++---------------
drivers/gpio/gpiolib.h | 3 --
2 files changed, 53 insertions(+), 31 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index f23b4efea5905a9eab51ed9e50b5159135a8e26c..956411fc467a26a9827c616d8dc067c70f9244bf 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -43,6 +43,11 @@ struct gpiod_data {
bool direction_can_change;
};
+struct gpiodev_data {
+ struct gpio_device *gdev;
+ struct device *cdev_base; /* Class device by GPIO base */
+};
+
/*
* Lock to serialise gpiod export and unexport, and prevent re-export of
* gpiod whose chip is being unregistered.
@@ -399,27 +404,27 @@ static const struct attribute_group *gpio_groups[] = {
static ssize_t base_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- const struct gpio_device *gdev = dev_get_drvdata(dev);
+ const struct gpiodev_data *data = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%u\n", gdev->base);
+ return sysfs_emit(buf, "%u\n", data->gdev->base);
}
static DEVICE_ATTR_RO(base);
static ssize_t label_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- const struct gpio_device *gdev = dev_get_drvdata(dev);
+ const struct gpiodev_data *data = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%s\n", gdev->label);
+ return sysfs_emit(buf, "%s\n", data->gdev->label);
}
static DEVICE_ATTR_RO(label);
static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- const struct gpio_device *gdev = dev_get_drvdata(dev);
+ const struct gpiodev_data *data = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%u\n", gdev->ngpio);
+ return sysfs_emit(buf, "%u\n", data->gdev->ngpio);
}
static DEVICE_ATTR_RO(ngpio);
@@ -545,6 +550,26 @@ static const struct class gpio_class = {
.class_groups = gpio_class_groups,
};
+static int match_gdev(struct device *dev, const void *desc)
+{
+ struct gpiodev_data *data = dev_get_drvdata(dev);
+ const struct gpio_device *gdev = desc;
+
+ return data && data->gdev == gdev;
+}
+
+static struct gpiodev_data *
+gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock)
+{
+ struct device *cdev __free(put_device) = class_find_device(&gpio_class,
+ NULL, gdev,
+ match_gdev);
+ if (!cdev)
+ return NULL;
+
+ return dev_get_drvdata(cdev);
+};
+
/**
* gpiod_export - export a GPIO through sysfs
* @desc: GPIO to make available, already requested
@@ -590,12 +615,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
guard(mutex)(&sysfs_lock);
- /* check if chip is being removed */
- if (!gdev->mockdev) {
- status = -ENODEV;
- goto err_clear_bit;
- }
-
if (!test_bit(FLAG_REQUESTED, &desc->flags)) {
gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__);
status = -EPERM;
@@ -719,9 +738,9 @@ EXPORT_SYMBOL_GPL(gpiod_unexport);
int gpiochip_sysfs_register(struct gpio_device *gdev)
{
+ struct gpiodev_data *data;
struct gpio_chip *chip;
struct device *parent;
- struct device *dev;
/*
* Many systems add gpio chips for SOC support very early,
@@ -747,32 +766,41 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
else
parent = &gdev->dev;
- /* use chip->base for the ID; it's already known to be unique */
- dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), gdev,
- gpiochip_groups, GPIOCHIP_NAME "%d",
- chip->base);
- if (IS_ERR(dev))
- return PTR_ERR(dev);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->gdev = gdev;
guard(mutex)(&sysfs_lock);
- gdev->mockdev = dev;
+
+ /* use chip->base for the ID; it's already known to be unique */
+ data->cdev_base = device_create_with_groups(&gpio_class, parent,
+ MKDEV(0, 0), data,
+ gpiochip_groups,
+ GPIOCHIP_NAME "%d",
+ chip->base);
+ if (IS_ERR(data->cdev_base)) {
+ kfree(data);
+ return PTR_ERR(data->cdev_base);
+ }
return 0;
}
void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{
+ struct gpiodev_data *data;
struct gpio_desc *desc;
struct gpio_chip *chip;
scoped_guard(mutex, &sysfs_lock) {
- if (!gdev->mockdev)
+ data = gdev_get_data(gdev);
+ if (!data)
return;
- device_unregister(gdev->mockdev);
-
- /* prevent further gpiod exports */
- gdev->mockdev = NULL;
+ device_unregister(data->cdev_base);
+ kfree(data);
}
guard(srcu)(&gdev->srcu);
@@ -798,9 +826,6 @@ static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
struct gpio_device *gdev = gc->gpiodev;
int ret;
- if (gdev->mockdev)
- return 0;
-
ret = gpiochip_sysfs_register(gdev);
if (ret)
chip_err(gc, "failed to register the sysfs entry: %d\n", ret);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 58f64056de77b05e8cbcb2395a55da793b1a52fa..9b74738a9ca5b1a4826c8d56d871f8a7cf6ea1e7 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -27,8 +27,6 @@
* @dev: the GPIO device struct
* @chrdev: character device for the GPIO device
* @id: numerical ID number for the GPIO chip
- * @mockdev: class device used by the deprecated sysfs interface (may be
- * NULL)
* @owner: helps prevent removal of modules exporting active GPIOs
* @chip: pointer to the corresponding gpiochip, holding static
* data for this device
@@ -65,7 +63,6 @@ struct gpio_device {
struct device dev;
struct cdev chrdev;
int id;
- struct device *mockdev;
struct module *owner;
struct gpio_chip __rcu *chip;
struct gpio_desc *descs;
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 07/15] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (5 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 06/15] gpio: sysfs: remove the mockdev pointer from struct gpio_device Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-11 8:27 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 08/15] gpio: sysfs: only get the dirent reference for the value attr once Bartosz Golaszewski
` (11 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
In order to enable moving away from the global GPIO numberspace-based
exporting of lines over sysfs: add a parallel, per-chip entry under
/sys/class/gpio/ for every registered GPIO chip, denoted by device ID
in the file name and not its base GPIO number.
Compared to the existing chip group: it does not contain the "base"
attribute as the goal of this change is to not refer to GPIOs by their
global number from user-space anymore. It also contains its own,
per-chip export/unexport attribute pair which allow to export lines by
their hardware offset within the chip.
Caveat #1: the new device cannot be a link to (or be linked to by) the
existing "gpiochip<BASE>" entry as we cannot create links in
/sys/class/xyz/.
Caveat #2: the new entry cannot be named "gpiochipX" as it could
conflict with devices whose base is statically defined to a low number.
Let's go with "chipX" instead.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Documentation/ABI/obsolete/sysfs-gpio | 5 +
drivers/gpio/gpiolib-sysfs.c | 191 +++++++++++++++++++++++++---------
2 files changed, 148 insertions(+), 48 deletions(-)
diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio
index 480316fee6d80fb7a0ed61706559838591ec0932..f856e286051d6bfa4990660dcb25b3fdb27cd0f2 100644
--- a/Documentation/ABI/obsolete/sysfs-gpio
+++ b/Documentation/ABI/obsolete/sysfs-gpio
@@ -27,6 +27,11 @@ Description:
/base ... (r/o) same as N
/label ... (r/o) descriptive, not necessarily unique
/ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
+ /chipX ... for each gpiochip; #X is the gpio device ID
+ /export ... asks the kernel to export a GPIO at HW offset X to userspace
+ /unexport ... to return a GPIO at HW offset X to the kernel
+ /label ... (r/o) descriptive, not necessarily unique
+ /ngpio ... (r/o) number of GPIOs exposed by the chip
This ABI is obsoleted by Documentation/ABI/testing/gpio-cdev and will be
removed after 2020.
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 956411fc467a26a9827c616d8dc067c70f9244bf..a3403b963d6488aad24e47e9e28e0439676de704 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -46,6 +46,7 @@ struct gpiod_data {
struct gpiodev_data {
struct gpio_device *gdev;
struct device *cdev_base; /* Class device by GPIO base */
+ struct device *cdev_id; /* Class device by GPIO device ID */
};
/*
@@ -399,6 +400,14 @@ static const struct attribute_group *gpio_groups[] = {
* /base ... matching gpio_chip.base (N)
* /label ... matching gpio_chip.label
* /ngpio ... matching gpio_chip.ngpio
+ *
+ * AND
+ *
+ * /sys/class/gpio/chipX/
+ * /export ... export GPIO at given offset
+ * /unexport ... unexport GPIO at given offset
+ * /label ... matching gpio_chip.label
+ * /ngpio ... matching gpio_chip.ngpio
*/
static ssize_t base_show(struct device *dev, struct device_attribute *attr,
@@ -428,6 +437,111 @@ static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(ngpio);
+static int export_gpio_desc(struct gpio_desc *desc)
+{
+ int offset, ret;
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
+ offset = gpio_chip_hwgpio(desc);
+ if (!gpiochip_line_is_valid(guard.gc, offset)) {
+ pr_debug_ratelimited("%s: GPIO %d masked\n", __func__,
+ gpio_chip_hwgpio(desc));
+ return -EINVAL;
+ }
+
+ /*
+ * No extra locking here; FLAG_SYSFS just signifies that the
+ * request and export were done by on behalf of userspace, so
+ * they may be undone on its behalf too.
+ */
+
+ ret = gpiod_request_user(desc, "sysfs");
+ if (ret)
+ return ret;
+
+ ret = gpiod_set_transitory(desc, false);
+ if (ret) {
+ gpiod_free(desc);
+ return ret;
+ }
+
+ ret = gpiod_export(desc, true);
+ if (ret < 0) {
+ gpiod_free(desc);
+ } else {
+ set_bit(FLAG_SYSFS, &desc->flags);
+ gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
+ }
+
+ return ret;
+}
+
+static int unexport_gpio_desc(struct gpio_desc *desc)
+{
+ /*
+ * No extra locking here; FLAG_SYSFS just signifies that the
+ * request and export were done by on behalf of userspace, so
+ * they may be undone on its behalf too.
+ */
+ if (!test_and_clear_bit(FLAG_SYSFS, &desc->flags))
+ return -EINVAL;
+
+ gpiod_unexport(desc);
+ gpiod_free(desc);
+
+ return 0;
+}
+
+static ssize_t do_chip_export_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, ssize_t size,
+ int (*handler)(struct gpio_desc *desc))
+{
+ struct gpiodev_data *data = dev_get_drvdata(dev);
+ struct gpio_device *gdev = data->gdev;
+ struct gpio_desc *desc;
+ unsigned int gpio;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &gpio);
+ if (ret)
+ return ret;
+
+ desc = gpio_device_get_desc(gdev, gpio);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ ret = handler(desc);
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static ssize_t chip_export_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ return do_chip_export_store(dev, attr, buf, size, export_gpio_desc);
+}
+
+static struct device_attribute dev_attr_export = __ATTR(export, 0200, NULL,
+ chip_export_store);
+
+static ssize_t chip_unexport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ return do_chip_export_store(dev, attr, buf, size, unexport_gpio_desc);
+}
+
+static struct device_attribute dev_attr_unexport = __ATTR(unexport, 0200,
+ NULL,
+ chip_unexport_store);
+
static struct attribute *gpiochip_attrs[] = {
&dev_attr_base.attr,
&dev_attr_label.attr,
@@ -436,6 +550,15 @@ static struct attribute *gpiochip_attrs[] = {
};
ATTRIBUTE_GROUPS(gpiochip);
+static struct attribute *gpiochip_ext_attrs[] = {
+ &dev_attr_label.attr,
+ &dev_attr_ngpio.attr,
+ &dev_attr_export.attr,
+ &dev_attr_unexport.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(gpiochip_ext);
+
/*
* /sys/class/gpio/export ... write-only
* integer N ... number of GPIO to export (full access)
@@ -447,7 +570,7 @@ static ssize_t export_store(const struct class *class,
const char *buf, size_t len)
{
struct gpio_desc *desc;
- int status, offset;
+ int status;
long gpio;
status = kstrtol(buf, 0, &gpio);
@@ -461,40 +584,7 @@ static ssize_t export_store(const struct class *class,
return -EINVAL;
}
- CLASS(gpio_chip_guard, guard)(desc);
- if (!guard.gc)
- return -ENODEV;
-
- offset = gpio_chip_hwgpio(desc);
- if (!gpiochip_line_is_valid(guard.gc, offset)) {
- pr_debug_ratelimited("%s: GPIO %ld masked\n", __func__, gpio);
- return -EINVAL;
- }
-
- /* No extra locking here; FLAG_SYSFS just signifies that the
- * request and export were done by on behalf of userspace, so
- * they may be undone on its behalf too.
- */
-
- status = gpiod_request_user(desc, "sysfs");
- if (status)
- goto done;
-
- status = gpiod_set_transitory(desc, false);
- if (status) {
- gpiod_free(desc);
- goto done;
- }
-
- status = gpiod_export(desc, true);
- if (status < 0) {
- gpiod_free(desc);
- } else {
- set_bit(FLAG_SYSFS, &desc->flags);
- gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
- }
-
-done:
+ status = export_gpio_desc(desc);
if (status)
pr_debug("%s: status %d\n", __func__, status);
return status ? : len;
@@ -511,7 +601,7 @@ static ssize_t unexport_store(const struct class *class,
status = kstrtol(buf, 0, &gpio);
if (status < 0)
- goto done;
+ return status;
desc = gpio_to_desc(gpio);
/* reject bogus commands (gpiod_unexport() ignores them) */
@@ -520,18 +610,7 @@ static ssize_t unexport_store(const struct class *class,
return -EINVAL;
}
- status = -EINVAL;
-
- /* No extra locking here; FLAG_SYSFS just signifies that the
- * request and export were done by on behalf of userspace, so
- * they may be undone on its behalf too.
- */
- if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) {
- gpiod_unexport(desc);
- gpiod_free(desc);
- status = 0;
- }
-done:
+ status = unexport_gpio_desc(desc);
if (status)
pr_debug("%s: status %d\n", __func__, status);
return status ? : len;
@@ -561,6 +640,11 @@ static int match_gdev(struct device *dev, const void *desc)
static struct gpiodev_data *
gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock)
{
+ /*
+ * Find the first device in GPIO class that matches. Whether that's
+ * the one indexed by GPIO base or device ID doesn't matter, it has
+ * the same address set as driver data.
+ */
struct device *cdev __free(put_device) = class_find_device(&gpio_class,
NULL, gdev,
match_gdev);
@@ -785,6 +869,16 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
return PTR_ERR(data->cdev_base);
}
+ data->cdev_id = device_create_with_groups(&gpio_class, parent,
+ MKDEV(0, 0), data,
+ gpiochip_ext_groups,
+ "chip%d", gdev->id);
+ if (IS_ERR(data->cdev_id)) {
+ device_unregister(data->cdev_base);
+ kfree(data);
+ return PTR_ERR(data->cdev_id);
+ }
+
return 0;
}
@@ -800,6 +894,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
return;
device_unregister(data->cdev_base);
+ device_unregister(data->cdev_id);
kfree(data);
}
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 08/15] gpio: sysfs: only get the dirent reference for the value attr once
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (6 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 07/15] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 09/15] gpio: sysfs: pass gpiod_data directly to internal GPIO sysfs functions Bartosz Golaszewski
` (10 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
There's no reason to retrieve the reference to the sysfs dirent every
time we request an interrupt, we can as well only do it once when
exporting the GPIO.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index a3403b963d6488aad24e47e9e28e0439676de704..642635c9f84bbd869727755e72781fae31a9cca3 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -36,7 +36,7 @@ struct gpiod_data {
struct gpio_desc *desc;
struct mutex mutex;
- struct kernfs_node *value_kn;
+ struct kernfs_node *value_class_node;
int irq;
unsigned char irq_flags;
@@ -156,7 +156,7 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
struct gpiod_data *data = priv;
- sysfs_notify_dirent(data->value_kn);
+ sysfs_notify_dirent(data->value_class_node);
return IRQ_HANDLED;
}
@@ -177,10 +177,6 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
if (data->irq < 0)
return -EIO;
- data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value");
- if (!data->value_kn)
- return -ENODEV;
-
irq_flags = IRQF_SHARED;
if (flags & GPIO_IRQF_TRIGGER_FALLING) {
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
@@ -203,7 +199,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
*/
ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
if (ret < 0)
- goto err_put_kn;
+ goto err_clr_bits;
ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags,
"gpiolib", data);
@@ -216,10 +212,9 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
err_unlock:
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
-err_put_kn:
+err_clr_bits:
clear_bit(FLAG_EDGE_RISING, &desc->flags);
clear_bit(FLAG_EDGE_FALLING, &desc->flags);
- sysfs_put(data->value_kn);
return ret;
}
@@ -242,7 +237,6 @@ static void gpio_sysfs_free_irq(struct device *dev)
gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
clear_bit(FLAG_EDGE_RISING, &desc->flags);
clear_bit(FLAG_EDGE_FALLING, &desc->flags);
- sysfs_put(data->value_kn);
}
static const char *const trigger_names[] = {
@@ -726,8 +720,16 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
goto err_free_data;
}
+ data->value_class_node = sysfs_get_dirent(dev->kobj.sd, "value");
+ if (!data->value_class_node) {
+ status = -ENODEV;
+ goto err_unregister_device;
+ }
+
return 0;
+err_unregister_device:
+ device_unregister(dev);
err_free_data:
kfree(data);
err_clear_bit:
@@ -804,6 +806,7 @@ void gpiod_unexport(struct gpio_desc *desc)
data = dev_get_drvdata(dev);
clear_bit(FLAG_EXPORT, &desc->flags);
+ sysfs_put(data->value_class_node);
device_unregister(dev);
/*
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 09/15] gpio: sysfs: pass gpiod_data directly to internal GPIO sysfs functions
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (7 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 08/15] gpio: sysfs: only get the dirent reference for the value attr once Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 10/15] gpio: sysfs: don't use driver data in sysfs callbacks for line attributes Bartosz Golaszewski
` (9 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
To make the transition to not using dev_get_drvdata() across line
callbacks for sysfs attributes, pass gpiod_data directly to
gpio_sysfs_request_irq(), gpio_sysfs_free_irq() and
gpio_sysfs_set_active_low() instead of having it wrapped in struct
device.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 642635c9f84bbd869727755e72781fae31a9cca3..811328c8e72d819bcd4b4215cda450c73aaa65cd 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -162,9 +162,8 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
}
/* Caller holds gpiod-data mutex. */
-static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
+static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
unsigned long irq_flags;
int ret;
@@ -223,9 +222,8 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
* Caller holds gpiod-data mutex (unless called after class-device
* deregistration).
*/
-static void gpio_sysfs_free_irq(struct device *dev)
+static void gpio_sysfs_free_irq(struct gpiod_data *data)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
CLASS(gpio_chip_guard, guard)(desc);
@@ -278,12 +276,12 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr,
return size;
if (data->irq_flags)
- gpio_sysfs_free_irq(dev);
+ gpio_sysfs_free_irq(data);
if (!flags)
return size;
- status = gpio_sysfs_request_irq(dev, flags);
+ status = gpio_sysfs_request_irq(data, flags);
if (status)
return status;
@@ -294,9 +292,8 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(edge);
/* Caller holds gpiod-data mutex. */
-static int gpio_sysfs_set_active_low(struct device *dev, int value)
+static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
unsigned int flags = data->irq_flags;
struct gpio_desc *desc = data->desc;
int status = 0;
@@ -309,8 +306,8 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value)
/* reconfigure poll(2) support if enabled on one edge only */
if (flags == GPIO_IRQF_TRIGGER_FALLING ||
flags == GPIO_IRQF_TRIGGER_RISING) {
- gpio_sysfs_free_irq(dev);
- status = gpio_sysfs_request_irq(dev, flags);
+ gpio_sysfs_free_irq(data);
+ status = gpio_sysfs_request_irq(data, flags);
}
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
@@ -345,7 +342,7 @@ static ssize_t active_low_store(struct device *dev,
guard(mutex)(&data->mutex);
- return gpio_sysfs_set_active_low(dev, value) ?: size;
+ return gpio_sysfs_set_active_low(data, value) ?: size;
}
static DEVICE_ATTR_RW(active_low);
@@ -814,7 +811,7 @@ void gpiod_unexport(struct gpio_desc *desc)
* edge_store.
*/
if (data->irq_flags)
- gpio_sysfs_free_irq(dev);
+ gpio_sysfs_free_irq(data);
}
put_device(dev);
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 10/15] gpio: sysfs: don't use driver data in sysfs callbacks for line attributes
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (8 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 09/15] gpio: sysfs: pass gpiod_data directly to internal GPIO sysfs functions Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 11/15] gpio: sysfs: rename the data variable in gpiod_(un)export() Bartosz Golaszewski
` (8 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Currently each exported GPIO is represented in sysfs as a separate class
device. This allows us to simply use dev_get_drvdata() to retrieve the
pointer passed to device_create_with_groups() from sysfs ops callbacks.
However, we're preparing to add a parallel set of per-line sysfs
attributes that will live inside the associated gpiochip group. They are
not registered as class devices and so have the parent device passed as
argument to their callbacks (the GPIO chip class device).
Put the attribute structs inside the GPIO descriptor data and
dereference the relevant ones using container_of() in the callbacks.
This way, we'll be able to reuse the same code for both the legacy and
new GPIO attributes.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 122 +++++++++++++++++++++++++++++--------------
1 file changed, 82 insertions(+), 40 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 811328c8e72d819bcd4b4215cda450c73aaa65cd..03e97697b7f26a8aa9f527f25f048f25588f3ca2 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -32,6 +32,15 @@ struct kernfs_node;
#define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \
GPIO_IRQF_TRIGGER_RISING)
+enum {
+ GPIO_SYSFS_LINE_ATTR_DIRECTION = 0,
+ GPIO_SYSFS_LINE_ATTR_VALUE,
+ GPIO_SYSFS_LINE_ATTR_EDGE,
+ GPIO_SYSFS_LINE_ATTR_ACTIVE_LOW,
+ GPIO_SYSFS_LINE_ATTR_SENTINEL,
+ GPIO_SYSFS_LINE_ATTR_SIZE,
+};
+
struct gpiod_data {
struct gpio_desc *desc;
@@ -41,6 +50,14 @@ struct gpiod_data {
unsigned char irq_flags;
bool direction_can_change;
+
+ struct device_attribute dir_attr;
+ struct device_attribute val_attr;
+ struct device_attribute edge_attr;
+ struct device_attribute active_low_attr;
+ struct attribute *attrs[GPIO_SYSFS_LINE_ATTR_SIZE];
+ struct attribute_group attr_group;
+ const struct attribute_group *attr_groups[2];
};
struct gpiodev_data {
@@ -79,7 +96,8 @@ static DEFINE_MUTEX(sysfs_lock);
static ssize_t direction_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ dir_attr);
struct gpio_desc *desc = data->desc;
int value;
@@ -95,7 +113,8 @@ static ssize_t direction_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t size)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ dir_attr);
struct gpio_desc *desc = data->desc;
ssize_t status;
@@ -112,12 +131,12 @@ static ssize_t direction_store(struct device *dev,
return status ? : size;
}
-static DEVICE_ATTR_RW(direction);
static ssize_t value_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ val_attr);
struct gpio_desc *desc = data->desc;
ssize_t status;
@@ -133,7 +152,8 @@ static ssize_t value_show(struct device *dev, struct device_attribute *attr,
static ssize_t value_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ val_attr);
struct gpio_desc *desc = data->desc;
ssize_t status;
long value;
@@ -150,7 +170,6 @@ static ssize_t value_store(struct device *dev, struct device_attribute *attr,
return size;
}
-static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store);
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
@@ -247,7 +266,8 @@ static const char *const trigger_names[] = {
static ssize_t edge_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ edge_attr);
int flags;
scoped_guard(mutex, &data->mutex)
@@ -262,7 +282,8 @@ static ssize_t edge_show(struct device *dev, struct device_attribute *attr,
static ssize_t edge_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ edge_attr);
ssize_t status = size;
int flags;
@@ -289,7 +310,6 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr,
return size;
}
-static DEVICE_ATTR_RW(edge);
/* Caller holds gpiod-data mutex. */
static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value)
@@ -318,7 +338,8 @@ static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value)
static ssize_t active_low_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ active_low_attr);
struct gpio_desc *desc = data->desc;
int value;
@@ -332,7 +353,8 @@ static ssize_t active_low_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
- struct gpiod_data *data = dev_get_drvdata(dev);
+ struct gpiod_data *data = container_of(attr, struct gpiod_data,
+ active_low_attr);
ssize_t status;
long value;
@@ -344,48 +366,34 @@ static ssize_t active_low_store(struct device *dev,
return gpio_sysfs_set_active_low(data, value) ?: size;
}
-static DEVICE_ATTR_RW(active_low);
static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
int n)
{
- struct device *dev = kobj_to_dev(kobj);
- struct gpiod_data *data = dev_get_drvdata(dev);
- struct gpio_desc *desc = data->desc;
+ struct device_attribute *dev_attr = container_of(attr,
+ struct device_attribute, attr);
umode_t mode = attr->mode;
- bool show_direction = data->direction_can_change;
+ struct gpiod_data *data;
- if (attr == &dev_attr_direction.attr) {
- if (!show_direction)
+ if (strcmp(attr->name, "direction") == 0) {
+ data = container_of(dev_attr, struct gpiod_data, dir_attr);
+
+ if (!data->direction_can_change)
mode = 0;
- } else if (attr == &dev_attr_edge.attr) {
- if (gpiod_to_irq(desc) < 0)
+ } else if (strcmp(attr->name, "edge") == 0) {
+ data = container_of(dev_attr, struct gpiod_data, edge_attr);
+
+ if (gpiod_to_irq(data->desc) < 0)
mode = 0;
- if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags))
+
+ if (!data->direction_can_change &&
+ test_bit(FLAG_IS_OUT, &data->desc->flags))
mode = 0;
}
return mode;
}
-static struct attribute *gpio_attrs[] = {
- &dev_attr_direction.attr,
- &dev_attr_edge.attr,
- &dev_attr_value.attr,
- &dev_attr_active_low.attr,
- NULL,
-};
-
-static const struct attribute_group gpio_group = {
- .attrs = gpio_attrs,
- .is_visible = gpio_is_visible,
-};
-
-static const struct attribute_group *gpio_groups[] = {
- &gpio_group,
- NULL
-};
-
/*
* /sys/class/gpio/gpiochipN/
* /base ... matching gpio_chip.base (N)
@@ -645,6 +653,21 @@ gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock)
return dev_get_drvdata(cdev);
};
+static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name,
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*store)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count))
+{
+ sysfs_attr_init(&dev_attr->attr);
+ dev_attr->attr.name = name;
+ dev_attr->attr.mode = 0644;
+ dev_attr->show = show;
+ dev_attr->store = store;
+}
+
/**
* gpiod_export - export a GPIO through sysfs
* @desc: GPIO to make available, already requested
@@ -664,6 +687,7 @@ gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock)
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
struct gpio_device *gdev;
+ struct attribute **attrs;
struct gpiod_data *data;
struct device *dev;
int status;
@@ -709,8 +733,26 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
else
data->direction_can_change = false;
+ gpiod_attr_init(&data->dir_attr, "direction",
+ direction_show, direction_store);
+ gpiod_attr_init(&data->val_attr, "value", value_show, value_store);
+ gpiod_attr_init(&data->edge_attr, "edge", edge_show, edge_store);
+ gpiod_attr_init(&data->active_low_attr, "active_low",
+ active_low_show, active_low_store);
+
+ attrs = data->attrs;
+ data->attr_group.is_visible = gpio_is_visible;
+ attrs[GPIO_SYSFS_LINE_ATTR_DIRECTION] = &data->dir_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_VALUE] = &data->val_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_EDGE] = &data->edge_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_ACTIVE_LOW] =
+ &data->active_low_attr.attr;
+
+ data->attr_group.attrs = data->attrs;
+ data->attr_groups[0] = &data->attr_group;
+
dev = device_create_with_groups(&gpio_class, &gdev->dev,
- MKDEV(0, 0), data, gpio_groups,
+ MKDEV(0, 0), data, data->attr_groups,
"gpio%u", desc_to_gpio(desc));
if (IS_ERR(dev)) {
status = PTR_ERR(dev);
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 11/15] gpio: sysfs: rename the data variable in gpiod_(un)export()
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (9 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 10/15] gpio: sysfs: don't use driver data in sysfs callbacks for line attributes Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 12/15] gpio: sysfs: don't look up exported lines as class devices Bartosz Golaszewski
` (7 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
In preparation for future commits which will make use of descriptor AND
GPIO-device data in the same functions rename the former from data to
desc_data separately which will make future changes smaller and easier
to read.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 63 ++++++++++++++++++++++----------------------
1 file changed, 32 insertions(+), 31 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 03e97697b7f26a8aa9f527f25f048f25588f3ca2..398cefb4be9e389a820dd53f79c82fa70783b5e0 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -686,9 +686,9 @@ static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name,
*/
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
+ struct gpiod_data *desc_data;
struct gpio_device *gdev;
struct attribute **attrs;
- struct gpiod_data *data;
struct device *dev;
int status;
@@ -720,47 +720,48 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
goto err_clear_bit;
}
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
+ desc_data = kzalloc(sizeof(*desc_data), GFP_KERNEL);
+ if (!desc_data) {
status = -ENOMEM;
goto err_clear_bit;
}
- data->desc = desc;
- mutex_init(&data->mutex);
+ desc_data->desc = desc;
+ mutex_init(&desc_data->mutex);
if (guard.gc->direction_input && guard.gc->direction_output)
- data->direction_can_change = direction_may_change;
+ desc_data->direction_can_change = direction_may_change;
else
- data->direction_can_change = false;
+ desc_data->direction_can_change = false;
- gpiod_attr_init(&data->dir_attr, "direction",
+ gpiod_attr_init(&desc_data->dir_attr, "direction",
direction_show, direction_store);
- gpiod_attr_init(&data->val_attr, "value", value_show, value_store);
- gpiod_attr_init(&data->edge_attr, "edge", edge_show, edge_store);
- gpiod_attr_init(&data->active_low_attr, "active_low",
- active_low_show, active_low_store);
+ gpiod_attr_init(&desc_data->val_attr, "value", value_show, value_store);
+ gpiod_attr_init(&desc_data->edge_attr, "edge", edge_show, edge_store);
+ gpiod_attr_init(&desc_data->active_low_attr, "active_low",
+ active_low_show, active_low_store);
- attrs = data->attrs;
- data->attr_group.is_visible = gpio_is_visible;
- attrs[GPIO_SYSFS_LINE_ATTR_DIRECTION] = &data->dir_attr.attr;
- attrs[GPIO_SYSFS_LINE_ATTR_VALUE] = &data->val_attr.attr;
- attrs[GPIO_SYSFS_LINE_ATTR_EDGE] = &data->edge_attr.attr;
+ attrs = desc_data->attrs;
+ desc_data->attr_group.is_visible = gpio_is_visible;
+ attrs[GPIO_SYSFS_LINE_ATTR_DIRECTION] = &desc_data->dir_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_VALUE] = &desc_data->val_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_EDGE] = &desc_data->edge_attr.attr;
attrs[GPIO_SYSFS_LINE_ATTR_ACTIVE_LOW] =
- &data->active_low_attr.attr;
+ &desc_data->active_low_attr.attr;
- data->attr_group.attrs = data->attrs;
- data->attr_groups[0] = &data->attr_group;
+ desc_data->attr_group.attrs = desc_data->attrs;
+ desc_data->attr_groups[0] = &desc_data->attr_group;
dev = device_create_with_groups(&gpio_class, &gdev->dev,
- MKDEV(0, 0), data, data->attr_groups,
+ MKDEV(0, 0), desc_data,
+ desc_data->attr_groups,
"gpio%u", desc_to_gpio(desc));
if (IS_ERR(dev)) {
status = PTR_ERR(dev);
goto err_free_data;
}
- data->value_class_node = sysfs_get_dirent(dev->kobj.sd, "value");
- if (!data->value_class_node) {
+ desc_data->value_class_node = sysfs_get_dirent(dev->kobj.sd, "value");
+ if (!desc_data->value_class_node) {
status = -ENODEV;
goto err_unregister_device;
}
@@ -770,7 +771,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
err_unregister_device:
device_unregister(dev);
err_free_data:
- kfree(data);
+ kfree(desc_data);
err_clear_bit:
clear_bit(FLAG_EXPORT, &desc->flags);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
@@ -827,7 +828,7 @@ EXPORT_SYMBOL_GPL(gpiod_export_link);
*/
void gpiod_unexport(struct gpio_desc *desc)
{
- struct gpiod_data *data;
+ struct gpiod_data *desc_data;
struct device *dev;
if (!desc) {
@@ -843,22 +844,22 @@ void gpiod_unexport(struct gpio_desc *desc)
if (!dev)
return;
- data = dev_get_drvdata(dev);
+ desc_data = dev_get_drvdata(dev);
clear_bit(FLAG_EXPORT, &desc->flags);
- sysfs_put(data->value_class_node);
+ sysfs_put(desc_data->value_class_node);
device_unregister(dev);
/*
* Release irq after deregistration to prevent race with
* edge_store.
*/
- if (data->irq_flags)
- gpio_sysfs_free_irq(data);
+ if (desc_data->irq_flags)
+ gpio_sysfs_free_irq(desc_data);
}
put_device(dev);
- mutex_destroy(&data->mutex);
- kfree(data);
+ mutex_destroy(&desc_data->mutex);
+ kfree(desc_data);
}
EXPORT_SYMBOL_GPL(gpiod_unexport);
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 12/15] gpio: sysfs: don't look up exported lines as class devices
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (10 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 11/15] gpio: sysfs: rename the data variable in gpiod_(un)export() Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 13/15] gpio: sysfs: export the GPIO directory locally in the gpiochip<id> directory Bartosz Golaszewski
` (6 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
In preparation for adding a parallel, per-chip attribute group for
exported GPIO lines, stop using class device APIs to refer to it in the
code. When unregistering the chip, don't call class_find_device() but
instead store exported lines in a linked list inside the GPIO chip data
object and look it up there.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/gpiolib-sysfs.c | 60 ++++++++++++++++++++++++++++++++------------
1 file changed, 44 insertions(+), 16 deletions(-)
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 398cefb4be9e389a820dd53f79c82fa70783b5e0..aa998d9e96cce5d64784645eea73f987471c7285 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -43,6 +43,7 @@ enum {
struct gpiod_data {
struct gpio_desc *desc;
+ struct device *dev;
struct mutex mutex;
struct kernfs_node *value_class_node;
@@ -58,12 +59,15 @@ struct gpiod_data {
struct attribute *attrs[GPIO_SYSFS_LINE_ATTR_SIZE];
struct attribute_group attr_group;
const struct attribute_group *attr_groups[2];
+
+ struct list_head list;
};
struct gpiodev_data {
struct gpio_device *gdev;
struct device *cdev_base; /* Class device by GPIO base */
struct device *cdev_id; /* Class device by GPIO device ID */
+ struct list_head exported_lines;
};
/*
@@ -686,10 +690,10 @@ static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name,
*/
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
+ struct gpiodev_data *gdev_data;
struct gpiod_data *desc_data;
struct gpio_device *gdev;
struct attribute **attrs;
- struct device *dev;
int status;
/* can't export until sysfs is available ... */
@@ -751,25 +755,40 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
desc_data->attr_group.attrs = desc_data->attrs;
desc_data->attr_groups[0] = &desc_data->attr_group;
- dev = device_create_with_groups(&gpio_class, &gdev->dev,
- MKDEV(0, 0), desc_data,
- desc_data->attr_groups,
- "gpio%u", desc_to_gpio(desc));
- if (IS_ERR(dev)) {
- status = PTR_ERR(dev);
+ /*
+ * Note: we need to continue passing desc_data here as there's still
+ * at least one known user of gpiod_export_link() in the tree. This
+ * function still uses class_find_device() internally.
+ */
+ desc_data->dev = device_create_with_groups(&gpio_class, &gdev->dev,
+ MKDEV(0, 0), desc_data,
+ desc_data->attr_groups,
+ "gpio%u",
+ desc_to_gpio(desc));
+ if (IS_ERR(desc_data->dev)) {
+ status = PTR_ERR(desc_data->dev);
goto err_free_data;
}
- desc_data->value_class_node = sysfs_get_dirent(dev->kobj.sd, "value");
+ desc_data->value_class_node = sysfs_get_dirent(desc_data->dev->kobj.sd,
+ "value");
if (!desc_data->value_class_node) {
status = -ENODEV;
goto err_unregister_device;
}
+ gdev_data = gdev_get_data(gdev);
+ if (!gdev_data) {
+ status = -ENODEV;
+ goto err_unregister_device;
+ }
+
+ list_add(&desc_data->list, &gdev_data->exported_lines);
+
return 0;
err_unregister_device:
- device_unregister(dev);
+ device_unregister(desc_data->dev);
err_free_data:
kfree(desc_data);
err_clear_bit:
@@ -828,8 +847,9 @@ EXPORT_SYMBOL_GPL(gpiod_export_link);
*/
void gpiod_unexport(struct gpio_desc *desc)
{
- struct gpiod_data *desc_data;
- struct device *dev;
+ struct gpiod_data *desc_data = NULL;
+ struct gpiodev_data *gdev_data;
+ struct gpio_device *gdev;
if (!desc) {
pr_warn("%s: invalid GPIO\n", __func__);
@@ -840,14 +860,22 @@ void gpiod_unexport(struct gpio_desc *desc)
if (!test_bit(FLAG_EXPORT, &desc->flags))
return;
- dev = class_find_device(&gpio_class, NULL, desc, match_export);
- if (!dev)
+ gdev = gpiod_to_gpio_device(desc);
+ gdev_data = gdev_get_data(gdev);
+ if (!gdev_data)
return;
- desc_data = dev_get_drvdata(dev);
+ list_for_each_entry(desc_data, &gdev_data->exported_lines, list)
+ if (desc == desc_data->desc)
+ break;
+
+ if (!desc_data)
+ return;
+
+ list_del(&desc_data->list);
clear_bit(FLAG_EXPORT, &desc->flags);
sysfs_put(desc_data->value_class_node);
- device_unregister(dev);
+ device_unregister(desc_data->dev);
/*
* Release irq after deregistration to prevent race with
@@ -857,7 +885,6 @@ void gpiod_unexport(struct gpio_desc *desc)
gpio_sysfs_free_irq(desc_data);
}
- put_device(dev);
mutex_destroy(&desc_data->mutex);
kfree(desc_data);
}
@@ -898,6 +925,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
return -ENOMEM;
data->gdev = gdev;
+ INIT_LIST_HEAD(&data->exported_lines);
guard(mutex)(&sysfs_lock);
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 13/15] gpio: sysfs: export the GPIO directory locally in the gpiochip<id> directory
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (11 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 12/15] gpio: sysfs: don't look up exported lines as class devices Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 14/15] gpio: sysfs: allow disabling the legacy parts of the GPIO sysfs interface Bartosz Golaszewski
` (5 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
As a way to allow the user-space to stop referring to GPIOs by their
global numbers, introduce a parallel group of line attributes for
exported GPIO that live inside the GPIO chip class device and are
referred to by their HW offset within their parent chip.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
Documentation/ABI/obsolete/sysfs-gpio | 5 +++++
drivers/gpio/gpiolib-sysfs.c | 39 ++++++++++++++++++++++++++++++++++-
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio
index f856e286051d6bfa4990660dcb25b3fdb27cd0f2..3212cf64f43b67b23a1ac17df025638a52a5bb31 100644
--- a/Documentation/ABI/obsolete/sysfs-gpio
+++ b/Documentation/ABI/obsolete/sysfs-gpio
@@ -27,6 +27,11 @@ Description:
/base ... (r/o) same as N
/label ... (r/o) descriptive, not necessarily unique
/ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
+ /gpio<OFFSET>
+ /value
+ /direction
+ /edge
+ /active-low
/chipX ... for each gpiochip; #X is the gpio device ID
/export ... asks the kernel to export a GPIO at HW offset X to userspace
/unexport ... to return a GPIO at HW offset X to the kernel
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index aa998d9e96cce5d64784645eea73f987471c7285..d8c17d71d5458011d15bc4239bd4c041244da3fd 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -47,11 +47,13 @@ struct gpiod_data {
struct mutex mutex;
struct kernfs_node *value_class_node;
+ struct kernfs_node *value_chip_node;
int irq;
unsigned char irq_flags;
bool direction_can_change;
+ struct kobject *parent;
struct device_attribute dir_attr;
struct device_attribute val_attr;
struct device_attribute edge_attr;
@@ -180,6 +182,7 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
struct gpiod_data *data = priv;
sysfs_notify_dirent(data->value_class_node);
+ kernfs_notify(data->value_chip_node);
return IRQ_HANDLED;
}
@@ -780,13 +783,46 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
gdev_data = gdev_get_data(gdev);
if (!gdev_data) {
status = -ENODEV;
- goto err_unregister_device;
+ goto err_put_dirent;
}
list_add(&desc_data->list, &gdev_data->exported_lines);
+ desc_data->attr_group.name = kasprintf(GFP_KERNEL, "gpio%u",
+ gpio_chip_hwgpio(desc));
+ if (!desc_data->attr_group.name) {
+ status = -ENOMEM;
+ goto err_put_dirent;
+ }
+
+ desc_data->parent = &gdev_data->cdev_id->kobj;
+ status = sysfs_create_groups(desc_data->parent,
+ desc_data->attr_groups);
+ if (status)
+ goto err_free_name;
+
+ char *path __free(kfree) = kasprintf(GFP_KERNEL, "gpio%u/value",
+ gpio_chip_hwgpio(desc));
+ if (!path) {
+ status = -ENOMEM;
+ goto err_remove_groups;
+ }
+
+ desc_data->value_chip_node = kernfs_walk_and_get(desc_data->parent->sd,
+ path);
+ if (!desc_data->value_chip_node) {
+ status = -ENODEV;
+ goto err_remove_groups;
+ }
+
return 0;
+err_remove_groups:
+ sysfs_remove_groups(desc_data->parent, desc_data->attr_groups);
+err_free_name:
+ kfree(desc_data->attr_group.name);
+err_put_dirent:
+ sysfs_put(desc_data->value_class_node);
err_unregister_device:
device_unregister(desc_data->dev);
err_free_data:
@@ -876,6 +912,7 @@ void gpiod_unexport(struct gpio_desc *desc)
clear_bit(FLAG_EXPORT, &desc->flags);
sysfs_put(desc_data->value_class_node);
device_unregister(desc_data->dev);
+ kernfs_put(desc_data->value_chip_node);
/*
* Release irq after deregistration to prevent race with
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 14/15] gpio: sysfs: allow disabling the legacy parts of the GPIO sysfs interface
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (12 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 13/15] gpio: sysfs: export the GPIO directory locally in the gpiochip<id> directory Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 15/15] gpio: TODO: remove the task for the sysfs rework Bartosz Golaszewski
` (4 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Add a Kconfig switch allowing to disable the legacy parts of the GPIO
sysfs interface. This means that even though we keep the
/sys/class/gpio/ directory, it no longer contains the global
export/unexport attribute pair (instead, the user should use the
per-chip export/unpexport) nor the gpiochip$BASE entries. This option
default to y if GPIO sysfs is enabled but we'll default it to n at some
point in the future.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/Kconfig | 8 ++++++++
drivers/gpio/gpiolib-sysfs.c | 32 ++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 44f922e10db2f8dcbdacf79ccd27b0fd9cd93564..d040fdd95ee4b19851057fbedbb023f277149c9c 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -69,6 +69,14 @@ config GPIO_SYSFS
use the character device /dev/gpiochipN with the appropriate
ioctl() operations instead.
+config GPIO_SYSFS_LEGACY
+ bool "Enable legacy functionalities of the sysfs interface"
+ depends on GPIO_SYSFS
+ default y if GPIO_SYSFS
+ help
+ Say Y here if you want to enable the legacy, global GPIO
+ numberspace-based functionalities of the sysfs interface.
+
config GPIO_CDEV
bool "Character device (/dev/gpiochipN) support" if EXPERT
default y
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index d8c17d71d5458011d15bc4239bd4c041244da3fd..48e1353f966837d46f00b552f870440097ecfe18 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -46,7 +46,9 @@ struct gpiod_data {
struct device *dev;
struct mutex mutex;
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
struct kernfs_node *value_class_node;
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
struct kernfs_node *value_chip_node;
int irq;
unsigned char irq_flags;
@@ -67,7 +69,9 @@ struct gpiod_data {
struct gpiodev_data {
struct gpio_device *gdev;
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
struct device *cdev_base; /* Class device by GPIO base */
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
struct device *cdev_id; /* Class device by GPIO device ID */
struct list_head exported_lines;
};
@@ -181,7 +185,9 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
struct gpiod_data *data = priv;
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
sysfs_notify_dirent(data->value_class_node);
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
kernfs_notify(data->value_chip_node);
return IRQ_HANDLED;
@@ -416,6 +422,7 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
* /ngpio ... matching gpio_chip.ngpio
*/
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
static ssize_t base_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -424,6 +431,7 @@ static ssize_t base_show(struct device *dev, struct device_attribute *attr,
return sysfs_emit(buf, "%u\n", data->gdev->base);
}
static DEVICE_ATTR_RO(base);
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
static ssize_t label_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -548,6 +556,7 @@ static struct device_attribute dev_attr_unexport = __ATTR(unexport, 0200,
NULL,
chip_unexport_store);
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
static struct attribute *gpiochip_attrs[] = {
&dev_attr_base.attr,
&dev_attr_label.attr,
@@ -555,6 +564,7 @@ static struct attribute *gpiochip_attrs[] = {
NULL,
};
ATTRIBUTE_GROUPS(gpiochip);
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
static struct attribute *gpiochip_ext_attrs[] = {
&dev_attr_label.attr,
@@ -565,6 +575,7 @@ static struct attribute *gpiochip_ext_attrs[] = {
};
ATTRIBUTE_GROUPS(gpiochip_ext);
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
/*
* /sys/class/gpio/export ... write-only
* integer N ... number of GPIO to export (full access)
@@ -629,10 +640,13 @@ static struct attribute *gpio_class_attrs[] = {
NULL,
};
ATTRIBUTE_GROUPS(gpio_class);
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
static const struct class gpio_class = {
.name = "gpio",
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
.class_groups = gpio_class_groups,
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
};
static int match_gdev(struct device *dev, const void *desc)
@@ -763,6 +777,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
* at least one known user of gpiod_export_link() in the tree. This
* function still uses class_find_device() internally.
*/
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
desc_data->dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), desc_data,
desc_data->attr_groups,
@@ -779,6 +794,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
status = -ENODEV;
goto err_unregister_device;
}
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
gdev_data = gdev_get_data(gdev);
if (!gdev_data) {
@@ -822,10 +838,12 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
err_free_name:
kfree(desc_data->attr_group.name);
err_put_dirent:
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
sysfs_put(desc_data->value_class_node);
err_unregister_device:
device_unregister(desc_data->dev);
err_free_data:
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
kfree(desc_data);
err_clear_bit:
clear_bit(FLAG_EXPORT, &desc->flags);
@@ -834,12 +852,14 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
}
EXPORT_SYMBOL_GPL(gpiod_export);
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
static int match_export(struct device *dev, const void *desc)
{
struct gpiod_data *data = dev_get_drvdata(dev);
return data->desc == desc;
}
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
/**
* gpiod_export_link - create a sysfs link to an exported GPIO node
@@ -856,6 +876,7 @@ static int match_export(struct device *dev, const void *desc)
int gpiod_export_link(struct device *dev, const char *name,
struct gpio_desc *desc)
{
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
struct device *cdev;
int ret;
@@ -872,6 +893,9 @@ int gpiod_export_link(struct device *dev, const char *name,
put_device(cdev);
return ret;
+#else
+ return -EOPNOTSUPP;
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
}
EXPORT_SYMBOL_GPL(gpiod_export_link);
@@ -910,8 +934,10 @@ void gpiod_unexport(struct gpio_desc *desc)
list_del(&desc_data->list);
clear_bit(FLAG_EXPORT, &desc->flags);
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
sysfs_put(desc_data->value_class_node);
device_unregister(desc_data->dev);
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
kernfs_put(desc_data->value_chip_node);
/*
@@ -966,6 +992,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
guard(mutex)(&sysfs_lock);
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
/* use chip->base for the ID; it's already known to be unique */
data->cdev_base = device_create_with_groups(&gpio_class, parent,
MKDEV(0, 0), data,
@@ -976,13 +1003,16 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
kfree(data);
return PTR_ERR(data->cdev_base);
}
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
data->cdev_id = device_create_with_groups(&gpio_class, parent,
MKDEV(0, 0), data,
gpiochip_ext_groups,
"chip%d", gdev->id);
if (IS_ERR(data->cdev_id)) {
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
device_unregister(data->cdev_base);
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
kfree(data);
return PTR_ERR(data->cdev_id);
}
@@ -1001,7 +1031,9 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
if (!data)
return;
+#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
device_unregister(data->cdev_base);
+#endif /* CONFIG_GPIO_SYSFS_LEGACY */
device_unregister(data->cdev_id);
kfree(data);
}
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH RFC/RFT 15/15] gpio: TODO: remove the task for the sysfs rework
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (13 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 14/15] gpio: sysfs: allow disabling the legacy parts of the GPIO sysfs interface Bartosz Golaszewski
@ 2025-06-10 14:38 ` Bartosz Golaszewski
2025-06-13 8:02 ` [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Geert Uytterhoeven
` (3 subsequent siblings)
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-10 14:38 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Remove the completed task tracking the rework of the sysfs interface.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/gpio/TODO | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO
index 4a8b349f2483a91883c74b07a43efb1462dbd377..97024925f14ab92b311741b0b7944acbddcf5c4b 100644
--- a/drivers/gpio/TODO
+++ b/drivers/gpio/TODO
@@ -183,19 +183,6 @@ remove the old ones and finally rename the new ones back to the old names.
-------------------------------------------------------------------------------
-Extend the sysfs ABI to allow exporting lines by their HW offsets
-
-The need to support the sysfs GPIO class is one of the main obstacles to
-removing the global GPIO numberspace from the kernel. In order to wean users
-off using global numbers from user-space, extend the existing interface with
-new per-gpiochip export/unexport attributes that allow to refer to GPIOs using
-their hardware offsets within the chip.
-
-Encourage users to switch to using them and eventually remove the existing
-global export/unexport attribues.
-
--------------------------------------------------------------------------------
-
Remove GPIOD_FLAGS_BIT_NONEXCLUSIVE
GPIOs in the linux kernel are meant to be an exclusive resource. This means
--
2.48.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 01/15] Documentation: gpio: undocument removed behavior
2025-06-10 14:38 ` [PATCH RFC/RFT 01/15] Documentation: gpio: undocument removed behavior Bartosz Golaszewski
@ 2025-06-11 8:15 ` Linus Walleij
0 siblings, 0 replies; 32+ messages in thread
From: Linus Walleij @ 2025-06-11 8:15 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> Since commit 700cdf7ed00f ("gpio: sysfs: make the sysfs export behavior
> consistent"), named GPIO lines are no longer exported in sysfs as links
> named after the them. Drop the misleading bit from the ABI docs.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 02/15] Documentation: gpio: document the active_low field in the sysfs ABI
2025-06-10 14:38 ` [PATCH RFC/RFT 02/15] Documentation: gpio: document the active_low field in the sysfs ABI Bartosz Golaszewski
@ 2025-06-11 8:15 ` Linus Walleij
0 siblings, 0 replies; 32+ messages in thread
From: Linus Walleij @ 2025-06-11 8:15 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> Exported GPIO lines also have the active_low attribute which is not
> documented. Add a short mention for it.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 03/15] gpio: sysfs: call mutex_destroy() in gpiod_unexport()
2025-06-10 14:38 ` [PATCH RFC/RFT 03/15] gpio: sysfs: call mutex_destroy() in gpiod_unexport() Bartosz Golaszewski
@ 2025-06-11 8:16 ` Linus Walleij
0 siblings, 0 replies; 32+ messages in thread
From: Linus Walleij @ 2025-06-11 8:16 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> While not critical, it's useful to have the corresponding call to
> mutex_destroy() whenever we use mutex_init(). Add the call right before
> kfreeing the GPIO data.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 04/15] gpio: sysfs: refactor the coding style
2025-06-10 14:38 ` [PATCH RFC/RFT 04/15] gpio: sysfs: refactor the coding style Bartosz Golaszewski
@ 2025-06-11 8:16 ` Linus Walleij
0 siblings, 0 replies; 32+ messages in thread
From: Linus Walleij @ 2025-06-11 8:16 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> Update the code to be more consistent with the rest of the codebase.
> Mostly correctly align line-breaks, remove unneeded tabs, stray newlines
> & spaces and tweak the comment style.
>
> No functional change.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 05/15] gpio: sysfs: remove unneeded headers
2025-06-10 14:38 ` [PATCH RFC/RFT 05/15] gpio: sysfs: remove unneeded headers Bartosz Golaszewski
@ 2025-06-11 8:17 ` Linus Walleij
0 siblings, 0 replies; 32+ messages in thread
From: Linus Walleij @ 2025-06-11 8:17 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> No symbols from the linux/idr.h or linux/spinlock.h headers are used in
> this file so remove them. We also don't technically need linux/list.h
> currently but one of the follow-up commits will start using it so let's
> leave it.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 06/15] gpio: sysfs: remove the mockdev pointer from struct gpio_device
2025-06-10 14:38 ` [PATCH RFC/RFT 06/15] gpio: sysfs: remove the mockdev pointer from struct gpio_device Bartosz Golaszewski
@ 2025-06-11 8:19 ` Linus Walleij
0 siblings, 0 replies; 32+ messages in thread
From: Linus Walleij @ 2025-06-11 8:19 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> The usage of the mockdev pointer in struct gpio_device is limited to the
> GPIO sysfs code. There's no reason to keep it in this top-level
> structure. Create a separate structure containing the reference to the
> GPIO device and the dummy class device that will be passed to
> device_create_with_groups(). The !gdev->mockdev checks can be removed as
> long as we make sure that all operations on the GPIO class are protected
> with the sysfs lock.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Much better like this!
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 07/15] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs
2025-06-10 14:38 ` [PATCH RFC/RFT 07/15] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs Bartosz Golaszewski
@ 2025-06-11 8:27 ` Linus Walleij
2025-06-23 8:02 ` Bartosz Golaszewski
0 siblings, 1 reply; 32+ messages in thread
From: Linus Walleij @ 2025-06-11 8:27 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> In order to enable moving away from the global GPIO numberspace-based
> exporting of lines over sysfs: add a parallel, per-chip entry under
> /sys/class/gpio/ for every registered GPIO chip, denoted by device ID
> in the file name and not its base GPIO number.
>
> Compared to the existing chip group: it does not contain the "base"
> attribute as the goal of this change is to not refer to GPIOs by their
> global number from user-space anymore. It also contains its own,
> per-chip export/unexport attribute pair which allow to export lines by
> their hardware offset within the chip.
>
> Caveat #1: the new device cannot be a link to (or be linked to by) the
> existing "gpiochip<BASE>" entry as we cannot create links in
> /sys/class/xyz/.
>
> Caveat #2: the new entry cannot be named "gpiochipX" as it could
> conflict with devices whose base is statically defined to a low number.
> Let's go with "chipX" instead.
That's unfortunate but it's good to separate them, and
gpio/gpiochip is a bit tautological so this is a better sysfs name
anyway.
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> + /chipX ... for each gpiochip; #X is the gpio device ID
> + /export ... asks the kernel to export a GPIO at HW offset X to userspace
> + /unexport ... to return a GPIO at HW offset X to the kernel
> + /label ... (r/o) descriptive, not necessarily unique
Not necessarily *globally* unique, I think it's required to be unique
per-gpiochip right? Otherwise it will be hard to create these files.
> + /ngpio ... (r/o) number of GPIOs exposed by the chip
I like this approach, it's a good compromise between different
desires of the sysfs ABI.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (14 preceding siblings ...)
2025-06-10 14:38 ` [PATCH RFC/RFT 15/15] gpio: TODO: remove the task for the sysfs rework Bartosz Golaszewski
@ 2025-06-13 8:02 ` Geert Uytterhoeven
2025-06-16 12:27 ` Bartosz Golaszewski
2025-06-18 13:38 ` Jan Lübbe
` (2 subsequent siblings)
18 siblings, 1 reply; 32+ messages in thread
From: Geert Uytterhoeven @ 2025-06-13 8:02 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, linux-gpio, linux-kernel,
Bartosz Golaszewski
Hi Bartosz,
On Tue, 10 Jun 2025 at 16:38, Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> Following our discussion[1], here's a proposal for extending the sysfs
> interface with attributes not referring to GPIO lines by their global
> numbers in a backward compatible way.
>
> Long story short: there is now a new class device for each GPIO chip.
> It's called chipX where X is the ID of the device as per the driver
> model and it lives next to the old gpiochipABC where ABC is the GPIO
> base. Each new chip class device has a pair of export/unexport
> attributes which work similarly to the global ones under /sys/class/gpio
> but take hardware offsets within the chip as input, instead of the
> global numbers. Finally, each exported line appears at the same time as
> the global /sys/class/gpio/gpioABC as well as per-chip
> /sys/class/gpio/chipX/gpioY sysfs group.
>
> First, there are some documentation updates, followed by a set of
> updates to the sysfs code that's useful even without the new
> functionality. Then the actual implementation of a parallel GPIO chip
> entry not containing the base GPIO number in the name and the
> corresponding sysfs attribute group for each exported line that lives
> under the new chip class device. Finally: also allow to compile out the
> legacy parts leaving only the new elements of the sysfs ABI.
>
> This series passes the compatibility tests I wrote while working on the
> user-space compatibility layer for sysfs[2].
>
> [1] https://lore.kernel.org/all/CAMRc=McUCeZcU6co1aN54rTudo+JfPjjForu4iKQ5npwXk6GXA@mail.gmail.com/
> [2] https://github.com/brgl/gpio-sysfs-compat-tests
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Thanks for your series!
I gave it a quick try, and it seems to work as expected, great!
Given the /sys/class/gpio/chip* numbering is volatile, I expect
script writers should use topological path names instead, .e.g.
/sys/devices/platform/soc/e6052000.gpio/gpio/chip*/export and
sys/devices/platform/soc/e6052000.gpio/gpio/chip*/gpio19
(note the wildcards).
I hope to find time to do a review of the patches soon...
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-13 8:02 ` [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Geert Uytterhoeven
@ 2025-06-16 12:27 ` Bartosz Golaszewski
0 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-16 12:27 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, linux-gpio, linux-kernel,
Bartosz Golaszewski
On Fri, Jun 13, 2025 at 10:02 AM Geert Uytterhoeven
<geert@linux-m68k.org> wrote:
>
> Hi Bartosz,
>
> On Tue, 10 Jun 2025 at 16:38, Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> > Following our discussion[1], here's a proposal for extending the sysfs
> > interface with attributes not referring to GPIO lines by their global
> > numbers in a backward compatible way.
> >
> > Long story short: there is now a new class device for each GPIO chip.
> > It's called chipX where X is the ID of the device as per the driver
> > model and it lives next to the old gpiochipABC where ABC is the GPIO
> > base. Each new chip class device has a pair of export/unexport
> > attributes which work similarly to the global ones under /sys/class/gpio
> > but take hardware offsets within the chip as input, instead of the
> > global numbers. Finally, each exported line appears at the same time as
> > the global /sys/class/gpio/gpioABC as well as per-chip
> > /sys/class/gpio/chipX/gpioY sysfs group.
> >
> > First, there are some documentation updates, followed by a set of
> > updates to the sysfs code that's useful even without the new
> > functionality. Then the actual implementation of a parallel GPIO chip
> > entry not containing the base GPIO number in the name and the
> > corresponding sysfs attribute group for each exported line that lives
> > under the new chip class device. Finally: also allow to compile out the
> > legacy parts leaving only the new elements of the sysfs ABI.
> >
> > This series passes the compatibility tests I wrote while working on the
> > user-space compatibility layer for sysfs[2].
> >
> > [1] https://lore.kernel.org/all/CAMRc=McUCeZcU6co1aN54rTudo+JfPjjForu4iKQ5npwXk6GXA@mail.gmail.com/
> > [2] https://github.com/brgl/gpio-sysfs-compat-tests
> >
> > Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> Thanks for your series!
>
> I gave it a quick try, and it seems to work as expected, great!
>
> Given the /sys/class/gpio/chip* numbering is volatile, I expect
> script writers should use topological path names instead, .e.g.
> /sys/devices/platform/soc/e6052000.gpio/gpio/chip*/export and
> sys/devices/platform/soc/e6052000.gpio/gpio/chip*/gpio19
> (note the wildcards).
>
Or they can use the chip label like what they would do with the
character device. The relevant attribute is still there.
Bartosz
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (15 preceding siblings ...)
2025-06-13 8:02 ` [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Geert Uytterhoeven
@ 2025-06-18 13:38 ` Jan Lübbe
2025-06-18 14:53 ` Bartosz Golaszewski
2025-06-18 15:56 ` Bartosz Golaszewski
2025-06-18 14:54 ` Bartosz Golaszewski
2025-06-20 7:30 ` (subset) " Bartosz Golaszewski
18 siblings, 2 replies; 32+ messages in thread
From: Jan Lübbe @ 2025-06-18 13:38 UTC (permalink / raw)
To: Bartosz Golaszewski, Ahmad Fatoum, Kent Gibson, Marek Vasut,
Geert Uytterhoeven, Linus Walleij
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
Hi Bartosz,
On Tue, 2025-06-10 at 16:38 +0200, Bartosz Golaszewski wrote:
> Following our discussion[1], here's a proposal for extending the sysfs
> interface with attributes not referring to GPIO lines by their global
> numbers in a backward compatible way.
>
> Long story short: there is now a new class device for each GPIO chip.
> It's called chipX where X is the ID of the device as per the driver
> model and it lives next to the old gpiochipABC where ABC is the GPIO
> base. Each new chip class device has a pair of export/unexport
> attributes which work similarly to the global ones under /sys/class/gpio
> but take hardware offsets within the chip as input, instead of the
> global numbers. Finally, each exported line appears at the same time as
> the global /sys/class/gpio/gpioABC as well as per-chip
> /sys/class/gpio/chipX/gpioY sysfs group.
>
> First, there are some documentation updates, followed by a set of
> updates to the sysfs code that's useful even without the new
> functionality. Then the actual implementation of a parallel GPIO chip
> entry not containing the base GPIO number in the name and the
> corresponding sysfs attribute group for each exported line that lives
> under the new chip class device. Finally: also allow to compile out the
> legacy parts leaving only the new elements of the sysfs ABI.
>
> This series passes the compatibility tests I wrote while working on the
> user-space compatibility layer for sysfs[2].
>
> [1] https://lore.kernel.org/all/CAMRc=McUCeZcU6co1aN54rTudo+JfPjjForu4iKQ5npwXk6GXA@mail.gmail.com/
> [2] https://github.com/brgl/gpio-sysfs-compat-tests
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
thanks for implementing this! I tried it on one of our boards and noticed a few
things.
After unexporting a GPIO from the chipX dir, the subdirectory is not removed:
root@lxatac-00006:/sys/class/gpio/chip9# echo 1 > export
root@lxatac-00006:/sys/class/gpio/chip9# echo 1 > unexport
root@lxatac-00006:/sys/class/gpio/chip9# ls -l gpio1/
total 0
-rw-r--r-- 1 root root 4096 Jun 18 12:52 active_low
-rw-r--r-- 1 root root 4096 Jun 18 12:52 direction
-rw-r--r-- 1 root root 4096 Jun 18 12:52 edge
-rw-r--r-- 1 root root 4096 Jun 18 12:52 value
Subsequent attempts to export it again fail.
The contents of /sys/kernel/debug/gpio don't really fit any more:
gpiochip10: GPIOs 660-663, parent: i2c/0-0024, pca9570, can sleep:
gpio-660 (DUT_PWR_EN |tacd ) out hi
gpio-661 (DUT_PWR_DISCH |tacd ) out lo
gpio-662 (DUT_PWR_ADCRST |reset ) out lo
The header is inconsistent: it uses the 'gpiochip' prefix, but not the base as
the old class devices in /sys/class/gpio/. Perhaps something like this?
chip10: GPIOs 0-2 (global IDs 660-663), parent: i2c/0-0024, pca9570, can sleep:
gpio-0 (660) (DUT_PWR_EN |tacd ) out hi
gpio-1 (661) (DUT_PWR_DISCH |tacd ) out lo
gpio-2 (662) (DUT_PWR_ADCRST |reset ) out lo
If GPIO_SYSFS_LEGACY is disabled, the global IDs could be hidden.
Unix permissions/ownership just works.
As far as I can see, this is basically everything I need to replace the old
global ID based GPIO access in labgrid. Thanks again! :)
Regards,
Jan
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-18 13:38 ` Jan Lübbe
@ 2025-06-18 14:53 ` Bartosz Golaszewski
2025-06-18 15:56 ` Bartosz Golaszewski
1 sibling, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-18 14:53 UTC (permalink / raw)
To: Jan Lübbe
Cc: Ahmad Fatoum, Kent Gibson, Marek Vasut, Geert Uytterhoeven,
Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski
On Wed, Jun 18, 2025 at 3:38 PM Jan Lübbe <jlu@pengutronix.de> wrote:
>
> Hi Bartosz,
>
> On Tue, 2025-06-10 at 16:38 +0200, Bartosz Golaszewski wrote:
> > Following our discussion[1], here's a proposal for extending the sysfs
> > interface with attributes not referring to GPIO lines by their global
> > numbers in a backward compatible way.
> >
> > Long story short: there is now a new class device for each GPIO chip.
> > It's called chipX where X is the ID of the device as per the driver
> > model and it lives next to the old gpiochipABC where ABC is the GPIO
> > base. Each new chip class device has a pair of export/unexport
> > attributes which work similarly to the global ones under /sys/class/gpio
> > but take hardware offsets within the chip as input, instead of the
> > global numbers. Finally, each exported line appears at the same time as
> > the global /sys/class/gpio/gpioABC as well as per-chip
> > /sys/class/gpio/chipX/gpioY sysfs group.
> >
> > First, there are some documentation updates, followed by a set of
> > updates to the sysfs code that's useful even without the new
> > functionality. Then the actual implementation of a parallel GPIO chip
> > entry not containing the base GPIO number in the name and the
> > corresponding sysfs attribute group for each exported line that lives
> > under the new chip class device. Finally: also allow to compile out the
> > legacy parts leaving only the new elements of the sysfs ABI.
> >
> > This series passes the compatibility tests I wrote while working on the
> > user-space compatibility layer for sysfs[2].
> >
> > [1] https://lore.kernel.org/all/CAMRc=McUCeZcU6co1aN54rTudo+JfPjjForu4iKQ5npwXk6GXA@mail.gmail.com/
> > [2] https://github.com/brgl/gpio-sysfs-compat-tests
> >
> > Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> thanks for implementing this! I tried it on one of our boards and noticed a few
> things.
>
> After unexporting a GPIO from the chipX dir, the subdirectory is not removed:
> root@lxatac-00006:/sys/class/gpio/chip9# echo 1 > export
> root@lxatac-00006:/sys/class/gpio/chip9# echo 1 > unexport
> root@lxatac-00006:/sys/class/gpio/chip9# ls -l gpio1/
> total 0
> -rw-r--r-- 1 root root 4096 Jun 18 12:52 active_low
> -rw-r--r-- 1 root root 4096 Jun 18 12:52 direction
> -rw-r--r-- 1 root root 4096 Jun 18 12:52 edge
> -rw-r--r-- 1 root root 4096 Jun 18 12:52 value
> Subsequent attempts to export it again fail.
>
Ah, seems like one of the last-minute rebases made me drop the
relevant sysfs_remove_groups(). Thanks for catching it.
> The contents of /sys/kernel/debug/gpio don't really fit any more:
> gpiochip10: GPIOs 660-663, parent: i2c/0-0024, pca9570, can sleep:
> gpio-660 (DUT_PWR_EN |tacd ) out hi
> gpio-661 (DUT_PWR_DISCH |tacd ) out lo
> gpio-662 (DUT_PWR_ADCRST |reset ) out lo
> The header is inconsistent: it uses the 'gpiochip' prefix, but not the base as
> the old class devices in /sys/class/gpio/. Perhaps something like this?
> chip10: GPIOs 0-2 (global IDs 660-663), parent: i2c/0-0024, pca9570, can sleep:
> gpio-0 (660) (DUT_PWR_EN |tacd ) out hi
> gpio-1 (661) (DUT_PWR_DISCH |tacd ) out lo
> gpio-2 (662) (DUT_PWR_ADCRST |reset ) out lo
> If GPIO_SYSFS_LEGACY is disabled, the global IDs could be hidden.
>
I have not paid attention to the debugfs output TBH. Let me check it in v2.
> Unix permissions/ownership just works.
>
>
> As far as I can see, this is basically everything I need to replace the old
> global ID based GPIO access in labgrid. Thanks again! :)
>
Bart
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (16 preceding siblings ...)
2025-06-18 13:38 ` Jan Lübbe
@ 2025-06-18 14:54 ` Bartosz Golaszewski
2025-06-20 7:30 ` (subset) " Bartosz Golaszewski
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-18 14:54 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: linux-gpio, linux-kernel, Bartosz Golaszewski
On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
>
> Following our discussion[1], here's a proposal for extending the sysfs
> interface with attributes not referring to GPIO lines by their global
> numbers in a backward compatible way.
>
> Long story short: there is now a new class device for each GPIO chip.
> It's called chipX where X is the ID of the device as per the driver
> model and it lives next to the old gpiochipABC where ABC is the GPIO
> base. Each new chip class device has a pair of export/unexport
> attributes which work similarly to the global ones under /sys/class/gpio
> but take hardware offsets within the chip as input, instead of the
> global numbers. Finally, each exported line appears at the same time as
> the global /sys/class/gpio/gpioABC as well as per-chip
> /sys/class/gpio/chipX/gpioY sysfs group.
>
> First, there are some documentation updates, followed by a set of
> updates to the sysfs code that's useful even without the new
> functionality. Then the actual implementation of a parallel GPIO chip
> entry not containing the base GPIO number in the name and the
> corresponding sysfs attribute group for each exported line that lives
> under the new chip class device. Finally: also allow to compile out the
> legacy parts leaving only the new elements of the sysfs ABI.
>
> This series passes the compatibility tests I wrote while working on the
> user-space compatibility layer for sysfs[2].
>
> [1] https://lore.kernel.org/all/CAMRc=McUCeZcU6co1aN54rTudo+JfPjjForu4iKQ5npwXk6GXA@mail.gmail.com/
> [2] https://github.com/brgl/gpio-sysfs-compat-tests
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> ---
> Bartosz Golaszewski (15):
> Documentation: gpio: undocument removed behavior
> Documentation: gpio: document the active_low field in the sysfs ABI
> gpio: sysfs: call mutex_destroy() in gpiod_unexport()
> gpio: sysfs: refactor the coding style
> gpio: sysfs: remove unneeded headers
> gpio: sysfs: remove the mockdev pointer from struct gpio_device
If there are no objections, I'd like to queue patches 1-6 for v6.17
already in the coming days. They are largely preparatory, have been
reviewed by Linus and don't introduce any changes to the sysfs
interface.
Bart
> gpio: sysfs: add a parallel class device for each GPIO chip using device IDs
> gpio: sysfs: only get the dirent reference for the value attr once
> gpio: sysfs: pass gpiod_data directly to internal GPIO sysfs functions
> gpio: sysfs: don't use driver data in sysfs callbacks for line attributes
> gpio: sysfs: rename the data variable in gpiod_(un)export()
> gpio: sysfs: don't look up exported lines as class devices
> gpio: sysfs: export the GPIO directory locally in the gpiochip<id> directory
> gpio: sysfs: allow disabling the legacy parts of the GPIO sysfs interface
> gpio: TODO: remove the task for the sysfs rework
>
> Documentation/ABI/obsolete/sysfs-gpio | 12 +-
> drivers/gpio/Kconfig | 8 +
> drivers/gpio/TODO | 13 -
> drivers/gpio/gpiolib-sysfs.c | 650 ++++++++++++++++++++++++----------
> drivers/gpio/gpiolib.h | 3 -
> 5 files changed, 474 insertions(+), 212 deletions(-)
> ---
> base-commit: 19272b37aa4f83ca52bdf9c16d5d81bdd1354494
> change-id: 20250402-gpio-sysfs-chip-export-84ac424b61c5
>
> Best regards,
> --
> Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-18 13:38 ` Jan Lübbe
2025-06-18 14:53 ` Bartosz Golaszewski
@ 2025-06-18 15:56 ` Bartosz Golaszewski
2025-06-23 8:27 ` Jan Lübbe
1 sibling, 1 reply; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-18 15:56 UTC (permalink / raw)
To: Jan Lübbe
Cc: Ahmad Fatoum, Kent Gibson, Marek Vasut, Geert Uytterhoeven,
Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski
On Wed, Jun 18, 2025 at 3:38 PM Jan Lübbe <jlu@pengutronix.de> wrote:
>
[snip]
> The contents of /sys/kernel/debug/gpio don't really fit any more:
> gpiochip10: GPIOs 660-663, parent: i2c/0-0024, pca9570, can sleep:
> gpio-660 (DUT_PWR_EN |tacd ) out hi
> gpio-661 (DUT_PWR_DISCH |tacd ) out lo
> gpio-662 (DUT_PWR_ADCRST |reset ) out lo
> The header is inconsistent: it uses the 'gpiochip' prefix, but not the base as
> the old class devices in /sys/class/gpio/. Perhaps something like this?
> chip10: GPIOs 0-2 (global IDs 660-663), parent: i2c/0-0024, pca9570, can sleep:
> gpio-0 (660) (DUT_PWR_EN |tacd ) out hi
> gpio-1 (661) (DUT_PWR_DISCH |tacd ) out lo
> gpio-2 (662) (DUT_PWR_ADCRST |reset ) out lo
> If GPIO_SYSFS_LEGACY is disabled, the global IDs could be hidden.
>
After a second look: IMO this is unrelated to the sysfs changes. We
definitely should change the debugfs output and rid it off the global
numbers but it shouldn't be part of this series. Also: are you using
this output in some way? Technically debugfs output is not stable ABI
so we can modify it without considering existing users but wanted to
run it by you to know if I'm going to break something for you.
Bart
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: (subset) [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
` (17 preceding siblings ...)
2025-06-18 14:54 ` Bartosz Golaszewski
@ 2025-06-20 7:30 ` Bartosz Golaszewski
18 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-20 7:30 UTC (permalink / raw)
To: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, Linus Walleij, Bartosz Golaszewski
Cc: Bartosz Golaszewski, linux-gpio, linux-kernel
From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
On Tue, 10 Jun 2025 16:38:15 +0200, Bartosz Golaszewski wrote:
> Following our discussion[1], here's a proposal for extending the sysfs
> interface with attributes not referring to GPIO lines by their global
> numbers in a backward compatible way.
>
> Long story short: there is now a new class device for each GPIO chip.
> It's called chipX where X is the ID of the device as per the driver
> model and it lives next to the old gpiochipABC where ABC is the GPIO
> base. Each new chip class device has a pair of export/unexport
> attributes which work similarly to the global ones under /sys/class/gpio
> but take hardware offsets within the chip as input, instead of the
> global numbers. Finally, each exported line appears at the same time as
> the global /sys/class/gpio/gpioABC as well as per-chip
> /sys/class/gpio/chipX/gpioY sysfs group.
>
> [...]
Applied, thanks!
[01/15] Documentation: gpio: undocument removed behavior
https://git.kernel.org/brgl/linux/c/5ed0d32805c19cfa5f03a25ec7e041dc845d3062
[02/15] Documentation: gpio: document the active_low field in the sysfs ABI
https://git.kernel.org/brgl/linux/c/1ae86030745013d9d54fc287c1ce875f7ddd99e6
[03/15] gpio: sysfs: call mutex_destroy() in gpiod_unexport()
https://git.kernel.org/brgl/linux/c/e1f02b40a741aac47016765c21b61e91d19aa1ec
[04/15] gpio: sysfs: refactor the coding style
https://git.kernel.org/brgl/linux/c/dc665b5248f90aa2dc74ecc1f2ebb731a6f5afd6
[05/15] gpio: sysfs: remove unneeded headers
https://git.kernel.org/brgl/linux/c/982ec96c3876349e65e60c7b4fd91d767099837e
[06/15] gpio: sysfs: remove the mockdev pointer from struct gpio_device
https://git.kernel.org/brgl/linux/c/fd19792851db77e74cff4e2dc772d25a83cdc34d
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 07/15] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs
2025-06-11 8:27 ` Linus Walleij
@ 2025-06-23 8:02 ` Bartosz Golaszewski
0 siblings, 0 replies; 32+ messages in thread
From: Bartosz Golaszewski @ 2025-06-23 8:02 UTC (permalink / raw)
To: Linus Walleij
Cc: Ahmad Fatoum, Kent Gibson, Jan Lübbe, Marek Vasut,
Geert Uytterhoeven, linux-gpio, linux-kernel, Bartosz Golaszewski
On Wed, Jun 11, 2025 at 10:27 AM Linus Walleij <linus.walleij@linaro.org> wrote:
>
> On Tue, Jun 10, 2025 at 4:38 PM Bartosz Golaszewski <brgl@bgdev.pl> wrote:
>
> > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> >
> > In order to enable moving away from the global GPIO numberspace-based
> > exporting of lines over sysfs: add a parallel, per-chip entry under
> > /sys/class/gpio/ for every registered GPIO chip, denoted by device ID
> > in the file name and not its base GPIO number.
> >
> > Compared to the existing chip group: it does not contain the "base"
> > attribute as the goal of this change is to not refer to GPIOs by their
> > global number from user-space anymore. It also contains its own,
> > per-chip export/unexport attribute pair which allow to export lines by
> > their hardware offset within the chip.
> >
> > Caveat #1: the new device cannot be a link to (or be linked to by) the
> > existing "gpiochip<BASE>" entry as we cannot create links in
> > /sys/class/xyz/.
> >
> > Caveat #2: the new entry cannot be named "gpiochipX" as it could
> > conflict with devices whose base is statically defined to a low number.
> > Let's go with "chipX" instead.
>
> That's unfortunate but it's good to separate them, and
> gpio/gpiochip is a bit tautological so this is a better sysfs name
> anyway.
>
> > Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> > + /chipX ... for each gpiochip; #X is the gpio device ID
> > + /export ... asks the kernel to export a GPIO at HW offset X to userspace
> > + /unexport ... to return a GPIO at HW offset X to the kernel
> > + /label ... (r/o) descriptive, not necessarily unique
>
> Not necessarily *globally* unique, I think it's required to be unique
> per-gpiochip right? Otherwise it will be hard to create these files.
>
Yes, this must be updated as well, the labels are of course unique.
Bart
> > + /ngpio ... (r/o) number of GPIOs exposed by the chip
>
> I like this approach, it's a good compromise between different
> desires of the sysfs ABI.
>
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
>
> Yours,
> Linus Walleij
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair
2025-06-18 15:56 ` Bartosz Golaszewski
@ 2025-06-23 8:27 ` Jan Lübbe
0 siblings, 0 replies; 32+ messages in thread
From: Jan Lübbe @ 2025-06-23 8:27 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Ahmad Fatoum, Kent Gibson, Marek Vasut, Geert Uytterhoeven,
Linus Walleij, linux-gpio, linux-kernel, Bartosz Golaszewski
On Wed, 2025-06-18 at 17:56 +0200, Bartosz Golaszewski wrote:
> On Wed, Jun 18, 2025 at 3:38 PM Jan Lübbe <jlu@pengutronix.de> wrote:
> >
>
> [snip]
>
>
> > The contents of /sys/kernel/debug/gpio don't really fit any more:
> > gpiochip10: GPIOs 660-663, parent: i2c/0-0024, pca9570, can sleep:
> > gpio-660 (DUT_PWR_EN |tacd ) out hi
> > gpio-661 (DUT_PWR_DISCH |tacd ) out lo
> > gpio-662 (DUT_PWR_ADCRST |reset ) out lo
> > The header is inconsistent: it uses the 'gpiochip' prefix, but not the base as
> > the old class devices in /sys/class/gpio/. Perhaps something like this?
> > chip10: GPIOs 0-2 (global IDs 660-663), parent: i2c/0-0024, pca9570, can sleep:
> > gpio-0 (660) (DUT_PWR_EN |tacd ) out hi
> > gpio-1 (661) (DUT_PWR_DISCH |tacd ) out lo
> > gpio-2 (662) (DUT_PWR_ADCRST |reset ) out lo
> > If GPIO_SYSFS_LEGACY is disabled, the global IDs could be hidden.
> >
>
> After a second look: IMO this is unrelated to the sysfs changes. We
> definitely should change the debugfs output and rid it off the global
> numbers but it shouldn't be part of this series.
Agreed.
> Also: are you using
> this output in some way? Technically debugfs output is not stable ABI
> so we can modify it without considering existing users but wanted to
> run it by you to know if I'm going to break something for you.
We're not parsing debugfs. :) I just checked manually if it matched the new
naming and saw that it didn't.
Regards,
Jan
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2025-06-23 8:27 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-10 14:38 [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 01/15] Documentation: gpio: undocument removed behavior Bartosz Golaszewski
2025-06-11 8:15 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 02/15] Documentation: gpio: document the active_low field in the sysfs ABI Bartosz Golaszewski
2025-06-11 8:15 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 03/15] gpio: sysfs: call mutex_destroy() in gpiod_unexport() Bartosz Golaszewski
2025-06-11 8:16 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 04/15] gpio: sysfs: refactor the coding style Bartosz Golaszewski
2025-06-11 8:16 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 05/15] gpio: sysfs: remove unneeded headers Bartosz Golaszewski
2025-06-11 8:17 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 06/15] gpio: sysfs: remove the mockdev pointer from struct gpio_device Bartosz Golaszewski
2025-06-11 8:19 ` Linus Walleij
2025-06-10 14:38 ` [PATCH RFC/RFT 07/15] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs Bartosz Golaszewski
2025-06-11 8:27 ` Linus Walleij
2025-06-23 8:02 ` Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 08/15] gpio: sysfs: only get the dirent reference for the value attr once Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 09/15] gpio: sysfs: pass gpiod_data directly to internal GPIO sysfs functions Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 10/15] gpio: sysfs: don't use driver data in sysfs callbacks for line attributes Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 11/15] gpio: sysfs: rename the data variable in gpiod_(un)export() Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 12/15] gpio: sysfs: don't look up exported lines as class devices Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 13/15] gpio: sysfs: export the GPIO directory locally in the gpiochip<id> directory Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 14/15] gpio: sysfs: allow disabling the legacy parts of the GPIO sysfs interface Bartosz Golaszewski
2025-06-10 14:38 ` [PATCH RFC/RFT 15/15] gpio: TODO: remove the task for the sysfs rework Bartosz Golaszewski
2025-06-13 8:02 ` [PATCH RFC/RFT 00/15] gpio: sysfs: add a per-chip export/unexport attribute pair Geert Uytterhoeven
2025-06-16 12:27 ` Bartosz Golaszewski
2025-06-18 13:38 ` Jan Lübbe
2025-06-18 14:53 ` Bartosz Golaszewski
2025-06-18 15:56 ` Bartosz Golaszewski
2025-06-23 8:27 ` Jan Lübbe
2025-06-18 14:54 ` Bartosz Golaszewski
2025-06-20 7:30 ` (subset) " Bartosz Golaszewski
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).