intel_epb.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Intel Performance and Energy Bias Hint support.
  4. *
  5. * Copyright (C) 2019 Intel Corporation
  6. *
  7. * Author:
  8. * Rafael J. Wysocki <[email protected]>
  9. */
  10. #include <linux/cpuhotplug.h>
  11. #include <linux/cpu.h>
  12. #include <linux/device.h>
  13. #include <linux/kernel.h>
  14. #include <linux/string.h>
  15. #include <linux/syscore_ops.h>
  16. #include <linux/pm.h>
  17. #include <asm/cpu_device_id.h>
  18. #include <asm/cpufeature.h>
  19. #include <asm/msr.h>
  20. /**
  21. * DOC: overview
  22. *
  23. * The Performance and Energy Bias Hint (EPB) allows software to specify its
  24. * preference with respect to the power-performance tradeoffs present in the
  25. * processor. Generally, the EPB is expected to be set by user space (directly
  26. * via sysfs or with the help of the x86_energy_perf_policy tool), but there are
  27. * two reasons for the kernel to update it.
  28. *
  29. * First, there are systems where the platform firmware resets the EPB during
  30. * system-wide transitions from sleep states back into the working state
  31. * effectively causing the previous EPB updates by user space to be lost.
  32. * Thus the kernel needs to save the current EPB values for all CPUs during
  33. * system-wide transitions to sleep states and restore them on the way back to
  34. * the working state. That can be achieved by saving EPB for secondary CPUs
  35. * when they are taken offline during transitions into system sleep states and
  36. * for the boot CPU in a syscore suspend operation, so that it can be restored
  37. * for the boot CPU in a syscore resume operation and for the other CPUs when
  38. * they are brought back online. However, CPUs that are already offline when
  39. * a system-wide PM transition is started are not taken offline again, but their
  40. * EPB values may still be reset by the platform firmware during the transition,
  41. * so in fact it is necessary to save the EPB of any CPU taken offline and to
  42. * restore it when the given CPU goes back online at all times.
  43. *
  44. * Second, on many systems the initial EPB value coming from the platform
  45. * firmware is 0 ('performance') and at least on some of them that is because
  46. * the platform firmware does not initialize EPB at all with the assumption that
  47. * the OS will do that anyway. That sometimes is problematic, as it may cause
  48. * the system battery to drain too fast, for example, so it is better to adjust
  49. * it on CPU bring-up and if the initial EPB value for a given CPU is 0, the
  50. * kernel changes it to 6 ('normal').
  51. */
  52. static DEFINE_PER_CPU(u8, saved_epb);
  53. #define EPB_MASK 0x0fULL
  54. #define EPB_SAVED 0x10ULL
  55. #define MAX_EPB EPB_MASK
  56. enum energy_perf_value_index {
  57. EPB_INDEX_PERFORMANCE,
  58. EPB_INDEX_BALANCE_PERFORMANCE,
  59. EPB_INDEX_NORMAL,
  60. EPB_INDEX_BALANCE_POWERSAVE,
  61. EPB_INDEX_POWERSAVE,
  62. };
  63. static u8 energ_perf_values[] = {
  64. [EPB_INDEX_PERFORMANCE] = ENERGY_PERF_BIAS_PERFORMANCE,
  65. [EPB_INDEX_BALANCE_PERFORMANCE] = ENERGY_PERF_BIAS_BALANCE_PERFORMANCE,
  66. [EPB_INDEX_NORMAL] = ENERGY_PERF_BIAS_NORMAL,
  67. [EPB_INDEX_BALANCE_POWERSAVE] = ENERGY_PERF_BIAS_BALANCE_POWERSAVE,
  68. [EPB_INDEX_POWERSAVE] = ENERGY_PERF_BIAS_POWERSAVE,
  69. };
  70. static int intel_epb_save(void)
  71. {
  72. u64 epb;
  73. rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
  74. /*
  75. * Ensure that saved_epb will always be nonzero after this write even if
  76. * the EPB value read from the MSR is 0.
  77. */
  78. this_cpu_write(saved_epb, (epb & EPB_MASK) | EPB_SAVED);
  79. return 0;
  80. }
  81. static void intel_epb_restore(void)
  82. {
  83. u64 val = this_cpu_read(saved_epb);
  84. u64 epb;
  85. rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
  86. if (val) {
  87. val &= EPB_MASK;
  88. } else {
  89. /*
  90. * Because intel_epb_save() has not run for the current CPU yet,
  91. * it is going online for the first time, so if its EPB value is
  92. * 0 ('performance') at this point, assume that it has not been
  93. * initialized by the platform firmware and set it to 6
  94. * ('normal').
  95. */
  96. val = epb & EPB_MASK;
  97. if (val == ENERGY_PERF_BIAS_PERFORMANCE) {
  98. val = energ_perf_values[EPB_INDEX_NORMAL];
  99. pr_warn_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n");
  100. }
  101. }
  102. wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, (epb & ~EPB_MASK) | val);
  103. }
  104. static struct syscore_ops intel_epb_syscore_ops = {
  105. .suspend = intel_epb_save,
  106. .resume = intel_epb_restore,
  107. };
  108. static const char * const energy_perf_strings[] = {
  109. [EPB_INDEX_PERFORMANCE] = "performance",
  110. [EPB_INDEX_BALANCE_PERFORMANCE] = "balance-performance",
  111. [EPB_INDEX_NORMAL] = "normal",
  112. [EPB_INDEX_BALANCE_POWERSAVE] = "balance-power",
  113. [EPB_INDEX_POWERSAVE] = "power",
  114. };
  115. static ssize_t energy_perf_bias_show(struct device *dev,
  116. struct device_attribute *attr,
  117. char *buf)
  118. {
  119. unsigned int cpu = dev->id;
  120. u64 epb;
  121. int ret;
  122. ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
  123. if (ret < 0)
  124. return ret;
  125. return sprintf(buf, "%llu\n", epb);
  126. }
  127. static ssize_t energy_perf_bias_store(struct device *dev,
  128. struct device_attribute *attr,
  129. const char *buf, size_t count)
  130. {
  131. unsigned int cpu = dev->id;
  132. u64 epb, val;
  133. int ret;
  134. ret = __sysfs_match_string(energy_perf_strings,
  135. ARRAY_SIZE(energy_perf_strings), buf);
  136. if (ret >= 0)
  137. val = energ_perf_values[ret];
  138. else if (kstrtou64(buf, 0, &val) || val > MAX_EPB)
  139. return -EINVAL;
  140. ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
  141. if (ret < 0)
  142. return ret;
  143. ret = wrmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS,
  144. (epb & ~EPB_MASK) | val);
  145. if (ret < 0)
  146. return ret;
  147. return count;
  148. }
  149. static DEVICE_ATTR_RW(energy_perf_bias);
  150. static struct attribute *intel_epb_attrs[] = {
  151. &dev_attr_energy_perf_bias.attr,
  152. NULL
  153. };
  154. static const struct attribute_group intel_epb_attr_group = {
  155. .name = power_group_name,
  156. .attrs = intel_epb_attrs
  157. };
  158. static int intel_epb_online(unsigned int cpu)
  159. {
  160. struct device *cpu_dev = get_cpu_device(cpu);
  161. intel_epb_restore();
  162. if (!cpuhp_tasks_frozen)
  163. sysfs_merge_group(&cpu_dev->kobj, &intel_epb_attr_group);
  164. return 0;
  165. }
  166. static int intel_epb_offline(unsigned int cpu)
  167. {
  168. struct device *cpu_dev = get_cpu_device(cpu);
  169. if (!cpuhp_tasks_frozen)
  170. sysfs_unmerge_group(&cpu_dev->kobj, &intel_epb_attr_group);
  171. intel_epb_save();
  172. return 0;
  173. }
  174. static const struct x86_cpu_id intel_epb_normal[] = {
  175. X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, 7),
  176. {}
  177. };
  178. static __init int intel_epb_init(void)
  179. {
  180. const struct x86_cpu_id *id = x86_match_cpu(intel_epb_normal);
  181. int ret;
  182. if (!boot_cpu_has(X86_FEATURE_EPB))
  183. return -ENODEV;
  184. if (id)
  185. energ_perf_values[EPB_INDEX_NORMAL] = id->driver_data;
  186. ret = cpuhp_setup_state(CPUHP_AP_X86_INTEL_EPB_ONLINE,
  187. "x86/intel/epb:online", intel_epb_online,
  188. intel_epb_offline);
  189. if (ret < 0)
  190. goto err_out_online;
  191. register_syscore_ops(&intel_epb_syscore_ops);
  192. return 0;
  193. err_out_online:
  194. cpuhp_remove_state(CPUHP_AP_X86_INTEL_EPB_ONLINE);
  195. return ret;
  196. }
  197. subsys_initcall(intel_epb_init);