[PATCH v4 09/12] gpio: aggregator: handle runtime registration of gpio_desc in gpiochip_fwd

Thomas Richard posted 12 patches 9 months, 2 weeks ago
There is a newer version of this series
[PATCH v4 09/12] gpio: aggregator: handle runtime registration of gpio_desc in gpiochip_fwd
Posted by Thomas Richard 9 months, 2 weeks ago
Add request() callback to check if the GPIO descriptor was well registered
in the gpiochip_fwd before to use it. This is done to handle the case
where GPIO descriptor is added at runtime in the forwarder.

If at least one GPIO descriptor was not added before the forwarder
registration, we assume the forwarder can sleep as if a GPIO is added at
runtime it may sleep.

Signed-off-by: Thomas Richard <thomas.richard@bootlin.com>
---
 drivers/gpio/gpio-aggregator.c | 55 ++++++++++++++++++++++++++++++++++++------
 include/linux/gpio/forwarder.h |  4 +++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
index accf4be68238..230bd8398119 100644
--- a/drivers/gpio/gpio-aggregator.c
+++ b/drivers/gpio/gpio-aggregator.c
@@ -268,6 +268,7 @@ struct gpiochip_fwd {
 		spinlock_t slock;	/* protects tmp[] if !can_sleep */
 	};
 	struct gpiochip_fwd_timing *delay_timings;
+	unsigned long *valid_mask;
 	unsigned long tmp[];		/* values and descs for multiple ops */
 };
 
@@ -288,6 +289,21 @@ struct gpio_chip *gpio_fwd_get_gpiochip(struct gpiochip_fwd *fwd)
 }
 EXPORT_SYMBOL_NS_GPL(gpio_fwd_get_gpiochip, "GPIO_FORWARDER");
 
+/**
+ * gpio_fwd_request - Request a line of the GPIO forwarder
+ * @chip: GPIO chip in the forwarder
+ * @offset: the offset of the line to request
+ *
+ * Returns: 0 on success, or negative errno on failure.
+ */
+int gpio_fwd_request(struct gpio_chip *chip, unsigned int offset)
+{
+	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+	return test_bit(offset, fwd->valid_mask) ? 0 : -ENODEV;
+}
+EXPORT_SYMBOL_NS_GPL(gpio_fwd_request, "GPIO_FORWARDER");
+
 /**
  * gpio_fwd_get_direction - Return the current direction of a GPIO forwarder line
  * @chip: GPIO chip in the forwarder
@@ -299,6 +315,13 @@ int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
 {
 	struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
 
+	/*
+	 * get_direction() is called during gpiochip registration, return input
+	 * direction if there is no descriptor for the line
+	 */
+	if (!test_bit(offset, fwd->valid_mask))
+		return GPIO_LINE_DIRECTION_IN;
+
 	return gpiod_get_direction(fwd->descs[offset]);
 }
 EXPORT_SYMBOL_NS_GPL(gpio_fwd_get_direction, "GPIO_FORWARDER");
@@ -618,11 +641,16 @@ devm_gpio_fwd_alloc(struct device *dev, unsigned int ngpios)
 	if (!fwd->descs)
 		return ERR_PTR(-ENOMEM);
 
+	fwd->valid_mask = devm_bitmap_zalloc(dev, ngpios, GFP_KERNEL);
+	if (!fwd->valid_mask)
+		return ERR_PTR(-ENOMEM);
+
 	chip = &fwd->chip;
 
 	chip->label = label;
 	chip->parent = dev;
 	chip->owner = THIS_MODULE;
+	chip->request = gpio_fwd_request;
 	chip->get_direction = gpio_fwd_get_direction;
 	chip->direction_input = gpio_fwd_direction_input;
 	chip->direction_output = gpio_fwd_direction_output;
@@ -649,27 +677,21 @@ EXPORT_SYMBOL_NS_GPL(devm_gpio_fwd_alloc, "GPIO_FORWARDER");
 int gpio_fwd_gpio_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc,
 		      unsigned int offset)
 {
-	struct gpio_chip *parent = gpiod_to_chip(desc);
 	struct gpio_chip *chip = &fwd->chip;
 
 	if (offset > chip->ngpio)
 		return -EINVAL;
 
-	if (fwd->descs[offset])
+	if (test_and_set_bit(offset, fwd->valid_mask))
 		return -EEXIST;
 
 	/*
 	 * If any of the GPIO lines are sleeping, then the entire forwarder
 	 * will be sleeping.
-	 * If any of the chips support .set_config(), then the forwarder will
-	 * support setting configs.
 	 */
 	if (gpiod_cansleep(desc))
 		chip->can_sleep = true;
 
-	if (parent && parent->set_config)
-		chip->set_config = gpio_fwd_set_config;
-
 	fwd->descs[offset] = desc;
 
 	dev_dbg(chip->parent, "%u => gpio %d irq %d\n", offset,
@@ -679,6 +701,18 @@ int gpio_fwd_gpio_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc,
 }
 EXPORT_SYMBOL_NS_GPL(gpio_fwd_gpio_add, "GPIO_FORWARDER");
 
+/**
+ * gpio_fwd_gpio_free - Remove a GPIO from the forwarder
+ * @fwd: GPIO forwarder
+ * @offset: offset of GPIO to remove
+ */
+void gpio_fwd_gpio_free(struct gpiochip_fwd *fwd, unsigned int offset)
+{
+	if (test_and_clear_bit(offset, fwd->valid_mask))
+		gpiod_put(fwd->descs[offset]);
+}
+EXPORT_SYMBOL_NS_GPL(gpio_fwd_gpio_free, "GPIO_FORWARDER");
+
 /**
  * gpio_fwd_register - Register a GPIO forwarder
  * @fwd: GPIO forwarder
@@ -689,6 +723,13 @@ int gpio_fwd_register(struct gpiochip_fwd *fwd)
 {
 	struct gpio_chip *chip = &fwd->chip;
 
+	/*
+	 * Some gpio_desc were not registered. They will be registered at runtime
+	 * but we have to suppose they can sleep.
+	 */
+	if (bitmap_full(fwd->valid_mask, chip->ngpio))
+		chip->can_sleep = true;
+
 	if (chip->can_sleep)
 		mutex_init(&fwd->mlock);
 	else
diff --git a/include/linux/gpio/forwarder.h b/include/linux/gpio/forwarder.h
index 73a1a15e09a5..f0deb7573f36 100644
--- a/include/linux/gpio/forwarder.h
+++ b/include/linux/gpio/forwarder.h
@@ -12,6 +12,8 @@ struct gpiochip_fwd;
 
 struct gpio_chip *gpio_fwd_get_gpiochip(struct gpiochip_fwd *fwd);
 
+int gpio_fwd_request(struct gpio_chip *chip, unsigned int offset);
+
 int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset);
 
 int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset);
@@ -40,6 +42,8 @@ struct gpiochip_fwd *devm_gpio_fwd_alloc(struct device *dev,
 int gpio_fwd_gpio_add(struct gpiochip_fwd *fwd,
 		      struct gpio_desc *desc, unsigned int offset);
 
+void gpio_fwd_gpio_free(struct gpiochip_fwd *fwd, unsigned int offset);
+
 int gpio_fwd_register(struct gpiochip_fwd *fwd);
 
 #endif

-- 
2.39.5
Re: [PATCH v4 09/12] gpio: aggregator: handle runtime registration of gpio_desc in gpiochip_fwd
Posted by Andy Shevchenko 9 months, 2 weeks ago
On Tue, Apr 29, 2025 at 5:08 PM Thomas Richard
<thomas.richard@bootlin.com> wrote:
>
> Add request() callback to check if the GPIO descriptor was well registered
> in the gpiochip_fwd before to use it. This is done to handle the case

to use --> using

> where GPIO descriptor is added at runtime in the forwarder.
>
> If at least one GPIO descriptor was not added before the forwarder
> registration, we assume the forwarder can sleep as if a GPIO is added at
> runtime it may sleep.

...

> +       /*
> +        * get_direction() is called during gpiochip registration, return input
> +        * direction if there is no descriptor for the line

Missing period at the end.

> +        */

...

> -       if (fwd->descs[offset])
> +       if (test_and_set_bit(offset, fwd->valid_mask))
>                 return -EEXIST;

Here you should add the entire conditional and not in the one of the
previous patches.

...

> +       /*
> +        * Some gpio_desc were not registered. They will be registered at runtime
> +        * but we have to suppose they can sleep.
> +        */

> +       if (bitmap_full(fwd->valid_mask, chip->ngpio))

I think I don't get this check. The bitmap_full() returns true if and
only if _all_ bits up to the nbits are set. In accordance with the
comment it seems you wanted something different (an opposite?).

> +               chip->can_sleep = true;

-- 
With Best Regards,
Andy Shevchenko