xref: /freebsd/sys/dev/acpica/acpi_thermal.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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 #include <sys/sysctl.h>
36 
37 #include "acpi.h"
38 
39 #include <dev/acpica/acpivar.h>
40 
41 /*
42  * Hooks for the ACPI CA debugging infrastructure
43  */
44 #define _COMPONENT	ACPI_THERMAL
45 MODULE_NAME("THERMAL")
46 
47 #define TZ_ZEROC	2732
48 #define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
49 
50 #define TZ_NOTIFY_TEMPERATURE	0x80
51 #define TZ_NOTIFY_DEVICES	0x81
52 #define TZ_NOTIFY_LEVELS	0x82
53 
54 #define TZ_POLLRATE	(hz * 10)	/* every ten seconds */
55 
56 #define TZ_NUMLEVELS	10		/* defined by ACPI spec */
57 struct acpi_tz_state {
58     int		ac[TZ_NUMLEVELS];
59     ACPI_BUFFER	al[TZ_NUMLEVELS];
60     int		crt;
61     int		hot;
62     ACPI_BUFFER	psl;
63     int		psv;
64     int		tc1;
65     int		tc2;
66     int		tsp;
67     int		tzp;
68 };
69 
70 
71 struct acpi_tz_softc {
72     device_t			tz_dev;
73     ACPI_HANDLE			tz_handle;
74     struct callout_handle	tz_timeout;
75     int				tz_temperature;
76     int				tz_active;
77 #define TZ_ACTIVE_NONE	-1
78     int				tz_flags;
79 #define TZ_FLAG_NONE	0
80 #define TZ_FLAG_PSV	(1<<0)
81 #define TZ_FLAG_HOT	(1<<2)
82 #define TZ_FLAG_CRT	(1<<3)
83 
84     struct sysctl_ctx_list	tz_sysctl_ctx;
85     struct sysctl_oid		*tz_sysctl_tree;
86 
87     struct acpi_tz_state 	tz_state;
88 };
89 
90 static int	acpi_tz_probe(device_t dev);
91 static int	acpi_tz_attach(device_t dev);
92 static int	acpi_tz_establish(struct acpi_tz_softc *sc);
93 static void	acpi_tz_monitor(struct acpi_tz_softc *sc);
94 static void	acpi_tz_all_off(struct acpi_tz_softc *sc);
95 static void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
96 static void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
97 static void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
98 static void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
99 static void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
100 static void	acpi_tz_timeout(void *arg);
101 
102 static device_method_t acpi_tz_methods[] = {
103     /* Device interface */
104     DEVMETHOD(device_probe,	acpi_tz_probe),
105     DEVMETHOD(device_attach,	acpi_tz_attach),
106 
107     {0, 0}
108 };
109 
110 static driver_t acpi_tz_driver = {
111     "acpi_tz",
112     acpi_tz_methods,
113     sizeof(struct acpi_tz_softc),
114 };
115 
116 devclass_t acpi_tz_devclass;
117 DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
118 
119 static struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
120 static struct sysctl_oid	*acpi_tz_sysctl_tree;
121 
122 /*
123  * Match an ACPI thermal zone.
124  */
125 static int
126 acpi_tz_probe(device_t dev)
127 {
128     int		result;
129 
130     ACPI_LOCK;
131 
132     /* no FUNCTION_TRACE - too noisy */
133 
134     if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
135 	!acpi_disabled("thermal")) {
136 	device_set_desc(dev, "thermal zone");
137 	result = -10;
138     } else {
139 	result = ENXIO;
140     }
141     ACPI_UNLOCK;
142     return(result);
143 }
144 
145 /*
146  * Attach to an ACPI thermal zone.
147  */
148 static int
149 acpi_tz_attach(device_t dev)
150 {
151     struct acpi_tz_softc	*sc;
152     struct acpi_softc		*acpi_sc;
153     int				error;
154     char			oidname[8];
155     int				i;
156 
157     FUNCTION_TRACE(__func__);
158 
159     ACPI_LOCK;
160 
161     sc = device_get_softc(dev);
162     sc->tz_dev = dev;
163     sc->tz_handle = acpi_get_handle(dev);
164 
165     /*
166      * Parse the current state of the thermal zone and build control
167      * structures.
168      */
169     if ((error = acpi_tz_establish(sc)) != 0)
170 	goto out;
171 
172     /*
173      * Register for any Notify events sent to this zone.
174      */
175     AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
176 			     acpi_tz_notify_handler, sc);
177 
178     /*
179      * Create our sysctl nodes.
180      *
181      * XXX we need a mechanism for adding nodes under ACPI.
182      */
183     if (device_get_unit(dev) == 0) {
184 	acpi_sc = acpi_device_get_parent_softc(dev);
185 	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
186 	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
187 					      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
188 					      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
189     }
190     sysctl_ctx_init(&sc->tz_sysctl_ctx);
191     sprintf(oidname, "tz%d", device_get_unit(dev));
192     sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
193 					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
194 					 oidname, CTLFLAG_RD, 0, "");
195     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
196 		   OID_AUTO, "temperature", CTLFLAG_RD,
197 		   &sc->tz_temperature, 0, "current thermal zone temperature");
198     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
199 		   OID_AUTO, "active", CTLFLAG_RD,
200 		   &sc->tz_active, 0, "active cooling mode");
201     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
202 		   OID_AUTO, "flags", CTLFLAG_RD,
203 		   &sc->tz_flags, 0, "thermal zone flags");
204 
205     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
206 		   OID_AUTO, "_PSV", CTLFLAG_RD,
207 		   &sc->tz_state.psv, 0, "");
208     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
209 		   OID_AUTO, "_HOT", CTLFLAG_RD,
210 		   &sc->tz_state.hot, 0, "");
211     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
212 		   OID_AUTO, "_CRT", CTLFLAG_RD,
213 		   &sc->tz_state.crt, 0, "");
214     for (i = 0; i < TZ_NUMLEVELS; i++) {
215 	sprintf(oidname, "_AC%d", i);
216 	SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
217 		       OID_AUTO, oidname, CTLFLAG_RD,
218 		       &sc->tz_state.ac[i], 0, "");
219     }
220 
221     /*
222      * Don't bother evaluating/printing the temperature at this point;
223      * on many systems it'll be bogus until the EC is running.
224      */
225 
226  out:
227     ACPI_UNLOCK;
228     return_VALUE(error);
229 }
230 
231 /*
232  * Parse the current state of this thermal zone and set up to use it.
233  *
234  * Note that we may have previous state, which will have to be discarded.
235  */
236 static int
237 acpi_tz_establish(struct acpi_tz_softc *sc)
238 {
239     ACPI_OBJECT	*obj;
240     int		i;
241     char	nbuf[8];
242 
243     FUNCTION_TRACE(__func__);
244 
245     ACPI_ASSERTLOCK;
246 
247     /*
248      * Power everything off and erase any existing state.
249      */
250     acpi_tz_all_off(sc);
251     for (i = 0; i < TZ_NUMLEVELS; i++)
252 	if (sc->tz_state.al[i].Pointer != NULL)
253 	    AcpiOsFree(sc->tz_state.al[i].Pointer);
254     if (sc->tz_state.psl.Pointer != NULL)
255 	AcpiOsFree(sc->tz_state.psl.Pointer);
256     bzero(&sc->tz_state, sizeof(sc->tz_state));
257 
258     /* kill the timeout (harmless if not running */
259     untimeout(acpi_tz_timeout, sc, sc->tz_timeout);
260 
261     /*
262      * Evaluate thermal zone parameters.
263      */
264     for (i = 0; i < TZ_NUMLEVELS; i++) {
265 	sprintf(nbuf, "_AC%d", i);
266 	acpi_tz_getparam(sc, nbuf, &sc->tz_state.ac[i]);
267 	sprintf(nbuf, "_AL%d", i);
268 	acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_state.al[i]);
269 	obj = (ACPI_OBJECT *)sc->tz_state.al[i].Pointer;
270 	if (obj != NULL) {
271 	    /* should be a package containing a list of power objects */
272 	    if (obj->Type != ACPI_TYPE_PACKAGE) {
273 		device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
274 			      nbuf, obj->Type);
275 		return_VALUE(ENXIO);
276 	    }
277 	}
278     }
279     acpi_tz_getparam(sc, "_CRT", &sc->tz_state.crt);
280     acpi_tz_getparam(sc, "_HOT", &sc->tz_state.hot);
281     acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_state.psl);
282     acpi_tz_getparam(sc, "_PSV", &sc->tz_state.psv);
283     acpi_tz_getparam(sc, "_TC1", &sc->tz_state.tc1);
284     acpi_tz_getparam(sc, "_TC2", &sc->tz_state.tc2);
285     acpi_tz_getparam(sc, "_TSP", &sc->tz_state.tsp);
286     acpi_tz_getparam(sc, "_TZP", &sc->tz_state.tzp);
287 
288     /*
289      * Sanity-check the values we've been given.
290      *
291      * XXX what do we do about systems that give us the same value for
292      *     more than one of these setpoints?
293      */
294     acpi_tz_sanity(sc, &sc->tz_state.crt, "_CRT");
295     acpi_tz_sanity(sc, &sc->tz_state.hot, "_HOT");
296     acpi_tz_sanity(sc, &sc->tz_state.psv, "_PSV");
297     for (i = 0; i < TZ_NUMLEVELS; i++)
298 	acpi_tz_sanity(sc, &sc->tz_state.ac[i], "_ACx");
299 
300     /*
301      * Power off everything that we've just been given.
302      */
303     acpi_tz_all_off(sc);
304 
305     /*
306      * The timeout routine always needs to run, since it may be involved
307      * in passive cooling.
308      */
309     sc->tz_timeout = timeout(acpi_tz_timeout, sc, 0);
310 
311 
312     return_VALUE(0);
313 }
314 
315 /*
316  * Evaluate the condition of a thermal zone, take appropriate actions.
317  */
318 static void
319 acpi_tz_monitor(struct acpi_tz_softc *sc)
320 {
321     int		temp;
322     int		i;
323     int		newactive, newflags;
324 
325     FUNCTION_TRACE(__func__);
326 
327     ACPI_ASSERTLOCK;
328 
329     /*
330      * Get the current temperature.
331      */
332     if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) {
333 	device_printf(sc->tz_dev, "error fetching current temperature\n");
334 	/* XXX disable zone? go to max cooling? */
335 	return_VOID;
336     }
337     DEBUG_PRINT(TRACE_VALUES, ("got %d.%dC\n", TZ_KELVTOC(temp)));
338     sc->tz_temperature = temp;
339 
340     /*
341      * Work out what we ought to be doing right now.
342      *
343      * Note that the _ACx levels sort from hot to cold.
344      */
345     newactive = TZ_ACTIVE_NONE;
346     for (i = TZ_NUMLEVELS - 1; i >= 0; i--)
347 	if ((sc->tz_state.ac[i] != -1) && (temp > sc->tz_state.ac[i]))
348 	    newactive = i;
349 
350     newflags = TZ_FLAG_NONE;
351     if ((sc->tz_state.psv != -1) && (temp > sc->tz_state.psv))
352 	newflags |= TZ_FLAG_PSV;
353     if ((sc->tz_state.hot != -1) && (temp > sc->tz_state.hot))
354 	newflags |= TZ_FLAG_HOT;
355     if ((sc->tz_state.crt != -1) && (temp > sc->tz_state.crt))
356 	newflags |= TZ_FLAG_CRT;
357 
358     /*
359      * If the active cooling state has changed, we have to switch things.
360      */
361     if (newactive != sc->tz_active) {
362 
363 	/* turn off the cooling devices that are on, if any are */
364 	if (sc->tz_active != TZ_ACTIVE_NONE)
365 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[sc->tz_active].Pointer,
366 				      acpi_tz_switch_cooler_off, sc);
367 
368 	/* turn on cooling devices that are required, if any are */
369 	if (newactive != TZ_ACTIVE_NONE)
370 	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[newactive].Pointer,
371 				      acpi_tz_switch_cooler_on, sc);
372 	sc->tz_active = newactive;
373     }
374 
375     /*
376      * XXX (de)activate any passive cooling that may be required.
377      */
378 
379     /*
380      * If we have just become _HOT or _CRT, warn the user.
381      *
382      * We should actually shut down at this point, but it's not clear
383      * that some systems don't actually map _CRT to the same value as _AC0.
384      */
385     if ((newflags & (TZ_FLAG_HOT | TZ_FLAG_CRT)) &&
386 	!(sc->tz_flags & (TZ_FLAG_HOT | TZ_FLAG_CRT))) {
387 	device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
388 		      TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature);
389 	/* shutdown_nice(RB_POWEROFF);*/
390     }
391     sc->tz_flags = newflags;
392 
393     return_VOID;
394 }
395 
396 /*
397  * Turn off all the cooling devices.
398  */
399 static void
400 acpi_tz_all_off(struct acpi_tz_softc *sc)
401 {
402     int		i;
403 
404     FUNCTION_TRACE(__func__);
405 
406     ACPI_ASSERTLOCK;
407 
408     /*
409      * Scan all the _AL objects, and turn them all off.
410      */
411     for (i = 0; i < TZ_NUMLEVELS; i++) {
412 	if (sc->tz_state.al[i].Pointer == NULL)
413 	    continue;
414 	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[i].Pointer,
415 				  acpi_tz_switch_cooler_off, sc);
416     }
417 
418     /*
419      * XXX revert any passive-cooling options.
420      */
421 
422     sc->tz_active = TZ_ACTIVE_NONE;
423     sc->tz_flags = TZ_FLAG_NONE;
424     return_VOID;
425 }
426 
427 /*
428  * Given an object, verify that it's a reference to a device of some sort,
429  * and try to switch it off.
430  */
431 static void
432 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
433 {
434     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
435     ACPI_HANDLE			cooler;
436 
437     FUNCTION_TRACE(__func__);
438 
439     ACPI_ASSERTLOCK;
440 
441     switch(obj->Type) {
442     case ACPI_TYPE_STRING:
443 	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
444 
445 	/*
446 	 * Find the handle for the device and turn it off.
447 	 * The String object here seems to contain a fully-qualified path, so we
448 	 * don't have to search for it in our parents.
449 	 *
450 	 * XXX This may not always be the case.
451 	 */
452 	if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK)
453 	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
454 	break;
455 
456     default:
457 	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
458 				    obj->Type));
459 	break;
460     }
461 	return_VOID;
462 }
463 
464 /*
465  * Given an object, verify that it's a reference to a device of some sort,
466  * and try to switch it on.
467  *
468  * XXX replication of off/on function code is bad, mmmkay?
469  */
470 static void
471 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
472 {
473     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
474     ACPI_HANDLE			cooler;
475 
476     FUNCTION_TRACE(__func__);
477 
478     ACPI_ASSERTLOCK;
479 
480     switch(obj->Type) {
481     case ACPI_TYPE_STRING:
482 	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
483 
484 	/*
485 	 * Find the handle for the device and turn it off.
486 	 * The String object here seems to contain a fully-qualified path, so we
487 	 * don't have to search for it in our parents.
488 	 *
489 	 * XXX This may not always be the case.
490 	 */
491 	if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK)
492 	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
493 	break;
494 
495     default:
496 	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
497 				    obj->Type));
498 	break;
499     }
500 	return_VOID;
501 }
502 
503 /*
504  * Read/debug-print a parameter, default it to -1.
505  */
506 static void
507 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
508 {
509 
510     FUNCTION_TRACE(__func__);
511 
512     ACPI_ASSERTLOCK;
513 
514     if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
515 	*data = -1;
516     } else {
517 	DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle),
518 				   node, *data));
519     }
520     return_VOID;
521 }
522 
523 /*
524  * Sanity-check a temperature value.  Assume that setpoints
525  * should be between 0C and 150C.
526  */
527 static void
528 acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
529 {
530     if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
531 	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
532 		      what, TZ_KELVTOC(*val));
533 	*val = -1;
534     }
535 }
536 
537 /*
538  * Respond to a Notify event sent to the zone.
539  */
540 static void
541 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
542 {
543     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
544 
545     FUNCTION_TRACE(__func__);
546 
547     ACPI_ASSERTLOCK;
548 
549     switch(notify) {
550     case TZ_NOTIFY_TEMPERATURE:
551 	/* temperature change occurred */
552 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
553 	break;
554     case TZ_NOTIFY_DEVICES:
555     case TZ_NOTIFY_LEVELS:
556 	/* zone devices/setpoints changed */
557 	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
558 	break;
559     default:
560 	device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
561 	break;
562     }
563     return_VOID;
564 }
565 
566 /*
567  * Poll the thermal zone.
568  */
569 static void
570 acpi_tz_timeout(void *arg)
571 {
572     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
573 
574     ACPI_LOCK;
575 
576     /* check temperature, take action */
577     AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
578 
579     /* XXX passive cooling actions? */
580 
581     /* re-register ourself */
582     sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
583 
584     ACPI_UNLOCK;
585 }
586