18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rmobile power management support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Renesas Solutions Corp. 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2014 Glider bvba 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * based on pm-sh7372.c 108c2ecf20Sopenharmony_ci * Copyright (C) 2011 Magnus Damm 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <linux/clk/renesas.h> 138c2ecf20Sopenharmony_ci#include <linux/console.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_address.h> 178c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/pm.h> 208c2ecf20Sopenharmony_ci#include <linux/pm_clock.h> 218c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <asm/io.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* SYSC */ 278c2ecf20Sopenharmony_ci#define SPDCR 0x08 /* SYS Power Down Control Register */ 288c2ecf20Sopenharmony_ci#define SWUCR 0x14 /* SYS Wakeup Control Register */ 298c2ecf20Sopenharmony_ci#define PSTR 0x80 /* Power Status Register */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PSTR_RETRIES 100 328c2ecf20Sopenharmony_ci#define PSTR_DELAY_US 10 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct rmobile_pm_domain { 358c2ecf20Sopenharmony_ci struct generic_pm_domain genpd; 368c2ecf20Sopenharmony_ci struct dev_power_governor *gov; 378c2ecf20Sopenharmony_ci int (*suspend)(void); 388c2ecf20Sopenharmony_ci void __iomem *base; 398c2ecf20Sopenharmony_ci unsigned int bit_shift; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline 438c2ecf20Sopenharmony_cistruct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return container_of(d, struct rmobile_pm_domain, genpd); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int rmobile_pd_power_down(struct generic_pm_domain *genpd) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); 518c2ecf20Sopenharmony_ci unsigned int mask = BIT(rmobile_pd->bit_shift); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (rmobile_pd->suspend) { 548c2ecf20Sopenharmony_ci int ret = rmobile_pd->suspend(); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (ret) 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (__raw_readl(rmobile_pd->base + PSTR) & mask) { 618c2ecf20Sopenharmony_ci unsigned int retry_count; 628c2ecf20Sopenharmony_ci __raw_writel(mask, rmobile_pd->base + SPDCR); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci for (retry_count = PSTR_RETRIES; retry_count; retry_count--) { 658c2ecf20Sopenharmony_ci if (!(__raw_readl(rmobile_pd->base + SPDCR) & mask)) 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci cpu_relax(); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", genpd->name, mask, 728c2ecf20Sopenharmony_ci __raw_readl(rmobile_pd->base + PSTR)); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned int mask = BIT(rmobile_pd->bit_shift); 808c2ecf20Sopenharmony_ci unsigned int retry_count; 818c2ecf20Sopenharmony_ci int ret = 0; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (__raw_readl(rmobile_pd->base + PSTR) & mask) 848c2ecf20Sopenharmony_ci return ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci __raw_writel(mask, rmobile_pd->base + SWUCR); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) { 898c2ecf20Sopenharmony_ci if (!(__raw_readl(rmobile_pd->base + SWUCR) & mask)) 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci if (retry_count > PSTR_RETRIES) 928c2ecf20Sopenharmony_ci udelay(PSTR_DELAY_US); 938c2ecf20Sopenharmony_ci else 948c2ecf20Sopenharmony_ci cpu_relax(); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci if (!retry_count) 978c2ecf20Sopenharmony_ci ret = -EIO; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n", 1008c2ecf20Sopenharmony_ci rmobile_pd->genpd.name, mask, 1018c2ecf20Sopenharmony_ci __raw_readl(rmobile_pd->base + PSTR)); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int rmobile_pd_power_up(struct generic_pm_domain *genpd) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci return __rmobile_pd_power_up(to_rmobile_pd(genpd)); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct generic_pm_domain *genpd = &rmobile_pd->genpd; 1148c2ecf20Sopenharmony_ci struct dev_power_governor *gov = rmobile_pd->gov; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; 1178c2ecf20Sopenharmony_ci genpd->attach_dev = cpg_mstp_attach_dev; 1188c2ecf20Sopenharmony_ci genpd->detach_dev = cpg_mstp_detach_dev; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) { 1218c2ecf20Sopenharmony_ci genpd->power_off = rmobile_pd_power_down; 1228c2ecf20Sopenharmony_ci genpd->power_on = rmobile_pd_power_up; 1238c2ecf20Sopenharmony_ci __rmobile_pd_power_up(rmobile_pd); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int rmobile_pd_suspend_console(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * Serial consoles make use of SCIF hardware located in this domain, 1338c2ecf20Sopenharmony_ci * hence keep the power domain on if "no_console_suspend" is set. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci return console_suspend_enabled ? 0 : -EBUSY; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cienum pd_types { 1398c2ecf20Sopenharmony_ci PD_NORMAL, 1408c2ecf20Sopenharmony_ci PD_CPU, 1418c2ecf20Sopenharmony_ci PD_CONSOLE, 1428c2ecf20Sopenharmony_ci PD_DEBUG, 1438c2ecf20Sopenharmony_ci PD_MEMCTL, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define MAX_NUM_SPECIAL_PDS 16 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct special_pd { 1498c2ecf20Sopenharmony_ci struct device_node *pd; 1508c2ecf20Sopenharmony_ci enum pd_types type; 1518c2ecf20Sopenharmony_ci} special_pds[MAX_NUM_SPECIAL_PDS] __initdata; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic unsigned int num_special_pds __initdata; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic const struct of_device_id special_ids[] __initconst = { 1568c2ecf20Sopenharmony_ci { .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG }, 1578c2ecf20Sopenharmony_ci { .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, }, 1588c2ecf20Sopenharmony_ci { .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, }, 1598c2ecf20Sopenharmony_ci { .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, }, 1608c2ecf20Sopenharmony_ci { /* sentinel */ }, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void __init add_special_pd(struct device_node *np, enum pd_types type) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci unsigned int i; 1668c2ecf20Sopenharmony_ci struct device_node *pd; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci pd = of_parse_phandle(np, "power-domains", 0); 1698c2ecf20Sopenharmony_ci if (!pd) 1708c2ecf20Sopenharmony_ci return; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci for (i = 0; i < num_special_pds; i++) 1738c2ecf20Sopenharmony_ci if (pd == special_pds[i].pd && type == special_pds[i].type) { 1748c2ecf20Sopenharmony_ci of_node_put(pd); 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (num_special_pds == ARRAY_SIZE(special_pds)) { 1798c2ecf20Sopenharmony_ci pr_warn("Too many special PM domains\n"); 1808c2ecf20Sopenharmony_ci of_node_put(pd); 1818c2ecf20Sopenharmony_ci return; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci pr_debug("Special PM domain %pOFn type %d for %pOF\n", pd, type, np); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci special_pds[num_special_pds].pd = pd; 1878c2ecf20Sopenharmony_ci special_pds[num_special_pds].type = type; 1888c2ecf20Sopenharmony_ci num_special_pds++; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void __init get_special_pds(void) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct device_node *np; 1948c2ecf20Sopenharmony_ci const struct of_device_id *id; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* PM domains containing CPUs */ 1978c2ecf20Sopenharmony_ci for_each_of_cpu_node(np) 1988c2ecf20Sopenharmony_ci add_special_pd(np, PD_CPU); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* PM domain containing console */ 2018c2ecf20Sopenharmony_ci if (of_stdout) 2028c2ecf20Sopenharmony_ci add_special_pd(of_stdout, PD_CONSOLE); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* PM domains containing other special devices */ 2058c2ecf20Sopenharmony_ci for_each_matching_node_and_match(np, special_ids, &id) 2068c2ecf20Sopenharmony_ci add_special_pd(np, (enum pd_types)id->data); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void __init put_special_pds(void) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci unsigned int i; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci for (i = 0; i < num_special_pds; i++) 2148c2ecf20Sopenharmony_ci of_node_put(special_pds[i].pd); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic enum pd_types __init pd_type(const struct device_node *pd) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci unsigned int i; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci for (i = 0; i < num_special_pds; i++) 2228c2ecf20Sopenharmony_ci if (pd == special_pds[i].pd) 2238c2ecf20Sopenharmony_ci return special_pds[i].type; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return PD_NORMAL; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void __init rmobile_setup_pm_domain(struct device_node *np, 2298c2ecf20Sopenharmony_ci struct rmobile_pm_domain *pd) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci const char *name = pd->genpd.name; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci switch (pd_type(np)) { 2348c2ecf20Sopenharmony_ci case PD_CPU: 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * This domain contains the CPU core and therefore it should 2378c2ecf20Sopenharmony_ci * only be turned off if the CPU is not in use. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci pr_debug("PM domain %s contains CPU\n", name); 2408c2ecf20Sopenharmony_ci pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci case PD_CONSOLE: 2448c2ecf20Sopenharmony_ci pr_debug("PM domain %s contains serial console\n", name); 2458c2ecf20Sopenharmony_ci pd->gov = &pm_domain_always_on_gov; 2468c2ecf20Sopenharmony_ci pd->suspend = rmobile_pd_suspend_console; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci case PD_DEBUG: 2508c2ecf20Sopenharmony_ci /* 2518c2ecf20Sopenharmony_ci * This domain contains the Coresight-ETM hardware block and 2528c2ecf20Sopenharmony_ci * therefore it should only be turned off if the debug module 2538c2ecf20Sopenharmony_ci * is not in use. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci pr_debug("PM domain %s contains Coresight-ETM\n", name); 2568c2ecf20Sopenharmony_ci pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci case PD_MEMCTL: 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * This domain contains a memory-controller and therefore it 2628c2ecf20Sopenharmony_ci * should only be turned off if memory is not in use. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci pr_debug("PM domain %s contains MEMCTL\n", name); 2658c2ecf20Sopenharmony_ci pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci case PD_NORMAL: 2698c2ecf20Sopenharmony_ci if (pd->bit_shift == ~0) { 2708c2ecf20Sopenharmony_ci /* Top-level always-on domain */ 2718c2ecf20Sopenharmony_ci pr_debug("PM domain %s is always-on domain\n", name); 2728c2ecf20Sopenharmony_ci pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci rmobile_init_pm_domain(pd); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int __init rmobile_add_pm_domains(void __iomem *base, 2818c2ecf20Sopenharmony_ci struct device_node *parent, 2828c2ecf20Sopenharmony_ci struct generic_pm_domain *genpd_parent) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct device_node *np; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci for_each_child_of_node(parent, np) { 2878c2ecf20Sopenharmony_ci struct rmobile_pm_domain *pd; 2888c2ecf20Sopenharmony_ci u32 idx = ~0; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "reg", &idx)) { 2918c2ecf20Sopenharmony_ci /* always-on domain */ 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci pd = kzalloc(sizeof(*pd), GFP_KERNEL); 2958c2ecf20Sopenharmony_ci if (!pd) { 2968c2ecf20Sopenharmony_ci of_node_put(np); 2978c2ecf20Sopenharmony_ci return -ENOMEM; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci pd->genpd.name = np->name; 3018c2ecf20Sopenharmony_ci pd->base = base; 3028c2ecf20Sopenharmony_ci pd->bit_shift = idx; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci rmobile_setup_pm_domain(np, pd); 3058c2ecf20Sopenharmony_ci if (genpd_parent) 3068c2ecf20Sopenharmony_ci pm_genpd_add_subdomain(genpd_parent, &pd->genpd); 3078c2ecf20Sopenharmony_ci of_genpd_add_provider_simple(np, &pd->genpd); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci rmobile_add_pm_domains(base, np, &pd->genpd); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int __init rmobile_init_pm_domains(void) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct device_node *np, *pmd; 3178c2ecf20Sopenharmony_ci bool scanned = false; 3188c2ecf20Sopenharmony_ci void __iomem *base; 3198c2ecf20Sopenharmony_ci int ret = 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") { 3228c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 3238c2ecf20Sopenharmony_ci if (!base) { 3248c2ecf20Sopenharmony_ci pr_warn("%pOF cannot map reg 0\n", np); 3258c2ecf20Sopenharmony_ci continue; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci pmd = of_get_child_by_name(np, "pm-domains"); 3298c2ecf20Sopenharmony_ci if (!pmd) { 3308c2ecf20Sopenharmony_ci iounmap(base); 3318c2ecf20Sopenharmony_ci pr_warn("%pOF lacks pm-domains node\n", np); 3328c2ecf20Sopenharmony_ci continue; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (!scanned) { 3368c2ecf20Sopenharmony_ci /* Find PM domains containing special blocks */ 3378c2ecf20Sopenharmony_ci get_special_pds(); 3388c2ecf20Sopenharmony_ci scanned = true; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = rmobile_add_pm_domains(base, pmd, NULL); 3428c2ecf20Sopenharmony_ci of_node_put(pmd); 3438c2ecf20Sopenharmony_ci if (ret) { 3448c2ecf20Sopenharmony_ci of_node_put(np); 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci put_special_pds(); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cicore_initcall(rmobile_init_pm_domains); 355