1 /* $NetBSD: acpi_tz.c,v 1.88 2015/04/23 23:23:00 pgoyette Exp $ */
2
3 /*
4 * Copyright (c) 2003 Jared D. McNeill <jmcneill@invisible.ca>
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 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /*
29 * ACPI Thermal Zone driver
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: acpi_tz.c,v 1.88 2015/04/23 23:23:00 pgoyette Exp $");
34
35 #include <sys/param.h>
36 #include <sys/device.h>
37 #include <sys/callout.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/systm.h>
41 #include <sys/kmem.h>
42
43 #include <dev/acpi/acpireg.h>
44 #include <dev/acpi/acpivar.h>
45 #include <dev/acpi/acpi_power.h>
46
47 #define _COMPONENT ACPI_TZ_COMPONENT
48 ACPI_MODULE_NAME ("acpi_tz")
49
50 #define ACPI_NOTIFY_TZ_ZONE 0x80
51 #define ACPI_NOTIFY_TZ_TRIP 0x81
52 #define ACPI_NOTIFY_TZ_DEVLIST 0x82
53
54 #define ATZ_F_CRITICAL 0x01 /* zone critical */
55 #define ATZ_F_HOT 0x02 /* zone hot */
56 #define ATZ_F_PASSIVE 0x04 /* zone passive cooling */
57 #define ATZ_F_PASSIVEONLY 0x08 /* zone is passive cooling only */
58
59 #define ATZ_ACTIVE_NONE -1
60
61 /*
62 * The constants are as follows:
63 *
64 * ATZ_TZP_RATE default polling interval (30 seconds) if no _TZP
65 * ATZ_NLEVELS number of cooling levels for _ACx and _ALx
66 * ATZ_ZEROC 0 C, measured in 0.1 Kelvin
67 * ATZ_TMP_INVALID temporarily invalid temperature
68 * ATZ_ZONE_EXPIRE zone info refetch interval (15 minutes)
69 */
70 #define ATZ_TZP_RATE 300
71 #define ATZ_NLEVELS 10
72 #define ATZ_ZEROC 2732
73 #define ATZ_TMP_INVALID 0xffffffff
74 #define ATZ_ZONE_EXPIRE 9000
75
76 /*
77 * All temperatures are reported in 0.1 Kelvin.
78 * The ACPI specification assumes that K = C + 273.2
79 * rather than the nominal 273.15 used by envsys(4).
80 */
81 #define ATZ2UKELVIN(t) ((t) * 100000 - 50000)
82
83 struct acpitz_zone {
84 ACPI_BUFFER al[ATZ_NLEVELS];
85 uint32_t ac[ATZ_NLEVELS];
86 uint32_t crt;
87 uint32_t hot;
88 uint32_t rtv;
89 uint32_t psv;
90 uint32_t tc1;
91 uint32_t tc2;
92 uint32_t tmp;
93 uint32_t prevtmp;
94 uint32_t tzp;
95 uint32_t fanmin;
96 uint32_t fanmax;
97 uint32_t fancurrent;
98 };
99
100 struct acpitz_softc {
101 struct acpi_devnode *sc_node;
102 struct sysmon_envsys *sc_sme;
103 struct acpitz_zone sc_zone;
104 struct callout sc_callout;
105 envsys_data_t sc_temp_sensor;
106 envsys_data_t sc_fan_sensor;
107 int sc_active;
108 int sc_flags;
109 int sc_zone_expire;
110 bool sc_first;
111 bool sc_have_fan;
112 struct cpu_info **sc_psl;
113 size_t sc_psl_size;
114 };
115
116 static int acpitz_match(device_t, cfdata_t, void *);
117 static void acpitz_attach(device_t, device_t, void *);
118 static int acpitz_detach(device_t, int);
119 static void acpitz_get_status(void *);
120 static void acpitz_get_zone(void *, int);
121 static void acpitz_get_zone_quiet(void *);
122 static char *acpitz_celcius_string(int);
123 static void acpitz_power_off(struct acpitz_softc *);
124 static void acpitz_power_zone(struct acpitz_softc *, int, int);
125 static void acpitz_sane_temp(uint32_t *tmp);
126 static ACPI_STATUS acpitz_switch_cooler(ACPI_OBJECT *, void *);
127 static void acpitz_notify_handler(ACPI_HANDLE, uint32_t, void *);
128 static int acpitz_get_integer(device_t, const char *, uint32_t *);
129 static void acpitz_tick(void *);
130 static void acpitz_init_envsys(device_t);
131 static void acpitz_get_limits(struct sysmon_envsys *,
132 envsys_data_t *,
133 sysmon_envsys_lim_t *, uint32_t *);
134 static int acpitz_get_fanspeed(device_t, uint32_t *,
135 uint32_t *, uint32_t *);
136 #ifdef notyet
137 static ACPI_STATUS acpitz_set_fanspeed(device_t, uint32_t);
138 #endif
139 static void acpitz_print_processor_list(device_t);
140
141 CFATTACH_DECL_NEW(acpitz, sizeof(struct acpitz_softc),
142 acpitz_match, acpitz_attach, acpitz_detach, NULL);
143
144 /*
145 * acpitz_match: autoconf(9) match routine
146 */
147 static int
acpitz_match(device_t parent,cfdata_t match,void * aux)148 acpitz_match(device_t parent, cfdata_t match, void *aux)
149 {
150 struct acpi_attach_args *aa = aux;
151
152 if (aa->aa_node->ad_type != ACPI_TYPE_THERMAL)
153 return 0;
154
155 return 1;
156 }
157
158 /*
159 * acpitz_attach: autoconf(9) attach routine
160 */
161 static void
acpitz_attach(device_t parent,device_t self,void * aux)162 acpitz_attach(device_t parent, device_t self, void *aux)
163 {
164 struct acpitz_softc *sc = device_private(self);
165 struct acpi_attach_args *aa = aux;
166 ACPI_INTEGER val;
167 ACPI_STATUS rv;
168
169 sc->sc_first = true;
170 sc->sc_have_fan = false;
171 sc->sc_node = aa->aa_node;
172 sc->sc_zone.tzp = ATZ_TZP_RATE;
173
174 aprint_naive("\n");
175 acpitz_print_processor_list(self);
176 aprint_normal("\n");
177
178 /*
179 * The _TZP (ACPI 4.0, p. 430) defines the recommended
180 * polling interval (in tenths of seconds). A value zero
181 * means that polling "should not be necessary".
182 */
183 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TZP", &val);
184
185 if (ACPI_SUCCESS(rv) && val != 0)
186 sc->sc_zone.tzp = val;
187
188 aprint_debug_dev(self, "polling interval %d.%d seconds\n",
189 sc->sc_zone.tzp / 10, sc->sc_zone.tzp % 10);
190
191 sc->sc_zone_expire = ATZ_ZONE_EXPIRE / sc->sc_zone.tzp;
192
193 /*
194 * XXX: The fan controls seen here are available on
195 * some HP laptops. Arguably these should not
196 * appear in a generic device driver like this.
197 */
198 if (acpitz_get_fanspeed(self, &sc->sc_zone.fanmin,
199 &sc->sc_zone.fanmax, &sc->sc_zone.fancurrent) == 0)
200 sc->sc_have_fan = true;
201
202 acpitz_get_zone(self, 1);
203 acpitz_get_status(self);
204
205 (void)pmf_device_register(self, NULL, NULL);
206 (void)acpi_power_register(sc->sc_node->ad_handle);
207 (void)acpi_register_notify(sc->sc_node, acpitz_notify_handler);
208
209 callout_init(&sc->sc_callout, CALLOUT_MPSAFE);
210 callout_setfunc(&sc->sc_callout, acpitz_tick, self);
211
212 acpitz_init_envsys(self);
213
214 callout_schedule(&sc->sc_callout, sc->sc_zone.tzp * hz / 10);
215 }
216
217 static int
acpitz_detach(device_t self,int flags)218 acpitz_detach(device_t self, int flags)
219 {
220 struct acpitz_softc *sc = device_private(self);
221 ACPI_HANDLE hdl;
222 ACPI_BUFFER al;
223 ACPI_STATUS rv;
224 int i;
225
226 callout_halt(&sc->sc_callout, NULL);
227 callout_destroy(&sc->sc_callout);
228
229 pmf_device_deregister(self);
230 acpi_deregister_notify(sc->sc_node);
231
232 /*
233 * Although the device itself should not contain any power
234 * resources, we have possibly used the resources of active
235 * cooling devices. To unregister these, first fetch a fresh
236 * active cooling zone, and then detach the resources from
237 * the reference handles contained in the cooling zone.
238 */
239 acpitz_get_zone(self, 0);
240
241 for (i = 0; i < ATZ_NLEVELS; i++) {
242
243 if (sc->sc_zone.al[i].Pointer == NULL)
244 continue;
245
246 al = sc->sc_zone.al[i];
247 rv = acpi_eval_reference_handle(al.Pointer, &hdl);
248
249 if (ACPI_SUCCESS(rv))
250 acpi_power_deregister(hdl);
251
252 ACPI_FREE(sc->sc_zone.al[i].Pointer);
253 }
254
255 if (sc->sc_psl)
256 kmem_free(sc->sc_psl, sc->sc_psl_size);
257
258 if (sc->sc_sme != NULL)
259 sysmon_envsys_unregister(sc->sc_sme);
260
261 return 0;
262 }
263
264 static void
acpitz_get_zone_quiet(void * opaque)265 acpitz_get_zone_quiet(void *opaque)
266 {
267 acpitz_get_zone(opaque, 0);
268 }
269
270 static void
acpitz_get_status(void * opaque)271 acpitz_get_status(void *opaque)
272 {
273 device_t dv = opaque;
274 struct acpitz_softc *sc = device_private(dv);
275 uint32_t tmp, fmin, fmax, fcurrent;
276 int active, changed, flags, i;
277
278 sc->sc_zone_expire--;
279
280 if (sc->sc_zone_expire <= 0) {
281 sc->sc_zone_expire = ATZ_ZONE_EXPIRE / sc->sc_zone.tzp;
282
283 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
284 "%s: zone refetch forced\n", device_xname(dv)));
285
286 acpitz_get_zone(dv, 0);
287 }
288
289 if (acpitz_get_integer(dv, "_TMP", &tmp) != 0)
290 return;
291
292 sc->sc_zone.prevtmp = sc->sc_zone.tmp;
293 sc->sc_zone.tmp = tmp;
294
295 if (sc->sc_first != false)
296 sc->sc_zone.prevtmp = tmp; /* XXX: Sanity check? */
297
298 if (acpitz_get_fanspeed(dv, &fmin, &fmax, &fcurrent) == 0) {
299
300 if (fcurrent != ATZ_TMP_INVALID)
301 sc->sc_zone.fancurrent = fcurrent;
302 }
303
304 sc->sc_temp_sensor.state = ENVSYS_SVALID;
305 sc->sc_temp_sensor.value_cur = ATZ2UKELVIN(sc->sc_zone.tmp);
306
307 sc->sc_fan_sensor.state = ENVSYS_SVALID;
308 sc->sc_fan_sensor.value_cur = sc->sc_zone.fancurrent;
309
310 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: zone temperature is %s C\n",
311 device_xname(dv), acpitz_celcius_string(sc->sc_zone.tmp)));
312
313 /*
314 * XXX: Passive cooling is not yet supported.
315 */
316 if ((sc->sc_flags & ATZ_F_PASSIVEONLY) != 0)
317 return;
318
319 /*
320 * As noted in ACPI 4.0 (p. 420), the temperature
321 * thresholds are conveyed in the optional _ACx
322 * object (x = 0 ... 9). The smaller the x, the
323 * greater the cooling level. We prefer to keep
324 * the highest cooling mode when in "active".
325 */
326 active = ATZ_ACTIVE_NONE;
327
328 for (i = ATZ_NLEVELS - 1; i >= 0; i--) {
329
330 if (sc->sc_zone.ac[i] == ATZ_TMP_INVALID)
331 continue;
332
333 if (sc->sc_zone.ac[i] <= tmp)
334 active = i;
335 }
336
337 flags = sc->sc_flags & ~(ATZ_F_CRITICAL | ATZ_F_HOT | ATZ_F_PASSIVE);
338
339 if (sc->sc_zone.psv != ATZ_TMP_INVALID && tmp >= sc->sc_zone.psv)
340 flags |= ATZ_F_PASSIVE;
341
342 if (sc->sc_zone.hot != ATZ_TMP_INVALID && tmp >= sc->sc_zone.hot)
343 flags |= ATZ_F_HOT;
344
345 if (sc->sc_zone.crt != ATZ_TMP_INVALID && tmp >= sc->sc_zone.crt)
346 flags |= ATZ_F_CRITICAL;
347
348 if (flags != sc->sc_flags) {
349
350 changed = (sc->sc_flags ^ flags) & flags;
351 sc->sc_flags = flags;
352
353 if ((changed & ATZ_F_CRITICAL) != 0) {
354 sc->sc_temp_sensor.state = ENVSYS_SCRITOVER;
355
356 aprint_debug_dev(dv, "zone went critical, %s C\n",
357 acpitz_celcius_string(tmp));
358
359 } else if ((changed & ATZ_F_HOT) != 0) {
360 sc->sc_temp_sensor.state = ENVSYS_SCRITOVER;
361
362 aprint_debug_dev(dv, "zone went hot, %s C\n",
363 acpitz_celcius_string(tmp));
364 }
365 }
366
367 /* Power on the fans. */
368 if (sc->sc_active != active) {
369
370 if (sc->sc_active != ATZ_ACTIVE_NONE)
371 acpitz_power_zone(sc, sc->sc_active, 0);
372
373 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: active cooling "
374 "level %d\n", device_xname(dv), active));
375
376 if (active != ATZ_ACTIVE_NONE)
377 acpitz_power_zone(sc, active, 1);
378
379 sc->sc_active = active;
380 }
381 }
382
383 static char *
acpitz_celcius_string(int dk)384 acpitz_celcius_string(int dk)
385 {
386 static char buf[10];
387 int dc;
388
389 dc = abs(dk - ATZ_ZEROC);
390
391 (void)snprintf(buf, sizeof(buf), "%s%d.%d",
392 (dk >= ATZ_ZEROC) ? "" : "-", dc / 10, dc % 10);
393
394 return buf;
395 }
396
397 static ACPI_STATUS
acpitz_switch_cooler(ACPI_OBJECT * obj,void * arg)398 acpitz_switch_cooler(ACPI_OBJECT *obj, void *arg)
399 {
400 int flag, pwr_state;
401 ACPI_HANDLE cooler;
402 ACPI_STATUS rv;
403
404 /*
405 * The _ALx object is a package in which the elements
406 * are reference handles to an active cooling device
407 * (typically PNP0C0B, ACPI fan device). Try to turn
408 * on (or off) the power resources behind these handles
409 * to start (or terminate) the active cooling.
410 */
411 flag = *(int *)arg;
412 pwr_state = (flag != 0) ? ACPI_STATE_D0 : ACPI_STATE_D3;
413
414 rv = acpi_eval_reference_handle(obj, &cooler);
415
416 if (ACPI_FAILURE(rv))
417 return rv;
418
419 (void)acpi_power_set(cooler, pwr_state);
420
421 return AE_OK;
422 }
423
424 /*
425 * acpitz_power_zone:
426 *
427 * Power on or off the i:th part of the zone zone.
428 */
429 static void
acpitz_power_zone(struct acpitz_softc * sc,int i,int on)430 acpitz_power_zone(struct acpitz_softc *sc, int i, int on)
431 {
432
433 KASSERT(i >= 0 && i < ATZ_NLEVELS);
434
435 (void)acpi_foreach_package_object(sc->sc_zone.al[i].Pointer,
436 acpitz_switch_cooler, &on);
437 }
438
439
440 /*
441 * acpitz_power_off:
442 *
443 * Power off parts of the zone.
444 */
445 static void
acpitz_power_off(struct acpitz_softc * sc)446 acpitz_power_off(struct acpitz_softc *sc)
447 {
448 int i;
449
450 for (i = 0 ; i < ATZ_NLEVELS; i++) {
451
452 if (sc->sc_zone.al[i].Pointer == NULL)
453 continue;
454
455 acpitz_power_zone(sc, i, 0);
456 }
457
458 sc->sc_active = ATZ_ACTIVE_NONE;
459 sc->sc_flags &= ~(ATZ_F_CRITICAL | ATZ_F_HOT | ATZ_F_PASSIVE);
460 }
461
462 static void
acpitz_get_zone(void * opaque,int verbose)463 acpitz_get_zone(void *opaque, int verbose)
464 {
465 device_t dv = opaque;
466 struct acpitz_softc *sc = device_private(dv);
467 int comma, i, valid_levels;
468 ACPI_OBJECT *obj;
469 ACPI_STATUS rv;
470 char buf[5];
471
472 if (sc->sc_first != true) {
473 acpitz_power_off(sc);
474
475 for (i = 0; i < ATZ_NLEVELS; i++) {
476
477 if (sc->sc_zone.al[i].Pointer != NULL)
478 ACPI_FREE(sc->sc_zone.al[i].Pointer);
479
480 sc->sc_zone.al[i].Pointer = NULL;
481 }
482 }
483
484 valid_levels = 0;
485
486 for (i = 0; i < ATZ_NLEVELS; i++) {
487
488 (void)snprintf(buf, sizeof(buf), "_AC%d", i);
489
490 if (acpitz_get_integer(dv, buf, &sc->sc_zone.ac[i]))
491 continue;
492
493 (void)snprintf(buf, sizeof(buf), "_AL%d", i);
494
495 rv = acpi_eval_struct(sc->sc_node->ad_handle, buf,
496 &sc->sc_zone.al[i]);
497
498 if (ACPI_FAILURE(rv)) {
499 sc->sc_zone.al[i].Pointer = NULL;
500 continue;
501 }
502
503 obj = sc->sc_zone.al[i].Pointer;
504
505 if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count == 0) {
506 sc->sc_zone.al[i].Pointer = NULL;
507 ACPI_FREE(obj);
508 continue;
509 }
510
511 if (sc->sc_first != false)
512 aprint_normal_dev(dv, "active cooling level %d: %sC\n",
513 i, acpitz_celcius_string(sc->sc_zone.ac[i]));
514
515 valid_levels++;
516 }
517
518 /*
519 * A brief summary (ACPI 4.0, section 11.4):
520 *
521 * _TMP : current temperature (in tenths of degrees)
522 * _CRT : critical trip-point at which to shutdown
523 * _HOT : critical trip-point at which to go to S4
524 * _PSV : passive cooling policy threshold
525 * _TC1 : thermal constant for passive cooling
526 * _TC2 : thermal constant for passive cooling
527 */
528 (void)acpitz_get_integer(dv, "_TMP", &sc->sc_zone.tmp);
529 (void)acpitz_get_integer(dv, "_CRT", &sc->sc_zone.crt);
530 (void)acpitz_get_integer(dv, "_HOT", &sc->sc_zone.hot);
531 (void)acpitz_get_integer(dv, "_PSV", &sc->sc_zone.psv);
532 (void)acpitz_get_integer(dv, "_TC1", &sc->sc_zone.tc1);
533 (void)acpitz_get_integer(dv, "_TC2", &sc->sc_zone.tc2);
534
535 /*
536 * If _RTV is not present or present and zero,
537 * values are absolute (see ACPI 4.0, 425).
538 */
539 acpitz_get_integer(dv, "_RTV", &sc->sc_zone.rtv);
540
541 if (sc->sc_zone.rtv == ATZ_TMP_INVALID)
542 sc->sc_zone.rtv = 0;
543
544 acpitz_sane_temp(&sc->sc_zone.tmp);
545 acpitz_sane_temp(&sc->sc_zone.crt);
546 acpitz_sane_temp(&sc->sc_zone.hot);
547 acpitz_sane_temp(&sc->sc_zone.psv);
548
549 if (verbose != 0) {
550 comma = 0;
551
552 aprint_verbose_dev(dv, "levels: ");
553
554 if (sc->sc_zone.crt != ATZ_TMP_INVALID) {
555 aprint_verbose("critical %s C",
556 acpitz_celcius_string(sc->sc_zone.crt));
557 comma = 1;
558 }
559
560 if (sc->sc_zone.hot != ATZ_TMP_INVALID) {
561 aprint_verbose("%shot %s C", comma ? ", " : "",
562 acpitz_celcius_string(sc->sc_zone.hot));
563 comma = 1;
564 }
565
566 if (sc->sc_zone.psv != ATZ_TMP_INVALID) {
567 aprint_verbose("%spassive %s C", comma ? ", " : "",
568 acpitz_celcius_string(sc->sc_zone.psv));
569 comma = 1;
570 }
571
572 if (valid_levels == 0) {
573 sc->sc_flags |= ATZ_F_PASSIVEONLY;
574
575 if (sc->sc_first != false)
576 aprint_verbose("%spassive cooling", comma ?
577 ", " : "");
578 }
579
580 aprint_verbose("\n");
581 }
582
583 for (i = 0; i < ATZ_NLEVELS; i++)
584 acpitz_sane_temp(&sc->sc_zone.ac[i]);
585
586 acpitz_power_off(sc);
587 sc->sc_first = false;
588 }
589
590 static void
acpitz_notify_handler(ACPI_HANDLE hdl,uint32_t notify,void * opaque)591 acpitz_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
592 {
593 ACPI_OSD_EXEC_CALLBACK func = NULL;
594 device_t dv = opaque;
595
596 switch (notify) {
597
598 case ACPI_NOTIFY_TZ_ZONE:
599 func = acpitz_get_status;
600 break;
601
602 case ACPI_NOTIFY_TZ_TRIP:
603 case ACPI_NOTIFY_TZ_DEVLIST:
604 func = acpitz_get_zone_quiet;
605 break;
606
607 default:
608 aprint_debug_dev(dv, "unknown notify 0x%02X\n", notify);
609 return;
610 }
611
612 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, func, dv);
613 }
614
615 static void
acpitz_sane_temp(uint32_t * tmp)616 acpitz_sane_temp(uint32_t *tmp)
617 {
618 /* Sane temperatures are beteen 0 and 150 C. */
619 if (*tmp < ATZ_ZEROC || *tmp > ATZ_ZEROC + 1500)
620 *tmp = ATZ_TMP_INVALID;
621 }
622
623 static int
acpitz_get_integer(device_t dv,const char * cm,uint32_t * val)624 acpitz_get_integer(device_t dv, const char *cm, uint32_t *val)
625 {
626 struct acpitz_softc *sc = device_private(dv);
627 ACPI_INTEGER tmp;
628 ACPI_STATUS rv;
629
630 rv = acpi_eval_integer(sc->sc_node->ad_handle, cm, &tmp);
631
632 if (ACPI_FAILURE(rv)) {
633 *val = ATZ_TMP_INVALID;
634
635 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT,
636 "%s: failed to evaluate %s: %s\n",
637 device_xname(dv), cm, AcpiFormatException(rv)));
638
639 return 1;
640 }
641
642 *val = tmp;
643
644 return 0;
645 }
646
647 static int
acpitz_get_fanspeed(device_t dv,uint32_t * fanmin,uint32_t * fanmax,uint32_t * fancurrent)648 acpitz_get_fanspeed(device_t dv,
649 uint32_t *fanmin, uint32_t *fanmax, uint32_t *fancurrent)
650 {
651 struct acpitz_softc *sc = device_private(dv);
652 ACPI_INTEGER fmin, fmax, fcurr;
653 ACPI_HANDLE handle;
654 ACPI_STATUS rv;
655 int rc = 0;
656
657 handle = sc->sc_node->ad_handle;
658
659 rv = acpi_eval_integer(handle, "FMIN", &fmin);
660
661 if (ACPI_FAILURE(rv)) {
662 fmin = ATZ_TMP_INVALID;
663 rc = 1;
664 }
665
666 rv = acpi_eval_integer(handle, "FMAX", &fmax);
667
668 if (ACPI_FAILURE(rv)) {
669 fmax = ATZ_TMP_INVALID;
670 rc = 1;
671 }
672 rv = acpi_eval_integer(handle, "FRSP", &fcurr);
673
674 if (ACPI_FAILURE(rv)) {
675 fcurr = ATZ_TMP_INVALID;
676 rc = 1;
677 }
678
679 if (fanmin != NULL)
680 *fanmin = fmin;
681
682 if (fanmax != NULL)
683 *fanmax = fmax;
684
685 if (fancurrent != NULL)
686 *fancurrent = fcurr;
687
688 return rc;
689 }
690
691 #ifdef notyet
692 static ACPI_STATUS
acpitz_set_fanspeed(device_t dv,uint32_t fanspeed)693 acpitz_set_fanspeed(device_t dv, uint32_t fanspeed)
694 {
695 struct acpitz_softc *sc = device_private(dv);
696 ACPI_HANDLE handle;
697 ACPI_STATUS rv;
698
699 handle = sc->sc_node->ad_handle;
700
701 rv = acpi_eval_set_integer(handle, "FSSP", fanspeed);
702
703 if (ACPI_FAILURE(rv))
704 aprint_debug_dev(dv, "failed to set fan speed to %u RPM: %s\n",
705 fanspeed, AcpiFormatException(rv));
706
707 return rv;
708 }
709 #endif
710
711 static void
acpitz_print_processor_list(device_t dv)712 acpitz_print_processor_list(device_t dv)
713 {
714 struct acpitz_softc *sc = device_private(dv);
715 ACPI_HANDLE handle = sc->sc_node->ad_handle;
716 ACPI_OBJECT *obj, *pref;
717 ACPI_HANDLE prhandle;
718 ACPI_BUFFER buf;
719 ACPI_STATUS rv;
720 struct cpu_info *ci;
721 unsigned int i, cnt;
722
723 rv = acpi_eval_struct(handle, "_PSL", &buf);
724
725 if (ACPI_FAILURE(rv) || buf.Pointer == NULL)
726 return;
727
728 obj = buf.Pointer;
729
730 if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count == 0)
731 goto done;
732
733 sc->sc_psl_size = sizeof(ci) * (obj->Package.Count + 1);
734 sc->sc_psl = kmem_zalloc(sc->sc_psl_size, KM_SLEEP);
735 for (cnt = i = 0; i < obj->Package.Count; i++) {
736
737 pref = &obj->Package.Elements[i];
738 rv = acpi_eval_reference_handle(pref, &prhandle);
739
740 if (ACPI_FAILURE(rv))
741 continue;
742
743 ci = acpi_match_cpu_handle(prhandle);
744
745 if (ci == NULL)
746 continue;
747
748 if (cnt == 0)
749 aprint_normal(":");
750
751 aprint_normal(" %s", device_xname(ci->ci_dev));
752
753 if (sc->sc_psl)
754 sc->sc_psl[cnt] = ci;
755 ++cnt;
756 }
757
758 done:
759 ACPI_FREE(buf.Pointer);
760 }
761
762 static void
acpitz_tick(void * opaque)763 acpitz_tick(void *opaque)
764 {
765 device_t dv = opaque;
766 struct acpitz_softc *sc = device_private(dv);
767
768 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpitz_get_status, dv);
769
770 callout_schedule(&sc->sc_callout, sc->sc_zone.tzp * hz / 10);
771 }
772
773 static void
acpitz_init_envsys(device_t dv)774 acpitz_init_envsys(device_t dv)
775 {
776 const int flags = ENVSYS_FMONLIMITS | ENVSYS_FMONNOTSUPP |
777 ENVSYS_FHAS_ENTROPY;
778 struct acpitz_softc *sc = device_private(dv);
779 unsigned int i;
780
781 sc->sc_sme = sysmon_envsys_create();
782
783 sc->sc_sme->sme_cookie = sc;
784 sc->sc_sme->sme_name = device_xname(dv);
785 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
786 sc->sc_sme->sme_get_limits = acpitz_get_limits;
787
788 sc->sc_temp_sensor.flags = flags;
789 sc->sc_temp_sensor.units = ENVSYS_STEMP;
790 sc->sc_temp_sensor.state = ENVSYS_SINVALID;
791
792 memset(sc->sc_temp_sensor.desc, 0, sizeof(sc->sc_temp_sensor.desc));
793 if (sc->sc_psl) {
794 for (i = 0; sc->sc_psl[i] != NULL; i++) {
795 if (i > 0)
796 strlcat(sc->sc_temp_sensor.desc, "/",
797 sizeof(sc->sc_temp_sensor.desc));
798 strlcat(sc->sc_temp_sensor.desc,
799 device_xname(sc->sc_psl[i]->ci_dev),
800 sizeof(sc->sc_temp_sensor.desc));
801 }
802 strlcat(sc->sc_temp_sensor.desc, " ",
803 sizeof(sc->sc_temp_sensor.desc));
804 }
805 strlcat(sc->sc_temp_sensor.desc, "temperature",
806 sizeof(sc->sc_temp_sensor.desc));
807
808 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_temp_sensor))
809 goto out;
810
811 if (sc->sc_have_fan != false) {
812
813 sc->sc_fan_sensor.flags = flags;
814 sc->sc_fan_sensor.units = ENVSYS_SFANRPM;
815 sc->sc_fan_sensor.state = ENVSYS_SINVALID;
816
817 (void)strlcpy(sc->sc_fan_sensor.desc,
818 "FAN", sizeof(sc->sc_fan_sensor.desc));
819
820 /* Ignore error because fan sensor is optional. */
821 (void)sysmon_envsys_sensor_attach(sc->sc_sme,
822 &sc->sc_fan_sensor);
823 }
824
825 if (sysmon_envsys_register(sc->sc_sme) == 0)
826 return;
827
828 out:
829 aprint_error_dev(dv, "unable to register with sysmon\n");
830
831 sysmon_envsys_destroy(sc->sc_sme);
832 sc->sc_sme = NULL;
833 }
834
835 static void
acpitz_get_limits(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)836 acpitz_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
837 sysmon_envsys_lim_t *limits, uint32_t *props)
838 {
839 struct acpitz_softc *sc = sme->sme_cookie;
840
841 switch (edata->units) {
842 case ENVSYS_STEMP:
843 *props = 0;
844 if (sc->sc_zone.hot != ATZ_TMP_INVALID) {
845 *props |= PROP_CRITMAX;
846 limits->sel_critmax = ATZ2UKELVIN(sc->sc_zone.hot);
847 } else if (sc->sc_zone.crt != ATZ_TMP_INVALID) {
848 *props |= PROP_CRITMAX;
849 limits->sel_critmax = ATZ2UKELVIN(sc->sc_zone.crt);
850 }
851 break;
852
853 case ENVSYS_SFANRPM:
854 *props = 0;
855 if (sc->sc_zone.fanmin != ATZ_TMP_INVALID) {
856 *props |= PROP_WARNMIN;
857 limits->sel_warnmin = sc->sc_zone.fanmin;
858 }
859 if (sc->sc_zone.fanmax != ATZ_TMP_INVALID) {
860 *props |= PROP_WARNMAX;
861 limits->sel_warnmax = sc->sc_zone.fanmax;
862 }
863 break;
864 }
865 }
866
867 MODULE(MODULE_CLASS_DRIVER, acpitz, "sysmon_envsys");
868
869 #ifdef _MODULE
870 #include "ioconf.c"
871 #endif
872
873 static int
acpitz_modcmd(modcmd_t cmd,void * aux)874 acpitz_modcmd(modcmd_t cmd, void *aux)
875 {
876 int rv = 0;
877
878 switch (cmd) {
879
880 case MODULE_CMD_INIT:
881
882 #ifdef _MODULE
883 rv = config_init_component(cfdriver_ioconf_acpitz,
884 cfattach_ioconf_acpitz, cfdata_ioconf_acpitz);
885 #endif
886 break;
887
888 case MODULE_CMD_FINI:
889
890 #ifdef _MODULE
891 rv = config_fini_component(cfdriver_ioconf_acpitz,
892 cfattach_ioconf_acpitz, cfdata_ioconf_acpitz);
893 #endif
894 break;
895
896 default:
897 rv = ENOTTY;
898 }
899
900 return rv;
901 }
902