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