Commit c2c7f0a4 authored by Rajendra Nayak's avatar Rajendra Nayak Committed by Stephen Boyd

clk: qcom: gdsc: Add support for hierarchical power domains

Some qcom SoCs' can have hierarchical power domains. Let the gdsc structs
specify the parents (if any) and the driver add genpd subdomains for them.
Signed-off-by: default avatarRajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent 7e0810c9
...@@ -185,6 +185,7 @@ int qcom_cc_really_probe(struct platform_device *pdev, ...@@ -185,6 +185,7 @@ int qcom_cc_really_probe(struct platform_device *pdev,
struct clk **clks; struct clk **clks;
struct qcom_reset_controller *reset; struct qcom_reset_controller *reset;
struct qcom_cc *cc; struct qcom_cc *cc;
struct gdsc_desc *scd;
size_t num_clks = desc->num_clks; size_t num_clks = desc->num_clks;
struct clk_regmap **rclks = desc->clks; struct clk_regmap **rclks = desc->clks;
...@@ -238,13 +239,22 @@ int qcom_cc_really_probe(struct platform_device *pdev, ...@@ -238,13 +239,22 @@ int qcom_cc_really_probe(struct platform_device *pdev,
return ret; return ret;
if (desc->gdscs && desc->num_gdscs) { if (desc->gdscs && desc->num_gdscs) {
ret = gdsc_register(dev, desc->gdscs, desc->num_gdscs, scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
&reset->rcdev, regmap); if (!scd)
return -ENOMEM;
scd->dev = dev;
scd->scs = desc->gdscs;
scd->num = desc->num_gdscs;
ret = gdsc_register(scd, &reset->rcdev, regmap);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister,
scd);
if (ret) if (ret)
return ret; return ret;
} }
return devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister, dev); return 0;
} }
EXPORT_SYMBOL_GPL(qcom_cc_really_probe); EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
......
...@@ -201,11 +201,14 @@ static int gdsc_init(struct gdsc *sc) ...@@ -201,11 +201,14 @@ static int gdsc_init(struct gdsc *sc)
return 0; return 0;
} }
int gdsc_register(struct device *dev, struct gdsc **scs, size_t num, int gdsc_register(struct gdsc_desc *desc,
struct reset_controller_dev *rcdev, struct regmap *regmap) struct reset_controller_dev *rcdev, struct regmap *regmap)
{ {
int i, ret; int i, ret;
struct genpd_onecell_data *data; struct genpd_onecell_data *data;
struct device *dev = desc->dev;
struct gdsc **scs = desc->scs;
size_t num = desc->num;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
...@@ -228,10 +231,30 @@ int gdsc_register(struct device *dev, struct gdsc **scs, size_t num, ...@@ -228,10 +231,30 @@ int gdsc_register(struct device *dev, struct gdsc **scs, size_t num,
data->domains[i] = &scs[i]->pd; data->domains[i] = &scs[i]->pd;
} }
/* Add subdomains */
for (i = 0; i < num; i++) {
if (!scs[i])
continue;
if (scs[i]->parent)
pm_genpd_add_subdomain(scs[i]->parent, &scs[i]->pd);
}
return of_genpd_add_provider_onecell(dev->of_node, data); return of_genpd_add_provider_onecell(dev->of_node, data);
} }
void gdsc_unregister(struct device *dev) void gdsc_unregister(struct gdsc_desc *desc)
{ {
int i;
struct device *dev = desc->dev;
struct gdsc **scs = desc->scs;
size_t num = desc->num;
/* Remove subdomains */
for (i = 0; i < num; i++) {
if (!scs[i])
continue;
if (scs[i]->parent)
pm_genpd_remove_subdomain(scs[i]->parent, &scs[i]->pd);
}
of_genpd_del_provider(dev->of_node); of_genpd_del_provider(dev->of_node);
} }
...@@ -41,6 +41,7 @@ struct reset_controller_dev; ...@@ -41,6 +41,7 @@ struct reset_controller_dev;
*/ */
struct gdsc { struct gdsc {
struct generic_pm_domain pd; struct generic_pm_domain pd;
struct generic_pm_domain *parent;
struct regmap *regmap; struct regmap *regmap;
unsigned int gdscr; unsigned int gdscr;
unsigned int *cxcs; unsigned int *cxcs;
...@@ -51,18 +52,24 @@ struct gdsc { ...@@ -51,18 +52,24 @@ struct gdsc {
unsigned int reset_count; unsigned int reset_count;
}; };
struct gdsc_desc {
struct device *dev;
struct gdsc **scs;
size_t num;
};
#ifdef CONFIG_QCOM_GDSC #ifdef CONFIG_QCOM_GDSC
int gdsc_register(struct device *, struct gdsc **, size_t n, int gdsc_register(struct gdsc_desc *desc, struct reset_controller_dev *,
struct reset_controller_dev *, struct regmap *); struct regmap *);
void gdsc_unregister(struct device *); void gdsc_unregister(struct gdsc_desc *desc);
#else #else
static inline int gdsc_register(struct device *d, struct gdsc **g, size_t n, static inline int gdsc_register(struct gdsc_desc *desc,
struct reset_controller_dev *rcdev, struct reset_controller_dev *rcdev,
struct regmap *r) struct regmap *r)
{ {
return -ENOSYS; return -ENOSYS;
} }
static inline void gdsc_unregister(struct device *d) {}; static inline void gdsc_unregister(struct gdsc_desc *desc) {};
#endif /* CONFIG_QCOM_GDSC */ #endif /* CONFIG_QCOM_GDSC */
#endif /* __QCOM_GDSC_H__ */ #endif /* __QCOM_GDSC_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment