xref: /dragonfly/sys/dev/acpica/acpi_thermal.c (revision 2ee85085)
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: src/sys/dev/acpica/acpi_thermal.c,v 1.29.6.1 2003/08/22 20:49:20 jhb Exp $
28  *      $DragonFly: src/sys/dev/acpica/Attic/acpi_thermal.c,v 1.3 2004/02/13 00:25:17 joerg Exp $
29  */
30 
31 #include "opt_acpi.h"
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/kthread.h>
35 #include <sys/bus.h>
36 #include <sys/proc.h>
37 #include <sys/reboot.h>
38 #include <sys/sysctl.h>
39 #include <sys/unistd.h>
40 #include <sys/power.h>
41 
42 #include "acpi.h"
43 
44 #include <dev/acpica/acpivar.h>
45 
46 /*
47  * Hooks for the ACPI CA debugging infrastructure
48  */
49 #define _COMPONENT	ACPI_THERMAL
50 ACPI_MODULE_NAME("THERMAL")
51 
52 #define TZ_ZEROC	2732
53 #define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
54 
55 #define TZ_NOTIFY_TEMPERATURE	0x80
56 #define TZ_NOTIFY_DEVICES	0x81
57 #define TZ_NOTIFY_LEVELS	0x82
58 
59 #define TZ_POLLRATE	30		/* every 30 seconds by default */
60 
61 #define TZ_NUMLEVELS	10		/* defined by ACPI spec */
62 struct acpi_tz_zone {
63     int		ac[TZ_NUMLEVELS];
64     ACPI_BUFFER	al[TZ_NUMLEVELS];
65     int		crt;
66     int		hot;
67     ACPI_BUFFER	psl;
68     int		psv;
69     int		tc1;
70     int		tc2;
71     int		tsp;
72     int		tzp;
73 };
74 
75 
76 struct acpi_tz_softc {
77     device_t			tz_dev;			/* device handle */
78     ACPI_HANDLE			tz_handle;		/* thermal zone handle */
79     int				tz_temperature;		/* current temperature */
80     int				tz_active;		/* current active cooling */
81 #define TZ_ACTIVE_NONE		-1
82     int				tz_requested;		/* user-requested minimum active cooling */
83     int				tz_thflags;		/* current temperature-related flags */
84 #define TZ_THFLAG_NONE		0
85 #define TZ_THFLAG_PSV		(1<<0)
86 #define TZ_THFLAG_HOT		(1<<2)
87 #define TZ_THFLAG_CRT		(1<<3)
88     int				tz_flags;
89 #define TZ_FLAG_NO_SCP		(1<<0)			/* no _SCP method */
90 #define TZ_FLAG_GETPROFILE	(1<<1)			/* fetch power_profile in timeout */
91     struct timespec		tz_cooling_started;	/* current cooling starting time */
92 
93     struct sysctl_ctx_list	tz_sysctl_ctx;		/* sysctl tree */
94     struct sysctl_oid		*tz_sysctl_tree;
95 
96     struct acpi_tz_zone 	tz_zone;		/* thermal zone parameters */
97     int				tz_tmp_updating;
98 };
99 
100 static int	acpi_tz_probe(device_t dev);
101 static int	acpi_tz_attach(device_t dev);
102 static int	acpi_tz_establish(struct acpi_tz_softc *sc);
103 static void	acpi_tz_monitor(struct acpi_tz_softc *sc);
104 static void	acpi_tz_all_off(struct acpi_tz_softc *sc);
105 static void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
106 static void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
107 static void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
108 static void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
109 static int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
110 static void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
111 static void	acpi_tz_timeout(struct acpi_tz_softc *sc);
112 static void	acpi_tz_power_profile(void *arg);
113 
114 static void	acpi_tz_thread(void *arg);
115 static struct thread *acpi_tz_proc;
116 
117 static device_method_t acpi_tz_methods[] = {
118     /* Device interface */
119     DEVMETHOD(device_probe,	acpi_tz_probe),
120     DEVMETHOD(device_attach,	acpi_tz_attach),
121 
122     {0, 0}
123 };
124 
125 static driver_t acpi_tz_driver = {
126     "acpi_tz",
127     acpi_tz_methods,
128     sizeof(struct acpi_tz_softc),
129 };
130 
131 static devclass_t acpi_tz_devclass;
132 DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
133 
134 static struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
135 static struct sysctl_oid	*acpi_tz_sysctl_tree;
136 
137 static int			acpi_tz_min_runtime = 0;/* minimum cooling run time */
138 static int			acpi_tz_polling_rate = TZ_POLLRATE;
139 
140 /*
141  * Match an ACPI thermal zone.
142  */
143 static int
144 acpi_tz_probe(device_t dev)
145 {
146     int		result;
147     ACPI_LOCK_DECL;
148 
149     ACPI_LOCK;
150 
151     /* no FUNCTION_TRACE - too noisy */
152 
153     if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
154 	!acpi_disabled("thermal")) {
155 	device_set_desc(dev, "thermal zone");
156 	result = -10;
157     } else {
158 	result = ENXIO;
159     }
160     ACPI_UNLOCK;
161     return(result);
162 }
163 
164 /*
165  * Attach to an ACPI thermal zone.
166  */
167 static int
168 acpi_tz_attach(device_t dev)
169 {
170     struct acpi_tz_softc	*sc;
171     struct acpi_softc		*acpi_sc;
172     int				error;
173     char			oidname[8];
174     ACPI_LOCK_DECL;
175 
176     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
177 
178     ACPI_LOCK;
179 
180     sc = device_get_softc(dev);
181     sc->tz_dev = dev;
182     sc->tz_handle = acpi_get_handle(dev);
183     sc->tz_requested = TZ_ACTIVE_NONE;
184     sc->tz_tmp_updating = 0;
185 
186     /*
187      * Parse the current state of the thermal zone and build control
188      * structures.
189      */
190     if ((error = acpi_tz_establish(sc)) != 0)
191 	goto out;
192 
193     /*
194      * Register for any Notify events sent to this zone.
195      */
196     AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
197 			     acpi_tz_notify_handler, sc);
198 
199     /*
200      * Create our sysctl nodes.
201      *
202      * XXX we need a mechanism for adding nodes under ACPI.
203      */
204     if (device_get_unit(dev) == 0) {
205 	acpi_sc = acpi_device_get_parent_softc(dev);
206 	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
207 	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
208 					      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
209 					      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
210 	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
211 		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
212 		       OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW,
213 		       &acpi_tz_min_runtime, 0, "minimum cooling run time in sec");
214 	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
215 		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
216 		       OID_AUTO, "polling_rate", CTLFLAG_RD | CTLFLAG_RW,
217 		       &acpi_tz_polling_rate, 0, "monitor polling rate");
218     }
219     sysctl_ctx_init(&sc->tz_sysctl_ctx);
220     sprintf(oidname, "tz%d", device_get_unit(dev));
221     sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
222 					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
223 					 oidname, CTLFLAG_RD, 0, "");
224     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
225 		   OID_AUTO, "temperature", CTLFLAG_RD,
226 		   &sc->tz_temperature, 0, "current thermal zone temperature");
227     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
228 		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
229 		    sc, 0, acpi_tz_active_sysctl, "I", "");
230 
231     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
232 		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
233 		   &sc->tz_thflags, 0, "thermal zone flags");
234     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
235 		   OID_AUTO, "_PSV", CTLFLAG_RD,
236 		   &sc->tz_zone.psv, 0, "");
237     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
238 		   OID_AUTO, "_HOT", CTLFLAG_RD,
239 		   &sc->tz_zone.hot, 0, "");
240     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
241 		   OID_AUTO, "_CRT", CTLFLAG_RD,
242 		   &sc->tz_zone.crt, 0, "");
243     SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
244 		      OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac,
245 		      sizeof(sc->tz_zone.ac), "I", "");
246 
247 
248     /*
249      * Register our power profile event handler, and flag it for a manual
250      * invocation by our timeout.  We defer it like this so that the rest
251      * of the subsystem has time to come up.
252      */
253     EVENTHANDLER_REGISTER(power_profile_change, acpi_tz_power_profile, sc, 0);
254     sc->tz_flags |= TZ_FLAG_GETPROFILE;
255 
256     /*
257      * Don't bother evaluating/printing the temperature at this point;
258      * on many systems it'll be bogus until the EC is running.
259      */
260 
261     /*
262      * Create our thread; we only need one, it will service all of the
263      * thermal zones.
264      */
265     if (acpi_tz_proc == NULL) {
266 	    error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_proc,
267 				   RFHIGHPID, 0, "acpi_thermal");
268 	    if (error != 0) {
269 		    device_printf(sc->tz_dev, "could not create thread - %d", error);
270 		    goto out;
271 	    }
272     }
273 
274  out:
275     ACPI_UNLOCK;
276 
277     return_VALUE(error);
278 }
279 
280 /*
281  * Parse the current state of this thermal zone and set up to use it.
282  *
283  * Note that we may have previous state, which will have to be discarded.
284  */
285 static int
286 acpi_tz_establish(struct acpi_tz_softc *sc)
287 {
288     ACPI_OBJECT	*obj;
289     int		i;
290     char	nbuf[8];
291 
292     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
293 
294     ACPI_ASSERTLOCK;
295 
296     /*
297      * Power everything off and erase any existing state.
298      */
299     acpi_tz_all_off(sc);
300     for (i = 0; i < TZ_NUMLEVELS; i++)
301 	if (sc->tz_zone.al[i].Pointer != NULL)
302 	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
303     if (sc->tz_zone.psl.Pointer != NULL)
304 	AcpiOsFree(sc->tz_zone.psl.Pointer);
305     bzero(&sc->tz_zone, sizeof(sc->tz_zone));
306 
307     /*
308      * Evaluate thermal zone parameters.
309      */
310     for (i = 0; i < TZ_NUMLEVELS; i++) {
311 	sprintf(nbuf, "_AC%d", i);
312 	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
313 	sprintf(nbuf, "_AL%d", i);
314 	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
315 	sc->tz_zone.al[i].Pointer = NULL;
316 	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
317 	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
318 	if (obj != NULL) {
319 	    /* should be a package containing a list of power objects */
320 	    if (obj->Type != ACPI_TYPE_PACKAGE) {
321 		device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
322 			      nbuf, obj->Type);
323 		return_VALUE(ENXIO);
324 	    }
325 	}
326     }
327     acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
328     acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
329     sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
330     sc->tz_zone.psl.Pointer = NULL;
331     AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
332     acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
333     acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
334     acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
335     acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
336     acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
337 
338     /*
339      * Sanity-check the values we've been given.
340      *
341      * XXX what do we do about systems that give us the same value for
342      *     more than one of these setpoints?
343      */
344     acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
345     acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
346     acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
347     for (i = 0; i < TZ_NUMLEVELS; i++)
348 	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
349 
350     /*
351      * Power off everything that we've just been given.
352      */
353     acpi_tz_all_off(sc);
354 
355     return_VALUE(0);
356 }
357 
358 static char	*aclevel_string[] =	{
359 	"NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
360 	"_AC5", "_AC6", "_AC7", "_AC8", "_AC9" };
361 
362 static __inline const char *
363 acpi_tz_aclevel_string(int active)
364 {
365 	if (active < -1 || active >= TZ_NUMLEVELS) {
366 		return (aclevel_string[0]);
367 	}
368 
369 	return (aclevel_string[active+1]);
370 }
371 
372 /*
373  * Evaluate the condition of a thermal zone, take appropriate actions.
374  */
375 static void
376 acpi_tz_monitor(struct acpi_tz_softc *sc)
377 {
378     int		temp;
379     int		i;
380     int		newactive, newflags;
381     struct	timespec curtime;
382     ACPI_STATUS	status;
383 
384     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
385 
386     ACPI_ASSERTLOCK;
387 
388     if (sc->tz_tmp_updating) {
389 	goto out;
390     }
391     sc->tz_tmp_updating = 1;
392 
393     /*
394      * Get the current temperature.
395      */
396     if (ACPI_FAILURE(status = acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp))) {
397 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
398 	    "error fetching current temperature -- %s\n",
399 	     AcpiFormatException(status));
400 	/* XXX disable zone? go to max cooling? */
401 	goto out;
402     }
403 
404     ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
405     sc->tz_temperature = temp;
406 
407     /*
408      * Work out what we ought to be doing right now.
409      *
410      * Note that the _ACx levels sort from hot to cold.
411      */
412     newactive = TZ_ACTIVE_NONE;
413     for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
414 	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
415 	    newactive = i;
416 	    if (sc->tz_active != newactive) {
417 		ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
418 		    "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
419 		    TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
420 		getnanotime(&sc->tz_cooling_started);
421 	    }
422 	}
423     }
424 
425     /*
426      * We are going to get _ACx level down (colder side), but give a guaranteed
427      * minimum cooling run time if requested.
428      */
429     if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
430 	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
431 	getnanotime(&curtime);
432 	timespecsub(&curtime, &sc->tz_cooling_started);
433 	if (curtime.tv_sec < acpi_tz_min_runtime) {
434 	    newactive = sc->tz_active;
435 	}
436     }
437 
438     /* handle user override of active mode */
439     if (sc->tz_requested > newactive)
440 	newactive = sc->tz_requested;
441 
442     /* update temperature-related flags */
443     newflags = TZ_THFLAG_NONE;
444     if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
445 	newflags |= TZ_THFLAG_PSV;
446     if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
447 	newflags |= TZ_THFLAG_HOT;
448     if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
449 	newflags |= TZ_THFLAG_CRT;
450 
451     /*
452      * If the active cooling state has changed, we have to switch things.
453      */
454     if (newactive != sc->tz_active) {
455 
456 	/* turn off the cooling devices that are on, if any are */
457 	if (sc->tz_active != TZ_ACTIVE_NONE)
458 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
459 				      acpi_tz_switch_cooler_off, sc);
460 
461 	/* turn on cooling devices that are required, if any are */
462 	if (newactive != TZ_ACTIVE_NONE)
463 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
464 				      acpi_tz_switch_cooler_on, sc);
465 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
466 	    "switched from %s to %s: %d.%dC\n",
467 	    acpi_tz_aclevel_string(sc->tz_active),
468 	    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
469 	sc->tz_active = newactive;
470     }
471 
472     /*
473      * XXX (de)activate any passive cooling that may be required.
474      */
475 
476     /*
477      * If we have just become _HOT or _CRT, warn the user.
478      *
479      * We should actually shut down at this point, but it's not clear
480      * that some systems don't actually map _CRT to the same value as _AC0.
481      */
482     if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) &&
483 	!(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) {
484 	device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
485 		      TZ_KELVTOC(sc->tz_temperature));
486 	/* shutdown_nice(RB_POWEROFF);*/
487     }
488     sc->tz_thflags = newflags;
489 
490 out:
491     sc->tz_tmp_updating = 0;
492     return_VOID;
493 }
494 
495 /*
496  * Turn off all the cooling devices.
497  */
498 static void
499 acpi_tz_all_off(struct acpi_tz_softc *sc)
500 {
501     int		i;
502 
503     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
504 
505     ACPI_ASSERTLOCK;
506 
507     /*
508      * Scan all the _ALx objects, and turn them all off.
509      */
510     for (i = 0; i < TZ_NUMLEVELS; i++) {
511 	if (sc->tz_zone.al[i].Pointer == NULL)
512 	    continue;
513 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
514 				  acpi_tz_switch_cooler_off, sc);
515     }
516 
517     /*
518      * XXX revert any passive-cooling options.
519      */
520 
521     sc->tz_active = TZ_ACTIVE_NONE;
522     sc->tz_thflags = TZ_THFLAG_NONE;
523     return_VOID;
524 }
525 
526 /*
527  * Given an object, verify that it's a reference to a device of some sort,
528  * and try to switch it off.
529  */
530 static void
531 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
532 {
533     ACPI_HANDLE		cooler;
534 
535     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
536 
537     ACPI_ASSERTLOCK;
538 
539     switch(obj->Type) {
540     case ACPI_TYPE_ANY:
541 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", acpi_name(obj->Reference.Handle)));
542 
543 	acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D3);
544 	break;
545 
546     case ACPI_TYPE_STRING:
547 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", obj->String.Pointer));
548 
549 	/*
550 	 * Find the handle for the device and turn it off.
551 	 * The String object here seems to contain a fully-qualified path, so we
552 	 * don't have to search for it in our parents.
553 	 *
554 	 * XXX This may not always be the case.
555 	 */
556 	if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler)))
557 	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
558 	break;
559 
560     default:
561 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
562 			  obj->Type));
563 	break;
564     }
565     return_VOID;
566 }
567 
568 /*
569  * Given an object, verify that it's a reference to a device of some sort,
570  * and try to switch it on.
571  *
572  * XXX replication of off/on function code is bad, mmmkay?
573  */
574 static void
575 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
576 {
577     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
578     ACPI_HANDLE			cooler;
579     ACPI_STATUS			status;
580 
581     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
582 
583     ACPI_ASSERTLOCK;
584 
585     switch(obj->Type) {
586     case ACPI_TYPE_ANY:
587 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", acpi_name(obj->Reference.Handle)));
588 
589 	if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D0))) {
590 	    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
591 		"failed to activate %s - %s\n", acpi_name(obj->Reference.Handle),
592 		AcpiFormatException(status));
593 	}
594 	break;
595 
596     case ACPI_TYPE_STRING:
597 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", obj->String.Pointer));
598 
599 	/*
600 	 * Find the handle for the device and turn it off.
601 	 * The String object here seems to contain a fully-qualified path, so we
602 	 * don't have to search for it in our parents.
603 	 *
604 	 * XXX This may not always be the case.
605 	 */
606 	if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler))) {
607 	    if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) {
608 		ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
609 		    "failed to activate %s - %s\n",
610 		    obj->String.Pointer, AcpiFormatException(status));
611 	    }
612 	} else {
613 	    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
614 		"couldn't find %s\n", obj->String.Pointer);
615 	}
616 	break;
617 
618     default:
619 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
620 			  obj->Type));
621 	break;
622     }
623 	return_VOID;
624 }
625 
626 /*
627  * Read/debug-print a parameter, default it to -1.
628  */
629 static void
630 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
631 {
632 
633     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
634 
635     ACPI_ASSERTLOCK;
636 
637     if (ACPI_FAILURE(acpi_EvaluateInteger(sc->tz_handle, node, data))) {
638 	*data = -1;
639     } else {
640 	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n", acpi_name(sc->tz_handle),
641 			  node, *data));
642     }
643     return_VOID;
644 }
645 
646 /*
647  * Sanity-check a temperature value.  Assume that setpoints
648  * should be between 0C and 150C.
649  */
650 static void
651 acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
652 {
653     if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
654 	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
655 		      what, TZ_KELVTOC(*val));
656 	*val = -1;
657     }
658 }
659 
660 /*
661  * Respond to a sysctl on the active state node.
662  */
663 static int
664 acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
665 {
666     struct acpi_tz_softc	*sc;
667     int				active;
668     int		 		error;
669     ACPI_LOCK_DECL;
670 
671     ACPI_LOCK;
672 
673     sc = (struct acpi_tz_softc *)oidp->oid_arg1;
674     active = sc->tz_active;
675     error = sysctl_handle_int(oidp, &active, 0, req);
676 
677     /* error or no new value */
678     if ((error != 0) || (req->newptr == NULL))
679 	goto out;
680 
681     /* range check */
682     if ((active < -1) || (active >= TZ_NUMLEVELS)) {
683 	error = EINVAL;
684 	goto out;
685     }
686 
687     /* set new preferred level and re-switch */
688     sc->tz_requested = active;
689     acpi_tz_monitor(sc);
690 
691  out:
692     ACPI_UNLOCK;
693     return(error);
694 }
695 
696 /*
697  * Respond to a Notify event sent to the zone.
698  */
699 static void
700 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
701 {
702     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
703 
704     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
705 
706     ACPI_ASSERTLOCK;
707 
708     switch(notify) {
709     case TZ_NOTIFY_TEMPERATURE:
710 	/* temperature change occurred */
711 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
712 	break;
713     case TZ_NOTIFY_DEVICES:
714     case TZ_NOTIFY_LEVELS:
715 	/* zone devices/setpoints changed */
716 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
717 	break;
718     default:
719 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
720 	    "unknown Notify event 0x%x\n", notify);
721 	break;
722     }
723     return_VOID;
724 }
725 
726 /*
727  * Poll the thermal zone.
728  */
729 static void
730 acpi_tz_timeout(struct acpi_tz_softc *sc)
731 {
732 
733     /* do we need to get the power profile settings? */
734     if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
735 	acpi_tz_power_profile((void *)sc);
736 	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
737     }
738 
739     ACPI_ASSERTLOCK;
740 
741     /* check the current temperature and take action based on it */
742     acpi_tz_monitor(sc);
743 
744     /* XXX passive cooling actions? */
745 }
746 
747 /*
748  * System power profile may have changed; fetch and notify the
749  * thermal zone accordingly.
750  *
751  * Since this can be called from an arbitrary eventhandler, it needs
752  * to get the ACPI lock itself.
753  */
754 static void
755 acpi_tz_power_profile(void *arg)
756 {
757     ACPI_OBJECT_LIST		args;
758     ACPI_OBJECT			obj;
759     ACPI_STATUS			status;
760     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
761     int				state;
762     ACPI_LOCK_DECL;
763 
764     state = power_profile_get_state();
765     if (state != POWER_PROFILE_PERFORMANCE &&
766         state != POWER_PROFILE_ECONOMY) {
767         return;
768     }
769 
770     ACPI_LOCK;
771 
772     /* check that we haven't decided there's no _SCP method */
773     if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) {
774 
775 	/* call _SCP to set the new profile */
776 	obj.Type = ACPI_TYPE_INTEGER;
777 	obj.Integer.Value = (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1;
778 	args.Count = 1;
779 	args.Pointer = &obj;
780 	if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) {
781 	    if (status != AE_NOT_FOUND)
782 		ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
783 		    "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle),
784 		    AcpiFormatException(status));
785 	    sc->tz_flags |= TZ_FLAG_NO_SCP;
786 	} else {
787 	    /* we have to re-evaluate the entire zone now */
788 	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
789 	}
790     }
791     ACPI_UNLOCK;
792 }
793 
794 /*
795  * Thermal zone monitor thread.
796  */
797 static void
798 acpi_tz_thread(void *arg)
799 {
800     device_t	*devs;
801     int		devcount, i;
802     ACPI_LOCK_DECL;
803 
804     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
805 
806 
807     devs = NULL;
808     devcount = 0;
809 
810     for (;;) {
811 	tsleep(&acpi_tz_proc, 0, "nothing", hz * acpi_tz_polling_rate);
812 
813 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
814 	mtx_lock(&Giant);
815 #endif
816 
817 	if (devcount == 0)
818 	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
819 
820 	ACPI_LOCK;
821 	for (i = 0; i < devcount; i++)
822 	    acpi_tz_timeout(device_get_softc(devs[i]));
823 	ACPI_UNLOCK;
824 
825 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
826 	mtx_unlock(&Giant);
827 #endif
828     }
829 }
830