1 /* $NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $ */
2 /* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */
3
4 /*
5 * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/cdefs.h>
21 #include <machine/_inttypes.h>
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/bus.h>
26 #include <sys/module.h>
27 #include <sys/malloc.h>
28 #include <sys/sysctl.h>
29 #include <sys/stdint.h>
30
31 #include <contrib/dev/acpica/include/acpi.h>
32 #include <dev/acpica/acpivar.h>
33
34 /*
35 * ASUSTeK AI Booster (ACPI ASOC ATK0110).
36 *
37 * This code was originally written for OpenBSD after the techniques
38 * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's
39 * acpi_aiboost.c were verified to be accurate on the actual hardware kindly
40 * provided by Sam Fourman Jr. It was subsequently ported from OpenBSD to
41 * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9).
42 *
43 * -- Constantine A. Murenin <http://cnst.su/>
44 */
45
46 #define _COMPONENT ACPI_OEM
47 ACPI_MODULE_NAME("aibs");
48 ACPI_SERIAL_DECL(aibs, "aibs");
49
50 #define AIBS_MORE_SENSORS
51 #define AIBS_VERBOSE
52
53 #define AIBS_GROUP_SENSORS 0x06
54
55 #define AIBS_SENS_TYPE(x) (((x) >> 16) & 0xff)
56 #define AIBS_SENS_TYPE_VOLT 2
57 #define AIBS_SENS_TYPE_TEMP 3
58 #define AIBS_SENS_TYPE_FAN 4
59
60 #define AIBS_SENS_TYPE_VOLT_NAME "volt"
61 #define AIBS_SENS_TYPE_VOLT_TEMP "temp"
62 #define AIBS_SENS_TYPE_VOLT_FAN "fan"
63
64 struct aibs_sensor {
65 ACPI_INTEGER v;
66 ACPI_INTEGER i;
67 ACPI_INTEGER l;
68 ACPI_INTEGER h;
69 int t;
70 };
71
72 struct aibs_softc {
73 device_t sc_dev;
74 ACPI_HANDLE sc_ah;
75
76 struct aibs_sensor *sc_asens_volt;
77 struct aibs_sensor *sc_asens_temp;
78 struct aibs_sensor *sc_asens_fan;
79 struct aibs_sensor *sc_asens_all;
80
81 struct sysctl_oid *sc_volt_sysctl;
82 struct sysctl_oid *sc_temp_sysctl;
83 struct sysctl_oid *sc_fan_sysctl;
84
85 bool sc_ggrp_method;
86 };
87
88 static int aibs_probe(device_t);
89 static int aibs_attach(device_t);
90 static int aibs_detach(device_t);
91 static int aibs_sysctl(SYSCTL_HANDLER_ARGS);
92 static int aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS);
93
94 static int aibs_attach_ggrp(struct aibs_softc *);
95 static int aibs_attach_sif(struct aibs_softc *, int);
96
97 static device_method_t aibs_methods[] = {
98 DEVMETHOD(device_probe, aibs_probe),
99 DEVMETHOD(device_attach, aibs_attach),
100 DEVMETHOD(device_detach, aibs_detach),
101 { NULL, NULL }
102 };
103
104 static driver_t aibs_driver = {
105 "aibs",
106 aibs_methods,
107 sizeof(struct aibs_softc)
108 };
109
110 DRIVER_MODULE(aibs, acpi, aibs_driver, NULL, NULL);
111 MODULE_DEPEND(aibs, acpi, 1, 1, 1);
112
113 static char* aibs_hids[] = {
114 "ATK0110",
115 NULL
116 };
117
118 static int
aibs_probe(device_t dev)119 aibs_probe(device_t dev)
120 {
121 int rv;
122
123 if (acpi_disabled("aibs"))
124 return (ENXIO);
125 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids, NULL);
126 if (rv <= 0 )
127 device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
128 return (rv);
129 }
130
131 static int
aibs_attach(device_t dev)132 aibs_attach(device_t dev)
133 {
134 struct aibs_softc *sc = device_get_softc(dev);
135 int err;
136
137 sc->sc_dev = dev;
138 sc->sc_ah = acpi_get_handle(dev);
139
140 sc->sc_ggrp_method = false;
141 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_VOLT);
142 if (err == 0)
143 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_TEMP);
144 if (err == 0)
145 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_FAN);
146
147 if (err == 0)
148 return (0);
149
150 /* Clean up whatever was allocated earlier. */
151 if (sc->sc_volt_sysctl != NULL)
152 sysctl_remove_oid(sc->sc_volt_sysctl, true, true);
153 if (sc->sc_temp_sysctl != NULL)
154 sysctl_remove_oid(sc->sc_temp_sysctl, true, true);
155 if (sc->sc_fan_sysctl != NULL)
156 sysctl_remove_oid(sc->sc_fan_sysctl, true, true);
157 aibs_detach(dev);
158
159 sc->sc_ggrp_method = true;
160 err = aibs_attach_ggrp(sc);
161 return (err);
162 }
163
164 static int
aibs_add_sensor(struct aibs_softc * sc,ACPI_OBJECT * o,struct aibs_sensor * sensor,const char ** descr)165 aibs_add_sensor(struct aibs_softc *sc, ACPI_OBJECT *o,
166 struct aibs_sensor* sensor, const char ** descr)
167 {
168 int off;
169
170 /*
171 * Packages for the old and new methods are quite
172 * similar except that the new package has two
173 * new (unknown / unused) fields after the name field.
174 */
175 if (sc->sc_ggrp_method)
176 off = 4;
177 else
178 off = 2;
179
180 if (o->Type != ACPI_TYPE_PACKAGE) {
181 device_printf(sc->sc_dev,
182 "sensor object is not a package: %i type\n",
183 o->Type);
184 return (ENXIO);
185 }
186 if (o[0].Package.Count != (off + 3) ||
187 o->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
188 o->Package.Elements[1].Type != ACPI_TYPE_STRING ||
189 o->Package.Elements[off].Type != ACPI_TYPE_INTEGER ||
190 o->Package.Elements[off + 1].Type != ACPI_TYPE_INTEGER ||
191 o->Package.Elements[off + 2].Type != ACPI_TYPE_INTEGER) {
192 device_printf(sc->sc_dev, "unexpected package content\n");
193 return (ENXIO);
194 }
195
196 sensor->i = o->Package.Elements[0].Integer.Value;
197 *descr = o->Package.Elements[1].String.Pointer;
198 sensor->l = o->Package.Elements[off].Integer.Value;
199 sensor->h = o->Package.Elements[off + 1].Integer.Value;
200 /* For the new method the second value is a range size. */
201 if (sc->sc_ggrp_method)
202 sensor->h += sensor->l;
203 sensor->t = AIBS_SENS_TYPE(sensor->i);
204
205 switch (sensor->t) {
206 case AIBS_SENS_TYPE_VOLT:
207 case AIBS_SENS_TYPE_TEMP:
208 case AIBS_SENS_TYPE_FAN:
209 return (0);
210 default:
211 device_printf(sc->sc_dev, "unknown sensor type 0x%x",
212 sensor->t);
213 return (ENXIO);
214 }
215 }
216
217 static void
aibs_sensor_added(struct aibs_softc * sc,struct sysctl_oid * so,const char * type_name,int idx,struct aibs_sensor * sensor,const char * descr)218 aibs_sensor_added(struct aibs_softc *sc, struct sysctl_oid *so,
219 const char *type_name, int idx, struct aibs_sensor *sensor,
220 const char *descr)
221 {
222 char sysctl_name[8];
223
224 snprintf(sysctl_name, sizeof(sysctl_name), "%i", idx);
225 #ifdef AIBS_VERBOSE
226 device_printf(sc->sc_dev, "%c%i: 0x%08jx %20s %5jd / %5jd\n",
227 type_name[0], idx,
228 (uintmax_t)sensor->i, descr, (intmax_t)sensor->l,
229 (intmax_t)sensor->h);
230 #endif
231 SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
232 SYSCTL_CHILDREN(so), idx, sysctl_name,
233 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, (uintptr_t)sensor,
234 sc->sc_ggrp_method ? aibs_sysctl_ggrp : aibs_sysctl,
235 sensor->t == AIBS_SENS_TYPE_TEMP ? "IK" : "I", descr);
236 }
237
238 static int
aibs_attach_ggrp(struct aibs_softc * sc)239 aibs_attach_ggrp(struct aibs_softc *sc)
240 {
241 ACPI_STATUS s;
242 ACPI_BUFFER buf;
243 ACPI_HANDLE h;
244 ACPI_OBJECT id;
245 ACPI_OBJECT *bp;
246 ACPI_OBJECT_LIST arg;
247 int i;
248 int t, v, f;
249 int err;
250 int *s_idx;
251 const char *name;
252 const char *descr;
253 struct aibs_sensor *sensor;
254 struct sysctl_oid **so;
255
256 /* First see if GITM is available. */
257 s = AcpiGetHandle(sc->sc_ah, "GITM", &h);
258 if (ACPI_FAILURE(s)) {
259 if (bootverbose)
260 device_printf(sc->sc_dev, "GITM not found\n");
261 return (ENXIO);
262 }
263
264 /*
265 * Now call GGRP with the appropriate argument to list sensors.
266 * The method lists different groups of entities depending on
267 * the argument.
268 */
269 id.Integer.Value = AIBS_GROUP_SENSORS;
270 id.Type = ACPI_TYPE_INTEGER;
271 arg.Count = 1;
272 arg.Pointer = &id;
273 buf.Length = ACPI_ALLOCATE_BUFFER;
274 buf.Pointer = NULL;
275 s = AcpiEvaluateObjectTyped(sc->sc_ah, "GGRP", &arg, &buf,
276 ACPI_TYPE_PACKAGE);
277 if (ACPI_FAILURE(s)) {
278 device_printf(sc->sc_dev, "GGRP not found\n");
279 return (ENXIO);
280 }
281
282 bp = buf.Pointer;
283 sc->sc_asens_all = malloc(sizeof(*sc->sc_asens_all) * bp->Package.Count,
284 M_DEVBUF, M_WAITOK | M_ZERO);
285 v = t = f = 0;
286 for (i = 0; i < bp->Package.Count; i++) {
287 sensor = &sc->sc_asens_all[i];
288 err = aibs_add_sensor(sc, &bp->Package.Elements[i], sensor,
289 &descr);
290 if (err != 0)
291 continue;
292
293 switch (sensor->t) {
294 case AIBS_SENS_TYPE_VOLT:
295 name = "volt";
296 so = &sc->sc_volt_sysctl;
297 s_idx = &v;
298 break;
299 case AIBS_SENS_TYPE_TEMP:
300 name = "temp";
301 so = &sc->sc_temp_sysctl;
302 s_idx = &t;
303 break;
304 case AIBS_SENS_TYPE_FAN:
305 name = "fan";
306 so = &sc->sc_fan_sysctl;
307 s_idx = &f;
308 break;
309 default:
310 panic("add_sensor succeeded for unknown sensor type %d",
311 sensor->t);
312 }
313
314 if (*so == NULL) {
315 /* sysctl subtree for sensors of this type */
316 *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
317 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)),
318 sensor->t, name, CTLFLAG_RD | CTLFLAG_MPSAFE,
319 NULL, NULL);
320 }
321 aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr);
322 *s_idx += 1;
323 }
324
325 AcpiOsFree(buf.Pointer);
326 return (0);
327 }
328
329 static int
aibs_attach_sif(struct aibs_softc * sc,int st)330 aibs_attach_sif(struct aibs_softc *sc, int st)
331 {
332 char name[] = "?SIF";
333 ACPI_STATUS s;
334 ACPI_BUFFER b;
335 ACPI_OBJECT *bp, *o;
336 const char *node;
337 struct aibs_sensor *as;
338 struct sysctl_oid **so;
339 int i, n;
340 int err;
341
342 switch (st) {
343 case AIBS_SENS_TYPE_VOLT:
344 node = "volt";
345 name[0] = 'V';
346 so = &sc->sc_volt_sysctl;
347 break;
348 case AIBS_SENS_TYPE_TEMP:
349 node = "temp";
350 name[0] = 'T';
351 so = &sc->sc_temp_sysctl;
352 break;
353 case AIBS_SENS_TYPE_FAN:
354 node = "fan";
355 name[0] = 'F';
356 so = &sc->sc_fan_sysctl;
357 break;
358 default:
359 panic("Unsupported sensor type %d", st);
360 }
361
362 b.Length = ACPI_ALLOCATE_BUFFER;
363 s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
364 ACPI_TYPE_PACKAGE);
365 if (ACPI_FAILURE(s)) {
366 device_printf(sc->sc_dev, "%s not found\n", name);
367 return (ENXIO);
368 }
369
370 bp = b.Pointer;
371 o = bp->Package.Elements;
372 if (o[0].Type != ACPI_TYPE_INTEGER) {
373 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
374 AcpiOsFree(b.Pointer);
375 return (ENXIO);
376 }
377
378 n = o[0].Integer.Value;
379 if (bp->Package.Count - 1 < n) {
380 device_printf(sc->sc_dev, "%s: invalid package\n", name);
381 AcpiOsFree(b.Pointer);
382 return (ENXIO);
383 } else if (bp->Package.Count - 1 > n) {
384 int on = n;
385
386 #ifdef AIBS_MORE_SENSORS
387 n = bp->Package.Count - 1;
388 #endif
389 device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
390 ", assume %i\n", name, on, bp->Package.Count - 1, n);
391 }
392 if (n < 1) {
393 device_printf(sc->sc_dev, "%s: no members in the package\n",
394 name);
395 AcpiOsFree(b.Pointer);
396 return (ENXIO);
397 }
398
399 as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO);
400 switch (st) {
401 case AIBS_SENS_TYPE_VOLT:
402 sc->sc_asens_volt = as;
403 break;
404 case AIBS_SENS_TYPE_TEMP:
405 sc->sc_asens_temp = as;
406 break;
407 case AIBS_SENS_TYPE_FAN:
408 sc->sc_asens_fan = as;
409 break;
410 }
411
412 /* sysctl subtree for sensors of this type */
413 *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
414 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
415 node, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, NULL);
416
417 for (i = 0, o++; i < n; i++, o++) {
418 const char *descr;
419
420 err = aibs_add_sensor(sc, o, &as[i], &descr);
421 if (err == 0)
422 aibs_sensor_added(sc, *so, node, i, &as[i], descr);
423 }
424
425 AcpiOsFree(b.Pointer);
426 return (0);
427 }
428
429 static int
aibs_detach(device_t dev)430 aibs_detach(device_t dev)
431 {
432 struct aibs_softc *sc = device_get_softc(dev);
433
434 if (sc->sc_asens_volt != NULL)
435 free(sc->sc_asens_volt, M_DEVBUF);
436 if (sc->sc_asens_temp != NULL)
437 free(sc->sc_asens_temp, M_DEVBUF);
438 if (sc->sc_asens_fan != NULL)
439 free(sc->sc_asens_fan, M_DEVBUF);
440 if (sc->sc_asens_all != NULL)
441 free(sc->sc_asens_all, M_DEVBUF);
442 return (0);
443 }
444
445 #ifdef AIBS_VERBOSE
446 #define ddevice_printf(x...) device_printf(x)
447 #else
448 #define ddevice_printf(x...)
449 #endif
450
451 static int
aibs_sysctl(SYSCTL_HANDLER_ARGS)452 aibs_sysctl(SYSCTL_HANDLER_ARGS)
453 {
454 struct aibs_softc *sc = arg1;
455 struct aibs_sensor *sensor = (void *)(intptr_t)arg2;
456 int i = oidp->oid_number;
457 ACPI_STATUS rs;
458 ACPI_OBJECT p, *bp;
459 ACPI_OBJECT_LIST mp;
460 ACPI_BUFFER b;
461 char *name;
462 ACPI_INTEGER v, l, h;
463 int so[3];
464
465 switch (sensor->t) {
466 case AIBS_SENS_TYPE_VOLT:
467 name = "RVLT";
468 break;
469 case AIBS_SENS_TYPE_TEMP:
470 name = "RTMP";
471 break;
472 case AIBS_SENS_TYPE_FAN:
473 name = "RFAN";
474 break;
475 default:
476 return (ENOENT);
477 }
478 l = sensor->l;
479 h = sensor->h;
480 p.Type = ACPI_TYPE_INTEGER;
481 p.Integer.Value = sensor->i;
482 mp.Count = 1;
483 mp.Pointer = &p;
484 b.Length = ACPI_ALLOCATE_BUFFER;
485 ACPI_SERIAL_BEGIN(aibs);
486 rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
487 ACPI_TYPE_INTEGER);
488 if (ACPI_FAILURE(rs)) {
489 ddevice_printf(sc->sc_dev,
490 "%s: %i: evaluation failed\n",
491 name, i);
492 ACPI_SERIAL_END(aibs);
493 return (EIO);
494 }
495 bp = b.Pointer;
496 v = bp->Integer.Value;
497 AcpiOsFree(b.Pointer);
498 ACPI_SERIAL_END(aibs);
499
500 switch (sensor->t) {
501 case AIBS_SENS_TYPE_VOLT:
502 break;
503 case AIBS_SENS_TYPE_TEMP:
504 v += 2731;
505 l += 2731;
506 h += 2731;
507 break;
508 case AIBS_SENS_TYPE_FAN:
509 break;
510 }
511 so[0] = v;
512 so[1] = l;
513 so[2] = h;
514 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
515 }
516
517 static int
aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS)518 aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS)
519 {
520 struct aibs_softc *sc = arg1;
521 struct aibs_sensor *sensor = (void *)(intptr_t)arg2;
522 ACPI_STATUS rs;
523 ACPI_OBJECT p, *bp;
524 ACPI_OBJECT_LIST arg;
525 ACPI_BUFFER buf;
526 ACPI_INTEGER v, l, h;
527 int so[3];
528 uint32_t *ret;
529 uint32_t cmd[3];
530
531 cmd[0] = sensor->i;
532 cmd[1] = 0;
533 cmd[2] = 0;
534 p.Type = ACPI_TYPE_BUFFER;
535 p.Buffer.Pointer = (void *)cmd;
536 p.Buffer.Length = sizeof(cmd);
537 arg.Count = 1;
538 arg.Pointer = &p;
539 buf.Pointer = NULL;
540 buf.Length = ACPI_ALLOCATE_BUFFER;
541 ACPI_SERIAL_BEGIN(aibs);
542 rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf,
543 ACPI_TYPE_BUFFER);
544 ACPI_SERIAL_END(aibs);
545 if (ACPI_FAILURE(rs)) {
546 device_printf(sc->sc_dev, "GITM evaluation failed\n");
547 return (EIO);
548 }
549 bp = buf.Pointer;
550 if (bp->Buffer.Length < 8) {
551 device_printf(sc->sc_dev, "GITM returned short buffer\n");
552 return (EIO);
553 }
554 ret = (uint32_t *)bp->Buffer.Pointer;
555 if (ret[0] == 0) {
556 device_printf(sc->sc_dev, "GITM returned error status\n");
557 return (EINVAL);
558 }
559 v = ret[1];
560 AcpiOsFree(buf.Pointer);
561
562 l = sensor->l;
563 h = sensor->h;
564
565 switch (sensor->t) {
566 case AIBS_SENS_TYPE_VOLT:
567 break;
568 case AIBS_SENS_TYPE_TEMP:
569 v += 2731;
570 l += 2731;
571 h += 2731;
572 break;
573 case AIBS_SENS_TYPE_FAN:
574 break;
575 }
576 so[0] = v;
577 so[1] = l;
578 so[2] = h;
579 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
580 }
581