1 /* $NetBSD: acpi_cpu_tstate.c,v 1.16 2010/08/21 18:25:45 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.16 2010/08/21 18:25:45 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_debug_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 /* 359 * The first entry with 100 % duty cycle 360 * should have zero in the control field. 361 */ 362 if (sc->sc_tstate[0].ts_control != 0) { 363 rv = AE_AML_BAD_RESOURCE_VALUE; 364 goto out; 365 } 366 367 out: 368 if (buf.Pointer != NULL) 369 ACPI_FREE(buf.Pointer); 370 371 return rv; 372 } 373 374 static ACPI_STATUS 375 acpicpu_tstate_tss_add(struct acpicpu_tstate *ts, ACPI_OBJECT *obj) 376 { 377 ACPI_OBJECT *elm; 378 uint32_t val[5]; 379 uint32_t *p; 380 int i; 381 382 if (obj->Type != ACPI_TYPE_PACKAGE) 383 return AE_TYPE; 384 385 if (obj->Package.Count != 5) 386 return AE_BAD_DATA; 387 388 elm = obj->Package.Elements; 389 390 for (i = 0; i < 5; i++) { 391 392 if (elm[i].Type != ACPI_TYPE_INTEGER) 393 return AE_TYPE; 394 395 if (elm[i].Integer.Value > UINT32_MAX) 396 return AE_AML_NUMERIC_OVERFLOW; 397 398 val[i] = elm[i].Integer.Value; 399 } 400 401 p = &ts->ts_percent; 402 403 for (i = 0; i < 5; i++, p++) 404 *p = val[i]; 405 406 /* 407 * The minimum should be around 100 / 8 = 12.5 %. 408 */ 409 if (ts->ts_percent < 10 || ts->ts_percent > 100) 410 return AE_BAD_DECIMAL_CONSTANT; 411 412 if (ts->ts_latency < 1) 413 ts->ts_latency = 1; 414 415 return AE_OK; 416 } 417 418 ACPI_STATUS 419 acpicpu_tstate_ptc(struct acpicpu_softc *sc) 420 { 421 static const size_t size = sizeof(struct acpicpu_reg); 422 struct acpicpu_reg *reg[2]; 423 ACPI_OBJECT *elm, *obj; 424 ACPI_BUFFER buf; 425 ACPI_STATUS rv; 426 int i; 427 428 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PTC", &buf); 429 430 if (ACPI_FAILURE(rv)) 431 return rv; 432 433 obj = buf.Pointer; 434 435 if (obj->Type != ACPI_TYPE_PACKAGE) { 436 rv = AE_TYPE; 437 goto out; 438 } 439 440 if (obj->Package.Count != 2) { 441 rv = AE_LIMIT; 442 goto out; 443 } 444 445 for (i = 0; i < 2; i++) { 446 447 elm = &obj->Package.Elements[i]; 448 449 if (elm->Type != ACPI_TYPE_BUFFER) { 450 rv = AE_TYPE; 451 goto out; 452 } 453 454 if (size > elm->Buffer.Length) { 455 rv = AE_AML_BAD_RESOURCE_LENGTH; 456 goto out; 457 } 458 459 reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer; 460 461 switch (reg[i]->reg_spaceid) { 462 463 case ACPI_ADR_SPACE_SYSTEM_IO: 464 465 if (reg[i]->reg_addr == 0) { 466 rv = AE_AML_ILLEGAL_ADDRESS; 467 goto out; 468 } 469 470 /* 471 * Check that the values match the IA32 clock 472 * modulation MSR, where the bit 0 is reserved, 473 * bits 1 through 3 define the duty cycle, and 474 * the fourth bit enables the modulation. 475 */ 476 if (reg[i]->reg_bitwidth != 4) { 477 rv = AE_AML_BAD_RESOURCE_VALUE; 478 goto out; 479 } 480 481 if (reg[i]->reg_bitoffset != 1) { 482 rv = AE_AML_BAD_RESOURCE_VALUE; 483 goto out; 484 } 485 486 break; 487 488 case ACPI_ADR_SPACE_FIXED_HARDWARE: 489 490 if ((sc->sc_flags & ACPICPU_FLAG_T_FFH) == 0) { 491 rv = AE_SUPPORT; 492 goto out; 493 } 494 495 break; 496 497 default: 498 rv = AE_AML_INVALID_SPACE_ID; 499 goto out; 500 } 501 } 502 503 if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) { 504 rv = AE_AML_INVALID_SPACE_ID; 505 goto out; 506 } 507 508 (void)memcpy(&sc->sc_tstate_control, reg[0], size); 509 (void)memcpy(&sc->sc_tstate_status, reg[1], size); 510 511 out: 512 if (buf.Pointer != NULL) 513 ACPI_FREE(buf.Pointer); 514 515 return rv; 516 } 517 518 static ACPI_STATUS 519 acpicpu_tstate_fadt(struct acpicpu_softc *sc) 520 { 521 static const size_t size = sizeof(struct acpicpu_tstate); 522 const uint8_t offset = AcpiGbl_FADT.DutyOffset; 523 const uint8_t width = AcpiGbl_FADT.DutyWidth; 524 uint8_t beta, count, i; 525 526 if (sc->sc_object.ao_pblkaddr == 0) 527 return AE_AML_ILLEGAL_ADDRESS; 528 529 /* 530 * A zero DUTY_WIDTH is used announce that 531 * T-states are not available via FADT. 532 */ 533 if (width == 0 || width + offset > 4) 534 return AE_AML_BAD_RESOURCE_VALUE; 535 536 count = 1 << width; 537 538 if (count > ACPICPU_T_STATE_MAX) 539 return AE_LIMIT; 540 541 if (sc->sc_tstate != NULL) 542 kmem_free(sc->sc_tstate, sc->sc_tstate_count * size); 543 544 sc->sc_tstate = kmem_zalloc(count * size, KM_SLEEP); 545 546 if (sc->sc_tstate == NULL) 547 return ENOMEM; 548 549 sc->sc_tstate_count = count; 550 551 /* 552 * Approximate duty cycles and set the MSR values. 553 */ 554 for (beta = 100 / count, i = 0; i < count; i++) { 555 sc->sc_tstate[i].ts_percent = 100 - beta * i; 556 sc->sc_tstate[i].ts_latency = 1; 557 } 558 559 for (i = 1; i < count; i++) 560 sc->sc_tstate[i].ts_control = (count - i) | __BIT(3); 561 562 /* 563 * Fake values for throttling registers. 564 */ 565 (void)memset(&sc->sc_tstate_status, 0, sizeof(struct acpicpu_reg)); 566 (void)memset(&sc->sc_tstate_control, 0, sizeof(struct acpicpu_reg)); 567 568 sc->sc_tstate_status.reg_bitwidth = width; 569 sc->sc_tstate_status.reg_bitoffset = offset; 570 sc->sc_tstate_status.reg_addr = sc->sc_object.ao_pblkaddr; 571 sc->sc_tstate_status.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; 572 573 sc->sc_tstate_control.reg_bitwidth = width; 574 sc->sc_tstate_control.reg_bitoffset = offset; 575 sc->sc_tstate_control.reg_addr = sc->sc_object.ao_pblkaddr; 576 sc->sc_tstate_control.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; 577 578 return AE_OK; 579 } 580 581 static ACPI_STATUS 582 acpicpu_tstate_change(struct acpicpu_softc *sc) 583 { 584 ACPI_INTEGER val; 585 ACPI_STATUS rv; 586 587 acpicpu_tstate_reset(sc); 588 589 /* 590 * Evaluate the available T-state window: 591 * 592 * _TPC : either this maximum or any lower power 593 * (i.e. higher numbered) state may be used. 594 * 595 * _TDL : either this minimum or any higher power 596 * (i.e. lower numbered) state may be used. 597 * 598 * _TDL >= _TPC || _TDL >= _TSS[last entry]. 599 */ 600 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TPC", &val); 601 602 if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { 603 604 if (sc->sc_tstate[val].ts_percent != 0) 605 sc->sc_tstate_max = val; 606 } 607 608 if (sc->sc_passive != true) 609 return AE_OK; 610 611 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TDL", &val); 612 613 if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { 614 615 if (val >= sc->sc_tstate_max && 616 sc->sc_tstate[val].ts_percent != 0) 617 sc->sc_tstate_min = val; 618 } 619 620 return AE_OK; 621 } 622 623 static void 624 acpicpu_tstate_reset(struct acpicpu_softc *sc) 625 { 626 627 sc->sc_tstate_max = 0; 628 sc->sc_tstate_min = sc->sc_tstate_count - 1; 629 } 630 631 int 632 acpicpu_tstate_get(struct acpicpu_softc *sc, uint32_t *percent) 633 { 634 const uint8_t method = sc->sc_tstate_control.reg_spaceid; 635 struct acpicpu_tstate *ts = NULL; 636 uint32_t i, val = 0; 637 uint8_t offset; 638 uint64_t addr; 639 int rv; 640 641 if (sc->sc_cold != false) { 642 rv = EBUSY; 643 goto fail; 644 } 645 646 if ((sc->sc_flags & ACPICPU_FLAG_T) == 0) { 647 rv = ENODEV; 648 goto fail; 649 } 650 651 mutex_enter(&sc->sc_mtx); 652 653 if (sc->sc_tstate_current != ACPICPU_T_STATE_UNKNOWN) { 654 *percent = sc->sc_tstate_current; 655 mutex_exit(&sc->sc_mtx); 656 return 0; 657 } 658 659 mutex_exit(&sc->sc_mtx); 660 661 switch (method) { 662 663 case ACPI_ADR_SPACE_FIXED_HARDWARE: 664 665 rv = acpicpu_md_tstate_get(sc, percent); 666 667 if (rv != 0) 668 goto fail; 669 670 break; 671 672 case ACPI_ADR_SPACE_SYSTEM_IO: 673 674 addr = sc->sc_tstate_status.reg_addr; 675 offset = sc->sc_tstate_status.reg_bitoffset; 676 677 (void)AcpiOsReadPort(addr, &val, 8); 678 679 val = (val >> offset) & 0x0F; 680 681 for (i = 0; i < sc->sc_tstate_count; i++) { 682 683 if (sc->sc_tstate[i].ts_percent == 0) 684 continue; 685 686 if (val == sc->sc_tstate[i].ts_status) { 687 ts = &sc->sc_tstate[i]; 688 break; 689 } 690 } 691 692 if (__predict_false(ts == NULL)) { 693 rv = EIO; 694 goto fail; 695 } 696 697 *percent = ts->ts_percent; 698 break; 699 700 default: 701 rv = ENOTTY; 702 goto fail; 703 } 704 705 mutex_enter(&sc->sc_mtx); 706 sc->sc_tstate_current = *percent; 707 mutex_exit(&sc->sc_mtx); 708 709 return 0; 710 711 fail: 712 aprint_error_dev(sc->sc_dev, "failed " 713 "to get T-state (err %d)\n", rv); 714 715 mutex_enter(&sc->sc_mtx); 716 *percent = sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; 717 mutex_exit(&sc->sc_mtx); 718 719 return rv; 720 } 721 722 int 723 acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent) 724 { 725 const uint8_t method = sc->sc_tstate_control.reg_spaceid; 726 struct acpicpu_tstate *ts = NULL; 727 uint32_t i, val; 728 uint8_t offset; 729 uint64_t addr; 730 int rv; 731 732 if (sc->sc_cold != false) { 733 rv = EBUSY; 734 goto fail; 735 } 736 737 if ((sc->sc_flags & ACPICPU_FLAG_T) == 0) { 738 rv = ENODEV; 739 goto fail; 740 } 741 742 mutex_enter(&sc->sc_mtx); 743 744 if (sc->sc_tstate_current == percent) { 745 mutex_exit(&sc->sc_mtx); 746 return 0; 747 } 748 749 for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) { 750 751 if (sc->sc_tstate[i].ts_percent == 0) 752 continue; 753 754 if (sc->sc_tstate[i].ts_percent == percent) { 755 ts = &sc->sc_tstate[i]; 756 break; 757 } 758 } 759 760 mutex_exit(&sc->sc_mtx); 761 762 if (__predict_false(ts == NULL)) { 763 rv = EINVAL; 764 goto fail; 765 } 766 767 switch (method) { 768 769 case ACPI_ADR_SPACE_FIXED_HARDWARE: 770 771 rv = acpicpu_md_tstate_set(ts); 772 773 if (rv != 0) 774 goto fail; 775 776 break; 777 778 case ACPI_ADR_SPACE_SYSTEM_IO: 779 780 addr = sc->sc_tstate_control.reg_addr; 781 offset = sc->sc_tstate_control.reg_bitoffset; 782 783 val = (ts->ts_control & 0x0F) << offset; 784 785 if (ts->ts_percent != 100 && (val & __BIT(4)) == 0) { 786 rv = EINVAL; 787 goto fail; 788 } 789 790 (void)AcpiOsWritePort(addr, val, 8); 791 792 /* 793 * If the status field is zero, the transition is 794 * specified to be "asynchronous" and there is no 795 * need to check the status (ACPI 4.0, 8.4.3.2). 796 */ 797 if (ts->ts_status == 0) 798 break; 799 800 addr = sc->sc_tstate_status.reg_addr; 801 offset = sc->sc_tstate_status.reg_bitoffset; 802 803 for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) { 804 805 (void)AcpiOsReadPort(addr, &val, 8); 806 807 val = (val >> offset) & 0x0F; 808 809 if (val == ts->ts_status) 810 break; 811 812 DELAY(ts->ts_latency); 813 } 814 815 if (i == ACPICPU_T_STATE_RETRY) { 816 rv = EAGAIN; 817 goto fail; 818 } 819 820 break; 821 822 default: 823 rv = ENOTTY; 824 goto fail; 825 } 826 827 mutex_enter(&sc->sc_mtx); 828 ts->ts_evcnt.ev_count++; 829 sc->sc_tstate_current = percent; 830 mutex_exit(&sc->sc_mtx); 831 832 return 0; 833 834 fail: 835 aprint_error_dev(sc->sc_dev, "failed to " 836 "throttle to %u %% (err %d)\n", percent, rv); 837 838 mutex_enter(&sc->sc_mtx); 839 sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; 840 mutex_exit(&sc->sc_mtx); 841 842 return rv; 843 } 844