xref: /freebsd/sys/x86/cpufreq/hwpstate_intel.c (revision df38ada2931f95d07b65b19fcfb4c9845d656ba9)
14577cf37SConrad Meyer /*-
24577cf37SConrad Meyer  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
34577cf37SConrad Meyer  *
44577cf37SConrad Meyer  * Copyright (c) 2018 Intel Corporation
54577cf37SConrad Meyer  *
64577cf37SConrad Meyer  * Redistribution and use in source and binary forms, with or without
74577cf37SConrad Meyer  * modification, are permitted providing that the following conditions
84577cf37SConrad Meyer  * are met:
94577cf37SConrad Meyer  * 1. Redistributions of source code must retain the above copyright
104577cf37SConrad Meyer  *    notice, this list of conditions and the following disclaimer.
114577cf37SConrad Meyer  * 2. Redistributions in binary form must reproduce the above copyright
124577cf37SConrad Meyer  *    notice, this list of conditions and the following disclaimer in the
134577cf37SConrad Meyer  *    documentation and/or other materials provided with the distribution.
144577cf37SConrad Meyer  *
154577cf37SConrad Meyer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
164577cf37SConrad Meyer  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
174577cf37SConrad Meyer  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184577cf37SConrad Meyer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
194577cf37SConrad Meyer  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204577cf37SConrad Meyer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214577cf37SConrad Meyer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224577cf37SConrad Meyer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
234577cf37SConrad Meyer  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
244577cf37SConrad Meyer  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
254577cf37SConrad Meyer  * POSSIBILITY OF SUCH DAMAGE.
264577cf37SConrad Meyer  */
274577cf37SConrad Meyer 
284577cf37SConrad Meyer #include <sys/cdefs.h>
294577cf37SConrad Meyer __FBSDID("$FreeBSD$");
304577cf37SConrad Meyer 
314577cf37SConrad Meyer #include <sys/types.h>
32*df38ada2SBjoern A. Zeeb #include <sys/param.h>
334577cf37SConrad Meyer #include <sys/sbuf.h>
344577cf37SConrad Meyer #include <sys/module.h>
354577cf37SConrad Meyer #include <sys/systm.h>
364577cf37SConrad Meyer #include <sys/errno.h>
374577cf37SConrad Meyer #include <sys/param.h>
384577cf37SConrad Meyer #include <sys/kernel.h>
394577cf37SConrad Meyer #include <sys/bus.h>
404577cf37SConrad Meyer #include <sys/cpu.h>
414577cf37SConrad Meyer #include <sys/smp.h>
424577cf37SConrad Meyer #include <sys/proc.h>
434577cf37SConrad Meyer #include <sys/sched.h>
444577cf37SConrad Meyer 
454577cf37SConrad Meyer #include <machine/cpu.h>
464577cf37SConrad Meyer #include <machine/md_var.h>
474577cf37SConrad Meyer #include <machine/cputypes.h>
484577cf37SConrad Meyer #include <machine/specialreg.h>
494577cf37SConrad Meyer 
504577cf37SConrad Meyer #include <contrib/dev/acpica/include/acpi.h>
514577cf37SConrad Meyer 
524577cf37SConrad Meyer #include <dev/acpica/acpivar.h>
534577cf37SConrad Meyer 
544577cf37SConrad Meyer #include <x86/cpufreq/hwpstate_intel_internal.h>
554577cf37SConrad Meyer 
564577cf37SConrad Meyer #include "acpi_if.h"
574577cf37SConrad Meyer #include "cpufreq_if.h"
584577cf37SConrad Meyer 
594577cf37SConrad Meyer extern uint64_t	tsc_freq;
604577cf37SConrad Meyer 
614577cf37SConrad Meyer static int	intel_hwpstate_probe(device_t dev);
624577cf37SConrad Meyer static int	intel_hwpstate_attach(device_t dev);
634577cf37SConrad Meyer static int	intel_hwpstate_detach(device_t dev);
644577cf37SConrad Meyer static int	intel_hwpstate_suspend(device_t dev);
654577cf37SConrad Meyer static int	intel_hwpstate_resume(device_t dev);
664577cf37SConrad Meyer 
674577cf37SConrad Meyer static int      intel_hwpstate_get(device_t dev, struct cf_setting *cf);
684577cf37SConrad Meyer static int      intel_hwpstate_type(device_t dev, int *type);
694577cf37SConrad Meyer 
704577cf37SConrad Meyer static device_method_t intel_hwpstate_methods[] = {
714577cf37SConrad Meyer 	/* Device interface */
724577cf37SConrad Meyer 	DEVMETHOD(device_identify,	intel_hwpstate_identify),
734577cf37SConrad Meyer 	DEVMETHOD(device_probe,		intel_hwpstate_probe),
744577cf37SConrad Meyer 	DEVMETHOD(device_attach,	intel_hwpstate_attach),
754577cf37SConrad Meyer 	DEVMETHOD(device_detach,	intel_hwpstate_detach),
764577cf37SConrad Meyer 	DEVMETHOD(device_suspend,	intel_hwpstate_suspend),
774577cf37SConrad Meyer 	DEVMETHOD(device_resume,	intel_hwpstate_resume),
784577cf37SConrad Meyer 
794577cf37SConrad Meyer 	/* cpufreq interface */
804577cf37SConrad Meyer 	DEVMETHOD(cpufreq_drv_get,      intel_hwpstate_get),
814577cf37SConrad Meyer 	DEVMETHOD(cpufreq_drv_type,     intel_hwpstate_type),
824577cf37SConrad Meyer 
834577cf37SConrad Meyer 	DEVMETHOD_END
844577cf37SConrad Meyer };
854577cf37SConrad Meyer 
864577cf37SConrad Meyer struct hwp_softc {
874577cf37SConrad Meyer 	device_t		dev;
884577cf37SConrad Meyer 	bool 			hwp_notifications;
894577cf37SConrad Meyer 	bool			hwp_activity_window;
904577cf37SConrad Meyer 	bool			hwp_pref_ctrl;
914577cf37SConrad Meyer 	bool			hwp_pkg_ctrl;
92cd4e43b2SConrad Meyer 	bool			hwp_pkg_ctrl_en;
93556a1a0bSConrad Meyer 	bool			hwp_perf_bias;
94e656fa70SConrad Meyer 	bool			hwp_perf_bias_cached;
954577cf37SConrad Meyer 
96e656fa70SConrad Meyer 	uint64_t		req; /* Cached copy of HWP_REQUEST */
97e656fa70SConrad Meyer 	uint64_t		hwp_energy_perf_bias;	/* Cache PERF_BIAS */
984577cf37SConrad Meyer 
994577cf37SConrad Meyer 	uint8_t			high;
1004577cf37SConrad Meyer 	uint8_t			guaranteed;
1014577cf37SConrad Meyer 	uint8_t			efficient;
1024577cf37SConrad Meyer 	uint8_t			low;
1034577cf37SConrad Meyer };
1044577cf37SConrad Meyer 
1054577cf37SConrad Meyer static devclass_t hwpstate_intel_devclass;
1064577cf37SConrad Meyer static driver_t hwpstate_intel_driver = {
1074577cf37SConrad Meyer 	"hwpstate_intel",
1084577cf37SConrad Meyer 	intel_hwpstate_methods,
1094577cf37SConrad Meyer 	sizeof(struct hwp_softc),
1104577cf37SConrad Meyer };
1114577cf37SConrad Meyer 
1124577cf37SConrad Meyer DRIVER_MODULE(hwpstate_intel, cpu, hwpstate_intel_driver,
1134577cf37SConrad Meyer     hwpstate_intel_devclass, NULL, NULL);
11408a220ddSConrad Meyer MODULE_VERSION(hwpstate_intel, 1);
1154577cf37SConrad Meyer 
116cd4e43b2SConrad Meyer static bool hwpstate_pkg_ctrl_enable = true;
117cd4e43b2SConrad Meyer SYSCTL_BOOL(_machdep, OID_AUTO, hwpstate_pkg_ctrl, CTLFLAG_RDTUN,
118cd4e43b2SConrad Meyer     &hwpstate_pkg_ctrl_enable, 0,
119cd4e43b2SConrad Meyer     "Set 1 (default) to enable package-level control, 0 to disable");
120cd4e43b2SConrad Meyer 
1214577cf37SConrad Meyer static int
1224577cf37SConrad Meyer intel_hwp_dump_sysctl_handler(SYSCTL_HANDLER_ARGS)
1234577cf37SConrad Meyer {
1244577cf37SConrad Meyer 	device_t dev;
1254577cf37SConrad Meyer 	struct pcpu *pc;
1264577cf37SConrad Meyer 	struct sbuf *sb;
1274577cf37SConrad Meyer 	struct hwp_softc *sc;
1284577cf37SConrad Meyer 	uint64_t data, data2;
1294577cf37SConrad Meyer 	int ret;
1304577cf37SConrad Meyer 
1314577cf37SConrad Meyer 	sc = (struct hwp_softc *)arg1;
1324577cf37SConrad Meyer 	dev = sc->dev;
1334577cf37SConrad Meyer 
1344577cf37SConrad Meyer 	pc = cpu_get_pcpu(dev);
1354577cf37SConrad Meyer 	if (pc == NULL)
1364577cf37SConrad Meyer 		return (ENXIO);
1374577cf37SConrad Meyer 
1384577cf37SConrad Meyer 	sb = sbuf_new(NULL, NULL, 1024, SBUF_FIXEDLEN | SBUF_INCLUDENUL);
1394577cf37SConrad Meyer 	sbuf_putc(sb, '\n');
1404577cf37SConrad Meyer 	thread_lock(curthread);
1414577cf37SConrad Meyer 	sched_bind(curthread, pc->pc_cpuid);
1424577cf37SConrad Meyer 	thread_unlock(curthread);
1434577cf37SConrad Meyer 
1444577cf37SConrad Meyer 	rdmsr_safe(MSR_IA32_PM_ENABLE, &data);
1454577cf37SConrad Meyer 	sbuf_printf(sb, "CPU%d: HWP %sabled\n", pc->pc_cpuid,
1464577cf37SConrad Meyer 	    ((data & 1) ? "En" : "Dis"));
1474577cf37SConrad Meyer 
1484577cf37SConrad Meyer 	if (data == 0) {
1494577cf37SConrad Meyer 		ret = 0;
1504577cf37SConrad Meyer 		goto out;
1514577cf37SConrad Meyer 	}
1524577cf37SConrad Meyer 
1534577cf37SConrad Meyer 	rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &data);
154ca9fb12aSCy Schubert 	sbuf_printf(sb, "\tHighest Performance: %03ju\n", data & 0xff);
155ca9fb12aSCy Schubert 	sbuf_printf(sb, "\tGuaranteed Performance: %03ju\n", (data >> 8) & 0xff);
156ca9fb12aSCy Schubert 	sbuf_printf(sb, "\tEfficient Performance: %03ju\n", (data >> 16) & 0xff);
157ca9fb12aSCy Schubert 	sbuf_printf(sb, "\tLowest Performance: %03ju\n", (data >> 24) & 0xff);
1584577cf37SConrad Meyer 
1594577cf37SConrad Meyer 	rdmsr_safe(MSR_IA32_HWP_REQUEST, &data);
16007a65f9dSConrad Meyer 	data2 = 0;
16107a65f9dSConrad Meyer 	if (sc->hwp_pkg_ctrl && (data & IA32_HWP_REQUEST_PACKAGE_CONTROL))
1624577cf37SConrad Meyer 		rdmsr_safe(MSR_IA32_HWP_REQUEST_PKG, &data2);
1634577cf37SConrad Meyer 
1644577cf37SConrad Meyer 	sbuf_putc(sb, '\n');
1654577cf37SConrad Meyer 
1664577cf37SConrad Meyer #define pkg_print(x, name, offset) do {					\
1674577cf37SConrad Meyer 	if (!sc->hwp_pkg_ctrl || (data & x) != 0) 			\
16807a65f9dSConrad Meyer 		sbuf_printf(sb, "\t%s: %03u\n", name,			\
16907a65f9dSConrad Meyer 		    (unsigned)(data >> offset) & 0xff);			\
1704577cf37SConrad Meyer 	else								\
17107a65f9dSConrad Meyer 		sbuf_printf(sb, "\t%s: %03u\n", name,			\
17207a65f9dSConrad Meyer 		    (unsigned)(data2 >> offset) & 0xff);		\
1734577cf37SConrad Meyer } while (0)
1744577cf37SConrad Meyer 
1754577cf37SConrad Meyer 	pkg_print(IA32_HWP_REQUEST_EPP_VALID,
1764577cf37SConrad Meyer 	    "Requested Efficiency Performance Preference", 24);
1774577cf37SConrad Meyer 	pkg_print(IA32_HWP_REQUEST_DESIRED_VALID,
1784577cf37SConrad Meyer 	    "Requested Desired Performance", 16);
1794577cf37SConrad Meyer 	pkg_print(IA32_HWP_REQUEST_MAXIMUM_VALID,
1804577cf37SConrad Meyer 	    "Requested Maximum Performance", 8);
1814577cf37SConrad Meyer 	pkg_print(IA32_HWP_REQUEST_MINIMUM_VALID,
1824577cf37SConrad Meyer 	    "Requested Minimum Performance", 0);
1834577cf37SConrad Meyer #undef pkg_print
1844577cf37SConrad Meyer 
1854577cf37SConrad Meyer 	sbuf_putc(sb, '\n');
1864577cf37SConrad Meyer 
1874577cf37SConrad Meyer out:
1884577cf37SConrad Meyer 	thread_lock(curthread);
1894577cf37SConrad Meyer 	sched_unbind(curthread);
1904577cf37SConrad Meyer 	thread_unlock(curthread);
1914577cf37SConrad Meyer 
1924577cf37SConrad Meyer 	ret = sbuf_finish(sb);
1934577cf37SConrad Meyer 	if (ret == 0)
1944577cf37SConrad Meyer 		ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
1954577cf37SConrad Meyer 	sbuf_delete(sb);
1964577cf37SConrad Meyer 
1974577cf37SConrad Meyer 	return (ret);
1984577cf37SConrad Meyer }
1994577cf37SConrad Meyer 
2004577cf37SConrad Meyer static inline int
2014577cf37SConrad Meyer percent_to_raw(int x)
2024577cf37SConrad Meyer {
2034577cf37SConrad Meyer 
2044577cf37SConrad Meyer 	MPASS(x <= 100 && x >= 0);
2054577cf37SConrad Meyer 	return (0xff * x / 100);
2064577cf37SConrad Meyer }
2074577cf37SConrad Meyer 
2084577cf37SConrad Meyer /*
2094577cf37SConrad Meyer  * Given x * 10 in [0, 1000], round to the integer nearest x.
2104577cf37SConrad Meyer  *
2114577cf37SConrad Meyer  * This allows round-tripping nice human readable numbers through this
2124577cf37SConrad Meyer  * interface.  Otherwise, user-provided percentages such as 25, 50, 75 get
2134577cf37SConrad Meyer  * rounded down to 24, 49, and 74, which is a bit ugly.
2144577cf37SConrad Meyer  */
2154577cf37SConrad Meyer static inline int
2164577cf37SConrad Meyer round10(int xtimes10)
2174577cf37SConrad Meyer {
2184577cf37SConrad Meyer 	return ((xtimes10 + 5) / 10);
2194577cf37SConrad Meyer }
2204577cf37SConrad Meyer 
2214577cf37SConrad Meyer static inline int
2224577cf37SConrad Meyer raw_to_percent(int x)
2234577cf37SConrad Meyer {
2244577cf37SConrad Meyer 	MPASS(x <= 0xff && x >= 0);
2254577cf37SConrad Meyer 	return (round10(x * 1000 / 0xff));
2264577cf37SConrad Meyer }
2274577cf37SConrad Meyer 
228556a1a0bSConrad Meyer /* Range of MSR_IA32_ENERGY_PERF_BIAS is more limited: 0-0xf. */
229556a1a0bSConrad Meyer static inline int
230556a1a0bSConrad Meyer percent_to_raw_perf_bias(int x)
231556a1a0bSConrad Meyer {
232556a1a0bSConrad Meyer 	/*
233556a1a0bSConrad Meyer 	 * Round up so that raw values present as nice round human numbers and
234556a1a0bSConrad Meyer 	 * also round-trip to the same raw value.
235556a1a0bSConrad Meyer 	 */
236556a1a0bSConrad Meyer 	MPASS(x <= 100 && x >= 0);
237556a1a0bSConrad Meyer 	return (((0xf * x) + 50) / 100);
238556a1a0bSConrad Meyer }
239556a1a0bSConrad Meyer 
240556a1a0bSConrad Meyer static inline int
241556a1a0bSConrad Meyer raw_to_percent_perf_bias(int x)
242556a1a0bSConrad Meyer {
243556a1a0bSConrad Meyer 	/* Rounding to nice human numbers despite a step interval of 6.67%. */
244556a1a0bSConrad Meyer 	MPASS(x <= 0xf && x >= 0);
245556a1a0bSConrad Meyer 	return (((x * 20) / 0xf) * 5);
246556a1a0bSConrad Meyer }
247556a1a0bSConrad Meyer 
2484577cf37SConrad Meyer static int
2494577cf37SConrad Meyer sysctl_epp_select(SYSCTL_HANDLER_ARGS)
2504577cf37SConrad Meyer {
251b80d476cSConrad Meyer 	struct hwp_softc *sc;
2524577cf37SConrad Meyer 	device_t dev;
2534577cf37SConrad Meyer 	struct pcpu *pc;
254e656fa70SConrad Meyer 	uint64_t epb;
2554577cf37SConrad Meyer 	uint32_t val;
2564577cf37SConrad Meyer 	int ret;
2574577cf37SConrad Meyer 
2584577cf37SConrad Meyer 	dev = oidp->oid_arg1;
259b80d476cSConrad Meyer 	sc = device_get_softc(dev);
260556a1a0bSConrad Meyer 	if (!sc->hwp_pref_ctrl && !sc->hwp_perf_bias)
261b80d476cSConrad Meyer 		return (ENODEV);
262b80d476cSConrad Meyer 
2634577cf37SConrad Meyer 	pc = cpu_get_pcpu(dev);
2644577cf37SConrad Meyer 	if (pc == NULL)
2654577cf37SConrad Meyer 		return (ENXIO);
2664577cf37SConrad Meyer 
2674577cf37SConrad Meyer 	thread_lock(curthread);
2684577cf37SConrad Meyer 	sched_bind(curthread, pc->pc_cpuid);
2694577cf37SConrad Meyer 	thread_unlock(curthread);
2704577cf37SConrad Meyer 
271556a1a0bSConrad Meyer 	if (sc->hwp_pref_ctrl) {
272e656fa70SConrad Meyer 		val = (sc->req & IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE) >> 24;
2734577cf37SConrad Meyer 		val = raw_to_percent(val);
274556a1a0bSConrad Meyer 	} else {
275556a1a0bSConrad Meyer 		/*
276556a1a0bSConrad Meyer 		 * If cpuid indicates EPP is not supported, the HWP controller
277556a1a0bSConrad Meyer 		 * uses MSR_IA32_ENERGY_PERF_BIAS instead (Intel SDM §14.4.4).
278556a1a0bSConrad Meyer 		 * This register is per-core (but not HT).
279556a1a0bSConrad Meyer 		 */
280e656fa70SConrad Meyer 		if (!sc->hwp_perf_bias_cached) {
281e656fa70SConrad Meyer 			ret = rdmsr_safe(MSR_IA32_ENERGY_PERF_BIAS, &epb);
282556a1a0bSConrad Meyer 			if (ret)
283556a1a0bSConrad Meyer 				goto out;
284e656fa70SConrad Meyer 			sc->hwp_energy_perf_bias = epb;
285e656fa70SConrad Meyer 			sc->hwp_perf_bias_cached = true;
286e656fa70SConrad Meyer 		}
287e656fa70SConrad Meyer 		val = sc->hwp_energy_perf_bias &
288e656fa70SConrad Meyer 		    IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK;
289556a1a0bSConrad Meyer 		val = raw_to_percent_perf_bias(val);
290556a1a0bSConrad Meyer 	}
2914577cf37SConrad Meyer 
2924577cf37SConrad Meyer 	MPASS(val >= 0 && val <= 100);
2934577cf37SConrad Meyer 
2944577cf37SConrad Meyer 	ret = sysctl_handle_int(oidp, &val, 0, req);
2954577cf37SConrad Meyer 	if (ret || req->newptr == NULL)
2964577cf37SConrad Meyer 		goto out;
2974577cf37SConrad Meyer 
2984577cf37SConrad Meyer 	if (val > 100) {
2994577cf37SConrad Meyer 		ret = EINVAL;
3004577cf37SConrad Meyer 		goto out;
3014577cf37SConrad Meyer 	}
3024577cf37SConrad Meyer 
303556a1a0bSConrad Meyer 	if (sc->hwp_pref_ctrl) {
3044577cf37SConrad Meyer 		val = percent_to_raw(val);
3054577cf37SConrad Meyer 
306e656fa70SConrad Meyer 		sc->req =
307e656fa70SConrad Meyer 		    ((sc->req & ~IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE)
308e656fa70SConrad Meyer 		    | (val << 24u));
3094577cf37SConrad Meyer 
310cd4e43b2SConrad Meyer 		if (sc->hwp_pkg_ctrl_en)
311e656fa70SConrad Meyer 			ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req);
312cd4e43b2SConrad Meyer 		else
313e656fa70SConrad Meyer 			ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
314556a1a0bSConrad Meyer 	} else {
315e656fa70SConrad Meyer 		val = percent_to_raw_perf_bias(val);
316e656fa70SConrad Meyer 		MPASS((val & ~IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK) == 0);
317e656fa70SConrad Meyer 
318e656fa70SConrad Meyer 		sc->hwp_energy_perf_bias =
319e656fa70SConrad Meyer 		    ((sc->hwp_energy_perf_bias &
320e656fa70SConrad Meyer 		    ~IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK) | val);
321e656fa70SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_ENERGY_PERF_BIAS,
322e656fa70SConrad Meyer 		    sc->hwp_energy_perf_bias);
323556a1a0bSConrad Meyer 	}
3244577cf37SConrad Meyer 
3254577cf37SConrad Meyer out:
3264577cf37SConrad Meyer 	thread_lock(curthread);
3274577cf37SConrad Meyer 	sched_unbind(curthread);
3284577cf37SConrad Meyer 	thread_unlock(curthread);
3294577cf37SConrad Meyer 
3304577cf37SConrad Meyer 	return (ret);
3314577cf37SConrad Meyer }
3324577cf37SConrad Meyer 
3334577cf37SConrad Meyer void
3344577cf37SConrad Meyer intel_hwpstate_identify(driver_t *driver, device_t parent)
3354577cf37SConrad Meyer {
3364577cf37SConrad Meyer 	if (device_find_child(parent, "hwpstate_intel", -1) != NULL)
3374577cf37SConrad Meyer 		return;
3384577cf37SConrad Meyer 
3394577cf37SConrad Meyer 	if (cpu_vendor_id != CPU_VENDOR_INTEL)
3404577cf37SConrad Meyer 		return;
3414577cf37SConrad Meyer 
3424577cf37SConrad Meyer 	if (resource_disabled("hwpstate_intel", 0))
3434577cf37SConrad Meyer 		return;
3444577cf37SConrad Meyer 
3454577cf37SConrad Meyer 	/*
3464577cf37SConrad Meyer 	 * Intel SDM 14.4.1 (HWP Programming Interfaces):
3474577cf37SConrad Meyer 	 *   Availability of HWP baseline resource and capability,
3484577cf37SConrad Meyer 	 *   CPUID.06H:EAX[bit 7]: If this bit is set, HWP provides several new
3494577cf37SConrad Meyer 	 *   architectural MSRs: IA32_PM_ENABLE, IA32_HWP_CAPABILITIES,
3504577cf37SConrad Meyer 	 *   IA32_HWP_REQUEST, IA32_HWP_STATUS.
3514577cf37SConrad Meyer 	 */
352f591c3c8SConrad Meyer 	if ((cpu_power_eax & CPUTPM1_HWP) == 0)
3534577cf37SConrad Meyer 		return;
3544577cf37SConrad Meyer 
355d3a8f98aSAlexander Motin 	if (BUS_ADD_CHILD(parent, 10, "hwpstate_intel", device_get_unit(parent))
356d3a8f98aSAlexander Motin 	    == NULL)
357d3a8f98aSAlexander Motin 		device_printf(parent, "hwpstate_intel: add child failed\n");
3584577cf37SConrad Meyer }
3594577cf37SConrad Meyer 
3604577cf37SConrad Meyer static int
3614577cf37SConrad Meyer intel_hwpstate_probe(device_t dev)
3624577cf37SConrad Meyer {
3634577cf37SConrad Meyer 
3644577cf37SConrad Meyer 	device_set_desc(dev, "Intel Speed Shift");
3654577cf37SConrad Meyer 	return (BUS_PROBE_NOWILDCARD);
3664577cf37SConrad Meyer }
3674577cf37SConrad Meyer 
3684577cf37SConrad Meyer static int
3694577cf37SConrad Meyer set_autonomous_hwp(struct hwp_softc *sc)
3704577cf37SConrad Meyer {
3714577cf37SConrad Meyer 	struct pcpu *pc;
3724577cf37SConrad Meyer 	device_t dev;
3734577cf37SConrad Meyer 	uint64_t caps;
3744577cf37SConrad Meyer 	int ret;
3754577cf37SConrad Meyer 
3764577cf37SConrad Meyer 	dev = sc->dev;
3774577cf37SConrad Meyer 
3784577cf37SConrad Meyer 	pc = cpu_get_pcpu(dev);
3794577cf37SConrad Meyer 	if (pc == NULL)
3804577cf37SConrad Meyer 		return (ENXIO);
3814577cf37SConrad Meyer 
3824577cf37SConrad Meyer 	thread_lock(curthread);
3834577cf37SConrad Meyer 	sched_bind(curthread, pc->pc_cpuid);
3844577cf37SConrad Meyer 	thread_unlock(curthread);
3854577cf37SConrad Meyer 
3864577cf37SConrad Meyer 	/* XXX: Many MSRs aren't readable until feature is enabled */
3874577cf37SConrad Meyer 	ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1);
3884577cf37SConrad Meyer 	if (ret) {
3895e3574c8SConrad Meyer 		/*
3905e3574c8SConrad Meyer 		 * This is actually a package-level MSR, and only the first
3915e3574c8SConrad Meyer 		 * write is not ignored.  So it is harmless to enable it across
3925e3574c8SConrad Meyer 		 * all devices, and this allows us not to care especially in
3935e3574c8SConrad Meyer 		 * which order cores (and packages) are probed.  This error
3945e3574c8SConrad Meyer 		 * condition should not happen given we gate on the HWP CPUID
3955e3574c8SConrad Meyer 		 * feature flag, if the Intel SDM is correct.
3965e3574c8SConrad Meyer 		 */
3974577cf37SConrad Meyer 		device_printf(dev, "Failed to enable HWP for cpu%d (%d)\n",
3984577cf37SConrad Meyer 		    pc->pc_cpuid, ret);
3994577cf37SConrad Meyer 		goto out;
4004577cf37SConrad Meyer 	}
4014577cf37SConrad Meyer 
4024577cf37SConrad Meyer 	ret = rdmsr_safe(MSR_IA32_HWP_REQUEST, &sc->req);
403351896d3SConrad Meyer 	if (ret) {
404351896d3SConrad Meyer 		device_printf(dev,
405351896d3SConrad Meyer 		    "Failed to read HWP request MSR for cpu%d (%d)\n",
406351896d3SConrad Meyer 		    pc->pc_cpuid, ret);
407351896d3SConrad Meyer 		goto out;
408351896d3SConrad Meyer 	}
4094577cf37SConrad Meyer 
4104577cf37SConrad Meyer 	ret = rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &caps);
411351896d3SConrad Meyer 	if (ret) {
412351896d3SConrad Meyer 		device_printf(dev,
413351896d3SConrad Meyer 		    "Failed to read HWP capabilities MSR for cpu%d (%d)\n",
414351896d3SConrad Meyer 		    pc->pc_cpuid, ret);
415351896d3SConrad Meyer 		goto out;
416351896d3SConrad Meyer 	}
4174577cf37SConrad Meyer 
4185e3574c8SConrad Meyer 	/*
4195e3574c8SConrad Meyer 	 * High and low are static; "guaranteed" is dynamic; and efficient is
4205e3574c8SConrad Meyer 	 * also dynamic.
4215e3574c8SConrad Meyer 	 */
4224577cf37SConrad Meyer 	sc->high = IA32_HWP_CAPABILITIES_HIGHEST_PERFORMANCE(caps);
4234577cf37SConrad Meyer 	sc->guaranteed = IA32_HWP_CAPABILITIES_GUARANTEED_PERFORMANCE(caps);
4244577cf37SConrad Meyer 	sc->efficient = IA32_HWP_CAPABILITIES_EFFICIENT_PERFORMANCE(caps);
4254577cf37SConrad Meyer 	sc->low = IA32_HWP_CAPABILITIES_LOWEST_PERFORMANCE(caps);
4264577cf37SConrad Meyer 
4274577cf37SConrad Meyer 	/* hardware autonomous selection determines the performance target */
4284577cf37SConrad Meyer 	sc->req &= ~IA32_HWP_DESIRED_PERFORMANCE;
4294577cf37SConrad Meyer 
4304577cf37SConrad Meyer 	/* enable HW dynamic selection of window size */
4314577cf37SConrad Meyer 	sc->req &= ~IA32_HWP_ACTIVITY_WINDOW;
4324577cf37SConrad Meyer 
4334577cf37SConrad Meyer 	/* IA32_HWP_REQUEST.Minimum_Performance = IA32_HWP_CAPABILITIES.Lowest_Performance */
4344577cf37SConrad Meyer 	sc->req &= ~IA32_HWP_MINIMUM_PERFORMANCE;
4354577cf37SConrad Meyer 	sc->req |= sc->low;
4364577cf37SConrad Meyer 
4374577cf37SConrad Meyer 	/* IA32_HWP_REQUEST.Maximum_Performance = IA32_HWP_CAPABILITIES.Highest_Performance. */
4384577cf37SConrad Meyer 	sc->req &= ~IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE;
4394577cf37SConrad Meyer 	sc->req |= sc->high << 8;
4404577cf37SConrad Meyer 
441cd4e43b2SConrad Meyer 	/* If supported, request package-level control for this CPU. */
442cd4e43b2SConrad Meyer 	if (sc->hwp_pkg_ctrl_en)
443cd4e43b2SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req |
444cd4e43b2SConrad Meyer 		    IA32_HWP_REQUEST_PACKAGE_CONTROL);
445cd4e43b2SConrad Meyer 	else
4464577cf37SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
4474577cf37SConrad Meyer 	if (ret) {
4484577cf37SConrad Meyer 		device_printf(dev,
449cd4e43b2SConrad Meyer 		    "Failed to setup%s autonomous HWP for cpu%d\n",
450cd4e43b2SConrad Meyer 		    sc->hwp_pkg_ctrl_en ? " PKG" : "", pc->pc_cpuid);
451cd4e43b2SConrad Meyer 		goto out;
452cd4e43b2SConrad Meyer 	}
453cd4e43b2SConrad Meyer 
454cd4e43b2SConrad Meyer 	/* If supported, write the PKG-wide control MSR. */
455cd4e43b2SConrad Meyer 	if (sc->hwp_pkg_ctrl_en) {
456cd4e43b2SConrad Meyer 		/*
457cd4e43b2SConrad Meyer 		 * "The structure of the IA32_HWP_REQUEST_PKG MSR
458cd4e43b2SConrad Meyer 		 * (package-level) is identical to the IA32_HWP_REQUEST MSR
459cd4e43b2SConrad Meyer 		 * with the exception of the Package Control field, which does
460cd4e43b2SConrad Meyer 		 * not exist." (Intel SDM §14.4.4)
461cd4e43b2SConrad Meyer 		 */
462cd4e43b2SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req);
46341062d68SYuri Pankov 		if (ret) {
464cd4e43b2SConrad Meyer 			device_printf(dev,
465cd4e43b2SConrad Meyer 			    "Failed to set autonomous HWP for package\n");
4664577cf37SConrad Meyer 		}
46741062d68SYuri Pankov 	}
4684577cf37SConrad Meyer 
4694577cf37SConrad Meyer out:
4704577cf37SConrad Meyer 	thread_lock(curthread);
4714577cf37SConrad Meyer 	sched_unbind(curthread);
4724577cf37SConrad Meyer 	thread_unlock(curthread);
4734577cf37SConrad Meyer 
4744577cf37SConrad Meyer 	return (ret);
4754577cf37SConrad Meyer }
4764577cf37SConrad Meyer 
4774577cf37SConrad Meyer static int
4784577cf37SConrad Meyer intel_hwpstate_attach(device_t dev)
4794577cf37SConrad Meyer {
4804577cf37SConrad Meyer 	struct hwp_softc *sc;
4814577cf37SConrad Meyer 	int ret;
4824577cf37SConrad Meyer 
4834577cf37SConrad Meyer 	sc = device_get_softc(dev);
4844577cf37SConrad Meyer 	sc->dev = dev;
4854577cf37SConrad Meyer 
486556a1a0bSConrad Meyer 	/* eax */
487f591c3c8SConrad Meyer 	if (cpu_power_eax & CPUTPM1_HWP_NOTIFICATION)
4884577cf37SConrad Meyer 		sc->hwp_notifications = true;
489f591c3c8SConrad Meyer 	if (cpu_power_eax & CPUTPM1_HWP_ACTIVITY_WINDOW)
4904577cf37SConrad Meyer 		sc->hwp_activity_window = true;
491f591c3c8SConrad Meyer 	if (cpu_power_eax & CPUTPM1_HWP_PERF_PREF)
4924577cf37SConrad Meyer 		sc->hwp_pref_ctrl = true;
493f591c3c8SConrad Meyer 	if (cpu_power_eax & CPUTPM1_HWP_PKG)
4944577cf37SConrad Meyer 		sc->hwp_pkg_ctrl = true;
4954577cf37SConrad Meyer 
496cd4e43b2SConrad Meyer 	/* Allow administrators to disable pkg-level control. */
497cd4e43b2SConrad Meyer 	sc->hwp_pkg_ctrl_en = (sc->hwp_pkg_ctrl && hwpstate_pkg_ctrl_enable);
498cd4e43b2SConrad Meyer 
499556a1a0bSConrad Meyer 	/* ecx */
500556a1a0bSConrad Meyer 	if (cpu_power_ecx & CPUID_PERF_BIAS)
501556a1a0bSConrad Meyer 		sc->hwp_perf_bias = true;
502556a1a0bSConrad Meyer 
5034577cf37SConrad Meyer 	ret = set_autonomous_hwp(sc);
5044577cf37SConrad Meyer 	if (ret)
5054577cf37SConrad Meyer 		return (ret);
5064577cf37SConrad Meyer 
5074577cf37SConrad Meyer 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
5084577cf37SConrad Meyer 	    SYSCTL_STATIC_CHILDREN(_debug), OID_AUTO, device_get_nameunit(dev),
5097029da5cSPawel Biernacki 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_NEEDGIANT,
5104577cf37SConrad Meyer 	    sc, 0, intel_hwp_dump_sysctl_handler, "A", "");
5114577cf37SConrad Meyer 
5124577cf37SConrad Meyer 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
5134577cf37SConrad Meyer 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
5147029da5cSPawel Biernacki 	    "epp", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, dev, 0,
5154577cf37SConrad Meyer 	    sysctl_epp_select, "I",
5164577cf37SConrad Meyer 	    "Efficiency/Performance Preference "
5174577cf37SConrad Meyer 	    "(range from 0, most performant, through 100, most efficient)");
5184577cf37SConrad Meyer 
5194577cf37SConrad Meyer 	return (cpufreq_register(dev));
5204577cf37SConrad Meyer }
5214577cf37SConrad Meyer 
5224577cf37SConrad Meyer static int
5234577cf37SConrad Meyer intel_hwpstate_detach(device_t dev)
5244577cf37SConrad Meyer {
5254577cf37SConrad Meyer 
5264577cf37SConrad Meyer 	return (cpufreq_unregister(dev));
5274577cf37SConrad Meyer }
5284577cf37SConrad Meyer 
5294577cf37SConrad Meyer static int
5304577cf37SConrad Meyer intel_hwpstate_get(device_t dev, struct cf_setting *set)
5314577cf37SConrad Meyer {
5324577cf37SConrad Meyer 	struct pcpu *pc;
5334577cf37SConrad Meyer 	uint64_t rate;
5344577cf37SConrad Meyer 	int ret;
5354577cf37SConrad Meyer 
5364577cf37SConrad Meyer 	if (set == NULL)
5374577cf37SConrad Meyer 		return (EINVAL);
5384577cf37SConrad Meyer 
5394577cf37SConrad Meyer 	pc = cpu_get_pcpu(dev);
5404577cf37SConrad Meyer 	if (pc == NULL)
5414577cf37SConrad Meyer 		return (ENXIO);
5424577cf37SConrad Meyer 
5434577cf37SConrad Meyer 	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
5444577cf37SConrad Meyer 	set->dev = dev;
5454577cf37SConrad Meyer 
5464577cf37SConrad Meyer 	ret = cpu_est_clockrate(pc->pc_cpuid, &rate);
5474577cf37SConrad Meyer 	if (ret == 0)
5484577cf37SConrad Meyer 		set->freq = rate / 1000000;
5494577cf37SConrad Meyer 
5504577cf37SConrad Meyer 	set->volts = CPUFREQ_VAL_UNKNOWN;
5514577cf37SConrad Meyer 	set->power = CPUFREQ_VAL_UNKNOWN;
5524577cf37SConrad Meyer 	set->lat = CPUFREQ_VAL_UNKNOWN;
5534577cf37SConrad Meyer 
5544577cf37SConrad Meyer 	return (0);
5554577cf37SConrad Meyer }
5564577cf37SConrad Meyer 
5574577cf37SConrad Meyer static int
5584577cf37SConrad Meyer intel_hwpstate_type(device_t dev, int *type)
5594577cf37SConrad Meyer {
5604577cf37SConrad Meyer 	if (type == NULL)
5614577cf37SConrad Meyer 		return (EINVAL);
5624577cf37SConrad Meyer 	*type = CPUFREQ_TYPE_ABSOLUTE | CPUFREQ_FLAG_INFO_ONLY | CPUFREQ_FLAG_UNCACHED;
5634577cf37SConrad Meyer 
5644577cf37SConrad Meyer 	return (0);
5654577cf37SConrad Meyer }
5664577cf37SConrad Meyer 
5674577cf37SConrad Meyer static int
5684577cf37SConrad Meyer intel_hwpstate_suspend(device_t dev)
5694577cf37SConrad Meyer {
5704577cf37SConrad Meyer 	return (0);
5714577cf37SConrad Meyer }
5724577cf37SConrad Meyer 
5734577cf37SConrad Meyer /*
5744577cf37SConrad Meyer  * Redo a subset of set_autonomous_hwp on resume; untested.  Without this,
5754577cf37SConrad Meyer  * testers observed that on resume MSR_IA32_HWP_REQUEST was bogus.
5764577cf37SConrad Meyer  */
5774577cf37SConrad Meyer static int
5784577cf37SConrad Meyer intel_hwpstate_resume(device_t dev)
5794577cf37SConrad Meyer {
5804577cf37SConrad Meyer 	struct hwp_softc *sc;
5814577cf37SConrad Meyer 	struct pcpu *pc;
5824577cf37SConrad Meyer 	int ret;
5834577cf37SConrad Meyer 
5844577cf37SConrad Meyer 	sc = device_get_softc(dev);
5854577cf37SConrad Meyer 
5864577cf37SConrad Meyer 	pc = cpu_get_pcpu(dev);
5874577cf37SConrad Meyer 	if (pc == NULL)
5884577cf37SConrad Meyer 		return (ENXIO);
5894577cf37SConrad Meyer 
5904577cf37SConrad Meyer 	thread_lock(curthread);
5914577cf37SConrad Meyer 	sched_bind(curthread, pc->pc_cpuid);
5924577cf37SConrad Meyer 	thread_unlock(curthread);
5934577cf37SConrad Meyer 
5944577cf37SConrad Meyer 	ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1);
5954577cf37SConrad Meyer 	if (ret) {
5964577cf37SConrad Meyer 		device_printf(dev,
5974577cf37SConrad Meyer 		    "Failed to enable HWP for cpu%d after suspend (%d)\n",
5984577cf37SConrad Meyer 		    pc->pc_cpuid, ret);
5994577cf37SConrad Meyer 		goto out;
6004577cf37SConrad Meyer 	}
6014577cf37SConrad Meyer 
602cd4e43b2SConrad Meyer 	if (sc->hwp_pkg_ctrl_en)
603cd4e43b2SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req |
604cd4e43b2SConrad Meyer 		    IA32_HWP_REQUEST_PACKAGE_CONTROL);
605cd4e43b2SConrad Meyer 	else
6064577cf37SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
6074577cf37SConrad Meyer 	if (ret) {
6084577cf37SConrad Meyer 		device_printf(dev,
609cd4e43b2SConrad Meyer 		    "Failed to set%s autonomous HWP for cpu%d after suspend\n",
610cd4e43b2SConrad Meyer 		    sc->hwp_pkg_ctrl_en ? " PKG" : "", pc->pc_cpuid);
611cd4e43b2SConrad Meyer 		goto out;
612cd4e43b2SConrad Meyer 	}
613cd4e43b2SConrad Meyer 	if (sc->hwp_pkg_ctrl_en) {
614cd4e43b2SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req);
615e656fa70SConrad Meyer 		if (ret) {
616cd4e43b2SConrad Meyer 			device_printf(dev,
617c93eb470SConrad Meyer 			    "Failed to set autonomous HWP for package after "
618c93eb470SConrad Meyer 			    "suspend\n");
619e656fa70SConrad Meyer 			goto out;
620e656fa70SConrad Meyer 		}
621e656fa70SConrad Meyer 	}
622e656fa70SConrad Meyer 	if (!sc->hwp_pref_ctrl && sc->hwp_perf_bias_cached) {
623e656fa70SConrad Meyer 		ret = wrmsr_safe(MSR_IA32_ENERGY_PERF_BIAS,
624e656fa70SConrad Meyer 		    sc->hwp_energy_perf_bias);
625e656fa70SConrad Meyer 		if (ret) {
626e656fa70SConrad Meyer 			device_printf(dev,
627e656fa70SConrad Meyer 			    "Failed to set energy perf bias for cpu%d after "
628e656fa70SConrad Meyer 			    "suspend\n", pc->pc_cpuid);
629e656fa70SConrad Meyer 		}
6304577cf37SConrad Meyer 	}
6314577cf37SConrad Meyer 
6324577cf37SConrad Meyer out:
6334577cf37SConrad Meyer 	thread_lock(curthread);
6344577cf37SConrad Meyer 	sched_unbind(curthread);
6354577cf37SConrad Meyer 	thread_unlock(curthread);
6364577cf37SConrad Meyer 
6374577cf37SConrad Meyer 	return (ret);
6384577cf37SConrad Meyer }
639