0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #define pr_fmt(fmt) "CPUidle PSCI: " fmt
0011
0012 #include <linux/cpu.h>
0013 #include <linux/device.h>
0014 #include <linux/kernel.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/pm_domain.h>
0017 #include <linux/pm_runtime.h>
0018 #include <linux/psci.h>
0019 #include <linux/slab.h>
0020 #include <linux/string.h>
0021
0022 #include "cpuidle-psci.h"
0023
0024 struct psci_pd_provider {
0025 struct list_head link;
0026 struct device_node *node;
0027 };
0028
0029 static LIST_HEAD(psci_pd_providers);
0030 static bool psci_pd_allow_domain_state;
0031
0032 static int psci_pd_power_off(struct generic_pm_domain *pd)
0033 {
0034 struct genpd_power_state *state = &pd->states[pd->state_idx];
0035 u32 *pd_state;
0036
0037 if (!state->data)
0038 return 0;
0039
0040 if (!psci_pd_allow_domain_state)
0041 return -EBUSY;
0042
0043
0044 pd_state = state->data;
0045 psci_set_domain_state(*pd_state);
0046
0047 return 0;
0048 }
0049
0050 static int psci_pd_init(struct device_node *np, bool use_osi)
0051 {
0052 struct generic_pm_domain *pd;
0053 struct psci_pd_provider *pd_provider;
0054 struct dev_power_governor *pd_gov;
0055 int ret = -ENOMEM;
0056
0057 pd = dt_idle_pd_alloc(np, psci_dt_parse_state_node);
0058 if (!pd)
0059 goto out;
0060
0061 pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL);
0062 if (!pd_provider)
0063 goto free_pd;
0064
0065 pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
0066
0067
0068 if (use_osi)
0069 pd->power_off = psci_pd_power_off;
0070 else
0071 pd->flags |= GENPD_FLAG_ALWAYS_ON;
0072
0073
0074 pd_gov = pd->states ? &pm_domain_cpu_gov : NULL;
0075
0076 ret = pm_genpd_init(pd, pd_gov, false);
0077 if (ret)
0078 goto free_pd_prov;
0079
0080 ret = of_genpd_add_provider_simple(np, pd);
0081 if (ret)
0082 goto remove_pd;
0083
0084 pd_provider->node = of_node_get(np);
0085 list_add(&pd_provider->link, &psci_pd_providers);
0086
0087 pr_debug("init PM domain %s\n", pd->name);
0088 return 0;
0089
0090 remove_pd:
0091 pm_genpd_remove(pd);
0092 free_pd_prov:
0093 kfree(pd_provider);
0094 free_pd:
0095 dt_idle_pd_free(pd);
0096 out:
0097 pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
0098 return ret;
0099 }
0100
0101 static void psci_pd_remove(void)
0102 {
0103 struct psci_pd_provider *pd_provider, *it;
0104 struct generic_pm_domain *genpd;
0105
0106 list_for_each_entry_safe(pd_provider, it, &psci_pd_providers, link) {
0107 of_genpd_del_provider(pd_provider->node);
0108
0109 genpd = of_genpd_remove_last(pd_provider->node);
0110 if (!IS_ERR(genpd))
0111 kfree(genpd);
0112
0113 of_node_put(pd_provider->node);
0114 list_del(&pd_provider->link);
0115 kfree(pd_provider);
0116 }
0117 }
0118
0119 static bool psci_pd_try_set_osi_mode(void)
0120 {
0121 int ret;
0122
0123 if (!psci_has_osi_support())
0124 return false;
0125
0126 ret = psci_set_osi_mode(true);
0127 if (ret) {
0128 pr_warn("failed to enable OSI mode: %d\n", ret);
0129 return false;
0130 }
0131
0132 return true;
0133 }
0134
0135 static void psci_cpuidle_domain_sync_state(struct device *dev)
0136 {
0137
0138
0139
0140
0141 psci_pd_allow_domain_state = true;
0142 }
0143
0144 static const struct of_device_id psci_of_match[] = {
0145 { .compatible = "arm,psci-1.0" },
0146 {}
0147 };
0148
0149 static int psci_cpuidle_domain_probe(struct platform_device *pdev)
0150 {
0151 struct device_node *np = pdev->dev.of_node;
0152 struct device_node *node;
0153 bool use_osi;
0154 int ret = 0, pd_count = 0;
0155
0156 if (!np)
0157 return -ENODEV;
0158
0159
0160 use_osi = psci_pd_try_set_osi_mode();
0161
0162
0163
0164
0165
0166 for_each_child_of_node(np, node) {
0167 if (!of_find_property(node, "#power-domain-cells", NULL))
0168 continue;
0169
0170 ret = psci_pd_init(node, use_osi);
0171 if (ret)
0172 goto put_node;
0173
0174 pd_count++;
0175 }
0176
0177
0178 if (!pd_count)
0179 goto no_pd;
0180
0181
0182 ret = dt_idle_pd_init_topology(np);
0183 if (ret)
0184 goto remove_pd;
0185
0186 pr_info("Initialized CPU PM domain topology\n");
0187 return 0;
0188
0189 put_node:
0190 of_node_put(node);
0191 remove_pd:
0192 psci_pd_remove();
0193 pr_err("failed to create CPU PM domains ret=%d\n", ret);
0194 no_pd:
0195 if (use_osi)
0196 psci_set_osi_mode(false);
0197 return ret;
0198 }
0199
0200 static struct platform_driver psci_cpuidle_domain_driver = {
0201 .probe = psci_cpuidle_domain_probe,
0202 .driver = {
0203 .name = "psci-cpuidle-domain",
0204 .of_match_table = psci_of_match,
0205 .sync_state = psci_cpuidle_domain_sync_state,
0206 },
0207 };
0208
0209 static int __init psci_idle_init_domains(void)
0210 {
0211 return platform_driver_register(&psci_cpuidle_domain_driver);
0212 }
0213 subsys_initcall(psci_idle_init_domains);