168349Sobrien/* $NetBSD: acpi_cpu_tstate.c,v 1.34 2020/12/07 10:57:41 jmcneill Exp $ */ 268349Sobrien 3302221Sdelphij/*- 468349Sobrien * Copyright (c) 2010 Jukka Ruohonen <jruohonen@iki.fi> 5186691Sobrien * All rights reserved. 6186691Sobrien * 7234449Sobrien * Redistribution and use in source and binary forms, with or without 8267897Sdelphij * modification, are permitted provided that the following conditions 9234449Sobrien * are met: 10234449Sobrien * 1168349Sobrien * 1. Redistributions of source code must retain the above copyright 1268349Sobrien * notice, this list of conditions and the following disclaimer. 13234449Sobrien * 2. Redistributions in binary form must reproduce the above copyright 14234449Sobrien * notice, this list of conditions and the following disclaimer in the 15234449Sobrien * documentation and/or other materials provided with the distribution. 16139368Sobrien * 17175296Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18175296Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19234449Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20159764Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21159764Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22234449Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23159764Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24159764Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25175296Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26175296Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27234449Sobrien * SUCH DAMAGE. 28234449Sobrien */ 29234449Sobrien#include <sys/cdefs.h> 30234449Sobrien__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_tstate.c,v 1.34 2020/12/07 10:57:41 jmcneill Exp $"); 31234449Sobrien 32175296Sobrien#include <sys/param.h> 33139368Sobrien#include <sys/kmem.h> 34139368Sobrien#include <sys/xcall.h> 35175296Sobrien#include <sys/cpu.h> 36234449Sobrien 37234449Sobrien#include <dev/acpi/acpireg.h> 38234449Sobrien#include <dev/acpi/acpivar.h> 39234449Sobrien#include <dev/acpi/acpi_cpu.h> 40234449Sobrien 41234449Sobrien#define _COMPONENT ACPI_BUS_COMPONENT 42234449SobrienACPI_MODULE_NAME ("acpi_cpu_tstate") 43234449Sobrien 44234449Sobrienstatic ACPI_STATUS acpicpu_tstate_tss(struct acpicpu_softc *); 45234449Sobrienstatic ACPI_STATUS acpicpu_tstate_tss_add(struct acpicpu_tstate *, 46234449Sobrien ACPI_OBJECT *); 47234449Sobrienstatic ACPI_STATUS acpicpu_tstate_ptc(struct acpicpu_softc *); 48234449Sobrienstatic ACPI_STATUS acpicpu_tstate_dep(struct acpicpu_softc *); 49234449Sobrienstatic ACPI_STATUS acpicpu_tstate_fadt(struct acpicpu_softc *); 50234449Sobrienstatic ACPI_STATUS acpicpu_tstate_change(struct acpicpu_softc *); 51234449Sobrienstatic void acpicpu_tstate_reset(struct acpicpu_softc *); 52234449Sobrienstatic void acpicpu_tstate_set_xcall(void *, void *); 53234449Sobrien 54234449Sobrienextern struct acpicpu_softc **acpicpu_sc; 55234449Sobrien 56175296Sobrienvoid 57234449Sobrienacpicpu_tstate_attach(device_t self) 58234449Sobrien{ 59234449Sobrien struct acpicpu_softc *sc = device_private(self); 60234449Sobrien const char *str; 61234449Sobrien ACPI_HANDLE tmp; 62234449Sobrien ACPI_STATUS rv; 63234449Sobrien 64234449Sobrien /* 65234449Sobrien * Disable T-states for PIIX4. 66234449Sobrien */ 67234449Sobrien if ((sc->sc_flags & ACPICPU_FLAG_PIIX4) != 0) 68234449Sobrien return; 69175296Sobrien 70234449Sobrien rv = acpicpu_tstate_tss(sc); 71234449Sobrien 72175296Sobrien if (ACPI_FAILURE(rv)) { 73175296Sobrien str = "_TSS"; 74234449Sobrien goto out; 75234449Sobrien } 76234449Sobrien 77234449Sobrien rv = acpicpu_tstate_ptc(sc); 78234449Sobrien 79234449Sobrien if (ACPI_FAILURE(rv)) { 80234449Sobrien str = "_PTC"; 81139368Sobrien goto out; 82139368Sobrien } 83139368Sobrien 84175296Sobrien /* 85175296Sobrien * Query the optional _TSD. 86175296Sobrien */ 87175296Sobrien rv = acpicpu_tstate_dep(sc); 88175296Sobrien 89175296Sobrien if (ACPI_SUCCESS(rv)) 90175296Sobrien sc->sc_flags |= ACPICPU_FLAG_T_DEP; 91175296Sobrien 92234449Sobrien /* 93234449Sobrien * Comparable to P-states, the _TPC object may 94175296Sobrien * be absent in some systems, even though it is 95175296Sobrien * required by ACPI 3.0 along with _TSS and _PTC. 96175296Sobrien */ 97175296Sobrien rv = AcpiGetHandle(sc->sc_node->ad_handle, "_TPC", &tmp); 98175296Sobrien 99175296Sobrien if (ACPI_FAILURE(rv)) { 100234449Sobrien aprint_debug_dev(self, "_TPC missing\n"); 101234449Sobrien rv = AE_OK; 102175296Sobrien } 103175296Sobrien 104175296Sobrienout: 105175296Sobrien if (ACPI_FAILURE(rv)) { 106175296Sobrien 107175296Sobrien if (rv != AE_NOT_FOUND) 108175296Sobrien aprint_error_dev(sc->sc_dev, "failed to evaluate " 109175296Sobrien "%s: %s\n", str, AcpiFormatException(rv)); 110175296Sobrien 111175296Sobrien rv = acpicpu_tstate_fadt(sc); 112234449Sobrien 113234449Sobrien if (ACPI_FAILURE(rv)) 114175296Sobrien return; 115175296Sobrien 116234449Sobrien sc->sc_flags |= ACPICPU_FLAG_T_FADT; 117234449Sobrien } 118234449Sobrien 119234449Sobrien sc->sc_flags |= ACPICPU_FLAG_T; 120234449Sobrien 121234449Sobrien acpicpu_tstate_reset(sc); 122234449Sobrien} 123175296Sobrien 124159764Sobrienvoid 125159764Sobrienacpicpu_tstate_detach(device_t self) 126159764Sobrien{ 127139368Sobrien struct acpicpu_softc *sc = device_private(self); 128159764Sobrien size_t size; 129234449Sobrien 130234449Sobrien if ((sc->sc_flags & ACPICPU_FLAG_T) == 0) 131234449Sobrien return; 132234449Sobrien 133139368Sobrien size = sc->sc_tstate_count * sizeof(*sc->sc_tstate); 134175296Sobrien 135234449Sobrien if (sc->sc_tstate != NULL) 136159764Sobrien kmem_free(sc->sc_tstate, size); 137267897Sdelphij 138267897Sdelphij sc->sc_flags &= ~ACPICPU_FLAG_T; 139267897Sdelphij} 140267897Sdelphij 141267897Sdelphijvoid 142267897Sdelphijacpicpu_tstate_start(device_t self) 143267897Sdelphij{ 144267897Sdelphij /* Nothing. */ 145267897Sdelphij} 146267897Sdelphij 147267897Sdelphijvoid 148267897Sdelphijacpicpu_tstate_suspend(void *aux) 149267897Sdelphij{ 150267897Sdelphij struct acpicpu_softc *sc; 151267897Sdelphij device_t self = aux; 152267897Sdelphij 153267897Sdelphij sc = device_private(self); 154267897Sdelphij 155267897Sdelphij mutex_enter(&sc->sc_mtx); 156267897Sdelphij acpicpu_tstate_reset(sc); 157267897Sdelphij mutex_exit(&sc->sc_mtx); 158267897Sdelphij} 159267897Sdelphij 160267897Sdelphijvoid 161267897Sdelphijacpicpu_tstate_resume(void *aux) 162175296Sobrien{ 163234449Sobrien /* Nothing. */ 164234449Sobrien} 165234449Sobrien 166234449Sobrienvoid 167234449Sobrienacpicpu_tstate_callback(void *aux) 168234449Sobrien{ 169234449Sobrien struct acpicpu_softc *sc; 170175296Sobrien device_t self = aux; 171234449Sobrien uint32_t omax, omin; 172234449Sobrien int i; 173234449Sobrien 174234449Sobrien sc = device_private(self); 175234449Sobrien 176234449Sobrien if ((sc->sc_flags & ACPICPU_FLAG_T_FADT) != 0) 177133359Sobrien return; 178234449Sobrien 179234449Sobrien mutex_enter(&sc->sc_mtx); 180234449Sobrien 181234449Sobrien /* 182234449Sobrien * If P-states are in use, we should ignore 183234449Sobrien * the interrupt unless we are in the highest 184133359Sobrien * P-state (see ACPI 4.0, section 8.4.3.3). 185175296Sobrien */ 186234449Sobrien if ((sc->sc_flags & ACPICPU_FLAG_P) != 0) { 187234449Sobrien 188234449Sobrien for (i = sc->sc_pstate_count - 1; i >= 0; i--) { 189234449Sobrien 190234449Sobrien if (sc->sc_pstate[i].ps_freq != 0) 191133359Sobrien break; 192175296Sobrien } 193234449Sobrien 194175296Sobrien if (sc->sc_pstate_current != sc->sc_pstate[i].ps_freq) { 195267897Sdelphij mutex_exit(&sc->sc_mtx); 196267897Sdelphij return; 197234449Sobrien } 198234449Sobrien } 199234449Sobrien 200234449Sobrien omax = sc->sc_tstate_max; 201267897Sdelphij omin = sc->sc_tstate_min; 202267897Sdelphij 203267897Sdelphij (void)acpicpu_tstate_change(sc); 204267897Sdelphij 205267897Sdelphij if (omax != sc->sc_tstate_max || omin != sc->sc_tstate_min) { 206267897Sdelphij 207267897Sdelphij aprint_debug_dev(sc->sc_dev, "throttling window " 208267897Sdelphij "changed from %u-%u %% to %u-%u %%\n", 209267897Sdelphij sc->sc_tstate[omax].ts_percent, 210234449Sobrien sc->sc_tstate[omin].ts_percent, 211234449Sobrien sc->sc_tstate[sc->sc_tstate_max].ts_percent, 212175296Sobrien sc->sc_tstate[sc->sc_tstate_min].ts_percent); 213234449Sobrien } 214175296Sobrien 215234449Sobrien mutex_exit(&sc->sc_mtx); 216175296Sobrien} 217175296Sobrien 218234449Sobrienstatic ACPI_STATUS 219234449Sobrienacpicpu_tstate_tss(struct acpicpu_softc *sc) 220133359Sobrien{ 221133359Sobrien struct acpicpu_tstate *ts; 222133359Sobrien ACPI_OBJECT *obj; 223133359Sobrien ACPI_BUFFER buf; 224234449Sobrien ACPI_STATUS rv; 225234449Sobrien uint32_t count; 226133359Sobrien uint32_t i, j; 227175296Sobrien 228234449Sobrien rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSS", &buf); 229234449Sobrien 230234449Sobrien if (ACPI_FAILURE(rv)) 231234449Sobrien return rv; 232234449Sobrien 233234449Sobrien obj = buf.Pointer; 234234449Sobrien 235234449Sobrien if (obj->Type != ACPI_TYPE_PACKAGE) { 236234449Sobrien rv = AE_TYPE; 237175296Sobrien goto out; 238175296Sobrien } 239234449Sobrien 240175296Sobrien sc->sc_tstate_count = obj->Package.Count; 241234449Sobrien 242234449Sobrien if (sc->sc_tstate_count == 0) { 243234449Sobrien rv = AE_NOT_EXIST; 244234449Sobrien goto out; 245175296Sobrien } 246175296Sobrien 247175296Sobrien sc->sc_tstate = kmem_zalloc(sc->sc_tstate_count * 248234449Sobrien sizeof(struct acpicpu_tstate), KM_SLEEP); 249267897Sdelphij 250267897Sdelphij if (sc->sc_tstate == NULL) { 251267897Sdelphij rv = AE_NO_MEMORY; 252267897Sdelphij goto out; 253267897Sdelphij } 254267897Sdelphij 255267897Sdelphij for (count = i = 0; i < sc->sc_tstate_count; i++) { 256267897Sdelphij 257267897Sdelphij ts = &sc->sc_tstate[i]; 258267897Sdelphij rv = acpicpu_tstate_tss_add(ts, &obj->Package.Elements[i]); 259267897Sdelphij 260267897Sdelphij if (ACPI_FAILURE(rv)) { 261267897Sdelphij ts->ts_percent = 0; 262267897Sdelphij continue; 263267897Sdelphij } 264267897Sdelphij 265267897Sdelphij for (j = 0; j < i; j++) { 266267897Sdelphij 267267897Sdelphij if (ts->ts_percent >= sc->sc_tstate[j].ts_percent) { 268175296Sobrien ts->ts_percent = 0; 269175296Sobrien break; 270234449Sobrien } 271234449Sobrien } 272234449Sobrien 273234449Sobrien if (ts->ts_percent != 0) 274234449Sobrien count++; 275234449Sobrien } 276234449Sobrien 277234449Sobrien if (count == 0) { 278234449Sobrien rv = AE_NOT_EXIST; 279234449Sobrien goto out; 280234449Sobrien } 281234449Sobrien 282234449Sobrien /* 283234449Sobrien * There must be an entry with the percent 284175296Sobrien * field of 100. If this is not true, and if 285175296Sobrien * this entry is not in the expected index, 286175296Sobrien * invalidate the use of T-states via _TSS. 287234449Sobrien */ 288234449Sobrien if (sc->sc_tstate[0].ts_percent != 100) { 289234449Sobrien rv = AE_BAD_DECIMAL_CONSTANT; 290234449Sobrien goto out; 291234449Sobrien } 292175296Sobrien 293234449Sobrienout: 294234449Sobrien if (buf.Pointer != NULL) 295234449Sobrien ACPI_FREE(buf.Pointer); 296234449Sobrien 297234449Sobrien return rv; 298234449Sobrien} 299234449Sobrien 300234449Sobrienstatic ACPI_STATUS 301234449Sobrienacpicpu_tstate_tss_add(struct acpicpu_tstate *ts, ACPI_OBJECT *obj) 302234449Sobrien{ 303234449Sobrien ACPI_OBJECT *elm; 304175296Sobrien uint32_t val[5]; 305234449Sobrien uint32_t *p; 306234449Sobrien int i; 307234449Sobrien 308234449Sobrien if (obj->Type != ACPI_TYPE_PACKAGE) 309234449Sobrien return AE_TYPE; 310234449Sobrien 311234449Sobrien if (obj->Package.Count != 5) 312175296Sobrien return AE_BAD_DATA; 313234449Sobrien 314234449Sobrien elm = obj->Package.Elements; 315234449Sobrien 316234449Sobrien for (i = 0; i < 5; i++) { 317234449Sobrien 318234449Sobrien if (elm[i].Type != ACPI_TYPE_INTEGER) 319234449Sobrien return AE_TYPE; 320234449Sobrien 321234449Sobrien if (elm[i].Integer.Value > UINT32_MAX) 322175296Sobrien return AE_AML_NUMERIC_OVERFLOW; 323234449Sobrien 324234449Sobrien val[i] = elm[i].Integer.Value; 325234449Sobrien } 326234449Sobrien 327234449Sobrien p = &ts->ts_percent; 328175296Sobrien 329234449Sobrien for (i = 0; i < 5; i++, p++) 330234449Sobrien *p = val[i]; 331234449Sobrien 332234449Sobrien /* 333234449Sobrien * The minimum should be either 12.5 % or 6.5 %, 334234449Sobrien * the latter 4-bit dynamic range being available 335234449Sobrien * in some newer models; see Section 14.5.3.1 in 336234449Sobrien * 337234449Sobrien * Intel 64 and IA-32 Architectures Software 338234449Sobrien * Developer's Manual. Volume 3B, Part 2. 2013. 339234449Sobrien */ 340234449Sobrien if (ts->ts_percent < 6 || ts->ts_percent > 100) 341234449Sobrien return AE_BAD_DECIMAL_CONSTANT; 342234449Sobrien 343234449Sobrien if (ts->ts_latency == 0 || ts->ts_latency > 1000) 344234449Sobrien ts->ts_latency = 1; 345234449Sobrien 346234449Sobrien return AE_OK; 347234449Sobrien} 348234449Sobrien 349234449SobrienACPI_STATUS 350234449Sobrienacpicpu_tstate_ptc(struct acpicpu_softc *sc) 351234449Sobrien{ 352234449Sobrien static const size_t size = sizeof(struct acpicpu_reg); 353234449Sobrien struct acpicpu_reg *reg[2]; 354234449Sobrien ACPI_OBJECT *elm, *obj; 355234449Sobrien ACPI_BUFFER buf; 356234449Sobrien ACPI_STATUS rv; 357234449Sobrien int i; 358234449Sobrien 359234449Sobrien rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PTC", &buf); 360234449Sobrien 361234449Sobrien if (ACPI_FAILURE(rv)) 362234449Sobrien return rv; 363234449Sobrien 364234449Sobrien obj = buf.Pointer; 365234449Sobrien 366234449Sobrien if (obj->Type != ACPI_TYPE_PACKAGE) { 367133359Sobrien rv = AE_TYPE; 368175296Sobrien goto out; 369234449Sobrien } 370267897Sdelphij 371267897Sdelphij if (obj->Package.Count != 2) { 372267897Sdelphij rv = AE_LIMIT; 373267897Sdelphij goto out; 374267897Sdelphij } 375267897Sdelphij 376267897Sdelphij for (i = 0; i < 2; i++) { 377267897Sdelphij 378234449Sobrien elm = &obj->Package.Elements[i]; 379234449Sobrien 380234449Sobrien if (elm->Type != ACPI_TYPE_BUFFER) { 381234449Sobrien rv = AE_TYPE; 382234449Sobrien goto out; 383234449Sobrien } 384234449Sobrien 385234449Sobrien if (size > elm->Buffer.Length) { 386234449Sobrien rv = AE_AML_BAD_RESOURCE_LENGTH; 387234449Sobrien goto out; 388234449Sobrien } 389234449Sobrien 390234449Sobrien reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer; 391234449Sobrien 392234449Sobrien switch (reg[i]->reg_spaceid) { 393234449Sobrien 394234449Sobrien case ACPI_ADR_SPACE_SYSTEM_MEMORY: 395175296Sobrien 396234449Sobrien if (reg[i]->reg_addr == 0) { 397234449Sobrien rv = AE_AML_ILLEGAL_ADDRESS; 398234449Sobrien goto out; 399234449Sobrien } 400234449Sobrien 401234449Sobrien break; 402234449Sobrien 403234449Sobrien case ACPI_ADR_SPACE_SYSTEM_IO: 404234449Sobrien 405234449Sobrien if (reg[i]->reg_addr == 0) { 406234449Sobrien rv = AE_AML_ILLEGAL_ADDRESS; 407234449Sobrien goto out; 408234449Sobrien } 409234449Sobrien 410234449Sobrien#if defined(__i386__) || defined(__x86_64__) 411234449Sobrien /* 412175296Sobrien * Check that the values match the IA32 clock 413175296Sobrien * modulation MSR, where the bit 0 is reserved, 414234449Sobrien * bits 1 through 3 define the duty cycle, and 415234449Sobrien * the fourth bit enables the modulation. 416234449Sobrien */ 417234449Sobrien if (reg[i]->reg_bitwidth != 4) { 418234449Sobrien rv = AE_AML_BAD_RESOURCE_VALUE; 419234449Sobrien goto out; 420234449Sobrien } 421234449Sobrien 422234449Sobrien if (reg[i]->reg_bitoffset != 1) { 423234449Sobrien rv = AE_AML_BAD_RESOURCE_VALUE; 424234449Sobrien goto out; 425234449Sobrien } 426234449Sobrien#endif 427234449Sobrien 428234449Sobrien break; 429175296Sobrien 430234449Sobrien case ACPI_ADR_SPACE_FIXED_HARDWARE: 431234449Sobrien 432234449Sobrien if ((sc->sc_flags & ACPICPU_FLAG_T_FFH) == 0) { 433175296Sobrien rv = AE_SUPPORT; 434234449Sobrien goto out; 435175296Sobrien } 436175296Sobrien 437234449Sobrien break; 438234449Sobrien 439175296Sobrien default: 440234449Sobrien rv = AE_AML_INVALID_SPACE_ID; 441175296Sobrien goto out; 442175296Sobrien } 443234449Sobrien } 444234449Sobrien 445175296Sobrien if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) { 446234449Sobrien rv = AE_AML_INVALID_SPACE_ID; 447175296Sobrien goto out; 448175296Sobrien } 449234449Sobrien 450234449Sobrien (void)memcpy(&sc->sc_tstate_control, reg[0], size); 451234449Sobrien (void)memcpy(&sc->sc_tstate_status, reg[1], size); 452234449Sobrien 453234449Sobrienout: 454234449Sobrien if (buf.Pointer != NULL) 455234449Sobrien ACPI_FREE(buf.Pointer); 456234449Sobrien 457234449Sobrien return rv; 458234449Sobrien} 459234449Sobrien 460234449Sobrienstatic ACPI_STATUS 461234449Sobrienacpicpu_tstate_dep(struct acpicpu_softc *sc) 462234449Sobrien{ 463234449Sobrien ACPI_OBJECT *elm, *obj; 464234449Sobrien ACPI_BUFFER buf; 465234449Sobrien ACPI_STATUS rv; 466234449Sobrien uint32_t val; 467175296Sobrien uint8_t i, n; 468234449Sobrien 469234449Sobrien rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSD", &buf); 470234449Sobrien 471234449Sobrien if (ACPI_FAILURE(rv)) 472234449Sobrien goto out; 473234449Sobrien 474175296Sobrien obj = buf.Pointer; 475175296Sobrien 476234449Sobrien if (obj->Type != ACPI_TYPE_PACKAGE) { 477234449Sobrien rv = AE_TYPE; 478234449Sobrien goto out; 479234449Sobrien } 480234449Sobrien 481175296Sobrien if (obj->Package.Count != 1) { 482175296Sobrien rv = AE_LIMIT; 483175296Sobrien goto out; 484175296Sobrien } 485133359Sobrien 486175296Sobrien elm = &obj->Package.Elements[0]; 487175296Sobrien 488175296Sobrien if (obj->Type != ACPI_TYPE_PACKAGE) { 489175296Sobrien rv = AE_TYPE; 490133359Sobrien goto out; 491175296Sobrien } 492175296Sobrien 493133359Sobrien n = elm->Package.Count; 494175296Sobrien 495133359Sobrien if (n != 5) { 496175296Sobrien rv = AE_LIMIT; 497234449Sobrien goto out; 498133359Sobrien } 499267897Sdelphij 500267897Sdelphij elm = elm->Package.Elements; 501267897Sdelphij 502267897Sdelphij for (i = 0; i < n; i++) { 503133359Sobrien 504133359Sobrien if (elm[i].Type != ACPI_TYPE_INTEGER) { 505175296Sobrien rv = AE_TYPE; 506175296Sobrien goto out; 507133359Sobrien } 508133359Sobrien 509133359Sobrien if (elm[i].Integer.Value > UINT32_MAX) { 510133359Sobrien rv = AE_AML_NUMERIC_OVERFLOW; 511175296Sobrien goto out; 512234449Sobrien } 513175296Sobrien } 514234449Sobrien 515175296Sobrien val = elm[1].Integer.Value; 516234449Sobrien 517234449Sobrien if (val != 0) 518234449Sobrien aprint_debug_dev(sc->sc_dev, "invalid revision in _TSD\n"); 519175296Sobrien 520175296Sobrien val = elm[3].Integer.Value; 521175296Sobrien 522133359Sobrien if (val < ACPICPU_DEP_SW_ALL || val > ACPICPU_DEP_HW_ALL) { 523133359Sobrien rv = AE_AML_BAD_RESOURCE_VALUE; 524133359Sobrien goto out; 525175296Sobrien } 526175296Sobrien 527175296Sobrien val = elm[4].Integer.Value; 528175296Sobrien 529234449Sobrien if (val > sc->sc_ncpus) { 530175296Sobrien rv = AE_BAD_VALUE; 531234449Sobrien goto out; 532234449Sobrien } 533234449Sobrien 534234449Sobrien sc->sc_tstate_dep.dep_domain = elm[2].Integer.Value; 535234449Sobrien sc->sc_tstate_dep.dep_type = elm[3].Integer.Value; 536234449Sobrien sc->sc_tstate_dep.dep_ncpus = elm[4].Integer.Value; 537267897Sdelphij 538234449Sobrienout: 539267897Sdelphij if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 540234449Sobrien aprint_debug_dev(sc->sc_dev, "failed to evaluate " 541234449Sobrien "_TSD: %s\n", AcpiFormatException(rv)); 542234449Sobrien 543267897Sdelphij if (buf.Pointer != NULL) 544234449Sobrien ACPI_FREE(buf.Pointer); 545133359Sobrien 546267897Sdelphij return rv; 547133359Sobrien} 548175296Sobrien 549175296Sobrienstatic ACPI_STATUS 550133359Sobrienacpicpu_tstate_fadt(struct acpicpu_softc *sc) 551159764Sobrien{ 552234449Sobrien static const size_t size = sizeof(struct acpicpu_tstate); 553159764Sobrien const uint8_t offset = AcpiGbl_FADT.DutyOffset; 554159764Sobrien const uint8_t width = AcpiGbl_FADT.DutyWidth; 555159764Sobrien uint8_t beta, count, i; 556159764Sobrien 557159764Sobrien if (sc->sc_object.ao_pblkaddr == 0) 558267897Sdelphij return AE_AML_ILLEGAL_ADDRESS; 559267897Sdelphij 560133359Sobrien /* 561133359Sobrien * A zero DUTY_WIDTH may be used announce 562159764Sobrien * that T-states are not available via FADT 563133359Sobrien * (ACPI 4.0, p. 121). See also (section 9.3): 564133359Sobrien * 565159764Sobrien * Advanced Micro Devices: BIOS and Kernel 566133359Sobrien * Developer's Guide for AMD Athlon 64 and 567159764Sobrien * AMD Opteron Processors. Revision 3.30, 568159764Sobrien * February 2006. 569159764Sobrien */ 570234449Sobrien if (width == 0 || width + offset > 4) 571234449Sobrien return AE_AML_BAD_RESOURCE_VALUE; 572159764Sobrien 573133359Sobrien count = 1 << width; 574234449Sobrien 575133359Sobrien if (sc->sc_tstate != NULL) 576133359Sobrien kmem_free(sc->sc_tstate, sc->sc_tstate_count * size); 577133359Sobrien 578133359Sobrien sc->sc_tstate = kmem_zalloc(count * size, KM_SLEEP); 579133359Sobrien sc->sc_tstate_count = count; 580133359Sobrien 58168349Sobrien /* 582175296Sobrien * Approximate duty cycles and set the MSR values. 583159764Sobrien */ 584175296Sobrien for (beta = 100 / count, i = 0; i < count; i++) { 585133359Sobrien sc->sc_tstate[i].ts_percent = 100 - beta * i; 586133359Sobrien sc->sc_tstate[i].ts_latency = 1; 587133359Sobrien } 588133359Sobrien 58968349Sobrien for (i = 1; i < count; i++) 590133359Sobrien sc->sc_tstate[i].ts_control = (count - i) | __BIT(3); 591186691Sobrien 592186691Sobrien /* 593302221Sdelphij * Fake values for throttling registers. 594302221Sdelphij */ 595186691Sobrien (void)memset(&sc->sc_tstate_status, 0, sizeof(struct acpicpu_reg)); 596234449Sobrien (void)memset(&sc->sc_tstate_control, 0, sizeof(struct acpicpu_reg)); 597133359Sobrien 598133359Sobrien sc->sc_tstate_status.reg_bitwidth = width; 599133359Sobrien sc->sc_tstate_status.reg_bitoffset = offset; 600133359Sobrien sc->sc_tstate_status.reg_addr = sc->sc_object.ao_pblkaddr; 601175296Sobrien sc->sc_tstate_status.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; 602133359Sobrien 603133359Sobrien sc->sc_tstate_control.reg_bitwidth = width; 604175296Sobrien sc->sc_tstate_control.reg_bitoffset = offset; 605133359Sobrien sc->sc_tstate_control.reg_addr = sc->sc_object.ao_pblkaddr; 606133359Sobrien sc->sc_tstate_control.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; 607175296Sobrien 608133359Sobrien return AE_OK; 609133359Sobrien} 610133359Sobrien 611175296Sobrienstatic ACPI_STATUS 612133359Sobrienacpicpu_tstate_change(struct acpicpu_softc *sc) 613133359Sobrien{ 614133359Sobrien ACPI_INTEGER val; 615175296Sobrien ACPI_STATUS rv; 616175296Sobrien 617133359Sobrien acpicpu_tstate_reset(sc); 618133359Sobrien 619133359Sobrien /* 620133359Sobrien * Evaluate the available T-state window: 621175296Sobrien * 622133359Sobrien * _TPC : either this maximum or any lower power 623133359Sobrien * (i.e. higher numbered) state may be used. 624175296Sobrien * 625133359Sobrien * _TDL : either this minimum or any higher power 626133359Sobrien * (i.e. lower numbered) state may be used. 627175296Sobrien * 628175296Sobrien * _TDL >= _TPC || _TDL >= _TSS[last entry]. 629175296Sobrien */ 630175296Sobrien rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TPC", &val); 631133359Sobrien 632133359Sobrien if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { 633133359Sobrien 634267897Sdelphij if (sc->sc_tstate[val].ts_percent != 0) 635234449Sobrien sc->sc_tstate_max = val; 636234449Sobrien } 637234449Sobrien 638234449Sobrien rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TDL", &val); 639234449Sobrien 640234449Sobrien if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { 641267897Sdelphij 642267897Sdelphij if (val >= sc->sc_tstate_max && 643234449Sobrien sc->sc_tstate[val].ts_percent != 0) 644234449Sobrien sc->sc_tstate_min = val; 645234449Sobrien } 646234449Sobrien 647234449Sobrien return AE_OK; 648267897Sdelphij} 649234449Sobrien 650267897Sdelphijstatic void 651234449Sobrienacpicpu_tstate_reset(struct acpicpu_softc *sc) 652267897Sdelphij{ 653234449Sobrien 654234449Sobrien sc->sc_tstate_max = 0; 655234449Sobrien sc->sc_tstate_min = sc->sc_tstate_count - 1; 656234449Sobrien} 657234449Sobrien 658234449Sobrienint 659234449Sobrienacpicpu_tstate_get(struct cpu_info *ci, uint32_t *percent) 660234449Sobrien{ 661175296Sobrien struct acpicpu_tstate *ts = NULL; 662234449Sobrien struct acpicpu_softc *sc; 663175296Sobrien uint32_t i, val = 0; 664175296Sobrien int rv; 665234449Sobrien 666234449Sobrien sc = acpicpu_sc[ci->ci_acpiid]; 667234449Sobrien 668267897Sdelphij if (__predict_false(sc == NULL)) { 669234449Sobrien rv = ENXIO; 670234449Sobrien goto fail; 671234449Sobrien } 672234449Sobrien 673234449Sobrien if (__predict_false(sc->sc_cold != false)) { 674234449Sobrien rv = EBUSY; 675234449Sobrien goto fail; 676234449Sobrien } 677234449Sobrien 678234449Sobrien if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) { 679234449Sobrien rv = ENODEV; 680234449Sobrien goto fail; 681234449Sobrien } 682234449Sobrien 683234449Sobrien mutex_enter(&sc->sc_mtx); 684234449Sobrien 685234449Sobrien if (sc->sc_tstate_current != ACPICPU_T_STATE_UNKNOWN) { 686234449Sobrien *percent = sc->sc_tstate_current; 687234449Sobrien mutex_exit(&sc->sc_mtx); 688234449Sobrien return 0; 689234449Sobrien } 690234449Sobrien 691234449Sobrien mutex_exit(&sc->sc_mtx); 692234449Sobrien 693234449Sobrien switch (sc->sc_tstate_status.reg_spaceid) { 694234449Sobrien 695234449Sobrien case ACPI_ADR_SPACE_FIXED_HARDWARE: 696234449Sobrien 697234449Sobrien rv = acpicpu_md_tstate_get(sc, percent); 698234449Sobrien 699234449Sobrien if (__predict_false(rv != 0)) 700267897Sdelphij goto fail; 701267897Sdelphij 702234449Sobrien break; 703234449Sobrien 704234449Sobrien case ACPI_ADR_SPACE_SYSTEM_IO: 705234449Sobrien case ACPI_ADR_SPACE_SYSTEM_MEMORY: 706234449Sobrien 707234449Sobrien val = acpicpu_readreg(&sc->sc_tstate_status); 708234449Sobrien 709234449Sobrien for (i = 0; i < sc->sc_tstate_count; i++) { 710234449Sobrien 711234449Sobrien if (sc->sc_tstate[i].ts_percent == 0) 712234449Sobrien continue; 713234449Sobrien 714234449Sobrien if (val == sc->sc_tstate[i].ts_status) { 715234449Sobrien ts = &sc->sc_tstate[i]; 716234449Sobrien break; 717234449Sobrien } 718234449Sobrien } 719234449Sobrien 720234449Sobrien if (ts == NULL) { 721234449Sobrien rv = EIO; 722234449Sobrien goto fail; 723234449Sobrien } 724234449Sobrien 725234449Sobrien *percent = ts->ts_percent; 726234449Sobrien break; 727234449Sobrien 728234449Sobrien default: 729234449Sobrien rv = ENOTTY; 730234449Sobrien goto fail; 731234449Sobrien } 732234449Sobrien 733234449Sobrien mutex_enter(&sc->sc_mtx); 734234449Sobrien sc->sc_tstate_current = *percent; 735234449Sobrien mutex_exit(&sc->sc_mtx); 736234449Sobrien 737234449Sobrien return 0; 738234449Sobrien 739234449Sobrienfail: 740234449Sobrien aprint_error_dev(sc->sc_dev, "failed " 741234449Sobrien "to get T-state (err %d)\n", rv); 742234449Sobrien 743234449Sobrien mutex_enter(&sc->sc_mtx); 744234449Sobrien *percent = sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; 745234449Sobrien mutex_exit(&sc->sc_mtx); 746234449Sobrien 747234449Sobrien return rv; 748234449Sobrien} 749234449Sobrien 750234449Sobrienvoid 751234449Sobrienacpicpu_tstate_set(struct cpu_info *ci, uint32_t percent) 752234449Sobrien{ 753234449Sobrien uint64_t xc; 754234449Sobrien 755234449Sobrien xc = xc_broadcast(0, acpicpu_tstate_set_xcall, &percent, NULL); 756234449Sobrien xc_wait(xc); 757234449Sobrien} 758234449Sobrien 759234449Sobrienstatic void 760234449Sobrienacpicpu_tstate_set_xcall(void *arg1, void *arg2) 761234449Sobrien{ 762234449Sobrien struct acpicpu_tstate *ts = NULL; 763159764Sobrien struct cpu_info *ci = curcpu(); 764234449Sobrien struct acpicpu_softc *sc; 765234449Sobrien uint32_t i, percent, val; 766234449Sobrien int rv; 767234449Sobrien 768234449Sobrien percent = *(uint32_t *)arg1; 769302221Sdelphij sc = acpicpu_sc[ci->ci_acpiid]; 770234449Sobrien 771234449Sobrien if (__predict_false(sc == NULL)) { 772234449Sobrien rv = ENXIO; 773234449Sobrien goto fail; 774267897Sdelphij } 775234449Sobrien 776234449Sobrien if (__predict_false(sc->sc_cold != false)) { 777267897Sdelphij rv = EBUSY; 778234449Sobrien goto fail; 779234449Sobrien } 780234449Sobrien 781234449Sobrien if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) { 782175296Sobrien rv = ENODEV; 783175296Sobrien goto fail; 784175296Sobrien } 785175296Sobrien 786175296Sobrien mutex_enter(&sc->sc_mtx); 787175296Sobrien 788175296Sobrien if (sc->sc_tstate_current == percent) { 789175296Sobrien mutex_exit(&sc->sc_mtx); 790234449Sobrien return; 791133359Sobrien } 792175296Sobrien 79368349Sobrien for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) { 794133359Sobrien 795133359Sobrien if (__predict_false(sc->sc_tstate[i].ts_percent == 0)) 796234449Sobrien continue; 797234449Sobrien 79868349Sobrien if (sc->sc_tstate[i].ts_percent == percent) { 79968349Sobrien ts = &sc->sc_tstate[i]; 800133359Sobrien break; 80168349Sobrien } 80268349Sobrien } 80368349Sobrien 80468349Sobrien mutex_exit(&sc->sc_mtx); 80568349Sobrien 80668349Sobrien if (__predict_false(ts == NULL)) { 80768349Sobrien rv = EINVAL; 80868349Sobrien goto fail; 80968349Sobrien } 81068349Sobrien 81168349Sobrien switch (sc->sc_tstate_control.reg_spaceid) { 81268349Sobrien 81368349Sobrien case ACPI_ADR_SPACE_FIXED_HARDWARE: 814133359Sobrien 815133359Sobrien rv = acpicpu_md_tstate_set(ts); 816133359Sobrien 817133359Sobrien if (__predict_false(rv != 0)) 818133359Sobrien goto fail; 819133359Sobrien 820175296Sobrien break; 82168349Sobrien 82268349Sobrien case ACPI_ADR_SPACE_SYSTEM_IO: 82368349Sobrien case ACPI_ADR_SPACE_SYSTEM_MEMORY: 824175296Sobrien 825175296Sobrien acpicpu_writereg(&sc->sc_tstate_control, ts->ts_control); 82668349Sobrien 82768349Sobrien /* 82868349Sobrien * If the status field is zero, the transition is 82968349Sobrien * specified to be "asynchronous" and there is no 83068349Sobrien * need to check the status (ACPI 4.0, 8.4.3.2). 831186691Sobrien */ 832175296Sobrien if (ts->ts_status == 0) 833175296Sobrien break; 834175296Sobrien 835175296Sobrien for (i = 0; i < ACPICPU_T_STATE_RETRY; i++) { 836175296Sobrien 837175296Sobrien val = acpicpu_readreg(&sc->sc_tstate_status); 838175296Sobrien 839175296Sobrien if (val == ts->ts_status) 84068349Sobrien break; 84168349Sobrien 842175296Sobrien DELAY(ts->ts_latency); 84368349Sobrien } 84468349Sobrien 84568349Sobrien if (i == ACPICPU_T_STATE_RETRY) { 84668349Sobrien rv = EAGAIN; 847175296Sobrien goto fail; 84868349Sobrien } 84968349Sobrien 85068349Sobrien break; 85168349Sobrien 852175296Sobrien default: 853234449Sobrien rv = ENOTTY; 854234449Sobrien goto fail; 855234449Sobrien } 856175296Sobrien 85768349Sobrien mutex_enter(&sc->sc_mtx); 85868349Sobrien ts->ts_evcnt.ev_count++; 85968349Sobrien sc->sc_tstate_current = percent; 860175296Sobrien mutex_exit(&sc->sc_mtx); 861175296Sobrien 862175296Sobrien return; 86368349Sobrien 86468349Sobrienfail: 86568349Sobrien if (rv != EINVAL) 86668349Sobrien aprint_error_dev(sc->sc_dev, "failed to " 867133359Sobrien "throttle to %u %% (err %d)\n", percent, rv); 86868349Sobrien 86968349Sobrien mutex_enter(&sc->sc_mtx); 870133359Sobrien sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; 87168349Sobrien mutex_exit(&sc->sc_mtx); 872133359Sobrien} 87368349Sobrien