1/* $NetBSD: acpi_cpu_tstate.c,v 1.29 2011/06/22 08:05:10 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.29 2011/06/22 08:05:10 jruoho Exp $");
31
32#include <sys/param.h>
33#include <sys/kmem.h>
34#include <sys/xcall.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
41ACPI_MODULE_NAME	 ("acpi_cpu_tstate")
42
43static ACPI_STATUS	 acpicpu_tstate_tss(struct acpicpu_softc *);
44static ACPI_STATUS	 acpicpu_tstate_tss_add(struct acpicpu_tstate *,
45						ACPI_OBJECT *);
46static ACPI_STATUS	 acpicpu_tstate_ptc(struct acpicpu_softc *);
47static ACPI_STATUS	 acpicpu_tstate_dep(struct acpicpu_softc *);
48static ACPI_STATUS	 acpicpu_tstate_fadt(struct acpicpu_softc *);
49static ACPI_STATUS	 acpicpu_tstate_change(struct acpicpu_softc *);
50static void		 acpicpu_tstate_reset(struct acpicpu_softc *);
51static void		 acpicpu_tstate_set_xcall(void *, void *);
52
53extern struct acpicpu_softc **acpicpu_sc;
54
55void
56acpicpu_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
103out:
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}
122
123void
124acpicpu_tstate_detach(device_t self)
125{
126	struct acpicpu_softc *sc = device_private(self);
127	size_t size;
128
129	if ((sc->sc_flags & ACPICPU_FLAG_T) == 0)
130		return;
131
132	size = sc->sc_tstate_count * sizeof(*sc->sc_tstate);
133
134	if (sc->sc_tstate != NULL)
135		kmem_free(sc->sc_tstate, size);
136
137	sc->sc_flags &= ~ACPICPU_FLAG_T;
138}
139
140void
141acpicpu_tstate_start(device_t self)
142{
143	/* Nothing. */
144}
145
146void
147acpicpu_tstate_suspend(void *aux)
148{
149	struct acpicpu_softc *sc;
150	device_t self = aux;
151
152	sc = device_private(self);
153
154	mutex_enter(&sc->sc_mtx);
155	acpicpu_tstate_reset(sc);
156	mutex_exit(&sc->sc_mtx);
157}
158
159void
160acpicpu_tstate_resume(void *aux)
161{
162	/* Nothing. */
163}
164
165void
166acpicpu_tstate_callback(void *aux)
167{
168	struct acpicpu_softc *sc;
169	device_t self = aux;
170	uint32_t omax, omin;
171	int i;
172
173	sc = device_private(self);
174
175	if ((sc->sc_flags & ACPICPU_FLAG_T_FADT) != 0)
176		return;
177
178	mutex_enter(&sc->sc_mtx);
179
180	/*
181	 * If P-states are in use, we should ignore
182	 * the interrupt unless we are in the highest
183	 * P-state (see ACPI 4.0, section 8.4.3.3).
184	 */
185	if ((sc->sc_flags & ACPICPU_FLAG_P) != 0) {
186
187		for (i = sc->sc_pstate_count - 1; i >= 0; i--) {
188
189			if (sc->sc_pstate[i].ps_freq != 0)
190				break;
191		}
192
193		if (sc->sc_pstate_current != sc->sc_pstate[i].ps_freq) {
194			mutex_exit(&sc->sc_mtx);
195			return;
196		}
197	}
198
199	omax = sc->sc_tstate_max;
200	omin = sc->sc_tstate_min;
201
202	(void)acpicpu_tstate_change(sc);
203
204	if (omax != sc->sc_tstate_max || omin != sc->sc_tstate_min) {
205
206		aprint_debug_dev(sc->sc_dev, "throttling window "
207		    "changed from %u-%u %% to %u-%u %%\n",
208		    sc->sc_tstate[omax].ts_percent,
209		    sc->sc_tstate[omin].ts_percent,
210		    sc->sc_tstate[sc->sc_tstate_max].ts_percent,
211		    sc->sc_tstate[sc->sc_tstate_min].ts_percent);
212	}
213
214	mutex_exit(&sc->sc_mtx);
215}
216
217static ACPI_STATUS
218acpicpu_tstate_tss(struct acpicpu_softc *sc)
219{
220	struct acpicpu_tstate *ts;
221	ACPI_OBJECT *obj;
222	ACPI_BUFFER buf;
223	ACPI_STATUS rv;
224	uint32_t count;
225	uint32_t i, j;
226
227	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSS", &buf);
228
229	if (ACPI_FAILURE(rv))
230		return rv;
231
232	obj = buf.Pointer;
233
234	if (obj->Type != ACPI_TYPE_PACKAGE) {
235		rv = AE_TYPE;
236		goto out;
237	}
238
239	sc->sc_tstate_count = obj->Package.Count;
240
241	if (sc->sc_tstate_count == 0) {
242		rv = AE_NOT_EXIST;
243		goto out;
244	}
245
246	if (sc->sc_tstate_count > ACPICPU_T_STATE_MAX) {
247		rv = AE_LIMIT;
248		goto out;
249	}
250
251	sc->sc_tstate = kmem_zalloc(sc->sc_tstate_count *
252	    sizeof(struct acpicpu_tstate), KM_SLEEP);
253
254	if (sc->sc_tstate == NULL) {
255		rv = AE_NO_MEMORY;
256		goto out;
257	}
258
259	for (count = i = 0; i < sc->sc_tstate_count; i++) {
260
261		ts = &sc->sc_tstate[i];
262		rv = acpicpu_tstate_tss_add(ts, &obj->Package.Elements[i]);
263
264		if (ACPI_FAILURE(rv)) {
265			ts->ts_percent = 0;
266			continue;
267		}
268
269		for (j = 0; j < i; j++) {
270
271			if (ts->ts_percent >= sc->sc_tstate[j].ts_percent) {
272				ts->ts_percent = 0;
273				break;
274			}
275		}
276
277		if (ts->ts_percent != 0)
278			count++;
279	}
280
281	if (count == 0) {
282		rv = AE_NOT_EXIST;
283		goto out;
284	}
285
286	/*
287	 * There must be an entry with the percent
288	 * field of 100. If this is not true, and if
289	 * this entry is not in the expected index,
290	 * invalidate the use of T-states via _TSS.
291	 */
292	if (sc->sc_tstate[0].ts_percent != 100) {
293		rv = AE_BAD_DECIMAL_CONSTANT;
294		goto out;
295	}
296
297out:
298	if (buf.Pointer != NULL)
299		ACPI_FREE(buf.Pointer);
300
301	return rv;
302}
303
304static ACPI_STATUS
305acpicpu_tstate_tss_add(struct acpicpu_tstate *ts, ACPI_OBJECT *obj)
306{
307	ACPI_OBJECT *elm;
308	uint32_t val[5];
309	uint32_t *p;
310	int i;
311
312	if (obj->Type != ACPI_TYPE_PACKAGE)
313		return AE_TYPE;
314
315	if (obj->Package.Count != 5)
316		return AE_BAD_DATA;
317
318	elm = obj->Package.Elements;
319
320	for (i = 0; i < 5; i++) {
321
322		if (elm[i].Type != ACPI_TYPE_INTEGER)
323			return AE_TYPE;
324
325		if (elm[i].Integer.Value > UINT32_MAX)
326			return AE_AML_NUMERIC_OVERFLOW;
327
328		val[i] = elm[i].Integer.Value;
329	}
330
331	p = &ts->ts_percent;
332
333	for (i = 0; i < 5; i++, p++)
334		*p = val[i];
335
336	/*
337	 * The minimum should be around 100 / 8 = 12.5 %.
338	 */
339        if (ts->ts_percent < 10 || ts->ts_percent > 100)
340		return AE_BAD_DECIMAL_CONSTANT;
341
342	if (ts->ts_latency == 0 || ts->ts_latency > 1000)
343		ts->ts_latency = 1;
344
345	return AE_OK;
346}
347
348ACPI_STATUS
349acpicpu_tstate_ptc(struct acpicpu_softc *sc)
350{
351	static const size_t size = sizeof(struct acpicpu_reg);
352	struct acpicpu_reg *reg[2];
353	ACPI_OBJECT *elm, *obj;
354	ACPI_BUFFER buf;
355	ACPI_STATUS rv;
356	int i;
357
358	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PTC", &buf);
359
360	if (ACPI_FAILURE(rv))
361		return rv;
362
363	obj = buf.Pointer;
364
365	if (obj->Type != ACPI_TYPE_PACKAGE) {
366		rv = AE_TYPE;
367		goto out;
368	}
369
370	if (obj->Package.Count != 2) {
371		rv = AE_LIMIT;
372		goto out;
373	}
374
375	for (i = 0; i < 2; i++) {
376
377		elm = &obj->Package.Elements[i];
378
379		if (elm->Type != ACPI_TYPE_BUFFER) {
380			rv = AE_TYPE;
381			goto out;
382		}
383
384		if (size > elm->Buffer.Length) {
385			rv = AE_AML_BAD_RESOURCE_LENGTH;
386			goto out;
387		}
388
389		reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer;
390
391		switch (reg[i]->reg_spaceid) {
392
393		case ACPI_ADR_SPACE_SYSTEM_IO:
394
395			if (reg[i]->reg_addr == 0) {
396				rv = AE_AML_ILLEGAL_ADDRESS;
397				goto out;
398			}
399
400			/*
401			 * Check that the values match the IA32 clock
402			 * modulation MSR, where the bit 0 is reserved,
403			 * bits 1 through 3 define the duty cycle, and
404			 * the fourth bit enables the modulation.
405			 */
406			if (reg[i]->reg_bitwidth != 4) {
407				rv = AE_AML_BAD_RESOURCE_VALUE;
408				goto out;
409			}
410
411			if (reg[i]->reg_bitoffset != 1) {
412				rv = AE_AML_BAD_RESOURCE_VALUE;
413				goto out;
414			}
415
416			break;
417
418		case ACPI_ADR_SPACE_FIXED_HARDWARE:
419
420			if ((sc->sc_flags & ACPICPU_FLAG_T_FFH) == 0) {
421				rv = AE_SUPPORT;
422				goto out;
423			}
424
425			break;
426
427		default:
428			rv = AE_AML_INVALID_SPACE_ID;
429			goto out;
430		}
431	}
432
433	if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) {
434		rv = AE_AML_INVALID_SPACE_ID;
435		goto out;
436	}
437
438	(void)memcpy(&sc->sc_tstate_control, reg[0], size);
439	(void)memcpy(&sc->sc_tstate_status,  reg[1], size);
440
441out:
442	if (buf.Pointer != NULL)
443		ACPI_FREE(buf.Pointer);
444
445	return rv;
446}
447
448static ACPI_STATUS
449acpicpu_tstate_dep(struct acpicpu_softc *sc)
450{
451	ACPI_OBJECT *elm, *obj;
452	ACPI_BUFFER buf;
453	ACPI_STATUS rv;
454	uint32_t val;
455	uint8_t i, n;
456
457	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSD", &buf);
458
459	if (ACPI_FAILURE(rv))
460		goto out;
461
462	obj = buf.Pointer;
463
464	if (obj->Type != ACPI_TYPE_PACKAGE) {
465		rv = AE_TYPE;
466		goto out;
467	}
468
469	if (obj->Package.Count != 1) {
470		rv = AE_LIMIT;
471		goto out;
472	}
473
474	elm = &obj->Package.Elements[0];
475
476	if (obj->Type != ACPI_TYPE_PACKAGE) {
477		rv = AE_TYPE;
478		goto out;
479	}
480
481	n = elm->Package.Count;
482
483	if (n != 5) {
484		rv = AE_LIMIT;
485		goto out;
486	}
487
488	elm = elm->Package.Elements;
489
490	for (i = 0; i < n; i++) {
491
492		if (elm[i].Type != ACPI_TYPE_INTEGER) {
493			rv = AE_TYPE;
494			goto out;
495		}
496
497		if (elm[i].Integer.Value > UINT32_MAX) {
498			rv = AE_AML_NUMERIC_OVERFLOW;
499			goto out;
500		}
501	}
502
503	val = elm[1].Integer.Value;
504
505	if (val != 0)
506		aprint_debug_dev(sc->sc_dev, "invalid revision in _TSD\n");
507
508	val = elm[3].Integer.Value;
509
510	if (val < ACPICPU_DEP_SW_ALL || val > ACPICPU_DEP_HW_ALL) {
511		rv = AE_AML_BAD_RESOURCE_VALUE;
512		goto out;
513	}
514
515	val = elm[4].Integer.Value;
516
517	if (val > sc->sc_ncpus) {
518		rv = AE_BAD_VALUE;
519		goto out;
520	}
521
522	sc->sc_tstate_dep.dep_domain = elm[2].Integer.Value;
523	sc->sc_tstate_dep.dep_type   = elm[3].Integer.Value;
524	sc->sc_tstate_dep.dep_ncpus  = elm[4].Integer.Value;
525
526out:
527	if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND)
528		aprint_debug_dev(sc->sc_dev, "failed to evaluate "
529		    "_TSD: %s\n", AcpiFormatException(rv));
530
531	if (buf.Pointer != NULL)
532		ACPI_FREE(buf.Pointer);
533
534	return rv;
535}
536
537static ACPI_STATUS
538acpicpu_tstate_fadt(struct acpicpu_softc *sc)
539{
540	static const size_t size = sizeof(struct acpicpu_tstate);
541	const uint8_t offset = AcpiGbl_FADT.DutyOffset;
542	const uint8_t width = AcpiGbl_FADT.DutyWidth;
543	uint8_t beta, count, i;
544
545	if (sc->sc_object.ao_pblkaddr == 0)
546		return AE_AML_ILLEGAL_ADDRESS;
547
548	/*
549	 * A zero DUTY_WIDTH may be used announce
550	 * that T-states are not available via FADT
551	 * (ACPI 4.0, p. 121). See also (section 9.3):
552	 *
553	 *	Advanced Micro Devices: BIOS and Kernel
554	 *	Developer's Guide for AMD Athlon 64 and
555	 *	AMD Opteron Processors. Revision 3.30,
556	 *	February 2006.
557	 */
558	if (width == 0 || width + offset > 4)
559		return AE_AML_BAD_RESOURCE_VALUE;
560
561	count = 1 << width;
562
563	if (count > ACPICPU_T_STATE_MAX)
564		return AE_LIMIT;
565
566	if (sc->sc_tstate != NULL)
567		kmem_free(sc->sc_tstate, sc->sc_tstate_count * size);
568
569	sc->sc_tstate = kmem_zalloc(count * size, KM_SLEEP);
570
571	if (sc->sc_tstate == NULL)
572		return ENOMEM;
573
574	sc->sc_tstate_count = count;
575
576	/*
577	 * Approximate duty cycles and set the MSR values.
578	 */
579	for (beta = 100 / count, i = 0; i < count; i++) {
580		sc->sc_tstate[i].ts_percent = 100 - beta * i;
581		sc->sc_tstate[i].ts_latency = 1;
582	}
583
584	for (i = 1; i < count; i++)
585		sc->sc_tstate[i].ts_control = (count - i) | __BIT(3);
586
587	/*
588	 * Fake values for throttling registers.
589	 */
590	(void)memset(&sc->sc_tstate_status, 0, sizeof(struct acpicpu_reg));
591	(void)memset(&sc->sc_tstate_control, 0, sizeof(struct acpicpu_reg));
592
593	sc->sc_tstate_status.reg_bitwidth = width;
594	sc->sc_tstate_status.reg_bitoffset = offset;
595	sc->sc_tstate_status.reg_addr = sc->sc_object.ao_pblkaddr;
596	sc->sc_tstate_status.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO;
597
598	sc->sc_tstate_control.reg_bitwidth = width;
599	sc->sc_tstate_control.reg_bitoffset = offset;
600	sc->sc_tstate_control.reg_addr = sc->sc_object.ao_pblkaddr;
601	sc->sc_tstate_control.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO;
602
603	return AE_OK;
604}
605
606static ACPI_STATUS
607acpicpu_tstate_change(struct acpicpu_softc *sc)
608{
609	ACPI_INTEGER val;
610	ACPI_STATUS rv;
611
612	acpicpu_tstate_reset(sc);
613
614	/*
615	 * Evaluate the available T-state window:
616	 *
617	 *   _TPC : either this maximum or any lower power
618	 *          (i.e. higher numbered) state may be used.
619	 *
620	 *   _TDL : either this minimum or any higher power
621	 *	    (i.e. lower numbered) state may be used.
622	 *
623	 *   _TDL >= _TPC || _TDL >= _TSS[last entry].
624	 */
625	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TPC", &val);
626
627	if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) {
628
629		if (sc->sc_tstate[val].ts_percent != 0)
630			sc->sc_tstate_max = val;
631	}
632
633	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TDL", &val);
634
635	if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) {
636
637		if (val >= sc->sc_tstate_max &&
638		    sc->sc_tstate[val].ts_percent != 0)
639			sc->sc_tstate_min = val;
640	}
641
642	return AE_OK;
643}
644
645static void
646acpicpu_tstate_reset(struct acpicpu_softc *sc)
647{
648
649	sc->sc_tstate_max = 0;
650	sc->sc_tstate_min = sc->sc_tstate_count - 1;
651}
652
653int
654acpicpu_tstate_get(struct cpu_info *ci, uint32_t *percent)
655{
656	struct acpicpu_tstate *ts = NULL;
657	struct acpicpu_softc *sc;
658	uint32_t i, val = 0;
659	uint8_t offset;
660	uint64_t addr;
661	int rv;
662
663	sc = acpicpu_sc[ci->ci_acpiid];
664
665	if (__predict_false(sc == NULL)) {
666		rv = ENXIO;
667		goto fail;
668	}
669
670	if (__predict_false(sc->sc_cold != false)) {
671		rv = EBUSY;
672		goto fail;
673	}
674
675	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) {
676		rv = ENODEV;
677		goto fail;
678	}
679
680	mutex_enter(&sc->sc_mtx);
681
682	if (sc->sc_tstate_current != ACPICPU_T_STATE_UNKNOWN) {
683		*percent = sc->sc_tstate_current;
684		mutex_exit(&sc->sc_mtx);
685		return 0;
686	}
687
688	mutex_exit(&sc->sc_mtx);
689
690	switch (sc->sc_tstate_status.reg_spaceid) {
691
692	case ACPI_ADR_SPACE_FIXED_HARDWARE:
693
694		rv = acpicpu_md_tstate_get(sc, percent);
695
696		if (__predict_false(rv != 0))
697			goto fail;
698
699		break;
700
701	case ACPI_ADR_SPACE_SYSTEM_IO:
702
703		addr   = sc->sc_tstate_status.reg_addr;
704		offset = sc->sc_tstate_status.reg_bitoffset;
705
706		(void)AcpiOsReadPort(addr, &val, 8);
707
708		val = (val >> offset) & 0x0F;
709
710		for (i = 0; i < sc->sc_tstate_count; i++) {
711
712			if (sc->sc_tstate[i].ts_percent == 0)
713				continue;
714
715			if (val == sc->sc_tstate[i].ts_status) {
716				ts = &sc->sc_tstate[i];
717				break;
718			}
719		}
720
721		if (ts == NULL) {
722			rv = EIO;
723			goto fail;
724		}
725
726		*percent = ts->ts_percent;
727		break;
728
729	default:
730		rv = ENOTTY;
731		goto fail;
732	}
733
734	mutex_enter(&sc->sc_mtx);
735	sc->sc_tstate_current = *percent;
736	mutex_exit(&sc->sc_mtx);
737
738	return 0;
739
740fail:
741	aprint_error_dev(sc->sc_dev, "failed "
742	    "to get T-state (err %d)\n", rv);
743
744	mutex_enter(&sc->sc_mtx);
745	*percent = sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN;
746	mutex_exit(&sc->sc_mtx);
747
748	return rv;
749}
750
751void
752acpicpu_tstate_set(struct cpu_info *ci, uint32_t percent)
753{
754	uint64_t xc;
755
756	xc = xc_broadcast(0, acpicpu_tstate_set_xcall, &percent, NULL);
757	xc_wait(xc);
758}
759
760static void
761acpicpu_tstate_set_xcall(void *arg1, void *arg2)
762{
763	struct acpicpu_tstate *ts = NULL;
764	struct cpu_info *ci = curcpu();
765	struct acpicpu_softc *sc;
766	uint32_t i, percent, val;
767	uint8_t offset;
768	uint64_t addr;
769	int rv;
770
771	percent = *(uint32_t *)arg1;
772	sc = acpicpu_sc[ci->ci_acpiid];
773
774	if (__predict_false(sc == NULL)) {
775		rv = ENXIO;
776		goto fail;
777	}
778
779	if (__predict_false(sc->sc_cold != false)) {
780		rv = EBUSY;
781		goto fail;
782	}
783
784	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) {
785		rv = ENODEV;
786		goto fail;
787	}
788
789	mutex_enter(&sc->sc_mtx);
790
791	if (sc->sc_tstate_current == percent) {
792		mutex_exit(&sc->sc_mtx);
793		return;
794	}
795
796	for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) {
797
798		if (__predict_false(sc->sc_tstate[i].ts_percent == 0))
799			continue;
800
801		if (sc->sc_tstate[i].ts_percent == percent) {
802			ts = &sc->sc_tstate[i];
803			break;
804		}
805	}
806
807	mutex_exit(&sc->sc_mtx);
808
809	if (__predict_false(ts == NULL)) {
810		rv = EINVAL;
811		goto fail;
812	}
813
814	switch (sc->sc_tstate_control.reg_spaceid) {
815
816	case ACPI_ADR_SPACE_FIXED_HARDWARE:
817
818		rv = acpicpu_md_tstate_set(ts);
819
820		if (__predict_false(rv != 0))
821			goto fail;
822
823		break;
824
825	case ACPI_ADR_SPACE_SYSTEM_IO:
826
827		addr   = sc->sc_tstate_control.reg_addr;
828		offset = sc->sc_tstate_control.reg_bitoffset;
829
830		val = (ts->ts_control & 0x0F) << offset;
831
832		if (ts->ts_percent != 100 && (val & __BIT(4)) == 0) {
833			rv = EINVAL;
834			goto fail;
835		}
836
837		(void)AcpiOsWritePort(addr, val, 8);
838
839		/*
840		 * If the status field is zero, the transition is
841		 * specified to be "asynchronous" and there is no
842		 * need to check the status (ACPI 4.0, 8.4.3.2).
843		 */
844		if (ts->ts_status == 0)
845			break;
846
847		addr   = sc->sc_tstate_status.reg_addr;
848		offset = sc->sc_tstate_status.reg_bitoffset;
849
850		for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) {
851
852			(void)AcpiOsReadPort(addr, &val, 8);
853
854			val = (val >> offset) & 0x0F;
855
856			if (val == ts->ts_status)
857				break;
858
859			DELAY(ts->ts_latency);
860		}
861
862		if (i == ACPICPU_T_STATE_RETRY) {
863			rv = EAGAIN;
864			goto fail;
865		}
866
867		break;
868
869	default:
870		rv = ENOTTY;
871		goto fail;
872	}
873
874	mutex_enter(&sc->sc_mtx);
875	ts->ts_evcnt.ev_count++;
876	sc->sc_tstate_current = percent;
877	mutex_exit(&sc->sc_mtx);
878
879	return;
880
881fail:
882	if (rv != EINVAL)
883		aprint_error_dev(sc->sc_dev, "failed to "
884		    "throttle to %u %% (err %d)\n", percent, rv);
885
886	mutex_enter(&sc->sc_mtx);
887	sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN;
888	mutex_exit(&sc->sc_mtx);
889}
890