[RFC PATCH 3/5] clk: qcom: gdsc: Avoid actual power off until sync state

From: Abel Vesa
Date: Wed Mar 15 2023 - 09:23:53 EST


In case there is a sync state callback registered for a provider,
do not actually power off any gdsc for that provider until sync state
has been reached and return busy instead. Since the qcom_cc is
private, add a helper that returns the gdsc_desc based on the device of
the provider. Finally, add the generic gdsc sync state callback to be
used by the platform specific providers.

Signed-off-by: Abel Vesa <abel.vesa@xxxxxxxxxx>
---
drivers/clk/qcom/common.c | 13 +++++++++++++
drivers/clk/qcom/common.h | 1 +
drivers/clk/qcom/gdsc.c | 26 ++++++++++++++++++++++++++
drivers/clk/qcom/gdsc.h | 6 ++++++
4 files changed, 46 insertions(+)

diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index 75f09e6e057e..9bcda6952c20 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -20,6 +20,7 @@
struct qcom_cc {
struct qcom_reset_controller reset;
struct clk_regmap **rclks;
+ struct gdsc_desc *scd;
size_t num_rclks;
};

@@ -234,6 +235,13 @@ static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;
}

+struct gdsc_desc *qcom_cc_get_gdsc_desc(struct device *dev)
+{
+ struct qcom_cc *cc = dev_get_drvdata(dev);
+
+ return cc->scd;
+}
+
int qcom_cc_really_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc, struct regmap *regmap)
{
@@ -251,6 +259,8 @@ int qcom_cc_really_probe(struct platform_device *pdev,
if (!cc)
return -ENOMEM;

+ dev_set_drvdata(dev, cc);
+
reset = &cc->reset;
reset->rcdev.of_node = dev->of_node;
reset->rcdev.ops = &qcom_reset_ops;
@@ -267,6 +277,9 @@ int qcom_cc_really_probe(struct platform_device *pdev,
scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
if (!scd)
return -ENOMEM;
+
+ cc->scd = scd;
+
scd->dev = dev;
scd->scs = desc->gdscs;
scd->num = desc->num_gdscs;
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 9c8f7b798d9f..170266b4e9e8 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -61,6 +61,7 @@ extern struct regmap *qcom_cc_map(struct platform_device *pdev,
extern int qcom_cc_really_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc,
struct regmap *regmap);
+extern struct gdsc_desc *qcom_cc_get_gdsc_desc(struct device *dev);
extern int qcom_cc_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc);
extern int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index 5358e28122ab..af745907dc49 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -15,6 +15,8 @@
#include <linux/regulator/consumer.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
+
+#include "common.h"
#include "gdsc.h"

#define PWR_ON_MASK BIT(31)
@@ -319,6 +321,9 @@ static int gdsc_disable(struct generic_pm_domain *domain)
struct gdsc *sc = domain_to_gdsc(domain);
int ret;

+ if (!sc->state_synced)
+ return -EBUSY;
+
if (sc->pwrsts == PWRSTS_ON)
return gdsc_assert_reset(sc);

@@ -365,6 +370,7 @@ static int gdsc_disable(struct generic_pm_domain *domain)

static int gdsc_init(struct gdsc *sc)
{
+ struct device *dev = sc->dev;
u32 mask, val;
int on, ret;

@@ -452,6 +458,9 @@ static int gdsc_init(struct gdsc *sc)
if (!sc->pd.power_on)
sc->pd.power_on = gdsc_enable;

+ if (!dev_has_sync_state(dev))
+ sc->state_synced = true;
+
ret = pm_genpd_init(&sc->pd, NULL, !on);
if (ret)
goto err_disable_supply;
@@ -496,6 +505,7 @@ int gdsc_register(struct gdsc_desc *desc,
for (i = 0; i < num; i++) {
if (!scs[i])
continue;
+ scs[i]->dev = dev;
scs[i]->regmap = regmap;
scs[i]->rcdev = rcdev;
ret = gdsc_init(scs[i]);
@@ -536,6 +546,22 @@ void gdsc_unregister(struct gdsc_desc *desc)
of_genpd_del_provider(dev->of_node);
}

+void gdsc_sync_state(struct device *dev)
+{
+ struct gdsc_desc *scd = qcom_cc_get_gdsc_desc(dev);
+ struct gdsc **scs = scd->scs;
+ size_t num = scd->num;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (!scs[i])
+ continue;
+
+ scs[i]->state_synced = true;
+ genpd_queue_power_off_work(&scs[i]->pd);
+ }
+}
+
/*
* On SDM845+ the GPU GX domain is *almost* entirely controlled by the GMU
* running in the CX domain so the CPU doesn't need to know anything about the
diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
index 803512688336..e1c902caecde 100644
--- a/drivers/clk/qcom/gdsc.h
+++ b/drivers/clk/qcom/gdsc.h
@@ -35,6 +35,7 @@ struct gdsc {
struct generic_pm_domain pd;
struct generic_pm_domain *parent;
struct regmap *regmap;
+ struct device *dev;
unsigned int gdscr;
unsigned int collapse_ctrl;
unsigned int collapse_mask;
@@ -73,6 +74,8 @@ struct gdsc {

const char *supply;
struct regulator *rsupply;
+
+ bool state_synced;
};

struct gdsc_desc {
@@ -86,6 +89,7 @@ int gdsc_register(struct gdsc_desc *desc, struct reset_controller_dev *,
struct regmap *);
void gdsc_unregister(struct gdsc_desc *desc);
int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain);
+void gdsc_sync_state(struct device *dev);
#else
static inline int gdsc_register(struct gdsc_desc *desc,
struct reset_controller_dev *rcdev,
@@ -94,6 +98,8 @@ static inline int gdsc_register(struct gdsc_desc *desc,
return -ENOSYS;
}

+static inline void gdsc_sync_state(struct device *dev) { }
+
static inline void gdsc_unregister(struct gdsc_desc *desc) {};
#endif /* CONFIG_QCOM_GDSC */
#endif /* __QCOM_GDSC_H__ */
--
2.34.1