[PATCH v4 4/6] gpio: Ensure struct gpio_chip for gpiochip_setup_dev()

From: Tzung-Bi Shih

Date: Mon Feb 23 2026 - 01:18:50 EST


Ensure struct gpio_chip for gpiochip_setup_dev(). This eliminates a few
checks for struct gpio_chip.

Signed-off-by: Tzung-Bi Shih <tzungbi@xxxxxxxxxx>
---
v4:
- To be consistent, rename `chip` -> `gc`.
- Add lockdep checks.

v3: https://lore.kernel.org/all/20260213092958.864411-5-tzungbi@xxxxxxxxxx
- Pass struct gpio_chip * only.

v2: https://lore.kernel.org/all/20260203061059.975605-5-tzungbi@xxxxxxxxxx
- No changes.

v1: https://lore.kernel.org/all/20260116081036.352286-10-tzungbi@xxxxxxxxxx

drivers/gpio/gpiolib-cdev.c | 14 ++++----------
drivers/gpio/gpiolib-cdev.h | 2 +-
drivers/gpio/gpiolib-sysfs.c | 21 ++++++++-------------
drivers/gpio/gpiolib-sysfs.h | 4 ++--
drivers/gpio/gpiolib.c | 24 +++++++++++++++++-------
5 files changed, 32 insertions(+), 33 deletions(-)

diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 73ae77f0f213..a154b04e9316 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -2782,11 +2782,13 @@ static const struct file_operations gpio_fileops = {
#endif
};

-int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
+int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt)
{
- struct gpio_chip *gc;
+ struct gpio_device *gdev = gc->gpiodev;
int ret;

+ lockdep_assert_held(&gdev->srcu);
+
cdev_init(&gdev->chrdev, &gpio_fileops);
gdev->chrdev.owner = THIS_MODULE;
gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id);
@@ -2802,14 +2804,6 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
return ret;
}

- guard(srcu)(&gdev->srcu);
- gc = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!gc) {
- cdev_device_del(&gdev->chrdev, &gdev->dev);
- destroy_workqueue(gdev->line_state_wq);
- return -ENODEV;
- }
-
gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);

return 0;
diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h
index b42644cbffb8..4a9cb3335d99 100644
--- a/drivers/gpio/gpiolib-cdev.h
+++ b/drivers/gpio/gpiolib-cdev.h
@@ -7,7 +7,7 @@

struct gpio_device;

-int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt);
+int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt);
void gpiolib_cdev_unregister(struct gpio_device *gdev);

#endif /* GPIOLIB_CDEV_H */
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 1c25a7dd3db4..748a3eb1bf35 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -983,13 +983,15 @@ void gpiod_unexport(struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiod_unexport);

-int gpiochip_sysfs_register(struct gpio_device *gdev)
+int gpiochip_sysfs_register(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct gpiodev_data *data;
- struct gpio_chip *chip;
struct device *parent;
int err;

+ lockdep_assert_held(&gdev->srcu);
+
/*
* Many systems add gpio chips for SOC support very early,
* before driver model support is available. In those cases we
@@ -999,18 +1001,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
if (!class_is_registered(&gpio_class))
return 0;

- guard(srcu)(&gdev->srcu);
-
- chip = srcu_dereference(gdev->chip, &gdev->srcu);
- if (!chip)
- return -ENODEV;
-
/*
* For sysfs backward compatibility we need to preserve this
* preferred parenting to the gpio_chip parent field, if set.
*/
- if (chip->parent)
- parent = chip->parent;
+ if (gc->parent)
+ parent = gc->parent;
else
parent = &gdev->dev;

@@ -1029,7 +1025,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
MKDEV(0, 0), data,
gpiochip_groups,
GPIOCHIP_NAME "%d",
- chip->base);
+ gc->base);
if (IS_ERR(data->cdev_base)) {
err = PTR_ERR(data->cdev_base);
kfree(data);
@@ -1085,10 +1081,9 @@ void gpiochip_sysfs_unregister(struct gpio_chip *gc)
*/
static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
{
- struct gpio_device *gdev = gc->gpiodev;
int ret;

- ret = gpiochip_sysfs_register(gdev);
+ ret = gpiochip_sysfs_register(gc);
if (ret)
gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret);

diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h
index fd5db5384681..d0998de043a2 100644
--- a/drivers/gpio/gpiolib-sysfs.h
+++ b/drivers/gpio/gpiolib-sysfs.h
@@ -7,12 +7,12 @@ struct gpio_device;

#ifdef CONFIG_GPIO_SYSFS

-int gpiochip_sysfs_register(struct gpio_device *gdev);
+int gpiochip_sysfs_register(struct gpio_chip *gc);
void gpiochip_sysfs_unregister(struct gpio_chip *gc);

#else

-static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
+static inline int gpiochip_sysfs_register(struct gpio_chip *gc)
{
return 0;
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d5070c538ba5..44635e9a29c3 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -881,14 +881,14 @@ static const struct device_type gpio_dev_type = {
};

#ifdef CONFIG_GPIO_CDEV
-#define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt))
+#define gcdev_register(gc, devt) gpiolib_cdev_register((gc), (devt))
#define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev))
#else
/*
* gpiolib_cdev_register() indirectly calls device_add(), which is still
* required even when cdev is not selected.
*/
-#define gcdev_register(gdev, devt) device_add(&(gdev)->dev)
+#define gcdev_register(gc, devt) device_add(&(gc)->gpiodev->dev)
#define gcdev_unregister(gdev) device_del(&(gdev)->dev)
#endif

@@ -896,8 +896,9 @@ static const struct device_type gpio_dev_type = {
* An initial reference count has been held in gpiochip_add_data_with_key().
* The caller should drop the reference via gpio_device_put() on errors.
*/
-static int gpiochip_setup_dev(struct gpio_device *gdev)
+static int gpiochip_setup_dev(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
int ret;

@@ -910,11 +911,11 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
if (fwnode && !fwnode->dev)
fwnode_dev_initialized(fwnode, false);

- ret = gcdev_register(gdev, gpio_devt);
+ ret = gcdev_register(gc, gpio_devt);
if (ret)
return ret;

- ret = gpiochip_sysfs_register(gdev);
+ ret = gpiochip_sysfs_register(gc);
if (ret)
goto err_remove_device;

@@ -961,13 +962,22 @@ static void machine_gpiochip_add(struct gpio_chip *gc)
static void gpiochip_setup_devs(void)
{
struct gpio_device *gdev;
+ struct gpio_chip *gc;
int ret;

guard(srcu)(&gpio_devices_srcu);

list_for_each_entry_srcu(gdev, &gpio_devices, list,
srcu_read_lock_held(&gpio_devices_srcu)) {
- ret = gpiochip_setup_dev(gdev);
+ guard(srcu)(&gdev->srcu);
+
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc) {
+ dev_err(&gdev->dev, "Underlying GPIO chip is gone\n");
+ continue;
+ }
+
+ ret = gpiochip_setup_dev(gc);
if (ret) {
gpio_device_put(gdev);
dev_err(&gdev->dev,
@@ -1225,7 +1235,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* (i.e., `gpio_bus_type` is ready). Otherwise, defer until later.
*/
if (gpiolib_initialized) {
- ret = gpiochip_setup_dev(gdev);
+ ret = gpiochip_setup_dev(gc);
if (ret)
goto err_teardown_shared;
}
--
2.51.0