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