xref: /freebsd/sys/x86/cpufreq/hwpstate_intel.c (revision 0b37c1590418417c894529d371800dfac71ef887)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Intel Corporation
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted providing that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/types.h>
32 #include <sys/sbuf.h>
33 #include <sys/module.h>
34 #include <sys/systm.h>
35 #include <sys/errno.h>
36 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/bus.h>
39 #include <sys/cpu.h>
40 #include <sys/smp.h>
41 #include <sys/proc.h>
42 #include <sys/sched.h>
43 
44 #include <machine/cpu.h>
45 #include <machine/md_var.h>
46 #include <machine/cputypes.h>
47 #include <machine/specialreg.h>
48 
49 #include <contrib/dev/acpica/include/acpi.h>
50 
51 #include <dev/acpica/acpivar.h>
52 
53 #include <x86/cpufreq/hwpstate_intel_internal.h>
54 
55 #include "acpi_if.h"
56 #include "cpufreq_if.h"
57 
58 extern uint64_t	tsc_freq;
59 
60 static int	intel_hwpstate_probe(device_t dev);
61 static int	intel_hwpstate_attach(device_t dev);
62 static int	intel_hwpstate_detach(device_t dev);
63 static int	intel_hwpstate_suspend(device_t dev);
64 static int	intel_hwpstate_resume(device_t dev);
65 
66 static int      intel_hwpstate_get(device_t dev, struct cf_setting *cf);
67 static int      intel_hwpstate_type(device_t dev, int *type);
68 
69 static device_method_t intel_hwpstate_methods[] = {
70 	/* Device interface */
71 	DEVMETHOD(device_identify,	intel_hwpstate_identify),
72 	DEVMETHOD(device_probe,		intel_hwpstate_probe),
73 	DEVMETHOD(device_attach,	intel_hwpstate_attach),
74 	DEVMETHOD(device_detach,	intel_hwpstate_detach),
75 	DEVMETHOD(device_suspend,	intel_hwpstate_suspend),
76 	DEVMETHOD(device_resume,	intel_hwpstate_resume),
77 
78 	/* cpufreq interface */
79 	DEVMETHOD(cpufreq_drv_get,      intel_hwpstate_get),
80 	DEVMETHOD(cpufreq_drv_type,     intel_hwpstate_type),
81 
82 	DEVMETHOD_END
83 };
84 
85 struct hwp_softc {
86 	device_t		dev;
87 	bool 			hwp_notifications;
88 	bool			hwp_activity_window;
89 	bool			hwp_pref_ctrl;
90 	bool			hwp_pkg_ctrl;
91 
92 	uint64_t		req; /* Cached copy of last request */
93 
94 	uint8_t			high;
95 	uint8_t			guaranteed;
96 	uint8_t			efficient;
97 	uint8_t			low;
98 };
99 
100 static devclass_t hwpstate_intel_devclass;
101 static driver_t hwpstate_intel_driver = {
102 	"hwpstate_intel",
103 	intel_hwpstate_methods,
104 	sizeof(struct hwp_softc),
105 };
106 
107 DRIVER_MODULE(hwpstate_intel, cpu, hwpstate_intel_driver,
108     hwpstate_intel_devclass, NULL, NULL);
109 MODULE_VERSION(hwpstate_intel, 1);
110 
111 static int
112 intel_hwp_dump_sysctl_handler(SYSCTL_HANDLER_ARGS)
113 {
114 	device_t dev;
115 	struct pcpu *pc;
116 	struct sbuf *sb;
117 	struct hwp_softc *sc;
118 	uint64_t data, data2;
119 	int ret;
120 
121 	sc = (struct hwp_softc *)arg1;
122 	dev = sc->dev;
123 
124 	pc = cpu_get_pcpu(dev);
125 	if (pc == NULL)
126 		return (ENXIO);
127 
128 	sb = sbuf_new(NULL, NULL, 1024, SBUF_FIXEDLEN | SBUF_INCLUDENUL);
129 	sbuf_putc(sb, '\n');
130 	thread_lock(curthread);
131 	sched_bind(curthread, pc->pc_cpuid);
132 	thread_unlock(curthread);
133 
134 	rdmsr_safe(MSR_IA32_PM_ENABLE, &data);
135 	sbuf_printf(sb, "CPU%d: HWP %sabled\n", pc->pc_cpuid,
136 	    ((data & 1) ? "En" : "Dis"));
137 
138 	if (data == 0) {
139 		ret = 0;
140 		goto out;
141 	}
142 
143 	rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &data);
144 	sbuf_printf(sb, "\tHighest Performance: %03ju\n", data & 0xff);
145 	sbuf_printf(sb, "\tGuaranteed Performance: %03ju\n", (data >> 8) & 0xff);
146 	sbuf_printf(sb, "\tEfficient Performance: %03ju\n", (data >> 16) & 0xff);
147 	sbuf_printf(sb, "\tLowest Performance: %03ju\n", (data >> 24) & 0xff);
148 
149 	rdmsr_safe(MSR_IA32_HWP_REQUEST, &data);
150 	if (sc->hwp_pkg_ctrl && (data & IA32_HWP_REQUEST_PACKAGE_CONTROL)) {
151 		rdmsr_safe(MSR_IA32_HWP_REQUEST_PKG, &data2);
152 	}
153 
154 	sbuf_putc(sb, '\n');
155 
156 #define pkg_print(x, name, offset) do {						\
157 	if (!sc->hwp_pkg_ctrl || (data & x) != 0) 				\
158 	sbuf_printf(sb, "\t%s: %03ju\n", name, (data >> offset) & 0xff);\
159 	else									\
160 	sbuf_printf(sb, "\t%s: %03ju\n", name, (data2 >> offset) & 0xff);\
161 } while (0)
162 
163 	pkg_print(IA32_HWP_REQUEST_EPP_VALID,
164 	    "Requested Efficiency Performance Preference", 24);
165 	pkg_print(IA32_HWP_REQUEST_DESIRED_VALID,
166 	    "Requested Desired Performance", 16);
167 	pkg_print(IA32_HWP_REQUEST_MAXIMUM_VALID,
168 	    "Requested Maximum Performance", 8);
169 	pkg_print(IA32_HWP_REQUEST_MINIMUM_VALID,
170 	    "Requested Minimum Performance", 0);
171 #undef pkg_print
172 
173 	sbuf_putc(sb, '\n');
174 
175 out:
176 	thread_lock(curthread);
177 	sched_unbind(curthread);
178 	thread_unlock(curthread);
179 
180 	ret = sbuf_finish(sb);
181 	if (ret == 0)
182 		ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
183 	sbuf_delete(sb);
184 
185 	return (ret);
186 }
187 
188 static inline int
189 percent_to_raw(int x)
190 {
191 
192 	MPASS(x <= 100 && x >= 0);
193 	return (0xff * x / 100);
194 }
195 
196 /*
197  * Given x * 10 in [0, 1000], round to the integer nearest x.
198  *
199  * This allows round-tripping nice human readable numbers through this
200  * interface.  Otherwise, user-provided percentages such as 25, 50, 75 get
201  * rounded down to 24, 49, and 74, which is a bit ugly.
202  */
203 static inline int
204 round10(int xtimes10)
205 {
206 	return ((xtimes10 + 5) / 10);
207 }
208 
209 static inline int
210 raw_to_percent(int x)
211 {
212 	MPASS(x <= 0xff && x >= 0);
213 	return (round10(x * 1000 / 0xff));
214 }
215 
216 static int
217 sysctl_epp_select(SYSCTL_HANDLER_ARGS)
218 {
219 	device_t dev;
220 	struct pcpu *pc;
221 	uint64_t requested;
222 	uint32_t val;
223 	int ret;
224 
225 	dev = oidp->oid_arg1;
226 	pc = cpu_get_pcpu(dev);
227 	if (pc == NULL)
228 		return (ENXIO);
229 
230 	thread_lock(curthread);
231 	sched_bind(curthread, pc->pc_cpuid);
232 	thread_unlock(curthread);
233 
234 	rdmsr_safe(MSR_IA32_HWP_REQUEST, &requested);
235 	val = (requested & IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE) >> 24;
236 	val = raw_to_percent(val);
237 
238 	MPASS(val >= 0 && val <= 100);
239 
240 	ret = sysctl_handle_int(oidp, &val, 0, req);
241 	if (ret || req->newptr == NULL)
242 		goto out;
243 
244 	if (val > 100) {
245 		ret = EINVAL;
246 		goto out;
247 	}
248 
249 	val = percent_to_raw(val);
250 
251 	requested &= ~IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE;
252 	requested |= val << 24;
253 
254 	wrmsr_safe(MSR_IA32_HWP_REQUEST, requested);
255 
256 out:
257 	thread_lock(curthread);
258 	sched_unbind(curthread);
259 	thread_unlock(curthread);
260 
261 	return (ret);
262 }
263 
264 void
265 intel_hwpstate_identify(driver_t *driver, device_t parent)
266 {
267 	uint32_t regs[4];
268 
269 	if (device_find_child(parent, "hwpstate_intel", -1) != NULL)
270 		return;
271 
272 	if (cpu_vendor_id != CPU_VENDOR_INTEL)
273 		return;
274 
275 	if (resource_disabled("hwpstate_intel", 0))
276 		return;
277 
278 	/*
279 	 * Intel SDM 14.4.1 (HWP Programming Interfaces):
280 	 *   The CPUID instruction allows software to discover the presence of
281 	 *   HWP support in an Intel processor. Specifically, execute CPUID
282 	 *   instruction with EAX=06H as input will return 5 bit flags covering
283 	 *   the following aspects in bits 7 through 11 of CPUID.06H:EAX.
284 	 */
285 
286 	if (cpu_high < 6)
287 		return;
288 
289 	/*
290 	 * Intel SDM 14.4.1 (HWP Programming Interfaces):
291 	 *   Availability of HWP baseline resource and capability,
292 	 *   CPUID.06H:EAX[bit 7]: If this bit is set, HWP provides several new
293 	 *   architectural MSRs: IA32_PM_ENABLE, IA32_HWP_CAPABILITIES,
294 	 *   IA32_HWP_REQUEST, IA32_HWP_STATUS.
295 	 */
296 
297 	do_cpuid(6, regs);
298 	if ((regs[0] & CPUTPM1_HWP) == 0)
299 		return;
300 
301 	if (BUS_ADD_CHILD(parent, 10, "hwpstate_intel", -1) == NULL)
302 		return;
303 
304 	if (bootverbose)
305 		device_printf(parent, "hwpstate registered\n");
306 }
307 
308 static int
309 intel_hwpstate_probe(device_t dev)
310 {
311 
312 	device_set_desc(dev, "Intel Speed Shift");
313 	return (BUS_PROBE_NOWILDCARD);
314 }
315 
316 /* FIXME: Need to support PKG variant */
317 static int
318 set_autonomous_hwp(struct hwp_softc *sc)
319 {
320 	struct pcpu *pc;
321 	device_t dev;
322 	uint64_t caps;
323 	int ret;
324 
325 	dev = sc->dev;
326 
327 	pc = cpu_get_pcpu(dev);
328 	if (pc == NULL)
329 		return (ENXIO);
330 
331 	thread_lock(curthread);
332 	sched_bind(curthread, pc->pc_cpuid);
333 	thread_unlock(curthread);
334 
335 	/* XXX: Many MSRs aren't readable until feature is enabled */
336 	ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1);
337 	if (ret) {
338 		device_printf(dev, "Failed to enable HWP for cpu%d (%d)\n",
339 		    pc->pc_cpuid, ret);
340 		goto out;
341 	}
342 
343 	ret = rdmsr_safe(MSR_IA32_HWP_REQUEST, &sc->req);
344 	if (ret)
345 		return (ret);
346 
347 	ret = rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &caps);
348 	if (ret)
349 		return (ret);
350 
351 	sc->high = IA32_HWP_CAPABILITIES_HIGHEST_PERFORMANCE(caps);
352 	sc->guaranteed = IA32_HWP_CAPABILITIES_GUARANTEED_PERFORMANCE(caps);
353 	sc->efficient = IA32_HWP_CAPABILITIES_EFFICIENT_PERFORMANCE(caps);
354 	sc->low = IA32_HWP_CAPABILITIES_LOWEST_PERFORMANCE(caps);
355 
356 	/* hardware autonomous selection determines the performance target */
357 	sc->req &= ~IA32_HWP_DESIRED_PERFORMANCE;
358 
359 	/* enable HW dynamic selection of window size */
360 	sc->req &= ~IA32_HWP_ACTIVITY_WINDOW;
361 
362 	/* IA32_HWP_REQUEST.Minimum_Performance = IA32_HWP_CAPABILITIES.Lowest_Performance */
363 	sc->req &= ~IA32_HWP_MINIMUM_PERFORMANCE;
364 	sc->req |= sc->low;
365 
366 	/* IA32_HWP_REQUEST.Maximum_Performance = IA32_HWP_CAPABILITIES.Highest_Performance. */
367 	sc->req &= ~IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE;
368 	sc->req |= sc->high << 8;
369 
370 	ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
371 	if (ret) {
372 		device_printf(dev,
373 		    "Failed to setup autonomous HWP for cpu%d (file a bug)\n",
374 		    pc->pc_cpuid);
375 	}
376 
377 out:
378 	thread_lock(curthread);
379 	sched_unbind(curthread);
380 	thread_unlock(curthread);
381 
382 	return (ret);
383 }
384 
385 static int
386 intel_hwpstate_attach(device_t dev)
387 {
388 	struct hwp_softc *sc;
389 	uint32_t regs[4];
390 	int ret;
391 
392 	sc = device_get_softc(dev);
393 	sc->dev = dev;
394 
395 	do_cpuid(6, regs);
396 	if (regs[0] & CPUTPM1_HWP_NOTIFICATION)
397 		sc->hwp_notifications = true;
398 	if (regs[0] & CPUTPM1_HWP_ACTIVITY_WINDOW)
399 		sc->hwp_activity_window = true;
400 	if (regs[0] & CPUTPM1_HWP_PERF_PREF)
401 		sc->hwp_pref_ctrl = true;
402 	if (regs[0] & CPUTPM1_HWP_PKG)
403 		sc->hwp_pkg_ctrl = true;
404 
405 	ret = set_autonomous_hwp(sc);
406 	if (ret)
407 		return (ret);
408 
409 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
410 	    SYSCTL_STATIC_CHILDREN(_debug), OID_AUTO, device_get_nameunit(dev),
411 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP,
412 	    sc, 0, intel_hwp_dump_sysctl_handler, "A", "");
413 
414 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
415 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
416 	    "epp", CTLTYPE_INT | CTLFLAG_RWTUN, dev, sizeof(dev),
417 	    sysctl_epp_select, "I",
418 	    "Efficiency/Performance Preference "
419 	    "(range from 0, most performant, through 100, most efficient)");
420 
421 	return (cpufreq_register(dev));
422 }
423 
424 static int
425 intel_hwpstate_detach(device_t dev)
426 {
427 
428 	return (cpufreq_unregister(dev));
429 }
430 
431 static int
432 intel_hwpstate_get(device_t dev, struct cf_setting *set)
433 {
434 	struct pcpu *pc;
435 	uint64_t rate;
436 	int ret;
437 
438 	if (set == NULL)
439 		return (EINVAL);
440 
441 	pc = cpu_get_pcpu(dev);
442 	if (pc == NULL)
443 		return (ENXIO);
444 
445 	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
446 	set->dev = dev;
447 
448 	ret = cpu_est_clockrate(pc->pc_cpuid, &rate);
449 	if (ret == 0)
450 		set->freq = rate / 1000000;
451 
452 	set->volts = CPUFREQ_VAL_UNKNOWN;
453 	set->power = CPUFREQ_VAL_UNKNOWN;
454 	set->lat = CPUFREQ_VAL_UNKNOWN;
455 
456 	return (0);
457 }
458 
459 static int
460 intel_hwpstate_type(device_t dev, int *type)
461 {
462 	if (type == NULL)
463 		return (EINVAL);
464 	*type = CPUFREQ_TYPE_ABSOLUTE | CPUFREQ_FLAG_INFO_ONLY | CPUFREQ_FLAG_UNCACHED;
465 
466 	return (0);
467 }
468 
469 static int
470 intel_hwpstate_suspend(device_t dev)
471 {
472 	return (0);
473 }
474 
475 /*
476  * Redo a subset of set_autonomous_hwp on resume; untested.  Without this,
477  * testers observed that on resume MSR_IA32_HWP_REQUEST was bogus.
478  */
479 static int
480 intel_hwpstate_resume(device_t dev)
481 {
482 	struct hwp_softc *sc;
483 	struct pcpu *pc;
484 	int ret;
485 
486 	sc = device_get_softc(dev);
487 
488 	pc = cpu_get_pcpu(dev);
489 	if (pc == NULL)
490 		return (ENXIO);
491 
492 	thread_lock(curthread);
493 	sched_bind(curthread, pc->pc_cpuid);
494 	thread_unlock(curthread);
495 
496 	ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1);
497 	if (ret) {
498 		device_printf(dev,
499 		    "Failed to enable HWP for cpu%d after suspend (%d)\n",
500 		    pc->pc_cpuid, ret);
501 		goto out;
502 	}
503 
504 	ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
505 	if (ret) {
506 		device_printf(dev,
507 		    "Failed to setup autonomous HWP for cpu%d after suspend\n",
508 		    pc->pc_cpuid);
509 	}
510 
511 out:
512 	thread_lock(curthread);
513 	sched_unbind(curthread);
514 	thread_unlock(curthread);
515 
516 	return (ret);
517 }
518