0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/cpuhotplug.h>
0012 #include <linux/cpu.h>
0013 #include <linux/device.h>
0014 #include <linux/kernel.h>
0015 #include <linux/string.h>
0016 #include <linux/syscore_ops.h>
0017 #include <linux/pm.h>
0018
0019 #include <asm/cpu_device_id.h>
0020 #include <asm/cpufeature.h>
0021 #include <asm/msr.h>
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056 static DEFINE_PER_CPU(u8, saved_epb);
0057
0058 #define EPB_MASK 0x0fULL
0059 #define EPB_SAVED 0x10ULL
0060 #define MAX_EPB EPB_MASK
0061
0062 enum energy_perf_value_index {
0063 EPB_INDEX_PERFORMANCE,
0064 EPB_INDEX_BALANCE_PERFORMANCE,
0065 EPB_INDEX_NORMAL,
0066 EPB_INDEX_BALANCE_POWERSAVE,
0067 EPB_INDEX_POWERSAVE,
0068 };
0069
0070 static u8 energ_perf_values[] = {
0071 [EPB_INDEX_PERFORMANCE] = ENERGY_PERF_BIAS_PERFORMANCE,
0072 [EPB_INDEX_BALANCE_PERFORMANCE] = ENERGY_PERF_BIAS_BALANCE_PERFORMANCE,
0073 [EPB_INDEX_NORMAL] = ENERGY_PERF_BIAS_NORMAL,
0074 [EPB_INDEX_BALANCE_POWERSAVE] = ENERGY_PERF_BIAS_BALANCE_POWERSAVE,
0075 [EPB_INDEX_POWERSAVE] = ENERGY_PERF_BIAS_POWERSAVE,
0076 };
0077
0078 static int intel_epb_save(void)
0079 {
0080 u64 epb;
0081
0082 rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
0083
0084
0085
0086
0087 this_cpu_write(saved_epb, (epb & EPB_MASK) | EPB_SAVED);
0088
0089 return 0;
0090 }
0091
0092 static void intel_epb_restore(void)
0093 {
0094 u64 val = this_cpu_read(saved_epb);
0095 u64 epb;
0096
0097 rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
0098 if (val) {
0099 val &= EPB_MASK;
0100 } else {
0101
0102
0103
0104
0105
0106
0107
0108 val = epb & EPB_MASK;
0109 if (val == ENERGY_PERF_BIAS_PERFORMANCE) {
0110 val = energ_perf_values[EPB_INDEX_NORMAL];
0111 pr_warn_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n");
0112 }
0113 }
0114 wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, (epb & ~EPB_MASK) | val);
0115 }
0116
0117 static struct syscore_ops intel_epb_syscore_ops = {
0118 .suspend = intel_epb_save,
0119 .resume = intel_epb_restore,
0120 };
0121
0122 static const char * const energy_perf_strings[] = {
0123 [EPB_INDEX_PERFORMANCE] = "performance",
0124 [EPB_INDEX_BALANCE_PERFORMANCE] = "balance-performance",
0125 [EPB_INDEX_NORMAL] = "normal",
0126 [EPB_INDEX_BALANCE_POWERSAVE] = "balance-power",
0127 [EPB_INDEX_POWERSAVE] = "power",
0128 };
0129
0130 static ssize_t energy_perf_bias_show(struct device *dev,
0131 struct device_attribute *attr,
0132 char *buf)
0133 {
0134 unsigned int cpu = dev->id;
0135 u64 epb;
0136 int ret;
0137
0138 ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
0139 if (ret < 0)
0140 return ret;
0141
0142 return sprintf(buf, "%llu\n", epb);
0143 }
0144
0145 static ssize_t energy_perf_bias_store(struct device *dev,
0146 struct device_attribute *attr,
0147 const char *buf, size_t count)
0148 {
0149 unsigned int cpu = dev->id;
0150 u64 epb, val;
0151 int ret;
0152
0153 ret = __sysfs_match_string(energy_perf_strings,
0154 ARRAY_SIZE(energy_perf_strings), buf);
0155 if (ret >= 0)
0156 val = energ_perf_values[ret];
0157 else if (kstrtou64(buf, 0, &val) || val > MAX_EPB)
0158 return -EINVAL;
0159
0160 ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
0161 if (ret < 0)
0162 return ret;
0163
0164 ret = wrmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS,
0165 (epb & ~EPB_MASK) | val);
0166 if (ret < 0)
0167 return ret;
0168
0169 return count;
0170 }
0171
0172 static DEVICE_ATTR_RW(energy_perf_bias);
0173
0174 static struct attribute *intel_epb_attrs[] = {
0175 &dev_attr_energy_perf_bias.attr,
0176 NULL
0177 };
0178
0179 static const struct attribute_group intel_epb_attr_group = {
0180 .name = power_group_name,
0181 .attrs = intel_epb_attrs
0182 };
0183
0184 static int intel_epb_online(unsigned int cpu)
0185 {
0186 struct device *cpu_dev = get_cpu_device(cpu);
0187
0188 intel_epb_restore();
0189 if (!cpuhp_tasks_frozen)
0190 sysfs_merge_group(&cpu_dev->kobj, &intel_epb_attr_group);
0191
0192 return 0;
0193 }
0194
0195 static int intel_epb_offline(unsigned int cpu)
0196 {
0197 struct device *cpu_dev = get_cpu_device(cpu);
0198
0199 if (!cpuhp_tasks_frozen)
0200 sysfs_unmerge_group(&cpu_dev->kobj, &intel_epb_attr_group);
0201
0202 intel_epb_save();
0203 return 0;
0204 }
0205
0206 static const struct x86_cpu_id intel_epb_normal[] = {
0207 X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, 7),
0208 {}
0209 };
0210
0211 static __init int intel_epb_init(void)
0212 {
0213 const struct x86_cpu_id *id = x86_match_cpu(intel_epb_normal);
0214 int ret;
0215
0216 if (!boot_cpu_has(X86_FEATURE_EPB))
0217 return -ENODEV;
0218
0219 if (id)
0220 energ_perf_values[EPB_INDEX_NORMAL] = id->driver_data;
0221
0222 ret = cpuhp_setup_state(CPUHP_AP_X86_INTEL_EPB_ONLINE,
0223 "x86/intel/epb:online", intel_epb_online,
0224 intel_epb_offline);
0225 if (ret < 0)
0226 goto err_out_online;
0227
0228 register_syscore_ops(&intel_epb_syscore_ops);
0229 return 0;
0230
0231 err_out_online:
0232 cpuhp_remove_state(CPUHP_AP_X86_INTEL_EPB_ONLINE);
0233 return ret;
0234 }
0235 subsys_initcall(intel_epb_init);