cpuidle-psci-domain.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * PM domains for CPUs via genpd - managed by cpuidle-psci.
  4. *
  5. * Copyright (C) 2019 Linaro Ltd.
  6. * Author: Ulf Hansson <[email protected]>
  7. *
  8. */
  9. #define pr_fmt(fmt) "CPUidle PSCI: " fmt
  10. #include <linux/cpu.h>
  11. #include <linux/device.h>
  12. #include <linux/kernel.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/pm_domain.h>
  15. #include <linux/pm_runtime.h>
  16. #include <linux/psci.h>
  17. #include <linux/slab.h>
  18. #include <linux/string.h>
  19. #include "cpuidle-psci.h"
  20. struct psci_pd_provider {
  21. struct list_head link;
  22. struct device_node *node;
  23. };
  24. static LIST_HEAD(psci_pd_providers);
  25. static bool psci_pd_allow_domain_state;
  26. static int psci_pd_power_off(struct generic_pm_domain *pd)
  27. {
  28. struct genpd_power_state *state = &pd->states[pd->state_idx];
  29. u32 *pd_state;
  30. if (!state->data)
  31. return 0;
  32. if (!psci_pd_allow_domain_state)
  33. return -EBUSY;
  34. /* OSI mode is enabled, set the corresponding domain state. */
  35. pd_state = state->data;
  36. psci_set_domain_state(*pd_state);
  37. return 0;
  38. }
  39. static int psci_pd_init(struct device_node *np, bool use_osi)
  40. {
  41. struct generic_pm_domain *pd;
  42. struct psci_pd_provider *pd_provider;
  43. struct dev_power_governor *pd_gov;
  44. int ret = -ENOMEM;
  45. pd = dt_idle_pd_alloc(np, psci_dt_parse_state_node);
  46. if (!pd)
  47. goto out;
  48. pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL);
  49. if (!pd_provider)
  50. goto free_pd;
  51. pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
  52. /* Allow power off when OSI has been successfully enabled. */
  53. if (use_osi)
  54. pd->power_off = psci_pd_power_off;
  55. else
  56. pd->flags |= GENPD_FLAG_ALWAYS_ON;
  57. /* Use governor for CPU PM domains if it has some states to manage. */
  58. pd_gov = pd->states ? &pm_domain_cpu_gov : NULL;
  59. ret = pm_genpd_init(pd, pd_gov, false);
  60. if (ret)
  61. goto free_pd_prov;
  62. ret = of_genpd_add_provider_simple(np, pd);
  63. if (ret)
  64. goto remove_pd;
  65. pd_provider->node = of_node_get(np);
  66. list_add(&pd_provider->link, &psci_pd_providers);
  67. pr_debug("init PM domain %s\n", pd->name);
  68. return 0;
  69. remove_pd:
  70. pm_genpd_remove(pd);
  71. free_pd_prov:
  72. kfree(pd_provider);
  73. free_pd:
  74. dt_idle_pd_free(pd);
  75. out:
  76. pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
  77. return ret;
  78. }
  79. static void psci_pd_remove(void)
  80. {
  81. struct psci_pd_provider *pd_provider, *it;
  82. struct generic_pm_domain *genpd;
  83. list_for_each_entry_safe_reverse(pd_provider, it,
  84. &psci_pd_providers, link) {
  85. of_genpd_del_provider(pd_provider->node);
  86. genpd = of_genpd_remove_last(pd_provider->node);
  87. if (!IS_ERR(genpd))
  88. kfree(genpd);
  89. of_node_put(pd_provider->node);
  90. list_del(&pd_provider->link);
  91. kfree(pd_provider);
  92. }
  93. }
  94. static void psci_cpuidle_domain_sync_state(struct device *dev)
  95. {
  96. /*
  97. * All devices have now been attached/probed to the PM domain topology,
  98. * hence it's fine to allow domain states to be picked.
  99. */
  100. psci_pd_allow_domain_state = true;
  101. }
  102. static const struct of_device_id psci_of_match[] = {
  103. { .compatible = "arm,psci-1.0" },
  104. {}
  105. };
  106. static int psci_cpuidle_domain_probe(struct platform_device *pdev)
  107. {
  108. struct device_node *np = pdev->dev.of_node;
  109. struct device_node *node;
  110. bool use_osi = psci_has_osi_support();
  111. int ret = 0, pd_count = 0;
  112. if (!np)
  113. return -ENODEV;
  114. /*
  115. * Parse child nodes for the "#power-domain-cells" property and
  116. * initialize a genpd/genpd-of-provider pair when it's found.
  117. */
  118. for_each_child_of_node(np, node) {
  119. if (!of_find_property(node, "#power-domain-cells", NULL))
  120. continue;
  121. ret = psci_pd_init(node, use_osi);
  122. if (ret) {
  123. of_node_put(node);
  124. goto exit;
  125. }
  126. pd_count++;
  127. }
  128. /* Bail out if not using the hierarchical CPU topology. */
  129. if (!pd_count)
  130. return 0;
  131. /* Link genpd masters/subdomains to model the CPU topology. */
  132. ret = dt_idle_pd_init_topology(np);
  133. if (ret)
  134. goto remove_pd;
  135. /* let's try to enable OSI. */
  136. ret = psci_set_osi_mode(use_osi);
  137. if (ret)
  138. goto remove_pd;
  139. pr_info("Initialized CPU PM domain topology using %s mode\n",
  140. use_osi ? "OSI" : "PC");
  141. return 0;
  142. remove_pd:
  143. dt_idle_pd_remove_topology(np);
  144. psci_pd_remove();
  145. exit:
  146. pr_err("failed to create CPU PM domains ret=%d\n", ret);
  147. return ret;
  148. }
  149. static struct platform_driver psci_cpuidle_domain_driver = {
  150. .probe = psci_cpuidle_domain_probe,
  151. .driver = {
  152. .name = "psci-cpuidle-domain",
  153. .of_match_table = psci_of_match,
  154. .sync_state = psci_cpuidle_domain_sync_state,
  155. },
  156. };
  157. static int __init psci_idle_init_domains(void)
  158. {
  159. return platform_driver_register(&psci_cpuidle_domain_driver);
  160. }
  161. subsys_initcall(psci_idle_init_domains);