xref: /openbsd/sys/dev/acpi/atk0110.c (revision 17df1aa7)
1 /*	$OpenBSD: atk0110.c,v 1.3 2010/02/26 17:24:11 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
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/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/sensors.h>
24 
25 #include <dev/acpi/acpireg.h>
26 #include <dev/acpi/acpivar.h>
27 #include <dev/acpi/acpidev.h>
28 #include <dev/acpi/amltypes.h>
29 #include <dev/acpi/dsdt.h>
30 
31 /*
32  * ASUSTeK AI Booster (ACPI ATK0110).
33  *
34  * The driver was inspired by Takanori Watanabe's acpi_aiboost driver.
35  * http://cvsweb.freebsd.org/src/sys/dev/acpi_support/acpi_aiboost.c
36  *
37  * Special thanks goes to Sam Fourman Jr. for providing access to several
38  * ASUS boxes where the driver could be tested.
39  *
40  *							-- cnst.su.
41  */
42 
43 #define AIBS_MORE_SENSORS
44 /* #define AIBS_VERBOSE */
45 
46 struct aibs_sensor {
47 	struct ksensor	s;
48 	int64_t		i;
49 	int64_t		l;
50 	int64_t		h;
51 };
52 
53 struct aibs_softc {
54 	struct device		sc_dev;
55 
56 	struct acpi_softc	*sc_acpi;
57 	struct aml_node		*sc_devnode;
58 
59 	struct aibs_sensor	*sc_asens_volt;
60 	struct aibs_sensor	*sc_asens_temp;
61 	struct aibs_sensor	*sc_asens_fan;
62 
63 	struct ksensordev	sc_sensordev;
64 };
65 
66 
67 int	aibs_match(struct device *, void *, void *);
68 void	aibs_attach(struct device *, struct device *, void *);
69 void	aibs_refresh(void *);
70 
71 void	aibs_attach_sif(struct aibs_softc *, enum sensor_type);
72 void	aibs_refresh_r(struct aibs_softc *, enum sensor_type);
73 
74 
75 struct cfattach aibs_ca = {
76 	sizeof(struct aibs_softc), aibs_match, aibs_attach
77 };
78 
79 struct cfdriver aibs_cd = {
80 	NULL, "aibs", DV_DULL
81 };
82 
83 static const char* aibs_hids[] = {
84 	ACPI_DEV_ASUSAIBOOSTER,
85 	NULL
86 };
87 
88 int
89 aibs_match(struct device *parent, void *match, void *aux)
90 {
91 	struct acpi_attach_args	*aa = aux;
92 	struct cfdata		*cf = match;
93 
94 	return acpi_matchhids(aa, aibs_hids, cf->cf_driver->cd_name);
95 }
96 
97 void
98 aibs_attach(struct device *parent, struct device *self, void *aux)
99 {
100 	struct aibs_softc	*sc = (struct aibs_softc *)self;
101 	struct acpi_attach_args	*aa = aux;
102 
103 	sc->sc_acpi = (struct acpi_softc *)parent;
104 	sc->sc_devnode = aa->aaa_node;
105 
106 	printf("\n");
107 
108 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
109 	    sizeof(sc->sc_sensordev.xname));
110 
111 	aibs_attach_sif(sc, SENSOR_TEMP);
112 	aibs_attach_sif(sc, SENSOR_FANRPM);
113 	aibs_attach_sif(sc, SENSOR_VOLTS_DC);
114 
115 	if (sc->sc_sensordev.sensors_count == 0) {
116 		printf("%s: no sensors found\n", DEVNAME(sc));
117 		return;
118 	}
119 
120 	if (sensor_task_register(sc, aibs_refresh, 5) == NULL) {
121 		printf("%s: unable to register update task\n", DEVNAME(sc));
122 		return;
123 	}
124 
125 	sensordev_install(&sc->sc_sensordev);
126 }
127 
128 void
129 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
130 {
131 	struct aml_value	res;
132 	struct aml_value	**v;
133 	int			i, n;
134 	char			*name = "?SIF";
135 	struct aibs_sensor	*as;
136 
137 	switch (st) {
138 	case SENSOR_TEMP:
139 		name[0] = 'T';
140 		break;
141 	case SENSOR_FANRPM:
142 		name[0] = 'F';
143 		break;
144 	case SENSOR_VOLTS_DC:
145 		name[0] = 'V';
146 		break;
147 	default:
148 		return;
149 	}
150 
151 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) {
152 		printf("%s: %s not found\n", DEVNAME(sc), name);
153 		aml_freevalue(&res);
154 		return;
155 	}
156 
157 	if (res.type != AML_OBJTYPE_PACKAGE) {
158 		printf("%s: %s: not a package\n", DEVNAME(sc), name);
159 		aml_freevalue(&res);
160 		return;
161 	}
162 
163 	v = res.v_package;
164 	if (v[0]->type != AML_OBJTYPE_INTEGER) {
165 		printf("%s: %s[0]: invalid type\n", DEVNAME(sc), name);
166 		aml_freevalue(&res);
167 		return;
168 	}
169 
170 	n = v[0]->v_integer;
171 	if (res.length - 1 < n) {
172 		printf("%s: %s: invalid package\n", DEVNAME(sc), name);
173 		aml_freevalue(&res);
174 		return;
175 	} else if (res.length - 1 > n) {
176 		printf("%s: %s: misformed package: %i/%i",
177 		    DEVNAME(sc), name, n, res.length - 1);
178 #ifdef AIBS_MORE_SENSORS
179 		n = res.length - 1;
180 #endif
181 		printf(", assume %i\n", n);
182 	}
183 	if (n < 1) {
184 		printf("%s: %s: no members in the package\n",
185 		    DEVNAME(sc), name);
186 		aml_freevalue(&res);
187 		return;
188 	}
189 
190 	as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
191 	if (as == NULL) {
192 		printf("%s: %s: malloc fail\n", DEVNAME(sc), name);
193 		aml_freevalue(&res);
194 		return;
195 	}
196 
197 	switch (st) {
198 	case SENSOR_TEMP:
199 		sc->sc_asens_temp = as;
200 		break;
201 	case SENSOR_FANRPM:
202 		sc->sc_asens_fan = as;
203 		break;
204 	case SENSOR_VOLTS_DC:
205 		sc->sc_asens_volt = as;
206 		break;
207 	default:
208 		/* NOTREACHED */
209 		return;
210 	}
211 
212 	for (i = 0, v++; i < n; i++, v++) {
213 		struct aml_value	ri;
214 
215 		if(v[0]->type != AML_OBJTYPE_STRING) {
216 			printf("%s: %s: %i: not a string: %i type\n",
217 			    DEVNAME(sc), name, i, v[0]->type);
218 			continue;
219 		}
220 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, v[0]->v_string,
221 		    0, NULL, &ri)) {
222 			printf("%s: %s: %i: %s not found\n",
223 			    DEVNAME(sc), name, i, v[0]->v_string);
224 			aml_freevalue(&ri);
225 			continue;
226 		}
227 		if (ri.type != AML_OBJTYPE_PACKAGE) {
228 			printf("%s: %s: %i: %s: not a package\n",
229 			    DEVNAME(sc), name, i, v[0]->v_string);
230 			aml_freevalue(&ri);
231 			continue;
232 		}
233 		if (ri.length != 5 ||
234 		    ri.v_package[0]->type != AML_OBJTYPE_INTEGER ||
235 		    ri.v_package[1]->type != AML_OBJTYPE_STRING ||
236 		    ri.v_package[2]->type != AML_OBJTYPE_INTEGER ||
237 		    ri.v_package[3]->type != AML_OBJTYPE_INTEGER ||
238 		    ri.v_package[4]->type != AML_OBJTYPE_INTEGER) {
239 			printf("%s: %s: %i: %s: invalid package\n",
240 			    DEVNAME(sc), name, i, v[0]->v_string);
241 			aml_freevalue(&ri);
242 			continue;
243 		}
244 		as[i].i = ri.v_package[0]->v_integer;
245 		strlcpy(as[i].s.desc, ri.v_package[1]->v_string,
246 		    sizeof(as[i].s.desc));
247 		as[i].l = ri.v_package[2]->v_integer;
248 		as[i].h = ri.v_package[3]->v_integer;
249 		as[i].s.type = st;
250 #ifdef AIBS_VERBOSE
251 		printf("%s: %s %2i: %4s: "
252 		    "0x%08llx %20s %5lli / %5lli  0x%llx\n",
253 		    DEVNAME(sc), name, i, v[0]->v_string,
254 		    as[i].i, as[i].s.desc, as[i].l, as[i].h,
255 		    ri.v_package[4]->v_integer);
256 #endif
257 		sensor_attach(&sc->sc_sensordev, &as[i].s);
258 		aml_freevalue(&ri);
259 	}
260 
261 	aml_freevalue(&res);
262 	return;
263 }
264 
265 void
266 aibs_refresh(void *arg)
267 {
268 	struct aibs_softc *sc = arg;
269 
270 	aibs_refresh_r(sc, SENSOR_TEMP);
271 	aibs_refresh_r(sc, SENSOR_FANRPM);
272 	aibs_refresh_r(sc, SENSOR_VOLTS_DC);
273 }
274 
275 void
276 aibs_refresh_r(struct aibs_softc *sc, enum sensor_type st)
277 {
278 	struct aml_node		*node;
279 	int			i, n = sc->sc_sensordev.maxnumt[st];
280 	char			*name;
281 	struct aibs_sensor	*as;
282 
283 	switch (st) {
284 	case SENSOR_TEMP:
285 		name = "RTMP";
286 		as = sc->sc_asens_temp;
287 		break;
288 	case SENSOR_FANRPM:
289 		name = "RFAN";
290 		as = sc->sc_asens_fan;
291 		break;
292 	case SENSOR_VOLTS_DC:
293 		name = "RVLT";
294 		as = sc->sc_asens_volt;
295 		break;
296 	default:
297 		return;
298 	}
299 
300 	if (as == NULL)
301 		return;
302 
303 	node = aml_searchname(sc->sc_devnode, name);
304 	if (node == NULL || node->value == NULL ||
305 	    node->value->type != AML_OBJTYPE_METHOD) {
306 		dprintf("%s: %s: method node not found\n",
307 		    DEVNAME(sc), name);
308 		for (i = 0; i < n; i++)
309 			as[i].s.flags |= SENSOR_FINVALID;
310 		return;
311 	}
312 
313 	for (i = 0; i < n; i++) {
314 		struct aml_value	req, res;
315 		int64_t			v;
316 		struct ksensor		*s = &as[i].s;
317 		const int64_t		l = as[i].l, h = as[i].h;
318 
319 		req.type = AML_OBJTYPE_INTEGER;
320 		req.v_integer = as[i].i;
321 		if (aml_evalnode(sc->sc_acpi, node, 1, &req, &res)) {
322 			dprintf("%s: %s: %i: evaluation failed\n",
323 			    DEVNAME(sc), name, i);
324 			aml_freevalue(&res);
325 			s->flags |= SENSOR_FINVALID;
326 			continue;
327 		}
328 		if (res.type != AML_OBJTYPE_INTEGER) {
329 			dprintf("%s: %s: %i: not an integer: type %i\n",
330 			    DEVNAME(sc), name, i, res.type);
331 			aml_freevalue(&res);
332 			s->flags |= SENSOR_FINVALID;
333 			continue;
334 		}
335 		v = res.v_integer;
336 		aml_freevalue(&res);
337 
338 		switch (st) {
339 		case SENSOR_TEMP:
340 			s->value = v * 100 * 1000 + 273150000;
341 			if (v == 0) {
342 				s->status = SENSOR_S_UNKNOWN;
343 				s->flags |= SENSOR_FINVALID;
344 			} else {
345 				if (v > h)
346 					s->status = SENSOR_S_CRIT;
347 				else if (v > l)
348 					s->status = SENSOR_S_WARN;
349 				else
350 					s->status = SENSOR_S_OK;
351 				s->flags &= ~SENSOR_FINVALID;
352 			}
353 			break;
354 		case SENSOR_FANRPM:
355 			s->value = v;
356 			/* some boards have strange limits for fans */
357 			if ((l != 0 && l < v && v < h) ||
358 			    (l == 0 && v > h))
359 				s->status = SENSOR_S_OK;
360 			else
361 				s->status = SENSOR_S_WARN;
362 			s->flags &= ~SENSOR_FINVALID;
363 			break;
364 		case SENSOR_VOLTS_DC:
365 			s->value = v * 1000;
366 			if (l < v && v < h)
367 				s->status = SENSOR_S_OK;
368 			else
369 				s->status = SENSOR_S_WARN;
370 			s->flags &= ~SENSOR_FINVALID;
371 			break;
372 		default:
373 			/* NOTREACHED */
374 			break;
375 		}
376 	}
377 
378 	return;
379 }
380