mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
gpio: regmap: Add optional runtime PM support
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. Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com> Reviewed-by: Michael Walle <mwalle@kernel.org> Signed-off-by: Janani Sunil <janani.sunil@analog.com> Reviewed-by: Linus Walleij <linusw@kernel.org> Link: https://patch.msgid.link/20260910-ad7768-gpio-v8-2-6aaaa15adeb3@analog.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
This commit is contained in:
parent
a4319947b5
commit
73748b4ec5
|
|
@ -6,11 +6,13 @@
|
|||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compiler_attributes.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>
|
||||
|
|
@ -23,6 +25,8 @@
|
|||
struct gpio_regmap {
|
||||
struct device *parent;
|
||||
struct regmap *regmap;
|
||||
struct device *pm_dev;
|
||||
|
||||
struct gpio_chip gpio_chip;
|
||||
|
||||
int reg_stride;
|
||||
|
|
@ -79,6 +83,33 @@ 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)
|
||||
|
||||
#define GPIO_REGMAP_RUNTIME_ACQUIRE(_gpio, _var) \
|
||||
ACQUIRE(gpio_regmap_runtime_try, _var)(_gpio)
|
||||
|
||||
#define GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(_var_ptr) \
|
||||
ACQUIRE_ERR(gpio_regmap_runtime, _var_ptr)
|
||||
|
||||
static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct gpio_regmap *gpio = gpiochip_get_data(chip);
|
||||
|
|
@ -91,7 +122,13 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
|
|||
else
|
||||
base = gpio_regmap_addr(gpio->reg_set_base);
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset, ®, &mask);
|
||||
GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
|
||||
ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset,
|
||||
®, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -114,7 +151,13 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
|
|||
unsigned int reg, mask, mask_val;
|
||||
int ret;
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, ®, &mask);
|
||||
GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
|
||||
ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset,
|
||||
®, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -146,6 +189,11 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
|
|||
unsigned int base, reg, mask, value = 0;
|
||||
int ret;
|
||||
|
||||
GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
|
||||
ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val)
|
||||
base = gpio_regmap_addr(gpio->reg_set_base);
|
||||
else
|
||||
|
|
@ -208,7 +256,13 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset, ®, &mask);
|
||||
GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
|
||||
ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset,
|
||||
®, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -262,7 +316,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset, ®, &mask);
|
||||
GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
|
||||
ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset,
|
||||
®, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -293,6 +353,11 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
|
|||
struct gpio_regmap *gpio = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
|
||||
GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
|
||||
ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&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
|
||||
|
|
@ -304,7 +369,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);
|
||||
}
|
||||
|
|
@ -399,6 +466,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;
|
||||
|
|
@ -406,7 +474,7 @@ 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;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ enum gpio_regmap_operation {
|
|||
* @parent: The parent device
|
||||
* @regmap: The regmap used to access the registers
|
||||
* given, the name of the device is used
|
||||
* @pm_dev: (Optional) Device to use for runtime power management.
|
||||
* @fwnode: (Optional) The firmware node.
|
||||
* If not given, the fwnode of the parent is used.
|
||||
* @label: (Optional) Descriptive name for GPIO controller.
|
||||
|
|
@ -117,6 +118,7 @@ enum gpio_regmap_operation {
|
|||
struct gpio_regmap_config {
|
||||
struct device *parent;
|
||||
struct regmap *regmap;
|
||||
struct device *pm_dev;
|
||||
struct fwnode_handle *fwnode;
|
||||
|
||||
const char *label;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user