xref: /haiku/src/add-ons/kernel/power/cpufreq/intel_pstates/intel_pstates.cpp (revision b5efad4eae1a0754ce3e4eb87458279e81f61f61)
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