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