1 /* $NetBSD: acpi_cpu_tstate.c,v 1.20 2011/02/18 07:00:05 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Jukka Ruohonen <jruohonen@iki.fi> 5 * All rights reserved. 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 the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: acpi_cpu_tstate.c,v 1.20 2011/02/18 07:00:05 jruoho Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/evcnt.h> 34 #include <sys/kmem.h> 35 36 #include <dev/acpi/acpireg.h> 37 #include <dev/acpi/acpivar.h> 38 #include <dev/acpi/acpi_cpu.h> 39 40 #define _COMPONENT ACPI_BUS_COMPONENT 41 ACPI_MODULE_NAME ("acpi_cpu_tstate") 42 43 static void acpicpu_tstate_attach_print(struct acpicpu_softc *); 44 static void acpicpu_tstate_attach_evcnt(struct acpicpu_softc *); 45 static void acpicpu_tstate_detach_evcnt(struct acpicpu_softc *); 46 static ACPI_STATUS acpicpu_tstate_tss(struct acpicpu_softc *); 47 static ACPI_STATUS acpicpu_tstate_tss_add(struct acpicpu_tstate *, 48 ACPI_OBJECT *); 49 static ACPI_STATUS acpicpu_tstate_ptc(struct acpicpu_softc *); 50 static ACPI_STATUS acpicpu_tstate_fadt(struct acpicpu_softc *); 51 static ACPI_STATUS acpicpu_tstate_change(struct acpicpu_softc *); 52 static void acpicpu_tstate_reset(struct acpicpu_softc *); 53 54 void 55 acpicpu_tstate_attach(device_t self) 56 { 57 struct acpicpu_softc *sc = device_private(self); 58 const char *str; 59 ACPI_HANDLE tmp; 60 ACPI_STATUS rv; 61 62 /* 63 * Disable T-states for PIIX4. 64 */ 65 if ((sc->sc_flags & ACPICPU_FLAG_PIIX4) != 0) 66 return; 67 68 rv = acpicpu_tstate_tss(sc); 69 70 if (ACPI_FAILURE(rv)) { 71 str = "_TSS"; 72 goto out; 73 } 74 75 rv = acpicpu_tstate_ptc(sc); 76 77 if (ACPI_FAILURE(rv)) { 78 str = "_PTC"; 79 goto out; 80 } 81 82 /* 83 * Comparable to P-states, the _TPC object may 84 * be absent in some systems, even though it is 85 * required by ACPI 3.0 along with _TSS and _PTC. 86 */ 87 rv = AcpiGetHandle(sc->sc_node->ad_handle, "_TPC", &tmp); 88 89 if (ACPI_FAILURE(rv)) { 90 aprint_debug_dev(self, "_TPC missing\n"); 91 rv = AE_OK; 92 } 93 94 out: 95 if (ACPI_FAILURE(rv)) { 96 97 if (rv != AE_NOT_FOUND) 98 aprint_error_dev(sc->sc_dev, "failed to evaluate " 99 "%s: %s\n", str, AcpiFormatException(rv)); 100 101 rv = acpicpu_tstate_fadt(sc); 102 103 if (ACPI_FAILURE(rv)) 104 return; 105 106 sc->sc_flags |= ACPICPU_FLAG_T_FADT; 107 } 108 109 sc->sc_flags |= ACPICPU_FLAG_T; 110 111 acpicpu_tstate_reset(sc); 112 acpicpu_tstate_attach_evcnt(sc); 113 acpicpu_tstate_attach_print(sc); 114 } 115 116 static void 117 acpicpu_tstate_attach_print(struct acpicpu_softc *sc) 118 { 119 const uint8_t method = sc->sc_tstate_control.reg_spaceid; 120 struct acpicpu_tstate *ts; 121 static bool once = false; 122 const char *str; 123 uint32_t i; 124 125 if (once != false) 126 return; 127 128 str = (method != ACPI_ADR_SPACE_FIXED_HARDWARE) ? "I/O" : "FFH"; 129 130 for (i = 0; i < sc->sc_tstate_count; i++) { 131 132 ts = &sc->sc_tstate[i]; 133 134 if (ts->ts_percent == 0) 135 continue; 136 137 aprint_verbose_dev(sc->sc_dev, "T%u: %3s, " 138 "lat %3u us, pow %5u mW, %3u %%\n", i, str, 139 ts->ts_latency, ts->ts_power, ts->ts_percent); 140 } 141 142 once = true; 143 } 144 145 static void 146 acpicpu_tstate_attach_evcnt(struct acpicpu_softc *sc) 147 { 148 struct acpicpu_tstate *ts; 149 uint32_t i; 150 151 for (i = 0; i < sc->sc_tstate_count; i++) { 152 153 ts = &sc->sc_tstate[i]; 154 155 if (ts->ts_percent == 0) 156 continue; 157 158 (void)snprintf(ts->ts_name, sizeof(ts->ts_name), 159 "T%u (%u %%)", i, ts->ts_percent); 160 161 evcnt_attach_dynamic(&ts->ts_evcnt, EVCNT_TYPE_MISC, 162 NULL, device_xname(sc->sc_dev), ts->ts_name); 163 } 164 } 165 166 int 167 acpicpu_tstate_detach(device_t self) 168 { 169 struct acpicpu_softc *sc = device_private(self); 170 size_t size; 171 172 if ((sc->sc_flags & ACPICPU_FLAG_T) == 0) 173 return 0; 174 175 size = sc->sc_tstate_count * sizeof(*sc->sc_tstate); 176 177 if (sc->sc_tstate != NULL) 178 kmem_free(sc->sc_tstate, size); 179 180 sc->sc_flags &= ~ACPICPU_FLAG_T; 181 acpicpu_tstate_detach_evcnt(sc); 182 183 return 0; 184 } 185 186 static void 187 acpicpu_tstate_detach_evcnt(struct acpicpu_softc *sc) 188 { 189 struct acpicpu_tstate *ts; 190 uint32_t i; 191 192 for (i = 0; i < sc->sc_tstate_count; i++) { 193 194 ts = &sc->sc_tstate[i]; 195 196 if (ts->ts_percent != 0) 197 evcnt_detach(&ts->ts_evcnt); 198 } 199 } 200 201 void 202 acpicpu_tstate_start(device_t self) 203 { 204 /* Nothing. */ 205 } 206 207 bool 208 acpicpu_tstate_suspend(device_t self) 209 { 210 struct acpicpu_softc *sc = device_private(self); 211 212 mutex_enter(&sc->sc_mtx); 213 acpicpu_tstate_reset(sc); 214 mutex_exit(&sc->sc_mtx); 215 216 return true; 217 } 218 219 bool 220 acpicpu_tstate_resume(device_t self) 221 { 222 223 return true; 224 } 225 226 void 227 acpicpu_tstate_callback(void *aux) 228 { 229 struct acpicpu_softc *sc; 230 device_t self = aux; 231 uint32_t omax, omin; 232 int i; 233 234 sc = device_private(self); 235 236 if ((sc->sc_flags & ACPICPU_FLAG_T_FADT) != 0) 237 return; 238 239 mutex_enter(&sc->sc_mtx); 240 241 /* 242 * If P-states are in use, we should ignore 243 * the interrupt unless we are in the highest 244 * P-state (see ACPI 4.0, section 8.4.3.3). 245 */ 246 if ((sc->sc_flags & ACPICPU_FLAG_P) != 0) { 247 248 for (i = sc->sc_pstate_count - 1; i >= 0; i--) { 249 250 if (sc->sc_pstate[i].ps_freq != 0) 251 break; 252 } 253 254 if (sc->sc_pstate_current != sc->sc_pstate[i].ps_freq) { 255 mutex_exit(&sc->sc_mtx); 256 return; 257 } 258 } 259 260 omax = sc->sc_tstate_max; 261 omin = sc->sc_tstate_min; 262 263 (void)acpicpu_tstate_change(sc); 264 265 if (omax != sc->sc_tstate_max || omin != sc->sc_tstate_min) { 266 267 aprint_debug_dev(sc->sc_dev, "throttling window " 268 "changed from %u-%u %% to %u-%u %%\n", 269 sc->sc_tstate[omax].ts_percent, 270 sc->sc_tstate[omin].ts_percent, 271 sc->sc_tstate[sc->sc_tstate_max].ts_percent, 272 sc->sc_tstate[sc->sc_tstate_min].ts_percent); 273 } 274 275 mutex_exit(&sc->sc_mtx); 276 } 277 278 static ACPI_STATUS 279 acpicpu_tstate_tss(struct acpicpu_softc *sc) 280 { 281 struct acpicpu_tstate *ts; 282 ACPI_OBJECT *obj; 283 ACPI_BUFFER buf; 284 ACPI_STATUS rv; 285 uint32_t count; 286 uint32_t i, j; 287 288 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSS", &buf); 289 290 if (ACPI_FAILURE(rv)) 291 return rv; 292 293 obj = buf.Pointer; 294 295 if (obj->Type != ACPI_TYPE_PACKAGE) { 296 rv = AE_TYPE; 297 goto out; 298 } 299 300 sc->sc_tstate_count = obj->Package.Count; 301 302 if (sc->sc_tstate_count == 0) { 303 rv = AE_NOT_EXIST; 304 goto out; 305 } 306 307 if (sc->sc_tstate_count > ACPICPU_T_STATE_MAX) { 308 rv = AE_LIMIT; 309 goto out; 310 } 311 312 sc->sc_tstate = kmem_zalloc(sc->sc_tstate_count * 313 sizeof(struct acpicpu_tstate), KM_SLEEP); 314 315 if (sc->sc_tstate == NULL) { 316 rv = AE_NO_MEMORY; 317 goto out; 318 } 319 320 for (count = i = 0; i < sc->sc_tstate_count; i++) { 321 322 ts = &sc->sc_tstate[i]; 323 rv = acpicpu_tstate_tss_add(ts, &obj->Package.Elements[i]); 324 325 if (ACPI_FAILURE(rv)) { 326 ts->ts_percent = 0; 327 continue; 328 } 329 330 for (j = 0; j < i; j++) { 331 332 if (ts->ts_percent >= sc->sc_tstate[j].ts_percent) { 333 ts->ts_percent = 0; 334 break; 335 } 336 } 337 338 if (ts->ts_percent != 0) 339 count++; 340 } 341 342 if (count == 0) { 343 rv = AE_NOT_EXIST; 344 goto out; 345 } 346 347 /* 348 * There must be an entry with the percent 349 * field of 100. If this is not true, and if 350 * this entry is not in the expected index, 351 * invalidate the use of T-states via _TSS. 352 */ 353 if (sc->sc_tstate[0].ts_percent != 100) { 354 rv = AE_BAD_DECIMAL_CONSTANT; 355 goto out; 356 } 357 358 out: 359 if (buf.Pointer != NULL) 360 ACPI_FREE(buf.Pointer); 361 362 return rv; 363 } 364 365 static ACPI_STATUS 366 acpicpu_tstate_tss_add(struct acpicpu_tstate *ts, ACPI_OBJECT *obj) 367 { 368 ACPI_OBJECT *elm; 369 uint32_t val[5]; 370 uint32_t *p; 371 int i; 372 373 if (obj->Type != ACPI_TYPE_PACKAGE) 374 return AE_TYPE; 375 376 if (obj->Package.Count != 5) 377 return AE_BAD_DATA; 378 379 elm = obj->Package.Elements; 380 381 for (i = 0; i < 5; i++) { 382 383 if (elm[i].Type != ACPI_TYPE_INTEGER) 384 return AE_TYPE; 385 386 if (elm[i].Integer.Value > UINT32_MAX) 387 return AE_AML_NUMERIC_OVERFLOW; 388 389 val[i] = elm[i].Integer.Value; 390 } 391 392 p = &ts->ts_percent; 393 394 for (i = 0; i < 5; i++, p++) 395 *p = val[i]; 396 397 /* 398 * The minimum should be around 100 / 8 = 12.5 %. 399 */ 400 if (ts->ts_percent < 10 || ts->ts_percent > 100) 401 return AE_BAD_DECIMAL_CONSTANT; 402 403 if (ts->ts_latency < 1) 404 ts->ts_latency = 1; 405 406 return AE_OK; 407 } 408 409 ACPI_STATUS 410 acpicpu_tstate_ptc(struct acpicpu_softc *sc) 411 { 412 static const size_t size = sizeof(struct acpicpu_reg); 413 struct acpicpu_reg *reg[2]; 414 ACPI_OBJECT *elm, *obj; 415 ACPI_BUFFER buf; 416 ACPI_STATUS rv; 417 int i; 418 419 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PTC", &buf); 420 421 if (ACPI_FAILURE(rv)) 422 return rv; 423 424 obj = buf.Pointer; 425 426 if (obj->Type != ACPI_TYPE_PACKAGE) { 427 rv = AE_TYPE; 428 goto out; 429 } 430 431 if (obj->Package.Count != 2) { 432 rv = AE_LIMIT; 433 goto out; 434 } 435 436 for (i = 0; i < 2; i++) { 437 438 elm = &obj->Package.Elements[i]; 439 440 if (elm->Type != ACPI_TYPE_BUFFER) { 441 rv = AE_TYPE; 442 goto out; 443 } 444 445 if (size > elm->Buffer.Length) { 446 rv = AE_AML_BAD_RESOURCE_LENGTH; 447 goto out; 448 } 449 450 reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer; 451 452 switch (reg[i]->reg_spaceid) { 453 454 case ACPI_ADR_SPACE_SYSTEM_IO: 455 456 if (reg[i]->reg_addr == 0) { 457 rv = AE_AML_ILLEGAL_ADDRESS; 458 goto out; 459 } 460 461 /* 462 * Check that the values match the IA32 clock 463 * modulation MSR, where the bit 0 is reserved, 464 * bits 1 through 3 define the duty cycle, and 465 * the fourth bit enables the modulation. 466 */ 467 if (reg[i]->reg_bitwidth != 4) { 468 rv = AE_AML_BAD_RESOURCE_VALUE; 469 goto out; 470 } 471 472 if (reg[i]->reg_bitoffset != 1) { 473 rv = AE_AML_BAD_RESOURCE_VALUE; 474 goto out; 475 } 476 477 break; 478 479 case ACPI_ADR_SPACE_FIXED_HARDWARE: 480 481 if ((sc->sc_flags & ACPICPU_FLAG_T_FFH) == 0) { 482 rv = AE_SUPPORT; 483 goto out; 484 } 485 486 break; 487 488 default: 489 rv = AE_AML_INVALID_SPACE_ID; 490 goto out; 491 } 492 } 493 494 if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) { 495 rv = AE_AML_INVALID_SPACE_ID; 496 goto out; 497 } 498 499 (void)memcpy(&sc->sc_tstate_control, reg[0], size); 500 (void)memcpy(&sc->sc_tstate_status, reg[1], size); 501 502 out: 503 if (buf.Pointer != NULL) 504 ACPI_FREE(buf.Pointer); 505 506 return rv; 507 } 508 509 static ACPI_STATUS 510 acpicpu_tstate_fadt(struct acpicpu_softc *sc) 511 { 512 static const size_t size = sizeof(struct acpicpu_tstate); 513 const uint8_t offset = AcpiGbl_FADT.DutyOffset; 514 const uint8_t width = AcpiGbl_FADT.DutyWidth; 515 uint8_t beta, count, i; 516 517 if (sc->sc_object.ao_pblkaddr == 0) 518 return AE_AML_ILLEGAL_ADDRESS; 519 520 /* 521 * A zero DUTY_WIDTH is used announce that 522 * T-states are not available via FADT. 523 */ 524 if (width == 0 || width + offset > 4) 525 return AE_AML_BAD_RESOURCE_VALUE; 526 527 count = 1 << width; 528 529 if (count > ACPICPU_T_STATE_MAX) 530 return AE_LIMIT; 531 532 if (sc->sc_tstate != NULL) 533 kmem_free(sc->sc_tstate, sc->sc_tstate_count * size); 534 535 sc->sc_tstate = kmem_zalloc(count * size, KM_SLEEP); 536 537 if (sc->sc_tstate == NULL) 538 return ENOMEM; 539 540 sc->sc_tstate_count = count; 541 542 /* 543 * Approximate duty cycles and set the MSR values. 544 */ 545 for (beta = 100 / count, i = 0; i < count; i++) { 546 sc->sc_tstate[i].ts_percent = 100 - beta * i; 547 sc->sc_tstate[i].ts_latency = 1; 548 } 549 550 for (i = 1; i < count; i++) 551 sc->sc_tstate[i].ts_control = (count - i) | __BIT(3); 552 553 /* 554 * Fake values for throttling registers. 555 */ 556 (void)memset(&sc->sc_tstate_status, 0, sizeof(struct acpicpu_reg)); 557 (void)memset(&sc->sc_tstate_control, 0, sizeof(struct acpicpu_reg)); 558 559 sc->sc_tstate_status.reg_bitwidth = width; 560 sc->sc_tstate_status.reg_bitoffset = offset; 561 sc->sc_tstate_status.reg_addr = sc->sc_object.ao_pblkaddr; 562 sc->sc_tstate_status.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; 563 564 sc->sc_tstate_control.reg_bitwidth = width; 565 sc->sc_tstate_control.reg_bitoffset = offset; 566 sc->sc_tstate_control.reg_addr = sc->sc_object.ao_pblkaddr; 567 sc->sc_tstate_control.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; 568 569 return AE_OK; 570 } 571 572 static ACPI_STATUS 573 acpicpu_tstate_change(struct acpicpu_softc *sc) 574 { 575 ACPI_INTEGER val; 576 ACPI_STATUS rv; 577 578 acpicpu_tstate_reset(sc); 579 580 /* 581 * Evaluate the available T-state window: 582 * 583 * _TPC : either this maximum or any lower power 584 * (i.e. higher numbered) state may be used. 585 * 586 * _TDL : either this minimum or any higher power 587 * (i.e. lower numbered) state may be used. 588 * 589 * _TDL >= _TPC || _TDL >= _TSS[last entry]. 590 */ 591 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TPC", &val); 592 593 if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { 594 595 if (sc->sc_tstate[val].ts_percent != 0) 596 sc->sc_tstate_max = val; 597 } 598 599 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TDL", &val); 600 601 if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { 602 603 if (val >= sc->sc_tstate_max && 604 sc->sc_tstate[val].ts_percent != 0) 605 sc->sc_tstate_min = val; 606 } 607 608 return AE_OK; 609 } 610 611 static void 612 acpicpu_tstate_reset(struct acpicpu_softc *sc) 613 { 614 615 sc->sc_tstate_max = 0; 616 sc->sc_tstate_min = sc->sc_tstate_count - 1; 617 } 618 619 int 620 acpicpu_tstate_get(struct acpicpu_softc *sc, uint32_t *percent) 621 { 622 const uint8_t method = sc->sc_tstate_control.reg_spaceid; 623 struct acpicpu_tstate *ts = NULL; 624 uint32_t i, val = 0; 625 uint8_t offset; 626 uint64_t addr; 627 int rv; 628 629 if (__predict_false(sc->sc_cold != false)) { 630 rv = EBUSY; 631 goto fail; 632 } 633 634 if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) { 635 rv = ENODEV; 636 goto fail; 637 } 638 639 mutex_enter(&sc->sc_mtx); 640 641 if (sc->sc_tstate_current != ACPICPU_T_STATE_UNKNOWN) { 642 *percent = sc->sc_tstate_current; 643 mutex_exit(&sc->sc_mtx); 644 return 0; 645 } 646 647 mutex_exit(&sc->sc_mtx); 648 649 switch (method) { 650 651 case ACPI_ADR_SPACE_FIXED_HARDWARE: 652 653 rv = acpicpu_md_tstate_get(sc, percent); 654 655 if (__predict_false(rv != 0)) 656 goto fail; 657 658 break; 659 660 case ACPI_ADR_SPACE_SYSTEM_IO: 661 662 addr = sc->sc_tstate_status.reg_addr; 663 offset = sc->sc_tstate_status.reg_bitoffset; 664 665 (void)AcpiOsReadPort(addr, &val, 8); 666 667 val = (val >> offset) & 0x0F; 668 669 for (i = 0; i < sc->sc_tstate_count; i++) { 670 671 if (sc->sc_tstate[i].ts_percent == 0) 672 continue; 673 674 if (val == sc->sc_tstate[i].ts_status) { 675 ts = &sc->sc_tstate[i]; 676 break; 677 } 678 } 679 680 if (ts == NULL) { 681 rv = EIO; 682 goto fail; 683 } 684 685 *percent = ts->ts_percent; 686 break; 687 688 default: 689 rv = ENOTTY; 690 goto fail; 691 } 692 693 mutex_enter(&sc->sc_mtx); 694 sc->sc_tstate_current = *percent; 695 mutex_exit(&sc->sc_mtx); 696 697 return 0; 698 699 fail: 700 aprint_error_dev(sc->sc_dev, "failed " 701 "to get T-state (err %d)\n", rv); 702 703 mutex_enter(&sc->sc_mtx); 704 *percent = sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; 705 mutex_exit(&sc->sc_mtx); 706 707 return rv; 708 } 709 710 int 711 acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent) 712 { 713 const uint8_t method = sc->sc_tstate_control.reg_spaceid; 714 struct acpicpu_tstate *ts = NULL; 715 uint32_t i, val; 716 uint8_t offset; 717 uint64_t addr; 718 int rv; 719 720 if (__predict_false(sc->sc_cold != false)) { 721 rv = EBUSY; 722 goto fail; 723 } 724 725 if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) { 726 rv = ENODEV; 727 goto fail; 728 } 729 730 mutex_enter(&sc->sc_mtx); 731 732 if (sc->sc_tstate_current == percent) { 733 mutex_exit(&sc->sc_mtx); 734 return 0; 735 } 736 737 for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) { 738 739 if (__predict_false(sc->sc_tstate[i].ts_percent == 0)) 740 continue; 741 742 if (sc->sc_tstate[i].ts_percent == percent) { 743 ts = &sc->sc_tstate[i]; 744 break; 745 } 746 } 747 748 mutex_exit(&sc->sc_mtx); 749 750 if (__predict_false(ts == NULL)) { 751 rv = EINVAL; 752 goto fail; 753 } 754 755 switch (method) { 756 757 case ACPI_ADR_SPACE_FIXED_HARDWARE: 758 759 rv = acpicpu_md_tstate_set(ts); 760 761 if (__predict_false(rv != 0)) 762 goto fail; 763 764 break; 765 766 case ACPI_ADR_SPACE_SYSTEM_IO: 767 768 addr = sc->sc_tstate_control.reg_addr; 769 offset = sc->sc_tstate_control.reg_bitoffset; 770 771 val = (ts->ts_control & 0x0F) << offset; 772 773 if (ts->ts_percent != 100 && (val & __BIT(4)) == 0) { 774 rv = EINVAL; 775 goto fail; 776 } 777 778 (void)AcpiOsWritePort(addr, val, 8); 779 780 /* 781 * If the status field is zero, the transition is 782 * specified to be "asynchronous" and there is no 783 * need to check the status (ACPI 4.0, 8.4.3.2). 784 */ 785 if (ts->ts_status == 0) 786 break; 787 788 addr = sc->sc_tstate_status.reg_addr; 789 offset = sc->sc_tstate_status.reg_bitoffset; 790 791 for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) { 792 793 (void)AcpiOsReadPort(addr, &val, 8); 794 795 val = (val >> offset) & 0x0F; 796 797 if (val == ts->ts_status) 798 break; 799 800 DELAY(ts->ts_latency); 801 } 802 803 if (i == ACPICPU_T_STATE_RETRY) { 804 rv = EAGAIN; 805 goto fail; 806 } 807 808 break; 809 810 default: 811 rv = ENOTTY; 812 goto fail; 813 } 814 815 mutex_enter(&sc->sc_mtx); 816 ts->ts_evcnt.ev_count++; 817 sc->sc_tstate_current = percent; 818 mutex_exit(&sc->sc_mtx); 819 820 return 0; 821 822 fail: 823 aprint_error_dev(sc->sc_dev, "failed to " 824 "throttle to %u %% (err %d)\n", percent, rv); 825 826 mutex_enter(&sc->sc_mtx); 827 sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; 828 mutex_exit(&sc->sc_mtx); 829 830 return rv; 831 } 832