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