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, 0x3a, 0x3c, 0x3d, 0x3e, 0x3f, 0x45, 0x46, 0x47, 0x4a, 167 0x4d, 0x4e, 0x4f, 0x55, 0x56, 0x57, 0x5a, 0x5c, 0x5e, 0x5f, 0x75, 168 0x7a, 0x85 169 }; 170 /* TODO: support Atom Silvermont and Airmont: 0x37, 0x4c */ 171 const int kSupportedFamily6ModelsCount 172 = sizeof(kSupportedFamily6Models) / sizeof(uint8); 173 174 int i; 175 for (i = 0; i < kSupportedFamily6ModelsCount; i++) { 176 if (model == kSupportedFamily6Models[i]) 177 break; 178 } 179 180 return i != kSupportedFamily6ModelsCount; 181 } 182 183 184 static void 185 set_normal_pstate(void* /* dummy */, int cpu) 186 { 187 if (sHWPActive) { 188 if (x86_check_feature(IA32_FEATURE_HWP_NOTIFY, FEATURE_6_EAX)) 189 x86_write_msr(IA32_MSR_HWP_INTERRUPT, 0); 190 x86_write_msr(IA32_MSR_PM_ENABLE, 1); 191 uint64 hwpRequest = x86_read_msr(IA32_MSR_HWP_REQUEST); 192 uint64 caps = x86_read_msr(IA32_MSR_HWP_CAPABILITIES); 193 sHWPLowest = IA32_HWP_CAPS_LOWEST_PERFORMANCE(caps); 194 sHWPEfficient = IA32_HWP_CAPS_EFFICIENT_PERFORMANCE(caps); 195 sHWPGuaranteed = IA32_HWP_CAPS_GUARANTEED_PERFORMANCE(caps); 196 sHWPHighest = IA32_HWP_CAPS_HIGHEST_PERFORMANCE(caps); 197 198 hwpRequest &= ~IA32_HWP_REQUEST_DESIRED_PERFORMANCE; 199 hwpRequest &= ~IA32_HWP_REQUEST_ACTIVITY_WINDOW; 200 201 hwpRequest &= ~IA32_HWP_REQUEST_MINIMUM_PERFORMANCE; 202 hwpRequest |= sHWPLowest; 203 204 hwpRequest &= ~IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE; 205 hwpRequest |= sHWPHighest << 8; 206 207 if (x86_check_feature(IA32_FEATURE_HWP_EPP, FEATURE_6_EAX)) { 208 hwpRequest &= ~IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE; 209 hwpRequest |= (sAvoidBoost ? 0x80ULL : 0x0ULL) << 24; 210 } else if (x86_check_feature(IA32_FEATURE_EPB, FEATURE_6_ECX)) { 211 uint64 perfBias = x86_read_msr(IA32_MSR_ENERGY_PERF_BIAS); 212 perfBias &= ~(0xfULL << 0); 213 perfBias |= (sAvoidBoost ? 0xfULL : 0x0ULL) << 0; 214 x86_write_msr(IA32_MSR_ENERGY_PERF_BIAS, perfBias); 215 } 216 217 if (sHWPPackage) { 218 x86_write_msr(IA32_MSR_HWP_REQUEST, hwpRequest 219 | IA32_HWP_REQUEST_PACKAGE_CONTROL); 220 x86_write_msr(IA32_MSR_HWP_REQUEST_PKG, hwpRequest); 221 } else 222 x86_write_msr(IA32_MSR_HWP_REQUEST, hwpRequest); 223 } else { 224 measure_pstate(&sCPUEntries[cpu]); 225 set_pstate(sMaxPState); 226 } 227 } 228 229 230 static status_t 231 init_pstates() 232 { 233 if (!x86_check_feature(IA32_FEATURE_MSR, FEATURE_COMMON)) 234 return B_ERROR; 235 236 if (!x86_check_feature(IA32_FEATURE_APERFMPERF, FEATURE_6_ECX)) 237 return B_ERROR; 238 239 int32 cpuCount = smp_get_num_cpus(); 240 for (int32 i = 0; i < cpuCount; i++) { 241 if (!is_cpu_model_supported(&gCPU[i])) 242 return B_ERROR; 243 } 244 245 uint64 platformInfo = x86_read_msr(IA32_MSR_PLATFORM_INFO); 246 sHWPEPP = x86_check_feature(IA32_FEATURE_HWP_EPP, FEATURE_6_EAX); 247 sHWPActive = (x86_check_feature(IA32_FEATURE_HWP, FEATURE_6_EAX) 248 && sHWPEPP); 249 sMinPState = (platformInfo >> 40) & 0xff; 250 sMaxPState = (platformInfo >> 8) & 0xff; 251 sBoostPState 252 = max_c(x86_read_msr(IA32_MSR_TURBO_RATIO_LIMIT) & 0xff, sMaxPState); 253 /* x86_check_feature(IA32_FEATURE_HWP_PLR, FEATURE_6_EAX)) */ 254 sHWPPackage = false; 255 256 dprintf("using Intel P-States: min %" B_PRIu16 ", max %" B_PRIu16 257 ", boost %" B_PRIu16 "%s\n", sMinPState, sMaxPState, sBoostPState, 258 sHWPActive ? ", HWP active" : ""); 259 260 if (sMaxPState <= sMinPState || sMaxPState == 0) { 261 dprintf("unexpected or invalid Intel P-States limits, aborting\n"); 262 return B_ERROR; 263 } 264 265 sCPUEntries = new(std::nothrow) CPUEntry[cpuCount]; 266 if (sCPUEntries == NULL) 267 return B_NO_MEMORY; 268 269 pstates_set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); 270 271 call_all_cpus_sync(set_normal_pstate, NULL); 272 return B_OK; 273 } 274 275 276 static status_t 277 uninit_pstates() 278 { 279 call_all_cpus_sync(set_normal_pstate, NULL); 280 delete[] sCPUEntries; 281 282 return B_OK; 283 } 284 285 286 static status_t 287 std_ops(int32 op, ...) 288 { 289 switch (op) { 290 case B_MODULE_INIT: 291 return init_pstates(); 292 293 case B_MODULE_UNINIT: 294 uninit_pstates(); 295 return B_OK; 296 } 297 298 return B_ERROR; 299 } 300 301 302 static cpufreq_module_info sIntelPStates = { 303 { 304 INTEL_PSTATES_MODULE_NAME, 305 0, 306 std_ops, 307 }, 308 309 1.0f, 310 311 pstates_set_scheduler_mode, 312 313 pstates_increase_performance, 314 pstates_decrease_performance, 315 }; 316 317 318 module_info* modules[] = { 319 (module_info*)&sIntelPStates, 320 NULL 321 }; 322 323