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