xref: /netbsd-src/sys/dev/acpi/acpi_cpu_tstate.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /* $NetBSD: acpi_cpu_tstate.c,v 1.18 2010/12/30 12:05:02 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.18 2010/12/30 12:05:02 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 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TDL", &val);
609 
610 	if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) {
611 
612 		if (val >= sc->sc_tstate_max &&
613 		    sc->sc_tstate[val].ts_percent != 0)
614 			sc->sc_tstate_min = val;
615 	}
616 
617 	return AE_OK;
618 }
619 
620 static void
621 acpicpu_tstate_reset(struct acpicpu_softc *sc)
622 {
623 
624 	sc->sc_tstate_max = 0;
625 	sc->sc_tstate_min = sc->sc_tstate_count - 1;
626 }
627 
628 int
629 acpicpu_tstate_get(struct acpicpu_softc *sc, uint32_t *percent)
630 {
631 	const uint8_t method = sc->sc_tstate_control.reg_spaceid;
632 	struct acpicpu_tstate *ts = NULL;
633 	uint32_t i, val = 0;
634 	uint8_t offset;
635 	uint64_t addr;
636 	int rv;
637 
638 	if (__predict_false(sc->sc_cold != false)) {
639 		rv = EBUSY;
640 		goto fail;
641 	}
642 
643 	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) {
644 		rv = ENODEV;
645 		goto fail;
646 	}
647 
648 	mutex_enter(&sc->sc_mtx);
649 
650 	if (sc->sc_tstate_current != ACPICPU_T_STATE_UNKNOWN) {
651 		*percent = sc->sc_tstate_current;
652 		mutex_exit(&sc->sc_mtx);
653 		return 0;
654 	}
655 
656 	mutex_exit(&sc->sc_mtx);
657 
658 	switch (method) {
659 
660 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
661 
662 		rv = acpicpu_md_tstate_get(sc, percent);
663 
664 		if (__predict_false(rv != 0))
665 			goto fail;
666 
667 		break;
668 
669 	case ACPI_ADR_SPACE_SYSTEM_IO:
670 
671 		addr   = sc->sc_tstate_status.reg_addr;
672 		offset = sc->sc_tstate_status.reg_bitoffset;
673 
674 		(void)AcpiOsReadPort(addr, &val, 8);
675 
676 		val = (val >> offset) & 0x0F;
677 
678 		for (i = 0; i < sc->sc_tstate_count; i++) {
679 
680 			if (sc->sc_tstate[i].ts_percent == 0)
681 				continue;
682 
683 			if (val == sc->sc_tstate[i].ts_status) {
684 				ts = &sc->sc_tstate[i];
685 				break;
686 			}
687 		}
688 
689 		if (ts == NULL) {
690 			rv = EIO;
691 			goto fail;
692 		}
693 
694 		*percent = ts->ts_percent;
695 		break;
696 
697 	default:
698 		rv = ENOTTY;
699 		goto fail;
700 	}
701 
702 	mutex_enter(&sc->sc_mtx);
703 	sc->sc_tstate_current = *percent;
704 	mutex_exit(&sc->sc_mtx);
705 
706 	return 0;
707 
708 fail:
709 	aprint_error_dev(sc->sc_dev, "failed "
710 	    "to get T-state (err %d)\n", rv);
711 
712 	mutex_enter(&sc->sc_mtx);
713 	*percent = sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN;
714 	mutex_exit(&sc->sc_mtx);
715 
716 	return rv;
717 }
718 
719 int
720 acpicpu_tstate_set(struct acpicpu_softc *sc, uint32_t percent)
721 {
722 	const uint8_t method = sc->sc_tstate_control.reg_spaceid;
723 	struct acpicpu_tstate *ts = NULL;
724 	uint32_t i, val;
725 	uint8_t offset;
726 	uint64_t addr;
727 	int rv;
728 
729 	if (__predict_false(sc->sc_cold != false)) {
730 		rv = EBUSY;
731 		goto fail;
732 	}
733 
734 	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) {
735 		rv = ENODEV;
736 		goto fail;
737 	}
738 
739 	mutex_enter(&sc->sc_mtx);
740 
741 	if (sc->sc_tstate_current == percent) {
742 		mutex_exit(&sc->sc_mtx);
743 		return 0;
744 	}
745 
746 	for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) {
747 
748 		if (__predict_false(sc->sc_tstate[i].ts_percent == 0))
749 			continue;
750 
751 		if (sc->sc_tstate[i].ts_percent == percent) {
752 			ts = &sc->sc_tstate[i];
753 			break;
754 		}
755 	}
756 
757 	mutex_exit(&sc->sc_mtx);
758 
759 	if (__predict_false(ts == NULL)) {
760 		rv = EINVAL;
761 		goto fail;
762 	}
763 
764 	switch (method) {
765 
766 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
767 
768 		rv = acpicpu_md_tstate_set(ts);
769 
770 		if (__predict_false(rv != 0))
771 			goto fail;
772 
773 		break;
774 
775 	case ACPI_ADR_SPACE_SYSTEM_IO:
776 
777 		addr   = sc->sc_tstate_control.reg_addr;
778 		offset = sc->sc_tstate_control.reg_bitoffset;
779 
780 		val = (ts->ts_control & 0x0F) << offset;
781 
782 		if (ts->ts_percent != 100 && (val & __BIT(4)) == 0) {
783 			rv = EINVAL;
784 			goto fail;
785 		}
786 
787 		(void)AcpiOsWritePort(addr, val, 8);
788 
789 		/*
790 		 * If the status field is zero, the transition is
791 		 * specified to be "asynchronous" and there is no
792 		 * need to check the status (ACPI 4.0, 8.4.3.2).
793 		 */
794 		if (ts->ts_status == 0)
795 			break;
796 
797 		addr   = sc->sc_tstate_status.reg_addr;
798 		offset = sc->sc_tstate_status.reg_bitoffset;
799 
800 		for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) {
801 
802 			(void)AcpiOsReadPort(addr, &val, 8);
803 
804 			val = (val >> offset) & 0x0F;
805 
806 			if (val == ts->ts_status)
807 				break;
808 
809 			DELAY(ts->ts_latency);
810 		}
811 
812 		if (i == ACPICPU_T_STATE_RETRY) {
813 			rv = EAGAIN;
814 			goto fail;
815 		}
816 
817 		break;
818 
819 	default:
820 		rv = ENOTTY;
821 		goto fail;
822 	}
823 
824 	mutex_enter(&sc->sc_mtx);
825 	ts->ts_evcnt.ev_count++;
826 	sc->sc_tstate_current = percent;
827 	mutex_exit(&sc->sc_mtx);
828 
829 	return 0;
830 
831 fail:
832 	aprint_error_dev(sc->sc_dev, "failed to "
833 	    "throttle to %u %% (err %d)\n", percent, rv);
834 
835 	mutex_enter(&sc->sc_mtx);
836 	sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN;
837 	mutex_exit(&sc->sc_mtx);
838 
839 	return rv;
840 }
841