acpi_thermal.c revision 119418
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: head/sys/dev/acpica/acpi_thermal.c 119418 2003-08-24 17:55:58Z obrien $");
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
50ACPI_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 */
62struct 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
76struct 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
100static int	acpi_tz_probe(device_t dev);
101static int	acpi_tz_attach(device_t dev);
102static int	acpi_tz_establish(struct acpi_tz_softc *sc);
103static void	acpi_tz_monitor(struct acpi_tz_softc *sc);
104static void	acpi_tz_all_off(struct acpi_tz_softc *sc);
105static void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
106static void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
107static void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
108static void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
109static int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
110static void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
111static void	acpi_tz_timeout(struct acpi_tz_softc *sc);
112static void	acpi_tz_power_profile(void *arg);
113
114static void	acpi_tz_thread(void *arg);
115static struct proc *acpi_tz_proc;
116
117static 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
125static driver_t acpi_tz_driver = {
126    "acpi_tz",
127    acpi_tz_methods,
128    sizeof(struct acpi_tz_softc),
129};
130
131static devclass_t acpi_tz_devclass;
132DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
133
134static struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
135static struct sysctl_oid	*acpi_tz_sysctl_tree;
136
137static int			acpi_tz_min_runtime = 0;/* minimum cooling run time */
138static int			acpi_tz_polling_rate = TZ_POLLRATE;
139
140/*
141 * Match an ACPI thermal zone.
142 */
143static int
144acpi_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 */
167static int
168acpi_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 */
285static int
286acpi_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
358static char	*aclevel_string[] =	{
359	"NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
360	"_AC5", "_AC6", "_AC7", "_AC8", "_AC9" };
361
362static __inline const char *
363acpi_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 */
375static void
376acpi_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
490out:
491    sc->tz_tmp_updating = 0;
492    return_VOID;
493}
494
495/*
496 * Turn off all the cooling devices.
497 */
498static void
499acpi_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 */
530static void
531acpi_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 */
574static void
575acpi_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 */
629static void
630acpi_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 */
650static void
651acpi_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 */
663static int
664acpi_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 */
699static void
700acpi_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 */
729static void
730acpi_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 */
754static void
755acpi_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 */
797static void
798acpi_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, PZERO, "nothing", hz * acpi_tz_polling_rate);
812
813#if __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 __FreeBSD_version >= 500000
826	mtx_unlock(&Giant);
827#endif
828    }
829}
830