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