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