xref: /netbsd-src/sys/dev/acpi/acpi_tz.c (revision fad4c9f71477ae11cea2ee75ec82151ac770a534)
1 /* $NetBSD: acpi_tz.c,v 1.18 2006/02/20 12:17:49 kochi 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.18 2006/02/20 12:17:49 kochi Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/errno.h>
39 #include <sys/ioctl.h>
40 #include <sys/syslog.h>
41 #include <sys/device.h>
42 #include <sys/callout.h>
43 #include <sys/proc.h>
44 #include <sys/lock.h>
45 #include <dev/sysmon/sysmonvar.h>
46 
47 #include <dev/acpi/acpica.h>
48 #include <dev/acpi/acpireg.h>
49 #include <dev/acpi/acpivar.h>
50 
51 /* flags */
52 #define ATZ_F_VERBOSE		0x01	/* show events to console */
53 #define ATZ_F_CRITICAL		0x02	/* zone critical */
54 #define ATZ_F_HOT		0x04	/* zone hot */
55 #define ATZ_F_PASSIVE		0x08	/* zone passive cooling */
56 #define ATZ_F_PASSIVEONLY	0x10	/* zone is passive cooling only */
57 
58 /* no active cooling level */
59 #define ATZ_ACTIVE_NONE -1
60 
61 /* constants */
62 #define ATZ_TZP_RATE	300	/* default if no _TZP CM present (30 secs) */
63 #define ATZ_NLEVELS	10	/* number of cooling levels, from ACPI spec */
64 #define ATZ_ZEROC	2732	/* 0C in tenths degrees Kelvin */
65 #define ATZ_TMP_INVALID	0xffffffff	/* invalid temperature */
66 #define ATZ_ZONE_EXPIRE	9000	/* zone info refetch interval (15min) */
67 
68 /* sensor indexes */
69 #define ATZ_SENSOR_TEMP	0	/* thermal zone temperature */
70 #define ATZ_NUMSENSORS	1	/* number of sensors */
71 
72 static const struct envsys_range acpitz_ranges[] = {
73 	{ 0, 1,	ATZ_SENSOR_TEMP },
74 };
75 
76 static int	acpitz_match(struct device *, struct cfdata *, void *);
77 static void	acpitz_attach(struct device *, struct device *, void *);
78 
79 /*
80  * ACPI Temperature Zone information. Note all temperatures are reported
81  * in tenths of degrees Kelvin
82  */
83 struct acpitz_zone {
84 	/* Active cooling temperature threshold */
85 	UINT32 ac[ATZ_NLEVELS];
86 	/* Package of references to all active cooling devices for a level */
87 	ACPI_BUFFER al[ATZ_NLEVELS];
88 	/* Critical temperature threshold for system shutdown */
89 	UINT32 crt;
90 	/* Critical temperature threshold for S4 sleep */
91 	UINT32 hot;
92 	/* Package of references to processor objects for passive cooling */
93 	ACPI_BUFFER psl;
94 	/* Passive cooling temperature threshold */
95 	UINT32 psv;
96 	/* Thermal constants for use in passive cooling formulas */
97 	UINT32 tc1, tc2;
98 	/* Current temperature of the thermal zone */
99 	UINT32 tmp;
100 	/* Thermal sampling period for passive cooling, in tenths of seconds */
101 	UINT32 tsp;
102 	/* Package of references to devices in this TZ (optional) */
103 	ACPI_BUFFER tzd;
104 	/* Recommended TZ polling frequency, in tenths of seconds */
105 	UINT32 tzp;
106 };
107 
108 struct acpitz_softc {
109 	struct device sc_dev;
110 	struct acpi_devnode *sc_devnode;
111 	struct acpitz_zone sc_zone;
112 	struct callout sc_callout;
113 	struct envsys_tre_data sc_data[ATZ_NUMSENSORS];
114 	struct envsys_basic_info sc_info[ATZ_NUMSENSORS];
115 	struct sysmon_envsys sc_sysmon;
116 	struct simplelock sc_slock;
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 
123 static void	acpitz_get_status(void *);
124 static void	acpitz_get_zone(void *, int);
125 static void	acpitz_get_zone_quiet(void *);
126 static char*	acpitz_celcius_string(int);
127 static void	acpitz_print_status(struct acpitz_softc *);
128 static void	acpitz_power_off(struct acpitz_softc *);
129 static void	acpitz_power_zone(struct acpitz_softc *, int, int);
130 static void	acpitz_sane_temp(UINT32 *tmp);
131 static ACPI_STATUS
132 		acpitz_switch_cooler(ACPI_OBJECT *, void *);
133 static void	acpitz_notify_handler(ACPI_HANDLE, UINT32, void *);
134 static int	acpitz_get_integer(struct acpitz_softc *, const char *, UINT32 *);
135 static void	acpitz_tick(void *);
136 static void	acpitz_init_envsys(struct acpitz_softc *);
137 static int	acpitz_gtredata(struct sysmon_envsys *,
138 				struct envsys_tre_data *);
139 static int	acpitz_streinfo(struct sysmon_envsys *,
140 				struct envsys_basic_info *);
141 
142 CFATTACH_DECL(acpitz, sizeof(struct acpitz_softc), acpitz_match,
143     acpitz_attach, NULL, NULL);
144 
145 /*
146  * acpitz_match: autoconf(9) match routine
147  */
148 static int
149 acpitz_match(struct device *parent, struct cfdata *match, void *aux)
150 {
151 	struct acpi_attach_args *aa = aux;
152 
153 	if (aa->aa_node->ad_type != ACPI_TYPE_THERMAL)
154 		return 0;
155 
156 	return 1;
157 }
158 
159 /*
160  * acpitz_attach: autoconf(9) attach routine
161  */
162 static void
163 acpitz_attach(struct device *parent, struct device *self, void *aux)
164 {
165 	struct acpitz_softc *sc = (struct acpitz_softc *)self;
166 	struct acpi_attach_args *aa = aux;
167 	ACPI_STATUS rv;
168 	ACPI_INTEGER v;
169 
170 #if 0
171 	sc->sc_flags = ATZ_F_VERBOSE;
172 #endif
173 	sc->sc_devnode = aa->aa_node;
174 
175 	aprint_naive(": ACPI Thermal Zone\n");
176 	aprint_normal(": ACPI Thermal Zone\n");
177 
178 	rv = acpi_eval_integer(sc->sc_devnode->ad_handle, "_TZP", &v);
179 	if (ACPI_FAILURE(rv)) {
180 		aprint_verbose("%s: unable to get polling interval; using default of",
181 		    sc->sc_dev.dv_xname);
182 		sc->sc_zone.tzp = ATZ_TZP_RATE;
183 	} else {
184 		sc->sc_zone.tzp = v;
185 		aprint_verbose("%s: polling interval is", sc->sc_dev.dv_xname);
186 	}
187 	aprint_verbose(" %d.%ds\n", sc->sc_zone.tzp / 10, sc->sc_zone.tzp % 10);
188 
189 	/* XXX a value of 0 means "polling is not necessary" */
190 	if (sc->sc_zone.tzp == 0)
191 		sc->sc_zone.tzp = ATZ_TZP_RATE;
192 
193 	sc->sc_zone_expire = ATZ_ZONE_EXPIRE / sc->sc_zone.tzp;
194 
195 	acpitz_get_zone(sc, 1);
196 	acpitz_get_status(sc);
197 
198 	rv = AcpiInstallNotifyHandler(sc->sc_devnode->ad_handle,
199 	    ACPI_SYSTEM_NOTIFY, acpitz_notify_handler, sc);
200 	if (ACPI_FAILURE(rv)) {
201 		aprint_error("%s: unable to install SYSTEM NOTIFY handler: %s\n",
202 		    sc->sc_dev.dv_xname, AcpiFormatException(rv));
203 		return;
204 	}
205 
206 	callout_init(&sc->sc_callout);
207 	callout_reset(&sc->sc_callout, sc->sc_zone.tzp * hz / 10,
208 	    acpitz_tick, sc);
209 
210 	acpitz_init_envsys(sc);
211 }
212 
213 static void
214 acpitz_get_zone_quiet(void *opaque)
215 {
216 	acpitz_get_zone(opaque, 0);
217 }
218 
219 static void
220 acpitz_get_status(void *opaque)
221 {
222 	struct acpitz_softc *sc = opaque;
223 	UINT32 tmp, active;
224 	int i, flags;
225 
226 	sc->sc_zone_expire--;
227 	if (sc->sc_zone_expire <= 0) {
228 		sc->sc_zone_expire = ATZ_ZONE_EXPIRE / sc->sc_zone.tzp;
229 		if (sc->sc_flags & ATZ_F_VERBOSE)
230 			printf("%s: force refetch zone\n", sc->sc_dev.dv_xname);
231 		acpitz_get_zone(sc, 0);
232 	}
233 
234 	if (acpitz_get_integer(sc, "_TMP", &tmp)) {
235 		printf("%s: failed to evaluate _TMP\n", sc->sc_dev.dv_xname);
236 		return;
237 	}
238 	sc->sc_zone.tmp = tmp;
239 	/* XXX sanity check for tmp here? */
240 
241 	/*
242 	 * The temperature unit for envsys(4) is microKelvin, so convert to
243 	 * that from ACPI's microKelvin. Also, the ACPI specification assumes
244 	 * that K = C + 273.2 rather than the nominal 273.15 used by envsys(4),
245 	 * so we correct for that too.
246 	 */
247 	sc->sc_data[ATZ_SENSOR_TEMP].cur.data_us =
248 	    sc->sc_zone.tmp * 100000 - 50000;
249 	sc->sc_data[ATZ_SENSOR_TEMP].validflags |= ENVSYS_FCURVALID;
250 
251 	if (sc->sc_flags & ATZ_F_VERBOSE)
252 		acpitz_print_status(sc);
253 
254 	if (sc->sc_flags & ATZ_F_PASSIVEONLY) {
255 		/* Passive Cooling: XXX not yet */
256 
257 	} else {
258 		/* Active Cooling */
259 
260 		/* temperature threshold: _AC0 > ... > _AC9 */
261 		active = ATZ_ACTIVE_NONE;
262 		for (i = ATZ_NLEVELS - 1; i >= 0; i--) {
263 			if (sc->sc_zone.ac[i] == ATZ_TMP_INVALID)
264 				continue;
265 
266 			/* we want to keep highest cooling mode in 'active' */
267 			if (sc->sc_zone.ac[i] <= tmp)
268 				active = i;
269 		}
270 
271 		flags = sc->sc_flags & ~(ATZ_F_CRITICAL|ATZ_F_HOT|ATZ_F_PASSIVE);
272 		if (sc->sc_zone.psv != ATZ_TMP_INVALID &&
273 		    tmp >= sc->sc_zone.psv)
274 			flags |= ATZ_F_PASSIVE;
275 		if (sc->sc_zone.hot != ATZ_TMP_INVALID &&
276 		    tmp >= sc->sc_zone.hot)
277 			flags |= ATZ_F_HOT;
278 		if (sc->sc_zone.crt != ATZ_TMP_INVALID &&
279 		    tmp >= sc->sc_zone.crt)
280 			flags |= ATZ_F_CRITICAL;
281 
282 		if (flags != sc->sc_flags) {
283 			int changed = (sc->sc_flags ^ flags) & flags;
284 			sc->sc_flags = flags;
285 			if (changed & ATZ_F_CRITICAL)
286 				printf("%s: zone went critical at temp %sC\n",
287 				    sc->sc_dev.dv_xname,
288 				    acpitz_celcius_string(tmp));
289 			else if (changed & ATZ_F_HOT)
290 				printf("%s: zone went hot at temp %sC\n",
291 				    sc->sc_dev.dv_xname,
292 				    acpitz_celcius_string(tmp));
293 		}
294 
295 		/* power on fans */
296 		if (sc->sc_active != active) {
297 			if (sc->sc_active != ATZ_ACTIVE_NONE)
298 				acpitz_power_zone(sc, sc->sc_active, 0);
299 
300 			if (active != ATZ_ACTIVE_NONE) {
301 				if (sc->sc_flags & ATZ_F_VERBOSE)
302 					printf("%s: active cooling level %u\n",
303 					    sc->sc_dev.dv_xname, active);
304 				acpitz_power_zone(sc, active, 1);
305 			}
306 
307 			sc->sc_active = active;
308 		}
309 	}
310 
311 	return;
312 }
313 
314 static char *
315 acpitz_celcius_string(int dk)
316 {
317 	static char buf[10];
318 
319 	snprintf(buf, sizeof(buf), "%d.%d", (dk - ATZ_ZEROC) / 10,
320 	    (dk - ATZ_ZEROC) % 10);
321 
322 	return buf;
323 }
324 
325 static void
326 acpitz_print_status(struct acpitz_softc *sc)
327 {
328 
329 	printf("%s: zone temperature is now %sC\n", sc->sc_dev.dv_xname,
330 	    acpitz_celcius_string(sc->sc_zone.tmp));
331 
332 	return;
333 }
334 
335 static ACPI_STATUS
336 acpitz_switch_cooler(ACPI_OBJECT *obj, void *arg)
337 {
338 	ACPI_HANDLE cooler;
339 	ACPI_STATUS rv;
340 	int pwr_state, flag;
341 
342 	flag = *(int *)arg;
343 
344 	if (flag)
345 		pwr_state = ACPI_STATE_D0;
346 	else
347 		pwr_state = ACPI_STATE_D3;
348 
349 	switch(obj->Type) {
350 	case ACPI_TYPE_ANY:
351 		cooler = obj->Reference.Handle;
352 		break;
353 	case ACPI_TYPE_STRING:
354 		rv = AcpiGetHandle(NULL, obj->String.Pointer, &cooler);
355 		if (ACPI_FAILURE(rv)) {
356 			printf("failed to get handler from %s\n",
357 			    obj->String.Pointer);
358 			return rv;
359 		}
360 		break;
361 	default:
362 		printf("unknown power type: %d\n", obj->Type);
363 		return AE_OK;
364 	}
365 
366 	rv = acpi_pwr_switch_consumer(cooler, pwr_state);
367 	if (rv != AE_BAD_PARAMETER && ACPI_FAILURE(rv)) {
368 		printf("failed to change state for %s: %s\n",
369 		    acpi_name(obj->Reference.Handle),
370 		    AcpiFormatException(rv));
371 	}
372 
373 	return AE_OK;
374 }
375 
376 /*
377  * acpitz_power_zone:
378  *	power on or off the i:th part of the zone zone
379  */
380 static void
381 acpitz_power_zone(struct acpitz_softc *sc, int i, int on)
382 {
383 	KASSERT(i >= 0 && i < ATZ_NLEVELS);
384 
385 	acpi_foreach_package_object(sc->sc_zone.al[i].Pointer,
386 	    acpitz_switch_cooler, &on);
387 }
388 
389 
390 /*
391  * acpitz_power_off:
392  *	power off parts of the zone
393  */
394 static void
395 acpitz_power_off(struct acpitz_softc *sc)
396 {
397 	int i;
398 
399 	for (i = 0 ; i < ATZ_NLEVELS; i++) {
400 		if (sc->sc_zone.al[i].Pointer == NULL)
401 			continue;
402 		acpitz_power_zone(sc, i, 0);
403 	}
404 	sc->sc_active = ATZ_ACTIVE_NONE;
405 	sc->sc_flags &= ~(ATZ_F_CRITICAL|ATZ_F_HOT|ATZ_F_PASSIVE);
406 }
407 
408 static void
409 acpitz_get_zone(void *opaque, int verbose)
410 {
411 	struct acpitz_softc *sc = opaque;
412 	ACPI_STATUS rv;
413 	char buf[8];
414 	int i, valid_levels;
415 	static int first = 1;
416 
417 	if (!first) {
418 		acpitz_power_off(sc);
419 
420 		for (i = 0; i < ATZ_NLEVELS; i++) {
421 			if (sc->sc_zone.al[i].Pointer != NULL)
422 				AcpiOsFree(sc->sc_zone.al[i].Pointer);
423 			sc->sc_zone.al[i].Pointer = NULL;
424 		}
425 	}
426 
427 	valid_levels = 0;
428 
429 	for (i = 0; i < ATZ_NLEVELS; i++) {
430 		ACPI_OBJECT *obj;
431 
432 		snprintf(buf, sizeof(buf), "_AC%d", i);
433 		if (acpitz_get_integer(sc, buf, &sc->sc_zone.ac[i]))
434 			continue;
435 
436 		snprintf(buf, sizeof(buf), "_AL%d", i);
437 		rv = acpi_eval_struct(sc->sc_devnode->ad_handle, buf,
438 		    &sc->sc_zone.al[i]);
439 		if (ACPI_FAILURE(rv)) {
440 			printf("failed getting _AL%d", i);
441 			sc->sc_zone.al[i].Pointer = NULL;
442 			continue;
443 		}
444 
445 		obj = sc->sc_zone.al[i].Pointer;
446 		if (obj != NULL) {
447 			if (obj->Type != ACPI_TYPE_PACKAGE) {
448 				printf("%s: ac%d not package\n",
449 				    sc->sc_dev.dv_xname, i);
450 				AcpiOsFree(obj);
451 				sc->sc_zone.al[i].Pointer = NULL;
452 				continue;
453 			}
454 		}
455 
456 		if (first)
457 			printf("%s: active cooling level %d: %sC\n",
458 			    sc->sc_dev.dv_xname, i,
459 			    acpitz_celcius_string(sc->sc_zone.ac[i]));
460 
461 		valid_levels++;
462 	}
463 
464 	if (valid_levels == 0) {
465 		sc->sc_flags |= ATZ_F_PASSIVEONLY;
466 		if (first)
467 			printf("%s: passive cooling mode only\n",
468 			    sc->sc_dev.dv_xname);
469 	}
470 
471 	acpitz_get_integer(sc, "_TMP", &sc->sc_zone.tmp);
472 	acpitz_get_integer(sc, "_CRT", &sc->sc_zone.crt);
473 	acpitz_get_integer(sc, "_HOT", &sc->sc_zone.hot);
474 	sc->sc_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
475 	sc->sc_zone.psl.Pointer = NULL;
476 	AcpiEvaluateObject(sc, "_PSL", NULL, &sc->sc_zone.psl);
477 	acpitz_get_integer(sc, "_PSV", &sc->sc_zone.psv);
478 	acpitz_get_integer(sc, "_TC1", &sc->sc_zone.tc1);
479 	acpitz_get_integer(sc, "_TC2", &sc->sc_zone.tc2);
480 
481 	acpitz_sane_temp(&sc->sc_zone.tmp);
482 	acpitz_sane_temp(&sc->sc_zone.crt);
483 	acpitz_sane_temp(&sc->sc_zone.hot);
484 	acpitz_sane_temp(&sc->sc_zone.psv);
485 
486 	if (verbose) {
487 		printf("%s:", sc->sc_dev.dv_xname);
488 		if (sc->sc_zone.crt != ATZ_TMP_INVALID)
489 			printf(" critical %sC",
490 			    acpitz_celcius_string(sc->sc_zone.crt));
491 		if (sc->sc_zone.hot != ATZ_TMP_INVALID)
492 			printf(" hot %sC",
493 			    acpitz_celcius_string(sc->sc_zone.hot));
494 		if (sc->sc_zone.psv != ATZ_TMP_INVALID)
495 			printf(" passive %sC",
496 			    acpitz_celcius_string(sc->sc_zone.tmp));
497 		printf("\n");
498 	}
499 
500 	for (i = 0; i < ATZ_NLEVELS; i++)
501 		acpitz_sane_temp(&sc->sc_zone.ac[i]);
502 
503 	acpitz_power_off(sc);
504 	first = 0;
505 }
506 
507 
508 static void
509 acpitz_notify_handler(ACPI_HANDLE hdl, UINT32 notify, void *opaque)
510 {
511 	struct acpitz_softc *sc = opaque;
512 	ACPI_OSD_EXEC_CALLBACK func = NULL;
513 	const char *name;
514 	int rv;
515 
516 	switch (notify) {
517 	case ACPI_NOTIFY_ThermalZoneStatusChanged:
518 		func = acpitz_get_status;
519 		name = "status check";
520 		break;
521 	case ACPI_NOTIFY_ThermalZoneTripPointsChanged:
522 	case ACPI_NOTIFY_DeviceListsChanged:
523 		func = acpitz_get_zone_quiet;
524 		name = "get zone";
525 		break;
526 	default:
527 		printf("%s: received unhandled notify message 0x%x\n",
528 		    sc->sc_dev.dv_xname, notify);
529 		return;
530 	}
531 
532 	KASSERT(func != NULL);
533 
534 	rv = AcpiOsQueueForExecution(OSD_PRIORITY_LO, func, sc);
535 	if (rv != AE_OK)
536 		printf("%s: unable to queue %s\n", sc->sc_dev.dv_xname, name);
537 
538 	return;
539 }
540 
541 static void
542 acpitz_sane_temp(UINT32 *tmp)
543 {
544 	/* Sane temperatures are beteen 0 and 150 C */
545 	if (*tmp < ATZ_ZEROC || *tmp > ATZ_ZEROC + 1500)
546 		*tmp = ATZ_TMP_INVALID;
547 }
548 
549 static int
550 acpitz_get_integer(struct acpitz_softc *sc, const char *cm, UINT32 *val)
551 {
552 	ACPI_STATUS rv;
553 	ACPI_INTEGER tmp;
554 
555 	rv = acpi_eval_integer(sc->sc_devnode->ad_handle, cm, &tmp);
556 	if (ACPI_FAILURE(rv)) {
557 #ifdef ACPI_DEBUG
558 		printf("%s: failed to evaluate %s: %s\n", sc->sc_dev.dv_xname,
559 		    cm, AcpiFormatException(rv));
560 #endif
561 		*val = ATZ_TMP_INVALID;
562 		return 1;
563 	}
564 
565 	*val = tmp;
566 
567 	return 0;
568 }
569 
570 static void
571 acpitz_tick(void *opaque)
572 {
573 	struct acpitz_softc *sc = opaque;
574 
575 	callout_reset(&sc->sc_callout, sc->sc_zone.tzp * hz / 10,
576 	    acpitz_tick, opaque);
577 	AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpitz_get_status, sc);
578 
579 	return;
580 }
581 
582 static void
583 acpitz_init_envsys(struct acpitz_softc *sc)
584 {
585 	int i;
586 
587 	simple_lock_init(&sc->sc_slock);
588 
589 	for (i = 0; i < ATZ_NUMSENSORS; i++) {
590 		sc->sc_data[i].sensor = sc->sc_info[i].sensor = i;
591 		sc->sc_data[i].validflags = ENVSYS_FVALID;
592 		sc->sc_info[i].validflags = ENVSYS_FVALID;
593 		sc->sc_data[i].warnflags = ENVSYS_WARN_OK;
594 	}
595 #define INITDATA(index, unit, string) \
596 	sc->sc_data[index].units = unit;				   \
597 	sc->sc_info[index].units = unit;				   \
598 	snprintf(sc->sc_info[index].desc, sizeof(sc->sc_info[index].desc), \
599 	    "%s %s", sc->sc_dev.dv_xname, string);
600 
601 	INITDATA(ATZ_SENSOR_TEMP, ENVSYS_STEMP, "temperature");
602 
603 	/* hook into sysmon */
604 	sc->sc_sysmon.sme_ranges = acpitz_ranges;
605 	sc->sc_sysmon.sme_sensor_info = sc->sc_info;
606 	sc->sc_sysmon.sme_sensor_data = sc->sc_data;
607 	sc->sc_sysmon.sme_cookie = sc;
608 	sc->sc_sysmon.sme_gtredata = acpitz_gtredata;
609 	sc->sc_sysmon.sme_streinfo = acpitz_streinfo;
610 	sc->sc_sysmon.sme_nsensors = ATZ_NUMSENSORS;
611 	sc->sc_sysmon.sme_envsys_version = 1000;
612 
613 	if (sysmon_envsys_register(&sc->sc_sysmon))
614 		printf("%s: unable to register with sysmon\n",
615 		    sc->sc_dev.dv_xname);
616 }
617 
618 int
619 acpitz_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
620 {
621 	struct acpitz_softc *sc = sme->sme_cookie;
622 
623 	simple_lock(&sc->sc_slock);
624 
625 	*tred = sc->sc_data[tred->sensor];
626 
627 	simple_unlock(&sc->sc_slock);
628 
629 	return 0;
630 }
631 
632 static int
633 acpitz_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
634 {
635 
636 	/* XXX not implemented */
637 	binfo->validflags = 0;
638 
639 	return 0;
640 }
641