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