xref: /freebsd/sys/dev/acpica/acpi_thermal.c (revision bfae45aa43bd0201341d5ffbbfb983d19f35a0e8)
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     DEBUG_PRINT(TRACE_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 	    device_printf(sc->tz_dev, "_AC%d: temperature %d > setpoint %d\n",
361 			  i, temp, sc->tz_zone.ac[i]);
362 	    newactive = i;
363 	}
364     }
365 
366     /* handle user override of active mode */
367     if (sc->tz_requested > newactive)
368 	newactive = sc->tz_requested;
369 
370     /* update temperature-related flags */
371     newflags = TZ_THFLAG_NONE;
372     if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
373 	newflags |= TZ_THFLAG_PSV;
374     if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
375 	newflags |= TZ_THFLAG_HOT;
376     if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
377 	newflags |= TZ_THFLAG_CRT;
378 
379     /*
380      * If the active cooling state has changed, we have to switch things.
381      */
382     if (newactive != sc->tz_active) {
383 
384 	/* turn off the cooling devices that are on, if any are */
385 	if (sc->tz_active != TZ_ACTIVE_NONE)
386 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
387 				      acpi_tz_switch_cooler_off, sc);
388 
389 	/* turn on cooling devices that are required, if any are */
390 	if (newactive != TZ_ACTIVE_NONE)
391 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
392 				      acpi_tz_switch_cooler_on, sc);
393 	device_printf(sc->tz_dev, "switched from _AC%d to _AC%d\n", sc->tz_active, newactive);
394 	sc->tz_active = newactive;
395     }
396 
397     /*
398      * XXX (de)activate any passive cooling that may be required.
399      */
400 
401     /*
402      * If we have just become _HOT or _CRT, warn the user.
403      *
404      * We should actually shut down at this point, but it's not clear
405      * that some systems don't actually map _CRT to the same value as _AC0.
406      */
407     if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) &&
408 	!(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) {
409 	device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
410 		      TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature);
411 	/* shutdown_nice(RB_POWEROFF);*/
412     }
413     sc->tz_thflags = newflags;
414 
415     return_VOID;
416 }
417 
418 /*
419  * Turn off all the cooling devices.
420  */
421 static void
422 acpi_tz_all_off(struct acpi_tz_softc *sc)
423 {
424     int		i;
425 
426     FUNCTION_TRACE(__func__);
427 
428     ACPI_ASSERTLOCK;
429 
430     /*
431      * Scan all the _ALx objects, and turn them all off.
432      */
433     for (i = 0; i < TZ_NUMLEVELS; i++) {
434 	if (sc->tz_zone.al[i].Pointer == NULL)
435 	    continue;
436 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
437 				  acpi_tz_switch_cooler_off, sc);
438     }
439 
440     /*
441      * XXX revert any passive-cooling options.
442      */
443 
444     sc->tz_active = TZ_ACTIVE_NONE;
445     sc->tz_thflags = TZ_THFLAG_NONE;
446     return_VOID;
447 }
448 
449 /*
450  * Given an object, verify that it's a reference to a device of some sort,
451  * and try to switch it off.
452  */
453 static void
454 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
455 {
456     ACPI_HANDLE		cooler;
457 
458     FUNCTION_TRACE(__func__);
459 
460     ACPI_ASSERTLOCK;
461 
462     switch(obj->Type) {
463     case ACPI_TYPE_STRING:
464 	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
465 
466 	/*
467 	 * Find the handle for the device and turn it off.
468 	 * The String object here seems to contain a fully-qualified path, so we
469 	 * don't have to search for it in our parents.
470 	 *
471 	 * XXX This may not always be the case.
472 	 */
473 	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK)
474 	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
475 	break;
476 
477     default:
478 	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
479 				    obj->Type));
480 	break;
481     }
482     return_VOID;
483 }
484 
485 /*
486  * Given an object, verify that it's a reference to a device of some sort,
487  * and try to switch it on.
488  *
489  * XXX replication of off/on function code is bad, mmmkay?
490  */
491 static void
492 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
493 {
494     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
495     ACPI_HANDLE			cooler;
496     ACPI_STATUS			status;
497 
498     FUNCTION_TRACE(__func__);
499 
500     ACPI_ASSERTLOCK;
501 
502     switch(obj->Type) {
503     case ACPI_TYPE_STRING:
504 	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s on\n", obj->String.Pointer));
505 
506 	/*
507 	 * Find the handle for the device and turn it off.
508 	 * The String object here seems to contain a fully-qualified path, so we
509 	 * don't have to search for it in our parents.
510 	 *
511 	 * XXX This may not always be the case.
512 	 */
513 	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK) {
514 	    if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) {
515 		device_printf(sc->tz_dev, "failed to activate %s - %s\n",
516 			      obj->String.Pointer, AcpiFormatException(status));
517 	    }
518 	} else {
519 	    device_printf(sc->tz_dev, "couldn't find %s\n", obj->String.Pointer);
520 	}
521 	break;
522 
523     default:
524 	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
525 				    obj->Type));
526 	break;
527     }
528 	return_VOID;
529 }
530 
531 /*
532  * Read/debug-print a parameter, default it to -1.
533  */
534 static void
535 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
536 {
537 
538     FUNCTION_TRACE(__func__);
539 
540     ACPI_ASSERTLOCK;
541 
542     if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
543 	*data = -1;
544     } else {
545 	DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle),
546 				   node, *data));
547     }
548     return_VOID;
549 }
550 
551 /*
552  * Sanity-check a temperature value.  Assume that setpoints
553  * should be between 0C and 150C.
554  */
555 static void
556 acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
557 {
558     if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
559 	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
560 		      what, TZ_KELVTOC(*val));
561 	*val = -1;
562     }
563 }
564 
565 /*
566  * Respond to a sysctl on the active state node.
567  */
568 static int
569 acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
570 {
571     struct acpi_tz_softc	*sc;
572     int				active;
573     int		 		error;
574 
575     ACPI_LOCK;
576 
577     sc = (struct acpi_tz_softc *)oidp->oid_arg1;
578     active = sc->tz_active;
579     error = sysctl_handle_int(oidp, &active, 0, req);
580 
581     /* error or no new value */
582     if ((error != 0) || (req->newptr == NULL))
583 	goto out;
584 
585     /* range check */
586     if ((active < -1) || (active >= TZ_NUMLEVELS)) {
587 	error = EINVAL;
588 	goto out;
589     }
590 
591     /* set new preferred level and re-switch */
592     sc->tz_requested = active;
593     acpi_tz_monitor(sc);
594 
595  out:
596     ACPI_UNLOCK;
597     return(error);
598 }
599 
600 /*
601  * Respond to a Notify event sent to the zone.
602  */
603 static void
604 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
605 {
606     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
607 
608     FUNCTION_TRACE(__func__);
609 
610     ACPI_ASSERTLOCK;
611 
612     switch(notify) {
613     case TZ_NOTIFY_TEMPERATURE:
614 	/* temperature change occurred */
615 	device_printf(sc->tz_dev, "notified of temperature reaching setpoint\n");
616 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
617 	break;
618     case TZ_NOTIFY_DEVICES:
619     case TZ_NOTIFY_LEVELS:
620 	/* zone devices/setpoints changed */
621 	device_printf(sc->tz_dev, "notified of zone configuration change\n");
622 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
623 	break;
624     default:
625 	device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
626 	break;
627     }
628     return_VOID;
629 }
630 
631 /*
632  * Poll the thermal zone.
633  */
634 static void
635 acpi_tz_timeout(void *arg)
636 {
637     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
638 
639     /* do we need to get the power profile settings? */
640     if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
641 	acpi_tz_powerprofile(arg);
642 	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
643     }
644 
645     ACPI_LOCK;
646 
647     /* check temperature, take action */
648     AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
649 
650     /* XXX passive cooling actions? */
651 
652     /* re-register ourself */
653     sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
654 
655     ACPI_UNLOCK;
656 }
657 
658 /*
659  * System power profile may have changed; fetch and notify the
660  * thermal zone accordingly.
661  *
662  * Since this can be called from an arbitrary eventhandler, it needs
663  * to get the ACPI lock itself.
664  */
665 static void
666 acpi_tz_powerprofile(void *arg)
667 {
668     ACPI_OBJECT_LIST		args;
669     ACPI_OBJECT			obj;
670     ACPI_STATUS			status;
671     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
672 
673     ACPI_LOCK;
674 
675     /* check that we haven't decided there's no _SCP method */
676     if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) {
677 
678 	/* call _SCP to set the new profile */
679 	obj.Type = ACPI_TYPE_INTEGER;
680 	obj.Integer.Value = (powerprofile_get_state() == POWERPROFILE_PERFORMANCE) ? 0 : 1;
681 	args.Count = 1;
682 	args.Pointer = &obj;
683 	if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) {
684 	    if (status != AE_NOT_FOUND)
685 		device_printf(sc->tz_dev, "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle),
686 			      AcpiFormatException(status));
687 	    sc->tz_flags |= TZ_FLAG_NO_SCP;
688 	} else {
689 	    /* we have to re-evaluate the entire zone now */
690 	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
691 	}
692     }
693     ACPI_UNLOCK;
694 }
695 
696