xref: /dragonfly/sys/dev/acpica/aibs/atk0110.c (revision d4ef6694)
1 /*	$OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Constantine A. Murenin <cnst+dfly@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 <machine/inttypes.h>
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/kernel.h>
24 #include <sys/bus.h>
25 #include <sys/module.h>
26 #include <sys/malloc.h>
27 
28 #include <sys/sensors.h>
29 
30 #include "acpi.h"
31 #include "acpivar.h"
32 
33 /*
34  * ASUSTeK AI Booster (ACPI ATK0110).
35  *
36  * This code was originally written for OpenBSD after the techniques
37  * described in the Linux's asus_atk0110.c and FreeBSD's acpi_aiboost.c
38  * were verified to be accurate on the actual hardware kindly provided by
39  * Sam Fourman Jr.  It was subsequently ported from OpenBSD to DragonFly BSD.
40  *
41  *				  -- Constantine A. Murenin <http://cnst.su/>
42  */
43 
44 #define AIBS_MORE_SENSORS
45 #define AIBS_VERBOSE
46 
47 struct aibs_sensor {
48 	struct ksensor	s;
49 	UINT64		i;
50 	UINT64		l;
51 	UINT64		h;
52 };
53 
54 struct aibs_softc {
55 	struct device		*sc_dev;
56 	ACPI_HANDLE		sc_ah;
57 
58 	struct aibs_sensor	*sc_asens_volt;
59 	struct aibs_sensor	*sc_asens_temp;
60 	struct aibs_sensor	*sc_asens_fan;
61 
62 	struct ksensordev	sc_sensordev;
63 };
64 
65 
66 static int aibs_probe(struct device *);
67 static int aibs_attach(struct device *);
68 static int aibs_detach(struct device *);
69 static void aibs_refresh(void *);
70 
71 static void aibs_attach_sif(struct aibs_softc *, enum sensor_type);
72 static void aibs_refresh_r(struct aibs_softc *, enum sensor_type);
73 
74 
75 static device_method_t aibs_methods[] = {
76 	DEVMETHOD(device_probe,		aibs_probe),
77 	DEVMETHOD(device_attach,	aibs_attach),
78 	DEVMETHOD(device_detach,	aibs_detach),
79 	{ NULL, NULL }
80 };
81 
82 static driver_t aibs_driver = {
83 	"aibs",
84 	aibs_methods,
85 	sizeof(struct aibs_softc)
86 };
87 
88 static devclass_t aibs_devclass;
89 
90 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
91 MODULE_DEPEND(aibs, acpi, 1, 1, 1);
92 
93 static char* aibs_hids[] = {
94 	"ATK0110",
95 	NULL
96 };
97 
98 static int
99 aibs_probe(struct device *dev)
100 {
101 
102 	if (acpi_disabled("aibs") ||
103 	    ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
104 		return ENXIO;
105 
106 	device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
107 	return 0;
108 }
109 
110 static int
111 aibs_attach(struct device *dev)
112 {
113 	struct aibs_softc	*sc;
114 
115 	sc = device_get_softc(dev);
116 	sc->sc_dev = dev;
117 	sc->sc_ah = acpi_get_handle(dev);
118 
119 	strlcpy(sc->sc_sensordev.xname, device_get_nameunit(dev),
120 	    sizeof(sc->sc_sensordev.xname));
121 
122 	aibs_attach_sif(sc, SENSOR_VOLTS_DC);
123 	aibs_attach_sif(sc, SENSOR_TEMP);
124 	aibs_attach_sif(sc, SENSOR_FANRPM);
125 
126 	if (sc->sc_sensordev.sensors_count == 0) {
127 		device_printf(dev, "no sensors found\n");
128 		return ENXIO;
129 	}
130 
131 	if (sensor_task_register(sc, aibs_refresh, 5)) {
132 		device_printf(dev, "unable to register update task\n");
133 		return ENXIO;
134 	}
135 
136 	sensordev_install(&sc->sc_sensordev);
137 	return 0;
138 }
139 
140 static void
141 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
142 {
143 	ACPI_STATUS		s;
144 	ACPI_BUFFER		b;
145 	ACPI_OBJECT		*bp, *o;
146 	int			i, n;
147 	char			name[] = "?SIF";
148 	struct aibs_sensor	*as;
149 
150 	switch (st) {
151 	case SENSOR_TEMP:
152 		name[0] = 'T';
153 		break;
154 	case SENSOR_FANRPM:
155 		name[0] = 'F';
156 		break;
157 	case SENSOR_VOLTS_DC:
158 		name[0] = 'V';
159 		break;
160 	default:
161 		return;
162 	}
163 
164 	b.Length = ACPI_ALLOCATE_BUFFER;
165 	s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
166 	    ACPI_TYPE_PACKAGE);
167 	if (ACPI_FAILURE(s)) {
168 		device_printf(sc->sc_dev, "%s not found\n", name);
169 		return;
170 	}
171 
172 	bp = b.Pointer;
173 	o = bp->Package.Elements;
174 	if (o[0].Type != ACPI_TYPE_INTEGER) {
175 		device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
176 		AcpiOsFree(b.Pointer);
177 		return;
178 	}
179 
180 	n = o[0].Integer.Value;
181 	if (bp->Package.Count - 1 < n) {
182 		device_printf(sc->sc_dev, "%s: invalid package\n", name);
183 		AcpiOsFree(b.Pointer);
184 		return;
185 	} else if (bp->Package.Count - 1 > n) {
186 		int on = n;
187 
188 #ifdef AIBS_MORE_SENSORS
189 		n = bp->Package.Count - 1;
190 #endif
191 		device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
192 		    ", assume %i\n", name, on, bp->Package.Count - 1, n);
193 	}
194 	if (n < 1) {
195 		device_printf(sc->sc_dev, "%s: no members in the package\n",
196 		    name);
197 		AcpiOsFree(b.Pointer);
198 		return;
199 	}
200 
201 	as = kmalloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
202 	if (as == NULL) {
203 		device_printf(sc->sc_dev, "%s: malloc fail\n", name);
204 		AcpiOsFree(b.Pointer);
205 		return;
206 	}
207 
208 	switch (st) {
209 	case SENSOR_TEMP:
210 		sc->sc_asens_temp = as;
211 		break;
212 	case SENSOR_FANRPM:
213 		sc->sc_asens_fan = as;
214 		break;
215 	case SENSOR_VOLTS_DC:
216 		sc->sc_asens_volt = as;
217 		break;
218 	default:
219 		/* NOTREACHED */
220 		return;
221 	}
222 
223 	for (i = 0, o++; i < n; i++, o++) {
224 		ACPI_OBJECT	*oi;
225 
226 		/* acpica automatically evaluates the referenced package */
227 		if (o[0].Type != ACPI_TYPE_PACKAGE) {
228 			device_printf(sc->sc_dev,
229 			    "%s: %i: not a package: %i type\n",
230 			    name, i, o[0].Type);
231 			continue;
232 		}
233 		oi = o[0].Package.Elements;
234 		if (o[0].Package.Count != 5 ||
235 		    oi[0].Type != ACPI_TYPE_INTEGER ||
236 		    oi[1].Type != ACPI_TYPE_STRING ||
237 		    oi[2].Type != ACPI_TYPE_INTEGER ||
238 		    oi[3].Type != ACPI_TYPE_INTEGER ||
239 		    oi[4].Type != ACPI_TYPE_INTEGER) {
240 			device_printf(sc->sc_dev,
241 			    "%s: %i: invalid package\n",
242 			    name, i);
243 			continue;
244 		}
245 		as[i].i = oi[0].Integer.Value;
246 		strlcpy(as[i].s.desc, oi[1].String.Pointer,
247 		    sizeof(as[i].s.desc));
248 		as[i].l = oi[2].Integer.Value;
249 		as[i].h = oi[3].Integer.Value;
250 		as[i].s.type = st;
251 #ifdef AIBS_VERBOSE
252 		device_printf(sc->sc_dev, "%c%i: "
253 		    "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64"  "
254 		    "0x%"PRIx64"\n",
255 		    name[0], i,
256 		    as[i].i, as[i].s.desc, (int64_t)as[i].l, (int64_t)as[i].h,
257 		    oi[4].Integer.Value);
258 #endif
259 		sensor_attach(&sc->sc_sensordev, &as[i].s);
260 	}
261 
262 	AcpiOsFree(b.Pointer);
263 	return;
264 }
265 
266 static int
267 aibs_detach(struct device *dev)
268 {
269 	struct aibs_softc	*sc = device_get_softc(dev);
270 
271 	sensordev_deinstall(&sc->sc_sensordev);
272 	sensor_task_unregister(sc);
273 	if (sc->sc_asens_volt != NULL)
274 		kfree(sc->sc_asens_volt, M_DEVBUF);
275 	if (sc->sc_asens_temp != NULL)
276 		kfree(sc->sc_asens_temp, M_DEVBUF);
277 	if (sc->sc_asens_fan != NULL)
278 		kfree(sc->sc_asens_fan, M_DEVBUF);
279 	return 0;
280 }
281 
282 #ifdef AIBS_VERBOSE
283 #define ddevice_printf(x...) device_printf(x)
284 #else
285 #define ddevice_printf(x...)
286 #endif
287 
288 static void
289 aibs_refresh(void *arg)
290 {
291 	struct aibs_softc *sc = arg;
292 
293 	aibs_refresh_r(sc, SENSOR_VOLTS_DC);
294 	aibs_refresh_r(sc, SENSOR_TEMP);
295 	aibs_refresh_r(sc, SENSOR_FANRPM);
296 }
297 
298 static void
299 aibs_refresh_r(struct aibs_softc *sc, enum sensor_type st)
300 {
301 	ACPI_STATUS		rs;
302 	ACPI_HANDLE		rh;
303 	int			i, n = sc->sc_sensordev.maxnumt[st];
304 	char			*name;
305 	struct aibs_sensor	*as;
306 
307 	switch (st) {
308 	case SENSOR_TEMP:
309 		name = "RTMP";
310 		as = sc->sc_asens_temp;
311 		break;
312 	case SENSOR_FANRPM:
313 		name = "RFAN";
314 		as = sc->sc_asens_fan;
315 		break;
316 	case SENSOR_VOLTS_DC:
317 		name = "RVLT";
318 		as = sc->sc_asens_volt;
319 		break;
320 	default:
321 		return;
322 	}
323 
324 	if (as == NULL)
325 		return;
326 
327 	rs = AcpiGetHandle(sc->sc_ah, name, &rh);
328 	if (ACPI_FAILURE(rs)) {
329 		ddevice_printf(sc->sc_dev, "%s: method handle not found\n",
330 		    name);
331 		for (i = 0; i < n; i++)
332 			as[i].s.flags |= SENSOR_FINVALID;
333 		return;
334 	}
335 
336 	for (i = 0; i < n; i++) {
337 		ACPI_OBJECT		p, *bp;
338 		ACPI_OBJECT_LIST	mp;
339 		ACPI_BUFFER		b;
340 		UINT64			v;
341 		struct ksensor		*s = &as[i].s;
342 		const UINT64		l = as[i].l, h = as[i].h;
343 
344 		p.Type = ACPI_TYPE_INTEGER;
345 		p.Integer.Value = as[i].i;
346 		mp.Count = 1;
347 		mp.Pointer = &p;
348 		b.Length = ACPI_ALLOCATE_BUFFER;
349 		rs = AcpiEvaluateObjectTyped(rh, NULL, &mp, &b,
350 		    ACPI_TYPE_INTEGER);
351 		if (ACPI_FAILURE(rs)) {
352 			ddevice_printf(sc->sc_dev,
353 			    "%s: %i: evaluation failed\n",
354 			    name, i);
355 			s->flags |= SENSOR_FINVALID;
356 			continue;
357 		}
358 		bp = b.Pointer;
359 		v = bp->Integer.Value;
360 		AcpiOsFree(b.Pointer);
361 
362 		switch (st) {
363 		case SENSOR_TEMP:
364 			s->value = v * 100 * 1000 + 273150000;
365 			if (v == 0) {
366 				s->status = SENSOR_S_UNKNOWN;
367 				s->flags |= SENSOR_FINVALID;
368 			} else {
369 				if (v > h)
370 					s->status = SENSOR_S_CRIT;
371 				else if (v > l)
372 					s->status = SENSOR_S_WARN;
373 				else
374 					s->status = SENSOR_S_OK;
375 				s->flags &= ~SENSOR_FINVALID;
376 			}
377 			break;
378 		case SENSOR_FANRPM:
379 			s->value = v;
380 			/* some boards have strange limits for fans */
381 			if ((l != 0 && l < v && v < h) ||
382 			    (l == 0 && v > h))
383 				s->status = SENSOR_S_OK;
384 			else
385 				s->status = SENSOR_S_WARN;
386 			s->flags &= ~SENSOR_FINVALID;
387 			break;
388 		case SENSOR_VOLTS_DC:
389 			s->value = v * 1000;
390 			if (l < v && v < h)
391 				s->status = SENSOR_S_OK;
392 			else
393 				s->status = SENSOR_S_WARN;
394 			s->flags &= ~SENSOR_FINVALID;
395 			break;
396 		default:
397 			/* NOTREACHED */
398 			break;
399 		}
400 	}
401 
402 	return;
403 }
404