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