clk: qcom: gdsc: Handle GDSC regulator supplies
authorBjorn Andersson <bjorn.andersson@linaro.org>
Fri, 17 Apr 2020 07:00:41 +0000 (00:00 -0700)
committerStephen Boyd <sboyd@kernel.org>
Thu, 14 May 2020 21:23:38 +0000 (14:23 -0700)
Certain GDSCs, such as the GPU_GX on MSM8996, requires that the upstream
regulator supply is powered in order to be turned on.

It's not guaranteed that the bootloader will leave these supplies on and
the driver core will attempt to enable any GDSCs before allowing the
individual drivers to probe defer on the PMIC regulator driver not yet
being present.

So the gdsc driver needs to be made aware of supplying regulators and
probe defer on their absence, and it needs to enable and disable the
regulator accordingly.

Voltage adjustments of the supplying regulator are deferred to the
client drivers themselves.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Link: https://lkml.kernel.org/r/20200417070044.1376212-2-bjorn.andersson@linaro.org
Reviewed-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/qcom/gdsc.c
drivers/clk/qcom/gdsc.h

index a250f59708d85fc4dcc5ce8f6fc3bbdbf3d28f49..04944f11659b659eaae355a8c4ce7290e387259e 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/ktime.h>
 #include <linux/pm_domain.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/reset-controller.h>
 #include <linux/slab.h>
 #include "gdsc.h"
@@ -112,6 +113,12 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status)
        int ret;
        u32 val = (status == GDSC_ON) ? 0 : SW_COLLAPSE_MASK;
 
+       if (status == GDSC_ON && sc->rsupply) {
+               ret = regulator_enable(sc->rsupply);
+               if (ret < 0)
+                       return ret;
+       }
+
        ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val);
        if (ret)
                return ret;
@@ -143,6 +150,13 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status)
 
        ret = gdsc_poll_status(sc, status);
        WARN(ret, "%s status stuck at 'o%s'", sc->pd.name, status ? "ff" : "n");
+
+       if (!ret && status == GDSC_OFF && sc->rsupply) {
+               ret = regulator_disable(sc->rsupply);
+               if (ret < 0)
+                       return ret;
+       }
+
        return ret;
 }
 
@@ -371,6 +385,15 @@ int gdsc_register(struct gdsc_desc *desc,
        if (!data->domains)
                return -ENOMEM;
 
+       for (i = 0; i < num; i++) {
+               if (!scs[i] || !scs[i]->supply)
+                       continue;
+
+               scs[i]->rsupply = devm_regulator_get(dev, scs[i]->supply);
+               if (IS_ERR(scs[i]->rsupply))
+                       return PTR_ERR(scs[i]->rsupply);
+       }
+
        data->num_domains = num;
        for (i = 0; i < num; i++) {
                if (!scs[i])
index 64cdc8cf0d4d2d5a7beb1aae5539f7cac8f11032..c36fc26dcdffe125a6493c9d1edf6938425125b0 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/pm_domain.h>
 
 struct regmap;
+struct regulator;
 struct reset_controller_dev;
 
 /**
@@ -52,6 +53,9 @@ struct gdsc {
        struct reset_controller_dev     *rcdev;
        unsigned int                    *resets;
        unsigned int                    reset_count;
+
+       const char                      *supply;
+       struct regulator                *rsupply;
 };
 
 struct gdsc_desc {