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 $ */
5 * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org>
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.
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.
20 #include <sys/cdefs.h>
21 __FBSDID("$FreeBSD$");
23 #include <machine/_inttypes.h>
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/kernel.h>
28 #include <sys/module.h>
29 #include <sys/malloc.h>
30 #include <sys/sysctl.h>
31 #include <sys/stdint.h>
33 #include <contrib/dev/acpica/include/acpi.h>
34 #include <dev/acpica/acpivar.h>
37 * ASUSTeK AI Booster (ACPI ASOC ATK0110).
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).
45 * -- Constantine A. Murenin <http://cnst.su/>
48 #define _COMPONENT ACPI_OEM
49 ACPI_MODULE_NAME("aibs");
50 ACPI_SERIAL_DECL(aibs, "aibs");
52 #define AIBS_MORE_SENSORS
55 #define AIBS_GROUP_SENSORS 0x06
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
62 #define AIBS_SENS_TYPE_VOLT_NAME "volt"
63 #define AIBS_SENS_TYPE_VOLT_TEMP "temp"
64 #define AIBS_SENS_TYPE_VOLT_FAN "fan"
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;
83 struct sysctl_oid *sc_volt_sysctl;
84 struct sysctl_oid *sc_temp_sysctl;
85 struct sysctl_oid *sc_fan_sysctl;
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);
96 static int aibs_attach_ggrp(struct aibs_softc *);
97 static int aibs_attach_sif(struct aibs_softc *, int);
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),
106 static driver_t aibs_driver = {
109 sizeof(struct aibs_softc)
112 static devclass_t aibs_devclass;
114 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
115 MODULE_DEPEND(aibs, acpi, 1, 1, 1);
117 static char* aibs_hids[] = {
123 aibs_probe(device_t dev)
127 if (acpi_disabled("aibs"))
129 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids, NULL);
131 device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
136 aibs_attach(device_t dev)
138 struct aibs_softc *sc = device_get_softc(dev);
142 sc->sc_ah = acpi_get_handle(dev);
144 sc->sc_ggrp_method = false;
145 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_VOLT);
147 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_TEMP);
149 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_FAN);
154 /* Clean up whatever was allocated earlier. */
155 if (sc->sc_volt_sysctl != NULL)
156 sysctl_remove_oid(sc->sc_volt_sysctl, true, true);
157 if (sc->sc_temp_sysctl != NULL)
158 sysctl_remove_oid(sc->sc_temp_sysctl, true, true);
159 if (sc->sc_fan_sysctl != NULL)
160 sysctl_remove_oid(sc->sc_fan_sysctl, true, true);
163 sc->sc_ggrp_method = true;
164 err = aibs_attach_ggrp(sc);
169 aibs_add_sensor(struct aibs_softc *sc, ACPI_OBJECT *o,
170 struct aibs_sensor* sensor, const char ** descr)
175 * Packages for the old and new methods are quite
176 * similar except that the new package has two
177 * new (unknown / unused) fields after the name field.
179 if (sc->sc_ggrp_method)
184 if (o->Type != ACPI_TYPE_PACKAGE) {
185 device_printf(sc->sc_dev,
186 "sensor object is not a package: %i type\n",
190 if (o[0].Package.Count != (off + 3) ||
191 o->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
192 o->Package.Elements[1].Type != ACPI_TYPE_STRING ||
193 o->Package.Elements[off].Type != ACPI_TYPE_INTEGER ||
194 o->Package.Elements[off + 1].Type != ACPI_TYPE_INTEGER ||
195 o->Package.Elements[off + 2].Type != ACPI_TYPE_INTEGER) {
196 device_printf(sc->sc_dev, "unexpected package content\n");
200 sensor->i = o->Package.Elements[0].Integer.Value;
201 *descr = o->Package.Elements[1].String.Pointer;
202 sensor->l = o->Package.Elements[off].Integer.Value;
203 sensor->h = o->Package.Elements[off + 1].Integer.Value;
204 /* For the new method the second value is a range size. */
205 if (sc->sc_ggrp_method)
206 sensor->h += sensor->l;
207 sensor->t = AIBS_SENS_TYPE(sensor->i);
210 case AIBS_SENS_TYPE_VOLT:
211 case AIBS_SENS_TYPE_TEMP:
212 case AIBS_SENS_TYPE_FAN:
215 device_printf(sc->sc_dev, "unknown sensor type 0x%x",
222 aibs_sensor_added(struct aibs_softc *sc, struct sysctl_oid *so,
223 const char *type_name, int idx, struct aibs_sensor *sensor,
228 snprintf(sysctl_name, sizeof(sysctl_name), "%i", idx);
230 device_printf(sc->sc_dev, "%c%i: 0x%08jx %20s %5jd / %5jd\n",
232 (uintmax_t)sensor->i, descr, (intmax_t)sensor->l,
233 (intmax_t)sensor->h);
235 SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
236 SYSCTL_CHILDREN(so), idx, sysctl_name,
237 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, (uintptr_t)sensor,
238 sc->sc_ggrp_method ? aibs_sysctl_ggrp : aibs_sysctl,
239 sensor->t == AIBS_SENS_TYPE_TEMP ? "IK" : "I", descr);
243 aibs_attach_ggrp(struct aibs_softc *sc)
250 ACPI_OBJECT_LIST arg;
257 struct aibs_sensor *sensor;
258 struct sysctl_oid **so;
260 /* First see if GITM is available. */
261 s = AcpiGetHandle(sc->sc_ah, "GITM", &h);
262 if (ACPI_FAILURE(s)) {
264 device_printf(sc->sc_dev, "GITM not found\n");
269 * Now call GGRP with the appropriate argument to list sensors.
270 * The method lists different groups of entities depending on
273 id.Integer.Value = AIBS_GROUP_SENSORS;
274 id.Type = ACPI_TYPE_INTEGER;
277 buf.Length = ACPI_ALLOCATE_BUFFER;
279 s = AcpiEvaluateObjectTyped(sc->sc_ah, "GGRP", &arg, &buf,
281 if (ACPI_FAILURE(s)) {
282 device_printf(sc->sc_dev, "GGRP not found\n");
287 sc->sc_asens_all = malloc(sizeof(*sc->sc_asens_all) * bp->Package.Count,
288 M_DEVBUF, M_WAITOK | M_ZERO);
290 for (i = 0; i < bp->Package.Count; i++) {
291 sensor = &sc->sc_asens_all[i];
292 err = aibs_add_sensor(sc, &bp->Package.Elements[i], sensor,
298 case AIBS_SENS_TYPE_VOLT:
300 so = &sc->sc_volt_sysctl;
303 case AIBS_SENS_TYPE_TEMP:
305 so = &sc->sc_temp_sysctl;
308 case AIBS_SENS_TYPE_FAN:
310 so = &sc->sc_fan_sysctl;
314 panic("add_sensor succeeded for unknown sensor type %d",
319 /* sysctl subtree for sensors of this type */
320 *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
321 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)),
322 sensor->t, name, CTLFLAG_RD | CTLFLAG_MPSAFE,
325 aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr);
329 AcpiOsFree(buf.Pointer);
334 aibs_attach_sif(struct aibs_softc *sc, int st)
336 char name[] = "?SIF";
341 struct aibs_sensor *as;
342 struct sysctl_oid **so;
347 case AIBS_SENS_TYPE_VOLT:
350 so = &sc->sc_volt_sysctl;
352 case AIBS_SENS_TYPE_TEMP:
355 so = &sc->sc_temp_sysctl;
357 case AIBS_SENS_TYPE_FAN:
360 so = &sc->sc_fan_sysctl;
363 panic("Unsupported sensor type %d", st);
366 b.Length = ACPI_ALLOCATE_BUFFER;
367 s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
369 if (ACPI_FAILURE(s)) {
370 device_printf(sc->sc_dev, "%s not found\n", name);
375 o = bp->Package.Elements;
376 if (o[0].Type != ACPI_TYPE_INTEGER) {
377 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
378 AcpiOsFree(b.Pointer);
382 n = o[0].Integer.Value;
383 if (bp->Package.Count - 1 < n) {
384 device_printf(sc->sc_dev, "%s: invalid package\n", name);
385 AcpiOsFree(b.Pointer);
387 } else if (bp->Package.Count - 1 > n) {
390 #ifdef AIBS_MORE_SENSORS
391 n = bp->Package.Count - 1;
393 device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
394 ", assume %i\n", name, on, bp->Package.Count - 1, n);
397 device_printf(sc->sc_dev, "%s: no members in the package\n",
399 AcpiOsFree(b.Pointer);
403 as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO);
405 case AIBS_SENS_TYPE_VOLT:
406 sc->sc_asens_volt = as;
408 case AIBS_SENS_TYPE_TEMP:
409 sc->sc_asens_temp = as;
411 case AIBS_SENS_TYPE_FAN:
412 sc->sc_asens_fan = as;
416 /* sysctl subtree for sensors of this type */
417 *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
418 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
419 node, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, NULL);
421 for (i = 0, o++; i < n; i++, o++) {
424 err = aibs_add_sensor(sc, o, &as[i], &descr);
426 aibs_sensor_added(sc, *so, node, i, &as[i], descr);
429 AcpiOsFree(b.Pointer);
434 aibs_detach(device_t dev)
436 struct aibs_softc *sc = device_get_softc(dev);
438 if (sc->sc_asens_volt != NULL)
439 free(sc->sc_asens_volt, M_DEVBUF);
440 if (sc->sc_asens_temp != NULL)
441 free(sc->sc_asens_temp, M_DEVBUF);
442 if (sc->sc_asens_fan != NULL)
443 free(sc->sc_asens_fan, M_DEVBUF);
444 if (sc->sc_asens_all != NULL)
445 free(sc->sc_asens_all, M_DEVBUF);
450 #define ddevice_printf(x...) device_printf(x)
452 #define ddevice_printf(x...)
456 aibs_sysctl(SYSCTL_HANDLER_ARGS)
458 struct aibs_softc *sc = arg1;
459 struct aibs_sensor *sensor = (void *)(intptr_t)arg2;
460 int i = oidp->oid_number;
466 ACPI_INTEGER v, l, h;
470 case AIBS_SENS_TYPE_VOLT:
473 case AIBS_SENS_TYPE_TEMP:
476 case AIBS_SENS_TYPE_FAN:
484 p.Type = ACPI_TYPE_INTEGER;
485 p.Integer.Value = sensor->i;
488 b.Length = ACPI_ALLOCATE_BUFFER;
489 ACPI_SERIAL_BEGIN(aibs);
490 rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
492 if (ACPI_FAILURE(rs)) {
493 ddevice_printf(sc->sc_dev,
494 "%s: %i: evaluation failed\n",
496 ACPI_SERIAL_END(aibs);
500 v = bp->Integer.Value;
501 AcpiOsFree(b.Pointer);
502 ACPI_SERIAL_END(aibs);
505 case AIBS_SENS_TYPE_VOLT:
507 case AIBS_SENS_TYPE_TEMP:
512 case AIBS_SENS_TYPE_FAN:
518 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
522 aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS)
524 struct aibs_softc *sc = arg1;
525 struct aibs_sensor *sensor = (void *)(intptr_t)arg2;
528 ACPI_OBJECT_LIST arg;
530 ACPI_INTEGER v, l, h;
538 p.Type = ACPI_TYPE_BUFFER;
539 p.Buffer.Pointer = (void *)cmd;
540 p.Buffer.Length = sizeof(cmd);
544 buf.Length = ACPI_ALLOCATE_BUFFER;
545 ACPI_SERIAL_BEGIN(aibs);
546 rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf,
548 ACPI_SERIAL_END(aibs);
549 if (ACPI_FAILURE(rs)) {
550 device_printf(sc->sc_dev, "GITM evaluation failed\n");
554 if (bp->Buffer.Length < 8) {
555 device_printf(sc->sc_dev, "GITM returned short buffer\n");
558 ret = (uint32_t *)bp->Buffer.Pointer;
560 device_printf(sc->sc_dev, "GITM returned error status\n");
564 AcpiOsFree(buf.Pointer);
570 case AIBS_SENS_TYPE_VOLT:
572 case AIBS_SENS_TYPE_TEMP:
577 case AIBS_SENS_TYPE_FAN:
583 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));