summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-aggregator.c
diff options
context:
space:
mode:
authorThomas Richard <thomas.richard@bootlin.com>2025-08-11 15:25:50 +0200
committerBartosz Golaszewski <bartosz.golaszewski@linaro.org>2025-08-11 15:39:31 +0200
commitb31c68fd851e74526ad963362ea205eb97b9a710 (patch)
treeb82d68ce0e89af90f2b882fa2dbd5f5738e1d421 /drivers/gpio/gpio-aggregator.c
parent6e986f8852f56cf9214ea2ec02b4b432e201d02c (diff)
gpio: aggregator: handle runtime registration of gpio_desc in gpiochip_fwd
Add request() callback to check if the GPIO descriptor was well registered in the gpiochip_fwd before using 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. Acked-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Thomas Richard <thomas.richard@bootlin.com> Link: https://lore.kernel.org/r/20250811-aaeon-up-board-pinctrl-support-v9-7-29f0cbbdfb30@bootlin.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Diffstat (limited to 'drivers/gpio/gpio-aggregator.c')
-rw-r--r--drivers/gpio/gpio-aggregator.c63
1 files changed, 57 insertions, 6 deletions
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
index c1952b28b7b7..f0d38d76cf73 100644
--- a/drivers/gpio/gpio-aggregator.c
+++ b/drivers/gpio/gpio-aggregator.c
@@ -246,6 +246,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 */
};
@@ -254,10 +255,24 @@ struct gpiochip_fwd {
#define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios))
+static 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;
+}
+
static 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
+ * -ENODEV if there is no GPIO desc for the line.
+ */
+ if (!test_bit(offset, fwd->valid_mask))
+ return -ENODEV;
+
return gpiod_get_direction(fwd->descs[offset]);
}
@@ -490,6 +505,21 @@ struct gpio_chip *gpiochip_fwd_get_gpiochip(struct gpiochip_fwd *fwd)
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_get_gpiochip, "GPIO_FORWARDER");
/**
+ * gpiochip_fwd_gpio_request - Request a line of the GPIO forwarder
+ * @fwd: GPIO forwarder
+ * @offset: the offset of the line to request
+ *
+ * Returns: 0 on success, or negative errno on failure.
+ */
+int gpiochip_fwd_gpio_request(struct gpiochip_fwd *fwd, unsigned int offset)
+{
+ struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
+
+ return gpio_fwd_request(gc, offset);
+}
+EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_request, "GPIO_FORWARDER");
+
+/**
* gpiochip_fwd_gpio_get_direction - Return the current direction of a GPIO forwarder line
* @fwd: GPIO forwarder
* @offset: the offset of the line
@@ -663,11 +693,16 @@ struct gpiochip_fwd *devm_gpiochip_fwd_alloc(struct device *dev,
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 = dev_name(dev);
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;
@@ -694,24 +729,21 @@ EXPORT_SYMBOL_NS_GPL(devm_gpiochip_fwd_alloc, "GPIO_FORWARDER");
int gpiochip_fwd_desc_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 (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,
@@ -722,6 +754,18 @@ int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc,
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_add, "GPIO_FORWARDER");
/**
+ * gpiochip_fwd_desc_free - Remove a GPIO desc from the forwarder
+ * @fwd: GPIO forwarder
+ * @offset: offset of GPIO desc to remove
+ */
+void gpiochip_fwd_desc_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(gpiochip_fwd_desc_free, "GPIO_FORWARDER");
+
+/**
* gpiochip_fwd_register - Register a GPIO forwarder
* @fwd: GPIO forwarder
*
@@ -731,6 +775,13 @@ int gpiochip_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