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