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