[PATCH v4 12/14] gpio: regmap: Add optional runtime PM support
From: Janani Sunil <janani.sunil@analog.com>
Date: 2026-08-21 14:09:03
Also in:
linux-doc, linux-gpio, linux-iio, lkml
Subsystem:
gpio regmap, gpio subsystem, the rest · Maintainers:
Michael Walle, Linus Walleij, Bartosz Golaszewski, Linus Torvalds
Some gpio-regmap consumers share their regmap with a parent device that may be runtime suspended. GPIO register accesses must resume that device first. Add an optional pm_dev field and acquire it before register translation or access. Release it using runtime autosuspend after each operation. Keep the device active across the complete direction-output sequence and propagate failure when setting the initial output value. Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- drivers/gpio/gpio-regmap.c | 62 +++++++++++++++++++++++++++++++++++++++++++-- include/linux/gpio/regmap.h | 2 ++ 2 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index 0012e03d0d4e..3e7030e47483 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c@@ -6,10 +6,12 @@ */ #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/types.h>
@@ -31,6 +33,7 @@ struct gpio_regmap { unsigned int reg_clr_base; unsigned int reg_dir_in_base; unsigned int reg_dir_out_base; + struct device *pm_dev; unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output;
@@ -67,6 +70,27 @@ static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio, return 0; } +static int gpio_regmap_runtime_get(struct gpio_regmap *gpio) +{ + if (!gpio->pm_dev) + return 0; + + return pm_runtime_get_active(gpio->pm_dev, RPM_TRANSPARENT); +} + +static void gpio_regmap_runtime_put(struct gpio_regmap *gpio) +{ + if (!gpio->pm_dev) + return; + + pm_runtime_put_autosuspend(gpio->pm_dev); +} + +DEFINE_GUARD(gpio_regmap_runtime, struct gpio_regmap *, + gpio_regmap_runtime_get(_T), gpio_regmap_runtime_put(_T)) +DEFINE_GUARD_COND(gpio_regmap_runtime, _try, + gpio_regmap_runtime_get(_T), _RET == 0) + static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) { struct gpio_regmap *gpio = gpiochip_get_data(chip);
@@ -79,6 +103,11 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset) else base = gpio_regmap_addr(gpio->reg_set_base); + ACQUIRE(gpio_regmap_runtime_try, pm)(gpio); + ret = ACQUIRE_ERR(gpio_regmap_runtime_try, &pm); + if (ret) + return ret; + ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); if (ret) return ret;
@@ -102,6 +131,11 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset, unsigned int reg, mask, mask_val; int ret; + ACQUIRE(gpio_regmap_runtime_try, pm)(gpio); + ret = ACQUIRE_ERR(gpio_regmap_runtime_try, &pm); + if (ret) + return ret; + ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); if (ret) return ret;
@@ -127,6 +161,11 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip, unsigned int base, reg, mask; int ret; + ACQUIRE(gpio_regmap_runtime_try, pm)(gpio); + ret = ACQUIRE_ERR(gpio_regmap_runtime_try, &pm); + if (ret) + return ret; + if (val) base = gpio_regmap_addr(gpio->reg_set_base); else
@@ -182,6 +221,11 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, return -ENOTSUPP; } + ACQUIRE(gpio_regmap_runtime_try, pm)(gpio); + ret = ACQUIRE_ERR(gpio_regmap_runtime_try, &pm); + if (ret) + return ret; + ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); if (ret) return ret;
@@ -236,6 +280,11 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip, return -ENOTSUPP; } + ACQUIRE(gpio_regmap_runtime_try, pm)(gpio); + ret = ACQUIRE_ERR(gpio_regmap_runtime_try, &pm); + if (ret) + return ret; + ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask); if (ret) return ret;
@@ -260,6 +309,11 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip, struct gpio_regmap *gpio = gpiochip_get_data(chip); int ret; + ACQUIRE(gpio_regmap_runtime_try, pm)(gpio); + ret = ACQUIRE_ERR(gpio_regmap_runtime_try, &pm); + if (ret) + return ret; + /* * First check if this is gonna work on a fixed direction line, * if it doesn't (i.e. this is a fixed input line), then do not
@@ -271,7 +325,9 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip, return ret; } - gpio_regmap_set(chip, offset, value); + ret = gpio_regmap_set(chip, offset, value); + if (ret) + return ret; return gpio_regmap_set_direction(chip, offset, true); }
@@ -323,6 +379,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config gpio->reg_clr_base = config->reg_clr_base; gpio->reg_dir_in_base = config->reg_dir_in_base; gpio->reg_dir_out_base = config->reg_dir_out_base; + gpio->pm_dev = config->pm_dev; chip = &gpio->gpio_chip; chip->parent = config->parent;
@@ -330,7 +387,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config chip->base = -1; chip->names = config->names; chip->label = config->label ?: dev_name(config->parent); - chip->can_sleep = regmap_might_sleep(config->regmap); + chip->can_sleep = config->pm_dev || + regmap_might_sleep(config->regmap); chip->init_valid_mask = config->init_valid_mask; chip->request = gpiochip_generic_request;
diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h
index 06255756710d..eceb31b89f50 100644
--- a/include/linux/gpio/regmap.h
+++ b/include/linux/gpio/regmap.h@@ -34,6 +34,7 @@ struct regmap; * @ngpio_per_reg: (Optional) Number of GPIOs per register * @irq_domain: (Optional) IRQ domain if the controller is * interrupt-capable + * @pm_dev: (Optional) Device to use for runtime power management. * @reg_mask_xlate: (Optional) Translates base address and GPIO * offset to a register/bitmask pair. If not * given the default gpio_regmap_simple_xlate()
@@ -95,6 +96,7 @@ struct gpio_regmap_config { int reg_stride; int ngpio_per_reg; struct irq_domain *irq_domain; + struct device *pm_dev; unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output;
--
2.43.0