18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/of.h> 78c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 88c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/version.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <soc/tegra/bpmp.h> 138c2ecf20Sopenharmony_ci#include <soc/tegra/bpmp-abi.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct tegra_powergate_info { 168c2ecf20Sopenharmony_ci unsigned int id; 178c2ecf20Sopenharmony_ci char *name; 188c2ecf20Sopenharmony_ci}; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct tegra_powergate { 218c2ecf20Sopenharmony_ci struct generic_pm_domain genpd; 228c2ecf20Sopenharmony_ci struct tegra_bpmp *bpmp; 238c2ecf20Sopenharmony_ci unsigned int id; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline struct tegra_powergate * 278c2ecf20Sopenharmony_cito_tegra_powergate(struct generic_pm_domain *genpd) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci return container_of(genpd, struct tegra_powergate, genpd); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp, 338c2ecf20Sopenharmony_ci unsigned int id, u32 state) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct mrq_pg_request request; 368c2ecf20Sopenharmony_ci struct tegra_bpmp_message msg; 378c2ecf20Sopenharmony_ci int err; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 408c2ecf20Sopenharmony_ci request.cmd = CMD_PG_SET_STATE; 418c2ecf20Sopenharmony_ci request.id = id; 428c2ecf20Sopenharmony_ci request.set_state.state = state; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 458c2ecf20Sopenharmony_ci msg.mrq = MRQ_PG; 468c2ecf20Sopenharmony_ci msg.tx.data = &request; 478c2ecf20Sopenharmony_ci msg.tx.size = sizeof(request); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci err = tegra_bpmp_transfer(bpmp, &msg); 508c2ecf20Sopenharmony_ci if (err < 0) 518c2ecf20Sopenharmony_ci return err; 528c2ecf20Sopenharmony_ci else if (msg.rx.ret < 0) 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp, 598c2ecf20Sopenharmony_ci unsigned int id) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct mrq_pg_response response; 628c2ecf20Sopenharmony_ci struct mrq_pg_request request; 638c2ecf20Sopenharmony_ci struct tegra_bpmp_message msg; 648c2ecf20Sopenharmony_ci int err; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 678c2ecf20Sopenharmony_ci request.cmd = CMD_PG_GET_STATE; 688c2ecf20Sopenharmony_ci request.id = id; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci memset(&response, 0, sizeof(response)); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 738c2ecf20Sopenharmony_ci msg.mrq = MRQ_PG; 748c2ecf20Sopenharmony_ci msg.tx.data = &request; 758c2ecf20Sopenharmony_ci msg.tx.size = sizeof(request); 768c2ecf20Sopenharmony_ci msg.rx.data = &response; 778c2ecf20Sopenharmony_ci msg.rx.size = sizeof(response); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci err = tegra_bpmp_transfer(bpmp, &msg); 808c2ecf20Sopenharmony_ci if (err < 0) 818c2ecf20Sopenharmony_ci return PG_STATE_OFF; 828c2ecf20Sopenharmony_ci else if (msg.rx.ret < 0) 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return response.get_state.state; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct mrq_pg_response response; 918c2ecf20Sopenharmony_ci struct mrq_pg_request request; 928c2ecf20Sopenharmony_ci struct tegra_bpmp_message msg; 938c2ecf20Sopenharmony_ci int err; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 968c2ecf20Sopenharmony_ci request.cmd = CMD_PG_GET_MAX_ID; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci memset(&response, 0, sizeof(response)); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 1018c2ecf20Sopenharmony_ci msg.mrq = MRQ_PG; 1028c2ecf20Sopenharmony_ci msg.tx.data = &request; 1038c2ecf20Sopenharmony_ci msg.tx.size = sizeof(request); 1048c2ecf20Sopenharmony_ci msg.rx.data = &response; 1058c2ecf20Sopenharmony_ci msg.rx.size = sizeof(response); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci err = tegra_bpmp_transfer(bpmp, &msg); 1088c2ecf20Sopenharmony_ci if (err < 0) 1098c2ecf20Sopenharmony_ci return err; 1108c2ecf20Sopenharmony_ci else if (msg.rx.ret < 0) 1118c2ecf20Sopenharmony_ci return -EINVAL; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return response.get_max_id.max_id; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp, 1178c2ecf20Sopenharmony_ci unsigned int id) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct mrq_pg_response response; 1208c2ecf20Sopenharmony_ci struct mrq_pg_request request; 1218c2ecf20Sopenharmony_ci struct tegra_bpmp_message msg; 1228c2ecf20Sopenharmony_ci int err; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci memset(&request, 0, sizeof(request)); 1258c2ecf20Sopenharmony_ci request.cmd = CMD_PG_GET_NAME; 1268c2ecf20Sopenharmony_ci request.id = id; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci memset(&response, 0, sizeof(response)); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 1318c2ecf20Sopenharmony_ci msg.mrq = MRQ_PG; 1328c2ecf20Sopenharmony_ci msg.tx.data = &request; 1338c2ecf20Sopenharmony_ci msg.tx.size = sizeof(request); 1348c2ecf20Sopenharmony_ci msg.rx.data = &response; 1358c2ecf20Sopenharmony_ci msg.rx.size = sizeof(response); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = tegra_bpmp_transfer(bpmp, &msg); 1388c2ecf20Sopenharmony_ci if (err < 0 || msg.rx.ret < 0) 1398c2ecf20Sopenharmony_ci return NULL; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return kstrdup(response.get_name.name, GFP_KERNEL); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp, 1458c2ecf20Sopenharmony_ci unsigned int id) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int tegra_powergate_power_on(struct generic_pm_domain *domain) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct tegra_powergate *powergate = to_tegra_powergate(domain); 1538c2ecf20Sopenharmony_ci struct tegra_bpmp *bpmp = powergate->bpmp; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return tegra_bpmp_powergate_set_state(bpmp, powergate->id, 1568c2ecf20Sopenharmony_ci PG_STATE_ON); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int tegra_powergate_power_off(struct generic_pm_domain *domain) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct tegra_powergate *powergate = to_tegra_powergate(domain); 1628c2ecf20Sopenharmony_ci struct tegra_bpmp *bpmp = powergate->bpmp; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return tegra_bpmp_powergate_set_state(bpmp, powergate->id, 1658c2ecf20Sopenharmony_ci PG_STATE_OFF); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic struct tegra_powergate * 1698c2ecf20Sopenharmony_citegra_powergate_add(struct tegra_bpmp *bpmp, 1708c2ecf20Sopenharmony_ci const struct tegra_powergate_info *info) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct tegra_powergate *powergate; 1738c2ecf20Sopenharmony_ci bool off; 1748c2ecf20Sopenharmony_ci int err; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci off = !tegra_bpmp_powergate_is_powered(bpmp, info->id); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL); 1798c2ecf20Sopenharmony_ci if (!powergate) 1808c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci powergate->id = info->id; 1838c2ecf20Sopenharmony_ci powergate->bpmp = bpmp; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci powergate->genpd.name = kstrdup(info->name, GFP_KERNEL); 1868c2ecf20Sopenharmony_ci powergate->genpd.power_on = tegra_powergate_power_on; 1878c2ecf20Sopenharmony_ci powergate->genpd.power_off = tegra_powergate_power_off; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci err = pm_genpd_init(&powergate->genpd, NULL, off); 1908c2ecf20Sopenharmony_ci if (err < 0) { 1918c2ecf20Sopenharmony_ci kfree(powergate->genpd.name); 1928c2ecf20Sopenharmony_ci return ERR_PTR(err); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return powergate; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void tegra_powergate_remove(struct tegra_powergate *powergate) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct generic_pm_domain *genpd = &powergate->genpd; 2018c2ecf20Sopenharmony_ci struct tegra_bpmp *bpmp = powergate->bpmp; 2028c2ecf20Sopenharmony_ci int err; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci err = pm_genpd_remove(genpd); 2058c2ecf20Sopenharmony_ci if (err < 0) 2068c2ecf20Sopenharmony_ci dev_err(bpmp->dev, "failed to remove power domain %s: %d\n", 2078c2ecf20Sopenharmony_ci genpd->name, err); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci kfree(genpd->name); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int 2138c2ecf20Sopenharmony_citegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp, 2148c2ecf20Sopenharmony_ci struct tegra_powergate_info **powergatesp) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct tegra_powergate_info *powergates; 2178c2ecf20Sopenharmony_ci unsigned int max_id, id, count = 0; 2188c2ecf20Sopenharmony_ci unsigned int num_holes = 0; 2198c2ecf20Sopenharmony_ci int err; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci err = tegra_bpmp_powergate_get_max_id(bpmp); 2228c2ecf20Sopenharmony_ci if (err < 0) 2238c2ecf20Sopenharmony_ci return err; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci max_id = err; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL); 2308c2ecf20Sopenharmony_ci if (!powergates) 2318c2ecf20Sopenharmony_ci return -ENOMEM; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci for (id = 0; id <= max_id; id++) { 2348c2ecf20Sopenharmony_ci struct tegra_powergate_info *info = &powergates[count]; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci info->name = tegra_bpmp_powergate_get_name(bpmp, id); 2378c2ecf20Sopenharmony_ci if (!info->name || info->name[0] == '\0') { 2388c2ecf20Sopenharmony_ci num_holes++; 2398c2ecf20Sopenharmony_ci continue; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci info->id = id; 2438c2ecf20Sopenharmony_ci count++; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci dev_dbg(bpmp->dev, "holes: %u\n", num_holes); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci *powergatesp = powergates; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return count; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp, 2548c2ecf20Sopenharmony_ci struct tegra_powergate_info *powergates, 2558c2ecf20Sopenharmony_ci unsigned int count) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct genpd_onecell_data *genpd = &bpmp->genpd; 2588c2ecf20Sopenharmony_ci struct generic_pm_domain **domains; 2598c2ecf20Sopenharmony_ci struct tegra_powergate *powergate; 2608c2ecf20Sopenharmony_ci unsigned int i; 2618c2ecf20Sopenharmony_ci int err; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci domains = kcalloc(count, sizeof(*domains), GFP_KERNEL); 2648c2ecf20Sopenharmony_ci if (!domains) 2658c2ecf20Sopenharmony_ci return -ENOMEM; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2688c2ecf20Sopenharmony_ci powergate = tegra_powergate_add(bpmp, &powergates[i]); 2698c2ecf20Sopenharmony_ci if (IS_ERR(powergate)) { 2708c2ecf20Sopenharmony_ci err = PTR_ERR(powergate); 2718c2ecf20Sopenharmony_ci goto remove; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci dev_dbg(bpmp->dev, "added power domain %s\n", 2758c2ecf20Sopenharmony_ci powergate->genpd.name); 2768c2ecf20Sopenharmony_ci domains[i] = &powergate->genpd; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci genpd->num_domains = count; 2808c2ecf20Sopenharmony_ci genpd->domains = domains; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciremove: 2858c2ecf20Sopenharmony_ci while (i--) { 2868c2ecf20Sopenharmony_ci powergate = to_tegra_powergate(domains[i]); 2878c2ecf20Sopenharmony_ci tegra_powergate_remove(powergate); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci kfree(genpd->domains); 2918c2ecf20Sopenharmony_ci return err; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct genpd_onecell_data *genpd = &bpmp->genpd; 2978c2ecf20Sopenharmony_ci unsigned int i = genpd->num_domains; 2988c2ecf20Sopenharmony_ci struct tegra_powergate *powergate; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci while (i--) { 3018c2ecf20Sopenharmony_ci dev_dbg(bpmp->dev, "removing power domain %s\n", 3028c2ecf20Sopenharmony_ci genpd->domains[i]->name); 3038c2ecf20Sopenharmony_ci powergate = to_tegra_powergate(genpd->domains[i]); 3048c2ecf20Sopenharmony_ci tegra_powergate_remove(powergate); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic struct generic_pm_domain * 3098c2ecf20Sopenharmony_citegra_powergate_xlate(struct of_phandle_args *spec, void *data) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct generic_pm_domain *domain = ERR_PTR(-ENOENT); 3128c2ecf20Sopenharmony_ci struct genpd_onecell_data *genpd = data; 3138c2ecf20Sopenharmony_ci unsigned int i; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci for (i = 0; i < genpd->num_domains; i++) { 3168c2ecf20Sopenharmony_ci struct tegra_powergate *powergate; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci powergate = to_tegra_powergate(genpd->domains[i]); 3198c2ecf20Sopenharmony_ci if (powergate->id == spec->args[0]) { 3208c2ecf20Sopenharmony_ci domain = &powergate->genpd; 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return domain; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ciint tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct device_node *np = bpmp->dev->of_node; 3318c2ecf20Sopenharmony_ci struct tegra_powergate_info *powergates; 3328c2ecf20Sopenharmony_ci struct device *dev = bpmp->dev; 3338c2ecf20Sopenharmony_ci unsigned int count, i; 3348c2ecf20Sopenharmony_ci int err; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci err = tegra_bpmp_probe_powergates(bpmp, &powergates); 3378c2ecf20Sopenharmony_ci if (err < 0) 3388c2ecf20Sopenharmony_ci return err; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci count = err; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci dev_dbg(dev, "%u power domains probed\n", count); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci err = tegra_bpmp_add_powergates(bpmp, powergates, count); 3458c2ecf20Sopenharmony_ci if (err < 0) 3468c2ecf20Sopenharmony_ci goto free; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci bpmp->genpd.xlate = tegra_powergate_xlate; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci err = of_genpd_add_provider_onecell(np, &bpmp->genpd); 3518c2ecf20Sopenharmony_ci if (err < 0) { 3528c2ecf20Sopenharmony_ci dev_err(dev, "failed to add power domain provider: %d\n", err); 3538c2ecf20Sopenharmony_ci tegra_bpmp_remove_powergates(bpmp); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cifree: 3578c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 3588c2ecf20Sopenharmony_ci kfree(powergates[i].name); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci kfree(powergates); 3618c2ecf20Sopenharmony_ci return err; 3628c2ecf20Sopenharmony_ci} 363