freq_table.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * linux/drivers/cpufreq/freq_table.c
  4. *
  5. * Copyright (C) 2002 - 2003 Dominik Brodowski
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/cpufreq.h>
  9. #include <linux/module.h>
  10. #include <trace/hooks/cpufreq.h>
  11. /*********************************************************************
  12. * FREQUENCY TABLE HELPERS *
  13. *********************************************************************/
  14. bool policy_has_boost_freq(struct cpufreq_policy *policy)
  15. {
  16. struct cpufreq_frequency_table *pos, *table = policy->freq_table;
  17. if (!table)
  18. return false;
  19. cpufreq_for_each_valid_entry(pos, table)
  20. if (pos->flags & CPUFREQ_BOOST_FREQ)
  21. return true;
  22. return false;
  23. }
  24. EXPORT_SYMBOL_GPL(policy_has_boost_freq);
  25. int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
  26. struct cpufreq_frequency_table *table)
  27. {
  28. struct cpufreq_frequency_table *pos;
  29. unsigned int min_freq = ~0;
  30. unsigned int max_freq = 0;
  31. unsigned int freq;
  32. cpufreq_for_each_valid_entry(pos, table) {
  33. freq = pos->frequency;
  34. if (!cpufreq_boost_enabled()
  35. && (pos->flags & CPUFREQ_BOOST_FREQ))
  36. continue;
  37. pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
  38. if (freq < min_freq)
  39. min_freq = freq;
  40. if (freq > max_freq)
  41. max_freq = freq;
  42. }
  43. trace_android_vh_freq_table_limits(policy, min_freq, max_freq);
  44. policy->min = policy->cpuinfo.min_freq = min_freq;
  45. policy->max = max_freq;
  46. /*
  47. * If the driver has set its own cpuinfo.max_freq above max_freq, leave
  48. * it as is.
  49. */
  50. if (policy->cpuinfo.max_freq < max_freq)
  51. policy->max = policy->cpuinfo.max_freq = max_freq;
  52. if (policy->min == ~0)
  53. return -EINVAL;
  54. else
  55. return 0;
  56. }
  57. int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy,
  58. struct cpufreq_frequency_table *table)
  59. {
  60. struct cpufreq_frequency_table *pos;
  61. unsigned int freq, next_larger = ~0;
  62. bool found = false;
  63. pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
  64. policy->min, policy->max, policy->cpu);
  65. cpufreq_verify_within_cpu_limits(policy);
  66. cpufreq_for_each_valid_entry(pos, table) {
  67. freq = pos->frequency;
  68. if ((freq >= policy->min) && (freq <= policy->max)) {
  69. found = true;
  70. break;
  71. }
  72. if ((next_larger > freq) && (freq > policy->max))
  73. next_larger = freq;
  74. }
  75. if (!found) {
  76. policy->max = next_larger;
  77. cpufreq_verify_within_cpu_limits(policy);
  78. }
  79. pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
  80. policy->min, policy->max, policy->cpu);
  81. return 0;
  82. }
  83. EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
  84. /*
  85. * Generic routine to verify policy & frequency table, requires driver to set
  86. * policy->freq_table prior to it.
  87. */
  88. int cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data *policy)
  89. {
  90. if (!policy->freq_table)
  91. return -ENODEV;
  92. return cpufreq_frequency_table_verify(policy, policy->freq_table);
  93. }
  94. EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
  95. int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
  96. unsigned int target_freq,
  97. unsigned int relation)
  98. {
  99. struct cpufreq_frequency_table optimal = {
  100. .driver_data = ~0,
  101. .frequency = 0,
  102. };
  103. struct cpufreq_frequency_table suboptimal = {
  104. .driver_data = ~0,
  105. .frequency = 0,
  106. };
  107. struct cpufreq_frequency_table *pos;
  108. struct cpufreq_frequency_table *table = policy->freq_table;
  109. unsigned int freq, diff, i = 0;
  110. int index;
  111. pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
  112. target_freq, relation, policy->cpu);
  113. switch (relation) {
  114. case CPUFREQ_RELATION_H:
  115. suboptimal.frequency = ~0;
  116. break;
  117. case CPUFREQ_RELATION_L:
  118. case CPUFREQ_RELATION_C:
  119. optimal.frequency = ~0;
  120. break;
  121. }
  122. cpufreq_for_each_valid_entry_idx(pos, table, i) {
  123. freq = pos->frequency;
  124. if ((freq < policy->min) || (freq > policy->max))
  125. continue;
  126. if (freq == target_freq) {
  127. optimal.driver_data = i;
  128. break;
  129. }
  130. switch (relation) {
  131. case CPUFREQ_RELATION_H:
  132. if (freq < target_freq) {
  133. if (freq >= optimal.frequency) {
  134. optimal.frequency = freq;
  135. optimal.driver_data = i;
  136. }
  137. } else {
  138. if (freq <= suboptimal.frequency) {
  139. suboptimal.frequency = freq;
  140. suboptimal.driver_data = i;
  141. }
  142. }
  143. break;
  144. case CPUFREQ_RELATION_L:
  145. if (freq > target_freq) {
  146. if (freq <= optimal.frequency) {
  147. optimal.frequency = freq;
  148. optimal.driver_data = i;
  149. }
  150. } else {
  151. if (freq >= suboptimal.frequency) {
  152. suboptimal.frequency = freq;
  153. suboptimal.driver_data = i;
  154. }
  155. }
  156. break;
  157. case CPUFREQ_RELATION_C:
  158. diff = abs(freq - target_freq);
  159. if (diff < optimal.frequency ||
  160. (diff == optimal.frequency &&
  161. freq > table[optimal.driver_data].frequency)) {
  162. optimal.frequency = diff;
  163. optimal.driver_data = i;
  164. }
  165. break;
  166. }
  167. }
  168. if (optimal.driver_data > i) {
  169. if (suboptimal.driver_data > i) {
  170. WARN(1, "Invalid frequency table: %d\n", policy->cpu);
  171. return 0;
  172. }
  173. index = suboptimal.driver_data;
  174. } else
  175. index = optimal.driver_data;
  176. pr_debug("target index is %u, freq is:%u kHz\n", index,
  177. table[index].frequency);
  178. return index;
  179. }
  180. EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
  181. int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
  182. unsigned int freq)
  183. {
  184. struct cpufreq_frequency_table *pos, *table = policy->freq_table;
  185. int idx;
  186. if (unlikely(!table)) {
  187. pr_debug("%s: Unable to find frequency table\n", __func__);
  188. return -ENOENT;
  189. }
  190. cpufreq_for_each_valid_entry_idx(pos, table, idx)
  191. if (pos->frequency == freq)
  192. return idx;
  193. return -EINVAL;
  194. }
  195. EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
  196. /*
  197. * show_available_freqs - show available frequencies for the specified CPU
  198. */
  199. static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
  200. bool show_boost)
  201. {
  202. ssize_t count = 0;
  203. struct cpufreq_frequency_table *pos, *table = policy->freq_table;
  204. if (!table)
  205. return -ENODEV;
  206. cpufreq_for_each_valid_entry(pos, table) {
  207. /*
  208. * show_boost = true and driver_data = BOOST freq
  209. * display BOOST freqs
  210. *
  211. * show_boost = false and driver_data = BOOST freq
  212. * show_boost = true and driver_data != BOOST freq
  213. * continue - do not display anything
  214. *
  215. * show_boost = false and driver_data != BOOST freq
  216. * display NON BOOST freqs
  217. */
  218. if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
  219. continue;
  220. count += sprintf(&buf[count], "%d ", pos->frequency);
  221. }
  222. count += sprintf(&buf[count], "\n");
  223. return count;
  224. }
  225. #define cpufreq_attr_available_freq(_name) \
  226. struct freq_attr cpufreq_freq_attr_##_name##_freqs = \
  227. __ATTR_RO(_name##_frequencies)
  228. /*
  229. * show_scaling_available_frequencies - show available normal frequencies for
  230. * the specified CPU
  231. */
  232. static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
  233. char *buf)
  234. {
  235. return show_available_freqs(policy, buf, false);
  236. }
  237. cpufreq_attr_available_freq(scaling_available);
  238. EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
  239. /*
  240. * show_available_boost_freqs - show available boost frequencies for
  241. * the specified CPU
  242. */
  243. static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
  244. char *buf)
  245. {
  246. return show_available_freqs(policy, buf, true);
  247. }
  248. cpufreq_attr_available_freq(scaling_boost);
  249. EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
  250. struct freq_attr *cpufreq_generic_attr[] = {
  251. &cpufreq_freq_attr_scaling_available_freqs,
  252. NULL,
  253. };
  254. EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
  255. static int set_freq_table_sorted(struct cpufreq_policy *policy)
  256. {
  257. struct cpufreq_frequency_table *pos, *table = policy->freq_table;
  258. struct cpufreq_frequency_table *prev = NULL;
  259. int ascending = 0;
  260. policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;
  261. cpufreq_for_each_valid_entry(pos, table) {
  262. if (!prev) {
  263. prev = pos;
  264. continue;
  265. }
  266. if (pos->frequency == prev->frequency) {
  267. pr_warn("Duplicate freq-table entries: %u\n",
  268. pos->frequency);
  269. return -EINVAL;
  270. }
  271. /* Frequency increased from prev to pos */
  272. if (pos->frequency > prev->frequency) {
  273. /* But frequency was decreasing earlier */
  274. if (ascending < 0) {
  275. pr_debug("Freq table is unsorted\n");
  276. return 0;
  277. }
  278. ascending++;
  279. } else {
  280. /* Frequency decreased from prev to pos */
  281. /* But frequency was increasing earlier */
  282. if (ascending > 0) {
  283. pr_debug("Freq table is unsorted\n");
  284. return 0;
  285. }
  286. ascending--;
  287. }
  288. prev = pos;
  289. }
  290. if (ascending > 0)
  291. policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;
  292. else
  293. policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING;
  294. pr_debug("Freq table is sorted in %s order\n",
  295. ascending > 0 ? "ascending" : "descending");
  296. return 0;
  297. }
  298. int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
  299. {
  300. int ret;
  301. if (!policy->freq_table)
  302. return 0;
  303. ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
  304. if (ret)
  305. return ret;
  306. return set_freq_table_sorted(policy);
  307. }
  308. MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
  309. MODULE_DESCRIPTION("CPUfreq frequency table helpers");
  310. MODULE_LICENSE("GPL");