xref: /openbmc/linux/arch/arm/kernel/cpuidle.c (revision 4cfd5520)
1e1689795SRobert Lee /*
2e1689795SRobert Lee  * Copyright 2012 Linaro Ltd.
3e1689795SRobert Lee  *
4e1689795SRobert Lee  * The code contained herein is licensed under the GNU General Public
5e1689795SRobert Lee  * License. You may obtain a copy of the GNU General Public License
6e1689795SRobert Lee  * Version 2 or later at the following locations:
7e1689795SRobert Lee  *
8e1689795SRobert Lee  * http://www.opensource.org/licenses/gpl-license.html
9e1689795SRobert Lee  * http://www.gnu.org/copyleft/gpl.html
10e1689795SRobert Lee  */
11e1689795SRobert Lee 
12e1689795SRobert Lee #include <linux/cpuidle.h>
13449e056cSDaniel Lezcano #include <linux/of.h>
14449e056cSDaniel Lezcano #include <linux/of_device.h>
15eeebc3bbSDaniel Lezcano #include <asm/cpuidle.h>
16e1689795SRobert Lee 
17449e056cSDaniel Lezcano extern struct of_cpuidle_method __cpuidle_method_of_table[];
18449e056cSDaniel Lezcano 
19449e056cSDaniel Lezcano static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel
20449e056cSDaniel Lezcano 	__used __section(__cpuidle_method_of_table_end);
21449e056cSDaniel Lezcano 
22449e056cSDaniel Lezcano static struct cpuidle_ops cpuidle_ops[NR_CPUS];
23449e056cSDaniel Lezcano 
249a309d6fSDaniel Lezcano /**
259a309d6fSDaniel Lezcano  * arm_cpuidle_simple_enter() - a wrapper to cpu_do_idle()
269a309d6fSDaniel Lezcano  * @dev: not used
279a309d6fSDaniel Lezcano  * @drv: not used
289a309d6fSDaniel Lezcano  * @index: not used
299a309d6fSDaniel Lezcano  *
309a309d6fSDaniel Lezcano  * A trivial wrapper to allow the cpu_do_idle function to be assigned as a
319a309d6fSDaniel Lezcano  * cpuidle callback by matching the function signature.
329a309d6fSDaniel Lezcano  *
339a309d6fSDaniel Lezcano  * Returns the index passed as parameter
349a309d6fSDaniel Lezcano  */
35e1689795SRobert Lee int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
36e1689795SRobert Lee 		struct cpuidle_driver *drv, int index)
37e1689795SRobert Lee {
38e1689795SRobert Lee 	cpu_do_idle();
39e1689795SRobert Lee 
40e1689795SRobert Lee 	return index;
41e1689795SRobert Lee }
42449e056cSDaniel Lezcano 
439a309d6fSDaniel Lezcano /**
449a309d6fSDaniel Lezcano  * arm_cpuidle_suspend() - function to enter low power idle states
459a309d6fSDaniel Lezcano  * @index: an integer used as an identifier for the low level PM callbacks
469a309d6fSDaniel Lezcano  *
479a309d6fSDaniel Lezcano  * This function calls the underlying arch specific low level PM code as
489a309d6fSDaniel Lezcano  * registered at the init time.
499a309d6fSDaniel Lezcano  *
509a309d6fSDaniel Lezcano  * Returns -EOPNOTSUPP if no suspend callback is defined, the result of the
519a309d6fSDaniel Lezcano  * callback otherwise.
529a309d6fSDaniel Lezcano  */
53449e056cSDaniel Lezcano int arm_cpuidle_suspend(int index)
54449e056cSDaniel Lezcano {
55449e056cSDaniel Lezcano 	int ret = -EOPNOTSUPP;
56449e056cSDaniel Lezcano 	int cpu = smp_processor_id();
57449e056cSDaniel Lezcano 
58449e056cSDaniel Lezcano 	if (cpuidle_ops[cpu].suspend)
59f6419f24SLorenzo Pieralisi 		ret = cpuidle_ops[cpu].suspend(index);
60449e056cSDaniel Lezcano 
61449e056cSDaniel Lezcano 	return ret;
62449e056cSDaniel Lezcano }
63449e056cSDaniel Lezcano 
649a309d6fSDaniel Lezcano /**
659a309d6fSDaniel Lezcano  * arm_cpuidle_get_ops() - find a registered cpuidle_ops by name
669a309d6fSDaniel Lezcano  * @method: the method name
679a309d6fSDaniel Lezcano  *
689a309d6fSDaniel Lezcano  * Search in the __cpuidle_method_of_table array the cpuidle ops matching the
699a309d6fSDaniel Lezcano  * method name.
709a309d6fSDaniel Lezcano  *
719a309d6fSDaniel Lezcano  * Returns a struct cpuidle_ops pointer, NULL if not found.
729a309d6fSDaniel Lezcano  */
734cfd5520SJisheng Zhang static const struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method)
74449e056cSDaniel Lezcano {
75449e056cSDaniel Lezcano 	struct of_cpuidle_method *m = __cpuidle_method_of_table;
76449e056cSDaniel Lezcano 
77449e056cSDaniel Lezcano 	for (; m->method; m++)
78449e056cSDaniel Lezcano 		if (!strcmp(m->method, method))
79449e056cSDaniel Lezcano 			return m->ops;
80449e056cSDaniel Lezcano 
81449e056cSDaniel Lezcano 	return NULL;
82449e056cSDaniel Lezcano }
83449e056cSDaniel Lezcano 
849a309d6fSDaniel Lezcano /**
859a309d6fSDaniel Lezcano  * arm_cpuidle_read_ops() - Initialize the cpuidle ops with the device tree
869a309d6fSDaniel Lezcano  * @dn: a pointer to a struct device node corresponding to a cpu node
879a309d6fSDaniel Lezcano  * @cpu: the cpu identifier
889a309d6fSDaniel Lezcano  *
899a309d6fSDaniel Lezcano  * Get the method name defined in the 'enable-method' property, retrieve the
909a309d6fSDaniel Lezcano  * associated cpuidle_ops and do a struct copy. This copy is needed because all
914cfd5520SJisheng Zhang  * cpuidle_ops are tagged __initconst and will be unloaded after the init
929a309d6fSDaniel Lezcano  * process.
939a309d6fSDaniel Lezcano  *
949a309d6fSDaniel Lezcano  * Return 0 on sucess, -ENOENT if no 'enable-method' is defined, -EOPNOTSUPP if
959a309d6fSDaniel Lezcano  * no cpuidle_ops is registered for the 'enable-method'.
969a309d6fSDaniel Lezcano  */
97449e056cSDaniel Lezcano static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu)
98449e056cSDaniel Lezcano {
99449e056cSDaniel Lezcano 	const char *enable_method;
1004cfd5520SJisheng Zhang 	const struct cpuidle_ops *ops;
101449e056cSDaniel Lezcano 
102449e056cSDaniel Lezcano 	enable_method = of_get_property(dn, "enable-method", NULL);
103449e056cSDaniel Lezcano 	if (!enable_method)
104449e056cSDaniel Lezcano 		return -ENOENT;
105449e056cSDaniel Lezcano 
106449e056cSDaniel Lezcano 	ops = arm_cpuidle_get_ops(enable_method);
107449e056cSDaniel Lezcano 	if (!ops) {
108449e056cSDaniel Lezcano 		pr_warn("%s: unsupported enable-method property: %s\n",
109449e056cSDaniel Lezcano 			dn->full_name, enable_method);
110449e056cSDaniel Lezcano 		return -EOPNOTSUPP;
111449e056cSDaniel Lezcano 	}
112449e056cSDaniel Lezcano 
113449e056cSDaniel Lezcano 	cpuidle_ops[cpu] = *ops; /* structure copy */
114449e056cSDaniel Lezcano 
115449e056cSDaniel Lezcano 	pr_notice("cpuidle: enable-method property '%s'"
116449e056cSDaniel Lezcano 		  " found operations\n", enable_method);
117449e056cSDaniel Lezcano 
118449e056cSDaniel Lezcano 	return 0;
119449e056cSDaniel Lezcano }
120449e056cSDaniel Lezcano 
1219a309d6fSDaniel Lezcano /**
1229a309d6fSDaniel Lezcano  * arm_cpuidle_init() - Initialize cpuidle_ops for a specific cpu
1239a309d6fSDaniel Lezcano  * @cpu: the cpu to be initialized
1249a309d6fSDaniel Lezcano  *
1259a309d6fSDaniel Lezcano  * Initialize the cpuidle ops with the device for the cpu and then call
1269a309d6fSDaniel Lezcano  * the cpu's idle initialization callback. This may fail if the underlying HW
1279a309d6fSDaniel Lezcano  * is not operational.
1289a309d6fSDaniel Lezcano  *
1299a309d6fSDaniel Lezcano  * Returns:
1309a309d6fSDaniel Lezcano  *  0 on success,
1319a309d6fSDaniel Lezcano  *  -ENODEV if it fails to find the cpu node in the device tree,
1329a309d6fSDaniel Lezcano  *  -EOPNOTSUPP if it does not find a registered cpuidle_ops for this cpu,
1339a309d6fSDaniel Lezcano  *  -ENOENT if it fails to find an 'enable-method' property,
1349a309d6fSDaniel Lezcano  *  -ENXIO if the HW reports a failure or a misconfiguration,
1359a309d6fSDaniel Lezcano  *  -ENOMEM if the HW report an memory allocation failure
1369a309d6fSDaniel Lezcano  */
137449e056cSDaniel Lezcano int __init arm_cpuidle_init(int cpu)
138449e056cSDaniel Lezcano {
139449e056cSDaniel Lezcano 	struct device_node *cpu_node = of_cpu_device_node_get(cpu);
140449e056cSDaniel Lezcano 	int ret;
141449e056cSDaniel Lezcano 
142449e056cSDaniel Lezcano 	if (!cpu_node)
143449e056cSDaniel Lezcano 		return -ENODEV;
144449e056cSDaniel Lezcano 
145449e056cSDaniel Lezcano 	ret = arm_cpuidle_read_ops(cpu_node, cpu);
146449e056cSDaniel Lezcano 	if (!ret && cpuidle_ops[cpu].init)
147449e056cSDaniel Lezcano 		ret = cpuidle_ops[cpu].init(cpu_node, cpu);
148449e056cSDaniel Lezcano 
149449e056cSDaniel Lezcano 	of_node_put(cpu_node);
150449e056cSDaniel Lezcano 
151449e056cSDaniel Lezcano 	return ret;
152449e056cSDaniel Lezcano }
153