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