qcom-simple-lpm.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
  4. */
  5. #include <linux/cpu.h>
  6. #include <linux/cpuidle.h>
  7. #include <linux/ktime.h>
  8. #include <linux/module.h>
  9. #include <linux/pm_qos.h>
  10. #include <linux/smp.h>
  11. #include <linux/string.h>
  12. #include <linux/suspend.h>
  13. #include <linux/tick.h>
  14. #include <trace/events/power.h>
  15. #include "qcom-simple-lpm.h"
  16. static bool cluster_gov_registered;
  17. bool simple_sleep_disabled = true;
  18. u64 cur_div = 100;
  19. static bool suspend_in_progress;
  20. static struct simple_cluster_governor *cluster_simple_gov_ops;
  21. DEFINE_PER_CPU(struct simple_lpm_cpu, simple_lpm_cpu_data);
  22. static inline bool check_cpu_isactive(int cpu)
  23. {
  24. return cpu_active(cpu);
  25. }
  26. static int simple_lpm_cpu_qos_notify(struct notifier_block *nfb,
  27. unsigned long val, void *ptr)
  28. {
  29. struct simple_lpm_cpu *cpu_gov = container_of(nfb, struct simple_lpm_cpu, nb);
  30. int cpu = cpu_gov->cpu;
  31. if (!cpu_gov->enable)
  32. return NOTIFY_OK;
  33. preempt_disable();
  34. if (cpu != smp_processor_id() && cpu_online(cpu) &&
  35. check_cpu_isactive(cpu))
  36. wake_up_if_idle(cpu);
  37. preempt_enable();
  38. return NOTIFY_OK;
  39. }
  40. static int lpm_offline_cpu(unsigned int cpu)
  41. {
  42. struct simple_lpm_cpu *cpu_gov = per_cpu_ptr(&simple_lpm_cpu_data, cpu);
  43. struct device *dev = get_cpu_device(cpu);
  44. if (!dev || !cpu_gov)
  45. return 0;
  46. dev_pm_qos_remove_notifier(dev, &cpu_gov->nb, DEV_PM_QOS_RESUME_LATENCY);
  47. return 0;
  48. }
  49. static int lpm_online_cpu(unsigned int cpu)
  50. {
  51. struct simple_lpm_cpu *cpu_gov = per_cpu_ptr(&simple_lpm_cpu_data, cpu);
  52. struct device *dev = get_cpu_device(cpu);
  53. if (!dev || !cpu_gov)
  54. return 0;
  55. cpu_gov->nb.notifier_call = simple_lpm_cpu_qos_notify;
  56. dev_pm_qos_add_notifier(dev, &cpu_gov->nb, DEV_PM_QOS_RESUME_LATENCY);
  57. return 0;
  58. }
  59. /**
  60. * get_cpus_qos() - Returns the aggrigated PM QoS request.
  61. * @mask: cpumask of the cpus
  62. */
  63. static inline s64 get_cpus_qos(const struct cpumask *mask)
  64. {
  65. int cpu;
  66. s64 n, latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
  67. for_each_cpu(cpu, mask) {
  68. if (!check_cpu_isactive(cpu))
  69. continue;
  70. n = cpuidle_governor_latency_req(cpu);
  71. do_div(n, NSEC_PER_USEC);
  72. if (n < latency)
  73. latency = n;
  74. }
  75. return latency;
  76. }
  77. void register_cluster_simple_governor_ops(struct simple_cluster_governor *ops)
  78. {
  79. if (!ops)
  80. return;
  81. cluster_simple_gov_ops = ops;
  82. }
  83. void unregister_cluster_simple_governor_ops(struct simple_cluster_governor *ops)
  84. {
  85. if (ops != cluster_simple_gov_ops)
  86. return;
  87. cluster_simple_gov_ops = NULL;
  88. }
  89. /**
  90. * lpm_select() - Find the best idle state for the cpu device
  91. * @dev: Target cpu
  92. * @state: Entered state
  93. * @stop_tick: Is the tick device stopped
  94. *
  95. * Return: Best cpu LPM mode to enter
  96. */
  97. static int lpm_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
  98. bool *stop_tick)
  99. {
  100. struct simple_lpm_cpu *cpu_gov = this_cpu_ptr(&simple_lpm_cpu_data);
  101. u64 latency_req = get_cpus_qos(cpumask_of(dev->cpu));
  102. ktime_t delta_tick;
  103. s64 duration_ns;
  104. int i = 0;
  105. if (!cpu_gov)
  106. return 0;
  107. if (simple_sleep_disabled)
  108. return 0;
  109. duration_ns = tick_nohz_get_sleep_length(&delta_tick);
  110. if (duration_ns < 0)
  111. return 0;
  112. for (i = drv->state_count - 1; i > 0; i--) {
  113. struct cpuidle_state *s = &drv->states[i];
  114. u64 target_latency = s->exit_latency;
  115. s64 target_residency = s->target_residency_ns;
  116. do_div(target_latency, cur_div);
  117. do_div(target_residency, cur_div);
  118. if (dev->states_usage[i].disable)
  119. continue;
  120. if (latency_req < target_latency)
  121. continue;
  122. if (target_residency > duration_ns)
  123. continue;
  124. break;
  125. }
  126. cpu_gov->last_idx = i;
  127. return i;
  128. }
  129. /**
  130. * lpm_reflect() - Update the state entered by the cpu device
  131. * @dev: Target CPU
  132. * @state: Entered state
  133. */
  134. static void lpm_reflect(struct cpuidle_device *dev, int state)
  135. {
  136. }
  137. /**
  138. * lpm_enable_device() - Initialize the governor's data for the CPU
  139. * @drv: cpuidle driver
  140. * @dev: Target CPU
  141. */
  142. static int lpm_enable_device(struct cpuidle_driver *drv,
  143. struct cpuidle_device *dev)
  144. {
  145. struct simple_lpm_cpu *cpu_gov = per_cpu_ptr(&simple_lpm_cpu_data, dev->cpu);
  146. cpu_gov->cpu = dev->cpu;
  147. cpu_gov->enable = true;
  148. cpu_gov->drv = drv;
  149. cpu_gov->dev = dev;
  150. cpu_gov->last_idx = -1;
  151. if (!cluster_gov_registered) {
  152. if (cluster_simple_gov_ops && cluster_simple_gov_ops->enable)
  153. cluster_simple_gov_ops->enable();
  154. cluster_gov_registered = true;
  155. }
  156. return 0;
  157. }
  158. /**
  159. * lpm_disable_device() - Clean up the governor's data for the CPU
  160. * @drv: cpuidle driver
  161. * @dev: Target CPU
  162. */
  163. static void lpm_disable_device(struct cpuidle_driver *drv,
  164. struct cpuidle_device *dev)
  165. {
  166. struct simple_lpm_cpu *cpu_gov = per_cpu_ptr(&simple_lpm_cpu_data, dev->cpu);
  167. int cpu;
  168. cpu_gov->enable = false;
  169. cpu_gov->last_idx = -1;
  170. for_each_possible_cpu(cpu) {
  171. struct simple_lpm_cpu *cpu_gov = per_cpu_ptr(&simple_lpm_cpu_data, cpu);
  172. if (cpu_gov->enable)
  173. return;
  174. }
  175. if (cluster_gov_registered) {
  176. if (cluster_simple_gov_ops && cluster_simple_gov_ops->disable)
  177. cluster_simple_gov_ops->disable();
  178. cluster_gov_registered = false;
  179. }
  180. }
  181. static void qcom_lpm_suspend_trace(void *unused, const char *action,
  182. int event, bool start)
  183. {
  184. int cpu;
  185. if (start && !strcmp("dpm_suspend_late", action)) {
  186. suspend_in_progress = true;
  187. for_each_online_cpu(cpu)
  188. wake_up_if_idle(cpu);
  189. return;
  190. }
  191. if (!start && !strcmp("dpm_resume_early", action)) {
  192. suspend_in_progress = false;
  193. for_each_online_cpu(cpu)
  194. wake_up_if_idle(cpu);
  195. }
  196. }
  197. static struct cpuidle_governor lpm_simple_governor = {
  198. .name = "qcom-simple-lpm",
  199. .rating = 10,
  200. .enable = lpm_enable_device,
  201. .disable = lpm_disable_device,
  202. .select = lpm_select,
  203. .reflect = lpm_reflect,
  204. };
  205. static int __init qcom_lpm_simple_governor_init(void)
  206. {
  207. int ret;
  208. ret = create_simple_gov_global_sysfs_nodes();
  209. if (ret)
  210. goto sysfs_fail;
  211. ret = qcom_cluster_lpm_simple_governor_init();
  212. if (ret)
  213. goto cluster_init_fail;
  214. ret = cpuidle_register_governor(&lpm_simple_governor);
  215. if (ret)
  216. goto cpuidle_reg_fail;
  217. ret = register_trace_suspend_resume(qcom_lpm_suspend_trace, NULL);
  218. if (ret)
  219. goto cpuidle_reg_fail;
  220. ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "qcom-simple-lpm",
  221. lpm_online_cpu, lpm_offline_cpu);
  222. if (ret < 0)
  223. goto cpuhp_setup_fail;
  224. return 0;
  225. cpuhp_setup_fail:
  226. unregister_trace_suspend_resume(qcom_lpm_suspend_trace, NULL);
  227. cpuidle_reg_fail:
  228. qcom_cluster_lpm_simple_governor_deinit();
  229. cluster_init_fail:
  230. remove_simple_gov_global_sysfs_nodes();
  231. sysfs_fail:
  232. return ret;
  233. }
  234. module_init(qcom_lpm_simple_governor_init);
  235. MODULE_DESCRIPTION("Qualcomm Technologies, Inc. simple LPM governor");
  236. MODULE_LICENSE("GPL");