acpi_cpu_pstate.c revision 1.26.2.2 1 /* $NetBSD: acpi_cpu_pstate.c,v 1.26.2.2 2010/08/17 06:45:59 uebayasi 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_pstate.c,v 1.26.2.2 2010/08/17 06:45:59 uebayasi Exp $");
31
32 #include <sys/param.h>
33 #include <sys/evcnt.h>
34 #include <sys/kmem.h>
35 #include <sys/once.h>
36
37 #include <dev/acpi/acpireg.h>
38 #include <dev/acpi/acpivar.h>
39 #include <dev/acpi/acpi_cpu.h>
40
41 #define _COMPONENT ACPI_BUS_COMPONENT
42 ACPI_MODULE_NAME ("acpi_cpu_pstate")
43
44 static void acpicpu_pstate_attach_print(struct acpicpu_softc *);
45 static void acpicpu_pstate_attach_evcnt(struct acpicpu_softc *);
46 static void acpicpu_pstate_detach_evcnt(struct acpicpu_softc *);
47 static ACPI_STATUS acpicpu_pstate_pss(struct acpicpu_softc *);
48 static ACPI_STATUS acpicpu_pstate_pss_add(struct acpicpu_pstate *,
49 ACPI_OBJECT *);
50 static ACPI_STATUS acpicpu_pstate_xpss(struct acpicpu_softc *);
51 static ACPI_STATUS acpicpu_pstate_xpss_add(struct acpicpu_pstate *,
52 ACPI_OBJECT *);
53 static ACPI_STATUS acpicpu_pstate_pct(struct acpicpu_softc *);
54 static int acpicpu_pstate_max(struct acpicpu_softc *);
55 static void acpicpu_pstate_change(struct acpicpu_softc *);
56 static void acpicpu_pstate_bios(void);
57
58 void
59 acpicpu_pstate_attach(device_t self)
60 {
61 struct acpicpu_softc *sc = device_private(self);
62 const char *str;
63 ACPI_STATUS rv;
64
65 /*
66 * The ACPI 3.0 and 4.0 specifications mandate three
67 * objects for P-states: _PSS, _PCT, and _PPC. A less
68 * strict wording is however used in the earlier 2.0
69 * standard, and some systems conforming to ACPI 2.0
70 * do not have _PPC, the method for dynamic maximum.
71 */
72 rv = acpicpu_pstate_pss(sc);
73
74 if (ACPI_FAILURE(rv)) {
75 str = "_PSS";
76 goto fail;
77 }
78
79 /*
80 * Check the availability of extended _PSS.
81 * If present, this will override the data.
82 * Note that XPSS can not be used on Intel
83 * systems where _PDC or _OSC may be used.
84 */
85 if (sc->sc_cap == 0) {
86
87 rv = acpicpu_pstate_xpss(sc);
88
89 if (ACPI_SUCCESS(rv))
90 sc->sc_flags |= ACPICPU_FLAG_P_XPSS;
91
92 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) {
93 str = "XPSS";
94 goto fail;
95 }
96 }
97
98 rv = acpicpu_pstate_pct(sc);
99
100 if (ACPI_FAILURE(rv)) {
101 str = "_PCT";
102 goto fail;
103 }
104
105 (void)acpicpu_pstate_max(sc);
106
107 sc->sc_flags |= ACPICPU_FLAG_P;
108 sc->sc_pstate_current = sc->sc_pstate[0].ps_freq;
109
110 acpicpu_pstate_bios();
111 acpicpu_pstate_attach_evcnt(sc);
112 acpicpu_pstate_attach_print(sc);
113
114 return;
115
116 fail:
117 switch (rv) {
118
119 case AE_NOT_FOUND:
120 return;
121
122 case AE_SUPPORT:
123 aprint_verbose_dev(sc->sc_dev, "P-states not supported\n");
124 return;
125
126 default:
127 aprint_error_dev(sc->sc_dev, "failed to evaluate "
128 "%s: %s\n", str, AcpiFormatException(rv));
129 }
130 }
131
132 static void
133 acpicpu_pstate_attach_print(struct acpicpu_softc *sc)
134 {
135 const uint8_t method = sc->sc_pstate_control.reg_spaceid;
136 struct acpicpu_pstate *ps;
137 static bool once = false;
138 const char *str;
139 uint32_t i;
140
141 if (once != false)
142 return;
143
144 str = (method != ACPI_ADR_SPACE_SYSTEM_IO) ? "FFH" : "I/O";
145
146 for (i = 0; i < sc->sc_pstate_count; i++) {
147
148 ps = &sc->sc_pstate[i];
149
150 if (ps->ps_freq == 0)
151 continue;
152
153 aprint_debug_dev(sc->sc_dev, "P%d: %3s, "
154 "lat %3u us, pow %5u mW, %4u MHz\n", i, str,
155 ps->ps_latency, ps->ps_power, ps->ps_freq);
156 }
157
158 once = true;
159 }
160
161 static void
162 acpicpu_pstate_attach_evcnt(struct acpicpu_softc *sc)
163 {
164 struct acpicpu_pstate *ps;
165 uint32_t i;
166
167 for (i = 0; i < sc->sc_pstate_count; i++) {
168
169 ps = &sc->sc_pstate[i];
170
171 if (ps->ps_freq == 0)
172 continue;
173
174 (void)snprintf(ps->ps_name, sizeof(ps->ps_name),
175 "P%u (%u MHz)", i, ps->ps_freq);
176
177 evcnt_attach_dynamic(&ps->ps_evcnt, EVCNT_TYPE_MISC,
178 NULL, device_xname(sc->sc_dev), ps->ps_name);
179 }
180 }
181
182 int
183 acpicpu_pstate_detach(device_t self)
184 {
185 struct acpicpu_softc *sc = device_private(self);
186 static ONCE_DECL(once_detach);
187 size_t size;
188 int rv;
189
190 if ((sc->sc_flags & ACPICPU_FLAG_P) == 0)
191 return 0;
192
193 rv = RUN_ONCE(&once_detach, acpicpu_md_pstate_stop);
194
195 if (rv != 0)
196 return rv;
197
198 size = sc->sc_pstate_count * sizeof(*sc->sc_pstate);
199
200 if (sc->sc_pstate != NULL)
201 kmem_free(sc->sc_pstate, size);
202
203 sc->sc_flags &= ~ACPICPU_FLAG_P;
204 acpicpu_pstate_detach_evcnt(sc);
205
206 return 0;
207 }
208
209 static void
210 acpicpu_pstate_detach_evcnt(struct acpicpu_softc *sc)
211 {
212 struct acpicpu_pstate *ps;
213 uint32_t i;
214
215 for (i = 0; i < sc->sc_pstate_count; i++) {
216
217 ps = &sc->sc_pstate[i];
218
219 if (ps->ps_freq != 0)
220 evcnt_detach(&ps->ps_evcnt);
221 }
222 }
223
224 int
225 acpicpu_pstate_start(device_t self)
226 {
227 struct acpicpu_softc *sc = device_private(self);
228 static ONCE_DECL(once_start);
229
230 if ((sc->sc_flags & ACPICPU_FLAG_P) == 0)
231 return 0;
232
233 return RUN_ONCE(&once_start, acpicpu_md_pstate_start);
234 }
235
236 bool
237 acpicpu_pstate_suspend(device_t self)
238 {
239
240 return true;
241 }
242
243 bool
244 acpicpu_pstate_resume(device_t self)
245 {
246
247 acpicpu_pstate_callback(self);
248
249 return true;
250 }
251
252 void
253 acpicpu_pstate_callback(void *aux)
254 {
255 struct acpicpu_softc *sc;
256 device_t self = aux;
257 uint32_t old, new;
258
259 sc = device_private(self);
260
261 mutex_enter(&sc->sc_mtx);
262 old = sc->sc_pstate_max;
263 acpicpu_pstate_change(sc);
264 new = sc->sc_pstate_max;
265 mutex_exit(&sc->sc_mtx);
266
267 if (old != new) {
268
269 aprint_debug_dev(sc->sc_dev, "maximum frequency "
270 "changed from P%u (%u MHz) to P%u (%u MHz)\n",
271 old, sc->sc_pstate[old].ps_freq, new,
272 sc->sc_pstate[sc->sc_pstate_max].ps_freq);
273 #if 0
274 /*
275 * If the maximum changed, proactively
276 * raise or lower the target frequency.
277 */
278 acpicpu_pstate_set(sc, sc->sc_pstate[new].ps_freq);
279
280 #endif
281 }
282 }
283
284 ACPI_STATUS
285 acpicpu_pstate_pss(struct acpicpu_softc *sc)
286 {
287 struct acpicpu_pstate *ps;
288 ACPI_OBJECT *obj;
289 ACPI_BUFFER buf;
290 ACPI_STATUS rv;
291 uint32_t count;
292 uint32_t i, j;
293
294 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PSS", &buf);
295
296 if (ACPI_FAILURE(rv))
297 return rv;
298
299 obj = buf.Pointer;
300
301 if (obj->Type != ACPI_TYPE_PACKAGE) {
302 rv = AE_TYPE;
303 goto out;
304 }
305
306 sc->sc_pstate_count = obj->Package.Count;
307
308 if (sc->sc_pstate_count == 0) {
309 rv = AE_NOT_EXIST;
310 goto out;
311 }
312
313 if (sc->sc_pstate_count > ACPICPU_P_STATE_MAX) {
314 rv = AE_LIMIT;
315 goto out;
316 }
317
318 sc->sc_pstate = kmem_zalloc(sc->sc_pstate_count *
319 sizeof(struct acpicpu_pstate), KM_SLEEP);
320
321 if (sc->sc_pstate == NULL) {
322 rv = AE_NO_MEMORY;
323 goto out;
324 }
325
326 for (count = i = 0; i < sc->sc_pstate_count; i++) {
327
328 ps = &sc->sc_pstate[i];
329 rv = acpicpu_pstate_pss_add(ps, &obj->Package.Elements[i]);
330
331 if (ACPI_FAILURE(rv)) {
332 ps->ps_freq = 0;
333 continue;
334 }
335
336 for (j = 0; j < i; j++) {
337
338 if (ps->ps_freq >= sc->sc_pstate[j].ps_freq) {
339 ps->ps_freq = 0;
340 break;
341 }
342 }
343
344 if (ps->ps_freq != 0)
345 count++;
346 }
347
348 rv = (count != 0) ? AE_OK : AE_NOT_EXIST;
349
350 out:
351 if (buf.Pointer != NULL)
352 ACPI_FREE(buf.Pointer);
353
354 return rv;
355 }
356
357 static ACPI_STATUS
358 acpicpu_pstate_pss_add(struct acpicpu_pstate *ps, ACPI_OBJECT *obj)
359 {
360 ACPI_OBJECT *elm;
361 int i;
362
363 if (obj->Type != ACPI_TYPE_PACKAGE)
364 return AE_TYPE;
365
366 if (obj->Package.Count != 6)
367 return AE_BAD_DATA;
368
369 elm = obj->Package.Elements;
370
371 for (i = 0; i < 6; i++) {
372
373 if (elm[i].Type != ACPI_TYPE_INTEGER)
374 return AE_TYPE;
375
376 if (elm[i].Integer.Value > UINT32_MAX)
377 return AE_AML_NUMERIC_OVERFLOW;
378 }
379
380 ps->ps_freq = elm[0].Integer.Value;
381 ps->ps_power = elm[1].Integer.Value;
382 ps->ps_latency = elm[2].Integer.Value;
383 ps->ps_latency_bm = elm[3].Integer.Value;
384 ps->ps_control = elm[4].Integer.Value;
385 ps->ps_status = elm[5].Integer.Value;
386
387 if (ps->ps_freq == 0 || ps->ps_freq > 9999)
388 return AE_BAD_DECIMAL_CONSTANT;
389
390 /*
391 * The latency is typically around 10 usec
392 * on Intel CPUs. Use that as the minimum.
393 */
394 if (ps->ps_latency < 10)
395 ps->ps_latency = 10;
396
397 return AE_OK;
398 }
399
400 static ACPI_STATUS
401 acpicpu_pstate_xpss(struct acpicpu_softc *sc)
402 {
403 static const size_t size = sizeof(struct acpicpu_pstate);
404 struct acpicpu_pstate *ps;
405 ACPI_OBJECT *obj;
406 ACPI_BUFFER buf;
407 ACPI_STATUS rv;
408 uint32_t count;
409 uint32_t i, j;
410
411 rv = acpi_eval_struct(sc->sc_node->ad_handle, "XPSS", &buf);
412
413 if (ACPI_FAILURE(rv))
414 return rv;
415
416 obj = buf.Pointer;
417
418 if (obj->Type != ACPI_TYPE_PACKAGE) {
419 rv = AE_TYPE;
420 goto out;
421 }
422
423 count = obj->Package.Count;
424
425 if (count == 0) {
426 rv = AE_NOT_EXIST;
427 goto out;
428 }
429
430 if (count > ACPICPU_P_STATE_MAX) {
431 rv = AE_LIMIT;
432 goto out;
433 }
434
435 if (sc->sc_pstate != NULL)
436 kmem_free(sc->sc_pstate, sc->sc_pstate_count * size);
437
438 sc->sc_pstate = kmem_zalloc(count * size, KM_SLEEP);
439
440 if (sc->sc_pstate == NULL) {
441 rv = AE_NO_MEMORY;
442 goto out;
443 }
444
445 sc->sc_pstate_count = count;
446
447 for (count = i = 0; i < sc->sc_pstate_count; i++) {
448
449 ps = &sc->sc_pstate[i];
450 rv = acpicpu_pstate_xpss_add(ps, &obj->Package.Elements[i]);
451
452 if (ACPI_FAILURE(rv)) {
453 ps->ps_freq = 0;
454 continue;
455 }
456
457 for (j = 0; j < i; j++) {
458
459 if (ps->ps_freq >= sc->sc_pstate[j].ps_freq) {
460 ps->ps_freq = 0;
461 break;
462 }
463 }
464
465 if (ps->ps_freq != 0)
466 count++;
467 }
468
469 rv = (count != 0) ? AE_OK : AE_NOT_EXIST;
470
471 out:
472 if (buf.Pointer != NULL)
473 ACPI_FREE(buf.Pointer);
474
475 return rv;
476 }
477
478 static ACPI_STATUS
479 acpicpu_pstate_xpss_add(struct acpicpu_pstate *ps, ACPI_OBJECT *obj)
480 {
481 static const size_t size = sizeof(uint64_t);
482 ACPI_OBJECT *elm;
483 int i;
484
485 if (obj->Type != ACPI_TYPE_PACKAGE)
486 return AE_TYPE;
487
488 if (obj->Package.Count != 8)
489 return AE_BAD_DATA;
490
491 elm = obj->Package.Elements;
492
493 for (i = 0; i < 4; i++) {
494
495 if (elm[i].Type != ACPI_TYPE_INTEGER)
496 return AE_TYPE;
497
498 if (elm[i].Integer.Value > UINT32_MAX)
499 return AE_AML_NUMERIC_OVERFLOW;
500 }
501
502 for (; i < 8; i++) {
503
504 if (elm[i].Type != ACPI_TYPE_BUFFER)
505 return AE_TYPE;
506
507 if (elm[i].Buffer.Length > size)
508 return AE_LIMIT;
509 }
510
511 ps->ps_freq = elm[0].Integer.Value;
512 ps->ps_power = elm[1].Integer.Value;
513 ps->ps_latency = elm[2].Integer.Value;
514 ps->ps_latency_bm = elm[3].Integer.Value;
515
516 if (ps->ps_freq == 0 || ps->ps_freq > 9999)
517 return AE_BAD_DECIMAL_CONSTANT;
518
519 (void)memcpy(&ps->ps_control, elm[4].Buffer.Pointer, size);
520 (void)memcpy(&ps->ps_status, elm[5].Buffer.Pointer, size);
521
522 (void)memcpy(&ps->ps_control_mask, elm[6].Buffer.Pointer, size);
523 (void)memcpy(&ps->ps_status_mask, elm[7].Buffer.Pointer, size);
524
525 /*
526 * The latency is often defined to be
527 * zero on AMD systems. Raise that to 1.
528 */
529 if (ps->ps_latency == 0)
530 ps->ps_latency = 1;
531
532 ps->ps_flags |= ACPICPU_FLAG_P_XPSS;
533
534 return AE_OK;
535 }
536
537 ACPI_STATUS
538 acpicpu_pstate_pct(struct acpicpu_softc *sc)
539 {
540 static const size_t size = sizeof(struct acpicpu_reg);
541 struct acpicpu_reg *reg[2];
542 struct acpicpu_pstate *ps;
543 ACPI_OBJECT *elm, *obj;
544 ACPI_BUFFER buf;
545 ACPI_STATUS rv;
546 uint8_t width;
547 uint32_t i;
548
549 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PCT", &buf);
550
551 if (ACPI_FAILURE(rv))
552 return rv;
553
554 obj = buf.Pointer;
555
556 if (obj->Type != ACPI_TYPE_PACKAGE) {
557 rv = AE_TYPE;
558 goto out;
559 }
560
561 if (obj->Package.Count != 2) {
562 rv = AE_LIMIT;
563 goto out;
564 }
565
566 for (i = 0; i < 2; i++) {
567
568 elm = &obj->Package.Elements[i];
569
570 if (elm->Type != ACPI_TYPE_BUFFER) {
571 rv = AE_TYPE;
572 goto out;
573 }
574
575 if (size > elm->Buffer.Length) {
576 rv = AE_AML_BAD_RESOURCE_LENGTH;
577 goto out;
578 }
579
580 reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer;
581
582 switch (reg[i]->reg_spaceid) {
583
584 case ACPI_ADR_SPACE_SYSTEM_IO:
585
586 if (reg[i]->reg_addr == 0) {
587 rv = AE_AML_ILLEGAL_ADDRESS;
588 goto out;
589 }
590
591 width = reg[i]->reg_bitwidth;
592
593 if (width + reg[i]->reg_bitoffset > 32) {
594 rv = AE_AML_BAD_RESOURCE_VALUE;
595 goto out;
596 }
597
598 if (width != 8 && width != 16 && width != 32) {
599 rv = AE_AML_BAD_RESOURCE_VALUE;
600 goto out;
601 }
602
603 break;
604
605 case ACPI_ADR_SPACE_FIXED_HARDWARE:
606
607 /*
608 * With XPSS the _PCT registers incorporate
609 * the addresses of the appropriate MSRs.
610 */
611 if ((sc->sc_flags & ACPICPU_FLAG_P_XPSS) != 0) {
612
613 if (reg[i]->reg_bitwidth != 64) {
614 rv = AE_AML_BAD_RESOURCE_VALUE;
615 goto out;
616 }
617
618 if (reg[i]->reg_bitoffset != 0) {
619 rv = AE_AML_BAD_RESOURCE_VALUE;
620 goto out;
621 }
622
623 break;
624 }
625
626 if ((sc->sc_flags & ACPICPU_FLAG_P_FFH) == 0) {
627 rv = AE_SUPPORT;
628 goto out;
629 }
630
631 break;
632
633 default:
634 rv = AE_AML_INVALID_SPACE_ID;
635 goto out;
636 }
637 }
638
639 if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) {
640 rv = AE_AML_INVALID_SPACE_ID;
641 goto out;
642 }
643
644 (void)memcpy(&sc->sc_pstate_control, reg[0], size);
645 (void)memcpy(&sc->sc_pstate_status, reg[1], size);
646
647 if ((sc->sc_flags & ACPICPU_FLAG_P_XPSS) == 0)
648 goto out;
649
650 /*
651 * In XPSS the control address can not be zero,
652 * but the status address may be. Comparable to
653 * T-states, in this we can ignore the status
654 * check during the P-state (FFH) transition.
655 */
656 if (sc->sc_pstate_control.reg_addr == 0) {
657 rv = AE_AML_BAD_RESOURCE_LENGTH;
658 goto out;
659 }
660
661 /*
662 * If XPSS is present, copy the MSR addresses
663 * to the P-state structures for convenience.
664 */
665 for (i = 0; i < sc->sc_pstate_count; i++) {
666
667 ps = &sc->sc_pstate[i];
668
669 if (ps->ps_freq == 0)
670 continue;
671
672 ps->ps_status_addr = sc->sc_pstate_status.reg_addr;
673 ps->ps_control_addr = sc->sc_pstate_control.reg_addr;
674 }
675
676 out:
677 if (buf.Pointer != NULL)
678 ACPI_FREE(buf.Pointer);
679
680 return rv;
681 }
682
683 static int
684 acpicpu_pstate_max(struct acpicpu_softc *sc)
685 {
686 ACPI_INTEGER val;
687 ACPI_STATUS rv;
688
689 /*
690 * Evaluate the currently highest P-state that can be used.
691 * If available, we can use either this state or any lower
692 * power (i.e. higher numbered) state from the _PSS object.
693 */
694 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PPC", &val);
695
696 sc->sc_pstate_max = 0;
697
698 if (ACPI_FAILURE(rv))
699 return 1;
700
701 if (val > sc->sc_pstate_count - 1)
702 return 1;
703
704 if (sc->sc_pstate[val].ps_freq == 0)
705 return 1;
706
707 sc->sc_pstate_max = val;
708
709 return 0;
710 }
711
712 static void
713 acpicpu_pstate_change(struct acpicpu_softc *sc)
714 {
715 ACPI_OBJECT_LIST arg;
716 ACPI_OBJECT obj[2];
717
718 arg.Count = 2;
719 arg.Pointer = obj;
720
721 obj[0].Type = ACPI_TYPE_INTEGER;
722 obj[1].Type = ACPI_TYPE_INTEGER;
723
724 obj[0].Integer.Value = ACPICPU_P_NOTIFY;
725 obj[1].Integer.Value = acpicpu_pstate_max(sc);
726
727 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "_OST", &arg, NULL);
728 }
729
730 static void
731 acpicpu_pstate_bios(void)
732 {
733 const uint8_t val = AcpiGbl_FADT.PstateControl;
734 const uint32_t addr = AcpiGbl_FADT.SmiCommand;
735
736 if (addr == 0 || val == 0)
737 return;
738
739 (void)AcpiOsWritePort(addr, val, 8);
740 }
741
742 int
743 acpicpu_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
744 {
745 const uint8_t method = sc->sc_pstate_control.reg_spaceid;
746 struct acpicpu_pstate *ps = NULL;
747 uint32_t i, val = 0;
748 uint64_t addr;
749 uint8_t width;
750 int rv;
751
752 if (sc->sc_cold != false) {
753 rv = EBUSY;
754 goto fail;
755 }
756
757 if ((sc->sc_flags & ACPICPU_FLAG_P) == 0) {
758 rv = ENODEV;
759 goto fail;
760 }
761
762 mutex_enter(&sc->sc_mtx);
763
764 if (sc->sc_pstate_current != ACPICPU_P_STATE_UNKNOWN) {
765 *freq = sc->sc_pstate_current;
766 mutex_exit(&sc->sc_mtx);
767 return 0;
768 }
769
770 mutex_exit(&sc->sc_mtx);
771
772 switch (method) {
773
774 case ACPI_ADR_SPACE_FIXED_HARDWARE:
775
776 rv = acpicpu_md_pstate_get(sc, freq);
777
778 if (rv != 0)
779 goto fail;
780
781 break;
782
783 case ACPI_ADR_SPACE_SYSTEM_IO:
784
785 addr = sc->sc_pstate_status.reg_addr;
786 width = sc->sc_pstate_status.reg_bitwidth;
787
788 (void)AcpiOsReadPort(addr, &val, width);
789
790 if (val == 0) {
791 rv = EIO;
792 goto fail;
793 }
794
795 for (i = 0; i < sc->sc_pstate_count; i++) {
796
797 if (sc->sc_pstate[i].ps_freq == 0)
798 continue;
799
800 if (val == sc->sc_pstate[i].ps_status) {
801 ps = &sc->sc_pstate[i];
802 break;
803 }
804 }
805
806 if (__predict_false(ps == NULL)) {
807 rv = EIO;
808 goto fail;
809 }
810
811 *freq = ps->ps_freq;
812 break;
813
814 default:
815 rv = ENOTTY;
816 goto fail;
817 }
818
819 mutex_enter(&sc->sc_mtx);
820 sc->sc_pstate_current = *freq;
821 mutex_exit(&sc->sc_mtx);
822
823 return 0;
824
825 fail:
826 aprint_error_dev(sc->sc_dev, "failed "
827 "to get frequency (err %d)\n", rv);
828
829 mutex_enter(&sc->sc_mtx);
830 *freq = sc->sc_pstate_current = ACPICPU_P_STATE_UNKNOWN;
831 mutex_exit(&sc->sc_mtx);
832
833 return rv;
834 }
835
836 int
837 acpicpu_pstate_set(struct acpicpu_softc *sc, uint32_t freq)
838 {
839 const uint8_t method = sc->sc_pstate_control.reg_spaceid;
840 struct acpicpu_pstate *ps = NULL;
841 uint32_t i, val;
842 uint64_t addr;
843 uint8_t width;
844 int rv;
845
846 if (sc->sc_cold != false) {
847 rv = EBUSY;
848 goto fail;
849 }
850
851 if ((sc->sc_flags & ACPICPU_FLAG_P) == 0) {
852 rv = ENODEV;
853 goto fail;
854 }
855
856 mutex_enter(&sc->sc_mtx);
857
858 for (i = sc->sc_pstate_max; i < sc->sc_pstate_count; i++) {
859
860 if (sc->sc_pstate[i].ps_freq == 0)
861 continue;
862
863 if (sc->sc_pstate[i].ps_freq == freq) {
864 ps = &sc->sc_pstate[i];
865 break;
866 }
867 }
868
869 mutex_exit(&sc->sc_mtx);
870
871 if (__predict_false(ps == NULL)) {
872 rv = EINVAL;
873 goto fail;
874 }
875
876 switch (method) {
877
878 case ACPI_ADR_SPACE_FIXED_HARDWARE:
879
880 rv = acpicpu_md_pstate_set(ps);
881
882 if (rv != 0)
883 goto fail;
884
885 break;
886
887 case ACPI_ADR_SPACE_SYSTEM_IO:
888
889 addr = sc->sc_pstate_control.reg_addr;
890 width = sc->sc_pstate_control.reg_bitwidth;
891
892 (void)AcpiOsWritePort(addr, ps->ps_control, width);
893
894 addr = sc->sc_pstate_status.reg_addr;
895 width = sc->sc_pstate_status.reg_bitwidth;
896
897 /*
898 * Some systems take longer to respond
899 * than the reported worst-case latency.
900 */
901 for (i = val = 0; i < ACPICPU_P_STATE_RETRY; i++) {
902
903 (void)AcpiOsReadPort(addr, &val, width);
904
905 if (val == ps->ps_status)
906 break;
907
908 DELAY(ps->ps_latency);
909 }
910
911 if (i == ACPICPU_P_STATE_RETRY) {
912 rv = EAGAIN;
913 goto fail;
914 }
915
916 break;
917
918 default:
919 rv = ENOTTY;
920 goto fail;
921 }
922
923 mutex_enter(&sc->sc_mtx);
924 ps->ps_evcnt.ev_count++;
925 sc->sc_pstate_current = freq;
926 mutex_exit(&sc->sc_mtx);
927
928 return 0;
929
930 fail:
931 aprint_error_dev(sc->sc_dev, "failed to set "
932 "frequency to %u (err %d)\n", freq, rv);
933
934 mutex_enter(&sc->sc_mtx);
935 sc->sc_pstate_current = ACPICPU_P_STATE_UNKNOWN;
936 mutex_exit(&sc->sc_mtx);
937
938 return rv;
939 }
940