energy.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. * Obtain energy cost data from DT and populate relevant scheduler data
  3. * structures.
  4. *
  5. * Copyright (C) 2015 ARM Ltd.
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #define pr_fmt(fmt) "sched-energy: " fmt
  20. #include <linux/gfp.h>
  21. #include <linux/of.h>
  22. #include <linux/printk.h>
  23. #include <linux/sched.h>
  24. #include <linux/sched/topology.h>
  25. #include <linux/sched/energy.h>
  26. #include <linux/stddef.h>
  27. #include <linux/arch_topology.h>
  28. #include <linux/cpu.h>
  29. #include <linux/pm_opp.h>
  30. #include <linux/platform_device.h>
  31. #include "energy_plus.h"
  32. #include "sched.h"
  33. struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
  34. #ifndef CONFIG_MTK_UNIFY_POWER
  35. static void free_resources(void)
  36. {
  37. int cpu, sd_level;
  38. struct sched_group_energy *sge;
  39. for_each_possible_cpu(cpu) {
  40. for_each_possible_sd_level(sd_level) {
  41. sge = sge_array[cpu][sd_level];
  42. if (sge) {
  43. kfree(sge->cap_states);
  44. kfree(sge->idle_states);
  45. kfree(sge);
  46. }
  47. }
  48. }
  49. }
  50. static bool sge_ready;
  51. static bool freq_energy_model;
  52. void check_max_cap_vs_cpu_scale(int cpu, struct sched_group_energy *sge)
  53. {
  54. unsigned long max_cap, cpu_scale;
  55. max_cap = sge->cap_states[sge->nr_cap_states - 1].cap;
  56. cpu_scale = topology_get_cpu_scale(NULL, cpu);
  57. if (max_cap == cpu_scale)
  58. return;
  59. pr_warn("CPU%d max energy model capacity=%ld != cpu_scale=%ld\n", cpu,
  60. max_cap, cpu_scale);
  61. }
  62. void init_sched_energy_costs(void)
  63. {
  64. struct device_node *cn, *cp;
  65. struct capacity_state *cap_states;
  66. struct idle_state *idle_states;
  67. struct sched_group_energy *sge;
  68. const struct property *prop;
  69. int sd_level, i, nstates, cpu;
  70. const __be32 *val;
  71. for_each_possible_cpu(cpu) {
  72. cn = of_get_cpu_node(cpu, NULL);
  73. if (!cn) {
  74. pr_warn("CPU device node missing for CPU %d\n", cpu);
  75. return;
  76. }
  77. if (!of_find_property(cn, "sched-energy-costs", NULL)) {
  78. pr_warn("CPU device node has no sched-energy-costs\n");
  79. return;
  80. }
  81. /* Check if the energy model contains frequency/power values */
  82. if (of_find_property(cn, "freq-energy-model", NULL))
  83. freq_energy_model = true;
  84. for_each_possible_sd_level(sd_level) {
  85. cp = of_parse_phandle(cn, "sched-energy-costs", sd_level);
  86. if (!cp)
  87. break;
  88. prop = of_find_property(cp, "busy-cost-data", NULL);
  89. if (!prop || !prop->value) {
  90. pr_warn("No busy-cost data, skipping sched_energy init\n");
  91. goto out;
  92. }
  93. sge = kcalloc(1, sizeof(struct sched_group_energy),
  94. GFP_NOWAIT);
  95. nstates = (prop->length / sizeof(u32)) / 2;
  96. cap_states = kcalloc(nstates,
  97. sizeof(struct capacity_state),
  98. GFP_NOWAIT);
  99. for (i = 0, val = prop->value; i < nstates; i++) {
  100. if (freq_energy_model) {
  101. /*
  102. * Capacity values will be calculated later using
  103. * frequency reported by OPP driver and cpu_uarch_scale
  104. * values.
  105. */
  106. cap_states[i].frequency = be32_to_cpup(val++);
  107. cap_states[i].cap = 0;
  108. } else {
  109. cap_states[i].frequency = 0;
  110. cap_states[i].cap = be32_to_cpup(val++);
  111. }
  112. cap_states[i].power = be32_to_cpup(val++);
  113. }
  114. sge->nr_cap_states = nstates;
  115. sge->cap_states = cap_states;
  116. prop = of_find_property(cp, "idle-cost-data", NULL);
  117. if (!prop || !prop->value) {
  118. pr_warn("No idle-cost data, skipping sched_energy init\n");
  119. goto out;
  120. }
  121. nstates = (prop->length / sizeof(u32));
  122. idle_states = kcalloc(nstates,
  123. sizeof(struct idle_state),
  124. GFP_NOWAIT);
  125. for (i = 0, val = prop->value; i < nstates; i++)
  126. idle_states[i].power = be32_to_cpup(val++);
  127. sge->nr_idle_states = nstates;
  128. sge->idle_states = idle_states;
  129. sge_array[cpu][sd_level] = sge;
  130. }
  131. if (!freq_energy_model)
  132. check_max_cap_vs_cpu_scale(cpu, sge_array[cpu][SD_LEVEL0]);
  133. }
  134. sge_ready = true;
  135. pr_info("Sched-energy-costs installed from DT\n");
  136. return;
  137. out:
  138. free_resources();
  139. }
  140. static int sched_energy_probe(struct platform_device *pdev)
  141. {
  142. int cpu;
  143. unsigned long *max_frequencies = NULL;
  144. int ret;
  145. if (!sge_ready)
  146. return -EPROBE_DEFER;
  147. if (!energy_aware() || !freq_energy_model)
  148. return 0;
  149. max_frequencies = kmalloc_array(nr_cpu_ids, sizeof(unsigned long),
  150. GFP_KERNEL);
  151. if (!max_frequencies) {
  152. ret = -ENOMEM;
  153. goto exit;
  154. }
  155. /*
  156. * Find system max possible frequency and max frequencies for each
  157. * CPUs.
  158. */
  159. for_each_possible_cpu(cpu) {
  160. struct device *cpu_dev;
  161. struct dev_pm_opp *opp;
  162. cpu_dev = get_cpu_device(cpu);
  163. if (IS_ERR_OR_NULL(cpu_dev)) {
  164. if (!cpu_dev)
  165. ret = -EINVAL;
  166. else
  167. ret = PTR_ERR(cpu_dev);
  168. goto exit;
  169. }
  170. max_frequencies[cpu] = ULONG_MAX;
  171. opp = dev_pm_opp_find_freq_floor(cpu_dev,
  172. &max_frequencies[cpu]);
  173. if (IS_ERR_OR_NULL(opp)) {
  174. if (!opp || PTR_ERR(opp) == -ENODEV)
  175. ret = -EPROBE_DEFER;
  176. else
  177. ret = PTR_ERR(opp);
  178. goto exit;
  179. }
  180. /* Convert HZ to KHZ */
  181. max_frequencies[cpu] /= 1000;
  182. }
  183. /* update capacity in energy model */
  184. for_each_possible_cpu(cpu) {
  185. unsigned long cpu_max_cap;
  186. struct sched_group_energy *sge_l0, *sge;
  187. cpu_max_cap = topology_get_cpu_scale(NULL, cpu);
  188. /*
  189. * All the cap_states have same frequency table so use
  190. * SD_LEVEL0's.
  191. */
  192. sge_l0 = sge_array[cpu][SD_LEVEL0];
  193. if (sge_l0 && sge_l0->nr_cap_states > 0) {
  194. int i;
  195. int ncapstates = sge_l0->nr_cap_states;
  196. for (i = 0; i < ncapstates; i++) {
  197. int sd_level;
  198. unsigned long freq, cap;
  199. /*
  200. * Energy model can contain more frequency
  201. * steps than actual for multiple speedbin
  202. * support. Ceil the max capacity with actual
  203. * one.
  204. */
  205. freq = min(sge_l0->cap_states[i].frequency,
  206. max_frequencies[cpu]);
  207. cap = DIV_ROUND_UP(cpu_max_cap * freq,
  208. max_frequencies[cpu]);
  209. for_each_possible_sd_level(sd_level) {
  210. sge = sge_array[cpu][sd_level];
  211. if (!sge)
  212. break;
  213. sge->cap_states[i].cap = cap;
  214. }
  215. dev_dbg(&pdev->dev,
  216. "cpu=%d freq=%ld cap=%ld power_d0=%ld\n",
  217. cpu, freq, sge_l0->cap_states[i].cap,
  218. sge_l0->cap_states[i].power);
  219. }
  220. dev_info(&pdev->dev,
  221. "cpu=%d [freq=%ld cap=%ld power_d0=%ld] -> [freq=%ld cap=%ld power_d0=%ld]\n",
  222. cpu,
  223. sge_l0->cap_states[0].frequency,
  224. sge_l0->cap_states[0].cap,
  225. sge_l0->cap_states[0].power,
  226. sge_l0->cap_states[ncapstates - 1].frequency,
  227. sge_l0->cap_states[ncapstates - 1].cap,
  228. sge_l0->cap_states[ncapstates - 1].power
  229. );
  230. }
  231. }
  232. kfree(max_frequencies);
  233. dev_info(&pdev->dev, "Sched-energy-costs capacity updated\n");
  234. return 0;
  235. exit:
  236. if (ret != -EPROBE_DEFER)
  237. dev_err(&pdev->dev, "error=%d\n", ret);
  238. kfree(max_frequencies);
  239. return ret;
  240. }
  241. static struct platform_driver energy_driver = {
  242. .driver = {
  243. .name = "sched-energy",
  244. },
  245. .probe = sched_energy_probe,
  246. };
  247. static struct platform_device energy_device = {
  248. .name = "sched-energy",
  249. };
  250. static int __init sched_energy_init(void)
  251. {
  252. int ret;
  253. ret = platform_device_register(&energy_device);
  254. if (ret)
  255. pr_err("%s device_register failed:%d\n", __func__, ret);
  256. ret = platform_driver_register(&energy_driver);
  257. if (ret) {
  258. pr_err("%s driver_register failed:%d\n", __func__, ret);
  259. platform_device_unregister(&energy_device);
  260. }
  261. return ret;
  262. }
  263. subsys_initcall(sched_energy_init);
  264. #endif
  265. #include "energy_plus.c"