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