Bring in YONETANI Tomokazu's acpi-update-2.patch (27-May-2004), a major
[dragonfly.git] / sys / dev / acpica5 / acpi_thermal.c
CommitLineData
5ed44076
MD
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 *
f9d8cd12
MD
27 * $FreeBSD: src/sys/dev/acpica/acpi_thermal.c,v 1.45 2004/05/06 02:57:24 njl Exp $
28 * $DragonFly: src/sys/dev/acpica5/acpi_thermal.c,v 1.2 2004/06/27 08:52:39 dillon Exp $
5ed44076
MD
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#include <dev/acpica5/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);
f9d8cd12 140MODULE_DEPEND(acpi_tz, acpi, 1, 1, 1);
5ed44076
MD
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 thread *acpi_tz_td;
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;
f9d8cd12
MD
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
5ed44076
MD
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_td == NULL) {
283 error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_td,
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. */
f9d8cd12 408 status = acpi_GetInteger(sc->tz_handle, "_TMP", &temp);
5ed44076
MD
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 */
f9d8cd12 453 if (sc->tz_requested != TZ_ACTIVE_NONE && sc->tz_requested < newactive)
5ed44076
MD
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{
f9d8cd12 554 ACPI_HANDLE cooler;
5ed44076
MD
555
556 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
557
558 ACPI_ASSERTLOCK;
559
f9d8cd12
MD
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;
5ed44076
MD
564 }
565
f9d8cd12
MD
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
5ed44076
MD
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 *
f9d8cd12 577 * XXX replication of off/on function code is bad.
5ed44076
MD
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
f9d8cd12
MD
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 }
5ed44076 595
f9d8cd12
MD
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));
5ed44076
MD
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
f9d8cd12 619 if (ACPI_FAILURE(acpi_GetInteger(sc->tz_handle, node, data))) {
5ed44076
MD
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{
5ed44076
MD
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 */
f9d8cd12
MD
756 status = acpi_SetInteger(sc->tz_handle, "_SCP",
757 (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1);
5ed44076
MD
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_td, 0, "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}