@@ -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;
@@ -319,6 +332,12 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
}
EXPORT_SYMBOL_GPL(qcom_cc_probe);
+void qcom_cc_sync_state(struct device *dev)
+{
+ gdsc_sync_state(dev);
+}
+EXPORT_SYMBOL_GPL(qcom_cc_sync_state);
+
int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
const struct qcom_cc_desc *desc)
{
@@ -61,9 +61,11 @@ 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,
const struct qcom_cc_desc *desc);
+extern void qcom_cc_sync_state(struct device *dev);
#endif
@@ -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
@@ -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__ */
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@linaro.org> --- drivers/clk/qcom/common.c | 19 +++++++++++++++++++ drivers/clk/qcom/common.h | 2 ++ drivers/clk/qcom/gdsc.c | 26 ++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 6 ++++++ 4 files changed, 53 insertions(+)