xref: /freebsd/sys/dev/acpica/acpi_thermal.c (revision dbad09ef38df6f5114cc3ca8e40e79f6e2f14d66)
1 /*-
2  * Copyright (c) 2000, 2001 Michael Smith
3  * Copyright (c) 2000 BSDi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, 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  *	$FreeBSD$
28  */
29 
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
34 #include <sys/reboot.h>
35 #include <sys/sysctl.h>
36 
37 #include "acpi.h"
38 
39 #include <dev/acpica/acpivar.h>
40 
41 /*
42  * Hooks for the ACPI CA debugging infrastructure
43  */
44 #define _COMPONENT	ACPI_THERMAL
45 MODULE_NAME("THERMAL")
46 
47 #define TZ_ZEROC	2732
48 #define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
49 
50 #define TZ_NOTIFY_TEMPERATURE	0x80
51 #define TZ_NOTIFY_DEVICES	0x81
52 #define TZ_NOTIFY_LEVELS	0x82
53 
54 #define TZ_POLLRATE	(hz * 10)	/* every ten seconds */
55 
56 #define TZ_NUMLEVELS	10		/* defined by ACPI spec */
57 struct acpi_tz_zone {
58     int		ac[TZ_NUMLEVELS];
59     ACPI_BUFFER	al[TZ_NUMLEVELS];
60     int		crt;
61     int		hot;
62     ACPI_BUFFER	psl;
63     int		psv;
64     int		tc1;
65     int		tc2;
66     int		tsp;
67     int		tzp;
68 };
69 
70 
71 struct acpi_tz_softc {
72     device_t			tz_dev;			/* device handle */
73     ACPI_HANDLE			tz_handle;		/* thermal zone handle */
74     struct callout_handle	tz_timeout;		/* poll routine handle */
75     int				tz_temperature;		/* current temperature */
76     int				tz_active;		/* current active cooling */
77 #define TZ_ACTIVE_NONE		-1
78     int				tz_requested;		/* user-requested minimum active cooling */
79     int				tz_thflags;		/* current temperature-related flags */
80 #define TZ_THFLAG_NONE		0
81 #define TZ_THFLAG_PSV		(1<<0)
82 #define TZ_THFLAG_HOT		(1<<2)
83 #define TZ_THFLAG_CRT		(1<<3)
84     int				tz_flags;
85 #define TZ_FLAG_NO_SCP		(1<<0)			/* no _SCP method */
86 #define TZ_FLAG_GETPROFILE	(1<<1)			/* fetch powerprofile in timeout */
87 
88     struct sysctl_ctx_list	tz_sysctl_ctx;		/* sysctl tree */
89     struct sysctl_oid		*tz_sysctl_tree;
90 
91     struct acpi_tz_zone 	tz_zone;		/* thermal zone parameters */
92 };
93 
94 static int	acpi_tz_probe(device_t dev);
95 static int	acpi_tz_attach(device_t dev);
96 static int	acpi_tz_establish(struct acpi_tz_softc *sc);
97 static void	acpi_tz_monitor(struct acpi_tz_softc *sc);
98 static void	acpi_tz_all_off(struct acpi_tz_softc *sc);
99 static void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
100 static void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
101 static void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
102 static void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
103 static int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
104 static void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
105 static void	acpi_tz_timeout(void *arg);
106 static void	acpi_tz_powerprofile(void *arg);
107 
108 static device_method_t acpi_tz_methods[] = {
109     /* Device interface */
110     DEVMETHOD(device_probe,	acpi_tz_probe),
111     DEVMETHOD(device_attach,	acpi_tz_attach),
112 
113     {0, 0}
114 };
115 
116 static driver_t acpi_tz_driver = {
117     "acpi_tz",
118     acpi_tz_methods,
119     sizeof(struct acpi_tz_softc),
120 };
121 
122 devclass_t acpi_tz_devclass;
123 DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
124 
125 static struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
126 static struct sysctl_oid	*acpi_tz_sysctl_tree;
127 
128 /*
129  * Match an ACPI thermal zone.
130  */
131 static int
132 acpi_tz_probe(device_t dev)
133 {
134     int		result;
135 
136     ACPI_LOCK;
137 
138     /* no FUNCTION_TRACE - too noisy */
139 
140     if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
141 	!acpi_disabled("thermal")) {
142 	device_set_desc(dev, "thermal zone");
143 	result = -10;
144     } else {
145 	result = ENXIO;
146     }
147     ACPI_UNLOCK;
148     return(result);
149 }
150 
151 /*
152  * Attach to an ACPI thermal zone.
153  */
154 static int
155 acpi_tz_attach(device_t dev)
156 {
157     struct acpi_tz_softc	*sc;
158     struct acpi_softc		*acpi_sc;
159     int				error;
160     char			oidname[8];
161     int				i;
162 
163     FUNCTION_TRACE(__func__);
164 
165     ACPI_LOCK;
166 
167     sc = device_get_softc(dev);
168     sc->tz_dev = dev;
169     sc->tz_handle = acpi_get_handle(dev);
170     sc->tz_requested = TZ_ACTIVE_NONE;
171 
172     /*
173      * Parse the current state of the thermal zone and build control
174      * structures.
175      */
176     if ((error = acpi_tz_establish(sc)) != 0)
177 	goto out;
178 
179     /*
180      * Register for any Notify events sent to this zone.
181      */
182     AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
183 			     acpi_tz_notify_handler, sc);
184 
185     /*
186      * Create our sysctl nodes.
187      *
188      * XXX we need a mechanism for adding nodes under ACPI.
189      */
190     if (device_get_unit(dev) == 0) {
191 	acpi_sc = acpi_device_get_parent_softc(dev);
192 	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
193 	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
194 					      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
195 					      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
196     }
197     sysctl_ctx_init(&sc->tz_sysctl_ctx);
198     sprintf(oidname, "tz%d", device_get_unit(dev));
199     sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
200 					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
201 					 oidname, CTLFLAG_RD, 0, "");
202     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
203 		   OID_AUTO, "temperature", CTLFLAG_RD,
204 		   &sc->tz_temperature, 0, "current thermal zone temperature");
205     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
206 		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
207 		    sc, 0, acpi_tz_active_sysctl, "I", "");
208 
209     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
210 		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
211 		   &sc->tz_thflags, 0, "thermal zone flags");
212     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
213 		   OID_AUTO, "_PSV", CTLFLAG_RD,
214 		   &sc->tz_zone.psv, 0, "");
215     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
216 		   OID_AUTO, "_HOT", CTLFLAG_RD,
217 		   &sc->tz_zone.hot, 0, "");
218     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
219 		   OID_AUTO, "_CRT", CTLFLAG_RD,
220 		   &sc->tz_zone.crt, 0, "");
221     for (i = 0; i < TZ_NUMLEVELS; i++) {
222 	sprintf(oidname, "_AC%d", i);
223 	SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
224 		       OID_AUTO, oidname, CTLFLAG_RD,
225 		       &sc->tz_zone.ac[i], 0, "");
226     }
227 
228     /*
229      * Register our power profile event handler, and flag it for a manual
230      * invocation by our timeout.  We defer it like this so that the rest
231      * of the subsystem has time to come up.
232      */
233     EVENTHANDLER_REGISTER(powerprofile_change, acpi_tz_powerprofile, sc, 0);
234     sc->tz_flags |= TZ_FLAG_GETPROFILE;
235 
236     /*
237      * Don't bother evaluating/printing the temperature at this point;
238      * on many systems it'll be bogus until the EC is running.
239      */
240 
241  out:
242     ACPI_UNLOCK;
243 
244     /*
245      * Start the timeout routine, with enough delay for the rest of the
246      * subsystem to come up.
247      */
248     sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
249 
250     return_VALUE(error);
251 }
252 
253 /*
254  * Parse the current state of this thermal zone and set up to use it.
255  *
256  * Note that we may have previous state, which will have to be discarded.
257  */
258 static int
259 acpi_tz_establish(struct acpi_tz_softc *sc)
260 {
261     ACPI_OBJECT	*obj;
262     int		i;
263     char	nbuf[8];
264 
265     FUNCTION_TRACE(__func__);
266 
267     ACPI_ASSERTLOCK;
268 
269     /*
270      * Power everything off and erase any existing state.
271      */
272     acpi_tz_all_off(sc);
273     for (i = 0; i < TZ_NUMLEVELS; i++)
274 	if (sc->tz_zone.al[i].Pointer != NULL)
275 	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
276     if (sc->tz_zone.psl.Pointer != NULL)
277 	AcpiOsFree(sc->tz_zone.psl.Pointer);
278     bzero(&sc->tz_zone, sizeof(sc->tz_zone));
279 
280     /*
281      * Evaluate thermal zone parameters.
282      */
283     for (i = 0; i < TZ_NUMLEVELS; i++) {
284 	sprintf(nbuf, "_AC%d", i);
285 	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
286 	sprintf(nbuf, "_AL%d", i);
287 	acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
288 	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
289 	if (obj != NULL) {
290 	    /* should be a package containing a list of power objects */
291 	    if (obj->Type != ACPI_TYPE_PACKAGE) {
292 		device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
293 			      nbuf, obj->Type);
294 		return_VALUE(ENXIO);
295 	    }
296 	}
297     }
298     acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
299     acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
300     acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
301     acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
302     acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
303     acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
304     acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
305     acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
306 
307     /*
308      * Sanity-check the values we've been given.
309      *
310      * XXX what do we do about systems that give us the same value for
311      *     more than one of these setpoints?
312      */
313     acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
314     acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
315     acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
316     for (i = 0; i < TZ_NUMLEVELS; i++)
317 	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
318 
319     /*
320      * Power off everything that we've just been given.
321      */
322     acpi_tz_all_off(sc);
323 
324     return_VALUE(0);
325 }
326 
327 /*
328  * Evaluate the condition of a thermal zone, take appropriate actions.
329  */
330 static void
331 acpi_tz_monitor(struct acpi_tz_softc *sc)
332 {
333     int		temp;
334     int		i;
335     int		newactive, newflags;
336 
337     FUNCTION_TRACE(__func__);
338 
339     ACPI_ASSERTLOCK;
340 
341     /*
342      * Get the current temperature.
343      */
344     if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) {
345 	device_printf(sc->tz_dev, "error fetching current temperature\n");
346 	/* XXX disable zone? go to max cooling? */
347 	return_VOID;
348     }
349     ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
350     sc->tz_temperature = temp;
351 
352     /*
353      * Work out what we ought to be doing right now.
354      *
355      * Note that the _ACx levels sort from hot to cold.
356      */
357     newactive = TZ_ACTIVE_NONE;
358     for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
359 	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
360 	    newactive = i;
361 	    if (sc->tz_active != newactive) {
362 		device_printf(sc->tz_dev,
363 			      "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
364 			      TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
365 	    }
366 	}
367     }
368 
369     /* handle user override of active mode */
370     if (sc->tz_requested > newactive)
371 	newactive = sc->tz_requested;
372 
373     /* update temperature-related flags */
374     newflags = TZ_THFLAG_NONE;
375     if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
376 	newflags |= TZ_THFLAG_PSV;
377     if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
378 	newflags |= TZ_THFLAG_HOT;
379     if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
380 	newflags |= TZ_THFLAG_CRT;
381 
382     /*
383      * If the active cooling state has changed, we have to switch things.
384      */
385     if (newactive != sc->tz_active) {
386 
387 	/* turn off the cooling devices that are on, if any are */
388 	if (sc->tz_active != TZ_ACTIVE_NONE)
389 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
390 				      acpi_tz_switch_cooler_off, sc);
391 
392 	/* turn on cooling devices that are required, if any are */
393 	if (newactive != TZ_ACTIVE_NONE)
394 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
395 				      acpi_tz_switch_cooler_on, sc);
396 	device_printf(sc->tz_dev, "switched from _AC%d to _AC%d\n", sc->tz_active, newactive);
397 	sc->tz_active = newactive;
398     }
399 
400     /*
401      * XXX (de)activate any passive cooling that may be required.
402      */
403 
404     /*
405      * If we have just become _HOT or _CRT, warn the user.
406      *
407      * We should actually shut down at this point, but it's not clear
408      * that some systems don't actually map _CRT to the same value as _AC0.
409      */
410     if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) &&
411 	!(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) {
412 	device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
413 		      TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature);
414 	/* shutdown_nice(RB_POWEROFF);*/
415     }
416     sc->tz_thflags = newflags;
417 
418     return_VOID;
419 }
420 
421 /*
422  * Turn off all the cooling devices.
423  */
424 static void
425 acpi_tz_all_off(struct acpi_tz_softc *sc)
426 {
427     int		i;
428 
429     FUNCTION_TRACE(__func__);
430 
431     ACPI_ASSERTLOCK;
432 
433     /*
434      * Scan all the _ALx objects, and turn them all off.
435      */
436     for (i = 0; i < TZ_NUMLEVELS; i++) {
437 	if (sc->tz_zone.al[i].Pointer == NULL)
438 	    continue;
439 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
440 				  acpi_tz_switch_cooler_off, sc);
441     }
442 
443     /*
444      * XXX revert any passive-cooling options.
445      */
446 
447     sc->tz_active = TZ_ACTIVE_NONE;
448     sc->tz_thflags = TZ_THFLAG_NONE;
449     return_VOID;
450 }
451 
452 /*
453  * Given an object, verify that it's a reference to a device of some sort,
454  * and try to switch it off.
455  */
456 static void
457 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
458 {
459     ACPI_HANDLE		cooler;
460 
461     FUNCTION_TRACE(__func__);
462 
463     ACPI_ASSERTLOCK;
464 
465     switch(obj->Type) {
466     case ACPI_TYPE_STRING:
467 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", obj->String.Pointer));
468 
469 	/*
470 	 * Find the handle for the device and turn it off.
471 	 * The String object here seems to contain a fully-qualified path, so we
472 	 * don't have to search for it in our parents.
473 	 *
474 	 * XXX This may not always be the case.
475 	 */
476 	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK)
477 	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
478 	break;
479 
480     default:
481 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
482 			  obj->Type));
483 	break;
484     }
485     return_VOID;
486 }
487 
488 /*
489  * Given an object, verify that it's a reference to a device of some sort,
490  * and try to switch it on.
491  *
492  * XXX replication of off/on function code is bad, mmmkay?
493  */
494 static void
495 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
496 {
497     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
498     ACPI_HANDLE			cooler;
499     ACPI_STATUS			status;
500 
501     FUNCTION_TRACE(__func__);
502 
503     ACPI_ASSERTLOCK;
504 
505     switch(obj->Type) {
506     case ACPI_TYPE_STRING:
507 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", obj->String.Pointer));
508 
509 	/*
510 	 * Find the handle for the device and turn it off.
511 	 * The String object here seems to contain a fully-qualified path, so we
512 	 * don't have to search for it in our parents.
513 	 *
514 	 * XXX This may not always be the case.
515 	 */
516 	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK) {
517 	    if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) {
518 		device_printf(sc->tz_dev, "failed to activate %s - %s\n",
519 			      obj->String.Pointer, AcpiFormatException(status));
520 	    }
521 	} else {
522 	    device_printf(sc->tz_dev, "couldn't find %s\n", obj->String.Pointer);
523 	}
524 	break;
525 
526     default:
527 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
528 			  obj->Type));
529 	break;
530     }
531 	return_VOID;
532 }
533 
534 /*
535  * Read/debug-print a parameter, default it to -1.
536  */
537 static void
538 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
539 {
540 
541     FUNCTION_TRACE(__func__);
542 
543     ACPI_ASSERTLOCK;
544 
545     if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
546 	*data = -1;
547     } else {
548 	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n", acpi_name(sc->tz_handle),
549 			  node, *data));
550     }
551     return_VOID;
552 }
553 
554 /*
555  * Sanity-check a temperature value.  Assume that setpoints
556  * should be between 0C and 150C.
557  */
558 static void
559 acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
560 {
561     if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
562 	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
563 		      what, TZ_KELVTOC(*val));
564 	*val = -1;
565     }
566 }
567 
568 /*
569  * Respond to a sysctl on the active state node.
570  */
571 static int
572 acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
573 {
574     struct acpi_tz_softc	*sc;
575     int				active;
576     int		 		error;
577 
578     ACPI_LOCK;
579 
580     sc = (struct acpi_tz_softc *)oidp->oid_arg1;
581     active = sc->tz_active;
582     error = sysctl_handle_int(oidp, &active, 0, req);
583 
584     /* error or no new value */
585     if ((error != 0) || (req->newptr == NULL))
586 	goto out;
587 
588     /* range check */
589     if ((active < -1) || (active >= TZ_NUMLEVELS)) {
590 	error = EINVAL;
591 	goto out;
592     }
593 
594     /* set new preferred level and re-switch */
595     sc->tz_requested = active;
596     acpi_tz_monitor(sc);
597 
598  out:
599     ACPI_UNLOCK;
600     return(error);
601 }
602 
603 /*
604  * Respond to a Notify event sent to the zone.
605  */
606 static void
607 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
608 {
609     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
610 
611     FUNCTION_TRACE(__func__);
612 
613     ACPI_ASSERTLOCK;
614 
615     switch(notify) {
616     case TZ_NOTIFY_TEMPERATURE:
617 	/* temperature change occurred */
618 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
619 	break;
620     case TZ_NOTIFY_DEVICES:
621     case TZ_NOTIFY_LEVELS:
622 	/* zone devices/setpoints changed */
623 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
624 	break;
625     default:
626 	device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
627 	break;
628     }
629     return_VOID;
630 }
631 
632 /*
633  * Poll the thermal zone.
634  */
635 static void
636 acpi_tz_timeout(void *arg)
637 {
638     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
639 
640     /* do we need to get the power profile settings? */
641     if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
642 	acpi_tz_powerprofile(arg);
643 	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
644     }
645 
646     ACPI_LOCK;
647 
648     /* check temperature, take action */
649     AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
650 
651     /* XXX passive cooling actions? */
652 
653     /* re-register ourself */
654     sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
655 
656     ACPI_UNLOCK;
657 }
658 
659 /*
660  * System power profile may have changed; fetch and notify the
661  * thermal zone accordingly.
662  *
663  * Since this can be called from an arbitrary eventhandler, it needs
664  * to get the ACPI lock itself.
665  */
666 static void
667 acpi_tz_powerprofile(void *arg)
668 {
669     ACPI_OBJECT_LIST		args;
670     ACPI_OBJECT			obj;
671     ACPI_STATUS			status;
672     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
673 
674     ACPI_LOCK;
675 
676     /* check that we haven't decided there's no _SCP method */
677     if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) {
678 
679 	/* call _SCP to set the new profile */
680 	obj.Type = ACPI_TYPE_INTEGER;
681 	obj.Integer.Value = (powerprofile_get_state() == POWERPROFILE_PERFORMANCE) ? 0 : 1;
682 	args.Count = 1;
683 	args.Pointer = &obj;
684 	if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) {
685 	    if (status != AE_NOT_FOUND)
686 		device_printf(sc->tz_dev, "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle),
687 			      AcpiFormatException(status));
688 	    sc->tz_flags |= TZ_FLAG_NO_SCP;
689 	} else {
690 	    /* we have to re-evaluate the entire zone now */
691 	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
692 	}
693     }
694     ACPI_UNLOCK;
695 }
696 
697