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