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