1 | /* $NetBSD: acpi_cpu_pstate.c,v 1.53 2011/11/15 07:43:37 jruoho Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2010, 2011 Jukka Ruohonen <jruohonen@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.53 2011/11/15 07:43:37 jruoho Exp $" ); |
31 | |
32 | #include <sys/param.h> |
33 | #include <sys/cpufreq.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_pstate" ) |
42 | |
43 | static ACPI_STATUS acpicpu_pstate_pss(struct acpicpu_softc *); |
44 | static ACPI_STATUS acpicpu_pstate_pss_add(struct acpicpu_pstate *, |
45 | ACPI_OBJECT *); |
46 | static ACPI_STATUS acpicpu_pstate_xpss(struct acpicpu_softc *); |
47 | static ACPI_STATUS acpicpu_pstate_xpss_add(struct acpicpu_pstate *, |
48 | ACPI_OBJECT *); |
49 | static ACPI_STATUS acpicpu_pstate_pct(struct acpicpu_softc *); |
50 | static ACPI_STATUS acpicpu_pstate_dep(struct acpicpu_softc *); |
51 | static int acpicpu_pstate_max(struct acpicpu_softc *); |
52 | static int acpicpu_pstate_min(struct acpicpu_softc *); |
53 | static void acpicpu_pstate_change(struct acpicpu_softc *); |
54 | static void acpicpu_pstate_reset(struct acpicpu_softc *); |
55 | static void acpicpu_pstate_bios(void); |
56 | |
57 | extern struct acpicpu_softc **acpicpu_sc; |
58 | |
59 | void |
60 | acpicpu_pstate_attach(device_t self) |
61 | { |
62 | struct acpicpu_softc *sc = device_private(self); |
63 | const char *str; |
64 | ACPI_HANDLE tmp; |
65 | ACPI_STATUS rv; |
66 | |
67 | rv = acpicpu_pstate_pss(sc); |
68 | |
69 | if (ACPI_FAILURE(rv)) { |
70 | str = "_PSS" ; |
71 | goto fail; |
72 | } |
73 | |
74 | /* |
75 | * Append additional information from the extended _PSS, |
76 | * if available. Note that XPSS can not be used on Intel |
77 | * systems that use either _PDC or _OSC. From the XPSS |
78 | * method specification: |
79 | * |
80 | * "The platform must not require the use of the |
81 | * optional _PDC or _OSC methods to coordinate |
82 | * between the operating system and firmware for |
83 | * the purposes of enabling specific processor |
84 | * power management features or implementations." |
85 | */ |
86 | if (sc->sc_cap == 0) { |
87 | |
88 | rv = acpicpu_pstate_xpss(sc); |
89 | |
90 | if (ACPI_SUCCESS(rv)) |
91 | sc->sc_flags |= ACPICPU_FLAG_P_XPSS; |
92 | } |
93 | |
94 | rv = acpicpu_pstate_pct(sc); |
95 | |
96 | if (ACPI_FAILURE(rv)) { |
97 | str = "_PCT" ; |
98 | goto fail; |
99 | } |
100 | |
101 | /* |
102 | * The ACPI 3.0 and 4.0 specifications mandate three |
103 | * objects for P-states: _PSS, _PCT, and _PPC. A less |
104 | * strict wording is however used in the earlier 2.0 |
105 | * standard, and some systems conforming to ACPI 2.0 |
106 | * do not have _PPC, the method for dynamic maximum. |
107 | */ |
108 | rv = AcpiGetHandle(sc->sc_node->ad_handle, "_PPC" , &tmp); |
109 | |
110 | if (ACPI_FAILURE(rv)) |
111 | aprint_debug_dev(self, "_PPC missing\n" ); |
112 | |
113 | /* |
114 | * Carry out MD initialization. |
115 | */ |
116 | rv = acpicpu_md_pstate_init(sc); |
117 | |
118 | if (rv != 0) { |
119 | rv = AE_SUPPORT; |
120 | goto fail; |
121 | } |
122 | |
123 | /* |
124 | * Query the optional _PSD. |
125 | */ |
126 | rv = acpicpu_pstate_dep(sc); |
127 | |
128 | if (ACPI_SUCCESS(rv)) |
129 | sc->sc_flags |= ACPICPU_FLAG_P_DEP; |
130 | |
131 | sc->sc_pstate_current = 0; |
132 | sc->sc_flags |= ACPICPU_FLAG_P; |
133 | |
134 | acpicpu_pstate_bios(); |
135 | acpicpu_pstate_reset(sc); |
136 | |
137 | return; |
138 | |
139 | fail: |
140 | switch (rv) { |
141 | |
142 | case AE_NOT_FOUND: |
143 | return; |
144 | |
145 | case AE_SUPPORT: |
146 | aprint_verbose_dev(self, "P-states not supported\n" ); |
147 | return; |
148 | |
149 | default: |
150 | aprint_error_dev(self, "failed to evaluate " |
151 | "%s: %s\n" , str, AcpiFormatException(rv)); |
152 | } |
153 | } |
154 | |
155 | void |
156 | acpicpu_pstate_detach(device_t self) |
157 | { |
158 | struct acpicpu_softc *sc = device_private(self); |
159 | size_t size; |
160 | |
161 | if ((sc->sc_flags & ACPICPU_FLAG_P) == 0) |
162 | return; |
163 | |
164 | (void)acpicpu_md_pstate_stop(); |
165 | |
166 | size = sc->sc_pstate_count * sizeof(*sc->sc_pstate); |
167 | |
168 | if (sc->sc_pstate != NULL) |
169 | kmem_free(sc->sc_pstate, size); |
170 | |
171 | sc->sc_flags &= ~ACPICPU_FLAG_P; |
172 | } |
173 | |
174 | void |
175 | acpicpu_pstate_start(device_t self) |
176 | { |
177 | struct acpicpu_softc *sc = device_private(self); |
178 | |
179 | if (acpicpu_md_pstate_start(sc) == 0) |
180 | return; |
181 | |
182 | sc->sc_flags &= ~ACPICPU_FLAG_P; |
183 | aprint_error_dev(self, "failed to start P-states\n" ); |
184 | } |
185 | |
186 | void |
187 | acpicpu_pstate_suspend(void *aux) |
188 | { |
189 | struct acpicpu_softc *sc; |
190 | device_t self = aux; |
191 | |
192 | /* |
193 | * Reset any dynamic limits. |
194 | */ |
195 | sc = device_private(self); |
196 | mutex_enter(&sc->sc_mtx); |
197 | acpicpu_pstate_reset(sc); |
198 | mutex_exit(&sc->sc_mtx); |
199 | } |
200 | |
201 | void |
202 | acpicpu_pstate_resume(void *aux) |
203 | { |
204 | /* Nothing. */ |
205 | } |
206 | |
207 | void |
208 | acpicpu_pstate_callback(void *aux) |
209 | { |
210 | struct acpicpu_softc *sc; |
211 | device_t self = aux; |
212 | uint32_t freq; |
213 | |
214 | sc = device_private(self); |
215 | mutex_enter(&sc->sc_mtx); |
216 | acpicpu_pstate_change(sc); |
217 | |
218 | freq = sc->sc_pstate[sc->sc_pstate_max].ps_freq; |
219 | |
220 | if (sc->sc_pstate_saved == 0) |
221 | sc->sc_pstate_saved = sc->sc_pstate_current; |
222 | |
223 | if (sc->sc_pstate_saved <= freq) { |
224 | freq = sc->sc_pstate_saved; |
225 | sc->sc_pstate_saved = 0; |
226 | } |
227 | |
228 | mutex_exit(&sc->sc_mtx); |
229 | cpufreq_set(sc->sc_ci, freq); |
230 | } |
231 | |
232 | static ACPI_STATUS |
233 | acpicpu_pstate_pss(struct acpicpu_softc *sc) |
234 | { |
235 | struct acpicpu_pstate *ps; |
236 | ACPI_OBJECT *obj; |
237 | ACPI_BUFFER buf; |
238 | ACPI_STATUS rv; |
239 | uint32_t count; |
240 | uint32_t i, j; |
241 | |
242 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PSS" , &buf); |
243 | |
244 | if (ACPI_FAILURE(rv)) |
245 | return rv; |
246 | |
247 | obj = buf.Pointer; |
248 | |
249 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
250 | rv = AE_TYPE; |
251 | goto out; |
252 | } |
253 | |
254 | sc->sc_pstate_count = obj->Package.Count; |
255 | |
256 | if (sc->sc_pstate_count == 0) { |
257 | rv = AE_NOT_EXIST; |
258 | goto out; |
259 | } |
260 | |
261 | if (sc->sc_pstate_count > ACPICPU_P_STATE_MAX) { |
262 | rv = AE_LIMIT; |
263 | goto out; |
264 | } |
265 | |
266 | sc->sc_pstate = kmem_zalloc(sc->sc_pstate_count * |
267 | sizeof(struct acpicpu_pstate), KM_SLEEP); |
268 | |
269 | if (sc->sc_pstate == NULL) { |
270 | rv = AE_NO_MEMORY; |
271 | goto out; |
272 | } |
273 | |
274 | for (count = i = 0; i < sc->sc_pstate_count; i++) { |
275 | |
276 | ps = &sc->sc_pstate[i]; |
277 | rv = acpicpu_pstate_pss_add(ps, &obj->Package.Elements[i]); |
278 | |
279 | if (ACPI_FAILURE(rv)) { |
280 | aprint_error_dev(sc->sc_dev, "failed to add " |
281 | "P-state: %s\n" , AcpiFormatException(rv)); |
282 | ps->ps_freq = 0; |
283 | continue; |
284 | } |
285 | |
286 | for (j = 0; j < i; j++) { |
287 | |
288 | if (ps->ps_freq >= sc->sc_pstate[j].ps_freq) { |
289 | ps->ps_freq = 0; |
290 | break; |
291 | } |
292 | } |
293 | |
294 | if (ps->ps_freq != 0) |
295 | count++; |
296 | } |
297 | |
298 | rv = (count != 0) ? AE_OK : AE_NOT_EXIST; |
299 | |
300 | out: |
301 | if (buf.Pointer != NULL) |
302 | ACPI_FREE(buf.Pointer); |
303 | |
304 | return rv; |
305 | } |
306 | |
307 | static ACPI_STATUS |
308 | acpicpu_pstate_pss_add(struct acpicpu_pstate *ps, ACPI_OBJECT *obj) |
309 | { |
310 | ACPI_OBJECT *elm; |
311 | int i; |
312 | |
313 | if (obj->Type != ACPI_TYPE_PACKAGE) |
314 | return AE_TYPE; |
315 | |
316 | if (obj->Package.Count != 6) |
317 | return AE_BAD_DATA; |
318 | |
319 | elm = obj->Package.Elements; |
320 | |
321 | for (i = 0; i < 6; i++) { |
322 | |
323 | if (elm[i].Type != ACPI_TYPE_INTEGER) |
324 | return AE_TYPE; |
325 | |
326 | if (elm[i].Integer.Value > UINT32_MAX) |
327 | return AE_AML_NUMERIC_OVERFLOW; |
328 | } |
329 | |
330 | ps->ps_freq = elm[0].Integer.Value; |
331 | ps->ps_power = elm[1].Integer.Value; |
332 | ps->ps_latency = elm[2].Integer.Value; |
333 | ps->ps_latency_bm = elm[3].Integer.Value; |
334 | ps->ps_control = elm[4].Integer.Value; |
335 | ps->ps_status = elm[5].Integer.Value; |
336 | |
337 | if (ps->ps_freq == 0 || ps->ps_freq > 9999) |
338 | return AE_BAD_DECIMAL_CONSTANT; |
339 | |
340 | /* |
341 | * Sanity check also the latency levels. Some systems may |
342 | * report a value zero, but we keep one microsecond as the |
343 | * lower bound; see for instance AMD family 12h, |
344 | * |
345 | * Advanced Micro Devices: BIOS and Kernel Developer's |
346 | * Guide (BKDG) for AMD Family 12h Processors. Section |
347 | * 2.5.3.1.9.2, Revision 3.02, October, 2011. |
348 | */ |
349 | if (ps->ps_latency == 0 || ps->ps_latency > 1000) |
350 | ps->ps_latency = 1; |
351 | |
352 | return AE_OK; |
353 | } |
354 | |
355 | static ACPI_STATUS |
356 | acpicpu_pstate_xpss(struct acpicpu_softc *sc) |
357 | { |
358 | struct acpicpu_pstate *ps; |
359 | ACPI_OBJECT *obj; |
360 | ACPI_BUFFER buf; |
361 | ACPI_STATUS rv; |
362 | uint32_t i = 0; |
363 | |
364 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "XPSS" , &buf); |
365 | |
366 | if (ACPI_FAILURE(rv)) |
367 | goto out; |
368 | |
369 | obj = buf.Pointer; |
370 | |
371 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
372 | rv = AE_TYPE; |
373 | goto out; |
374 | } |
375 | |
376 | if (obj->Package.Count != sc->sc_pstate_count) { |
377 | rv = AE_LIMIT; |
378 | goto out; |
379 | } |
380 | |
381 | while (i < sc->sc_pstate_count) { |
382 | |
383 | ps = &sc->sc_pstate[i]; |
384 | acpicpu_pstate_xpss_add(ps, &obj->Package.Elements[i]); |
385 | |
386 | i++; |
387 | } |
388 | |
389 | out: |
390 | if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) |
391 | aprint_error_dev(sc->sc_dev, "failed to evaluate " |
392 | "XPSS: %s\n" , AcpiFormatException(rv)); |
393 | |
394 | if (buf.Pointer != NULL) |
395 | ACPI_FREE(buf.Pointer); |
396 | |
397 | return rv; |
398 | } |
399 | |
400 | static ACPI_STATUS |
401 | acpicpu_pstate_xpss_add(struct acpicpu_pstate *ps, ACPI_OBJECT *obj) |
402 | { |
403 | ACPI_OBJECT *elm; |
404 | int i; |
405 | |
406 | if (obj->Type != ACPI_TYPE_PACKAGE) |
407 | return AE_TYPE; |
408 | |
409 | if (obj->Package.Count != 8) |
410 | return AE_BAD_DATA; |
411 | |
412 | elm = obj->Package.Elements; |
413 | |
414 | for (i = 0; i < 4; i++) { |
415 | |
416 | if (elm[i].Type != ACPI_TYPE_INTEGER) |
417 | return AE_TYPE; |
418 | |
419 | if (elm[i].Integer.Value > UINT32_MAX) |
420 | return AE_AML_NUMERIC_OVERFLOW; |
421 | } |
422 | |
423 | for (; i < 8; i++) { |
424 | |
425 | if (elm[i].Type != ACPI_TYPE_BUFFER) |
426 | return AE_TYPE; |
427 | |
428 | if (elm[i].Buffer.Length != 8) |
429 | return AE_LIMIT; |
430 | } |
431 | |
432 | /* |
433 | * Only overwrite the elements that were |
434 | * not available from the conventional _PSS. |
435 | */ |
436 | if (ps->ps_freq == 0) |
437 | ps->ps_freq = elm[0].Integer.Value; |
438 | |
439 | if (ps->ps_power == 0) |
440 | ps->ps_power = elm[1].Integer.Value; |
441 | |
442 | if (ps->ps_latency == 0) |
443 | ps->ps_latency = elm[2].Integer.Value; |
444 | |
445 | if (ps->ps_latency_bm == 0) |
446 | ps->ps_latency_bm = elm[3].Integer.Value; |
447 | |
448 | if (ps->ps_control == 0) |
449 | ps->ps_control = ACPI_GET64(elm[4].Buffer.Pointer); |
450 | |
451 | if (ps->ps_status == 0) |
452 | ps->ps_status = ACPI_GET64(elm[5].Buffer.Pointer); |
453 | |
454 | if (ps->ps_control_mask == 0) |
455 | ps->ps_control_mask = ACPI_GET64(elm[6].Buffer.Pointer); |
456 | |
457 | if (ps->ps_status_mask == 0) |
458 | ps->ps_status_mask = ACPI_GET64(elm[7].Buffer.Pointer); |
459 | |
460 | ps->ps_flags |= ACPICPU_FLAG_P_XPSS; |
461 | |
462 | if (ps->ps_freq == 0 || ps->ps_freq > 9999) |
463 | return AE_BAD_DECIMAL_CONSTANT; |
464 | |
465 | if (ps->ps_latency == 0 || ps->ps_latency > 1000) |
466 | ps->ps_latency = 1; |
467 | |
468 | return AE_OK; |
469 | } |
470 | |
471 | static ACPI_STATUS |
472 | acpicpu_pstate_pct(struct acpicpu_softc *sc) |
473 | { |
474 | static const size_t size = sizeof(struct acpicpu_reg); |
475 | struct acpicpu_reg *reg[2]; |
476 | struct acpicpu_pstate *ps; |
477 | ACPI_OBJECT *elm, *obj; |
478 | ACPI_BUFFER buf; |
479 | ACPI_STATUS rv; |
480 | uint8_t width; |
481 | uint32_t i; |
482 | |
483 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PCT" , &buf); |
484 | |
485 | if (ACPI_FAILURE(rv)) |
486 | return rv; |
487 | |
488 | obj = buf.Pointer; |
489 | |
490 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
491 | rv = AE_TYPE; |
492 | goto out; |
493 | } |
494 | |
495 | if (obj->Package.Count != 2) { |
496 | rv = AE_LIMIT; |
497 | goto out; |
498 | } |
499 | |
500 | for (i = 0; i < 2; i++) { |
501 | |
502 | elm = &obj->Package.Elements[i]; |
503 | |
504 | if (elm->Type != ACPI_TYPE_BUFFER) { |
505 | rv = AE_TYPE; |
506 | goto out; |
507 | } |
508 | |
509 | if (size > elm->Buffer.Length) { |
510 | rv = AE_AML_BAD_RESOURCE_LENGTH; |
511 | goto out; |
512 | } |
513 | |
514 | reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer; |
515 | |
516 | switch (reg[i]->reg_spaceid) { |
517 | |
518 | case ACPI_ADR_SPACE_SYSTEM_IO: |
519 | |
520 | if (reg[i]->reg_addr == 0) { |
521 | rv = AE_AML_ILLEGAL_ADDRESS; |
522 | goto out; |
523 | } |
524 | |
525 | width = reg[i]->reg_bitwidth; |
526 | |
527 | if (width + reg[i]->reg_bitoffset > 32) { |
528 | rv = AE_AML_BAD_RESOURCE_VALUE; |
529 | goto out; |
530 | } |
531 | |
532 | if (width != 8 && width != 16 && width != 32) { |
533 | rv = AE_AML_BAD_RESOURCE_VALUE; |
534 | goto out; |
535 | } |
536 | |
537 | break; |
538 | |
539 | case ACPI_ADR_SPACE_FIXED_HARDWARE: |
540 | |
541 | if ((sc->sc_flags & ACPICPU_FLAG_P_XPSS) != 0) { |
542 | |
543 | if (reg[i]->reg_bitwidth != 64) { |
544 | rv = AE_AML_BAD_RESOURCE_VALUE; |
545 | goto out; |
546 | } |
547 | |
548 | if (reg[i]->reg_bitoffset != 0) { |
549 | rv = AE_AML_BAD_RESOURCE_VALUE; |
550 | goto out; |
551 | } |
552 | |
553 | break; |
554 | } |
555 | |
556 | if ((sc->sc_flags & ACPICPU_FLAG_P_FFH) == 0) { |
557 | rv = AE_SUPPORT; |
558 | goto out; |
559 | } |
560 | |
561 | break; |
562 | |
563 | default: |
564 | rv = AE_AML_INVALID_SPACE_ID; |
565 | goto out; |
566 | } |
567 | } |
568 | |
569 | if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) { |
570 | rv = AE_AML_INVALID_SPACE_ID; |
571 | goto out; |
572 | } |
573 | |
574 | (void)memcpy(&sc->sc_pstate_control, reg[0], size); |
575 | (void)memcpy(&sc->sc_pstate_status, reg[1], size); |
576 | |
577 | if ((sc->sc_flags & ACPICPU_FLAG_P_XPSS) != 0) { |
578 | |
579 | /* |
580 | * At the very least, mandate that |
581 | * XPSS supplies the control address. |
582 | */ |
583 | if (sc->sc_pstate_control.reg_addr == 0) { |
584 | rv = AE_AML_BAD_RESOURCE_LENGTH; |
585 | goto out; |
586 | } |
587 | |
588 | /* |
589 | * If XPSS is present, copy the supplied |
590 | * MSR addresses to the P-state structures. |
591 | */ |
592 | for (i = 0; i < sc->sc_pstate_count; i++) { |
593 | |
594 | ps = &sc->sc_pstate[i]; |
595 | |
596 | if (ps->ps_freq == 0) |
597 | continue; |
598 | |
599 | ps->ps_status_addr = sc->sc_pstate_status.reg_addr; |
600 | ps->ps_control_addr = sc->sc_pstate_control.reg_addr; |
601 | } |
602 | } |
603 | |
604 | out: |
605 | if (buf.Pointer != NULL) |
606 | ACPI_FREE(buf.Pointer); |
607 | |
608 | return rv; |
609 | } |
610 | |
611 | static ACPI_STATUS |
612 | acpicpu_pstate_dep(struct acpicpu_softc *sc) |
613 | { |
614 | ACPI_OBJECT *elm, *obj; |
615 | ACPI_BUFFER buf; |
616 | ACPI_STATUS rv; |
617 | uint32_t val; |
618 | uint8_t i, n; |
619 | |
620 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PSD" , &buf); |
621 | |
622 | if (ACPI_FAILURE(rv)) |
623 | goto out; |
624 | |
625 | obj = buf.Pointer; |
626 | |
627 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
628 | rv = AE_TYPE; |
629 | goto out; |
630 | } |
631 | |
632 | if (obj->Package.Count != 1) { |
633 | rv = AE_LIMIT; |
634 | goto out; |
635 | } |
636 | |
637 | elm = &obj->Package.Elements[0]; |
638 | |
639 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
640 | rv = AE_TYPE; |
641 | goto out; |
642 | } |
643 | |
644 | n = elm->Package.Count; |
645 | |
646 | if (n != 5) { |
647 | rv = AE_LIMIT; |
648 | goto out; |
649 | } |
650 | |
651 | elm = elm->Package.Elements; |
652 | |
653 | for (i = 0; i < n; i++) { |
654 | |
655 | if (elm[i].Type != ACPI_TYPE_INTEGER) { |
656 | rv = AE_TYPE; |
657 | goto out; |
658 | } |
659 | |
660 | if (elm[i].Integer.Value > UINT32_MAX) { |
661 | rv = AE_AML_NUMERIC_OVERFLOW; |
662 | goto out; |
663 | } |
664 | } |
665 | |
666 | val = elm[1].Integer.Value; |
667 | |
668 | if (val != 0) |
669 | aprint_debug_dev(sc->sc_dev, "invalid revision in _PSD\n" ); |
670 | |
671 | val = elm[3].Integer.Value; |
672 | |
673 | if (val < ACPICPU_DEP_SW_ALL || val > ACPICPU_DEP_HW_ALL) { |
674 | rv = AE_AML_BAD_RESOURCE_VALUE; |
675 | goto out; |
676 | } |
677 | |
678 | val = elm[4].Integer.Value; |
679 | |
680 | if (val > sc->sc_ncpus) { |
681 | rv = AE_BAD_VALUE; |
682 | goto out; |
683 | } |
684 | |
685 | sc->sc_pstate_dep.dep_domain = elm[2].Integer.Value; |
686 | sc->sc_pstate_dep.dep_type = elm[3].Integer.Value; |
687 | sc->sc_pstate_dep.dep_ncpus = elm[4].Integer.Value; |
688 | |
689 | out: |
690 | if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) |
691 | aprint_debug_dev(sc->sc_dev, "failed to evaluate " |
692 | "_PSD: %s\n" , AcpiFormatException(rv)); |
693 | |
694 | if (buf.Pointer != NULL) |
695 | ACPI_FREE(buf.Pointer); |
696 | |
697 | return rv; |
698 | } |
699 | |
700 | static int |
701 | acpicpu_pstate_max(struct acpicpu_softc *sc) |
702 | { |
703 | ACPI_INTEGER val; |
704 | ACPI_STATUS rv; |
705 | |
706 | /* |
707 | * Evaluate the currently highest P-state that can be used. |
708 | * If available, we can use either this state or any lower |
709 | * power (i.e. higher numbered) state from the _PSS object. |
710 | * Note that the return value must match the _OST parameter. |
711 | */ |
712 | rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PPC" , &val); |
713 | |
714 | if (ACPI_SUCCESS(rv) && val < sc->sc_pstate_count) { |
715 | |
716 | if (sc->sc_pstate[val].ps_freq != 0) { |
717 | sc->sc_pstate_max = val; |
718 | return 0; |
719 | } |
720 | } |
721 | |
722 | return 1; |
723 | } |
724 | |
725 | static int |
726 | acpicpu_pstate_min(struct acpicpu_softc *sc) |
727 | { |
728 | ACPI_INTEGER val; |
729 | ACPI_STATUS rv; |
730 | |
731 | /* |
732 | * The _PDL object defines the minimum when passive cooling |
733 | * is being performed. If available, we can use the returned |
734 | * state or any higher power (i.e. lower numbered) state. |
735 | */ |
736 | rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PDL" , &val); |
737 | |
738 | if (ACPI_SUCCESS(rv) && val < sc->sc_pstate_count) { |
739 | |
740 | if (sc->sc_pstate[val].ps_freq == 0) |
741 | return 1; |
742 | |
743 | if (val >= sc->sc_pstate_max) { |
744 | sc->sc_pstate_min = val; |
745 | return 0; |
746 | } |
747 | } |
748 | |
749 | return 1; |
750 | } |
751 | |
752 | static void |
753 | acpicpu_pstate_change(struct acpicpu_softc *sc) |
754 | { |
755 | static ACPI_STATUS rv = AE_OK; |
756 | ACPI_OBJECT_LIST arg; |
757 | ACPI_OBJECT obj[2]; |
758 | static int val = 0; |
759 | |
760 | acpicpu_pstate_reset(sc); |
761 | |
762 | /* |
763 | * Cache the checks as the optional |
764 | * _PDL and _OST are rarely present. |
765 | */ |
766 | if (val == 0) |
767 | val = acpicpu_pstate_min(sc); |
768 | |
769 | arg.Count = 2; |
770 | arg.Pointer = obj; |
771 | |
772 | obj[0].Type = ACPI_TYPE_INTEGER; |
773 | obj[1].Type = ACPI_TYPE_INTEGER; |
774 | |
775 | obj[0].Integer.Value = ACPICPU_P_NOTIFY; |
776 | obj[1].Integer.Value = acpicpu_pstate_max(sc); |
777 | |
778 | if (ACPI_FAILURE(rv)) |
779 | return; |
780 | |
781 | rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "_OST" , &arg, NULL); |
782 | } |
783 | |
784 | static void |
785 | acpicpu_pstate_reset(struct acpicpu_softc *sc) |
786 | { |
787 | |
788 | sc->sc_pstate_max = 0; |
789 | sc->sc_pstate_min = sc->sc_pstate_count - 1; |
790 | |
791 | } |
792 | |
793 | static void |
794 | acpicpu_pstate_bios(void) |
795 | { |
796 | const uint8_t val = AcpiGbl_FADT.PstateControl; |
797 | const uint32_t addr = AcpiGbl_FADT.SmiCommand; |
798 | |
799 | if (addr == 0 || val == 0) |
800 | return; |
801 | |
802 | (void)AcpiOsWritePort(addr, val, 8); |
803 | } |
804 | |
805 | void |
806 | acpicpu_pstate_get(void *aux, void *cpu_freq) |
807 | { |
808 | struct acpicpu_pstate *ps = NULL; |
809 | struct cpu_info *ci = curcpu(); |
810 | struct acpicpu_softc *sc; |
811 | uint32_t freq, i, val = 0; |
812 | uint64_t addr; |
813 | uint8_t width; |
814 | int rv; |
815 | |
816 | sc = acpicpu_sc[ci->ci_acpiid]; |
817 | |
818 | if (__predict_false(sc == NULL)) { |
819 | rv = ENXIO; |
820 | goto fail; |
821 | } |
822 | |
823 | if (__predict_false((sc->sc_flags & ACPICPU_FLAG_P) == 0)) { |
824 | rv = ENODEV; |
825 | goto fail; |
826 | } |
827 | |
828 | mutex_enter(&sc->sc_mtx); |
829 | |
830 | /* |
831 | * Use the cached value, if available. |
832 | */ |
833 | if (sc->sc_pstate_current != 0) { |
834 | *(uint32_t *)cpu_freq = sc->sc_pstate_current; |
835 | mutex_exit(&sc->sc_mtx); |
836 | return; |
837 | } |
838 | |
839 | mutex_exit(&sc->sc_mtx); |
840 | |
841 | switch (sc->sc_pstate_status.reg_spaceid) { |
842 | |
843 | case ACPI_ADR_SPACE_FIXED_HARDWARE: |
844 | |
845 | rv = acpicpu_md_pstate_get(sc, &freq); |
846 | |
847 | if (__predict_false(rv != 0)) |
848 | goto fail; |
849 | |
850 | break; |
851 | |
852 | case ACPI_ADR_SPACE_SYSTEM_IO: |
853 | |
854 | addr = sc->sc_pstate_status.reg_addr; |
855 | width = sc->sc_pstate_status.reg_bitwidth; |
856 | |
857 | (void)AcpiOsReadPort(addr, &val, width); |
858 | |
859 | if (val == 0) { |
860 | rv = EIO; |
861 | goto fail; |
862 | } |
863 | |
864 | for (i = 0; i < sc->sc_pstate_count; i++) { |
865 | |
866 | if (sc->sc_pstate[i].ps_freq == 0) |
867 | continue; |
868 | |
869 | if (val == sc->sc_pstate[i].ps_status) { |
870 | ps = &sc->sc_pstate[i]; |
871 | break; |
872 | } |
873 | } |
874 | |
875 | if (ps == NULL) { |
876 | rv = EIO; |
877 | goto fail; |
878 | } |
879 | |
880 | freq = ps->ps_freq; |
881 | break; |
882 | |
883 | default: |
884 | rv = ENOTTY; |
885 | goto fail; |
886 | } |
887 | |
888 | mutex_enter(&sc->sc_mtx); |
889 | sc->sc_pstate_current = freq; |
890 | *(uint32_t *)cpu_freq = freq; |
891 | mutex_exit(&sc->sc_mtx); |
892 | |
893 | return; |
894 | |
895 | fail: |
896 | aprint_error_dev(sc->sc_dev, "failed " |
897 | "to get frequency (err %d)\n" , rv); |
898 | |
899 | mutex_enter(&sc->sc_mtx); |
900 | sc->sc_pstate_current = 0; |
901 | *(uint32_t *)cpu_freq = 0; |
902 | mutex_exit(&sc->sc_mtx); |
903 | } |
904 | |
905 | void |
906 | acpicpu_pstate_set(void *aux, void *cpu_freq) |
907 | { |
908 | struct acpicpu_pstate *ps = NULL; |
909 | struct cpu_info *ci = curcpu(); |
910 | struct acpicpu_softc *sc; |
911 | uint32_t freq, i, val; |
912 | uint64_t addr; |
913 | uint8_t width; |
914 | int rv; |
915 | |
916 | freq = *(uint32_t *)cpu_freq; |
917 | sc = acpicpu_sc[ci->ci_acpiid]; |
918 | |
919 | if (__predict_false(sc == NULL)) { |
920 | rv = ENXIO; |
921 | goto fail; |
922 | } |
923 | |
924 | if (__predict_false((sc->sc_flags & ACPICPU_FLAG_P) == 0)) { |
925 | rv = ENODEV; |
926 | goto fail; |
927 | } |
928 | |
929 | mutex_enter(&sc->sc_mtx); |
930 | |
931 | if (sc->sc_pstate_current == freq) { |
932 | mutex_exit(&sc->sc_mtx); |
933 | return; |
934 | } |
935 | |
936 | /* |
937 | * Verify that the requested frequency is available. |
938 | * |
939 | * The access needs to be protected since the currently |
940 | * available maximum and minimum may change dynamically. |
941 | */ |
942 | for (i = sc->sc_pstate_max; i <= sc->sc_pstate_min; i++) { |
943 | |
944 | if (__predict_false(sc->sc_pstate[i].ps_freq == 0)) |
945 | continue; |
946 | |
947 | if (sc->sc_pstate[i].ps_freq == freq) { |
948 | ps = &sc->sc_pstate[i]; |
949 | break; |
950 | } |
951 | } |
952 | |
953 | mutex_exit(&sc->sc_mtx); |
954 | |
955 | if (__predict_false(ps == NULL)) { |
956 | rv = EINVAL; |
957 | goto fail; |
958 | } |
959 | |
960 | switch (sc->sc_pstate_control.reg_spaceid) { |
961 | |
962 | case ACPI_ADR_SPACE_FIXED_HARDWARE: |
963 | |
964 | rv = acpicpu_md_pstate_set(ps); |
965 | |
966 | if (__predict_false(rv != 0)) |
967 | goto fail; |
968 | |
969 | break; |
970 | |
971 | case ACPI_ADR_SPACE_SYSTEM_IO: |
972 | |
973 | addr = sc->sc_pstate_control.reg_addr; |
974 | width = sc->sc_pstate_control.reg_bitwidth; |
975 | |
976 | (void)AcpiOsWritePort(addr, ps->ps_control, width); |
977 | |
978 | addr = sc->sc_pstate_status.reg_addr; |
979 | width = sc->sc_pstate_status.reg_bitwidth; |
980 | |
981 | /* |
982 | * Some systems take longer to respond |
983 | * than the reported worst-case latency. |
984 | */ |
985 | for (i = val = 0; i < ACPICPU_P_STATE_RETRY; i++) { |
986 | |
987 | (void)AcpiOsReadPort(addr, &val, width); |
988 | |
989 | if (val == ps->ps_status) |
990 | break; |
991 | |
992 | DELAY(ps->ps_latency); |
993 | } |
994 | |
995 | if (i == ACPICPU_P_STATE_RETRY) { |
996 | rv = EAGAIN; |
997 | goto fail; |
998 | } |
999 | |
1000 | break; |
1001 | |
1002 | default: |
1003 | rv = ENOTTY; |
1004 | goto fail; |
1005 | } |
1006 | |
1007 | mutex_enter(&sc->sc_mtx); |
1008 | ps->ps_evcnt.ev_count++; |
1009 | sc->sc_pstate_current = freq; |
1010 | mutex_exit(&sc->sc_mtx); |
1011 | |
1012 | return; |
1013 | |
1014 | fail: |
1015 | if (rv != EINVAL) |
1016 | aprint_error_dev(sc->sc_dev, "failed to set " |
1017 | "frequency to %u (err %d)\n" , freq, rv); |
1018 | |
1019 | mutex_enter(&sc->sc_mtx); |
1020 | sc->sc_pstate_current = 0; |
1021 | mutex_exit(&sc->sc_mtx); |
1022 | } |
1023 | |