1 /* 2 * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. 3 * Copyright 2013, Haiku, Inc. All Rights Reserved. 4 * Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Paweł Dziepak, <pdziepak@quarnos.org> 8 */ 9 10 11 #include <cpufreq.h> 12 #include <KernelExport.h> 13 14 #include <arch_cpu.h> 15 #include <cpu.h> 16 #include <smp.h> 17 #include <util/AutoLock.h> 18 19 20 #define INTEL_PSTATES_MODULE_NAME CPUFREQ_MODULES_PREFIX "/intel_pstates/v1" 21 22 23 const int kMinimalInterval = 50000; 24 25 static uint16 sMinPState; 26 static uint16 sMaxPState; 27 static uint16 sBoostPState; 28 static bool sHWPActive; 29 static bool sHWPEPP; 30 static uint8 sHWPLowest; 31 static uint8 sHWPGuaranteed; 32 static uint8 sHWPEfficient; 33 static uint8 sHWPHighest; 34 static bool sHWPPackage; 35 36 static bool sAvoidBoost; 37 38 39 struct CPUEntry { 40 CPUEntry(); 41 42 uint16 fCurrentPState; 43 44 bigtime_t fLastUpdate; 45 46 uint64 fPrevAperf; 47 uint64 fPrevMperf; 48 } CACHE_LINE_ALIGN; 49 static CPUEntry* sCPUEntries; 50 51 52 CPUEntry::CPUEntry() 53 : 54 fCurrentPState(sMinPState - 1), 55 fLastUpdate(0) 56 { 57 } 58 59 60 static void set_normal_pstate(void* /* dummy */, int cpu); 61 62 63 static void 64 pstates_set_scheduler_mode(scheduler_mode mode) 65 { 66 sAvoidBoost = mode == SCHEDULER_MODE_POWER_SAVING; 67 if (sHWPActive) 68 call_all_cpus(set_normal_pstate, NULL); 69 } 70 71 72 static int 73 measure_pstate(CPUEntry* entry) 74 { 75 InterruptsLocker locker; 76 77 uint64 mperf = x86_read_msr(IA32_MSR_MPERF); 78 uint64 aperf = x86_read_msr(IA32_MSR_APERF); 79 80 locker.Unlock(); 81 82 if (mperf == 0 || mperf == entry->fPrevMperf) 83 return sMinPState; 84 85 int oldPState = sMaxPState * (aperf - entry->fPrevAperf) 86 / (mperf - entry->fPrevMperf); 87 oldPState = min_c(max_c(oldPState, sMinPState), sBoostPState); 88 entry->fPrevAperf = aperf; 89 entry->fPrevMperf = mperf; 90 91 return oldPState; 92 } 93 94 95 static inline void 96 set_pstate(uint16 pstate) 97 { 98 CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()]; 99 pstate = min_c(max_c(sMinPState, pstate), sBoostPState); 100 101 if (entry->fCurrentPState != pstate) { 102 entry->fLastUpdate = system_time(); 103 entry->fCurrentPState = pstate; 104 105 x86_write_msr(IA32_MSR_PERF_CTL, pstate << 8); 106 } 107 } 108 109 110 static status_t 111 pstates_increase_performance(int delta) 112 { 113 CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()]; 114 115 if (sHWPActive) 116 return B_NOT_SUPPORTED; 117 118 if (system_time() - entry->fLastUpdate < kMinimalInterval) 119 return B_OK; 120 121 int pState = measure_pstate(entry); 122 pState += (sBoostPState - pState) * delta / kCPUPerformanceScaleMax; 123 124 if (sAvoidBoost && pState < (sMaxPState + sBoostPState) / 2) 125 pState = min_c(pState, sMaxPState); 126 127 set_pstate(pState); 128 return B_OK; 129 } 130 131 132 static status_t 133 pstates_decrease_performance(int delta) 134 { 135 CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()]; 136 137 if (sHWPActive) 138 return B_NOT_SUPPORTED; 139 140 if (system_time() - entry->fLastUpdate < kMinimalInterval) 141 return B_OK; 142 143 int pState = measure_pstate(entry); 144 pState -= (pState - sMinPState) * delta / kCPUPerformanceScaleMax; 145 146 set_pstate(pState); 147 return B_OK; 148 } 149 150 151 static bool 152 is_cpu_model_supported(cpu_ent* cpu) 153 { 154 uint8 model = cpu->arch.model + (cpu->arch.extended_model << 4); 155 156 if (cpu->arch.vendor != VENDOR_INTEL) 157 return false; 158 159 if (cpu->arch.family != 6) 160 return false; 161 162 if (x86_check_feature(IA32_FEATURE_HWP, FEATURE_6_EAX)) 163 return true; 164 165 const uint8 kSupportedFamily6Models[] = { 166 0x2a, 0x2d, 0x37, 0x3a, 0x3c, 0x3d, 0x3e, 0x3f, 0x45, 0x46, 0x47, 0x4a, 167 0x4c, 0x4d, 0x4e, 0x4f, 0x55, 0x56, 0x57, 0x5a, 0x5c, 0x5e, 0x5f, 0x75, 168 0x7a, 0x85 169 }; 170 const int kSupportedFamily6ModelsCount 171 = sizeof(kSupportedFamily6Models) / sizeof(uint8); 172 173 int i; 174 for (i = 0; i < kSupportedFamily6ModelsCount; i++) { 175 if (model == kSupportedFamily6Models[i]) 176 break; 177 } 178 179 return i != kSupportedFamily6ModelsCount; 180 } 181 182 183 static void 184 set_normal_pstate(void* /* dummy */, int cpu) 185 { 186 if (sHWPActive) { 187 if (x86_check_feature(IA32_FEATURE_HWP_NOTIFY, FEATURE_6_EAX)) 188 x86_write_msr(IA32_MSR_HWP_INTERRUPT, 0); 189 x86_write_msr(IA32_MSR_PM_ENABLE, 1); 190 uint64 hwpRequest = x86_read_msr(IA32_MSR_HWP_REQUEST); 191 uint64 caps = x86_read_msr(IA32_MSR_HWP_CAPABILITIES); 192 sHWPLowest = IA32_HWP_CAPS_LOWEST_PERFORMANCE(caps); 193 sHWPEfficient = IA32_HWP_CAPS_EFFICIENT_PERFORMANCE(caps); 194 sHWPGuaranteed = IA32_HWP_CAPS_GUARANTEED_PERFORMANCE(caps); 195 sHWPHighest = IA32_HWP_CAPS_HIGHEST_PERFORMANCE(caps); 196 197 hwpRequest &= ~IA32_HWP_REQUEST_DESIRED_PERFORMANCE; 198 hwpRequest &= ~IA32_HWP_REQUEST_ACTIVITY_WINDOW; 199 200 hwpRequest &= ~IA32_HWP_REQUEST_MINIMUM_PERFORMANCE; 201 hwpRequest |= sHWPLowest; 202 203 hwpRequest &= ~IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE; 204 hwpRequest |= sHWPHighest << 8; 205 206 if (x86_check_feature(IA32_FEATURE_HWP_EPP, FEATURE_6_EAX)) { 207 hwpRequest &= ~IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE; 208 hwpRequest |= (sAvoidBoost ? 0x80ULL : 0x0ULL) << 24; 209 } else if (x86_check_feature(IA32_FEATURE_EPB, FEATURE_6_ECX)) { 210 uint64 perfBias = x86_read_msr(IA32_MSR_ENERGY_PERF_BIAS); 211 perfBias &= ~(0xfULL << 0); 212 perfBias |= (sAvoidBoost ? 0xfULL : 0x0ULL) << 0; 213 x86_write_msr(IA32_MSR_ENERGY_PERF_BIAS, perfBias); 214 } 215 216 if (sHWPPackage) { 217 x86_write_msr(IA32_MSR_HWP_REQUEST, hwpRequest 218 | IA32_HWP_REQUEST_PACKAGE_CONTROL); 219 x86_write_msr(IA32_MSR_HWP_REQUEST_PKG, hwpRequest); 220 } else 221 x86_write_msr(IA32_MSR_HWP_REQUEST, hwpRequest); 222 } else { 223 measure_pstate(&sCPUEntries[cpu]); 224 set_pstate(sMaxPState); 225 } 226 } 227 228 229 static status_t 230 init_pstates() 231 { 232 if (!x86_check_feature(IA32_FEATURE_MSR, FEATURE_COMMON)) 233 return B_ERROR; 234 235 if (!x86_check_feature(IA32_FEATURE_APERFMPERF, FEATURE_6_ECX)) 236 return B_ERROR; 237 238 int32 cpuCount = smp_get_num_cpus(); 239 for (int32 i = 0; i < cpuCount; i++) { 240 if (!is_cpu_model_supported(&gCPU[i])) 241 return B_ERROR; 242 } 243 244 uint64 platformInfo = x86_read_msr(IA32_MSR_PLATFORM_INFO); 245 sHWPEPP = x86_check_feature(IA32_FEATURE_HWP_EPP, FEATURE_6_EAX); 246 sHWPActive = (x86_check_feature(IA32_FEATURE_HWP, FEATURE_6_EAX) 247 && sHWPEPP); 248 sMinPState = (platformInfo >> 40) & 0xff; 249 sMaxPState = (platformInfo >> 8) & 0xff; 250 sBoostPState 251 = max_c(x86_read_msr(IA32_MSR_TURBO_RATIO_LIMIT) & 0xff, sMaxPState); 252 /* x86_check_feature(IA32_FEATURE_HWP_PLR, FEATURE_6_EAX)) */ 253 sHWPPackage = false; 254 255 dprintf("using Intel P-States: min %" B_PRIu16 ", max %" B_PRIu16 256 ", boost %" B_PRIu16 "%s\n", sMinPState, sMaxPState, sBoostPState, 257 sHWPActive ? ", HWP active" : ""); 258 259 if (sMaxPState <= sMinPState || sMaxPState == 0) { 260 dprintf("unexpected or invalid Intel P-States limits, aborting\n"); 261 return B_ERROR; 262 } 263 264 sCPUEntries = new(std::nothrow) CPUEntry[cpuCount]; 265 if (sCPUEntries == NULL) 266 return B_NO_MEMORY; 267 268 pstates_set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); 269 270 call_all_cpus_sync(set_normal_pstate, NULL); 271 return B_OK; 272 } 273 274 275 static status_t 276 uninit_pstates() 277 { 278 call_all_cpus_sync(set_normal_pstate, NULL); 279 delete[] sCPUEntries; 280 281 return B_OK; 282 } 283 284 285 static status_t 286 std_ops(int32 op, ...) 287 { 288 switch (op) { 289 case B_MODULE_INIT: 290 return init_pstates(); 291 292 case B_MODULE_UNINIT: 293 uninit_pstates(); 294 return B_OK; 295 } 296 297 return B_ERROR; 298 } 299 300 301 static cpufreq_module_info sIntelPStates = { 302 { 303 INTEL_PSTATES_MODULE_NAME, 304 0, 305 std_ops, 306 }, 307 308 1.0f, 309 310 pstates_set_scheduler_mode, 311 312 pstates_increase_performance, 313 pstates_decrease_performance, 314 }; 315 316 317 module_info* modules[] = { 318 (module_info*)&sIntelPStates, 319 NULL 320 }; 321 322