1 /* 2 * Copyright (c) 2014 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sepherosa Ziehau <sepherosa@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/conf.h> 37 #include <sys/kernel.h> 38 #include <sys/bus.h> 39 #include <sys/cpu_topology.h> 40 #include <sys/cpuhelper.h> 41 #include <sys/module.h> 42 #include <sys/queue.h> 43 #include <sys/serialize.h> 44 #include <sys/sysctl.h> 45 #include <sys/systm.h> 46 47 #include <machine/specialreg.h> 48 #include <machine/cpufunc.h> 49 #include <machine/cputypes.h> 50 #include <machine/md_var.h> 51 52 struct clockmod_dom; 53 54 struct clockmod_softc { 55 TAILQ_ENTRY(clockmod_softc) sc_link; 56 struct clockmod_dom *sc_dom; 57 int sc_cpuid; 58 }; 59 60 struct clockmod_dom { 61 TAILQ_ENTRY(clockmod_dom) dom_link; 62 TAILQ_HEAD(, clockmod_softc) dom_list; 63 struct sysctl_ctx_list dom_sysctl_ctx; 64 struct sysctl_oid *dom_sysctl_tree; 65 cpumask_t dom_cpumask; 66 char dom_name[16]; 67 int dom_select; 68 uint32_t dom_flags; 69 }; 70 71 #define CLOCKMOD_DOM_FLAG_ACTIVE 0x1 72 73 struct clockmod_dom_ctrl { 74 char ctl_name[8]; 75 uint64_t ctl_value; 76 }; 77 78 static int clockmod_dom_attach(struct clockmod_softc *); 79 static void clockmod_dom_detach(struct clockmod_softc *); 80 static struct clockmod_dom *clockmod_dom_find(cpumask_t); 81 static struct clockmod_dom *clockmod_dom_create(cpumask_t); 82 static void clockmod_dom_destroy(struct clockmod_dom *); 83 84 static int clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS); 85 static int clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS); 86 static int clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS); 87 88 static void clockmod_identify(driver_t *, device_t); 89 static int clockmod_probe(device_t); 90 static int clockmod_attach(device_t); 91 static int clockmod_detach(device_t); 92 93 static void clockmod_select_handler(struct cpuhelper_msg *); 94 static int clockmod_select(const struct clockmod_softc *, 95 const struct clockmod_dom_ctrl *); 96 97 static boolean_t clockmod_errata_duty(int); 98 99 static struct lwkt_serialize clockmod_dom_slize = LWKT_SERIALIZE_INITIALIZER; 100 static int clockmod_dom_id; 101 static TAILQ_HEAD(, clockmod_dom) clockmod_dom_list = 102 TAILQ_HEAD_INITIALIZER(clockmod_dom_list); 103 static int clockmod_dom_nctrl; 104 static struct clockmod_dom_ctrl *clockmod_dom_controls; 105 106 static device_method_t clockmod_methods[] = { 107 /* Device interface */ 108 DEVMETHOD(device_identify, clockmod_identify), 109 DEVMETHOD(device_probe, clockmod_probe), 110 DEVMETHOD(device_attach, clockmod_attach), 111 DEVMETHOD(device_detach, clockmod_detach), 112 113 DEVMETHOD_END 114 }; 115 116 static driver_t clockmod_driver = { 117 "clockmod", 118 clockmod_methods, 119 sizeof(struct clockmod_softc), 120 }; 121 122 static devclass_t clockmod_devclass; 123 DRIVER_MODULE(clockmod, cpu, clockmod_driver, clockmod_devclass, NULL, NULL); 124 125 static void 126 clockmod_identify(driver_t *driver, device_t parent) 127 { 128 device_t child; 129 130 if (device_find_child(parent, "clockmod", -1) != NULL) 131 return; 132 133 if (cpu_vendor_id != CPU_VENDOR_INTEL) 134 return; 135 136 if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM)) 137 return; 138 139 child = device_add_child(parent, "clockmod", device_get_unit(parent)); 140 if (child == NULL) 141 device_printf(parent, "add clockmod failed\n"); 142 } 143 144 static int 145 clockmod_probe(device_t dev) 146 { 147 device_set_desc(dev, "CPU clock modulation"); 148 return 0; 149 } 150 151 static int 152 clockmod_attach(device_t dev) 153 { 154 struct clockmod_softc *sc = device_get_softc(dev); 155 int error; 156 157 sc->sc_cpuid = device_get_unit(dev); 158 159 error = clockmod_dom_attach(sc); 160 if (error) { 161 device_printf(dev, "domain attach failed\n"); 162 return error; 163 } 164 165 return 0; 166 } 167 168 static int 169 clockmod_detach(device_t dev) 170 { 171 clockmod_dom_detach(device_get_softc(dev)); 172 return 0; 173 } 174 175 static int 176 clockmod_dom_attach(struct clockmod_softc *sc) 177 { 178 struct clockmod_softc *sc1; 179 struct clockmod_dom *dom; 180 cpumask_t mask, found_mask; 181 int error = 0; 182 183 CPUMASK_ASSZERO(found_mask); 184 185 mask = get_cpumask_from_level(sc->sc_cpuid, CORE_LEVEL); 186 if (CPUMASK_TESTZERO(mask)) 187 CPUMASK_ASSBIT(mask, sc->sc_cpuid); 188 189 lwkt_serialize_enter(&clockmod_dom_slize); 190 191 dom = clockmod_dom_find(mask); 192 if (dom == NULL) { 193 dom = clockmod_dom_create(mask); 194 if (dom == NULL) { 195 error = ENOMEM; 196 goto back; 197 } 198 } 199 200 sc->sc_dom = dom; 201 TAILQ_INSERT_TAIL(&dom->dom_list, sc, sc_link); 202 203 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link) 204 CPUMASK_ORBIT(found_mask, sc1->sc_cpuid); 205 206 if (CPUMASK_CMPMASKEQ(found_mask, dom->dom_cpumask)) { 207 /* All cpus in this domain is found */ 208 dom->dom_flags |= CLOCKMOD_DOM_FLAG_ACTIVE; 209 } 210 back: 211 lwkt_serialize_exit(&clockmod_dom_slize); 212 return error; 213 } 214 215 static void 216 clockmod_dom_detach(struct clockmod_softc *sc) 217 { 218 struct clockmod_dom *dom; 219 220 lwkt_serialize_enter(&clockmod_dom_slize); 221 222 dom = sc->sc_dom; 223 sc->sc_dom = NULL; 224 225 if (dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) { 226 struct clockmod_softc *sc1; 227 228 /* Raise to 100% */ 229 TAILQ_FOREACH(sc1, &dom->dom_list, sc_link) 230 clockmod_select(sc1, &clockmod_dom_controls[0]); 231 } 232 233 /* One cpu is leaving; domain is no longer active */ 234 dom->dom_flags &= ~CLOCKMOD_DOM_FLAG_ACTIVE; 235 236 TAILQ_REMOVE(&dom->dom_list, sc, sc_link); 237 if (TAILQ_EMPTY(&dom->dom_list)) 238 clockmod_dom_destroy(dom); 239 240 lwkt_serialize_exit(&clockmod_dom_slize); 241 } 242 243 static struct clockmod_dom * 244 clockmod_dom_find(cpumask_t mask) 245 { 246 struct clockmod_dom *dom; 247 248 TAILQ_FOREACH(dom, &clockmod_dom_list, dom_link) { 249 if (CPUMASK_CMPMASKEQ(dom->dom_cpumask, mask)) 250 return dom; 251 } 252 return NULL; 253 } 254 255 static struct clockmod_dom * 256 clockmod_dom_create(cpumask_t mask) 257 { 258 struct clockmod_dom *dom; 259 int id; 260 261 id = clockmod_dom_id++; 262 dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO); 263 264 TAILQ_INIT(&dom->dom_list); 265 dom->dom_cpumask = mask; 266 ksnprintf(dom->dom_name, sizeof(dom->dom_name), "clockmod_dom%d", id); 267 268 sysctl_ctx_init(&dom->dom_sysctl_ctx); 269 dom->dom_sysctl_tree = SYSCTL_ADD_NODE(&dom->dom_sysctl_ctx, 270 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, dom->dom_name, 271 CTLFLAG_RD, 0, ""); 272 if (dom->dom_sysctl_tree == NULL) { 273 kprintf("%s: can't add sysctl node\n", dom->dom_name); 274 kfree(dom, M_DEVBUF); 275 return NULL; 276 } 277 278 SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx, 279 SYSCTL_CHILDREN(dom->dom_sysctl_tree), 280 OID_AUTO, "members", CTLTYPE_STRING | CTLFLAG_RD, 281 dom, 0, clockmod_dom_sysctl_members, "A", "member cpus"); 282 283 SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx, 284 SYSCTL_CHILDREN(dom->dom_sysctl_tree), 285 OID_AUTO, "available", CTLTYPE_STRING | CTLFLAG_RD, 286 dom, 0, clockmod_dom_sysctl_available, "A", 287 "available duty percent"); 288 289 SYSCTL_ADD_PROC(&dom->dom_sysctl_ctx, 290 SYSCTL_CHILDREN(dom->dom_sysctl_tree), 291 OID_AUTO, "select", CTLTYPE_STRING | CTLFLAG_RW, 292 dom, 0, clockmod_dom_sysctl_select, "A", "select duty"); 293 294 TAILQ_INSERT_TAIL(&clockmod_dom_list, dom, dom_link); 295 296 if (clockmod_dom_controls == NULL) { 297 int nctrl, step, i, shift, cnt; 298 299 #ifdef __x86_64__ 300 if (cpu_thermal_feature & CPUID_THERMAL_ECMD) 301 shift = 0; 302 else 303 #endif 304 shift = 1; 305 306 nctrl = 8 << (1 - shift); 307 step = 10000 / nctrl; 308 309 clockmod_dom_controls = 310 kmalloc(sizeof(struct clockmod_dom_ctrl) * nctrl, M_DEVBUF, 311 M_WAITOK | M_ZERO); 312 313 if (bootverbose) 314 kprintf("clock modulation:\n"); 315 316 cnt = 0; 317 for (i = 0; i < nctrl; ++i) { 318 struct clockmod_dom_ctrl *ctrl = 319 &clockmod_dom_controls[cnt]; 320 int duty; 321 322 duty = 10000 - (i * step); 323 if (clockmod_errata_duty(duty)) 324 continue; 325 ++cnt; 326 327 ksnprintf(ctrl->ctl_name, sizeof(ctrl->ctl_name), 328 "%d.%02d%%", duty / 100, duty % 100); 329 ctrl->ctl_value = (((nctrl - i) << shift) & 0xf); 330 if (i != 0) 331 ctrl->ctl_value |= 1 << 4; 332 333 if (bootverbose) { 334 kprintf(" 0x%04jx %s\n", 335 (uintmax_t)ctrl->ctl_value, 336 ctrl->ctl_name); 337 } 338 } 339 clockmod_dom_nctrl = cnt; 340 } 341 return dom; 342 } 343 344 static void 345 clockmod_dom_destroy(struct clockmod_dom *dom) 346 { 347 KASSERT(TAILQ_EMPTY(&dom->dom_list), 348 ("%s: still has member cpus", dom->dom_name)); 349 TAILQ_REMOVE(&clockmod_dom_list, dom, dom_link); 350 351 sysctl_ctx_free(&dom->dom_sysctl_ctx); 352 kfree(dom, M_DEVBUF); 353 354 if (TAILQ_EMPTY(&clockmod_dom_list)) { 355 clockmod_dom_nctrl = 0; 356 kfree(clockmod_dom_controls, M_DEVBUF); 357 clockmod_dom_controls = NULL; 358 } 359 } 360 361 static int 362 clockmod_dom_sysctl_members(SYSCTL_HANDLER_ARGS) 363 { 364 struct clockmod_dom *dom = arg1; 365 struct clockmod_softc *sc; 366 int loop, error; 367 368 lwkt_serialize_enter(&clockmod_dom_slize); 369 370 loop = error = 0; 371 TAILQ_FOREACH(sc, &dom->dom_list, sc_link) { 372 char buf[16]; 373 374 if (error == 0 && loop) 375 error = SYSCTL_OUT(req, " ", 1); 376 if (error == 0) { 377 ksnprintf(buf, sizeof(buf), "cpu%d", sc->sc_cpuid); 378 error = SYSCTL_OUT(req, buf, strlen(buf)); 379 } 380 ++loop; 381 } 382 383 lwkt_serialize_exit(&clockmod_dom_slize); 384 return error; 385 } 386 387 static int 388 clockmod_dom_sysctl_available(SYSCTL_HANDLER_ARGS) 389 { 390 struct clockmod_dom *dom = arg1; 391 int loop, error, i; 392 393 lwkt_serialize_enter(&clockmod_dom_slize); 394 395 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) { 396 error = SYSCTL_OUT(req, " ", 1); 397 goto done; 398 } 399 400 loop = error = 0; 401 for (i = 0; i < clockmod_dom_nctrl; ++i) { 402 if (error == 0 && loop) 403 error = SYSCTL_OUT(req, " ", 1); 404 if (error == 0) { 405 error = SYSCTL_OUT(req, 406 clockmod_dom_controls[i].ctl_name, 407 strlen(clockmod_dom_controls[i].ctl_name)); 408 } 409 ++loop; 410 } 411 done: 412 lwkt_serialize_exit(&clockmod_dom_slize); 413 return error; 414 } 415 416 static int 417 clockmod_dom_sysctl_select(SYSCTL_HANDLER_ARGS) 418 { 419 struct clockmod_dom *dom = arg1; 420 struct clockmod_softc *sc; 421 const struct clockmod_dom_ctrl *ctrl = NULL; 422 char duty[16]; 423 int error, i; 424 425 lwkt_serialize_enter(&clockmod_dom_slize); 426 KKASSERT(dom->dom_select >= 0 && dom->dom_select < clockmod_dom_nctrl); 427 ksnprintf(duty, sizeof(duty), "%s", 428 clockmod_dom_controls[dom->dom_select].ctl_name); 429 lwkt_serialize_exit(&clockmod_dom_slize); 430 431 error = sysctl_handle_string(oidp, duty, sizeof(duty), req); 432 if (error != 0 || req->newptr == NULL) 433 return error; 434 435 lwkt_serialize_enter(&clockmod_dom_slize); 436 437 if ((dom->dom_flags & CLOCKMOD_DOM_FLAG_ACTIVE) == 0) { 438 error = EOPNOTSUPP; 439 goto back; 440 } 441 442 for (i = 0; i < clockmod_dom_nctrl; ++i) { 443 ctrl = &clockmod_dom_controls[i]; 444 if (strcmp(duty, ctrl->ctl_name) == 0) 445 break; 446 } 447 if (i == clockmod_dom_nctrl) { 448 error = EINVAL; 449 goto back; 450 } 451 dom->dom_select = i; 452 453 TAILQ_FOREACH(sc, &dom->dom_list, sc_link) 454 clockmod_select(sc, ctrl); 455 back: 456 lwkt_serialize_exit(&clockmod_dom_slize); 457 return error; 458 } 459 460 static void 461 clockmod_select_handler(struct cpuhelper_msg *msg) 462 { 463 uint64_t ctl_value = *((const uint64_t *)msg->ch_cbarg); 464 465 #if 0 466 if (bootverbose) { 467 kprintf("cpu%d: clockmod 0x%04jx\n", mycpuid, 468 (uintmax_t)ctl_value); 469 } 470 #endif 471 wrmsr(MSR_THERM_CONTROL, ctl_value); 472 cpuhelper_replymsg(msg, 0); 473 } 474 475 static int 476 clockmod_select(const struct clockmod_softc *sc, 477 const struct clockmod_dom_ctrl *ctrl) 478 { 479 struct cpuhelper_msg msg; 480 481 cpuhelper_initmsg(&msg, &curthread->td_msgport, 482 clockmod_select_handler, __DECONST(void *, &ctrl->ctl_value), 483 MSGF_PRIORITY); 484 return (cpuhelper_domsg(&msg, sc->sc_cpuid)); 485 } 486 487 static boolean_t 488 clockmod_errata_duty(int duty) 489 { 490 uint32_t model, stepping; 491 492 /* 493 * This is obtained from the original p4tcc code. 494 * 495 * The original errata checking code in p4tcc is obviously wrong. 496 * However, I am no longer being able to find the errata mentioned 497 * in the code. The guess is that the errata only affects family 498 * 0x0f CPUs, since: 499 * - The errata applies to only to model 0x00, 0x01 and 0x02 in 500 * the original p4tcc code. 501 * - Software controlled clock modulation has been supported since 502 * 0f_00 and the model of the oldest family 0x06 CPUs supporting 503 * this feature is 0x09. 504 */ 505 if (CPUID_TO_FAMILY(cpu_id) != 0xf) 506 return FALSE; 507 508 model = CPUID_TO_MODEL(cpu_id); 509 stepping = cpu_id & 0xf; 510 511 if (model == 0x6) { 512 switch (stepping) { 513 case 0x2: 514 case 0x4: 515 case 0x5: 516 /* Hang w/ 12.50% and 25.00% */ 517 if (duty == 1250 || duty == 2500) 518 return TRUE; 519 break; 520 } 521 } else if (model == 0x2) { 522 switch (stepping) { 523 case 0x2: 524 case 0x4: 525 case 0x5: 526 case 0x7: 527 case 0x9: 528 /* Hang w/ 12.50% */ 529 if (duty == 1250) 530 return TRUE; 531 break; 532 } 533 } else if (model == 0x1) { 534 switch (stepping) { 535 case 0x2: 536 case 0x3: 537 /* Hang w/ 12.50% and 25.00% */ 538 if (duty == 1250 || duty == 2500) 539 return TRUE; 540 break; 541 } 542 } else if (model == 0x0) { 543 switch (stepping) { 544 case 0x7: 545 case 0xa: 546 /* Hang w/ 12.50% and 25.00% */ 547 if (duty == 1250 || duty == 2500) 548 return TRUE; 549 break; 550 } 551 } 552 return FALSE; 553 } 554