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