[PATCH v6 2/2] pinctrl: qcom: lpass-lpi: Switch to PM clock framework for runtime PM
From: Ajay Kumar Nandam
Date: Fri May 22 2026 - 16:34:27 EST
Convert the LPASS LPI pinctrl driver to use the PM clock framework for
runtime power management.
This allows the LPASS LPI pinctrl driver to drop clock votes when idle,
improves power efficiency on platforms using LPASS LPI island mode, and
aligns the driver with common runtime PM patterns used across Qualcomm
LPASS subsystems.
Guard GPIO register read/write helpers and slew-rate register programming
with synchronous runtime PM calls so the device is active during MMIO
operations whenever autosuspend is enabled.
Make PINCTRL_LPASS_LPI depend on PM_CLK, since this patch introduces
direct PM clock API use in the shared core.
Suggested-by: Konrad Dybcio <konrad.dybcio@xxxxxxxxxxxxxxxx>
Signed-off-by: Ajay Kumar Nandam <ajay.nandam@xxxxxxxxxxxxxxxx>
---
drivers/pinctrl/qcom/Kconfig | 1 +
drivers/pinctrl/qcom/pinctrl-lpass-lpi.c | 144 +++++++++++++++--------
2 files changed, 99 insertions(+), 46 deletions(-)
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index a09e840a01c6..18db350222b9 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -55,6 +55,7 @@ config PINCTRL_LPASS_LPI
select PINCONF
select GENERIC_PINCONF
select GENERIC_PINCTRL_GROUPS
+ depends on PM_CLK
depends on GPIOLIB
help
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
diff --git a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
index 15ced5027579..4d758fd117c4 100644
--- a/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
+++ b/drivers/pinctrl/qcom/pinctrl-lpass-lpi.c
@@ -11,10 +11,13 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
+#include <linux/cleanup.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_runtime.h>
#include "../pinctrl-utils.h"
@@ -22,7 +25,6 @@
#define MAX_NR_GPIO 32
#define GPIO_FUNC 0
-#define MAX_LPI_NUM_CLKS 2
struct lpi_pinctrl {
struct device *dev;
@@ -31,15 +33,14 @@ struct lpi_pinctrl {
struct pinctrl_desc desc;
char __iomem *tlmm_base;
char __iomem *slew_base;
- struct clk_bulk_data clks[MAX_LPI_NUM_CLKS];
/* Protects from concurrent register updates */
struct mutex lock;
DECLARE_BITMAP(ever_gpio, MAX_NR_GPIO);
const struct lpi_pinctrl_variant_data *data;
};
-static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin,
- unsigned int addr)
+static void __iomem *lpi_gpio_reg(struct lpi_pinctrl *state,
+ unsigned int pin, unsigned int addr)
{
u32 pin_offset;
@@ -48,22 +49,48 @@ static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin,
else
pin_offset = LPI_TLMM_REG_OFFSET * pin;
- return ioread32(state->tlmm_base + pin_offset + addr);
+ return state->tlmm_base + pin_offset + addr;
+}
+
+static void __lpi_gpio_read(struct lpi_pinctrl *state,
+ unsigned int pin, unsigned int addr, u32 *val)
+{
+ *val = ioread32(lpi_gpio_reg(state, pin, addr));
+}
+
+static void __lpi_gpio_write(struct lpi_pinctrl *state,
+ unsigned int pin, unsigned int addr,
+ unsigned int val)
+{
+ iowrite32(val, lpi_gpio_reg(state, pin, addr));
+}
+
+static int lpi_gpio_read(struct lpi_pinctrl *state, unsigned int pin,
+ unsigned int addr, u32 *val)
+{
+ int ret;
+
+ ret = pm_runtime_resume_and_get(state->dev);
+ if (ret < 0)
+ return ret;
+
+ __lpi_gpio_read(state, pin, addr, val);
+
+ return pm_runtime_put_autosuspend(state->dev);
}
static int lpi_gpio_write(struct lpi_pinctrl *state, unsigned int pin,
unsigned int addr, unsigned int val)
{
- u32 pin_offset;
+ int ret;
- if (state->data->flags & LPI_FLAG_USE_PREDEFINED_PIN_OFFSET)
- pin_offset = state->data->groups[pin].pin_offset;
- else
- pin_offset = LPI_TLMM_REG_OFFSET * pin;
+ ret = pm_runtime_resume_and_get(state->dev);
+ if (ret < 0)
+ return ret;
- iowrite32(val, state->tlmm_base + pin_offset + addr);
+ __lpi_gpio_write(state, pin, addr, val);
- return 0;
+ return pm_runtime_put_autosuspend(state->dev);
}
static const struct pinctrl_ops lpi_gpio_pinctrl_ops = {
@@ -107,8 +134,8 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
{
struct lpi_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
const struct lpi_pingroup *g = &pctrl->data->groups[group];
- u32 val;
- int i, pin = g->pin;
+ u32 io_val, val;
+ int i, pin = g->pin, ret;
for (i = 0; i < g->nfuncs; i++) {
if (g->funcs[i] == function)
@@ -118,8 +145,12 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
if (WARN_ON(i == g->nfuncs))
return -EINVAL;
- mutex_lock(&pctrl->lock);
- val = lpi_gpio_read(pctrl, pin, LPI_GPIO_CFG_REG);
+ ret = pm_runtime_resume_and_get(pctrl->dev);
+ if (ret < 0)
+ return ret;
+
+ guard(mutex)(&pctrl->lock);
+ __lpi_gpio_read(pctrl, pin, LPI_GPIO_CFG_REG, &val);
/*
* If this is the first time muxing to GPIO and the direction is
@@ -129,24 +160,25 @@ static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
*/
if (i == GPIO_FUNC && (val & LPI_GPIO_OE_MASK) &&
!test_and_set_bit(group, pctrl->ever_gpio)) {
- u32 io_val = lpi_gpio_read(pctrl, group, LPI_GPIO_VALUE_REG);
+ __lpi_gpio_read(pctrl, group, LPI_GPIO_VALUE_REG, &io_val);
if (io_val & LPI_GPIO_VALUE_IN_MASK) {
if (!(io_val & LPI_GPIO_VALUE_OUT_MASK))
- lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG,
- io_val | LPI_GPIO_VALUE_OUT_MASK);
+ __lpi_gpio_write(pctrl, group,
+ LPI_GPIO_VALUE_REG,
+ io_val | LPI_GPIO_VALUE_OUT_MASK);
} else {
if (io_val & LPI_GPIO_VALUE_OUT_MASK)
- lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG,
- io_val & ~LPI_GPIO_VALUE_OUT_MASK);
+ __lpi_gpio_write(pctrl, group,
+ LPI_GPIO_VALUE_REG,
+ io_val & ~LPI_GPIO_VALUE_OUT_MASK);
}
}
u32p_replace_bits(&val, i, LPI_GPIO_FUNCTION_MASK);
- lpi_gpio_write(pctrl, pin, LPI_GPIO_CFG_REG, val);
- mutex_unlock(&pctrl->lock);
+ __lpi_gpio_write(pctrl, pin, LPI_GPIO_CFG_REG, val);
- return 0;
+ return pm_runtime_put_autosuspend(pctrl->dev);
}
static const struct pinmux_ops lpi_gpio_pinmux_ops = {
@@ -162,11 +194,15 @@ static int lpi_config_get(struct pinctrl_dev *pctldev,
unsigned int param = pinconf_to_config_param(*config);
struct lpi_pinctrl *state = dev_get_drvdata(pctldev->dev);
unsigned int arg = 0;
+ u32 ctl_reg;
int is_out;
int pull;
- u32 ctl_reg;
+ int ret;
+
+ ret = lpi_gpio_read(state, pin, LPI_GPIO_CFG_REG, &ctl_reg);
+ if (ret)
+ return ret;
- ctl_reg = lpi_gpio_read(state, pin, LPI_GPIO_CFG_REG);
is_out = ctl_reg & LPI_GPIO_OE_MASK;
pull = FIELD_GET(LPI_GPIO_PULL_MASK, ctl_reg);
@@ -197,6 +233,7 @@ static int lpi_config_get(struct pinctrl_dev *pctldev,
}
*config = pinconf_to_config_packed(param, arg);
+
return 0;
}
@@ -206,7 +243,7 @@ static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl,
{
unsigned long sval;
void __iomem *reg;
- int slew_offset;
+ int slew_offset, ret;
if (slew > LPI_SLEW_RATE_MAX) {
dev_err(pctrl->dev, "invalid slew rate %u for pin: %d\n",
@@ -225,6 +262,10 @@ static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl,
else
reg = pctrl->slew_base + LPI_SLEW_RATE_CTL_REG;
+ ret = pm_runtime_resume_and_get(pctrl->dev);
+ if (ret < 0)
+ return ret;
+
mutex_lock(&pctrl->lock);
sval = ioread32(reg);
@@ -234,7 +275,7 @@ static int lpi_config_set_slew_rate(struct lpi_pinctrl *pctrl,
mutex_unlock(&pctrl->lock);
- return 0;
+ return pm_runtime_put_autosuspend(pctrl->dev);
}
static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group,
@@ -244,8 +285,8 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group,
unsigned int param, arg, pullup = LPI_GPIO_BIAS_DISABLE, strength = 2;
bool value, output_enabled = false;
const struct lpi_pingroup *g;
- int i, ret;
u32 val;
+ int i, ret;
g = &pctrl->data->groups[group];
for (i = 0; i < nconfs; i++) {
@@ -289,23 +330,26 @@ static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int group,
* As per Hardware Programming Guide, when configuring pin as output,
* set the pin value before setting output-enable (OE).
*/
+ ret = pm_runtime_resume_and_get(pctrl->dev);
+ if (ret < 0)
+ return ret;
+
+ guard(mutex)(&pctrl->lock);
if (output_enabled) {
val = u32_encode_bits(value ? 1 : 0, LPI_GPIO_VALUE_OUT_MASK);
- lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, val);
+ __lpi_gpio_write(pctrl, group, LPI_GPIO_VALUE_REG, val);
}
- mutex_lock(&pctrl->lock);
- val = lpi_gpio_read(pctrl, group, LPI_GPIO_CFG_REG);
+ __lpi_gpio_read(pctrl, group, LPI_GPIO_CFG_REG, &val);
u32p_replace_bits(&val, pullup, LPI_GPIO_PULL_MASK);
u32p_replace_bits(&val, LPI_GPIO_DS_TO_VAL(strength),
LPI_GPIO_OUT_STRENGTH_MASK);
u32p_replace_bits(&val, output_enabled, LPI_GPIO_OE_MASK);
- lpi_gpio_write(pctrl, group, LPI_GPIO_CFG_REG, val);
- mutex_unlock(&pctrl->lock);
+ __lpi_gpio_write(pctrl, group, LPI_GPIO_CFG_REG, val);
- return 0;
+ return pm_runtime_put_autosuspend(pctrl->dev);
}
static const struct pinconf_ops lpi_gpio_pinconf_ops = {
@@ -354,9 +398,14 @@ static int lpi_gpio_direction_output(struct gpio_chip *chip,
static int lpi_gpio_get(struct gpio_chip *chip, unsigned int pin)
{
struct lpi_pinctrl *state = gpiochip_get_data(chip);
+ u32 val;
+ int ret;
+
+ ret = lpi_gpio_read(state, pin, LPI_GPIO_VALUE_REG, &val);
+ if (ret)
+ return ret;
- return lpi_gpio_read(state, pin, LPI_GPIO_VALUE_REG) &
- LPI_GPIO_VALUE_IN_MASK;
+ return val & LPI_GPIO_VALUE_IN_MASK;
}
static int lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
@@ -399,7 +448,9 @@ static void lpi_gpio_dbg_show_one(struct seq_file *s,
pctldev = pctldev ? : state->ctrl;
pindesc = pctldev->desc->pins[offset];
- ctl_reg = lpi_gpio_read(state, offset, LPI_GPIO_CFG_REG);
+ if (lpi_gpio_read(state, offset, LPI_GPIO_CFG_REG, &ctl_reg))
+ return;
+
is_out = ctl_reg & LPI_GPIO_OE_MASK;
func = FIELD_GET(LPI_GPIO_FUNCTION_MASK, ctl_reg);
@@ -482,9 +533,6 @@ int lpi_pinctrl_probe(struct platform_device *pdev)
pctrl->data = data;
pctrl->dev = &pdev->dev;
- pctrl->clks[0].id = "core";
- pctrl->clks[1].id = "audio";
-
pctrl->tlmm_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pctrl->tlmm_base))
return dev_err_probe(dev, PTR_ERR(pctrl->tlmm_base),
@@ -497,13 +545,19 @@ int lpi_pinctrl_probe(struct platform_device *pdev)
"Slew resource not provided\n");
}
- ret = devm_clk_bulk_get_optional(dev, MAX_LPI_NUM_CLKS, pctrl->clks);
+ ret = devm_pm_clk_create(dev);
if (ret)
return ret;
- ret = clk_bulk_prepare_enable(MAX_LPI_NUM_CLKS, pctrl->clks);
+ ret = of_pm_clk_add_clks(dev);
+ if (ret < 0 && ret != -ENODEV)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 100);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_enable(dev);
if (ret)
- return dev_err_probe(dev, ret, "Can't enable clocks\n");
+ return ret;
pctrl->desc.pctlops = &lpi_gpio_pinctrl_ops;
pctrl->desc.pmxops = &lpi_gpio_pinmux_ops;
@@ -542,7 +596,6 @@ int lpi_pinctrl_probe(struct platform_device *pdev)
err_pinctrl:
mutex_destroy(&pctrl->lock);
- clk_bulk_disable_unprepare(MAX_LPI_NUM_CLKS, pctrl->clks);
return ret;
}
@@ -554,7 +607,6 @@ void lpi_pinctrl_remove(struct platform_device *pdev)
int i;
mutex_destroy(&pctrl->lock);
- clk_bulk_disable_unprepare(MAX_LPI_NUM_CLKS, pctrl->clks);
for (i = 0; i < pctrl->data->npins; i++)
pinctrl_generic_remove_group(pctrl->ctrl, i);
--
2.34.1