1 | /* $NetBSD: acpi_cpu_tstate.c,v 1.32 2013/11/20 13:39:59 jruoho Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2010 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_tstate.c,v 1.32 2013/11/20 13:39:59 jruoho Exp $" ); |
31 | |
32 | #include <sys/param.h> |
33 | #include <sys/kmem.h> |
34 | #include <sys/xcall.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 ACPI_STATUS acpicpu_tstate_tss(struct acpicpu_softc *); |
44 | static ACPI_STATUS acpicpu_tstate_tss_add(struct acpicpu_tstate *, |
45 | ACPI_OBJECT *); |
46 | static ACPI_STATUS acpicpu_tstate_ptc(struct acpicpu_softc *); |
47 | static ACPI_STATUS acpicpu_tstate_dep(struct acpicpu_softc *); |
48 | static ACPI_STATUS acpicpu_tstate_fadt(struct acpicpu_softc *); |
49 | static ACPI_STATUS acpicpu_tstate_change(struct acpicpu_softc *); |
50 | static void acpicpu_tstate_reset(struct acpicpu_softc *); |
51 | static void acpicpu_tstate_set_xcall(void *, void *); |
52 | |
53 | extern struct acpicpu_softc **acpicpu_sc; |
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 | } |
122 | |
123 | void |
124 | acpicpu_tstate_detach(device_t self) |
125 | { |
126 | struct acpicpu_softc *sc = device_private(self); |
127 | size_t size; |
128 | |
129 | if ((sc->sc_flags & ACPICPU_FLAG_T) == 0) |
130 | return; |
131 | |
132 | size = sc->sc_tstate_count * sizeof(*sc->sc_tstate); |
133 | |
134 | if (sc->sc_tstate != NULL) |
135 | kmem_free(sc->sc_tstate, size); |
136 | |
137 | sc->sc_flags &= ~ACPICPU_FLAG_T; |
138 | } |
139 | |
140 | void |
141 | acpicpu_tstate_start(device_t self) |
142 | { |
143 | /* Nothing. */ |
144 | } |
145 | |
146 | void |
147 | acpicpu_tstate_suspend(void *aux) |
148 | { |
149 | struct acpicpu_softc *sc; |
150 | device_t self = aux; |
151 | |
152 | sc = device_private(self); |
153 | |
154 | mutex_enter(&sc->sc_mtx); |
155 | acpicpu_tstate_reset(sc); |
156 | mutex_exit(&sc->sc_mtx); |
157 | } |
158 | |
159 | void |
160 | acpicpu_tstate_resume(void *aux) |
161 | { |
162 | /* Nothing. */ |
163 | } |
164 | |
165 | void |
166 | acpicpu_tstate_callback(void *aux) |
167 | { |
168 | struct acpicpu_softc *sc; |
169 | device_t self = aux; |
170 | uint32_t omax, omin; |
171 | int i; |
172 | |
173 | sc = device_private(self); |
174 | |
175 | if ((sc->sc_flags & ACPICPU_FLAG_T_FADT) != 0) |
176 | return; |
177 | |
178 | mutex_enter(&sc->sc_mtx); |
179 | |
180 | /* |
181 | * If P-states are in use, we should ignore |
182 | * the interrupt unless we are in the highest |
183 | * P-state (see ACPI 4.0, section 8.4.3.3). |
184 | */ |
185 | if ((sc->sc_flags & ACPICPU_FLAG_P) != 0) { |
186 | |
187 | for (i = sc->sc_pstate_count - 1; i >= 0; i--) { |
188 | |
189 | if (sc->sc_pstate[i].ps_freq != 0) |
190 | break; |
191 | } |
192 | |
193 | if (sc->sc_pstate_current != sc->sc_pstate[i].ps_freq) { |
194 | mutex_exit(&sc->sc_mtx); |
195 | return; |
196 | } |
197 | } |
198 | |
199 | omax = sc->sc_tstate_max; |
200 | omin = sc->sc_tstate_min; |
201 | |
202 | (void)acpicpu_tstate_change(sc); |
203 | |
204 | if (omax != sc->sc_tstate_max || omin != sc->sc_tstate_min) { |
205 | |
206 | aprint_debug_dev(sc->sc_dev, "throttling window " |
207 | "changed from %u-%u %% to %u-%u %%\n" , |
208 | sc->sc_tstate[omax].ts_percent, |
209 | sc->sc_tstate[omin].ts_percent, |
210 | sc->sc_tstate[sc->sc_tstate_max].ts_percent, |
211 | sc->sc_tstate[sc->sc_tstate_min].ts_percent); |
212 | } |
213 | |
214 | mutex_exit(&sc->sc_mtx); |
215 | } |
216 | |
217 | static ACPI_STATUS |
218 | acpicpu_tstate_tss(struct acpicpu_softc *sc) |
219 | { |
220 | struct acpicpu_tstate *ts; |
221 | ACPI_OBJECT *obj; |
222 | ACPI_BUFFER buf; |
223 | ACPI_STATUS rv; |
224 | uint32_t count; |
225 | uint32_t i, j; |
226 | |
227 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSS" , &buf); |
228 | |
229 | if (ACPI_FAILURE(rv)) |
230 | return rv; |
231 | |
232 | obj = buf.Pointer; |
233 | |
234 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
235 | rv = AE_TYPE; |
236 | goto out; |
237 | } |
238 | |
239 | sc->sc_tstate_count = obj->Package.Count; |
240 | |
241 | if (sc->sc_tstate_count == 0) { |
242 | rv = AE_NOT_EXIST; |
243 | goto out; |
244 | } |
245 | |
246 | sc->sc_tstate = kmem_zalloc(sc->sc_tstate_count * |
247 | sizeof(struct acpicpu_tstate), KM_SLEEP); |
248 | |
249 | if (sc->sc_tstate == NULL) { |
250 | rv = AE_NO_MEMORY; |
251 | goto out; |
252 | } |
253 | |
254 | for (count = i = 0; i < sc->sc_tstate_count; i++) { |
255 | |
256 | ts = &sc->sc_tstate[i]; |
257 | rv = acpicpu_tstate_tss_add(ts, &obj->Package.Elements[i]); |
258 | |
259 | if (ACPI_FAILURE(rv)) { |
260 | ts->ts_percent = 0; |
261 | continue; |
262 | } |
263 | |
264 | for (j = 0; j < i; j++) { |
265 | |
266 | if (ts->ts_percent >= sc->sc_tstate[j].ts_percent) { |
267 | ts->ts_percent = 0; |
268 | break; |
269 | } |
270 | } |
271 | |
272 | if (ts->ts_percent != 0) |
273 | count++; |
274 | } |
275 | |
276 | if (count == 0) { |
277 | rv = AE_NOT_EXIST; |
278 | goto out; |
279 | } |
280 | |
281 | /* |
282 | * There must be an entry with the percent |
283 | * field of 100. If this is not true, and if |
284 | * this entry is not in the expected index, |
285 | * invalidate the use of T-states via _TSS. |
286 | */ |
287 | if (sc->sc_tstate[0].ts_percent != 100) { |
288 | rv = AE_BAD_DECIMAL_CONSTANT; |
289 | goto out; |
290 | } |
291 | |
292 | out: |
293 | if (buf.Pointer != NULL) |
294 | ACPI_FREE(buf.Pointer); |
295 | |
296 | return rv; |
297 | } |
298 | |
299 | static ACPI_STATUS |
300 | acpicpu_tstate_tss_add(struct acpicpu_tstate *ts, ACPI_OBJECT *obj) |
301 | { |
302 | ACPI_OBJECT *elm; |
303 | uint32_t val[5]; |
304 | uint32_t *p; |
305 | int i; |
306 | |
307 | if (obj->Type != ACPI_TYPE_PACKAGE) |
308 | return AE_TYPE; |
309 | |
310 | if (obj->Package.Count != 5) |
311 | return AE_BAD_DATA; |
312 | |
313 | elm = obj->Package.Elements; |
314 | |
315 | for (i = 0; i < 5; i++) { |
316 | |
317 | if (elm[i].Type != ACPI_TYPE_INTEGER) |
318 | return AE_TYPE; |
319 | |
320 | if (elm[i].Integer.Value > UINT32_MAX) |
321 | return AE_AML_NUMERIC_OVERFLOW; |
322 | |
323 | val[i] = elm[i].Integer.Value; |
324 | } |
325 | |
326 | p = &ts->ts_percent; |
327 | |
328 | for (i = 0; i < 5; i++, p++) |
329 | *p = val[i]; |
330 | |
331 | /* |
332 | * The minimum should be either 12.5 % or 6.5 %, |
333 | * the latter 4-bit dynamic range being available |
334 | * in some newer models; see Section 14.5.3.1 in |
335 | * |
336 | * Intel 64 and IA-32 Architectures Software |
337 | * Developer's Manual. Volume 3B, Part 2. 2013. |
338 | */ |
339 | if (ts->ts_percent < 6 || ts->ts_percent > 100) |
340 | return AE_BAD_DECIMAL_CONSTANT; |
341 | |
342 | if (ts->ts_latency == 0 || ts->ts_latency > 1000) |
343 | ts->ts_latency = 1; |
344 | |
345 | return AE_OK; |
346 | } |
347 | |
348 | ACPI_STATUS |
349 | acpicpu_tstate_ptc(struct acpicpu_softc *sc) |
350 | { |
351 | static const size_t size = sizeof(struct acpicpu_reg); |
352 | struct acpicpu_reg *reg[2]; |
353 | ACPI_OBJECT *elm, *obj; |
354 | ACPI_BUFFER buf; |
355 | ACPI_STATUS rv; |
356 | int i; |
357 | |
358 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PTC" , &buf); |
359 | |
360 | if (ACPI_FAILURE(rv)) |
361 | return rv; |
362 | |
363 | obj = buf.Pointer; |
364 | |
365 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
366 | rv = AE_TYPE; |
367 | goto out; |
368 | } |
369 | |
370 | if (obj->Package.Count != 2) { |
371 | rv = AE_LIMIT; |
372 | goto out; |
373 | } |
374 | |
375 | for (i = 0; i < 2; i++) { |
376 | |
377 | elm = &obj->Package.Elements[i]; |
378 | |
379 | if (elm->Type != ACPI_TYPE_BUFFER) { |
380 | rv = AE_TYPE; |
381 | goto out; |
382 | } |
383 | |
384 | if (size > elm->Buffer.Length) { |
385 | rv = AE_AML_BAD_RESOURCE_LENGTH; |
386 | goto out; |
387 | } |
388 | |
389 | reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer; |
390 | |
391 | switch (reg[i]->reg_spaceid) { |
392 | |
393 | case ACPI_ADR_SPACE_SYSTEM_IO: |
394 | |
395 | if (reg[i]->reg_addr == 0) { |
396 | rv = AE_AML_ILLEGAL_ADDRESS; |
397 | goto out; |
398 | } |
399 | |
400 | /* |
401 | * Check that the values match the IA32 clock |
402 | * modulation MSR, where the bit 0 is reserved, |
403 | * bits 1 through 3 define the duty cycle, and |
404 | * the fourth bit enables the modulation. |
405 | */ |
406 | if (reg[i]->reg_bitwidth != 4) { |
407 | rv = AE_AML_BAD_RESOURCE_VALUE; |
408 | goto out; |
409 | } |
410 | |
411 | if (reg[i]->reg_bitoffset != 1) { |
412 | rv = AE_AML_BAD_RESOURCE_VALUE; |
413 | goto out; |
414 | } |
415 | |
416 | break; |
417 | |
418 | case ACPI_ADR_SPACE_FIXED_HARDWARE: |
419 | |
420 | if ((sc->sc_flags & ACPICPU_FLAG_T_FFH) == 0) { |
421 | rv = AE_SUPPORT; |
422 | goto out; |
423 | } |
424 | |
425 | break; |
426 | |
427 | default: |
428 | rv = AE_AML_INVALID_SPACE_ID; |
429 | goto out; |
430 | } |
431 | } |
432 | |
433 | if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) { |
434 | rv = AE_AML_INVALID_SPACE_ID; |
435 | goto out; |
436 | } |
437 | |
438 | (void)memcpy(&sc->sc_tstate_control, reg[0], size); |
439 | (void)memcpy(&sc->sc_tstate_status, reg[1], size); |
440 | |
441 | out: |
442 | if (buf.Pointer != NULL) |
443 | ACPI_FREE(buf.Pointer); |
444 | |
445 | return rv; |
446 | } |
447 | |
448 | static ACPI_STATUS |
449 | acpicpu_tstate_dep(struct acpicpu_softc *sc) |
450 | { |
451 | ACPI_OBJECT *elm, *obj; |
452 | ACPI_BUFFER buf; |
453 | ACPI_STATUS rv; |
454 | uint32_t val; |
455 | uint8_t i, n; |
456 | |
457 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_TSD" , &buf); |
458 | |
459 | if (ACPI_FAILURE(rv)) |
460 | goto out; |
461 | |
462 | obj = buf.Pointer; |
463 | |
464 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
465 | rv = AE_TYPE; |
466 | goto out; |
467 | } |
468 | |
469 | if (obj->Package.Count != 1) { |
470 | rv = AE_LIMIT; |
471 | goto out; |
472 | } |
473 | |
474 | elm = &obj->Package.Elements[0]; |
475 | |
476 | if (obj->Type != ACPI_TYPE_PACKAGE) { |
477 | rv = AE_TYPE; |
478 | goto out; |
479 | } |
480 | |
481 | n = elm->Package.Count; |
482 | |
483 | if (n != 5) { |
484 | rv = AE_LIMIT; |
485 | goto out; |
486 | } |
487 | |
488 | elm = elm->Package.Elements; |
489 | |
490 | for (i = 0; i < n; i++) { |
491 | |
492 | if (elm[i].Type != ACPI_TYPE_INTEGER) { |
493 | rv = AE_TYPE; |
494 | goto out; |
495 | } |
496 | |
497 | if (elm[i].Integer.Value > UINT32_MAX) { |
498 | rv = AE_AML_NUMERIC_OVERFLOW; |
499 | goto out; |
500 | } |
501 | } |
502 | |
503 | val = elm[1].Integer.Value; |
504 | |
505 | if (val != 0) |
506 | aprint_debug_dev(sc->sc_dev, "invalid revision in _TSD\n" ); |
507 | |
508 | val = elm[3].Integer.Value; |
509 | |
510 | if (val < ACPICPU_DEP_SW_ALL || val > ACPICPU_DEP_HW_ALL) { |
511 | rv = AE_AML_BAD_RESOURCE_VALUE; |
512 | goto out; |
513 | } |
514 | |
515 | val = elm[4].Integer.Value; |
516 | |
517 | if (val > sc->sc_ncpus) { |
518 | rv = AE_BAD_VALUE; |
519 | goto out; |
520 | } |
521 | |
522 | sc->sc_tstate_dep.dep_domain = elm[2].Integer.Value; |
523 | sc->sc_tstate_dep.dep_type = elm[3].Integer.Value; |
524 | sc->sc_tstate_dep.dep_ncpus = elm[4].Integer.Value; |
525 | |
526 | out: |
527 | if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) |
528 | aprint_debug_dev(sc->sc_dev, "failed to evaluate " |
529 | "_TSD: %s\n" , AcpiFormatException(rv)); |
530 | |
531 | if (buf.Pointer != NULL) |
532 | ACPI_FREE(buf.Pointer); |
533 | |
534 | return rv; |
535 | } |
536 | |
537 | static ACPI_STATUS |
538 | acpicpu_tstate_fadt(struct acpicpu_softc *sc) |
539 | { |
540 | static const size_t size = sizeof(struct acpicpu_tstate); |
541 | const uint8_t offset = AcpiGbl_FADT.DutyOffset; |
542 | const uint8_t width = AcpiGbl_FADT.DutyWidth; |
543 | uint8_t beta, count, i; |
544 | |
545 | if (sc->sc_object.ao_pblkaddr == 0) |
546 | return AE_AML_ILLEGAL_ADDRESS; |
547 | |
548 | /* |
549 | * A zero DUTY_WIDTH may be used announce |
550 | * that T-states are not available via FADT |
551 | * (ACPI 4.0, p. 121). See also (section 9.3): |
552 | * |
553 | * Advanced Micro Devices: BIOS and Kernel |
554 | * Developer's Guide for AMD Athlon 64 and |
555 | * AMD Opteron Processors. Revision 3.30, |
556 | * February 2006. |
557 | */ |
558 | if (width == 0 || width + offset > 4) |
559 | return AE_AML_BAD_RESOURCE_VALUE; |
560 | |
561 | count = 1 << width; |
562 | |
563 | if (sc->sc_tstate != NULL) |
564 | kmem_free(sc->sc_tstate, sc->sc_tstate_count * size); |
565 | |
566 | sc->sc_tstate = kmem_zalloc(count * size, KM_SLEEP); |
567 | |
568 | if (sc->sc_tstate == NULL) |
569 | return ENOMEM; |
570 | |
571 | sc->sc_tstate_count = count; |
572 | |
573 | /* |
574 | * Approximate duty cycles and set the MSR values. |
575 | */ |
576 | for (beta = 100 / count, i = 0; i < count; i++) { |
577 | sc->sc_tstate[i].ts_percent = 100 - beta * i; |
578 | sc->sc_tstate[i].ts_latency = 1; |
579 | } |
580 | |
581 | for (i = 1; i < count; i++) |
582 | sc->sc_tstate[i].ts_control = (count - i) | __BIT(3); |
583 | |
584 | /* |
585 | * Fake values for throttling registers. |
586 | */ |
587 | (void)memset(&sc->sc_tstate_status, 0, sizeof(struct acpicpu_reg)); |
588 | (void)memset(&sc->sc_tstate_control, 0, sizeof(struct acpicpu_reg)); |
589 | |
590 | sc->sc_tstate_status.reg_bitwidth = width; |
591 | sc->sc_tstate_status.reg_bitoffset = offset; |
592 | sc->sc_tstate_status.reg_addr = sc->sc_object.ao_pblkaddr; |
593 | sc->sc_tstate_status.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; |
594 | |
595 | sc->sc_tstate_control.reg_bitwidth = width; |
596 | sc->sc_tstate_control.reg_bitoffset = offset; |
597 | sc->sc_tstate_control.reg_addr = sc->sc_object.ao_pblkaddr; |
598 | sc->sc_tstate_control.reg_spaceid = ACPI_ADR_SPACE_SYSTEM_IO; |
599 | |
600 | return AE_OK; |
601 | } |
602 | |
603 | static ACPI_STATUS |
604 | acpicpu_tstate_change(struct acpicpu_softc *sc) |
605 | { |
606 | ACPI_INTEGER val; |
607 | ACPI_STATUS rv; |
608 | |
609 | acpicpu_tstate_reset(sc); |
610 | |
611 | /* |
612 | * Evaluate the available T-state window: |
613 | * |
614 | * _TPC : either this maximum or any lower power |
615 | * (i.e. higher numbered) state may be used. |
616 | * |
617 | * _TDL : either this minimum or any higher power |
618 | * (i.e. lower numbered) state may be used. |
619 | * |
620 | * _TDL >= _TPC || _TDL >= _TSS[last entry]. |
621 | */ |
622 | rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TPC" , &val); |
623 | |
624 | if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { |
625 | |
626 | if (sc->sc_tstate[val].ts_percent != 0) |
627 | sc->sc_tstate_max = val; |
628 | } |
629 | |
630 | rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TDL" , &val); |
631 | |
632 | if (ACPI_SUCCESS(rv) && val < sc->sc_tstate_count) { |
633 | |
634 | if (val >= sc->sc_tstate_max && |
635 | sc->sc_tstate[val].ts_percent != 0) |
636 | sc->sc_tstate_min = val; |
637 | } |
638 | |
639 | return AE_OK; |
640 | } |
641 | |
642 | static void |
643 | acpicpu_tstate_reset(struct acpicpu_softc *sc) |
644 | { |
645 | |
646 | sc->sc_tstate_max = 0; |
647 | sc->sc_tstate_min = sc->sc_tstate_count - 1; |
648 | } |
649 | |
650 | int |
651 | acpicpu_tstate_get(struct cpu_info *ci, uint32_t *percent) |
652 | { |
653 | struct acpicpu_tstate *ts = NULL; |
654 | struct acpicpu_softc *sc; |
655 | uint32_t i, val = 0; |
656 | uint8_t offset; |
657 | uint64_t addr; |
658 | int rv; |
659 | |
660 | sc = acpicpu_sc[ci->ci_acpiid]; |
661 | |
662 | if (__predict_false(sc == NULL)) { |
663 | rv = ENXIO; |
664 | goto fail; |
665 | } |
666 | |
667 | if (__predict_false(sc->sc_cold != false)) { |
668 | rv = EBUSY; |
669 | goto fail; |
670 | } |
671 | |
672 | if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) { |
673 | rv = ENODEV; |
674 | goto fail; |
675 | } |
676 | |
677 | mutex_enter(&sc->sc_mtx); |
678 | |
679 | if (sc->sc_tstate_current != ACPICPU_T_STATE_UNKNOWN) { |
680 | *percent = sc->sc_tstate_current; |
681 | mutex_exit(&sc->sc_mtx); |
682 | return 0; |
683 | } |
684 | |
685 | mutex_exit(&sc->sc_mtx); |
686 | |
687 | switch (sc->sc_tstate_status.reg_spaceid) { |
688 | |
689 | case ACPI_ADR_SPACE_FIXED_HARDWARE: |
690 | |
691 | rv = acpicpu_md_tstate_get(sc, percent); |
692 | |
693 | if (__predict_false(rv != 0)) |
694 | goto fail; |
695 | |
696 | break; |
697 | |
698 | case ACPI_ADR_SPACE_SYSTEM_IO: |
699 | |
700 | addr = sc->sc_tstate_status.reg_addr; |
701 | offset = sc->sc_tstate_status.reg_bitoffset; |
702 | |
703 | (void)AcpiOsReadPort(addr, &val, 8); |
704 | |
705 | val = (val >> offset) & 0x0F; |
706 | |
707 | for (i = 0; i < sc->sc_tstate_count; i++) { |
708 | |
709 | if (sc->sc_tstate[i].ts_percent == 0) |
710 | continue; |
711 | |
712 | if (val == sc->sc_tstate[i].ts_status) { |
713 | ts = &sc->sc_tstate[i]; |
714 | break; |
715 | } |
716 | } |
717 | |
718 | if (ts == NULL) { |
719 | rv = EIO; |
720 | goto fail; |
721 | } |
722 | |
723 | *percent = ts->ts_percent; |
724 | break; |
725 | |
726 | default: |
727 | rv = ENOTTY; |
728 | goto fail; |
729 | } |
730 | |
731 | mutex_enter(&sc->sc_mtx); |
732 | sc->sc_tstate_current = *percent; |
733 | mutex_exit(&sc->sc_mtx); |
734 | |
735 | return 0; |
736 | |
737 | fail: |
738 | aprint_error_dev(sc->sc_dev, "failed " |
739 | "to get T-state (err %d)\n" , rv); |
740 | |
741 | mutex_enter(&sc->sc_mtx); |
742 | *percent = sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; |
743 | mutex_exit(&sc->sc_mtx); |
744 | |
745 | return rv; |
746 | } |
747 | |
748 | void |
749 | acpicpu_tstate_set(struct cpu_info *ci, uint32_t percent) |
750 | { |
751 | uint64_t xc; |
752 | |
753 | xc = xc_broadcast(0, acpicpu_tstate_set_xcall, &percent, NULL); |
754 | xc_wait(xc); |
755 | } |
756 | |
757 | static void |
758 | acpicpu_tstate_set_xcall(void *arg1, void *arg2) |
759 | { |
760 | struct acpicpu_tstate *ts = NULL; |
761 | struct cpu_info *ci = curcpu(); |
762 | struct acpicpu_softc *sc; |
763 | uint32_t i, percent, val; |
764 | uint8_t offset; |
765 | uint64_t addr; |
766 | int rv; |
767 | |
768 | percent = *(uint32_t *)arg1; |
769 | sc = acpicpu_sc[ci->ci_acpiid]; |
770 | |
771 | if (__predict_false(sc == NULL)) { |
772 | rv = ENXIO; |
773 | goto fail; |
774 | } |
775 | |
776 | if (__predict_false(sc->sc_cold != false)) { |
777 | rv = EBUSY; |
778 | goto fail; |
779 | } |
780 | |
781 | if (__predict_false((sc->sc_flags & ACPICPU_FLAG_T) == 0)) { |
782 | rv = ENODEV; |
783 | goto fail; |
784 | } |
785 | |
786 | mutex_enter(&sc->sc_mtx); |
787 | |
788 | if (sc->sc_tstate_current == percent) { |
789 | mutex_exit(&sc->sc_mtx); |
790 | return; |
791 | } |
792 | |
793 | for (i = sc->sc_tstate_max; i <= sc->sc_tstate_min; i++) { |
794 | |
795 | if (__predict_false(sc->sc_tstate[i].ts_percent == 0)) |
796 | continue; |
797 | |
798 | if (sc->sc_tstate[i].ts_percent == percent) { |
799 | ts = &sc->sc_tstate[i]; |
800 | break; |
801 | } |
802 | } |
803 | |
804 | mutex_exit(&sc->sc_mtx); |
805 | |
806 | if (__predict_false(ts == NULL)) { |
807 | rv = EINVAL; |
808 | goto fail; |
809 | } |
810 | |
811 | switch (sc->sc_tstate_control.reg_spaceid) { |
812 | |
813 | case ACPI_ADR_SPACE_FIXED_HARDWARE: |
814 | |
815 | rv = acpicpu_md_tstate_set(ts); |
816 | |
817 | if (__predict_false(rv != 0)) |
818 | goto fail; |
819 | |
820 | break; |
821 | |
822 | case ACPI_ADR_SPACE_SYSTEM_IO: |
823 | |
824 | addr = sc->sc_tstate_control.reg_addr; |
825 | offset = sc->sc_tstate_control.reg_bitoffset; |
826 | |
827 | val = (ts->ts_control & 0x0F) << offset; |
828 | |
829 | if (ts->ts_percent != 100 && (val & __BIT(4)) == 0) { |
830 | rv = EINVAL; |
831 | goto fail; |
832 | } |
833 | |
834 | (void)AcpiOsWritePort(addr, val, 8); |
835 | |
836 | /* |
837 | * If the status field is zero, the transition is |
838 | * specified to be "asynchronous" and there is no |
839 | * need to check the status (ACPI 4.0, 8.4.3.2). |
840 | */ |
841 | if (ts->ts_status == 0) |
842 | break; |
843 | |
844 | addr = sc->sc_tstate_status.reg_addr; |
845 | offset = sc->sc_tstate_status.reg_bitoffset; |
846 | |
847 | for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) { |
848 | |
849 | (void)AcpiOsReadPort(addr, &val, 8); |
850 | |
851 | val = (val >> offset) & 0x0F; |
852 | |
853 | if (val == ts->ts_status) |
854 | break; |
855 | |
856 | DELAY(ts->ts_latency); |
857 | } |
858 | |
859 | if (i == ACPICPU_T_STATE_RETRY) { |
860 | rv = EAGAIN; |
861 | goto fail; |
862 | } |
863 | |
864 | break; |
865 | |
866 | default: |
867 | rv = ENOTTY; |
868 | goto fail; |
869 | } |
870 | |
871 | mutex_enter(&sc->sc_mtx); |
872 | ts->ts_evcnt.ev_count++; |
873 | sc->sc_tstate_current = percent; |
874 | mutex_exit(&sc->sc_mtx); |
875 | |
876 | return; |
877 | |
878 | fail: |
879 | if (rv != EINVAL) |
880 | aprint_error_dev(sc->sc_dev, "failed to " |
881 | "throttle to %u %% (err %d)\n" , percent, rv); |
882 | |
883 | mutex_enter(&sc->sc_mtx); |
884 | sc->sc_tstate_current = ACPICPU_T_STATE_UNKNOWN; |
885 | mutex_exit(&sc->sc_mtx); |
886 | } |
887 | |