1a5e0454cSUlf Hansson // SPDX-License-Identifier: GPL-2.0 2a5e0454cSUlf Hansson /* 3a5e0454cSUlf Hansson * PM domains for CPUs via genpd - managed by cpuidle-psci. 4a5e0454cSUlf Hansson * 5a5e0454cSUlf Hansson * Copyright (C) 2019 Linaro Ltd. 6a5e0454cSUlf Hansson * Author: Ulf Hansson <ulf.hansson@linaro.org> 7a5e0454cSUlf Hansson * 8a5e0454cSUlf Hansson */ 9a5e0454cSUlf Hansson 10a65a397fSUlf Hansson #define pr_fmt(fmt) "CPUidle PSCI: " fmt 11a65a397fSUlf Hansson 12a5e0454cSUlf Hansson #include <linux/cpu.h> 13a5e0454cSUlf Hansson #include <linux/device.h> 14a5e0454cSUlf Hansson #include <linux/kernel.h> 15ee7c34caSUlf Hansson #include <linux/platform_device.h> 16a5e0454cSUlf Hansson #include <linux/pm_domain.h> 17a5e0454cSUlf Hansson #include <linux/pm_runtime.h> 18a65a397fSUlf Hansson #include <linux/psci.h> 19a65a397fSUlf Hansson #include <linux/slab.h> 20a65a397fSUlf Hansson #include <linux/string.h> 21a5e0454cSUlf Hansson 22a5e0454cSUlf Hansson #include "cpuidle-psci.h" 23a5e0454cSUlf Hansson 24a65a397fSUlf Hansson struct psci_pd_provider { 25a65a397fSUlf Hansson struct list_head link; 26a65a397fSUlf Hansson struct device_node *node; 27a65a397fSUlf Hansson }; 28a65a397fSUlf Hansson 29a65a397fSUlf Hansson static LIST_HEAD(psci_pd_providers); 3081f94ddfSUlf Hansson static bool psci_pd_allow_domain_state; 31a65a397fSUlf Hansson 32a65a397fSUlf Hansson static int psci_pd_power_off(struct generic_pm_domain *pd) 33a65a397fSUlf Hansson { 34a65a397fSUlf Hansson struct genpd_power_state *state = &pd->states[pd->state_idx]; 35a65a397fSUlf Hansson u32 *pd_state; 36a65a397fSUlf Hansson 37a65a397fSUlf Hansson if (!state->data) 38a65a397fSUlf Hansson return 0; 39a65a397fSUlf Hansson 4081f94ddfSUlf Hansson if (!psci_pd_allow_domain_state) 4181f94ddfSUlf Hansson return -EBUSY; 4281f94ddfSUlf Hansson 43a65a397fSUlf Hansson /* OSI mode is enabled, set the corresponding domain state. */ 44a65a397fSUlf Hansson pd_state = state->data; 45a65a397fSUlf Hansson psci_set_domain_state(*pd_state); 46a65a397fSUlf Hansson 47a65a397fSUlf Hansson return 0; 48a65a397fSUlf Hansson } 49a65a397fSUlf Hansson 50ee7c34caSUlf Hansson static int psci_pd_parse_state_nodes(struct genpd_power_state *states, 51a65a397fSUlf Hansson int state_count) 52a65a397fSUlf Hansson { 53a65a397fSUlf Hansson int i, ret; 54a65a397fSUlf Hansson u32 psci_state, *psci_state_buf; 55a65a397fSUlf Hansson 56a65a397fSUlf Hansson for (i = 0; i < state_count; i++) { 57a65a397fSUlf Hansson ret = psci_dt_parse_state_node(to_of_node(states[i].fwnode), 58a65a397fSUlf Hansson &psci_state); 59a65a397fSUlf Hansson if (ret) 60a65a397fSUlf Hansson goto free_state; 61a65a397fSUlf Hansson 62a65a397fSUlf Hansson psci_state_buf = kmalloc(sizeof(u32), GFP_KERNEL); 63a65a397fSUlf Hansson if (!psci_state_buf) { 64a65a397fSUlf Hansson ret = -ENOMEM; 65a65a397fSUlf Hansson goto free_state; 66a65a397fSUlf Hansson } 67a65a397fSUlf Hansson *psci_state_buf = psci_state; 68a65a397fSUlf Hansson states[i].data = psci_state_buf; 69a65a397fSUlf Hansson } 70a65a397fSUlf Hansson 71a65a397fSUlf Hansson return 0; 72a65a397fSUlf Hansson 73a65a397fSUlf Hansson free_state: 74a65a397fSUlf Hansson i--; 75a65a397fSUlf Hansson for (; i >= 0; i--) 76a65a397fSUlf Hansson kfree(states[i].data); 77a65a397fSUlf Hansson return ret; 78a65a397fSUlf Hansson } 79a65a397fSUlf Hansson 80ee7c34caSUlf Hansson static int psci_pd_parse_states(struct device_node *np, 81a65a397fSUlf Hansson struct genpd_power_state **states, int *state_count) 82a65a397fSUlf Hansson { 83a65a397fSUlf Hansson int ret; 84a65a397fSUlf Hansson 85a65a397fSUlf Hansson /* Parse the domain idle states. */ 86a65a397fSUlf Hansson ret = of_genpd_parse_idle_states(np, states, state_count); 87a65a397fSUlf Hansson if (ret) 88a65a397fSUlf Hansson return ret; 89a65a397fSUlf Hansson 90a65a397fSUlf Hansson /* Fill out the PSCI specifics for each found state. */ 91a65a397fSUlf Hansson ret = psci_pd_parse_state_nodes(*states, *state_count); 92a65a397fSUlf Hansson if (ret) 93a65a397fSUlf Hansson kfree(*states); 94a65a397fSUlf Hansson 95a65a397fSUlf Hansson return ret; 96a65a397fSUlf Hansson } 97a65a397fSUlf Hansson 98a65a397fSUlf Hansson static void psci_pd_free_states(struct genpd_power_state *states, 99a65a397fSUlf Hansson unsigned int state_count) 100a65a397fSUlf Hansson { 101a65a397fSUlf Hansson int i; 102a65a397fSUlf Hansson 103a65a397fSUlf Hansson for (i = 0; i < state_count; i++) 104a65a397fSUlf Hansson kfree(states[i].data); 105a65a397fSUlf Hansson kfree(states); 106a65a397fSUlf Hansson } 107a65a397fSUlf Hansson 10870c179b4SUlf Hansson static int psci_pd_init(struct device_node *np, bool use_osi) 109a65a397fSUlf Hansson { 110a65a397fSUlf Hansson struct generic_pm_domain *pd; 111a65a397fSUlf Hansson struct psci_pd_provider *pd_provider; 112a65a397fSUlf Hansson struct dev_power_governor *pd_gov; 113a65a397fSUlf Hansson struct genpd_power_state *states = NULL; 114a65a397fSUlf Hansson int ret = -ENOMEM, state_count = 0; 115a65a397fSUlf Hansson 116a65a397fSUlf Hansson pd = kzalloc(sizeof(*pd), GFP_KERNEL); 117a65a397fSUlf Hansson if (!pd) 118a65a397fSUlf Hansson goto out; 119a65a397fSUlf Hansson 120a65a397fSUlf Hansson pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL); 121a65a397fSUlf Hansson if (!pd_provider) 122a65a397fSUlf Hansson goto free_pd; 123a65a397fSUlf Hansson 124a65a397fSUlf Hansson pd->name = kasprintf(GFP_KERNEL, "%pOF", np); 125a65a397fSUlf Hansson if (!pd->name) 126a65a397fSUlf Hansson goto free_pd_prov; 127a65a397fSUlf Hansson 128a65a397fSUlf Hansson /* 129a65a397fSUlf Hansson * Parse the domain idle states and let genpd manage the state selection 130a65a397fSUlf Hansson * for those being compatible with "domain-idle-state". 131a65a397fSUlf Hansson */ 132a65a397fSUlf Hansson ret = psci_pd_parse_states(np, &states, &state_count); 133a65a397fSUlf Hansson if (ret) 134a65a397fSUlf Hansson goto free_name; 135a65a397fSUlf Hansson 136a65a397fSUlf Hansson pd->free_states = psci_pd_free_states; 137a65a397fSUlf Hansson pd->name = kbasename(pd->name); 138a65a397fSUlf Hansson pd->states = states; 139a65a397fSUlf Hansson pd->state_count = state_count; 140a65a397fSUlf Hansson pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN; 141a65a397fSUlf Hansson 14270c179b4SUlf Hansson /* Allow power off when OSI has been successfully enabled. */ 14370c179b4SUlf Hansson if (use_osi) 14470c179b4SUlf Hansson pd->power_off = psci_pd_power_off; 14570c179b4SUlf Hansson else 14670c179b4SUlf Hansson pd->flags |= GENPD_FLAG_ALWAYS_ON; 14770c179b4SUlf Hansson 148a65a397fSUlf Hansson /* Use governor for CPU PM domains if it has some states to manage. */ 149a65a397fSUlf Hansson pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL; 150a65a397fSUlf Hansson 151a65a397fSUlf Hansson ret = pm_genpd_init(pd, pd_gov, false); 152a65a397fSUlf Hansson if (ret) { 153a65a397fSUlf Hansson psci_pd_free_states(states, state_count); 154a65a397fSUlf Hansson goto free_name; 155a65a397fSUlf Hansson } 156a65a397fSUlf Hansson 157a65a397fSUlf Hansson ret = of_genpd_add_provider_simple(np, pd); 158a65a397fSUlf Hansson if (ret) 159a65a397fSUlf Hansson goto remove_pd; 160a65a397fSUlf Hansson 161a65a397fSUlf Hansson pd_provider->node = of_node_get(np); 162a65a397fSUlf Hansson list_add(&pd_provider->link, &psci_pd_providers); 163a65a397fSUlf Hansson 164a65a397fSUlf Hansson pr_debug("init PM domain %s\n", pd->name); 165a65a397fSUlf Hansson return 0; 166a65a397fSUlf Hansson 167a65a397fSUlf Hansson remove_pd: 168a65a397fSUlf Hansson pm_genpd_remove(pd); 169a65a397fSUlf Hansson free_name: 170a65a397fSUlf Hansson kfree(pd->name); 171a65a397fSUlf Hansson free_pd_prov: 172a65a397fSUlf Hansson kfree(pd_provider); 173a65a397fSUlf Hansson free_pd: 174a65a397fSUlf Hansson kfree(pd); 175a65a397fSUlf Hansson out: 176a65a397fSUlf Hansson pr_err("failed to init PM domain ret=%d %pOF\n", ret, np); 177a65a397fSUlf Hansson return ret; 178a65a397fSUlf Hansson } 179a65a397fSUlf Hansson 180ee7c34caSUlf Hansson static void psci_pd_remove(void) 181a65a397fSUlf Hansson { 182a65a397fSUlf Hansson struct psci_pd_provider *pd_provider, *it; 183a65a397fSUlf Hansson struct generic_pm_domain *genpd; 184a65a397fSUlf Hansson 185a65a397fSUlf Hansson list_for_each_entry_safe(pd_provider, it, &psci_pd_providers, link) { 186a65a397fSUlf Hansson of_genpd_del_provider(pd_provider->node); 187a65a397fSUlf Hansson 188a65a397fSUlf Hansson genpd = of_genpd_remove_last(pd_provider->node); 189a65a397fSUlf Hansson if (!IS_ERR(genpd)) 190a65a397fSUlf Hansson kfree(genpd); 191a65a397fSUlf Hansson 192a65a397fSUlf Hansson of_node_put(pd_provider->node); 193a65a397fSUlf Hansson list_del(&pd_provider->link); 194a65a397fSUlf Hansson kfree(pd_provider); 195a65a397fSUlf Hansson } 196a65a397fSUlf Hansson } 197a65a397fSUlf Hansson 19870c179b4SUlf Hansson static int psci_pd_init_topology(struct device_node *np) 199a65a397fSUlf Hansson { 200a65a397fSUlf Hansson struct device_node *node; 201a65a397fSUlf Hansson struct of_phandle_args child, parent; 202a65a397fSUlf Hansson int ret; 203a65a397fSUlf Hansson 204a65a397fSUlf Hansson for_each_child_of_node(np, node) { 205a65a397fSUlf Hansson if (of_parse_phandle_with_args(node, "power-domains", 206a65a397fSUlf Hansson "#power-domain-cells", 0, &parent)) 207a65a397fSUlf Hansson continue; 208a65a397fSUlf Hansson 209a65a397fSUlf Hansson child.np = node; 210a65a397fSUlf Hansson child.args_count = 0; 21170c179b4SUlf Hansson ret = of_genpd_add_subdomain(&parent, &child); 212a65a397fSUlf Hansson of_node_put(parent.np); 213a65a397fSUlf Hansson if (ret) { 214a65a397fSUlf Hansson of_node_put(node); 215a65a397fSUlf Hansson return ret; 216a65a397fSUlf Hansson } 217a65a397fSUlf Hansson } 218a65a397fSUlf Hansson 219a65a397fSUlf Hansson return 0; 220a65a397fSUlf Hansson } 221a65a397fSUlf Hansson 22270c179b4SUlf Hansson static bool psci_pd_try_set_osi_mode(void) 223a65a397fSUlf Hansson { 22470c179b4SUlf Hansson int ret; 22570c179b4SUlf Hansson 22670c179b4SUlf Hansson if (!psci_has_osi_support()) 22770c179b4SUlf Hansson return false; 22870c179b4SUlf Hansson 22970c179b4SUlf Hansson ret = psci_set_osi_mode(true); 23070c179b4SUlf Hansson if (ret) { 23170c179b4SUlf Hansson pr_warn("failed to enable OSI mode: %d\n", ret); 23270c179b4SUlf Hansson return false; 233a65a397fSUlf Hansson } 234a65a397fSUlf Hansson 23570c179b4SUlf Hansson return true; 236a65a397fSUlf Hansson } 237a65a397fSUlf Hansson 23881f94ddfSUlf Hansson static void psci_cpuidle_domain_sync_state(struct device *dev) 23981f94ddfSUlf Hansson { 24081f94ddfSUlf Hansson /* 24181f94ddfSUlf Hansson * All devices have now been attached/probed to the PM domain topology, 24281f94ddfSUlf Hansson * hence it's fine to allow domain states to be picked. 24381f94ddfSUlf Hansson */ 24481f94ddfSUlf Hansson psci_pd_allow_domain_state = true; 24581f94ddfSUlf Hansson } 24681f94ddfSUlf Hansson 247ee7c34caSUlf Hansson static const struct of_device_id psci_of_match[] = { 248a65a397fSUlf Hansson { .compatible = "arm,psci-1.0" }, 249a65a397fSUlf Hansson {} 250a65a397fSUlf Hansson }; 251a65a397fSUlf Hansson 252ee7c34caSUlf Hansson static int psci_cpuidle_domain_probe(struct platform_device *pdev) 253a65a397fSUlf Hansson { 254ee7c34caSUlf Hansson struct device_node *np = pdev->dev.of_node; 255a65a397fSUlf Hansson struct device_node *node; 25670c179b4SUlf Hansson bool use_osi; 257a65a397fSUlf Hansson int ret = 0, pd_count = 0; 258a65a397fSUlf Hansson 259a65a397fSUlf Hansson if (!np) 260a65a397fSUlf Hansson return -ENODEV; 261a65a397fSUlf Hansson 26270c179b4SUlf Hansson /* If OSI mode is supported, let's try to enable it. */ 26370c179b4SUlf Hansson use_osi = psci_pd_try_set_osi_mode(); 264a65a397fSUlf Hansson 265a65a397fSUlf Hansson /* 266a65a397fSUlf Hansson * Parse child nodes for the "#power-domain-cells" property and 267a65a397fSUlf Hansson * initialize a genpd/genpd-of-provider pair when it's found. 268a65a397fSUlf Hansson */ 269a65a397fSUlf Hansson for_each_child_of_node(np, node) { 270a65a397fSUlf Hansson if (!of_find_property(node, "#power-domain-cells", NULL)) 271a65a397fSUlf Hansson continue; 272a65a397fSUlf Hansson 27370c179b4SUlf Hansson ret = psci_pd_init(node, use_osi); 274a65a397fSUlf Hansson if (ret) 275a65a397fSUlf Hansson goto put_node; 276a65a397fSUlf Hansson 277a65a397fSUlf Hansson pd_count++; 278a65a397fSUlf Hansson } 279a65a397fSUlf Hansson 280a65a397fSUlf Hansson /* Bail out if not using the hierarchical CPU topology. */ 281a65a397fSUlf Hansson if (!pd_count) 28270c179b4SUlf Hansson goto no_pd; 283a65a397fSUlf Hansson 284a65a397fSUlf Hansson /* Link genpd masters/subdomains to model the CPU topology. */ 28570c179b4SUlf Hansson ret = psci_pd_init_topology(np); 286a65a397fSUlf Hansson if (ret) 287a65a397fSUlf Hansson goto remove_pd; 288a65a397fSUlf Hansson 289a65a397fSUlf Hansson pr_info("Initialized CPU PM domain topology\n"); 290ee7c34caSUlf Hansson return 0; 291a65a397fSUlf Hansson 292a65a397fSUlf Hansson put_node: 293a65a397fSUlf Hansson of_node_put(node); 294a65a397fSUlf Hansson remove_pd: 295a65a397fSUlf Hansson psci_pd_remove(); 296a65a397fSUlf Hansson pr_err("failed to create CPU PM domains ret=%d\n", ret); 29770c179b4SUlf Hansson no_pd: 29870c179b4SUlf Hansson if (use_osi) 29970c179b4SUlf Hansson psci_set_osi_mode(false); 300a65a397fSUlf Hansson return ret; 301a65a397fSUlf Hansson } 302ee7c34caSUlf Hansson 303ee7c34caSUlf Hansson static struct platform_driver psci_cpuidle_domain_driver = { 304ee7c34caSUlf Hansson .probe = psci_cpuidle_domain_probe, 305ee7c34caSUlf Hansson .driver = { 306ee7c34caSUlf Hansson .name = "psci-cpuidle-domain", 307ee7c34caSUlf Hansson .of_match_table = psci_of_match, 30881f94ddfSUlf Hansson .sync_state = psci_cpuidle_domain_sync_state, 309ee7c34caSUlf Hansson }, 310ee7c34caSUlf Hansson }; 311ee7c34caSUlf Hansson 312ee7c34caSUlf Hansson static int __init psci_idle_init_domains(void) 313ee7c34caSUlf Hansson { 314ee7c34caSUlf Hansson return platform_driver_register(&psci_cpuidle_domain_driver); 315ee7c34caSUlf Hansson } 316a65a397fSUlf Hansson subsys_initcall(psci_idle_init_domains); 317a65a397fSUlf Hansson 318166bf835SUlf Hansson struct device *psci_dt_attach_cpu(int cpu) 319a5e0454cSUlf Hansson { 320a5e0454cSUlf Hansson struct device *dev; 321a5e0454cSUlf Hansson 322a5e0454cSUlf Hansson dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), "psci"); 323a5e0454cSUlf Hansson if (IS_ERR_OR_NULL(dev)) 324a5e0454cSUlf Hansson return dev; 325a5e0454cSUlf Hansson 326a5e0454cSUlf Hansson pm_runtime_irq_safe(dev); 327a5e0454cSUlf Hansson if (cpu_online(cpu)) 328a5e0454cSUlf Hansson pm_runtime_get_sync(dev); 329a5e0454cSUlf Hansson 330*670c90deSUlf Hansson dev_pm_syscore_device(dev, true); 331*670c90deSUlf Hansson 332a5e0454cSUlf Hansson return dev; 333a5e0454cSUlf Hansson } 334166bf835SUlf Hansson 335166bf835SUlf Hansson void psci_dt_detach_cpu(struct device *dev) 336166bf835SUlf Hansson { 337166bf835SUlf Hansson if (IS_ERR_OR_NULL(dev)) 338166bf835SUlf Hansson return; 339166bf835SUlf Hansson 340166bf835SUlf Hansson dev_pm_domain_detach(dev, false); 341166bf835SUlf Hansson } 342