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