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