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