Deleted Added
full compact
acpi_thermal.c (78662) acpi_thermal.c (78915)
1/*-
1/*-
2 * Copyright (c) 2000 Michael Smith
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.

--- 8 unchanged lines hidden (view full) ---

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 *
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.

--- 8 unchanged lines hidden (view full) ---

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: head/sys/dev/acpica/acpi_thermal.c 78662 2001-06-23 10:38:25Z iwasaki $
27 * $FreeBSD: head/sys/dev/acpica/acpi_thermal.c 78915 2001-06-28 06:17:16Z msmith $
28 */
29
30#include "opt_acpi.h"
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/bus.h>
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>
34
35#include "acpi.h"
36
37#include <dev/acpica/acpivar.h>
38
39/*
40 * Hooks for the ACPI CA debugging infrastructure
41 */
42#define _COMPONENT ACPI_THERMAL_ZONE
43MODULE_NAME("THERMAL")
44
45#define TZ_ZEROC 2732
46#define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
47
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_ZONE
44MODULE_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 */
56struct 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
48struct acpi_tz_softc {
70struct acpi_tz_softc {
49 device_t tz_dev;
50 ACPI_HANDLE tz_handle;
51 int tz_tmp;
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;
52};
53
54static int acpi_tz_probe(device_t dev);
55static int acpi_tz_attach(device_t dev);
82};
83
84static int acpi_tz_probe(device_t dev);
85static int acpi_tz_attach(device_t dev);
56static void acpi_tz_check_tripping_point(void *context);
86static int acpi_tz_establish(struct acpi_tz_softc *sc);
87static void acpi_tz_all_off(struct acpi_tz_softc *sc);
88static void acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
89static void acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
90static void acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
57static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
91static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
92static void acpi_tz_timeout(void *arg);
58
59static device_method_t acpi_tz_methods[] = {
60 /* Device interface */
61 DEVMETHOD(device_probe, acpi_tz_probe),
62 DEVMETHOD(device_attach, acpi_tz_attach),
63
64 {0, 0}
65};
66
67static driver_t acpi_tz_driver = {
68 "acpi_tz",
69 acpi_tz_methods,
70 sizeof(struct acpi_tz_softc),
71};
72
73devclass_t acpi_tz_devclass;
74DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
75
93
94static device_method_t acpi_tz_methods[] = {
95 /* Device interface */
96 DEVMETHOD(device_probe, acpi_tz_probe),
97 DEVMETHOD(device_attach, acpi_tz_attach),
98
99 {0, 0}
100};
101
102static driver_t acpi_tz_driver = {
103 "acpi_tz",
104 acpi_tz_methods,
105 sizeof(struct acpi_tz_softc),
106};
107
108devclass_t acpi_tz_devclass;
109DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
110
111/*
112 * Match an ACPI thermal zone.
113 */
76static int
77acpi_tz_probe(device_t dev)
78{
79
114static int
115acpi_tz_probe(device_t dev)
116{
117
80 FUNCTION_TRACE(__func__);
118 /* no FUNCTION_TRACE - too noisy */
81
82 if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
83 !acpi_disabled("thermal")) {
84 device_set_desc(dev, "thermal zone");
119
120 if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
121 !acpi_disabled("thermal")) {
122 device_set_desc(dev, "thermal zone");
85 return_VALUE(0);
123 return(-10);
86 }
124 }
87 return_VALUE(ENXIO);
125 return(ENXIO);
88}
89
126}
127
128/*
129 * Attach to an ACPI thermal zone.
130 */
90static int
91acpi_tz_attach(device_t dev)
92{
93 struct acpi_tz_softc *sc;
131static int
132acpi_tz_attach(device_t dev)
133{
134 struct acpi_tz_softc *sc;
94 struct acpi_softc *acpi_sc;
135 int error;
95
96 FUNCTION_TRACE(__func__);
97
98 sc = device_get_softc(dev);
99 sc->tz_dev = dev;
100 sc->tz_handle = acpi_get_handle(dev);
101
136
137 FUNCTION_TRACE(__func__);
138
139 sc = device_get_softc(dev);
140 sc->tz_dev = dev;
141 sc->tz_handle = acpi_get_handle(dev);
142
143 /*
144 * Parse the current state of the thermal zone and build control
145 * structures.
146 */
147 if ((error = acpi_tz_establish(sc)) != 0)
148 return_VALUE(error);
149
150 /*
151 * Register for any Notify events sent to this zone.
152 */
102 AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
103 acpi_tz_notify_handler, dev);
104
153 AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
154 acpi_tz_notify_handler, dev);
155
105 if (device_get_unit(dev) == 0) {
106 acpi_sc = acpi_device_get_parent_softc(dev);
107 SYSCTL_ADD_UINT(&acpi_sc->acpi_sysctl_ctx,
108 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
109 OID_AUTO, "temperature", CTLFLAG_RD,
110 &sc->tz_tmp, 0, "");
111 }
112
113 /*
114 * Don't bother evaluating/printing the temperature at this point;
115 * on many systems it'll be bogus until the EC is running.
116 */
117 return_VALUE(0);
118}
119
156 /*
157 * Don't bother evaluating/printing the temperature at this point;
158 * on many systems it'll be bogus until the EC is running.
159 */
160 return_VALUE(0);
161}
162
163/*
164 * Parse the current state of this thermal zone and set up to use it.
165 *
166 * Note that we may have previous state, which will have to be discarded.
167 */
168static int
169acpi_tz_establish(struct acpi_tz_softc *sc)
170{
171 ACPI_OBJECT *obj;
172 int i;
173 char nbuf[8];
174
175 FUNCTION_TRACE(__func__);
176
177 /*
178 * Power everything off and erase any existing state.
179 */
180 acpi_tz_all_off(sc);
181 for (i = 0; i < TZ_NUMLEVELS; i++)
182 if (sc->tz_state.al[i].Pointer != NULL)
183 AcpiOsFree(sc->tz_state.al[i].Pointer);
184 if (sc->tz_state.psl.Pointer != NULL)
185 AcpiOsFree(sc->tz_state.psl.Pointer);
186 bzero(&sc->tz_state, sizeof(sc->tz_state));
187
188 /* kill the timeout (harmless if not running */
189 untimeout(acpi_tz_timeout, sc, sc->tz_timeout);
190
191 /*
192 * Evaluate thermal zone parameters.
193 */
194 for (i = 0; i < TZ_NUMLEVELS; i++) {
195 sprintf(nbuf, "_AC%d", i);
196 acpi_tz_getparam(sc, nbuf, &sc->tz_state.ac[i]);
197 sprintf(nbuf, "_AL%d", i);
198 acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_state.al[i]);
199 obj = (ACPI_OBJECT *)sc->tz_state.al[i].Pointer;
200 if (obj != NULL) {
201 /* should be a package containing a list of power objects */
202 if (obj->Type != ACPI_TYPE_PACKAGE) {
203 device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
204 nbuf, obj->Type);
205 return_VALUE(ENXIO);
206 }
207 }
208 }
209 acpi_tz_getparam(sc, "_CRT", &sc->tz_state.crt);
210 acpi_tz_getparam(sc, "_HOT", &sc->tz_state.hot);
211 acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_state.psl);
212 acpi_tz_getparam(sc, "_PSV", &sc->tz_state.psv);
213 acpi_tz_getparam(sc, "_TC1", &sc->tz_state.tc1);
214 acpi_tz_getparam(sc, "_TC2", &sc->tz_state.tc2);
215 acpi_tz_getparam(sc, "_TSP", &sc->tz_state.tsp);
216 acpi_tz_getparam(sc, "_TZP", &sc->tz_state.tzp);
217
218 /*
219 * Power off everything that we've just been given.
220 */
221 acpi_tz_all_off(sc);
222
223 /*
224 * Do we need to poll the thermal zone? Ignore the suggested
225 * rate.
226 */
227 if (sc->tz_state.tzp != 0)
228 sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
229
230
231 return_VALUE(0);
232}
233
234/*
235 * Evaluate the condition of a thermal zone, take appropriate actions.
236 */
120static void
237static void
121acpi_tz_check_tripping_point(void *context)
238acpi_tz_monitor(struct acpi_tz_softc *sc)
122{
239{
123 struct acpi_tz_softc *sc;
124 device_t dev = context;
125 ACPI_STATUS status;
126 int tp;
240 int temp, new;
241 int i;
127
128 FUNCTION_TRACE(__func__);
129
242
243 FUNCTION_TRACE(__func__);
244
130 sc = device_get_softc(dev);
131 if ((status = acpi_EvaluateInteger(sc->tz_handle, "_TMP", &tp)) != AE_OK) {
132 device_printf(dev, "can't evaluate _TMP method - %s\n", acpi_strerror(status));
245 /*
246 * Get the current temperature.
247 */
248 if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) {
249 device_printf(sc->tz_dev, "error fetching current temperature\n");
250 /* XXX disable zone? go to max cooling? */
133 return_VOID;
134 }
251 return_VOID;
252 }
253
254 /*
255 * Work out what we ought to be doing right now.
256 */
257 new = TZ_STATE_NONE;
258 if ((sc->tz_state.psv != -1) && (temp > sc->tz_state.psv))
259 new = TZ_STATE_PSV;
260 for (i = 0; i < TZ_NUMLEVELS; i++)
261 if ((sc->tz_state.ac[i] != -1) && (temp > sc->tz_state.ac[i]))
262 new = TZ_STATE_AC0 + i;
263 if ((sc->tz_state.hot != -1) && (temp > sc->tz_state.hot))
264 new = TZ_STATE_HOT;
265 if ((sc->tz_state.crt != -1) && (temp > sc->tz_state.crt))
266 new = TZ_STATE_CRT;
267
268 /*
269 * If our state has not changed, do nothing.
270 */
271 if (new == sc->tz_current)
272 return_VOID;
135
273
136 sc->tz_tmp = (tp - TZ_ZEROC) / 10;
137 if (bootverbose) {
138 device_printf(dev, "%dC\n", sc->tz_tmp);
274 /*
275 * XXX if we're in a passive-cooling mode, revert to full-speed operation.
276 */
277 if (sc->tz_current == TZ_STATE_PSV) {
278 /* XXX implement */
139 }
279 }
280
281 /*
282 * If we're in an active-cooling mode, turn off the current cooler(s).
283 */
284 if ((sc->tz_current >= TZ_STATE_AC0) && (sc->tz_current < (TZ_STATE_AC0 + TZ_NUMLEVELS)))
285 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[sc->tz_current - TZ_STATE_AC0].Pointer,
286 acpi_tz_switch_cooler_off, sc);
287
288 /*
289 * XXX If the new mode is passive-cooling, make appropriate adjustments.
290 */
291
292 /*
293 * If the new mode is an active-cooling mode, turn on the new cooler(s).
294 */
295 if ((new >= TZ_STATE_AC0) && (new < (TZ_STATE_AC0 + TZ_NUMLEVELS)))
296 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[new - TZ_STATE_AC0].Pointer,
297 acpi_tz_switch_cooler_on, sc);
298
299 /*
300 * If we're _HOT or _CRT, shut down now!
301 */
302 if ((new == TZ_STATE_HOT) || (new == TZ_STATE_CRT)) {
303 device_printf(sc->tz_dev, "WARNING - emergency thermal shutdown in progress.\n");
304 shutdown_nice(RB_POWEROFF);
305 }
306
307 /* gone to new state */
308 sc->tz_current = new;
309
140 return_VOID;
141}
142
310 return_VOID;
311}
312
143#define ACPI_TZ_STATUS_CHANGE 0x80
144#define ACPI_TZ_TRIPPOINT_CHANGE 0x81
313/*
314 * Turn off all the cooling devices.
315 */
145static void
316static void
317acpi_tz_all_off(struct acpi_tz_softc *sc)
318{
319 int i;
320
321 FUNCTION_TRACE(__func__);
322
323 /*
324 * Scan all the _AL objects, and turn them all off.
325 */
326 for (i = 0; i < TZ_NUMLEVELS; i++) {
327 if (sc->tz_state.al[i].Pointer == NULL)
328 continue;
329 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[i].Pointer,
330 acpi_tz_switch_cooler_off, sc);
331 }
332
333 /*
334 * XXX revert any passive-cooling options.
335 */
336
337 sc->tz_current = TZ_STATE_NONE;
338 return_VOID;
339}
340
341/*
342 * Given an object, verify that it's a reference to a device of some sort,
343 * and try to switch it off.
344 */
345static void
346acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
347{
348 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
349 ACPI_HANDLE cooler;
350
351 FUNCTION_TRACE(__func__);
352
353 switch(obj->Type) {
354 case ACPI_TYPE_STRING:
355 DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
356
357 /*
358 * Find the handle for the device and turn it off.
359 * The String object here seems to contain a fully-qualified path, so we
360 * don't have to search for it in our parents.
361 *
362 * XXX This may not always be the case.
363 */
364 if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK)
365 acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
366 break;
367
368 default:
369 DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
370 obj->Type));
371 break;
372 }
373 return_VOID;
374}
375
376/*
377 * Given an object, verify that it's a reference to a device of some sort,
378 * and try to switch it on.
379 *
380 * XXX replication of off/on function code is bad, mmmkay?
381 */
382static void
383acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
384{
385 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
386 ACPI_HANDLE cooler, parent;
387
388 FUNCTION_TRACE(__func__);
389
390 switch(obj->Type) {
391 case ACPI_TYPE_STRING:
392 DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
393
394 /* find the handle for the device and turn it off */
395 if (acpi_GetHandleInScope(sc->tz_handle, obj->String.Pointer, &cooler) == AE_OK)
396 acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
397 break;
398
399 default:
400 DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
401 obj->Type));
402 break;
403 }
404 return_VOID;
405}
406
407/*
408 * Read/debug-print a parameter, default it to -1.
409 */
410static void
411acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
412{
413
414 FUNCTION_TRACE(__func__);
415
416 if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
417 *data = -1;
418 } else {
419 DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle),
420 node, *data));
421 }
422 return_VOID;
423}
424
425/*
426 * Respond to a Notify event sent to the zone.
427 */
428static void
146acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
147{
429acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
430{
431 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)context;
432
148 FUNCTION_TRACE(__func__);
149
433 FUNCTION_TRACE(__func__);
434
150 switch(notify){
151 case ACPI_TZ_STATUS_CHANGE:
152 case ACPI_TZ_TRIPPOINT_CHANGE:
153 /*Check trip point*/
154 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_tz_check_tripping_point, context);
155 break;
435 switch(notify) {
436 case TZ_NOTIFY_TEMPERATURE:
437 acpi_tz_monitor(sc); /* temperature change occurred */
438 break;
439 case TZ_NOTIFY_DEVICES:
440 case TZ_NOTIFY_LEVELS:
441 acpi_tz_establish(sc); /* zone devices/setpoints changed */
442 break;
443 default:
444 device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
445 break;
156 }
157 return_VOID;
158}
159
446 }
447 return_VOID;
448}
449
450/*
451 * Poll the thermal zone.
452 */
453static void
454acpi_tz_timeout(void *arg)
455{
456 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
457
458 /* check temperature, take action */
459 acpi_tz_monitor(sc);
460
461 /* XXX passive cooling actions? */
462
463 /* re-register ourself */
464 sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
465}