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