diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c index f0a6339affd7..8d49d9ee3cd2 100644 --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -750,7 +750,8 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) } static struct -generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node) +generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node, + u8 *domains_idx, u8 *num_domains) { const struct scpsys_domain_data *domain_data; const struct scpsys_hwv_domain_data *hwv_domain_data; @@ -925,6 +926,7 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no else pm_genpd_init(&pd->genpd, NULL, false); + domains_idx[(*num_domains)++] = (u8) id; scpsys->domains[id] = &pd->genpd; return scpsys->pd_data.domains[id]; @@ -936,7 +938,8 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no return ERR_PTR(ret); } -static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *parent) +static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *parent, + u8 *domains_idx, u8 *num_domains) { struct generic_pm_domain *child_pd, *parent_pd; struct device_node *child; @@ -959,7 +962,7 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren parent_pd = scpsys->pd_data.domains[id]; - child_pd = scpsys_add_one_domain(scpsys, child); + child_pd = scpsys_add_one_domain(scpsys, child, domains_idx, num_domains); if (IS_ERR(child_pd)) { ret = PTR_ERR(child_pd); dev_err_probe(scpsys->dev, ret, "%pOF: failed to get child domain id\n", @@ -968,7 +971,7 @@ static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *paren } /* recursive call to add all subdomains */ - ret = scpsys_add_subdomain(scpsys, child); + ret = scpsys_add_subdomain(scpsys, child, domains_idx, num_domains); if (ret) goto err_put_node; @@ -1017,14 +1020,16 @@ static void scpsys_remove_one_domain(struct scpsys_domain *pd) clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); } -static void scpsys_domain_cleanup(struct scpsys *scpsys) +static void scpsys_domain_cleanup(struct scpsys *scpsys, u8 *domains_idx, u8 num_probed) { struct generic_pm_domain *genpd; struct scpsys_domain *pd; int i; - for (i = scpsys->pd_data.num_domains - 1; i >= 0; i--) { - genpd = scpsys->pd_data.domains[i]; + for (i = num_probed - 1; i >= 0; i--) { + u8 pd_idx = domains_idx[i]; + + genpd = scpsys->pd_data.domains[pd_idx]; if (genpd) { pd = to_scpsys_domain(genpd); scpsys_remove_one_domain(pd); @@ -1241,6 +1246,8 @@ static int scpsys_probe(struct platform_device *pdev) struct device *parent; struct scpsys *scpsys; int num_domains, ret; + u8 num_added_pds = 0; + u8 *added_pds_idx; soc = of_device_get_match_data(&pdev->dev); if (!soc) { @@ -1254,6 +1261,19 @@ static int scpsys_probe(struct platform_device *pdev) if (!scpsys) return -ENOMEM; + /* + * Temporarily store the IDs of the power domains that are added as in + * case of a probe deferral this can be used to correctly cleanup all + * of what was added before. + * + * Note that this array is used only in the probe function and must be + * freed at the end, regardless of whether all of the power domains were + * probed successfully or any failure happened. + */ + added_pds_idx = devm_kmalloc_array(dev, num_domains, sizeof(*added_pds_idx), GFP_KERNEL); + if (!added_pds_idx) + return -ENOMEM; + scpsys->dev = dev; scpsys->soc_data = soc; @@ -1284,13 +1304,15 @@ static int scpsys_probe(struct platform_device *pdev) for_each_available_child_of_node_scoped(np, node) { struct generic_pm_domain *domain; - domain = scpsys_add_one_domain(scpsys, node); + domain = scpsys_add_one_domain(scpsys, node, + added_pds_idx, &num_added_pds); if (IS_ERR(domain)) { ret = PTR_ERR(domain); goto err_cleanup_domains; } - ret = scpsys_add_subdomain(scpsys, node); + ret = scpsys_add_subdomain(scpsys, node, + added_pds_idx, &num_added_pds); if (ret) goto err_cleanup_domains; } @@ -1306,10 +1328,11 @@ static int scpsys_probe(struct platform_device *pdev) goto err_cleanup_domains; } + devm_kfree(dev, added_pds_idx); return 0; err_cleanup_domains: - scpsys_domain_cleanup(scpsys); + scpsys_domain_cleanup(scpsys, added_pds_idx, num_added_pds); return ret; }