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