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