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