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