]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/acpi_support/atk0110.c
Upgrade Unbound to 1.8.0. More to follow.
[FreeBSD/FreeBSD.git] / sys / dev / acpi_support / atk0110.c
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 __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
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
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
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
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
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
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
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
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
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 }