14577cf37SConrad Meyer /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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/types.h>
29df38ada2SBjoern A. Zeeb #include <sys/param.h>
304577cf37SConrad Meyer #include <sys/sbuf.h>
314577cf37SConrad Meyer #include <sys/module.h>
324577cf37SConrad Meyer #include <sys/systm.h>
334577cf37SConrad Meyer #include <sys/errno.h>
344577cf37SConrad Meyer #include <sys/param.h>
354577cf37SConrad Meyer #include <sys/kernel.h>
364577cf37SConrad Meyer #include <sys/bus.h>
374577cf37SConrad Meyer #include <sys/cpu.h>
384577cf37SConrad Meyer #include <sys/smp.h>
394577cf37SConrad Meyer #include <sys/proc.h>
404577cf37SConrad Meyer #include <sys/sched.h>
414577cf37SConrad Meyer
424577cf37SConrad Meyer #include <machine/cpu.h>
434577cf37SConrad Meyer #include <machine/md_var.h>
444577cf37SConrad Meyer #include <machine/cputypes.h>
454577cf37SConrad Meyer #include <machine/specialreg.h>
464577cf37SConrad Meyer
474577cf37SConrad Meyer #include <contrib/dev/acpica/include/acpi.h>
484577cf37SConrad Meyer
494577cf37SConrad Meyer #include <dev/acpica/acpivar.h>
504577cf37SConrad Meyer
514577cf37SConrad Meyer #include <x86/cpufreq/hwpstate_intel_internal.h>
524577cf37SConrad Meyer
534577cf37SConrad Meyer #include "acpi_if.h"
544577cf37SConrad Meyer #include "cpufreq_if.h"
554577cf37SConrad Meyer
564577cf37SConrad Meyer extern uint64_t tsc_freq;
574577cf37SConrad Meyer
584577cf37SConrad Meyer static int intel_hwpstate_probe(device_t dev);
594577cf37SConrad Meyer static int intel_hwpstate_attach(device_t dev);
604577cf37SConrad Meyer static int intel_hwpstate_detach(device_t dev);
614577cf37SConrad Meyer static int intel_hwpstate_suspend(device_t dev);
624577cf37SConrad Meyer static int intel_hwpstate_resume(device_t dev);
634577cf37SConrad Meyer
644577cf37SConrad Meyer static int intel_hwpstate_get(device_t dev, struct cf_setting *cf);
654577cf37SConrad Meyer static int intel_hwpstate_type(device_t dev, int *type);
664577cf37SConrad Meyer
674577cf37SConrad Meyer static device_method_t intel_hwpstate_methods[] = {
684577cf37SConrad Meyer /* Device interface */
694577cf37SConrad Meyer DEVMETHOD(device_identify, intel_hwpstate_identify),
704577cf37SConrad Meyer DEVMETHOD(device_probe, intel_hwpstate_probe),
714577cf37SConrad Meyer DEVMETHOD(device_attach, intel_hwpstate_attach),
724577cf37SConrad Meyer DEVMETHOD(device_detach, intel_hwpstate_detach),
734577cf37SConrad Meyer DEVMETHOD(device_suspend, intel_hwpstate_suspend),
744577cf37SConrad Meyer DEVMETHOD(device_resume, intel_hwpstate_resume),
754577cf37SConrad Meyer
764577cf37SConrad Meyer /* cpufreq interface */
774577cf37SConrad Meyer DEVMETHOD(cpufreq_drv_get, intel_hwpstate_get),
784577cf37SConrad Meyer DEVMETHOD(cpufreq_drv_type, intel_hwpstate_type),
794577cf37SConrad Meyer
804577cf37SConrad Meyer DEVMETHOD_END
814577cf37SConrad Meyer };
824577cf37SConrad Meyer
834577cf37SConrad Meyer struct hwp_softc {
844577cf37SConrad Meyer device_t dev;
854577cf37SConrad Meyer bool hwp_notifications;
864577cf37SConrad Meyer bool hwp_activity_window;
874577cf37SConrad Meyer bool hwp_pref_ctrl;
884577cf37SConrad Meyer bool hwp_pkg_ctrl;
89cd4e43b2SConrad Meyer bool hwp_pkg_ctrl_en;
90556a1a0bSConrad Meyer bool hwp_perf_bias;
91e656fa70SConrad Meyer bool hwp_perf_bias_cached;
924577cf37SConrad Meyer
93e656fa70SConrad Meyer uint64_t req; /* Cached copy of HWP_REQUEST */
94e656fa70SConrad Meyer uint64_t hwp_energy_perf_bias; /* Cache PERF_BIAS */
954577cf37SConrad Meyer
964577cf37SConrad Meyer uint8_t high;
974577cf37SConrad Meyer uint8_t guaranteed;
984577cf37SConrad Meyer uint8_t efficient;
994577cf37SConrad Meyer uint8_t low;
1004577cf37SConrad Meyer };
1014577cf37SConrad Meyer
1024577cf37SConrad Meyer static driver_t hwpstate_intel_driver = {
1034577cf37SConrad Meyer "hwpstate_intel",
1044577cf37SConrad Meyer intel_hwpstate_methods,
1054577cf37SConrad Meyer sizeof(struct hwp_softc),
1064577cf37SConrad Meyer };
1074577cf37SConrad Meyer
108b3407dccSJohn Baldwin DRIVER_MODULE(hwpstate_intel, cpu, hwpstate_intel_driver, NULL, NULL);
10908a220ddSConrad Meyer MODULE_VERSION(hwpstate_intel, 1);
1104577cf37SConrad Meyer
111cd4e43b2SConrad Meyer static bool hwpstate_pkg_ctrl_enable = true;
112cd4e43b2SConrad Meyer SYSCTL_BOOL(_machdep, OID_AUTO, hwpstate_pkg_ctrl, CTLFLAG_RDTUN,
113cd4e43b2SConrad Meyer &hwpstate_pkg_ctrl_enable, 0,
114cd4e43b2SConrad Meyer "Set 1 (default) to enable package-level control, 0 to disable");
115cd4e43b2SConrad Meyer
1164577cf37SConrad Meyer static int
intel_hwp_dump_sysctl_handler(SYSCTL_HANDLER_ARGS)1174577cf37SConrad Meyer intel_hwp_dump_sysctl_handler(SYSCTL_HANDLER_ARGS)
1184577cf37SConrad Meyer {
1194577cf37SConrad Meyer device_t dev;
1204577cf37SConrad Meyer struct pcpu *pc;
1214577cf37SConrad Meyer struct sbuf *sb;
1224577cf37SConrad Meyer struct hwp_softc *sc;
1234577cf37SConrad Meyer uint64_t data, data2;
1244577cf37SConrad Meyer int ret;
1254577cf37SConrad Meyer
1264577cf37SConrad Meyer sc = (struct hwp_softc *)arg1;
1274577cf37SConrad Meyer dev = sc->dev;
1284577cf37SConrad Meyer
1294577cf37SConrad Meyer pc = cpu_get_pcpu(dev);
1304577cf37SConrad Meyer if (pc == NULL)
1314577cf37SConrad Meyer return (ENXIO);
1324577cf37SConrad Meyer
1334577cf37SConrad Meyer sb = sbuf_new(NULL, NULL, 1024, SBUF_FIXEDLEN | SBUF_INCLUDENUL);
1344577cf37SConrad Meyer sbuf_putc(sb, '\n');
1354577cf37SConrad Meyer thread_lock(curthread);
1364577cf37SConrad Meyer sched_bind(curthread, pc->pc_cpuid);
1374577cf37SConrad Meyer thread_unlock(curthread);
1384577cf37SConrad Meyer
1394577cf37SConrad Meyer rdmsr_safe(MSR_IA32_PM_ENABLE, &data);
1404577cf37SConrad Meyer sbuf_printf(sb, "CPU%d: HWP %sabled\n", pc->pc_cpuid,
1414577cf37SConrad Meyer ((data & 1) ? "En" : "Dis"));
1424577cf37SConrad Meyer
1434577cf37SConrad Meyer if (data == 0) {
1444577cf37SConrad Meyer ret = 0;
1454577cf37SConrad Meyer goto out;
1464577cf37SConrad Meyer }
1474577cf37SConrad Meyer
1484577cf37SConrad Meyer rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &data);
149ca9fb12aSCy Schubert sbuf_printf(sb, "\tHighest Performance: %03ju\n", data & 0xff);
150ca9fb12aSCy Schubert sbuf_printf(sb, "\tGuaranteed Performance: %03ju\n", (data >> 8) & 0xff);
151ca9fb12aSCy Schubert sbuf_printf(sb, "\tEfficient Performance: %03ju\n", (data >> 16) & 0xff);
152ca9fb12aSCy Schubert sbuf_printf(sb, "\tLowest Performance: %03ju\n", (data >> 24) & 0xff);
1534577cf37SConrad Meyer
1544577cf37SConrad Meyer rdmsr_safe(MSR_IA32_HWP_REQUEST, &data);
15507a65f9dSConrad Meyer data2 = 0;
15607a65f9dSConrad Meyer if (sc->hwp_pkg_ctrl && (data & IA32_HWP_REQUEST_PACKAGE_CONTROL))
1574577cf37SConrad Meyer rdmsr_safe(MSR_IA32_HWP_REQUEST_PKG, &data2);
1584577cf37SConrad Meyer
1594577cf37SConrad Meyer sbuf_putc(sb, '\n');
1604577cf37SConrad Meyer
1614577cf37SConrad Meyer #define pkg_print(x, name, offset) do { \
1624577cf37SConrad Meyer if (!sc->hwp_pkg_ctrl || (data & x) != 0) \
16307a65f9dSConrad Meyer sbuf_printf(sb, "\t%s: %03u\n", name, \
16407a65f9dSConrad Meyer (unsigned)(data >> offset) & 0xff); \
1654577cf37SConrad Meyer else \
16607a65f9dSConrad Meyer sbuf_printf(sb, "\t%s: %03u\n", name, \
16707a65f9dSConrad Meyer (unsigned)(data2 >> offset) & 0xff); \
1684577cf37SConrad Meyer } while (0)
1694577cf37SConrad Meyer
1704577cf37SConrad Meyer pkg_print(IA32_HWP_REQUEST_EPP_VALID,
1714577cf37SConrad Meyer "Requested Efficiency Performance Preference", 24);
1724577cf37SConrad Meyer pkg_print(IA32_HWP_REQUEST_DESIRED_VALID,
1734577cf37SConrad Meyer "Requested Desired Performance", 16);
1744577cf37SConrad Meyer pkg_print(IA32_HWP_REQUEST_MAXIMUM_VALID,
1754577cf37SConrad Meyer "Requested Maximum Performance", 8);
1764577cf37SConrad Meyer pkg_print(IA32_HWP_REQUEST_MINIMUM_VALID,
1774577cf37SConrad Meyer "Requested Minimum Performance", 0);
1784577cf37SConrad Meyer #undef pkg_print
1794577cf37SConrad Meyer
1804577cf37SConrad Meyer sbuf_putc(sb, '\n');
1814577cf37SConrad Meyer
1824577cf37SConrad Meyer out:
1834577cf37SConrad Meyer thread_lock(curthread);
1844577cf37SConrad Meyer sched_unbind(curthread);
1854577cf37SConrad Meyer thread_unlock(curthread);
1864577cf37SConrad Meyer
1874577cf37SConrad Meyer ret = sbuf_finish(sb);
1884577cf37SConrad Meyer if (ret == 0)
1894577cf37SConrad Meyer ret = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb));
1904577cf37SConrad Meyer sbuf_delete(sb);
1914577cf37SConrad Meyer
1924577cf37SConrad Meyer return (ret);
1934577cf37SConrad Meyer }
1944577cf37SConrad Meyer
1954577cf37SConrad Meyer static inline int
percent_to_raw(int x)1964577cf37SConrad Meyer percent_to_raw(int x)
1974577cf37SConrad Meyer {
1984577cf37SConrad Meyer
1994577cf37SConrad Meyer MPASS(x <= 100 && x >= 0);
2004577cf37SConrad Meyer return (0xff * x / 100);
2014577cf37SConrad Meyer }
2024577cf37SConrad Meyer
2034577cf37SConrad Meyer /*
2044577cf37SConrad Meyer * Given x * 10 in [0, 1000], round to the integer nearest x.
2054577cf37SConrad Meyer *
2064577cf37SConrad Meyer * This allows round-tripping nice human readable numbers through this
2074577cf37SConrad Meyer * interface. Otherwise, user-provided percentages such as 25, 50, 75 get
2084577cf37SConrad Meyer * rounded down to 24, 49, and 74, which is a bit ugly.
2094577cf37SConrad Meyer */
2104577cf37SConrad Meyer static inline int
round10(int xtimes10)2114577cf37SConrad Meyer round10(int xtimes10)
2124577cf37SConrad Meyer {
2134577cf37SConrad Meyer return ((xtimes10 + 5) / 10);
2144577cf37SConrad Meyer }
2154577cf37SConrad Meyer
2164577cf37SConrad Meyer static inline int
raw_to_percent(int x)2174577cf37SConrad Meyer raw_to_percent(int x)
2184577cf37SConrad Meyer {
2194577cf37SConrad Meyer MPASS(x <= 0xff && x >= 0);
2204577cf37SConrad Meyer return (round10(x * 1000 / 0xff));
2214577cf37SConrad Meyer }
2224577cf37SConrad Meyer
223556a1a0bSConrad Meyer /* Range of MSR_IA32_ENERGY_PERF_BIAS is more limited: 0-0xf. */
224556a1a0bSConrad Meyer static inline int
percent_to_raw_perf_bias(int x)225556a1a0bSConrad Meyer percent_to_raw_perf_bias(int x)
226556a1a0bSConrad Meyer {
227556a1a0bSConrad Meyer /*
228556a1a0bSConrad Meyer * Round up so that raw values present as nice round human numbers and
229556a1a0bSConrad Meyer * also round-trip to the same raw value.
230556a1a0bSConrad Meyer */
231556a1a0bSConrad Meyer MPASS(x <= 100 && x >= 0);
232556a1a0bSConrad Meyer return (((0xf * x) + 50) / 100);
233556a1a0bSConrad Meyer }
234556a1a0bSConrad Meyer
235556a1a0bSConrad Meyer static inline int
raw_to_percent_perf_bias(int x)236556a1a0bSConrad Meyer raw_to_percent_perf_bias(int x)
237556a1a0bSConrad Meyer {
238556a1a0bSConrad Meyer /* Rounding to nice human numbers despite a step interval of 6.67%. */
239556a1a0bSConrad Meyer MPASS(x <= 0xf && x >= 0);
240556a1a0bSConrad Meyer return (((x * 20) / 0xf) * 5);
241556a1a0bSConrad Meyer }
242556a1a0bSConrad Meyer
2434577cf37SConrad Meyer static int
sysctl_epp_select(SYSCTL_HANDLER_ARGS)2444577cf37SConrad Meyer sysctl_epp_select(SYSCTL_HANDLER_ARGS)
2454577cf37SConrad Meyer {
246b80d476cSConrad Meyer struct hwp_softc *sc;
2474577cf37SConrad Meyer device_t dev;
2484577cf37SConrad Meyer struct pcpu *pc;
249e656fa70SConrad Meyer uint64_t epb;
2504577cf37SConrad Meyer uint32_t val;
2514577cf37SConrad Meyer int ret;
2524577cf37SConrad Meyer
2534577cf37SConrad Meyer dev = oidp->oid_arg1;
254b80d476cSConrad Meyer sc = device_get_softc(dev);
255556a1a0bSConrad Meyer if (!sc->hwp_pref_ctrl && !sc->hwp_perf_bias)
256b80d476cSConrad Meyer return (ENODEV);
257b80d476cSConrad Meyer
2584577cf37SConrad Meyer pc = cpu_get_pcpu(dev);
2594577cf37SConrad Meyer if (pc == NULL)
2604577cf37SConrad Meyer return (ENXIO);
2614577cf37SConrad Meyer
2624577cf37SConrad Meyer thread_lock(curthread);
2634577cf37SConrad Meyer sched_bind(curthread, pc->pc_cpuid);
2644577cf37SConrad Meyer thread_unlock(curthread);
2654577cf37SConrad Meyer
266556a1a0bSConrad Meyer if (sc->hwp_pref_ctrl) {
267e656fa70SConrad Meyer val = (sc->req & IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE) >> 24;
2684577cf37SConrad Meyer val = raw_to_percent(val);
269556a1a0bSConrad Meyer } else {
270556a1a0bSConrad Meyer /*
271556a1a0bSConrad Meyer * If cpuid indicates EPP is not supported, the HWP controller
272556a1a0bSConrad Meyer * uses MSR_IA32_ENERGY_PERF_BIAS instead (Intel SDM §14.4.4).
273556a1a0bSConrad Meyer * This register is per-core (but not HT).
274556a1a0bSConrad Meyer */
275e656fa70SConrad Meyer if (!sc->hwp_perf_bias_cached) {
276e656fa70SConrad Meyer ret = rdmsr_safe(MSR_IA32_ENERGY_PERF_BIAS, &epb);
277556a1a0bSConrad Meyer if (ret)
278556a1a0bSConrad Meyer goto out;
279e656fa70SConrad Meyer sc->hwp_energy_perf_bias = epb;
280e656fa70SConrad Meyer sc->hwp_perf_bias_cached = true;
281e656fa70SConrad Meyer }
282e656fa70SConrad Meyer val = sc->hwp_energy_perf_bias &
283e656fa70SConrad Meyer IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK;
284556a1a0bSConrad Meyer val = raw_to_percent_perf_bias(val);
285556a1a0bSConrad Meyer }
2864577cf37SConrad Meyer
2874577cf37SConrad Meyer MPASS(val >= 0 && val <= 100);
2884577cf37SConrad Meyer
2894577cf37SConrad Meyer ret = sysctl_handle_int(oidp, &val, 0, req);
2904577cf37SConrad Meyer if (ret || req->newptr == NULL)
2914577cf37SConrad Meyer goto out;
2924577cf37SConrad Meyer
2934577cf37SConrad Meyer if (val > 100) {
2944577cf37SConrad Meyer ret = EINVAL;
2954577cf37SConrad Meyer goto out;
2964577cf37SConrad Meyer }
2974577cf37SConrad Meyer
298556a1a0bSConrad Meyer if (sc->hwp_pref_ctrl) {
2994577cf37SConrad Meyer val = percent_to_raw(val);
3004577cf37SConrad Meyer
301e656fa70SConrad Meyer sc->req =
302e656fa70SConrad Meyer ((sc->req & ~IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE)
303e656fa70SConrad Meyer | (val << 24u));
3044577cf37SConrad Meyer
305cd4e43b2SConrad Meyer if (sc->hwp_pkg_ctrl_en)
306e656fa70SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req);
307cd4e43b2SConrad Meyer else
308e656fa70SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
309556a1a0bSConrad Meyer } else {
310e656fa70SConrad Meyer val = percent_to_raw_perf_bias(val);
311e656fa70SConrad Meyer MPASS((val & ~IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK) == 0);
312e656fa70SConrad Meyer
313e656fa70SConrad Meyer sc->hwp_energy_perf_bias =
314e656fa70SConrad Meyer ((sc->hwp_energy_perf_bias &
315e656fa70SConrad Meyer ~IA32_ENERGY_PERF_BIAS_POLICY_HINT_MASK) | val);
316e656fa70SConrad Meyer ret = wrmsr_safe(MSR_IA32_ENERGY_PERF_BIAS,
317e656fa70SConrad Meyer sc->hwp_energy_perf_bias);
318556a1a0bSConrad Meyer }
3194577cf37SConrad Meyer
3204577cf37SConrad Meyer out:
3214577cf37SConrad Meyer thread_lock(curthread);
3224577cf37SConrad Meyer sched_unbind(curthread);
3234577cf37SConrad Meyer thread_unlock(curthread);
3244577cf37SConrad Meyer
3254577cf37SConrad Meyer return (ret);
3264577cf37SConrad Meyer }
3274577cf37SConrad Meyer
3284577cf37SConrad Meyer void
intel_hwpstate_identify(driver_t * driver,device_t parent)3294577cf37SConrad Meyer intel_hwpstate_identify(driver_t *driver, device_t parent)
3304577cf37SConrad Meyer {
3314577cf37SConrad Meyer if (device_find_child(parent, "hwpstate_intel", -1) != NULL)
3324577cf37SConrad Meyer return;
3334577cf37SConrad Meyer
3344577cf37SConrad Meyer if (cpu_vendor_id != CPU_VENDOR_INTEL)
3354577cf37SConrad Meyer return;
3364577cf37SConrad Meyer
3374577cf37SConrad Meyer if (resource_disabled("hwpstate_intel", 0))
3384577cf37SConrad Meyer return;
3394577cf37SConrad Meyer
3404577cf37SConrad Meyer /*
3414577cf37SConrad Meyer * Intel SDM 14.4.1 (HWP Programming Interfaces):
3424577cf37SConrad Meyer * Availability of HWP baseline resource and capability,
3434577cf37SConrad Meyer * CPUID.06H:EAX[bit 7]: If this bit is set, HWP provides several new
3444577cf37SConrad Meyer * architectural MSRs: IA32_PM_ENABLE, IA32_HWP_CAPABILITIES,
3454577cf37SConrad Meyer * IA32_HWP_REQUEST, IA32_HWP_STATUS.
3464577cf37SConrad Meyer */
347f591c3c8SConrad Meyer if ((cpu_power_eax & CPUTPM1_HWP) == 0)
3484577cf37SConrad Meyer return;
3494577cf37SConrad Meyer
350d3a8f98aSAlexander Motin if (BUS_ADD_CHILD(parent, 10, "hwpstate_intel", device_get_unit(parent))
351d3a8f98aSAlexander Motin == NULL)
352d3a8f98aSAlexander Motin device_printf(parent, "hwpstate_intel: add child failed\n");
3534577cf37SConrad Meyer }
3544577cf37SConrad Meyer
3554577cf37SConrad Meyer static int
intel_hwpstate_probe(device_t dev)3564577cf37SConrad Meyer intel_hwpstate_probe(device_t dev)
3574577cf37SConrad Meyer {
3584577cf37SConrad Meyer
3594577cf37SConrad Meyer device_set_desc(dev, "Intel Speed Shift");
3604577cf37SConrad Meyer return (BUS_PROBE_NOWILDCARD);
3614577cf37SConrad Meyer }
3624577cf37SConrad Meyer
3634577cf37SConrad Meyer static int
set_autonomous_hwp(struct hwp_softc * sc)3644577cf37SConrad Meyer set_autonomous_hwp(struct hwp_softc *sc)
3654577cf37SConrad Meyer {
3664577cf37SConrad Meyer struct pcpu *pc;
3674577cf37SConrad Meyer device_t dev;
3684577cf37SConrad Meyer uint64_t caps;
3694577cf37SConrad Meyer int ret;
3704577cf37SConrad Meyer
3714577cf37SConrad Meyer dev = sc->dev;
3724577cf37SConrad Meyer
3734577cf37SConrad Meyer pc = cpu_get_pcpu(dev);
3744577cf37SConrad Meyer if (pc == NULL)
3754577cf37SConrad Meyer return (ENXIO);
3764577cf37SConrad Meyer
3774577cf37SConrad Meyer thread_lock(curthread);
3784577cf37SConrad Meyer sched_bind(curthread, pc->pc_cpuid);
3794577cf37SConrad Meyer thread_unlock(curthread);
3804577cf37SConrad Meyer
3814577cf37SConrad Meyer /* XXX: Many MSRs aren't readable until feature is enabled */
3824577cf37SConrad Meyer ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1);
3834577cf37SConrad Meyer if (ret) {
3845e3574c8SConrad Meyer /*
3855e3574c8SConrad Meyer * This is actually a package-level MSR, and only the first
3865e3574c8SConrad Meyer * write is not ignored. So it is harmless to enable it across
3875e3574c8SConrad Meyer * all devices, and this allows us not to care especially in
3885e3574c8SConrad Meyer * which order cores (and packages) are probed. This error
3895e3574c8SConrad Meyer * condition should not happen given we gate on the HWP CPUID
3905e3574c8SConrad Meyer * feature flag, if the Intel SDM is correct.
3915e3574c8SConrad Meyer */
3924577cf37SConrad Meyer device_printf(dev, "Failed to enable HWP for cpu%d (%d)\n",
3934577cf37SConrad Meyer pc->pc_cpuid, ret);
3944577cf37SConrad Meyer goto out;
3954577cf37SConrad Meyer }
3964577cf37SConrad Meyer
3974577cf37SConrad Meyer ret = rdmsr_safe(MSR_IA32_HWP_REQUEST, &sc->req);
398351896d3SConrad Meyer if (ret) {
399351896d3SConrad Meyer device_printf(dev,
400351896d3SConrad Meyer "Failed to read HWP request MSR for cpu%d (%d)\n",
401351896d3SConrad Meyer pc->pc_cpuid, ret);
402351896d3SConrad Meyer goto out;
403351896d3SConrad Meyer }
4044577cf37SConrad Meyer
4054577cf37SConrad Meyer ret = rdmsr_safe(MSR_IA32_HWP_CAPABILITIES, &caps);
406351896d3SConrad Meyer if (ret) {
407351896d3SConrad Meyer device_printf(dev,
408351896d3SConrad Meyer "Failed to read HWP capabilities MSR for cpu%d (%d)\n",
409351896d3SConrad Meyer pc->pc_cpuid, ret);
410351896d3SConrad Meyer goto out;
411351896d3SConrad Meyer }
4124577cf37SConrad Meyer
4135e3574c8SConrad Meyer /*
4145e3574c8SConrad Meyer * High and low are static; "guaranteed" is dynamic; and efficient is
4155e3574c8SConrad Meyer * also dynamic.
4165e3574c8SConrad Meyer */
4174577cf37SConrad Meyer sc->high = IA32_HWP_CAPABILITIES_HIGHEST_PERFORMANCE(caps);
4184577cf37SConrad Meyer sc->guaranteed = IA32_HWP_CAPABILITIES_GUARANTEED_PERFORMANCE(caps);
4194577cf37SConrad Meyer sc->efficient = IA32_HWP_CAPABILITIES_EFFICIENT_PERFORMANCE(caps);
4204577cf37SConrad Meyer sc->low = IA32_HWP_CAPABILITIES_LOWEST_PERFORMANCE(caps);
4214577cf37SConrad Meyer
4224577cf37SConrad Meyer /* hardware autonomous selection determines the performance target */
4234577cf37SConrad Meyer sc->req &= ~IA32_HWP_DESIRED_PERFORMANCE;
4244577cf37SConrad Meyer
4254577cf37SConrad Meyer /* enable HW dynamic selection of window size */
4264577cf37SConrad Meyer sc->req &= ~IA32_HWP_ACTIVITY_WINDOW;
4274577cf37SConrad Meyer
4284577cf37SConrad Meyer /* IA32_HWP_REQUEST.Minimum_Performance = IA32_HWP_CAPABILITIES.Lowest_Performance */
4294577cf37SConrad Meyer sc->req &= ~IA32_HWP_MINIMUM_PERFORMANCE;
4304577cf37SConrad Meyer sc->req |= sc->low;
4314577cf37SConrad Meyer
4324577cf37SConrad Meyer /* IA32_HWP_REQUEST.Maximum_Performance = IA32_HWP_CAPABILITIES.Highest_Performance. */
4334577cf37SConrad Meyer sc->req &= ~IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE;
4344577cf37SConrad Meyer sc->req |= sc->high << 8;
4354577cf37SConrad Meyer
436cd4e43b2SConrad Meyer /* If supported, request package-level control for this CPU. */
437cd4e43b2SConrad Meyer if (sc->hwp_pkg_ctrl_en)
438cd4e43b2SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req |
439cd4e43b2SConrad Meyer IA32_HWP_REQUEST_PACKAGE_CONTROL);
440cd4e43b2SConrad Meyer else
4414577cf37SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
4424577cf37SConrad Meyer if (ret) {
4434577cf37SConrad Meyer device_printf(dev,
444cd4e43b2SConrad Meyer "Failed to setup%s autonomous HWP for cpu%d\n",
445cd4e43b2SConrad Meyer sc->hwp_pkg_ctrl_en ? " PKG" : "", pc->pc_cpuid);
446cd4e43b2SConrad Meyer goto out;
447cd4e43b2SConrad Meyer }
448cd4e43b2SConrad Meyer
449cd4e43b2SConrad Meyer /* If supported, write the PKG-wide control MSR. */
450cd4e43b2SConrad Meyer if (sc->hwp_pkg_ctrl_en) {
451cd4e43b2SConrad Meyer /*
452cd4e43b2SConrad Meyer * "The structure of the IA32_HWP_REQUEST_PKG MSR
453cd4e43b2SConrad Meyer * (package-level) is identical to the IA32_HWP_REQUEST MSR
454cd4e43b2SConrad Meyer * with the exception of the Package Control field, which does
455cd4e43b2SConrad Meyer * not exist." (Intel SDM §14.4.4)
456cd4e43b2SConrad Meyer */
457cd4e43b2SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req);
45841062d68SYuri Pankov if (ret) {
459cd4e43b2SConrad Meyer device_printf(dev,
460cd4e43b2SConrad Meyer "Failed to set autonomous HWP for package\n");
4614577cf37SConrad Meyer }
46241062d68SYuri Pankov }
4634577cf37SConrad Meyer
4644577cf37SConrad Meyer out:
4654577cf37SConrad Meyer thread_lock(curthread);
4664577cf37SConrad Meyer sched_unbind(curthread);
4674577cf37SConrad Meyer thread_unlock(curthread);
4684577cf37SConrad Meyer
4694577cf37SConrad Meyer return (ret);
4704577cf37SConrad Meyer }
4714577cf37SConrad Meyer
4724577cf37SConrad Meyer static int
intel_hwpstate_attach(device_t dev)4734577cf37SConrad Meyer intel_hwpstate_attach(device_t dev)
4744577cf37SConrad Meyer {
4754577cf37SConrad Meyer struct hwp_softc *sc;
4764577cf37SConrad Meyer int ret;
4774577cf37SConrad Meyer
4784577cf37SConrad Meyer sc = device_get_softc(dev);
4794577cf37SConrad Meyer sc->dev = dev;
4804577cf37SConrad Meyer
481556a1a0bSConrad Meyer /* eax */
482f591c3c8SConrad Meyer if (cpu_power_eax & CPUTPM1_HWP_NOTIFICATION)
4834577cf37SConrad Meyer sc->hwp_notifications = true;
484f591c3c8SConrad Meyer if (cpu_power_eax & CPUTPM1_HWP_ACTIVITY_WINDOW)
4854577cf37SConrad Meyer sc->hwp_activity_window = true;
486f591c3c8SConrad Meyer if (cpu_power_eax & CPUTPM1_HWP_PERF_PREF)
4874577cf37SConrad Meyer sc->hwp_pref_ctrl = true;
488f591c3c8SConrad Meyer if (cpu_power_eax & CPUTPM1_HWP_PKG)
4894577cf37SConrad Meyer sc->hwp_pkg_ctrl = true;
4904577cf37SConrad Meyer
491cd4e43b2SConrad Meyer /* Allow administrators to disable pkg-level control. */
492cd4e43b2SConrad Meyer sc->hwp_pkg_ctrl_en = (sc->hwp_pkg_ctrl && hwpstate_pkg_ctrl_enable);
493cd4e43b2SConrad Meyer
494556a1a0bSConrad Meyer /* ecx */
495556a1a0bSConrad Meyer if (cpu_power_ecx & CPUID_PERF_BIAS)
496556a1a0bSConrad Meyer sc->hwp_perf_bias = true;
497556a1a0bSConrad Meyer
4984577cf37SConrad Meyer ret = set_autonomous_hwp(sc);
4994577cf37SConrad Meyer if (ret)
5004577cf37SConrad Meyer return (ret);
5014577cf37SConrad Meyer
5024577cf37SConrad Meyer SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
5034577cf37SConrad Meyer SYSCTL_STATIC_CHILDREN(_debug), OID_AUTO, device_get_nameunit(dev),
5041d6fb900SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE,
5054577cf37SConrad Meyer sc, 0, intel_hwp_dump_sysctl_handler, "A", "");
5064577cf37SConrad Meyer
5074577cf37SConrad Meyer SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
5084577cf37SConrad Meyer SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
5091d6fb900SAlexander Motin "epp", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, dev, 0,
5104577cf37SConrad Meyer sysctl_epp_select, "I",
5114577cf37SConrad Meyer "Efficiency/Performance Preference "
5124577cf37SConrad Meyer "(range from 0, most performant, through 100, most efficient)");
5134577cf37SConrad Meyer
5144577cf37SConrad Meyer return (cpufreq_register(dev));
5154577cf37SConrad Meyer }
5164577cf37SConrad Meyer
5174577cf37SConrad Meyer static int
intel_hwpstate_detach(device_t dev)5184577cf37SConrad Meyer intel_hwpstate_detach(device_t dev)
5194577cf37SConrad Meyer {
5204577cf37SConrad Meyer
5214577cf37SConrad Meyer return (cpufreq_unregister(dev));
5224577cf37SConrad Meyer }
5234577cf37SConrad Meyer
5244577cf37SConrad Meyer static int
intel_hwpstate_get(device_t dev,struct cf_setting * set)5254577cf37SConrad Meyer intel_hwpstate_get(device_t dev, struct cf_setting *set)
5264577cf37SConrad Meyer {
5274577cf37SConrad Meyer struct pcpu *pc;
5284577cf37SConrad Meyer uint64_t rate;
5294577cf37SConrad Meyer int ret;
5304577cf37SConrad Meyer
5314577cf37SConrad Meyer if (set == NULL)
5324577cf37SConrad Meyer return (EINVAL);
5334577cf37SConrad Meyer
5344577cf37SConrad Meyer pc = cpu_get_pcpu(dev);
5354577cf37SConrad Meyer if (pc == NULL)
5364577cf37SConrad Meyer return (ENXIO);
5374577cf37SConrad Meyer
5384577cf37SConrad Meyer memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
5394577cf37SConrad Meyer set->dev = dev;
5404577cf37SConrad Meyer
5414577cf37SConrad Meyer ret = cpu_est_clockrate(pc->pc_cpuid, &rate);
5424577cf37SConrad Meyer if (ret == 0)
5434577cf37SConrad Meyer set->freq = rate / 1000000;
5444577cf37SConrad Meyer
5454577cf37SConrad Meyer set->volts = CPUFREQ_VAL_UNKNOWN;
5464577cf37SConrad Meyer set->power = CPUFREQ_VAL_UNKNOWN;
5474577cf37SConrad Meyer set->lat = CPUFREQ_VAL_UNKNOWN;
5484577cf37SConrad Meyer
5494577cf37SConrad Meyer return (0);
5504577cf37SConrad Meyer }
5514577cf37SConrad Meyer
5524577cf37SConrad Meyer static int
intel_hwpstate_type(device_t dev,int * type)5534577cf37SConrad Meyer intel_hwpstate_type(device_t dev, int *type)
5544577cf37SConrad Meyer {
5554577cf37SConrad Meyer if (type == NULL)
5564577cf37SConrad Meyer return (EINVAL);
5574577cf37SConrad Meyer *type = CPUFREQ_TYPE_ABSOLUTE | CPUFREQ_FLAG_INFO_ONLY | CPUFREQ_FLAG_UNCACHED;
5584577cf37SConrad Meyer
5594577cf37SConrad Meyer return (0);
5604577cf37SConrad Meyer }
5614577cf37SConrad Meyer
5624577cf37SConrad Meyer static int
intel_hwpstate_suspend(device_t dev)5634577cf37SConrad Meyer intel_hwpstate_suspend(device_t dev)
5644577cf37SConrad Meyer {
5654577cf37SConrad Meyer return (0);
5664577cf37SConrad Meyer }
5674577cf37SConrad Meyer
5684577cf37SConrad Meyer /*
5694577cf37SConrad Meyer * Redo a subset of set_autonomous_hwp on resume; untested. Without this,
5704577cf37SConrad Meyer * testers observed that on resume MSR_IA32_HWP_REQUEST was bogus.
5714577cf37SConrad Meyer */
5724577cf37SConrad Meyer static int
intel_hwpstate_resume(device_t dev)5734577cf37SConrad Meyer intel_hwpstate_resume(device_t dev)
5744577cf37SConrad Meyer {
5754577cf37SConrad Meyer struct hwp_softc *sc;
5764577cf37SConrad Meyer struct pcpu *pc;
5774577cf37SConrad Meyer int ret;
5784577cf37SConrad Meyer
5794577cf37SConrad Meyer sc = device_get_softc(dev);
5804577cf37SConrad Meyer
5814577cf37SConrad Meyer pc = cpu_get_pcpu(dev);
5824577cf37SConrad Meyer if (pc == NULL)
5834577cf37SConrad Meyer return (ENXIO);
5844577cf37SConrad Meyer
5854577cf37SConrad Meyer thread_lock(curthread);
5864577cf37SConrad Meyer sched_bind(curthread, pc->pc_cpuid);
5874577cf37SConrad Meyer thread_unlock(curthread);
5884577cf37SConrad Meyer
5894577cf37SConrad Meyer ret = wrmsr_safe(MSR_IA32_PM_ENABLE, 1);
5904577cf37SConrad Meyer if (ret) {
5914577cf37SConrad Meyer device_printf(dev,
5924577cf37SConrad Meyer "Failed to enable HWP for cpu%d after suspend (%d)\n",
5934577cf37SConrad Meyer pc->pc_cpuid, ret);
5944577cf37SConrad Meyer goto out;
5954577cf37SConrad Meyer }
5964577cf37SConrad Meyer
597cd4e43b2SConrad Meyer if (sc->hwp_pkg_ctrl_en)
598cd4e43b2SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req |
599cd4e43b2SConrad Meyer IA32_HWP_REQUEST_PACKAGE_CONTROL);
600cd4e43b2SConrad Meyer else
6014577cf37SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST, sc->req);
6024577cf37SConrad Meyer if (ret) {
6034577cf37SConrad Meyer device_printf(dev,
604cd4e43b2SConrad Meyer "Failed to set%s autonomous HWP for cpu%d after suspend\n",
605cd4e43b2SConrad Meyer sc->hwp_pkg_ctrl_en ? " PKG" : "", pc->pc_cpuid);
606cd4e43b2SConrad Meyer goto out;
607cd4e43b2SConrad Meyer }
608cd4e43b2SConrad Meyer if (sc->hwp_pkg_ctrl_en) {
609cd4e43b2SConrad Meyer ret = wrmsr_safe(MSR_IA32_HWP_REQUEST_PKG, sc->req);
610e656fa70SConrad Meyer if (ret) {
611cd4e43b2SConrad Meyer device_printf(dev,
612c93eb470SConrad Meyer "Failed to set autonomous HWP for package after "
613c93eb470SConrad Meyer "suspend\n");
614e656fa70SConrad Meyer goto out;
615e656fa70SConrad Meyer }
616e656fa70SConrad Meyer }
617e656fa70SConrad Meyer if (!sc->hwp_pref_ctrl && sc->hwp_perf_bias_cached) {
618e656fa70SConrad Meyer ret = wrmsr_safe(MSR_IA32_ENERGY_PERF_BIAS,
619e656fa70SConrad Meyer sc->hwp_energy_perf_bias);
620e656fa70SConrad Meyer if (ret) {
621e656fa70SConrad Meyer device_printf(dev,
622e656fa70SConrad Meyer "Failed to set energy perf bias for cpu%d after "
623e656fa70SConrad Meyer "suspend\n", pc->pc_cpuid);
624e656fa70SConrad Meyer }
6254577cf37SConrad Meyer }
6264577cf37SConrad Meyer
6274577cf37SConrad Meyer out:
6284577cf37SConrad Meyer thread_lock(curthread);
6294577cf37SConrad Meyer sched_unbind(curthread);
6304577cf37SConrad Meyer thread_unlock(curthread);
6314577cf37SConrad Meyer
6324577cf37SConrad Meyer return (ret);
6334577cf37SConrad Meyer }
634