linux/arch/arm/kernel/cpuidle.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Linaro Ltd.
   3 *
   4 * The code contained herein is licensed under the GNU General Public
   5 * License. You may obtain a copy of the GNU General Public License
   6 * Version 2 or later at the following locations:
   7 *
   8 * http://www.opensource.org/licenses/gpl-license.html
   9 * http://www.gnu.org/copyleft/gpl.html
  10 */
  11
  12#include <linux/cpuidle.h>
  13#include <linux/of.h>
  14#include <linux/of_device.h>
  15#include <asm/cpuidle.h>
  16
  17extern struct of_cpuidle_method __cpuidle_method_of_table[];
  18
  19static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel
  20        __used __section(__cpuidle_method_of_table_end);
  21
  22static struct cpuidle_ops cpuidle_ops[NR_CPUS];
  23
  24/**
  25 * arm_cpuidle_simple_enter() - a wrapper to cpu_do_idle()
  26 * @dev: not used
  27 * @drv: not used
  28 * @index: not used
  29 *
  30 * A trivial wrapper to allow the cpu_do_idle function to be assigned as a
  31 * cpuidle callback by matching the function signature.
  32 *
  33 * Returns the index passed as parameter
  34 */
  35int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
  36                struct cpuidle_driver *drv, int index)
  37{
  38        cpu_do_idle();
  39
  40        return index;
  41}
  42
  43/**
  44 * arm_cpuidle_suspend() - function to enter low power idle states
  45 * @index: an integer used as an identifier for the low level PM callbacks
  46 *
  47 * This function calls the underlying arch specific low level PM code as
  48 * registered at the init time.
  49 *
  50 * Returns -EOPNOTSUPP if no suspend callback is defined, the result of the
  51 * callback otherwise.
  52 */
  53int arm_cpuidle_suspend(int index)
  54{
  55        int ret = -EOPNOTSUPP;
  56        int cpu = smp_processor_id();
  57
  58        if (cpuidle_ops[cpu].suspend)
  59                ret = cpuidle_ops[cpu].suspend(index);
  60
  61        return ret;
  62}
  63
  64/**
  65 * arm_cpuidle_get_ops() - find a registered cpuidle_ops by name
  66 * @method: the method name
  67 *
  68 * Search in the __cpuidle_method_of_table array the cpuidle ops matching the
  69 * method name.
  70 *
  71 * Returns a struct cpuidle_ops pointer, NULL if not found.
  72 */
  73static struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method)
  74{
  75        struct of_cpuidle_method *m = __cpuidle_method_of_table;
  76
  77        for (; m->method; m++)
  78                if (!strcmp(m->method, method))
  79                        return m->ops;
  80
  81        return NULL;
  82}
  83
  84/**
  85 * arm_cpuidle_read_ops() - Initialize the cpuidle ops with the device tree
  86 * @dn: a pointer to a struct device node corresponding to a cpu node
  87 * @cpu: the cpu identifier
  88 *
  89 * Get the method name defined in the 'enable-method' property, retrieve the
  90 * associated cpuidle_ops and do a struct copy. This copy is needed because all
  91 * cpuidle_ops are tagged __initdata and will be unloaded after the init
  92 * process.
  93 *
  94 * Return 0 on sucess, -ENOENT if no 'enable-method' is defined, -EOPNOTSUPP if
  95 * no cpuidle_ops is registered for the 'enable-method'.
  96 */
  97static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu)
  98{
  99        const char *enable_method;
 100        struct cpuidle_ops *ops;
 101
 102        enable_method = of_get_property(dn, "enable-method", NULL);
 103        if (!enable_method)
 104                return -ENOENT;
 105
 106        ops = arm_cpuidle_get_ops(enable_method);
 107        if (!ops) {
 108                pr_warn("%s: unsupported enable-method property: %s\n",
 109                        dn->full_name, enable_method);
 110                return -EOPNOTSUPP;
 111        }
 112
 113        cpuidle_ops[cpu] = *ops; /* structure copy */
 114
 115        pr_notice("cpuidle: enable-method property '%s'"
 116                  " found operations\n", enable_method);
 117
 118        return 0;
 119}
 120
 121/**
 122 * arm_cpuidle_init() - Initialize cpuidle_ops for a specific cpu
 123 * @cpu: the cpu to be initialized
 124 *
 125 * Initialize the cpuidle ops with the device for the cpu and then call
 126 * the cpu's idle initialization callback. This may fail if the underlying HW
 127 * is not operational.
 128 *
 129 * Returns:
 130 *  0 on success,
 131 *  -ENODEV if it fails to find the cpu node in the device tree,
 132 *  -EOPNOTSUPP if it does not find a registered cpuidle_ops for this cpu,
 133 *  -ENOENT if it fails to find an 'enable-method' property,
 134 *  -ENXIO if the HW reports a failure or a misconfiguration,
 135 *  -ENOMEM if the HW report an memory allocation failure 
 136 */
 137int __init arm_cpuidle_init(int cpu)
 138{
 139        struct device_node *cpu_node = of_cpu_device_node_get(cpu);
 140        int ret;
 141
 142        if (!cpu_node)
 143                return -ENODEV;
 144
 145        ret = arm_cpuidle_read_ops(cpu_node, cpu);
 146        if (!ret && cpuidle_ops[cpu].init)
 147                ret = cpuidle_ops[cpu].init(cpu_node, cpu);
 148
 149        of_node_put(cpu_node);
 150
 151        return ret;
 152}
 153