xref: /freebsd/sys/dev/acpica/acpi_thermal.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
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 #include <dev/acpica/acpivar.h>
44 
45 /* Hooks for the ACPI CA debugging infrastructure */
46 #define _COMPONENT	ACPI_THERMAL
47 ACPI_MODULE_NAME("THERMAL")
48 
49 #define TZ_ZEROC	2732
50 #define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
51 
52 #define TZ_NOTIFY_TEMPERATURE	0x80
53 #define TZ_NOTIFY_DEVICES	0x81
54 #define TZ_NOTIFY_LEVELS	0x82
55 
56 /* Check for temperature changes every 30 seconds by default */
57 #define TZ_POLLRATE	30
58 
59 /* ACPI spec defines this */
60 #define TZ_NUMLEVELS	10
61 struct acpi_tz_zone {
62     int		ac[TZ_NUMLEVELS];
63     ACPI_BUFFER	al[TZ_NUMLEVELS];
64     int		crt;
65     int		hot;
66     ACPI_BUFFER	psl;
67     int		psv;
68     int		tc1;
69     int		tc2;
70     int		tsp;
71     int		tzp;
72 };
73 
74 
75 struct acpi_tz_softc {
76     device_t			tz_dev;
77     ACPI_HANDLE			tz_handle;	/*Thermal zone handle*/
78     int				tz_temperature;	/*Current temperature*/
79     int				tz_active;	/*Current active cooling*/
80 #define TZ_ACTIVE_NONE		-1
81     int				tz_requested;	/*Minimum active cooling*/
82     int				tz_thflags;	/*Current temp-related flags*/
83 #define TZ_THFLAG_NONE		0
84 #define TZ_THFLAG_PSV		(1<<0)
85 #define TZ_THFLAG_HOT		(1<<2)
86 #define TZ_THFLAG_CRT		(1<<3)
87     int				tz_flags;
88 #define TZ_FLAG_NO_SCP		(1<<0)		/*No _SCP method*/
89 #define TZ_FLAG_GETPROFILE	(1<<1)		/*Get power_profile in timeout*/
90     struct timespec		tz_cooling_started;
91 					/*Current cooling starting time*/
92 
93     struct sysctl_ctx_list	tz_sysctl_ctx;
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(void *Context);
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,
108 				 int *data);
109 static void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
110 static int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
111 static void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify,
112 				       void *context);
113 static void	acpi_tz_timeout(struct acpi_tz_softc *sc);
114 static void	acpi_tz_power_profile(void *arg);
115 static void	acpi_tz_thread(void *arg);
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 /* Minimum cooling run time */
138 static int			acpi_tz_min_runtime = 0;
139 static int			acpi_tz_polling_rate = TZ_POLLRATE;
140 
141 /* Timezone polling thread */
142 static struct proc		*acpi_tz_proc;
143 
144 /*
145  * Match an ACPI thermal zone.
146  */
147 static int
148 acpi_tz_probe(device_t dev)
149 {
150     int		result;
151     ACPI_LOCK_DECL;
152 
153     ACPI_LOCK;
154 
155     /* No FUNCTION_TRACE - too noisy */
156 
157     if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) {
158 	device_set_desc(dev, "thermal zone");
159 	result = -10;
160     } else {
161 	result = ENXIO;
162     }
163     ACPI_UNLOCK;
164     return (result);
165 }
166 
167 /*
168  * Attach to an ACPI thermal zone.
169  */
170 static int
171 acpi_tz_attach(device_t dev)
172 {
173     struct acpi_tz_softc	*sc;
174     struct acpi_softc		*acpi_sc;
175     int				error;
176     char			oidname[8];
177     ACPI_LOCK_DECL;
178 
179     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
180 
181     ACPI_LOCK;
182 
183     sc = device_get_softc(dev);
184     sc->tz_dev = dev;
185     sc->tz_handle = acpi_get_handle(dev);
186     sc->tz_requested = TZ_ACTIVE_NONE;
187     sc->tz_tmp_updating = 0;
188 
189     /*
190      * Parse the current state of the thermal zone and build control
191      * structures.
192      */
193     if ((error = acpi_tz_establish(sc)) != 0)
194 	goto out;
195 
196     /*
197      * Register for any Notify events sent to this zone.
198      */
199     AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
200 			     acpi_tz_notify_handler, sc);
201 
202     /*
203      * Create our sysctl nodes.
204      *
205      * XXX we need a mechanism for adding nodes under ACPI.
206      */
207     if (device_get_unit(dev) == 0) {
208 	acpi_sc = acpi_device_get_parent_softc(dev);
209 	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
210 	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
211 			      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
212 			      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
213 	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
214 		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
215 		       OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW,
216 		       &acpi_tz_min_runtime, 0,
217 		       "minimum cooling run time in sec");
218 	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
219 		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
220 		       OID_AUTO, "polling_rate", CTLFLAG_RD | CTLFLAG_RW,
221 		       &acpi_tz_polling_rate, 0, "monitor polling rate");
222     }
223     sysctl_ctx_init(&sc->tz_sysctl_ctx);
224     sprintf(oidname, "tz%d", device_get_unit(dev));
225     sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
226 					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
227 					 OID_AUTO, oidname, CTLFLAG_RD, 0, "");
228     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
229 		   OID_AUTO, "temperature", CTLFLAG_RD,
230 		   &sc->tz_temperature, 0, "current thermal zone temperature");
231     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
232 		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
233 		    sc, 0, acpi_tz_active_sysctl, "I", "");
234 
235     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
236 		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
237 		   &sc->tz_thflags, 0, "thermal zone flags");
238     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
239 		   OID_AUTO, "_PSV", CTLFLAG_RD,
240 		   &sc->tz_zone.psv, 0, "");
241     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
242 		   OID_AUTO, "_HOT", CTLFLAG_RD,
243 		   &sc->tz_zone.hot, 0, "");
244     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
245 		   OID_AUTO, "_CRT", CTLFLAG_RD,
246 		   &sc->tz_zone.crt, 0, "");
247     SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
248 		      OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac,
249 		      sizeof(sc->tz_zone.ac), "I", "");
250 
251     /*
252      * Register our power profile event handler, and flag it for a manual
253      * invocation by our timeout.  We defer it like this so that the rest
254      * of the subsystem has time to come up.
255      */
256     EVENTHANDLER_REGISTER(power_profile_change, acpi_tz_power_profile, sc, 0);
257     sc->tz_flags |= TZ_FLAG_GETPROFILE;
258 
259     /*
260      * Don't bother evaluating/printing the temperature at this point;
261      * on many systems it'll be bogus until the EC is running.
262      */
263 
264     /*
265      * Create our thread; we only need one, it will service all of the
266      * thermal zones.
267      */
268     if (acpi_tz_proc == NULL) {
269 	    error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_proc,
270 				   RFHIGHPID, 0, "acpi_thermal");
271 	    if (error != 0) {
272 		    device_printf(sc->tz_dev, "could not create thread - %d",
273 				  error);
274 		    goto out;
275 	    }
276     }
277 
278  out:
279     ACPI_UNLOCK;
280 
281     return_VALUE (error);
282 }
283 
284 /*
285  * Parse the current state of this thermal zone and set up to use it.
286  *
287  * Note that we may have previous state, which will have to be discarded.
288  */
289 static int
290 acpi_tz_establish(struct acpi_tz_softc *sc)
291 {
292     ACPI_OBJECT	*obj;
293     int		i;
294     char	nbuf[8];
295 
296     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
297 
298     ACPI_ASSERTLOCK;
299 
300     /* Power everything off and erase any existing state. */
301     acpi_tz_all_off(sc);
302     for (i = 0; i < TZ_NUMLEVELS; i++)
303 	if (sc->tz_zone.al[i].Pointer != NULL)
304 	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
305     if (sc->tz_zone.psl.Pointer != NULL)
306 	AcpiOsFree(sc->tz_zone.psl.Pointer);
307     bzero(&sc->tz_zone, sizeof(sc->tz_zone));
308 
309     /* Evaluate thermal zone parameters. */
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 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 	return (aclevel_string[active+1]);
369 }
370 
371 /*
372  * Evaluate the condition of a thermal zone, take appropriate actions.
373  */
374 static void
375 acpi_tz_monitor(void *Context)
376 {
377     struct acpi_tz_softc *sc;
378     struct	timespec curtime;
379     int		temp;
380     int		i;
381     int		newactive, newflags;
382     ACPI_STATUS	status;
383 
384     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
385 
386     ACPI_ASSERTLOCK;
387 
388     sc = (struct acpi_tz_softc *)Context;
389     if (sc->tz_tmp_updating)
390 	goto out;
391     sc->tz_tmp_updating = 1;
392 
393     /* Get the current temperature. */
394     status = acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp);
395     if (ACPI_FAILURE(status)) {
396 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
397 	    "error fetching current temperature -- %s\n",
398 	     AcpiFormatException(status));
399 	/* XXX disable zone? go to max cooling? */
400 	goto out;
401     }
402 
403     ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
404     sc->tz_temperature = temp;
405 
406     /*
407      * Work out what we ought to be doing right now.
408      *
409      * Note that the _ACx levels sort from hot to cold.
410      */
411     newactive = TZ_ACTIVE_NONE;
412     for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
413 	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
414 	    newactive = i;
415 	    if (sc->tz_active != newactive) {
416 		ACPI_VPRINT(sc->tz_dev,
417 			    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 
432 	getnanotime(&curtime);
433 	timespecsub(&curtime, &sc->tz_cooling_started);
434 	if (curtime.tv_sec < acpi_tz_min_runtime)
435 	    newactive = sc->tz_active;
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     /* If the active cooling state has changed, we have to switch things. */
452     if (newactive != sc->tz_active) {
453 	/* Turn off the cooling devices that are on, if any are */
454 	if (sc->tz_active != TZ_ACTIVE_NONE)
455 	    acpi_ForeachPackageObject(
456 		(ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
457 		acpi_tz_switch_cooler_off, sc);
458 
459 	/* Turn on cooling devices that are required, if any are */
460 	if (newactive != TZ_ACTIVE_NONE) {
461 	    acpi_ForeachPackageObject(
462 		(ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
463 		acpi_tz_switch_cooler_on, sc);
464 	}
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     /* XXX (de)activate any passive cooling that may be required. */
473 
474     /*
475      * If we have just become _HOT or _CRT, warn the user.
476      *
477      * We should actually shut down at this point, but it's not clear
478      * that some systems don't actually map _CRT to the same value as _AC0.
479      */
480     if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0 &&
481 	(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) == 0) {
482 
483 	device_printf(sc->tz_dev,
484 	    "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     /* Scan all the _ALx objects and turn them all off. */
508     for (i = 0; i < TZ_NUMLEVELS; i++) {
509 	if (sc->tz_zone.al[i].Pointer == NULL)
510 	    continue;
511 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
512 				  acpi_tz_switch_cooler_off, sc);
513     }
514 
515     /*
516      * XXX revert any passive-cooling options.
517      */
518 
519     sc->tz_active = TZ_ACTIVE_NONE;
520     sc->tz_thflags = TZ_THFLAG_NONE;
521 
522     return_VOID;
523 }
524 
525 /*
526  * Given an object, verify that it's a reference to a device of some sort,
527  * and try to switch it off.
528  */
529 static void
530 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
531 {
532     ACPI_HANDLE		cooler;
533 
534     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
535 
536     ACPI_ASSERTLOCK;
537 
538     switch(obj->Type) {
539     case ACPI_TYPE_ANY:
540 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
541 			 acpi_name(obj->Reference.Handle)));
542 
543 	acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D3);
544 	break;
545     case ACPI_TYPE_STRING:
546 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
547 			 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     default:
560 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
561 			 "called to handle unsupported object type %d\n",
562 			 obj->Type));
563 	break;
564     }
565 
566     return_VOID;
567 }
568 
569 /*
570  * Given an object, verify that it's a reference to a device of some sort,
571  * and try to switch it on.
572  *
573  * XXX replication of off/on function code is bad, mmmkay?
574  */
575 static void
576 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
577 {
578     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
579     ACPI_HANDLE			cooler;
580     ACPI_STATUS			status;
581 
582     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
583 
584     ACPI_ASSERTLOCK;
585 
586     switch(obj->Type) {
587     case ACPI_TYPE_ANY:
588 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
589 			 acpi_name(obj->Reference.Handle)));
590 
591 	status = acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D0);
592 	if (ACPI_FAILURE(status)) {
593 	    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
594 			"failed to activate %s - %s\n",
595 			acpi_name(obj->Reference.Handle),
596 			AcpiFormatException(status));
597 	}
598 	break;
599     case ACPI_TYPE_STRING:
600 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
601 			 obj->String.Pointer));
602 
603 	/*
604 	 * Find the handle for the device and turn it off.
605 	 * The String object here seems to contain a fully-qualified path, so we
606 	 * don't have to search for it in our parents.
607 	 *
608 	 * XXX This may not always be the case.
609 	 */
610 	if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler))) {
611 	    status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
612 	    if (ACPI_FAILURE(status)) {
613 		ACPI_VPRINT(sc->tz_dev,
614 			    acpi_device_get_parent_softc(sc->tz_dev),
615 			    "failed to activate %s - %s\n",
616 			    obj->String.Pointer, AcpiFormatException(status));
617 	    }
618 	} else {
619 	    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
620 			"couldn't find %s\n", obj->String.Pointer);
621 	}
622 	break;
623     default:
624 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "unsupported object type %d\n",
625 			  obj->Type));
626 	break;
627     }
628 
629     return_VOID;
630 }
631 
632 /*
633  * Read/debug-print a parameter, default it to -1.
634  */
635 static void
636 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
637 {
638 
639     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
640 
641     ACPI_ASSERTLOCK;
642 
643     if (ACPI_FAILURE(acpi_EvaluateInteger(sc->tz_handle, node, data))) {
644 	*data = -1;
645     } else {
646 	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n",
647 			 acpi_name(sc->tz_handle), node, *data));
648     }
649 
650     return_VOID;
651 }
652 
653 /*
654  * Sanity-check a temperature value.  Assume that setpoints
655  * should be between 0C and 150C.
656  */
657 static void
658 acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
659 {
660     if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 1500)) {
661 	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
662 		      what, TZ_KELVTOC(*val));
663 	*val = -1;
664     }
665 }
666 
667 /*
668  * Respond to a sysctl on the active state node.
669  */
670 static int
671 acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
672 {
673     struct acpi_tz_softc	*sc;
674     int				active;
675     int		 		error;
676     ACPI_LOCK_DECL;
677 
678     ACPI_LOCK;
679 
680     sc = (struct acpi_tz_softc *)oidp->oid_arg1;
681     active = sc->tz_active;
682     error = sysctl_handle_int(oidp, &active, 0, req);
683 
684     /* Error or no new value */
685     if (error != 0 || req->newptr == NULL)
686 	goto out;
687     if (active < -1 || active >= TZ_NUMLEVELS) {
688 	error = EINVAL;
689 	goto out;
690     }
691 
692     /* Set new preferred level and re-switch */
693     sc->tz_requested = active;
694     acpi_tz_monitor(sc);
695 
696  out:
697     ACPI_UNLOCK;
698     return (error);
699 }
700 
701 /*
702  * Respond to a Notify event sent to the zone.
703  */
704 static void
705 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
706 {
707     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
708 
709     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
710 
711     ACPI_ASSERTLOCK;
712 
713     switch(notify) {
714     case TZ_NOTIFY_TEMPERATURE:
715 	/* Temperature change occurred */
716 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, acpi_tz_monitor, sc);
717 	break;
718     case TZ_NOTIFY_DEVICES:
719     case TZ_NOTIFY_LEVELS:
720 	/* Zone devices/setpoints changed */
721 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
722 				(OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
723 	break;
724     default:
725 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
726 		    "unknown Notify event 0x%x\n", notify);
727 	break;
728     }
729 
730     return_VOID;
731 }
732 
733 /*
734  * Poll the thermal zone.
735  */
736 static void
737 acpi_tz_timeout(struct acpi_tz_softc *sc)
738 {
739     /* Do we need to get the power profile settings? */
740     if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
741 	acpi_tz_power_profile((void *)sc);
742 	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
743     }
744 
745     ACPI_ASSERTLOCK;
746 
747     /* Check the current temperature and take action based on it */
748     acpi_tz_monitor(sc);
749 
750     /* XXX passive cooling actions? */
751 }
752 
753 /*
754  * System power profile may have changed; fetch and notify the
755  * thermal zone accordingly.
756  *
757  * Since this can be called from an arbitrary eventhandler, it needs
758  * to get the ACPI lock itself.
759  */
760 static void
761 acpi_tz_power_profile(void *arg)
762 {
763     ACPI_OBJECT_LIST		args;
764     ACPI_OBJECT			obj;
765     ACPI_STATUS			status;
766     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
767     int				state;
768     ACPI_LOCK_DECL;
769 
770     state = power_profile_get_state();
771     if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
772 	return;
773 
774     ACPI_LOCK;
775 
776     /* check that we haven't decided there's no _SCP method */
777     if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) {
778 
779 	/* Call _SCP to set the new profile */
780 	obj.Type = ACPI_TYPE_INTEGER;
781 	obj.Integer.Value = (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1;
782 	args.Count = 1;
783 	args.Pointer = &obj;
784 	status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL);
785 	if (ACPI_FAILURE(status)) {
786 	    if (status != AE_NOT_FOUND)
787 		ACPI_VPRINT(sc->tz_dev,
788 			    acpi_device_get_parent_softc(sc->tz_dev),
789 			    "can't evaluate %s._SCP - %s\n",
790 			    acpi_name(sc->tz_handle),
791 			    AcpiFormatException(status));
792 	    sc->tz_flags |= TZ_FLAG_NO_SCP;
793 	} else {
794 	    /* We have to re-evaluate the entire zone now */
795 	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
796 				    (OSD_EXECUTION_CALLBACK)acpi_tz_establish,
797 				    sc);
798 	}
799     }
800 
801     ACPI_UNLOCK;
802 }
803 
804 /*
805  * Thermal zone monitor thread.
806  */
807 static void
808 acpi_tz_thread(void *arg)
809 {
810     device_t	*devs;
811     int		devcount, i;
812     ACPI_LOCK_DECL;
813 
814     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
815 
816     devs = NULL;
817     devcount = 0;
818 
819     for (;;) {
820 	tsleep(&acpi_tz_proc, PZERO, "tzpoll", hz * acpi_tz_polling_rate);
821 
822 #if __FreeBSD_version >= 500000
823 	mtx_lock(&Giant);
824 #endif
825 
826 	if (devcount == 0)
827 	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
828 
829 	ACPI_LOCK;
830 	for (i = 0; i < devcount; i++)
831 	    acpi_tz_timeout(device_get_softc(devs[i]));
832 	ACPI_UNLOCK;
833 
834 #if __FreeBSD_version >= 500000
835 	mtx_unlock(&Giant);
836 #endif
837     }
838 }
839