mirror of
https://github.com/torvalds/linux.git
synced 2026-07-29 02:31:27 +02:00
pmdomain: core: add support for power-domains-child-ids
Currently, PM domains can only support hierarchy for simple
providers (e.g. ones with #power-domain-cells = 0).
Add support for oncell providers as well by adding a new property
`power-domains-child-ids` to describe the parent/child relationship.
For example, an SCMI PM domain provider has multiple domains, each of
which might be a child of diffeent parent domains. In this example,
the parent domains are MAIN_PD and WKUP_PD:
scmi_pds: protocol@11 {
reg = <0x11>;
#power-domain-cells = <1>;
power-domains = <&MAIN_PD>, <&WKUP_PD>;
power-domains-child-ids = <15>, <19>;
};
With this example using the new property, SCMI PM domain 15 becomes a
child domain of MAIN_PD, and SCMI domain 19 becomes a child domain of
WKUP_PD.
To support this feature, add two new core functions
- of_genpd_add_child_ids()
- of_genpd_remove_child_ids()
which can be called by pmdomain providers to add/remove child domains
if they support the new property power-domains-child-ids.
The add function is "all or nothing". If it cannot add all of the
child domains in the list, it will unwind any additions already made
and report a failure.
Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
53df57b3ae
commit
29e34a882b
|
|
@ -2924,6 +2924,173 @@ static struct generic_pm_domain *genpd_get_from_provider(
|
|||
return genpd;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_genpd_add_child_ids() - Parse power-domains-child-ids property
|
||||
* @np: Device node pointer associated with the PM domain provider.
|
||||
* @data: Pointer to the onecell data associated with the PM domain provider.
|
||||
*
|
||||
* Parse the power-domains and power-domains-child-ids properties to establish
|
||||
* parent-child relationships for PM domains. The power-domains property lists
|
||||
* parent domains, and power-domains-child-ids lists which child domain IDs
|
||||
* should be associated with each parent.
|
||||
*
|
||||
* Uses "all or nothing" semantics: either all relationships are established
|
||||
* successfully, or none are (any partially-added relationships are unwound
|
||||
* on error).
|
||||
*
|
||||
* Returns the number of parent-child relationships established on success,
|
||||
* 0 if the properties don't exist, or a negative error code on failure.
|
||||
*/
|
||||
int of_genpd_add_child_ids(struct device_node *np,
|
||||
struct genpd_onecell_data *data)
|
||||
{
|
||||
struct of_phandle_args parent_args;
|
||||
struct generic_pm_domain *parent_genpd, *child_genpd;
|
||||
struct generic_pm_domain **pairs; /* pairs[2*i]=parent, pairs[2*i+1]=child */
|
||||
u32 child_id;
|
||||
int i, ret, count, child_count, added = 0;
|
||||
|
||||
/* Check if both properties exist */
|
||||
count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells");
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
|
||||
child_count = of_property_count_u32_elems(np, "power-domains-child-ids");
|
||||
if (child_count < 0)
|
||||
return 0;
|
||||
if (child_count != count)
|
||||
return -EINVAL;
|
||||
|
||||
/* Allocate tracking array for error unwind (parent/child pairs) */
|
||||
pairs = kmalloc_array(count * 2, sizeof(*pairs), GFP_KERNEL);
|
||||
if (!pairs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = of_property_read_u32_index(np, "power-domains-child-ids",
|
||||
i, &child_id);
|
||||
if (ret)
|
||||
goto err_unwind;
|
||||
|
||||
/* Validate child ID is within bounds */
|
||||
if (child_id >= data->num_domains) {
|
||||
pr_err("Child ID %u out of bounds (max %u) for %pOF\n",
|
||||
child_id, data->num_domains - 1, np);
|
||||
ret = -EINVAL;
|
||||
goto err_unwind;
|
||||
}
|
||||
|
||||
/* Get the child domain */
|
||||
child_genpd = data->domains[child_id];
|
||||
if (!child_genpd) {
|
||||
pr_err("Child domain %u is NULL for %pOF\n", child_id, np);
|
||||
ret = -EINVAL;
|
||||
goto err_unwind;
|
||||
}
|
||||
|
||||
ret = of_parse_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells", i,
|
||||
&parent_args);
|
||||
if (ret)
|
||||
goto err_unwind;
|
||||
|
||||
/* Get the parent domain */
|
||||
parent_genpd = genpd_get_from_provider(&parent_args);
|
||||
of_node_put(parent_args.np);
|
||||
if (IS_ERR(parent_genpd)) {
|
||||
pr_err("Failed to get parent domain for %pOF: %ld\n",
|
||||
np, PTR_ERR(parent_genpd));
|
||||
ret = PTR_ERR(parent_genpd);
|
||||
goto err_unwind;
|
||||
}
|
||||
|
||||
/* Establish parent-child relationship */
|
||||
ret = pm_genpd_add_subdomain(parent_genpd, child_genpd);
|
||||
if (ret) {
|
||||
pr_err("Failed to add child domain %u to parent in %pOF: %d\n",
|
||||
child_id, np, ret);
|
||||
goto err_unwind;
|
||||
}
|
||||
|
||||
/* Track for potential unwind */
|
||||
pairs[2 * added] = parent_genpd;
|
||||
pairs[2 * added + 1] = child_genpd;
|
||||
added++;
|
||||
|
||||
pr_debug("Added child domain %u (%s) to parent %s for %pOF\n",
|
||||
child_id, child_genpd->name, parent_genpd->name, np);
|
||||
}
|
||||
|
||||
kfree(pairs);
|
||||
return count;
|
||||
|
||||
err_unwind:
|
||||
/* Reverse all previously established relationships */
|
||||
while (added-- > 0)
|
||||
pm_genpd_remove_subdomain(pairs[2 * added], pairs[2 * added + 1]);
|
||||
kfree(pairs);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_genpd_add_child_ids);
|
||||
|
||||
/**
|
||||
* of_genpd_remove_child_ids() - Remove parent-child PM domain relationships
|
||||
* @np: Device node pointer associated with the PM domain provider.
|
||||
* @data: Pointer to the onecell data associated with the PM domain provider.
|
||||
*
|
||||
* Reverses the effect of of_genpd_add_child_ids() by parsing the same
|
||||
* power-domains and power-domains-child-ids properties and calling
|
||||
* pm_genpd_remove_subdomain() for each established relationship.
|
||||
*
|
||||
* Returns 0 on success, -ENOENT if properties don't exist, or negative error
|
||||
* code on failure.
|
||||
*/
|
||||
int of_genpd_remove_child_ids(struct device_node *np,
|
||||
struct genpd_onecell_data *data)
|
||||
{
|
||||
struct of_phandle_args parent_args;
|
||||
struct generic_pm_domain *parent_genpd, *child_genpd;
|
||||
u32 child_id;
|
||||
int i, ret, count, child_count;
|
||||
|
||||
/* Check if both properties exist */
|
||||
count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells");
|
||||
if (count <= 0)
|
||||
return -ENOENT;
|
||||
|
||||
child_count = of_property_count_u32_elems(np, "power-domains-child-ids");
|
||||
if (child_count < 0)
|
||||
return -ENOENT;
|
||||
if (child_count != count)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (of_property_read_u32_index(np, "power-domains-child-ids",
|
||||
i, &child_id))
|
||||
continue;
|
||||
|
||||
if (child_id >= data->num_domains || !data->domains[child_id])
|
||||
continue;
|
||||
|
||||
ret = of_parse_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells", i,
|
||||
&parent_args);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
parent_genpd = genpd_get_from_provider(&parent_args);
|
||||
of_node_put(parent_args.np);
|
||||
if (IS_ERR(parent_genpd))
|
||||
continue;
|
||||
|
||||
child_genpd = data->domains[child_id];
|
||||
pm_genpd_remove_subdomain(parent_genpd, child_genpd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_genpd_remove_child_ids);
|
||||
|
||||
/**
|
||||
* of_genpd_add_device() - Add a device to an I/O PM domain
|
||||
* @genpdspec: OF phandle args to use for look-up PM domain
|
||||
|
|
|
|||
|
|
@ -467,6 +467,10 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
|
|||
int of_genpd_parse_idle_states(struct device_node *dn,
|
||||
struct genpd_power_state **states, int *n);
|
||||
void of_genpd_sync_state(struct device_node *np);
|
||||
int of_genpd_add_child_ids(struct device_node *np,
|
||||
struct genpd_onecell_data *data);
|
||||
int of_genpd_remove_child_ids(struct device_node *np,
|
||||
struct genpd_onecell_data *data);
|
||||
|
||||
int genpd_dev_pm_attach(struct device *dev);
|
||||
struct device *genpd_dev_pm_attach_by_id(struct device *dev,
|
||||
|
|
@ -536,6 +540,18 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
|
|||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline int of_genpd_add_child_ids(struct device_node *np,
|
||||
struct genpd_onecell_data *data)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int of_genpd_remove_child_ids(struct device_node *np,
|
||||
struct genpd_onecell_data *data)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user