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