Message ID | 20250528-pmdomain-hierarchy-onecell-v2-2-7885ae45e59c@baylibre.com |
---|---|
State | New |
Headers | show |
Series | pmdomains: core: add support for domain hierarchies in DT | expand |
On Wed, May 28, 2025 at 02:58:52PM -0700, Kevin Hilman wrote: > 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 support for a nexus > node map, as described in section 2.5.1 of the DT spec. > > For example, an SCMI PM domain provider might be a subdomain of > multiple 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-domain-map = <15 &MAIN_PD>, > <19 &WKUP_PD>; > }; > > With the new map, child domain 15 (scmi_pds 15) becomes a > subdomain of MAIN_PD, and child domain 19 (scmi_pds 19) becomes a > subdomain of WKUP_PD. > > Signed-off-by: Kevin Hilman <khilman@baylibre.com> > --- > drivers/pmdomain/core.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 166 insertions(+) > > diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c > index d6c1ddb807b2..b8e505516f3d 100644 > --- a/drivers/pmdomain/core.c > +++ b/drivers/pmdomain/core.c > @@ -2441,6 +2441,9 @@ static LIST_HEAD(of_genpd_providers); > /* Mutex to protect the list above. */ > static DEFINE_MUTEX(of_genpd_mutex); > > +static int of_genpd_parse_domains_map(struct device_node *np, > + struct genpd_onecell_data *data); > + > /** > * genpd_xlate_simple() - Xlate function for direct node-domain mapping > * @genpdspec: OF phandle args to map into a PM domain > @@ -2635,6 +2638,14 @@ int of_genpd_add_provider_onecell(struct device_node *np, > if (ret < 0) > goto error; > > + /* Parse power-domains-map property for Nexus node mapping */ > + ret = of_genpd_parse_domains_map(np, data); > + if (ret < 0 && ret != -ENOENT) { > + pr_err("Failed to parse power-domains-map for %pOF: %d\n", np, ret); > + of_genpd_del_provider(np); > + goto error; > + } > + > return 0; > > error: > @@ -2734,6 +2745,161 @@ static struct generic_pm_domain *genpd_get_from_provider( > return genpd; > } > > +/** > + * of_genpd_parse_domains_map() - Parse power-domains-map property for Nexus mapping > + * @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-map property to establish parent-child relationships > + * for PM domains using Nexus node mapping as defined in the device tree > + * specification section v2.5.1. > + * > + * The power-domains-map property format is: > + * power-domains-map = <child_specifier target_phandle [target_specifier]>, ...; > + * > + * Where: > + * - child_specifier: The child domain ID that should be mapped > + * - target_phandle: Phandle to the parent PM domain provider > + * - target_specifier: Optional arguments for the parent provider (if it has #power-domain-cells > 0) > + * > + * Returns 0 on success, -ENOENT if property doesn't exist, or negative error code. > + */ > +static int of_genpd_parse_domains_map(struct device_node *np, > + struct genpd_onecell_data *data) > +{ > + struct of_phandle_args parent_args; > + struct generic_pm_domain *parent_genpd, *child_genpd; > + u32 *map_entries; > + int map_len, child_cells, i, ret; > + u32 child_id; > + > + /* Check if power-domains-map property exists */ > + map_len = of_property_count_u32_elems(np, "power-domains-map"); > + if (map_len <= 0) > + return -ENOENT; Don't implement your own map parsing. Use or extend of_parse_phandle_with_args_map(). Rob
On Wed, 28 May 2025 at 23:59, Kevin Hilman <khilman@baylibre.com> wrote: > > 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 support for a nexus > node map, as described in section 2.5.1 of the DT spec. > > For example, an SCMI PM domain provider might be a subdomain of > multiple 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-domain-map = <15 &MAIN_PD>, > <19 &WKUP_PD>; > }; > > With the new map, child domain 15 (scmi_pds 15) becomes a > subdomain of MAIN_PD, and child domain 19 (scmi_pds 19) becomes a > subdomain of WKUP_PD. > > Signed-off-by: Kevin Hilman <khilman@baylibre.com> > --- > drivers/pmdomain/core.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 166 insertions(+) > > diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c > index d6c1ddb807b2..b8e505516f3d 100644 > --- a/drivers/pmdomain/core.c > +++ b/drivers/pmdomain/core.c > @@ -2441,6 +2441,9 @@ static LIST_HEAD(of_genpd_providers); > /* Mutex to protect the list above. */ > static DEFINE_MUTEX(of_genpd_mutex); > > +static int of_genpd_parse_domains_map(struct device_node *np, > + struct genpd_onecell_data *data); > + > /** > * genpd_xlate_simple() - Xlate function for direct node-domain mapping > * @genpdspec: OF phandle args to map into a PM domain > @@ -2635,6 +2638,14 @@ int of_genpd_add_provider_onecell(struct device_node *np, > if (ret < 0) > goto error; > > + /* Parse power-domains-map property for Nexus node mapping */ > + ret = of_genpd_parse_domains_map(np, data); > + if (ret < 0 && ret != -ENOENT) { > + pr_err("Failed to parse power-domains-map for %pOF: %d\n", np, ret); > + of_genpd_del_provider(np); > + goto error; > + } To be consistent with the current way for how we usually add parent/child-domains in genpd (see of_genpd_add|remove_subdomain), I suggest we keep this part being specific to each genpd provider driver, rather than calling this from of_genpd_add_provider_onecell(). That said, I don't mind if we need to export another genpd helper function along the lines of of_genpd_parse_domains_map() as below. Also don't forget to consider if there is a corresponding "cleanup" helper function needed. [...] Kind regards Uffe
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index d6c1ddb807b2..b8e505516f3d 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -2441,6 +2441,9 @@ static LIST_HEAD(of_genpd_providers); /* Mutex to protect the list above. */ static DEFINE_MUTEX(of_genpd_mutex); +static int of_genpd_parse_domains_map(struct device_node *np, + struct genpd_onecell_data *data); + /** * genpd_xlate_simple() - Xlate function for direct node-domain mapping * @genpdspec: OF phandle args to map into a PM domain @@ -2635,6 +2638,14 @@ int of_genpd_add_provider_onecell(struct device_node *np, if (ret < 0) goto error; + /* Parse power-domains-map property for Nexus node mapping */ + ret = of_genpd_parse_domains_map(np, data); + if (ret < 0 && ret != -ENOENT) { + pr_err("Failed to parse power-domains-map for %pOF: %d\n", np, ret); + of_genpd_del_provider(np); + goto error; + } + return 0; error: @@ -2734,6 +2745,161 @@ static struct generic_pm_domain *genpd_get_from_provider( return genpd; } +/** + * of_genpd_parse_domains_map() - Parse power-domains-map property for Nexus mapping + * @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-map property to establish parent-child relationships + * for PM domains using Nexus node mapping as defined in the device tree + * specification section v2.5.1. + * + * The power-domains-map property format is: + * power-domains-map = <child_specifier target_phandle [target_specifier]>, ...; + * + * Where: + * - child_specifier: The child domain ID that should be mapped + * - target_phandle: Phandle to the parent PM domain provider + * - target_specifier: Optional arguments for the parent provider (if it has #power-domain-cells > 0) + * + * Returns 0 on success, -ENOENT if property doesn't exist, or negative error code. + */ +static int of_genpd_parse_domains_map(struct device_node *np, + struct genpd_onecell_data *data) +{ + struct of_phandle_args parent_args; + struct generic_pm_domain *parent_genpd, *child_genpd; + u32 *map_entries; + int map_len, child_cells, i, ret; + u32 child_id; + + /* Check if power-domains-map property exists */ + map_len = of_property_count_u32_elems(np, "power-domains-map"); + if (map_len <= 0) + return -ENOENT; + + /* Get #power-domain-cells from this node (should be 1 for onecell) */ + ret = of_property_read_u32(np, "#power-domain-cells", &child_cells); + if (ret) { + pr_err("Missing #power-domain-cells property for %pOF\n", np); + return ret; + } + + if (child_cells != 1) { + pr_err("power-domains-map only supported for #power-domain-cells = 1, got %u for %pOF\n", + child_cells, np); + return -EINVAL; + } + + map_entries = kcalloc(map_len, sizeof(*map_entries), GFP_KERNEL); + if (!map_entries) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "power-domains-map", map_entries, map_len); + if (ret) { + pr_err("Failed to read power-domains-map for %pOF: %d\n", np, ret); + goto out_free; + } + + /* Parse the map entries */ + i = 0; + while (i < map_len) { + struct device_node *parent_np; + u32 parent_cells; + + /* Extract child domain ID */ + if (i >= map_len) { + pr_err("Incomplete power-domains-map entry at index %d for %pOF\n", i, np); + ret = -EINVAL; + goto out_free; + } + child_id = map_entries[i++]; + + /* Extract parent phandle */ + if (i >= map_len) { + pr_err("Missing parent phandle in power-domains-map at index %d for %pOF\n", i, np); + ret = -EINVAL; + goto out_free; + } + parent_np = of_find_node_by_phandle(map_entries[i++]); + if (!parent_np) { + pr_err("Invalid parent phandle 0x%x in power-domains-map for %pOF\n", + map_entries[i-1], np); + ret = -EINVAL; + goto out_free; + } + + /* Get #power-domain-cells from parent */ + ret = of_property_read_u32(parent_np, "#power-domain-cells", &parent_cells); + if (ret) { + pr_err("Missing #power-domain-cells in parent %pOF for %pOF\n", parent_np, np); + of_node_put(parent_np); + goto out_free; + } + + /* Build parent_args structure */ + parent_args.np = parent_np; + parent_args.args_count = parent_cells; + + /* Extract parent specifier arguments if any */ + if (parent_cells > 0) { + if (i + parent_cells > map_len) { + pr_err("Insufficient parent specifier args in power-domains-map for %pOF\n", np); + of_node_put(parent_np); + ret = -EINVAL; + goto out_free; + } + memcpy(parent_args.args, &map_entries[i], parent_cells * sizeof(u32)); + i += parent_cells; + } + + /* Validate child ID is within bounds */ + if (child_id >= data->num_domains) { + pr_err("Child ID %u out of bounds (max %u) in power-domains-map for %pOF\n", + child_id, data->num_domains - 1, np); + of_node_put(parent_np); + ret = -EINVAL; + goto out_free; + } + + /* Get the child domain */ + child_genpd = data->domains[child_id]; + if (!child_genpd) { + pr_err("Child domain %u is NULL in power-domains-map for %pOF\n", child_id, np); + of_node_put(parent_np); + ret = -EINVAL; + goto out_free; + } + + /* Get the parent domain */ + parent_genpd = genpd_get_from_provider(&parent_args); + of_node_put(parent_np); + if (IS_ERR(parent_genpd)) { + pr_err("Failed to get parent domain for child %u in power-domains-map for %pOF: %ld\n", + child_id, np, PTR_ERR(parent_genpd)); + ret = PTR_ERR(parent_genpd); + goto out_free; + } + + /* Establish parent-child relationship */ + ret = genpd_add_subdomain(parent_genpd, child_genpd); + if (ret) { + pr_err("Failed to add child domain %u to parent in power-domains-map for %pOF: %d\n", + child_id, np, ret); + goto out_free; + } + + pr_debug("Added child domain %u (%s) to parent %s via power-domains-map for %pOF\n", + child_id, child_genpd->name, parent_genpd->name, np); + } + + ret = 0; + +out_free: + kfree(map_entries); + return ret; +} + /** * of_genpd_add_device() - Add a device to an I/O PM domain * @genpdspec: OF phandle args to use for look-up PM domain
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 support for a nexus node map, as described in section 2.5.1 of the DT spec. For example, an SCMI PM domain provider might be a subdomain of multiple 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-domain-map = <15 &MAIN_PD>, <19 &WKUP_PD>; }; With the new map, child domain 15 (scmi_pds 15) becomes a subdomain of MAIN_PD, and child domain 19 (scmi_pds 19) becomes a subdomain of WKUP_PD. Signed-off-by: Kevin Hilman <khilman@baylibre.com> --- drivers/pmdomain/core.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+)