Home | History | Annotate | Line # | Download | only in acpi
acpi_cpu_tstate.c revision 1.23
      1 /* $NetBSD: acpi_cpu_tstate.c,v 1.23 2011/02/25 19:55:07 jruoho Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2010 Jukka Ruohonen <jruohonen (at) 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.23 2011/02/25 19:55:07 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_dep(struct acpicpu_softc *);
     51 static ACPI_STATUS	 acpicpu_tstate_fadt(struct acpicpu_softc *);
     52 static ACPI_STATUS	 acpicpu_tstate_change(struct acpicpu_softc *);
     53 static void		 acpicpu_tstate_reset(struct acpicpu_softc *);
     54 
     55 void
     56 acpicpu_tstate_attach(device_t self)
     57 {
     58 	struct acpicpu_softc *sc = device_private(self);
     59 	const char *str;
     60 	ACPI_HANDLE tmp;
     61 	ACPI_STATUS rv;
     62 
     63 	/*
     64 	 * Disable T-states for PIIX4.
     65 	 */
     66 	if ((sc->sc_flags & ACPICPU_FLAG_PIIX4) != 0)
     67 		return;
     68 
     69 	rv  = acpicpu_tstate_tss(sc);
     70 
     71 	if (ACPI_FAILURE(rv)) {
     72 		str = "_TSS";
     73 		goto out;
     74 	}
     75 
     76 	rv = acpicpu_tstate_ptc(sc);
     77 
     78 	if (ACPI_FAILURE(rv)) {
     79 		str = "_PTC";
     80 		goto out;
     81 	}
     82 
     83 	/*
     84 	 * Query the optional _TSD.
     85 	 */
     86 	rv = acpicpu_tstate_dep(sc);
     87 
     88 	if (ACPI_SUCCESS(rv))
     89 		sc->sc_flags |= ACPICPU_FLAG_T_DEP;
     90 
     91 	/*
     92 	 * Comparable to P-states, the _TPC object may
     93 	 * be absent in some systems, even though it is
     94 	 * required by ACPI 3.0 along with _TSS and _PTC.
     95 	 */
     96 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "_TPC", &tmp);
     97 
     98 	if (ACPI_FAILURE(rv)) {
     99 		aprint_debug_dev(self, "_TPC missing\n");
    100 		rv = AE_OK;
    101 	}
    102 
    103 out:
    104 	if (ACPI_FAILURE(rv)) {
    105 
    106 		if (rv != AE_NOT_FOUND)
    107 			aprint_error_dev(sc->sc_dev, "failed to evaluate "
    108 			    "%s: %s\n", str, AcpiFormatException(rv));
    109 
    110 		rv = acpicpu_tstate_fadt(sc);
    111 
    112 		if (ACPI_FAILURE(rv))
    113 			return;
    114 
    115 		sc->sc_flags |= ACPICPU_FLAG_T_FADT;
    116 	}
    117 
    118 	sc->sc_flags |= ACPICPU_FLAG_T;
    119 
    120 	acpicpu_tstate_reset(sc);
    121 	acpicpu_tstate_attach_evcnt(sc);
    122 	acpicpu_tstate_attach_print(sc);
    123 }
    124 
    125 static void
    126 acpicpu_tstate_attach_print(struct acpicpu_softc *sc)
    127 {
    128 	const uint8_t method = sc->sc_tstate_control.reg_spaceid;
    129 	struct acpicpu_tstate *ts;
    130 	static bool once = false;
    131 	const char *str;
    132 	uint32_t i;
    133 
    134 	if (once != false)
    135 		return;
    136 
    137 	str = (method != ACPI_ADR_SPACE_FIXED_HARDWARE) ? "I/O" : "FFH";
    138 
    139 	for (i = 0; i < sc->sc_tstate_count; i++) {
    140 
    141 		ts = &sc->sc_tstate[i];
    142 
    143 		if (ts->ts_percent == 0)
    144 			continue;
    145 
    146 		aprint_verbose_dev(sc->sc_dev, "T%u: %3s, "
    147 		    "lat %3u us, pow %5u mW, %3u %%\n", i, str,
    148 		    ts->ts_latency, ts->ts_power, ts->ts_percent);
    149 	}
    150 
    151 	once = true;
    152 }
    153 
    154 static void
    155 acpicpu_tstate_attach_evcnt(struct acpicpu_softc *sc)
    156 {
    157 	struct acpicpu_tstate *ts;
    158 	uint32_t i;
    159 
    160 	for (i = 0; i < sc->sc_tstate_count; i++) {
    161 
    162 		ts = &sc->sc_tstate[i];
    163 
    164 		if (ts->ts_percent == 0)
    165 			continue;
    166 
    167 		(void)snprintf(ts->ts_name, sizeof(ts->ts_name),
    168 		    "T%u (%u %%)", i, ts->ts_percent);
    169 
    170 		evcnt_attach_dynamic(&ts->ts_evcnt, EVCNT_TYPE_MISC,
    171 		    NULL, device_xname(sc->sc_dev), ts->ts_name);
    172 	}
    173 }
    174 
    175 int
    176 acpicpu_tstate_detach(device_t self)
    177 {
    178 	struct acpicpu_softc *sc = device_private(self);
    179 	size_t size;
    180 
    181 	if ((sc->sc_flags & ACPICPU_FLAG_T) == 0)
    182 		return 0;
    183 
    184 	size = sc->sc_tstate_count * sizeof(*sc->sc_tstate);
    185 
    186 	if (sc->sc_tstate != NULL)
    187 		kmem_free(sc->sc_tstate, size);
    188 
    189 	sc->sc_flags &= ~ACPICPU_FLAG_T;
    190 	acpicpu_tstate_detach_evcnt(sc);
    191 
    192 	return 0;
    193 }
    194 
    195 static void
    196 acpicpu_tstate_detach_evcnt(struct acpicpu_softc *sc)
    197 {
    198 	struct acpicpu_tstate *ts;
    199 	uint32_t i;
    200 
    201 	for (i = 0; i < sc->sc_tstate_count; i++) {
    202 
    203 		ts = &sc->sc_tstate[i];
    204 
    205 		if (ts->ts_percent != 0)
    206 			evcnt_detach(&ts->ts_evcnt);
    207 	}
    208 }
    209 
    210 void
    211 acpicpu_tstate_start(device_t self)
    212 {
    213 	/* Nothing. */
    214 }
    215 
    216 bool
    217 acpicpu_tstate_suspend(device_t self)
    218 {
    219 	struct acpicpu_softc *sc = device_private(self);
    220 
    221 	mutex_enter(&sc->sc_mtx);
    222 	acpicpu_tstate_reset(sc);
    223 	mutex_exit(&sc->sc_mtx);
    224 
    225 	return true;
    226 }
    227 
    228 bool
    229 acpicpu_tstate_resume(device_t self)
    230 {
    231 
    232 	return true;
    233 }
    234 
    235 void
    236 acpicpu_tstate_callback(void *aux)
    237 {
    238 	struct acpicpu_softc *sc;
    239 	device_t self = aux;
    240 	uint32_t omax, omin;
    241 	int i;
    242 
    243 	sc = device_private(self);
    244 
    245 	if ((sc->sc_flags & ACPICPU_FLAG_T_FADT) != 0)
    246 		return;
    247 
    248 	mutex_enter(&sc->sc_mtx);
    249 
    250 	/*
    251 	 * If P-states are in use, we should ignore
    252 	 * the interrupt unless we are in the highest
    253 	 * P-state (see ACPI 4.0, section 8.4.3.3).
    254 	 */
    255 	if ((sc->sc_flags & ACPICPU_FLAG_P) != 0) {
    256 
    257 		for (i = sc->sc_pstate_count - 1; i >= 0; i--) {
    258 
    259 			if (sc->sc_pstate[i].ps_freq != 0)
    260 				break;
    261 		}
    262 
    263 		if (sc->sc_pstate_current != sc->sc_pstate[i].ps_freq) {
    264 			mutex_exit(&sc->sc_mtx);
    265 			return;
    266 		}
    267 	}
    268 
    269 	omax = sc->sc_tstate_max;
    270 	omin = sc->sc_tstate_min;
    271 
    272 	(void)acpicpu_tstate_change(sc);
    273 
    274 	if (omax != sc->sc_tstate_max || omin != sc->sc_tstate_min) {
    275 
    276 		aprint_debug_dev(sc->sc_dev, "throttling window "
    277 		    "changed from %u-%u %% to %u-%u %%\n",
    278 		    sc->sc_tstate[omax].ts_percent,
    279 		    sc->sc_tstate[omin].ts_percent,
    280 		    sc->sc_tstate[sc->sc_tstate_max].ts_percent,
    281 		    sc->sc_tstate[sc->sc_tstate_min].ts_percent);
    282 	}
    283 
    284 	mutex_exit(&sc->sc_mtx);
    285 }
    286 
    287 static ACPI_STATUS
    288 acpicpu_tstate_tss(struct acpicpu_softc *sc)
    289 {
    290 	struct acpicpu_tstate *ts;
    291 	ACPI_OBJECT *obj;
    292 	ACPI_BUFFER buf;
    293 	ACPI_STATUS rv;
    294 	uint32_t count;
    295 	uint32_t i, j;
    296 
    297 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSS", &buf);
    298 
    299 	if (ACPI_FAILURE(rv))
    300 		return rv;
    301 
    302 	obj = buf.Pointer;
    303 
    304 	if (obj->Type != ACPI_TYPE_PACKAGE) {
    305 		rv = AE_TYPE;
    306 		goto out;
    307 	}
    308 
    309 	sc->sc_tstate_count = obj->Package.Count;
    310 
    311 	if (sc->sc_tstate_count == 0) {
    312 		rv = AE_NOT_EXIST;
    313 		goto out;
    314 	}
    315 
    316 	if (sc->sc_tstate_count > ACPICPU_T_STATE_MAX) {
    317 		rv = AE_LIMIT;
    318 		goto out;
    319 	}
    320 
    321 	sc->sc_tstate = kmem_zalloc(sc->sc_tstate_count *
    322 	    sizeof(struct acpicpu_tstate), KM_SLEEP);
    323 
    324 	if (sc->sc_tstate == NULL) {
    325 		rv = AE_NO_MEMORY;
    326 		goto out;
    327 	}
    328 
    329 	for (count = i = 0; i < sc->sc_tstate_count; i++) {
    330 
    331 		ts = &sc->sc_tstate[i];
    332 		rv = acpicpu_tstate_tss_add(ts, &obj->Package.Elements[i]);
    333 
    334 		if (ACPI_FAILURE(rv)) {
    335 			ts->ts_percent = 0;
    336 			continue;
    337 		}
    338 
    339 		for (j = 0; j < i; j++) {
    340 
    341 			if (ts->ts_percent >= sc->sc_tstate[j].ts_percent) {
    342 				ts->ts_percent = 0;
    343 				break;
    344 			}
    345 		}
    346 
    347 		if (ts->ts_percent != 0)
    348 			count++;
    349 	}
    350 
    351 	if (count == 0) {
    352 		rv = AE_NOT_EXIST;
    353 		goto out;
    354 	}
    355 
    356 	/*
    357 	 * There must be an entry with the percent
    358 	 * field of 100. If this is not true, and if
    359 	 * this entry is not in the expected index,
    360 	 * invalidate the use of T-states via _TSS.
    361 	 */
    362 	if (sc->sc_tstate[0].ts_percent != 100) {
    363 		rv = AE_BAD_DECIMAL_CONSTANT;
    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 == 0 || ts->ts_latency > 1000)
    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_dep(struct acpicpu_softc *sc)
    520 {
    521 	ACPI_OBJECT *elm, *obj;
    522 	ACPI_BUFFER buf;
    523 	ACPI_STATUS rv;
    524 	uint32_t val;
    525 	uint8_t i, n;
    526 
    527 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSD", &buf);
    528 
    529 	if (ACPI_FAILURE(rv))
    530 		goto out;
    531 
    532 	obj = buf.Pointer;
    533 
    534 	if (obj->Type != ACPI_TYPE_PACKAGE) {
    535 		rv = AE_TYPE;
    536 		goto out;
    537 	}
    538 
    539 	if (obj->Package.Count != 1) {
    540 		rv = AE_LIMIT;
    541 		goto out;
    542 	}
    543 
    544 	elm = &obj->Package.Elements[0];
    545 
    546 	if (obj->Type != ACPI_TYPE_PACKAGE) {
    547 		rv = AE_TYPE;
    548 		goto out;
    549 	}
    550 
    551 	n = elm->Package.Count;
    552 
    553 	if (n != 5) {
    554 		rv = AE_LIMIT;
    555 		goto out;
    556 	}
    557 
    558 	elm = elm->Package.Elements;
    559 
    560 	for (i = 0; i < n; i++) {
    561 
    562 		if (elm[i].Type != ACPI_TYPE_INTEGER) {
    563 			rv = AE_TYPE;
    564 			goto out;
    565 		}
    566 
    567 		if (elm[i].Integer.Value > UINT32_MAX) {
    568 			rv = AE_AML_NUMERIC_OVERFLOW;
    569 			goto out;
    570 		}
    571 	}
    572 
    573 	val = elm[1].Integer.Value;
    574 
    575 	if (val != 0)
    576 		aprint_debug_dev(sc->sc_dev, "invalid revision in _TSD\n");
    577 
    578 	val = elm[3].Integer.Value;
    579 
    580 	if (val < ACPICPU_DEP_SW_ALL || val > ACPICPU_DEP_HW_ALL) {
    581 		rv = AE_AML_BAD_RESOURCE_VALUE;
    582 		goto out;
    583 	}
    584 
    585 	val = elm[4].Integer.Value;
    586 
    587 	if (val > sc->sc_ncpus) {
    588 		rv = AE_BAD_VALUE;
    589 		goto out;
    590 	}
    591 
    592 	sc->sc_tstate_dep.dep_domain = elm[2].Integer.Value;
    593 	sc->sc_tstate_dep.dep_type   = elm[3].Integer.Value;
    594 	sc->sc_tstate_dep.dep_ncpus  = elm[4].Integer.Value;
    595 
    596 out:
    597 	if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND)
    598 		aprint_debug_dev(sc->sc_dev, "failed to evaluate "
    599 		    "_TSD: %s\n", AcpiFormatException(rv));
    600 
    601 	if (buf.Pointer != NULL)
    602 		ACPI_FREE(buf.Pointer);
    603 
    604 	return rv;
    605 }
    606 
    607 static ACPI_STATUS
    608 acpicpu_tstate_fadt(struct acpicpu_softc *sc)
    609 {
    610 	static const size_t size = sizeof(struct acpicpu_tstate);
    611 	const uint8_t offset = AcpiGbl_FADT.DutyOffset;
    612 	const uint8_t width = AcpiGbl_FADT.DutyWidth;
    613 	uint8_t beta, count, i;
    614 
    615 	if (sc->sc_object.ao_pblkaddr == 0)
    616 		return AE_AML_ILLEGAL_ADDRESS;
    617 
    618 	/*
    619 	 * A zero DUTY_WIDTH may be used announce
    620 	 * that T-states are not available via FADT
    621 	 * (ACPI 4.0, p. 121). See also (section 9.3):
    622 	 *
    623 	 *	Advanced Micro Devices: BIOS and Kernel
    624 	 *	Developer's Guide for AMD Athlon 64 and
    625 	 *	AMD Opteron Processors. Revision 3.30,
    626 	 *	February 2006.
    627 	 */
    628 	if (width == 0 || width + offset > 4)
    629 		return AE_AML_BAD_RESOURCE_VALUE;
    630 
    631 	count = 1 << width;
    632 
    633 	if (count > ACPICPU_T_STATE_MAX)
    634 		return AE_LIMIT;
    635 
    636 	if (sc->sc_tstate != NULL)
    637 		kmem_free(sc->sc_tstate, sc->sc_tstate_count * size);
    638 
    639 	sc->sc_tstate = kmem_zalloc(count * size, KM_SLEEP);
    640 
    641 	if (sc->sc_tstate == NULL)
    642 		return ENOMEM;
    643 
    644 	sc->sc_tstate_count = count;
    645 
    646 	/*
    647 	 * Approximate duty cycles and set the MSR values.
    648 	 */
    649 	for (beta = 100 / count, i = 0; i < count; i++) {
    650 		sc->sc_tstate[i].ts_percent = 100 - beta * i;
    651 		sc->sc_tstate[i].ts_latency = 1;
    652 	}
    653 
    654 	for (i = 1; i < count; i++)
    655 		sc->sc_tstate[i].ts_control = (count - i) | __BIT(3);
    656 
    657 	/*
    658 	 * Fake values for throttling registers.
    659 	 */
    660 	(void)memset(&sc->sc_tstate_status, 0, sizeof(struct acpicpu_reg));
    661 	(void)memset(&sc->sc_tstate_control, 0, sizeof(struct acpicpu_reg));
    662 
    663 	sc->sc_tstate_status.reg_bitwidth = width;
    664 	sc->sc_tstate_status.reg_bitoffset = offset;
    665 	sc->sc_tstate_status.reg_addr = sc->sc_object.ao_pblkaddr;
    666 	sc->sc_tstate_status.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO;
    667 
    668 	sc->sc_tstate_control.reg_bitwidth = width;
    669 	sc->sc_tstate_control.reg_bitoffset = offset;
    670 	sc->sc_tstate_control.reg_addr = sc->sc_object.ao_pblkaddr;
    671 	sc->sc_tstate_control.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO;
    672 
    673 	return AE_OK;
    674 }
    675 
    676 static ACPI_STATUS
    677 acpicpu_tstate_change(struct acpicpu_softc *sc)
    678 {
    679 	ACPI_INTEGER val;
    680 	ACPI_STATUS rv;
    681 
    682 	acpicpu_tstate_reset(sc);
    683 
    684 	/*
    685 	 * Evaluate the available T-state window:
    686 	 *
    687 	 *   _TPC : either this maximum or any lower power
    688 	 *          (i.e. higher numbered) state may be used.
    689 	 *
    690 	 *   _TDL : either this minimum or any higher power
    691 	 *	    (i.e. lower numbered) state may be used.
    692 	 *
    693 	 *   _TDL >= _TPC || _TDL >= _TSS[last entry].
    694 	 */
    695 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TPC", &val);
    696 
    697 	if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) {
    698 
    699 		if (sc->sc_tstate[val].ts_percent != 0)
    700 			sc->sc_tstate_max = val;
    701 	}
    702 
    703 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TDL", &val);
    704 
    705 	if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) {
    706 
    707 		if (val >= sc->sc_tstate_max &&
    708 		    sc->sc_tstate[val].ts_percent != 0)
    709 			sc->sc_tstate_min = val;
    710 	}
    711 
    712 	return AE_OK;
    713 }
    714 
    715 static void
    716 acpicpu_tstate_reset(struct acpicpu_softc *sc)
    717 {
    718 
    719 	sc->sc_tstate_max = 0;
    720 	sc->sc_tstate_min = sc->sc_tstate_count - 1;
    721 }
    722 
    723 int
    724 acpicpu_tstate_get(struct acpicpu_softc *sc, uint32_t *percent)
    725 {
    726 	const uint8_t method = sc->sc_tstate_control.reg_spaceid;
    727 	struct acpicpu_tstate *ts = NULL;
    728 	uint32_t i, val = 0;
    729 	uint8_t offset;
    730 	uint64_t addr;
    731 	int rv;
    732 
    733 	if (__predict_false(sc->sc_cold != false)) {
    734 		rv = EBUSY;
    735 		goto fail;
    736 	}
    737 
    738 	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) {
    739 		rv = ENODEV;
    740 		goto fail;
    741 	}
    742 
    743 	mutex_enter(&sc->sc_mtx);
    744 
    745 	if (sc->sc_tstate_current != ACPICPU_T_STATE_UNKNOWN) {
    746 		*percent = sc->sc_tstate_current;
    747 		mutex_exit(&sc->sc_mtx);
    748 		return 0;
    749 	}
    750 
    751 	mutex_exit(&sc->sc_mtx);
    752 
    753 	switch (method) {
    754 
    755 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
    756 
    757 		rv = acpicpu_md_tstate_get(sc, percent);
    758 
    759 		if (__predict_false(rv != 0))
    760 			goto fail;
    761 
    762 		break;
    763 
    764 	case ACPI_ADR_SPACE_SYSTEM_IO:
    765 
    766 		addr   = sc->sc_tstate_status.reg_addr;
    767 		offset = sc->sc_tstate_status.reg_bitoffset;
    768 
    769 		(void)AcpiOsReadPort(addr, &val, 8);
    770 
    771 		val = (val >> offset) & 0x0F;
    772 
    773 		for (i = 0; i < sc->sc_tstate_count; i++) {
    774 
    775 			if (sc->sc_tstate[i].ts_percent == 0)
    776 				continue;
    777 
    778 			if (val == sc->sc_tstate[i].ts_status) {
    779 				ts = &sc->sc_tstate[i];
    780 				break;
    781 			}
    782 		}
    783 
    784 		if (ts == NULL) {
    785 			rv = EIO;
    786 			goto fail;
    787 		}
    788 
    789 		*percent = ts->ts_percent;
    790 		break;
    791 
    792 	default:
    793 		rv = ENOTTY;
    794 		goto fail;
    795 	}
    796 
    797 	mutex_enter(&sc->sc_mtx);
    798 	sc->sc_tstate_current = *percent;
    799 	mutex_exit(&sc->sc_mtx);
    800 
    801 	return 0;
    802 
    803 fail:
    804 	aprint_error_dev(sc->sc_dev, "failed "
    805 	    "to get T-state (err %d)\n", rv);
    806 
    807 	mutex_enter(&sc->sc_mtx);
    808 	*percent = sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN;
    809 	mutex_exit(&sc->sc_mtx);
    810 
    811 	return rv;
    812 }
    813 
    814 int
    815 acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent)
    816 {
    817 	const uint8_t method = sc->sc_tstate_control.reg_spaceid;
    818 	struct acpicpu_tstate *ts = NULL;
    819 	uint32_t i, val;
    820 	uint8_t offset;
    821 	uint64_t addr;
    822 	int rv;
    823 
    824 	if (__predict_false(sc->sc_cold != false)) {
    825 		rv = EBUSY;
    826 		goto fail;
    827 	}
    828 
    829 	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) {
    830 		rv = ENODEV;
    831 		goto fail;
    832 	}
    833 
    834 	mutex_enter(&sc->sc_mtx);
    835 
    836 	if (sc->sc_tstate_current == percent) {
    837 		mutex_exit(&sc->sc_mtx);
    838 		return 0;
    839 	}
    840 
    841 	for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) {
    842 
    843 		if (__predict_false(sc->sc_tstate[i].ts_percent == 0))
    844 			continue;
    845 
    846 		if (sc->sc_tstate[i].ts_percent == percent) {
    847 			ts = &sc->sc_tstate[i];
    848 			break;
    849 		}
    850 	}
    851 
    852 	mutex_exit(&sc->sc_mtx);
    853 
    854 	if (__predict_false(ts == NULL)) {
    855 		rv = EINVAL;
    856 		goto fail;
    857 	}
    858 
    859 	switch (method) {
    860 
    861 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
    862 
    863 		rv = acpicpu_md_tstate_set(ts);
    864 
    865 		if (__predict_false(rv != 0))
    866 			goto fail;
    867 
    868 		break;
    869 
    870 	case ACPI_ADR_SPACE_SYSTEM_IO:
    871 
    872 		addr   = sc->sc_tstate_control.reg_addr;
    873 		offset = sc->sc_tstate_control.reg_bitoffset;
    874 
    875 		val = (ts->ts_control & 0x0F) << offset;
    876 
    877 		if (ts->ts_percent != 100 && (val & __BIT(4)) == 0) {
    878 			rv = EINVAL;
    879 			goto fail;
    880 		}
    881 
    882 		(void)AcpiOsWritePort(addr, val, 8);
    883 
    884 		/*
    885 		 * If the status field is zero, the transition is
    886 		 * specified to be "asynchronous" and there is no
    887 		 * need to check the status (ACPI 4.0, 8.4.3.2).
    888 		 */
    889 		if (ts->ts_status == 0)
    890 			break;
    891 
    892 		addr   = sc->sc_tstate_status.reg_addr;
    893 		offset = sc->sc_tstate_status.reg_bitoffset;
    894 
    895 		for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) {
    896 
    897 			(void)AcpiOsReadPort(addr, &val, 8);
    898 
    899 			val = (val >> offset) & 0x0F;
    900 
    901 			if (val == ts->ts_status)
    902 				break;
    903 
    904 			DELAY(ts->ts_latency);
    905 		}
    906 
    907 		if (i == ACPICPU_T_STATE_RETRY) {
    908 			rv = EAGAIN;
    909 			goto fail;
    910 		}
    911 
    912 		break;
    913 
    914 	default:
    915 		rv = ENOTTY;
    916 		goto fail;
    917 	}
    918 
    919 	mutex_enter(&sc->sc_mtx);
    920 	ts->ts_evcnt.ev_count++;
    921 	sc->sc_tstate_current = percent;
    922 	mutex_exit(&sc->sc_mtx);
    923 
    924 	return 0;
    925 
    926 fail:
    927 	aprint_error_dev(sc->sc_dev, "failed to "
    928 	    "throttle to %u %% (err %d)\n", percent, rv);
    929 
    930 	mutex_enter(&sc->sc_mtx);
    931 	sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN;
    932 	mutex_exit(&sc->sc_mtx);
    933 
    934 	return rv;
    935 }
    936