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] __ro_after_init;
  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 the result of the suspend callback.
  51 */
  52int arm_cpuidle_suspend(int index)
  53{
  54        int cpu = smp_processor_id();
  55
  56        return cpuidle_ops[cpu].suspend(index);
  57}
  58
  59/**
  60 * arm_cpuidle_get_ops() - find a registered cpuidle_ops by name
  61 * @method: the method name
  62 *
  63 * Search in the __cpuidle_method_of_table array the cpuidle ops matching the
  64 * method name.
  65 *
  66 * Returns a struct cpuidle_ops pointer, NULL if not found.
  67 */
  68static const struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method)
  69{
  70        struct of_cpuidle_method *m = __cpuidle_method_of_table;
  71
  72        for (; m->method; m++)
  73                if (!strcmp(m->method, method))
  74                        return m->ops;
  75
  76        return NULL;
  77}
  78
  79/**
  80 * arm_cpuidle_read_ops() - Initialize the cpuidle ops with the device tree
  81 * @dn: a pointer to a struct device node corresponding to a cpu node
  82 * @cpu: the cpu identifier
  83 *
  84 * Get the method name defined in the 'enable-method' property, retrieve the
  85 * associated cpuidle_ops and do a struct copy. This copy is needed because all
  86 * cpuidle_ops are tagged __initconst and will be unloaded after the init
  87 * process.
  88 *
  89 * Return 0 on sucess, -ENOENT if no 'enable-method' is defined, -EOPNOTSUPP if
  90 * no cpuidle_ops is registered for the 'enable-method', or if either init or
  91 * suspend callback isn't defined.
  92 */
  93static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu)
  94{
  95        const char *enable_method;
  96        const struct cpuidle_ops *ops;
  97
  98        enable_method = of_get_property(dn, "enable-method", NULL);
  99        if (!enable_method)
 100                return -ENOENT;
 101
 102        ops = arm_cpuidle_get_ops(enable_method);
 103        if (!ops) {
 104                pr_warn("%s: unsupported enable-method property: %s\n",
 105                        dn->full_name, enable_method);
 106                return -EOPNOTSUPP;
 107        }
 108
 109        if (!ops->init || !ops->suspend) {
 110                pr_warn("cpuidle_ops '%s': no init or suspend callback\n",
 111                        enable_method);
 112                return -EOPNOTSUPP;
 113        }
 114
 115        cpuidle_ops[cpu] = *ops; /* structure copy */
 116
 117        pr_notice("cpuidle: enable-method property '%s'"
 118                  " found operations\n", enable_method);
 119
 120        return 0;
 121}
 122
 123/**
 124 * arm_cpuidle_init() - Initialize cpuidle_ops for a specific cpu
 125 * @cpu: the cpu to be initialized
 126 *
 127 * Initialize the cpuidle ops with the device for the cpu and then call
 128 * the cpu's idle initialization callback. This may fail if the underlying HW
 129 * is not operational.
 130 *
 131 * Returns:
 132 *  0 on success,
 133 *  -ENODEV if it fails to find the cpu node in the device tree,
 134 *  -EOPNOTSUPP if it does not find a registered and valid cpuidle_ops for
 135 *  this cpu,
 136 *  -ENOENT if it fails to find an 'enable-method' property,
 137 *  -ENXIO if the HW reports a failure or a misconfiguration,
 138 *  -ENOMEM if the HW report an memory allocation failure 
 139 */
 140int __init arm_cpuidle_init(int cpu)
 141{
 142        struct device_node *cpu_node = of_cpu_device_node_get(cpu);
 143        int ret;
 144
 145        if (!cpu_node)
 146                return -ENODEV;
 147
 148        ret = arm_cpuidle_read_ops(cpu_node, cpu);
 149        if (!ret)
 150                ret = cpuidle_ops[cpu].init(cpu_node, cpu);
 151
 152        of_node_put(cpu_node);
 153
 154        return ret;
 155}
 156