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