]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
pinctrl: qcom: lpass-lpi: Switch to PM clock framework for runtime PM
authorAjay Kumar Nandam <ajay.nandam@oss.qualcomm.com>
Fri, 22 May 2026 20:46:44 +0000 (02:16 +0530)
committerLinus Walleij <linusw@kernel.org>
Mon, 8 Jun 2026 08:46:42 +0000 (10:46 +0200)
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@oss.qualcomm.com>
Signed-off-by: Ajay Kumar Nandam <ajay.nandam@oss.qualcomm.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
drivers/pinctrl/qcom/Kconfig
drivers/pinctrl/qcom/pinctrl-lpass-lpi.c

index a09e840a01c66045f7c757ca4e813a8536fd60e7..18db350222b997a9568dd84092af1907857e1acc 100644 (file)
@@ -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
index 15ced5027579bfc67bf3d593d1b2ca6ee4c9c4ba..4d758fd117c48cce279b9938f47ba2dee2ea6333 100644 (file)
 #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);