xref: /freebsd/sys/dev/acpica/acpi_thermal.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*-
2  * Copyright (c) 2000, 2001 Michael Smith
3  * Copyright (c) 2000 BSDi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *	$FreeBSD$
28  */
29 
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
34 #include <sys/reboot.h>
35 
36 #include "acpi.h"
37 
38 #include <dev/acpica/acpivar.h>
39 
40 /*
41  * Hooks for the ACPI CA debugging infrastructure
42  */
43 #define _COMPONENT	ACPI_THERMAL
44 MODULE_NAME("THERMAL")
45 
46 #define TZ_ZEROC	2732
47 #define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
48 
49 #define TZ_NOTIFY_TEMPERATURE	0x80
50 #define TZ_NOTIFY_DEVICES	0x81
51 #define TZ_NOTIFY_LEVELS	0x82
52 
53 #define TZ_POLLRATE	(hz * 10)	/* every ten seconds */
54 
55 #define TZ_NUMLEVELS	10		/* defined by ACPI spec */
56 struct acpi_tz_state {
57     int		ac[TZ_NUMLEVELS];
58     ACPI_BUFFER	al[TZ_NUMLEVELS];
59     int		crt;
60     int		hot;
61     ACPI_BUFFER	psl;
62     int		psv;
63     int		tc1;
64     int		tc2;
65     int		tsp;
66     int		tzp;
67 };
68 
69 
70 struct acpi_tz_softc {
71     device_t			tz_dev;
72     ACPI_HANDLE			tz_handle;
73     struct callout_handle	tz_timeout;
74     int				tz_current;
75 #define TZ_STATE_NONE		0
76 #define TZ_STATE_PSV		1
77 #define TZ_STATE_AC0		2
78 #define TZ_STATE_HOT		(TZ_STATE_AC0 + TZ_NUMLEVELS)
79 #define TZ_STATE_CRT		(TZ_STATE_AC0 + TZ_NUMLEVELS + 1)
80 
81     struct acpi_tz_state 	tz_state;
82 };
83 
84 static int	acpi_tz_probe(device_t dev);
85 static int	acpi_tz_attach(device_t dev);
86 static int	acpi_tz_establish(struct acpi_tz_softc *sc);
87 static void	acpi_tz_monitor(struct acpi_tz_softc *sc);
88 static void	acpi_tz_all_off(struct acpi_tz_softc *sc);
89 static void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
90 static void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
91 static void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
92 static void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
93 static void	acpi_tz_timeout(void *arg);
94 
95 static device_method_t acpi_tz_methods[] = {
96     /* Device interface */
97     DEVMETHOD(device_probe,	acpi_tz_probe),
98     DEVMETHOD(device_attach,	acpi_tz_attach),
99 
100     {0, 0}
101 };
102 
103 static driver_t acpi_tz_driver = {
104     "acpi_tz",
105     acpi_tz_methods,
106     sizeof(struct acpi_tz_softc),
107 };
108 
109 devclass_t acpi_tz_devclass;
110 DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
111 
112 /*
113  * Match an ACPI thermal zone.
114  */
115 static int
116 acpi_tz_probe(device_t dev)
117 {
118     int		result;
119 
120     ACPI_LOCK;
121 
122     /* no FUNCTION_TRACE - too noisy */
123 
124     if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
125 	!acpi_disabled("thermal")) {
126 	device_set_desc(dev, "thermal zone");
127 	result = -10;
128     } else {
129 	result = ENXIO;
130     }
131     ACPI_UNLOCK;
132     return(result);
133 }
134 
135 /*
136  * Attach to an ACPI thermal zone.
137  */
138 static int
139 acpi_tz_attach(device_t dev)
140 {
141     struct acpi_tz_softc	*sc;
142     int				error;
143 
144     FUNCTION_TRACE(__func__);
145 
146     ACPI_LOCK;
147 
148     sc = device_get_softc(dev);
149     sc->tz_dev = dev;
150     sc->tz_handle = acpi_get_handle(dev);
151 
152     /*
153      * Parse the current state of the thermal zone and build control
154      * structures.
155      */
156     if ((error = acpi_tz_establish(sc)) != 0)
157 	goto out;
158 
159     /*
160      * Register for any Notify events sent to this zone.
161      */
162     AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
163 			     acpi_tz_notify_handler, sc);
164 
165     /*
166      * Don't bother evaluating/printing the temperature at this point;
167      * on many systems it'll be bogus until the EC is running.
168      */
169 
170  out:
171     ACPI_UNLOCK;
172     return_VALUE(error);
173 }
174 
175 /*
176  * Parse the current state of this thermal zone and set up to use it.
177  *
178  * Note that we may have previous state, which will have to be discarded.
179  */
180 static int
181 acpi_tz_establish(struct acpi_tz_softc *sc)
182 {
183     ACPI_OBJECT	*obj;
184     int		i;
185     char	nbuf[8];
186 
187     FUNCTION_TRACE(__func__);
188 
189     ACPI_ASSERTLOCK;
190 
191     /*
192      * Power everything off and erase any existing state.
193      */
194     acpi_tz_all_off(sc);
195     for (i = 0; i < TZ_NUMLEVELS; i++)
196 	if (sc->tz_state.al[i].Pointer != NULL)
197 	    AcpiOsFree(sc->tz_state.al[i].Pointer);
198     if (sc->tz_state.psl.Pointer != NULL)
199 	AcpiOsFree(sc->tz_state.psl.Pointer);
200     bzero(&sc->tz_state, sizeof(sc->tz_state));
201 
202     /* kill the timeout (harmless if not running */
203     untimeout(acpi_tz_timeout, sc, sc->tz_timeout);
204 
205     /*
206      * Evaluate thermal zone parameters.
207      */
208     for (i = 0; i < TZ_NUMLEVELS; i++) {
209 	sprintf(nbuf, "_AC%d", i);
210 	acpi_tz_getparam(sc, nbuf, &sc->tz_state.ac[i]);
211 	sprintf(nbuf, "_AL%d", i);
212 	acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_state.al[i]);
213 	obj = (ACPI_OBJECT *)sc->tz_state.al[i].Pointer;
214 	if (obj != NULL) {
215 	    /* should be a package containing a list of power objects */
216 	    if (obj->Type != ACPI_TYPE_PACKAGE) {
217 		device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
218 			      nbuf, obj->Type);
219 		return_VALUE(ENXIO);
220 	    }
221 	}
222     }
223     acpi_tz_getparam(sc, "_CRT", &sc->tz_state.crt);
224     acpi_tz_getparam(sc, "_HOT", &sc->tz_state.hot);
225     acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_state.psl);
226     acpi_tz_getparam(sc, "_PSV", &sc->tz_state.psv);
227     acpi_tz_getparam(sc, "_TC1", &sc->tz_state.tc1);
228     acpi_tz_getparam(sc, "_TC2", &sc->tz_state.tc2);
229     acpi_tz_getparam(sc, "_TSP", &sc->tz_state.tsp);
230     acpi_tz_getparam(sc, "_TZP", &sc->tz_state.tzp);
231 
232     /*
233      * Power off everything that we've just been given.
234      */
235     acpi_tz_all_off(sc);
236 
237     /*
238      * Do we need to poll the thermal zone?  Ignore the suggested
239      * rate.
240      */
241     if (sc->tz_state.tzp != 0)
242 	sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
243 
244 
245     return_VALUE(0);
246 }
247 
248 /*
249  * Evaluate the condition of a thermal zone, take appropriate actions.
250  */
251 static void
252 acpi_tz_monitor(struct acpi_tz_softc *sc)
253 {
254     int		temp, new;
255     int		i;
256 
257     FUNCTION_TRACE(__func__);
258 
259     ACPI_ASSERTLOCK;
260 
261     /*
262      * Get the current temperature.
263      */
264     if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) {
265 	device_printf(sc->tz_dev, "error fetching current temperature\n");
266 	/* XXX disable zone? go to max cooling? */
267 	return_VOID;
268     }
269     DEBUG_PRINT(TRACE_VALUES, ("got %d.%dC\n", TZ_KELVTOC(temp)));
270 
271     /*
272      * Work out what we ought to be doing right now.
273      */
274     new = TZ_STATE_NONE;
275     if ((sc->tz_state.psv != -1) && (temp > sc->tz_state.psv))
276 	new = TZ_STATE_PSV;
277     for (i = 0; i < TZ_NUMLEVELS; i++)
278 	if ((sc->tz_state.ac[i] != -1) && (temp > sc->tz_state.ac[i]))
279 	    new = TZ_STATE_AC0 + i;
280     if ((sc->tz_state.hot != -1) && (temp > sc->tz_state.hot))
281 	new = TZ_STATE_HOT;
282     if ((sc->tz_state.crt != -1) && (temp > sc->tz_state.crt))
283 	new = TZ_STATE_CRT;
284 
285     /*
286      * If our state has not changed, do nothing.
287      */
288     if (new == sc->tz_current)
289 	return_VOID;
290 
291     /*
292      * XXX if we're in a passive-cooling mode, revert to full-speed operation.
293      */
294     if (sc->tz_current == TZ_STATE_PSV) {
295 	/* XXX implement */
296     }
297 
298     /*
299      * If we're in an active-cooling mode, turn off the current cooler(s).
300      */
301     if ((sc->tz_current >= TZ_STATE_AC0) && (sc->tz_current < (TZ_STATE_AC0 + TZ_NUMLEVELS)))
302 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[sc->tz_current - TZ_STATE_AC0].Pointer,
303 				  acpi_tz_switch_cooler_off, sc);
304 
305     /*
306      * XXX If the new mode is passive-cooling, make appropriate adjustments.
307      */
308 
309     /*
310      * If the new mode is an active-cooling mode, turn on the new cooler(s).
311      */
312     if ((new >= TZ_STATE_AC0) && (new < (TZ_STATE_AC0 + TZ_NUMLEVELS)))
313 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[new - TZ_STATE_AC0].Pointer,
314 				  acpi_tz_switch_cooler_on, sc);
315 
316     /*
317      * If we're _HOT or _CRT, shut down now!
318      */
319     if ((new == TZ_STATE_HOT) || (new == TZ_STATE_CRT)) {
320 	device_printf(sc->tz_dev, "WARNING - emergency thermal shutdown in progress.\n");
321 	shutdown_nice(RB_POWEROFF);
322     }
323 
324     /* gone to new state */
325     sc->tz_current = new;
326 
327     return_VOID;
328 }
329 
330 /*
331  * Turn off all the cooling devices.
332  */
333 static void
334 acpi_tz_all_off(struct acpi_tz_softc *sc)
335 {
336     int		i;
337 
338     FUNCTION_TRACE(__func__);
339 
340     ACPI_ASSERTLOCK;
341 
342     /*
343      * Scan all the _AL objects, and turn them all off.
344      */
345     for (i = 0; i < TZ_NUMLEVELS; i++) {
346 	if (sc->tz_state.al[i].Pointer == NULL)
347 	    continue;
348 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[i].Pointer,
349 				  acpi_tz_switch_cooler_off, sc);
350     }
351 
352     /*
353      * XXX revert any passive-cooling options.
354      */
355 
356     sc->tz_current = TZ_STATE_NONE;
357     return_VOID;
358 }
359 
360 /*
361  * Given an object, verify that it's a reference to a device of some sort,
362  * and try to switch it off.
363  */
364 static void
365 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
366 {
367     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
368     ACPI_HANDLE			cooler;
369 
370     FUNCTION_TRACE(__func__);
371 
372     ACPI_ASSERTLOCK;
373 
374     switch(obj->Type) {
375     case ACPI_TYPE_STRING:
376 	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
377 
378 	/*
379 	 * Find the handle for the device and turn it off.
380 	 * The String object here seems to contain a fully-qualified path, so we
381 	 * don't have to search for it in our parents.
382 	 *
383 	 * XXX This may not always be the case.
384 	 */
385 	if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK)
386 	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
387 	break;
388 
389     default:
390 	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
391 				    obj->Type));
392 	break;
393     }
394 	return_VOID;
395 }
396 
397 /*
398  * Given an object, verify that it's a reference to a device of some sort,
399  * and try to switch it on.
400  *
401  * XXX replication of off/on function code is bad, mmmkay?
402  */
403 static void
404 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
405 {
406     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
407     ACPI_HANDLE			cooler;
408 
409     FUNCTION_TRACE(__func__);
410 
411     ACPI_ASSERTLOCK;
412 
413     switch(obj->Type) {
414     case ACPI_TYPE_STRING:
415 	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
416 
417 	/*
418 	 * Find the handle for the device and turn it off.
419 	 * The String object here seems to contain a fully-qualified path, so we
420 	 * don't have to search for it in our parents.
421 	 *
422 	 * XXX This may not always be the case.
423 	 */
424 	if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK)
425 	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
426 	break;
427 
428     default:
429 	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
430 				    obj->Type));
431 	break;
432     }
433 	return_VOID;
434 }
435 
436 /*
437  * Read/debug-print a parameter, default it to -1.
438  */
439 static void
440 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
441 {
442 
443     FUNCTION_TRACE(__func__);
444 
445     ACPI_ASSERTLOCK;
446 
447     if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
448 	*data = -1;
449     } else {
450 	DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle),
451 				   node, *data));
452     }
453     return_VOID;
454 }
455 
456 /*
457  * Respond to a Notify event sent to the zone.
458  */
459 static void
460 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
461 {
462     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
463 
464     FUNCTION_TRACE(__func__);
465 
466     ACPI_ASSERTLOCK;
467 
468     switch(notify) {
469     case TZ_NOTIFY_TEMPERATURE:
470 	/* temperature change occurred */
471 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
472 	break;
473     case TZ_NOTIFY_DEVICES:
474     case TZ_NOTIFY_LEVELS:
475 	/* zone devices/setpoints changed */
476 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
477 	break;
478     default:
479 	device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
480 	break;
481     }
482     return_VOID;
483 }
484 
485 /*
486  * Poll the thermal zone.
487  */
488 static void
489 acpi_tz_timeout(void *arg)
490 {
491     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
492 
493     ACPI_LOCK;
494 
495     /* check temperature, take action */
496     AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
497 
498     /* XXX passive cooling actions? */
499 
500     /* re-register ourself */
501     sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
502 
503     ACPI_UNLOCK;
504 }
505