dtpm_cpu.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2020 Linaro Limited
  4. *
  5. * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6. *
  7. * The DTPM CPU is based on the energy model. It hooks the CPU in the
  8. * DTPM tree which in turns update the power number by propagating the
  9. * power number from the CPU energy model information to the parents.
  10. *
  11. * The association between the power and the performance state, allows
  12. * to set the power of the CPU at the OPP granularity.
  13. *
  14. * The CPU hotplug is supported and the power numbers will be updated
  15. * if a CPU is hot plugged / unplugged.
  16. */
  17. #include <linux/cpumask.h>
  18. #include <linux/cpufreq.h>
  19. #include <linux/cpuhotplug.h>
  20. #include <linux/dtpm.h>
  21. #include <linux/energy_model.h>
  22. #include <linux/pm_qos.h>
  23. #include <linux/slab.h>
  24. #include <linux/units.h>
  25. static struct dtpm *__parent;
  26. static DEFINE_PER_CPU(struct dtpm *, dtpm_per_cpu);
  27. struct dtpm_cpu {
  28. struct freq_qos_request qos_req;
  29. int cpu;
  30. };
  31. /*
  32. * When a new CPU is inserted at hotplug or boot time, add the power
  33. * contribution and update the dtpm tree.
  34. */
  35. static int power_add(struct dtpm *dtpm, struct em_perf_domain *em)
  36. {
  37. u64 power_min, power_max;
  38. power_min = em->table[0].power;
  39. power_min *= MICROWATT_PER_MILLIWATT;
  40. power_min += dtpm->power_min;
  41. power_max = em->table[em->nr_perf_states - 1].power;
  42. power_max *= MICROWATT_PER_MILLIWATT;
  43. power_max += dtpm->power_max;
  44. return dtpm_update_power(dtpm, power_min, power_max);
  45. }
  46. /*
  47. * When a CPU is unplugged, remove its power contribution from the
  48. * dtpm tree.
  49. */
  50. static int power_sub(struct dtpm *dtpm, struct em_perf_domain *em)
  51. {
  52. u64 power_min, power_max;
  53. power_min = em->table[0].power;
  54. power_min *= MICROWATT_PER_MILLIWATT;
  55. power_min = dtpm->power_min - power_min;
  56. power_max = em->table[em->nr_perf_states - 1].power;
  57. power_max *= MICROWATT_PER_MILLIWATT;
  58. power_max = dtpm->power_max - power_max;
  59. return dtpm_update_power(dtpm, power_min, power_max);
  60. }
  61. static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
  62. {
  63. struct dtpm_cpu *dtpm_cpu = dtpm->private;
  64. struct em_perf_domain *pd;
  65. struct cpumask cpus;
  66. unsigned long freq;
  67. u64 power;
  68. int i, nr_cpus;
  69. pd = em_cpu_get(dtpm_cpu->cpu);
  70. cpumask_and(&cpus, cpu_online_mask, to_cpumask(pd->cpus));
  71. nr_cpus = cpumask_weight(&cpus);
  72. for (i = 0; i < pd->nr_perf_states; i++) {
  73. power = pd->table[i].power * MICROWATT_PER_MILLIWATT * nr_cpus;
  74. if (power > power_limit)
  75. break;
  76. }
  77. freq = pd->table[i - 1].frequency;
  78. freq_qos_update_request(&dtpm_cpu->qos_req, freq);
  79. power_limit = pd->table[i - 1].power *
  80. MICROWATT_PER_MILLIWATT * nr_cpus;
  81. return power_limit;
  82. }
  83. static u64 get_pd_power_uw(struct dtpm *dtpm)
  84. {
  85. struct dtpm_cpu *dtpm_cpu = dtpm->private;
  86. struct em_perf_domain *pd;
  87. struct cpumask cpus;
  88. unsigned long freq;
  89. int i, nr_cpus;
  90. pd = em_cpu_get(dtpm_cpu->cpu);
  91. freq = cpufreq_quick_get(dtpm_cpu->cpu);
  92. cpumask_and(&cpus, cpu_online_mask, to_cpumask(pd->cpus));
  93. nr_cpus = cpumask_weight(&cpus);
  94. for (i = 0; i < pd->nr_perf_states; i++) {
  95. if (pd->table[i].frequency < freq)
  96. continue;
  97. return pd->table[i].power *
  98. MICROWATT_PER_MILLIWATT * nr_cpus;
  99. }
  100. return 0;
  101. }
  102. static void pd_release(struct dtpm *dtpm)
  103. {
  104. struct dtpm_cpu *dtpm_cpu = dtpm->private;
  105. if (freq_qos_request_active(&dtpm_cpu->qos_req))
  106. freq_qos_remove_request(&dtpm_cpu->qos_req);
  107. kfree(dtpm_cpu);
  108. }
  109. static struct dtpm_ops dtpm_ops = {
  110. .set_power_uw = set_pd_power_limit,
  111. .get_power_uw = get_pd_power_uw,
  112. .release = pd_release,
  113. };
  114. static int cpuhp_dtpm_cpu_offline(unsigned int cpu)
  115. {
  116. struct cpufreq_policy *policy;
  117. struct em_perf_domain *pd;
  118. struct dtpm *dtpm;
  119. policy = cpufreq_cpu_get(cpu);
  120. if (!policy)
  121. return 0;
  122. pd = em_cpu_get(cpu);
  123. if (!pd)
  124. return -EINVAL;
  125. dtpm = per_cpu(dtpm_per_cpu, cpu);
  126. power_sub(dtpm, pd);
  127. if (cpumask_weight(policy->cpus) != 1)
  128. return 0;
  129. for_each_cpu(cpu, policy->related_cpus)
  130. per_cpu(dtpm_per_cpu, cpu) = NULL;
  131. dtpm_unregister(dtpm);
  132. return 0;
  133. }
  134. static int cpuhp_dtpm_cpu_online(unsigned int cpu)
  135. {
  136. struct dtpm *dtpm;
  137. struct dtpm_cpu *dtpm_cpu;
  138. struct cpufreq_policy *policy;
  139. struct em_perf_domain *pd;
  140. char name[CPUFREQ_NAME_LEN];
  141. int ret = -ENOMEM;
  142. policy = cpufreq_cpu_get(cpu);
  143. if (!policy)
  144. return 0;
  145. pd = em_cpu_get(cpu);
  146. if (!pd)
  147. return -EINVAL;
  148. dtpm = per_cpu(dtpm_per_cpu, cpu);
  149. if (dtpm)
  150. return power_add(dtpm, pd);
  151. dtpm = dtpm_alloc(&dtpm_ops);
  152. if (!dtpm)
  153. return -EINVAL;
  154. dtpm_cpu = kzalloc(sizeof(*dtpm_cpu), GFP_KERNEL);
  155. if (!dtpm_cpu)
  156. goto out_kfree_dtpm;
  157. dtpm->private = dtpm_cpu;
  158. dtpm_cpu->cpu = cpu;
  159. for_each_cpu(cpu, policy->related_cpus)
  160. per_cpu(dtpm_per_cpu, cpu) = dtpm;
  161. sprintf(name, "cpu%d", dtpm_cpu->cpu);
  162. ret = dtpm_register(name, dtpm, __parent);
  163. if (ret)
  164. goto out_kfree_dtpm_cpu;
  165. ret = power_add(dtpm, pd);
  166. if (ret)
  167. goto out_dtpm_unregister;
  168. ret = freq_qos_add_request(&policy->constraints,
  169. &dtpm_cpu->qos_req, FREQ_QOS_MAX,
  170. pd->table[pd->nr_perf_states - 1].frequency);
  171. if (ret)
  172. goto out_power_sub;
  173. return 0;
  174. out_power_sub:
  175. power_sub(dtpm, pd);
  176. out_dtpm_unregister:
  177. dtpm_unregister(dtpm);
  178. dtpm_cpu = NULL;
  179. dtpm = NULL;
  180. out_kfree_dtpm_cpu:
  181. for_each_cpu(cpu, policy->related_cpus)
  182. per_cpu(dtpm_per_cpu, cpu) = NULL;
  183. kfree(dtpm_cpu);
  184. out_kfree_dtpm:
  185. kfree(dtpm);
  186. return ret;
  187. }
  188. int dtpm_register_cpu(struct dtpm *parent)
  189. {
  190. __parent = parent;
  191. return cpuhp_setup_state(CPUHP_AP_DTPM_CPU_ONLINE,
  192. "dtpm_cpu:online",
  193. cpuhp_dtpm_cpu_online,
  194. cpuhp_dtpm_cpu_offline);
  195. }