1 /* $OpenBSD: acpitz.c,v 1.60 2024/05/13 01:15:50 jsg 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 const 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_refresh(void *);
82 int acpitz_notify(struct aml_node *, int, void *);
83 int acpitz_gettempreading(struct acpitz_softc *, char *);
84 int acpitz_getreading(struct acpitz_softc *, char *);
85 int acpitz_setfan(struct acpitz_softc *, int, char *);
86 void acpitz_init(struct acpitz_softc *, int);
87
88 void (*acpitz_cpu_setperf)(int);
89 int acpitz_perflevel = -1;
90 extern void (*cpu_setperf)(int);
91 extern int perflevel;
92 #define PERFSTEP 10
93
94 #define ACPITZ_TRIPS (1L << 0)
95 #define ACPITZ_DEVLIST (1L << 1)
96 #define ACPITZ_INIT (ACPITZ_TRIPS|ACPITZ_DEVLIST)
97
98 void
acpitz_init_perf(void * arg)99 acpitz_init_perf(void *arg)
100 {
101 if (acpitz_perflevel == -1)
102 acpitz_perflevel = perflevel;
103
104 if (cpu_setperf != acpitz_setperf) {
105 acpitz_cpu_setperf = cpu_setperf;
106 cpu_setperf = acpitz_setperf;
107 }
108 }
109
110 void
acpitz_setperf(int level)111 acpitz_setperf(int level)
112 {
113 extern struct acpi_softc *acpi_softc;
114
115 if (level < 0 || level > 100)
116 return;
117
118 if (acpi_softc == NULL)
119 return;
120 if (acpi_softc->sc_pse && level > acpitz_perflevel)
121 return;
122
123 if (acpitz_cpu_setperf)
124 acpitz_cpu_setperf(level);
125 }
126
127 void
acpitz_init(struct acpitz_softc * sc,int flag)128 acpitz_init(struct acpitz_softc *sc, int flag)
129 {
130 int i;
131 char name[5];
132 struct aml_value res;
133
134 /* Read trip points */
135 if (flag & ACPITZ_TRIPS) {
136 sc->sc_psv = acpitz_getreading(sc, "_PSV");
137 for (i = 0; i < ACPITZ_MAX_AC; i++) {
138 snprintf(name, sizeof(name), "_AC%d", i);
139 sc->sc_ac[i] = acpitz_getreading(sc, name);
140 }
141 }
142
143 /* Read device lists */
144 if (flag & ACPITZ_DEVLIST) {
145 if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL",
146 0, NULL, &res)) {
147 acpi_freedevlist(&sc->sc_psl);
148 acpi_getdevlist(&sc->sc_psl, sc->sc_devnode, &res, 0);
149 aml_freevalue(&res);
150 }
151 for (i = 0; i < ACPITZ_MAX_AC; i++) {
152 snprintf(name, sizeof(name), "_AL%d", i);
153 if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
154 0, NULL, &res)) {
155 acpi_freedevlist(&sc->sc_alx[i]);
156 acpi_getdevlist(&sc->sc_alx[i],
157 sc->sc_devnode, &res, 0);
158 aml_freevalue(&res);
159 }
160 /* initialize current state to unknown */
161 sc->sc_ac_stat[i] = ACPITZ_UNKNOWN;
162 }
163 }
164 }
165
166 int
acpitz_match(struct device * parent,void * match,void * aux)167 acpitz_match(struct device *parent, void *match, void *aux)
168 {
169 struct acpi_attach_args *aa = aux;
170 struct cfdata *cf = match;
171
172 /* sanity */
173 if (aa->aaa_name == NULL ||
174 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
175 aa->aaa_table != NULL)
176 return (0);
177
178 if (aa->aaa_node->value->type != AML_OBJTYPE_THERMZONE)
179 return (0);
180
181 return (1);
182 }
183
184 void
acpitz_attach(struct device * parent,struct device * self,void * aux)185 acpitz_attach(struct device *parent, struct device *self, void *aux)
186 {
187 struct acpitz_softc *sc = (struct acpitz_softc *)self;
188 struct acpi_attach_args *aa = aux;
189 int i;
190 char name[5];
191
192 sc->sc_acpi = (struct acpi_softc *)parent;
193 sc->sc_devnode = aa->aaa_node;
194
195 TAILQ_INIT(&sc->sc_psl);
196 for (i = 0; i < ACPITZ_MAX_AC; i++)
197 TAILQ_INIT(&sc->sc_alx[i]);
198
199 /*
200 * Preread the trip points (discard/ignore values read here as we will
201 * re-read them later)
202 */
203 acpitz_gettempreading(sc, "_CRT");
204 acpitz_gettempreading(sc, "_HOT");
205 acpitz_gettempreading(sc, "_PSV");
206 for (i = 0; i < ACPITZ_MAX_AC; i++) {
207 snprintf(name, sizeof(name), "_AC%d", i);
208 acpitz_getreading(sc, name);
209 }
210 acpitz_gettempreading(sc, "_TMP");
211
212 sc->sc_lasttmp = -1;
213 if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) {
214 dnprintf(10, ": failed to read _TMP");
215 printf("\n");
216 return;
217 }
218
219 if ((sc->sc_crt = acpitz_gettempreading(sc, "_CRT")) == -1)
220 printf(": no critical temperature defined\n");
221 else
222 printf(": critical temperature is %d degC\n", KTOC(sc->sc_crt));
223
224 sc->sc_hot = acpitz_gettempreading(sc, "_HOT");
225 sc->sc_tc1 = acpitz_getreading(sc, "_TC1");
226 sc->sc_tc2 = acpitz_getreading(sc, "_TC2");
227
228 /* get _PSL, _ALx */
229 acpitz_init(sc, ACPITZ_INIT);
230
231 dnprintf(10, "%s: _HOT: %d _TC1: %d _TC2: %d _PSV: %d _TMP: %d "
232 "_CRT: %d\n", DEVNAME(sc), sc->sc_hot, sc->sc_tc1, sc->sc_tc2,
233 sc->sc_psv, sc->sc_tmp, sc->sc_crt);
234
235 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
236 sizeof(sc->sc_sensdev.xname));
237 strlcpy(sc->sc_sens.desc, "zone temperature",
238 sizeof(sc->sc_sens.desc));
239 sc->sc_sens.type = SENSOR_TEMP;
240 sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
241 sensordev_install(&sc->sc_sensdev);
242
243 aml_register_notify(sc->sc_devnode, NULL,
244 acpitz_notify, sc, ACPIDEV_POLL);
245
246 /*
247 * XXX use kthread_create_deferred to ensure we are the very last
248 * piece of code that touches this pointer after all CPUs have been
249 * fully attached
250 */
251 kthread_create_deferred(acpitz_init_perf, sc);
252 }
253
254 int
acpitz_activate(struct device * self,int act)255 acpitz_activate(struct device *self, int act)
256 {
257 struct acpitz_softc *sc = (struct acpitz_softc *)self;
258
259 switch (act) {
260 case DVACT_WAKEUP:
261 acpitz_init(sc, ACPITZ_INIT);
262 break;
263 }
264 return 0;
265 }
266
267 int
acpitz_setfan(struct acpitz_softc * sc,int i,char * method)268 acpitz_setfan(struct acpitz_softc *sc, int i, char *method)
269 {
270 struct aml_node *node;
271 struct aml_value res1, *ref;
272 char name[8];
273 int rv = 1, x, y;
274 int64_t sta;
275 struct acpi_devlist *dl;
276
277 dnprintf(20, "%s: acpitz_setfan(%d, %s)\n", DEVNAME(sc), i, method);
278
279 x = 0;
280 snprintf(name, sizeof(name), "_AL%d", i);
281 TAILQ_FOREACH(dl, &sc->sc_alx[i], dev_link) {
282 if (aml_evalname(sc->sc_acpi, dl->dev_node, "_PR0",0 , NULL,
283 &res1)) {
284 printf("%s: %s[%d] _PR0 failed\n", DEVNAME(sc),
285 name, x);
286 aml_freevalue(&res1);
287 x++;
288
289 /*
290 * This fan lacks the right method to operate:
291 * disabling active cooling trip points.
292 */
293 sc->sc_ac[i] = -1;
294 continue;
295 }
296 if (res1.type != AML_OBJTYPE_PACKAGE) {
297 printf("%s: %s[%d] _PR0 not a package\n", DEVNAME(sc),
298 name, x);
299 aml_freevalue(&res1);
300 x++;
301 continue;
302 }
303 for (y = 0; y < res1.length; y++) {
304 ref = res1.v_package[y];
305 if (ref->type == AML_OBJTYPE_NAMEREF) {
306 node = aml_searchrel(sc->sc_devnode,
307 aml_getname(ref->v_nameref));
308 if (node == NULL) {
309 printf("%s: %s[%d.%d] _PR0"
310 " not a valid device\n",
311 DEVNAME(sc), name, x, y);
312 continue;
313 }
314 ref = node->value;
315 }
316 if (ref->type == AML_OBJTYPE_OBJREF) {
317 ref = ref->v_objref.ref;
318 }
319 if (ref->type != AML_OBJTYPE_DEVICE &&
320 ref->type != AML_OBJTYPE_POWERRSRC) {
321 printf("%s: %s[%d.%d] _PR0 not a package\n",
322 DEVNAME(sc), name, x, y);
323 continue;
324 }
325 if (aml_evalname(sc->sc_acpi, ref->node, method, 0,
326 NULL, NULL))
327 printf("%s: %s[%d.%d] %s fails\n",
328 DEVNAME(sc), name, x, y, method);
329
330 /* save off status of fan */
331 if (aml_evalinteger(sc->sc_acpi, ref->node, "_STA", 0,
332 NULL, &sta))
333 printf("%s: %s[%d.%d] _STA fails\n",
334 DEVNAME(sc), name, x, y);
335 else {
336 sc->sc_ac_stat[i] = sta;
337 }
338 }
339 aml_freevalue(&res1);
340 x++;
341 }
342 rv = 0;
343 return (rv);
344 }
345
346 void
acpitz_refresh(void * arg)347 acpitz_refresh(void *arg)
348 {
349 struct acpitz_softc *sc = arg;
350 int i, trend, nperf;
351
352 dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc),
353 sc->sc_devnode->name);
354
355 /* get _TMP and debounce the value */
356 if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) {
357 printf("%s: %s: failed to read temp\n", DEVNAME(sc),
358 sc->sc_devnode->name);
359 return;
360 }
361 /* critical trip points */
362 if (sc->sc_crt != -1 && sc->sc_crt <= sc->sc_tmp) {
363 /* do critical shutdown */
364 printf("%s: critical temperature exceeded %dC, shutting "
365 "down\n", DEVNAME(sc), KTOC(sc->sc_tmp));
366 prsignal(initprocess, SIGUSR2);
367 }
368 if (sc->sc_hot != -1 && sc->sc_hot <= sc->sc_tmp) {
369 printf("%s: _HOT temperature\n", DEVNAME(sc));
370 /* XXX go to S4, until then cool as hard as we can */
371 }
372
373 /* passive cooling */
374 if (sc->sc_lasttmp != -1 && sc->sc_tc1 != -1 && sc->sc_tc2 != -1 &&
375 sc->sc_psv != -1) {
376 dnprintf(30, "%s: passive cooling: lasttmp: %d tc1: %d "
377 "tc2: %d psv: %d\n", DEVNAME(sc), sc->sc_lasttmp,
378 sc->sc_tc1, sc->sc_tc2, sc->sc_psv);
379
380 nperf = acpitz_perflevel;
381 if (sc->sc_psv <= sc->sc_tmp) {
382 /* Passive cooling enabled */
383 dnprintf(1, "%s: enabling passive %d %d\n",
384 DEVNAME(sc), sc->sc_tmp, sc->sc_psv);
385 if (!sc->sc_pse)
386 sc->sc_acpi->sc_pse++;
387 sc->sc_pse = 1;
388
389 trend = sc->sc_tc1 * (sc->sc_tmp - sc->sc_lasttmp) +
390 sc->sc_tc2 * (sc->sc_tmp - sc->sc_psv);
391
392 /* Depending on trend, slow down/speed up */
393 if (trend > 0)
394 nperf -= PERFSTEP;
395 else
396 nperf += PERFSTEP;
397 }
398 else {
399 /* Passive cooling disabled, increase % */
400 dnprintf(1, "%s: disabling passive %d %d\n",
401 DEVNAME(sc), sc->sc_tmp, sc->sc_psv);
402 if (sc->sc_pse)
403 sc->sc_acpi->sc_pse--;
404 sc->sc_pse = 0;
405 nperf += PERFSTEP;
406 }
407 if (nperf < 0)
408 nperf = 0;
409 else if (nperf > 100)
410 nperf = 100;
411
412 /* clamp passive cooling request */
413 if (nperf > perflevel)
414 nperf = perflevel;
415
416 /* Perform CPU setperf */
417 if (acpitz_cpu_setperf && nperf != acpitz_perflevel) {
418 acpitz_perflevel = nperf;
419 acpitz_cpu_setperf(nperf);
420 }
421 }
422 sc->sc_lasttmp = sc->sc_tmp;
423
424 /* active cooling */
425 for (i = 0; i < ACPITZ_MAX_AC; i++) {
426 if (sc->sc_ac[i] != -1 && sc->sc_ac[i] <= sc->sc_tmp) {
427 /* turn on fan i */
428 if (sc->sc_ac_stat[i] <= 0)
429 acpitz_setfan(sc, i, "_ON_");
430 } else if (sc->sc_ac[i] != -1) {
431 /* turn off fan i */
432 if ((sc->sc_ac_stat[i] == ACPITZ_UNKNOWN) ||
433 (sc->sc_ac_stat[i] > 0))
434 acpitz_setfan(sc, i, "_OFF");
435 }
436 }
437 sc->sc_sens.value = sc->sc_tmp * 100000 - 50000;
438 }
439
440 int
acpitz_getreading(struct acpitz_softc * sc,char * name)441 acpitz_getreading(struct acpitz_softc *sc, char *name)
442 {
443 uint64_t val;
444
445 if (!aml_evalinteger(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &val))
446 return (val);
447
448 return (-1);
449 }
450
451 int
acpitz_gettempreading(struct acpitz_softc * sc,char * name)452 acpitz_gettempreading(struct acpitz_softc *sc, char *name)
453 {
454 int rv = -1, tmp = -1, i;
455
456 for (i = 0; i < ACPITZ_TMP_RETRY; i++) {
457 tmp = acpitz_getreading(sc, name);
458 if (tmp == -1)
459 goto out;
460 if (KTOC(tmp) >= 0) {
461 rv = tmp;
462 break;
463 } else {
464 dnprintf(20, "%s: %d invalid reading on %s, "
465 "debouncing\n", DEVNAME(sc), tmp, name);
466 }
467
468 acpi_sleep(1000, "acpitz"); /* debounce: 1000 msec */
469 }
470 if (i >= ACPITZ_TMP_RETRY) {
471 printf("%s: %s: failed to read %s\n", DEVNAME(sc),
472 sc->sc_devnode->name, name);
473 goto out;
474 }
475 out:
476 dnprintf(30, "%s: name: %s tmp: %d => %dC, rv: %d\n", DEVNAME(sc),
477 name, tmp, KTOC(tmp), rv);
478 return (rv);
479 }
480
481 int
acpitz_notify(struct aml_node * node,int notify_type,void * arg)482 acpitz_notify(struct aml_node *node, int notify_type, void *arg)
483 {
484 struct acpitz_softc *sc = arg;
485
486 dnprintf(10, "%s notify: %.2x %s\n", DEVNAME(sc), notify_type,
487 sc->sc_devnode->name);
488
489 switch (notify_type) {
490 case 0x80: /* hardware notifications */
491 break;
492 case 0x81: /* operating Points changed */
493 acpitz_init(sc, ACPITZ_TRIPS);
494 break;
495 case 0x82: /* re-evaluate thermal device list */
496 acpitz_init(sc, ACPITZ_DEVLIST);
497 break;
498 default:
499 break;
500 }
501
502 acpitz_refresh(sc);
503 return (0);
504 }
505