Line data Source code
1 : /* $OpenBSD: acpitz.c,v 1.54 2018/06/29 17:39:18 kettenis Exp $ */
2 : /*
3 : * Copyright (c) 2006 Can Erkin Acar <canacar@openbsd.org>
4 : * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 : #include <sys/proc.h>
21 : #include <sys/signalvar.h>
22 : #include <sys/systm.h>
23 : #include <sys/device.h>
24 : #include <sys/malloc.h>
25 : #include <sys/kernel.h>
26 : #include <sys/kthread.h>
27 :
28 : #include <machine/bus.h>
29 :
30 : #include <dev/acpi/acpivar.h>
31 : #include <dev/acpi/acpidev.h>
32 : #include <dev/acpi/amltypes.h>
33 : #include <dev/acpi/dsdt.h>
34 :
35 : #include <sys/sensors.h>
36 :
37 : #define KTOC(k) ((k - 2732) / 10)
38 : #define ACPITZ_MAX_AC (10)
39 : #define ACPITZ_TMP_RETRY (3)
40 : #define ACPITZ_UNKNOWN (-1)
41 :
42 : struct acpitz_softc {
43 : struct device sc_dev;
44 :
45 : struct acpi_softc *sc_acpi;
46 : struct aml_node *sc_devnode;
47 :
48 : int sc_tmp;
49 : int sc_crt;
50 : int sc_hot;
51 : int sc_ac[ACPITZ_MAX_AC];
52 : int sc_ac_stat[ACPITZ_MAX_AC];
53 : int sc_pse;
54 : int sc_psv;
55 : int sc_tc1;
56 : int sc_tc2;
57 : int sc_lasttmp;
58 :
59 : struct ksensor sc_sens;
60 : struct ksensordev sc_sensdev;
61 :
62 : struct acpi_devlist_head sc_psl;
63 : struct acpi_devlist_head sc_alx[ACPITZ_MAX_AC];
64 : };
65 :
66 : int acpitz_match(struct device *, void *, void *);
67 : void acpitz_attach(struct device *, struct device *, void *);
68 : int acpitz_activate(struct device *, int);
69 :
70 : struct cfattach acpitz_ca = {
71 : sizeof(struct acpitz_softc), acpitz_match, acpitz_attach,
72 : NULL, acpitz_activate
73 : };
74 :
75 : struct cfdriver acpitz_cd = {
76 : NULL, "acpitz", DV_DULL
77 : };
78 :
79 : void acpitz_init_perf(void *);
80 : void acpitz_setperf(int);
81 : void acpitz_monitor(struct acpitz_softc *);
82 : void acpitz_refresh(void *);
83 : int acpitz_notify(struct aml_node *, int, void *);
84 : int acpitz_gettempreading(struct acpitz_softc *, char *);
85 : int acpitz_getreading(struct acpitz_softc *, char *);
86 : int acpitz_setfan(struct acpitz_softc *, int, char *);
87 : void acpitz_init(struct acpitz_softc *, int);
88 :
89 : void (*acpitz_cpu_setperf)(int);
90 : int acpitz_perflevel = -1;
91 : extern void (*cpu_setperf)(int);
92 : extern int perflevel;
93 : #define PERFSTEP 10
94 :
95 : #define ACPITZ_TRIPS (1L << 0)
96 : #define ACPITZ_DEVLIST (1L << 1)
97 : #define ACPITZ_INIT (ACPITZ_TRIPS|ACPITZ_DEVLIST)
98 :
99 : extern struct aml_node aml_root;
100 :
101 : void
102 0 : acpitz_init_perf(void *arg)
103 : {
104 0 : if (acpitz_perflevel == -1)
105 0 : acpitz_perflevel = perflevel;
106 :
107 0 : if (cpu_setperf != acpitz_setperf) {
108 0 : acpitz_cpu_setperf = cpu_setperf;
109 0 : cpu_setperf = acpitz_setperf;
110 0 : }
111 0 : }
112 :
113 : void
114 0 : acpitz_setperf(int level)
115 : {
116 : extern struct acpi_softc *acpi_softc;
117 :
118 0 : if (level < 0 || level > 100)
119 : return;
120 :
121 0 : if (acpi_softc == NULL)
122 : return;
123 0 : if (acpi_softc->sc_pse && level > acpitz_perflevel)
124 : return;
125 :
126 0 : if (acpitz_cpu_setperf)
127 0 : acpitz_cpu_setperf(level);
128 0 : }
129 :
130 : void
131 0 : acpitz_init(struct acpitz_softc *sc, int flag)
132 : {
133 : int i;
134 0 : char name[5];
135 0 : struct aml_value res;
136 :
137 : /* Read trip points */
138 0 : if (flag & ACPITZ_TRIPS) {
139 0 : sc->sc_psv = acpitz_getreading(sc, "_PSV");
140 0 : for (i = 0; i < ACPITZ_MAX_AC; i++) {
141 0 : snprintf(name, sizeof(name), "_AC%d", i);
142 0 : sc->sc_ac[i] = acpitz_getreading(sc, name);
143 : }
144 : }
145 :
146 : /* Read device lists */
147 0 : if (flag & ACPITZ_DEVLIST) {
148 0 : if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL",
149 : 0, NULL, &res)) {
150 0 : acpi_freedevlist(&sc->sc_psl);
151 0 : acpi_getdevlist(&sc->sc_psl, sc->sc_devnode, &res, 0);
152 0 : aml_freevalue(&res);
153 0 : }
154 0 : for (i = 0; i < ACPITZ_MAX_AC; i++) {
155 0 : snprintf(name, sizeof(name), "_AL%d", i);
156 0 : if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
157 : 0, NULL, &res)) {
158 0 : acpi_freedevlist(&sc->sc_alx[i]);
159 0 : acpi_getdevlist(&sc->sc_alx[i],
160 0 : sc->sc_devnode, &res, 0);
161 0 : aml_freevalue(&res);
162 0 : }
163 : /* initialize current state to unknown */
164 0 : sc->sc_ac_stat[i] = ACPITZ_UNKNOWN;
165 : }
166 : }
167 0 : }
168 :
169 : int
170 0 : acpitz_match(struct device *parent, void *match, void *aux)
171 : {
172 0 : struct acpi_attach_args *aa = aux;
173 0 : struct cfdata *cf = match;
174 :
175 : /* sanity */
176 0 : if (aa->aaa_name == NULL ||
177 0 : strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
178 0 : aa->aaa_table != NULL)
179 0 : return (0);
180 :
181 0 : if (aa->aaa_node->value->type != AML_OBJTYPE_THERMZONE)
182 0 : return (0);
183 :
184 0 : return (1);
185 0 : }
186 :
187 : void
188 0 : acpitz_attach(struct device *parent, struct device *self, void *aux)
189 : {
190 0 : struct acpitz_softc *sc = (struct acpitz_softc *)self;
191 0 : struct acpi_attach_args *aa = aux;
192 : int i;
193 0 : char name[5];
194 :
195 0 : sc->sc_acpi = (struct acpi_softc *)parent;
196 0 : sc->sc_devnode = aa->aaa_node;
197 :
198 0 : TAILQ_INIT(&sc->sc_psl);
199 0 : for (i = 0; i < ACPITZ_MAX_AC; i++)
200 0 : TAILQ_INIT(&sc->sc_alx[i]);
201 :
202 : /*
203 : * Preread the trip points (discard/ignore values read here as we will
204 : * re-read them later)
205 : */
206 0 : acpitz_gettempreading(sc, "_CRT");
207 0 : acpitz_gettempreading(sc, "_HOT");
208 0 : acpitz_gettempreading(sc, "_PSV");
209 0 : for (i = 0; i < ACPITZ_MAX_AC; i++) {
210 0 : snprintf(name, sizeof(name), "_AC%d", i);
211 0 : acpitz_getreading(sc, name);
212 : }
213 0 : acpitz_gettempreading(sc, "_TMP");
214 :
215 0 : sc->sc_lasttmp = -1;
216 0 : if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) {
217 : dnprintf(10, ": failed to read _TMP\n");
218 0 : return;
219 : }
220 :
221 0 : if ((sc->sc_crt = acpitz_gettempreading(sc, "_CRT")) == -1)
222 0 : printf(": no critical temperature defined\n");
223 : else
224 0 : printf(": critical temperature is %d degC\n", KTOC(sc->sc_crt));
225 :
226 0 : sc->sc_hot = acpitz_gettempreading(sc, "_HOT");
227 0 : sc->sc_tc1 = acpitz_getreading(sc, "_TC1");
228 0 : sc->sc_tc2 = acpitz_getreading(sc, "_TC2");
229 :
230 : /* get _PSL, _ALx */
231 0 : acpitz_init(sc, ACPITZ_INIT);
232 :
233 : dnprintf(10, "%s: _HOT: %d _TC1: %d _TC2: %d _PSV: %d _TMP: %d "
234 : "_CRT: %d\n", DEVNAME(sc), sc->sc_hot, sc->sc_tc1, sc->sc_tc2,
235 : sc->sc_psv, sc->sc_tmp, sc->sc_crt);
236 :
237 0 : strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
238 : sizeof(sc->sc_sensdev.xname));
239 0 : strlcpy(sc->sc_sens.desc, "zone temperature",
240 : sizeof(sc->sc_sens.desc));
241 0 : sc->sc_sens.type = SENSOR_TEMP;
242 0 : sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
243 0 : sensordev_install(&sc->sc_sensdev);
244 :
245 0 : aml_register_notify(sc->sc_devnode, NULL,
246 0 : acpitz_notify, sc, ACPIDEV_POLL);
247 :
248 : /*
249 : * XXX use kthread_create_deferred to ensure we are the very last
250 : * piece of code that touches this pointer after all CPUs have been
251 : * fully attached
252 : */
253 0 : kthread_create_deferred(acpitz_init_perf, sc);
254 0 : }
255 :
256 : int
257 0 : acpitz_activate(struct device *self, int act)
258 : {
259 0 : struct acpitz_softc *sc = (struct acpitz_softc *)self;
260 :
261 0 : switch (act) {
262 : case DVACT_WAKEUP:
263 0 : acpitz_init(sc, ACPITZ_INIT);
264 0 : break;
265 : }
266 0 : return 0;
267 : }
268 :
269 : int
270 0 : acpitz_setfan(struct acpitz_softc *sc, int i, char *method)
271 : {
272 : struct aml_node *node;
273 0 : struct aml_value res1, *ref;
274 0 : char name[8];
275 : int rv = 1, x, y;
276 0 : int64_t sta;
277 : struct acpi_devlist *dl;
278 :
279 : dnprintf(20, "%s: acpitz_setfan(%d, %s)\n", DEVNAME(sc), i, method);
280 :
281 : x = 0;
282 0 : snprintf(name, sizeof(name), "_AL%d", i);
283 0 : TAILQ_FOREACH(dl, &sc->sc_alx[i], dev_link) {
284 0 : if (aml_evalname(sc->sc_acpi, dl->dev_node, "_PR0",0 , NULL,
285 : &res1)) {
286 0 : printf("%s: %s[%d] _PR0 failed\n", DEVNAME(sc),
287 : name, x);
288 0 : aml_freevalue(&res1);
289 0 : x++;
290 :
291 : /*
292 : * This fan lacks the right method to operate:
293 : * disabling active cooling trip points.
294 : */
295 0 : sc->sc_ac[i] = -1;
296 0 : continue;
297 : }
298 0 : if (res1.type != AML_OBJTYPE_PACKAGE) {
299 0 : printf("%s: %s[%d] _PR0 not a package\n", DEVNAME(sc),
300 : name, x);
301 0 : aml_freevalue(&res1);
302 0 : x++;
303 0 : continue;
304 : }
305 0 : for (y = 0; y < res1.length; y++) {
306 0 : ref = res1.v_package[y];
307 0 : if (ref->type == AML_OBJTYPE_STRING) {
308 0 : node = aml_searchrel(sc->sc_devnode,
309 0 : ref->v_string);
310 0 : if (node == NULL) {
311 0 : printf("%s: %s[%d.%d] _PR0"
312 : " not a valid device\n",
313 0 : DEVNAME(sc), name, x, y);
314 0 : continue;
315 : }
316 0 : ref = node->value;
317 0 : }
318 0 : if (ref->type == AML_OBJTYPE_OBJREF) {
319 0 : ref = ref->v_objref.ref;
320 0 : }
321 0 : if (ref->type != AML_OBJTYPE_DEVICE &&
322 0 : ref->type != AML_OBJTYPE_POWERRSRC) {
323 0 : printf("%s: %s[%d.%d] _PR0 not a package\n",
324 0 : DEVNAME(sc), name, x, y);
325 0 : continue;
326 : }
327 0 : if (aml_evalname(sc->sc_acpi, ref->node, method, 0,
328 : NULL, NULL))
329 0 : printf("%s: %s[%d.%d] %s fails\n",
330 0 : DEVNAME(sc), name, x, y, method);
331 :
332 : /* save off status of fan */
333 0 : if (aml_evalinteger(sc->sc_acpi, ref->node, "_STA", 0,
334 : NULL, &sta))
335 0 : printf("%s: %s[%d.%d] _STA fails\n",
336 0 : DEVNAME(sc), name, x, y);
337 : else {
338 0 : sc->sc_ac_stat[i] = sta;
339 : }
340 : }
341 0 : aml_freevalue(&res1);
342 0 : x++;
343 0 : }
344 : rv = 0;
345 0 : return (rv);
346 0 : }
347 :
348 : void
349 0 : acpitz_refresh(void *arg)
350 : {
351 0 : struct acpitz_softc *sc = arg;
352 : int i, trend, nperf;
353 :
354 : dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc),
355 : sc->sc_devnode->name);
356 :
357 : /* get _TMP and debounce the value */
358 0 : if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) {
359 0 : printf("%s: %s: failed to read temp\n", DEVNAME(sc),
360 0 : sc->sc_devnode->name);
361 0 : return;
362 : }
363 : /* critical trip points */
364 0 : if (sc->sc_crt != -1 && sc->sc_crt <= sc->sc_tmp) {
365 : /* do critical shutdown */
366 0 : printf("%s: critical temperature exceeded %dC, shutting "
367 0 : "down\n", DEVNAME(sc), KTOC(sc->sc_tmp));
368 0 : prsignal(initprocess, SIGUSR2);
369 0 : }
370 0 : if (sc->sc_hot != -1 && sc->sc_hot <= sc->sc_tmp) {
371 0 : printf("%s: _HOT temperature\n", DEVNAME(sc));
372 : /* XXX go to S4, until then cool as hard as we can */
373 0 : }
374 :
375 : /* passive cooling */
376 0 : if (sc->sc_lasttmp != -1 && sc->sc_tc1 != -1 && sc->sc_tc2 != -1 &&
377 0 : sc->sc_psv != -1) {
378 : dnprintf(30, "%s: passive cooling: lasttmp: %d tc1: %d "
379 : "tc2: %d psv: %d\n", DEVNAME(sc), sc->sc_lasttmp,
380 : sc->sc_tc1, sc->sc_tc2, sc->sc_psv);
381 :
382 0 : nperf = acpitz_perflevel;
383 0 : if (sc->sc_psv <= sc->sc_tmp) {
384 : /* Passive cooling enabled */
385 : dnprintf(1, "%s: enabling passive %d %d\n",
386 : DEVNAME(sc), sc->sc_tmp, sc->sc_psv);
387 0 : if (!sc->sc_pse)
388 0 : sc->sc_acpi->sc_pse++;
389 0 : sc->sc_pse = 1;
390 :
391 0 : trend = sc->sc_tc1 * (sc->sc_tmp - sc->sc_lasttmp) +
392 0 : sc->sc_tc2 * (sc->sc_tmp - sc->sc_psv);
393 :
394 : /* Depending on trend, slow down/speed up */
395 0 : if (trend > 0)
396 0 : nperf -= PERFSTEP;
397 : else
398 0 : nperf += PERFSTEP;
399 : }
400 : else {
401 : /* Passive cooling disabled, increase % */
402 : dnprintf(1, "%s: disabling passive %d %d\n",
403 : DEVNAME(sc), sc->sc_tmp, sc->sc_psv);
404 0 : if (sc->sc_pse)
405 0 : sc->sc_acpi->sc_pse--;
406 0 : sc->sc_pse = 0;
407 0 : nperf += PERFSTEP;
408 : }
409 0 : if (nperf < 0)
410 0 : nperf = 0;
411 0 : else if (nperf > 100)
412 0 : nperf = 100;
413 :
414 : /* clamp passive cooling request */
415 0 : if (nperf > perflevel)
416 0 : nperf = perflevel;
417 :
418 : /* Perform CPU setperf */
419 0 : if (acpitz_cpu_setperf && nperf != acpitz_perflevel) {
420 0 : acpitz_perflevel = nperf;
421 0 : acpitz_cpu_setperf(nperf);
422 0 : }
423 : }
424 0 : sc->sc_lasttmp = sc->sc_tmp;
425 :
426 : /* active cooling */
427 0 : for (i = 0; i < ACPITZ_MAX_AC; i++) {
428 0 : if (sc->sc_ac[i] != -1 && sc->sc_ac[i] <= sc->sc_tmp) {
429 : /* turn on fan i */
430 0 : if (sc->sc_ac_stat[i] <= 0)
431 0 : acpitz_setfan(sc, i, "_ON_");
432 0 : } else if (sc->sc_ac[i] != -1) {
433 : /* turn off fan i */
434 0 : if ((sc->sc_ac_stat[i] == ACPITZ_UNKNOWN) ||
435 0 : (sc->sc_ac_stat[i] > 0))
436 0 : acpitz_setfan(sc, i, "_OFF");
437 : }
438 : }
439 0 : sc->sc_sens.value = sc->sc_tmp * 100000 - 50000;
440 0 : }
441 :
442 : int
443 0 : acpitz_getreading(struct acpitz_softc *sc, char *name)
444 : {
445 0 : uint64_t val;
446 :
447 0 : if (!aml_evalinteger(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &val))
448 0 : return (val);
449 :
450 0 : return (-1);
451 0 : }
452 :
453 : int
454 0 : acpitz_gettempreading(struct acpitz_softc *sc, char *name)
455 : {
456 : int rv = -1, tmp = -1, i;
457 :
458 0 : for (i = 0; i < ACPITZ_TMP_RETRY; i++) {
459 0 : tmp = acpitz_getreading(sc, name);
460 0 : if (tmp == -1)
461 : goto out;
462 0 : if (KTOC(tmp) >= 0) {
463 : rv = tmp;
464 0 : break;
465 : } else {
466 : dnprintf(20, "%s: %d invalid reading on %s, "
467 : "debouncing\n", DEVNAME(sc), tmp, name);
468 : }
469 :
470 0 : acpi_sleep(1000, "acpitz"); /* debounce: 1000 msec */
471 : }
472 0 : if (i >= ACPITZ_TMP_RETRY) {
473 0 : printf("%s: %s: failed to read %s\n", DEVNAME(sc),
474 0 : sc->sc_devnode->name, name);
475 0 : goto out;
476 : }
477 : out:
478 : dnprintf(30, "%s: name: %s tmp: %d => %dC, rv: %d\n", DEVNAME(sc),
479 : name, tmp, KTOC(tmp), rv);
480 0 : return (rv);
481 : }
482 :
483 : int
484 0 : acpitz_notify(struct aml_node *node, int notify_type, void *arg)
485 : {
486 0 : struct acpitz_softc *sc = arg;
487 :
488 : dnprintf(10, "%s notify: %.2x %s\n", DEVNAME(sc), notify_type,
489 : sc->sc_devnode->name);
490 :
491 0 : switch (notify_type) {
492 : case 0x80: /* hardware notifications */
493 : break;
494 : case 0x81: /* operating Points changed */
495 0 : acpitz_init(sc, ACPITZ_TRIPS);
496 0 : break;
497 : case 0x82: /* re-evaluate thermal device list */
498 0 : acpitz_init(sc, ACPITZ_DEVLIST);
499 0 : break;
500 : default:
501 : break;
502 : }
503 :
504 0 : acpitz_refresh(sc);
505 0 : return (0);
506 : }
|