cpuidle.c 4.2 KB

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