xref: /openbsd-src/sys/dev/acpi/acpitz.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /* $OpenBSD: acpitz.c,v 1.44 2012/10/31 16:35:36 deraadt 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/acpireg.h>
31 #include <dev/acpi/acpivar.h>
32 #include <dev/acpi/acpidev.h>
33 #include <dev/acpi/amltypes.h>
34 #include <dev/acpi/dsdt.h>
35 
36 #include <sys/sensors.h>
37 
38 #define KTOC(k)			((k - 2732) / 10)
39 #define ACPITZ_MAX_AC		(10)
40 #define ACPITZ_TMP_RETRY	(3)
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 
69 struct cfattach acpitz_ca = {
70 	sizeof(struct acpitz_softc), acpitz_match, acpitz_attach
71 };
72 
73 struct cfdriver acpitz_cd = {
74 	NULL, "acpitz", DV_DULL
75 };
76 
77 void	acpitz_init_perf(void *);
78 void	acpitz_setperf(int);
79 void	acpitz_monitor(struct acpitz_softc *);
80 void	acpitz_refresh(void *);
81 int	acpitz_notify(struct aml_node *, int, void *);
82 int	acpitz_gettempreading(struct acpitz_softc *, char *);
83 int	acpitz_getreading(struct acpitz_softc *, char *);
84 int	acpitz_setfan(struct acpitz_softc *, int, char *);
85 void	acpitz_init(struct acpitz_softc *, int);
86 
87 void		(*acpitz_cpu_setperf)(int);
88 int		acpitz_perflevel = -1;
89 extern void	(*cpu_setperf)(int);
90 extern int	perflevel;
91 #define PERFSTEP 10
92 
93 #define ACPITZ_TRIPS	(1L << 0)
94 #define ACPITZ_DEVLIST	(1L << 1)
95 #define ACPITZ_INIT	ACPITZ_TRIPS+ACPITZ_DEVLIST
96 
97 extern struct aml_node aml_root;
98 
99 void
100 acpitz_init_perf(void *arg)
101 {
102 	if (acpitz_perflevel == -1)
103 		acpitz_perflevel = perflevel;
104 
105 	if (cpu_setperf != acpitz_setperf) {
106 		acpitz_cpu_setperf = cpu_setperf;
107 		cpu_setperf = acpitz_setperf;
108 	}
109 }
110 
111 void
112 acpitz_setperf(int level)
113 {
114 	extern struct acpi_softc *acpi_softc;
115 
116 	if (level < 0 || level > 100)
117 		return;
118 
119 	if (acpi_softc == NULL)
120 		return;
121 	if (acpi_softc->sc_pse && level > acpitz_perflevel)
122 		return;
123 
124 	if (acpitz_cpu_setperf)
125 		acpitz_cpu_setperf(level);
126 }
127 
128 void
129 acpitz_init(struct acpitz_softc *sc, int flag)
130 {
131 	int i;
132 	char name[5];
133 	struct aml_value res;
134 
135 	/* Read trip points */
136 	if (flag & ACPITZ_TRIPS) {
137 		sc->sc_psv = acpitz_getreading(sc, "_PSV");
138 		for (i = 0; i < ACPITZ_MAX_AC; i++) {
139 			snprintf(name, sizeof(name), "_AC%d", i);
140 			sc->sc_ac[i] = acpitz_getreading(sc, name);
141 			sc->sc_ac_stat[i] = -1;
142 		}
143 	}
144 
145 	/* Read device lists */
146 	if (flag & ACPITZ_DEVLIST) {
147 		if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL",
148 		     0, NULL, &res)) {
149 			acpi_freedevlist(&sc->sc_psl);
150 			acpi_getdevlist(&sc->sc_psl, sc->sc_devnode, &res, 0);
151 			aml_freevalue(&res);
152 		}
153 		for (i = 0; i < ACPITZ_MAX_AC; i++) {
154 			snprintf(name, sizeof(name), "_AL%d", i);
155 			if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
156 			    0, NULL, &res)) {
157 				acpi_freedevlist(&sc->sc_alx[i]);
158 				acpi_getdevlist(&sc->sc_alx[i],
159 				    sc->sc_devnode, &res, 0);
160 				aml_freevalue(&res);
161 			}
162 		}
163 	}
164 }
165 
166 int
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
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 
191 	sc->sc_acpi = (struct acpi_softc *)parent;
192 	sc->sc_devnode = aa->aaa_node;
193 
194 	TAILQ_INIT(&sc->sc_psl);
195 	for (i = 0; i < ACPITZ_MAX_AC; i++)
196 		TAILQ_INIT(&sc->sc_alx[i]);
197 
198 	sc->sc_lasttmp = -1;
199 	if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) {
200 		dnprintf(10, ": failed to read _TMP\n");
201 		return;
202 	}
203 
204 	if ((sc->sc_crt = acpitz_gettempreading(sc, "_CRT")) == -1)
205 		printf(": no critical temperature defined\n");
206 	else
207 		printf(": critical temperature is %d degC\n", KTOC(sc->sc_crt));
208 
209 	sc->sc_hot = acpitz_gettempreading(sc, "_HOT");
210 	sc->sc_tc1 = acpitz_getreading(sc, "_TC1");
211 	sc->sc_tc2 = acpitz_getreading(sc, "_TC2");
212 
213 	/* get _PSL, _ALx */
214 	acpitz_init(sc, ACPITZ_INIT);
215 
216 	dnprintf(10, "%s: _HOT: %d _TC1: %d _TC2: %d _PSV: %d _TMP: %d "
217 	    "_CRT: %d\n", DEVNAME(sc), sc->sc_hot, sc->sc_tc1, sc->sc_tc2,
218 	    sc->sc_psv, sc->sc_tmp, sc->sc_crt);
219 
220 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
221 	    sizeof(sc->sc_sensdev.xname));
222 	strlcpy(sc->sc_sens.desc, "zone temperature",
223 	    sizeof(sc->sc_sens.desc));
224 	sc->sc_sens.type = SENSOR_TEMP;
225 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
226 	sensordev_install(&sc->sc_sensdev);
227 
228 	aml_register_notify(sc->sc_devnode, NULL,
229 	    acpitz_notify, sc, ACPIDEV_POLL);
230 
231 	/*
232 	 * XXX use kthread_create_deferred to ensure we are the very last
233 	 * piece of code that touches this pointer after all CPUs have been
234 	 * fully attached
235 	 */
236 	kthread_create_deferred(acpitz_init_perf, sc);
237 }
238 
239 int
240 acpitz_setfan(struct acpitz_softc *sc, int i, char *method)
241 {
242 	struct aml_node		*node;
243 	struct aml_value	res1, *ref;
244 	char			name[8];
245 	int			rv = 1, x, y;
246 	int64_t			sta;
247 	struct acpi_devlist    *dl;
248 
249 	dnprintf(20, "%s: acpitz_setfan(%d, %s)\n", DEVNAME(sc), i, method);
250 
251 	x = 0;
252 	snprintf(name, sizeof(name), "_AL%d", i);
253 	TAILQ_FOREACH(dl, &sc->sc_alx[i], dev_link) {
254 		if (aml_evalname(sc->sc_acpi, dl->dev_node, "_PR0",0 , NULL,
255 		    &res1)) {
256 			printf("%s: %s[%d] _PR0 failed\n", DEVNAME(sc),
257 			    name, x);
258 			aml_freevalue(&res1);
259 			x++;
260 			continue;
261 		}
262 		if (res1.type != AML_OBJTYPE_PACKAGE) {
263 			printf("%s: %s[%d] _PR0 not a package\n", DEVNAME(sc),
264 			    name, x);
265 			aml_freevalue(&res1);
266 			x++;
267 			continue;
268 		}
269 		for (y = 0; y < res1.length; y++) {
270 			ref = res1.v_package[y];
271 			if (ref->type == AML_OBJTYPE_STRING) {
272 				node = aml_searchrel(sc->sc_devnode,
273 				    ref->v_string);
274 				if (node == NULL) {
275 					printf("%s: %s[%d.%d] _PRO"
276 					    " not a valid device\n",
277 					    DEVNAME(sc), name, x, y);
278 					continue;
279 				}
280 				ref = node->value;
281 			}
282 			if (ref->type == AML_OBJTYPE_OBJREF) {
283 				ref = ref->v_objref.ref;
284 			}
285 			if (ref->type != AML_OBJTYPE_DEVICE &&
286 			    ref->type != AML_OBJTYPE_POWERRSRC) {
287 				printf("%s: %s[%d.%d] _PRO not a package\n",
288 				    DEVNAME(sc), name, x, y);
289 				continue;
290 			}
291 			if (aml_evalname(sc->sc_acpi, ref->node, method, 0,
292 			    NULL, NULL))
293 				printf("%s: %s[%d.%d] %s fails\n",
294 				    DEVNAME(sc), name, x, y, method);
295 
296 			/* save off status of fan */
297 			if (aml_evalinteger(sc->sc_acpi, ref->node, "_STA", 0,
298 			    NULL, &sta))
299 				printf("%s: %s[%d.%d] _STA fails\n",
300 				    DEVNAME(sc), name, x, y);
301 			else {
302 				sc->sc_ac_stat[i] = sta;
303 			}
304 		}
305 		aml_freevalue(&res1);
306 		x++;
307 	}
308 	rv = 0;
309 	return (rv);
310 }
311 
312 void
313 acpitz_refresh(void *arg)
314 {
315 	struct acpitz_softc	*sc = arg;
316 	int			i, trend, nperf;
317 
318 	dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc),
319 	    sc->sc_devnode->name);
320 
321 	/* get _TMP and debounce the value */
322 	if (-1 == (sc->sc_tmp = acpitz_gettempreading(sc, "_TMP"))) {
323 		printf("%s: %s: failed to read temp\n", DEVNAME(sc),
324 		    sc->sc_devnode->name);
325 		return;
326 	}
327 	/* critical trip points */
328 	if (sc->sc_crt != -1 && sc->sc_crt <= sc->sc_tmp) {
329 		/* do critical shutdown */
330 		printf("%s: critical temperature exceeded %dC, shutting "
331 		    "down\n", DEVNAME(sc), KTOC(sc->sc_tmp));
332 		psignal(initproc, SIGUSR2);
333 	}
334 	if (sc->sc_hot != -1 && sc->sc_hot <= sc->sc_tmp) {
335 		printf("%s: _HOT temperature\n", DEVNAME(sc));
336 		/* XXX go to S4, until then cool as hard as we can */
337 	}
338 
339 	/* passive cooling */
340 	if (sc->sc_lasttmp != -1 && sc->sc_tc1 != -1 && sc->sc_tc2 != -1 &&
341 	    sc->sc_psv != -1) {
342 		dnprintf(30, "%s: passive cooling: lasttmp: %d tc1: %d "
343 		    "tc2: %d psv: %d\n", DEVNAME(sc), sc->sc_lasttmp,
344 		    sc->sc_tc1, sc->sc_tc2, sc->sc_psv);
345 
346 		nperf = acpitz_perflevel;
347 		if (sc->sc_psv <= sc->sc_tmp) {
348 			/* Passive cooling enabled */
349 			dnprintf(1, "%s: enabling passive %d %d\n",
350 			    DEVNAME(sc), sc->sc_tmp, sc->sc_psv);
351 			if (!sc->sc_pse)
352 				sc->sc_acpi->sc_pse++;
353 			sc->sc_pse = 1;
354 
355 			trend = sc->sc_tc1 * (sc->sc_tmp - sc->sc_lasttmp) +
356 			    sc->sc_tc2 * (sc->sc_tmp - sc->sc_psv);
357 
358 			/* Depending on trend, slow down/speed up */
359 			if (trend > 0)
360 				nperf -= PERFSTEP;
361 			else
362 				nperf += PERFSTEP;
363 		}
364 		else {
365 			/* Passive cooling disabled, increase % */
366 			dnprintf(1, "%s: disabling passive %d %d\n",
367 			    DEVNAME(sc), sc->sc_tmp, sc->sc_psv);
368 			if (sc->sc_pse)
369 				sc->sc_acpi->sc_pse--;
370 			sc->sc_pse = 0;
371 			nperf += PERFSTEP;
372 		}
373 		if (nperf < 0)
374 			nperf = 0;
375 		else if (nperf > 100)
376 			nperf = 100;
377 
378 		/* clamp passive cooling request */
379 		if (nperf > perflevel)
380 			nperf = perflevel;
381 
382 		/* Perform CPU setperf */
383 		if (acpitz_cpu_setperf && nperf != acpitz_perflevel) {
384 			acpitz_perflevel = nperf;
385 			acpitz_cpu_setperf(nperf);
386 		}
387 	}
388 	sc->sc_lasttmp = sc->sc_tmp;
389 
390 	/* active cooling */
391 	for (i = 0; i < ACPITZ_MAX_AC; i++) {
392 		if (sc->sc_ac[i] != -1 && sc->sc_ac[i] <= sc->sc_tmp) {
393 			/* turn on fan i */
394 			if (sc->sc_ac_stat[i] <= 0)
395 				acpitz_setfan(sc, i, "_ON_");
396 		} else if (sc->sc_ac[i] != -1) {
397 			/* turn off fan i */
398 			if (sc->sc_ac_stat[i] > 0)
399 				acpitz_setfan(sc, i, "_OFF");
400 		}
401 	}
402 	sc->sc_sens.value = sc->sc_tmp * 100000 - 50000;
403 }
404 
405 int
406 acpitz_getreading(struct acpitz_softc *sc, char *name)
407 {
408 	u_int64_t		val;
409 
410 	if (!aml_evalinteger(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &val))
411 		return (val);
412 
413 	return (-1);
414 }
415 
416 int
417 acpitz_gettempreading(struct acpitz_softc *sc, char *name)
418 {
419 	int			rv = -1, tmp = -1, i;
420 
421 	for (i = 0; i < ACPITZ_TMP_RETRY; i++) {
422 		tmp = acpitz_getreading(sc, name);
423 		if (tmp == -1)
424 			goto out;
425 		if (KTOC(tmp) > 0) {
426 			rv = tmp;
427 			break;
428 		} else {
429 			dnprintf(20, "%s: %d invalid reading on %s, "
430 			    "debouncing\n", DEVNAME(sc), tmp, name);
431 		}
432 
433 		acpi_sleep(1000, "acpitz");	/* debounce: 1000 msec */
434 	}
435 	if (i >= ACPITZ_TMP_RETRY) {
436 		printf("%s: %s: failed to read %s\n", DEVNAME(sc),
437 		    sc->sc_devnode->name, name);
438 		goto out;
439 	}
440  out:
441 	dnprintf(30, "%s: name: %s tmp: %d => %dC, rv: %d\n", DEVNAME(sc),
442 	    name, tmp, KTOC(tmp), rv);
443 	return (rv);
444 }
445 
446 int
447 acpitz_notify(struct aml_node *node, int notify_type, void *arg)
448 {
449 	struct acpitz_softc	*sc = arg;
450 
451 	dnprintf(10, "%s notify: %.2x %s\n", DEVNAME(sc), notify_type,
452 	    sc->sc_devnode->name);
453 
454 	switch (notify_type) {
455 	case 0x80:	/* hardware notifications */
456 		break;
457 	case 0x81:	/* operating Points changed */
458 		acpitz_init(sc, ACPITZ_TRIPS);
459 		break;
460 	case 0x82:	/* re-evaluate thermal device list */
461 		acpitz_init(sc, ACPITZ_DEVLIST);
462 		break;
463 	default:
464 		break;
465 	}
466 
467 	acpitz_refresh(sc);
468 	return (0);
469 }
470