]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/acpi_support/atk0110.c
MFV r354383: 10592 misc. metaslab and vdev related ZoL bug fixes
[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         int rv;
126
127         if (acpi_disabled("aibs"))
128                 return (ENXIO);
129         rv = ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids, NULL);
130         if (rv <= 0 )
131                 device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
132         return (rv);
133 }
134
135 static int
136 aibs_attach(device_t dev)
137 {
138         struct aibs_softc *sc = device_get_softc(dev);
139         int err;
140
141         sc->sc_dev = dev;
142         sc->sc_ah = acpi_get_handle(dev);
143
144         sc->sc_ggrp_method = false;
145         err = aibs_attach_sif(sc, AIBS_SENS_TYPE_VOLT);
146         if (err == 0)
147                 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_TEMP);
148         if (err == 0)
149                 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_FAN);
150
151         if (err == 0)
152                 return (0);
153
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);
161         aibs_detach(dev);
162
163         sc->sc_ggrp_method = true;
164         err = aibs_attach_ggrp(sc);
165         return (err);
166 }
167
168 static int
169 aibs_add_sensor(struct aibs_softc *sc, ACPI_OBJECT *o,
170     struct aibs_sensor* sensor, const char ** descr)
171 {
172         int             off;
173
174         /*
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.
178          */
179         if (sc->sc_ggrp_method)
180                 off = 4;
181         else
182                 off = 2;
183
184         if (o->Type != ACPI_TYPE_PACKAGE) {
185                 device_printf(sc->sc_dev,
186                     "sensor object is not a package: %i type\n",
187                      o->Type);
188                 return (ENXIO);
189         }
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");
197                 return (ENXIO);
198         }
199
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);
208
209         switch (sensor->t) {
210         case AIBS_SENS_TYPE_VOLT:
211         case AIBS_SENS_TYPE_TEMP:
212         case AIBS_SENS_TYPE_FAN:
213                 return (0);
214         default:
215                 device_printf(sc->sc_dev, "unknown sensor type 0x%x",
216                     sensor->t);
217                 return (ENXIO);
218         }
219 }
220
221 static void
222 aibs_sensor_added(struct aibs_softc *sc, struct sysctl_oid *so,
223     const char *type_name, int idx, struct aibs_sensor *sensor,
224     const char *descr)
225 {
226         char    sysctl_name[8];
227
228         snprintf(sysctl_name, sizeof(sysctl_name), "%i", idx);
229 #ifdef AIBS_VERBOSE
230         device_printf(sc->sc_dev, "%c%i: 0x%08jx %20s %5jd / %5jd\n",
231             type_name[0], idx,
232             (uintmax_t)sensor->i, descr, (intmax_t)sensor->l,
233             (intmax_t)sensor->h);
234 #endif
235         SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
236             SYSCTL_CHILDREN(so), idx, sysctl_name,
237             CTLTYPE_INT | CTLFLAG_RD, sc, (uintptr_t)sensor,
238             sc->sc_ggrp_method ? aibs_sysctl_ggrp : aibs_sysctl,
239             sensor->t == AIBS_SENS_TYPE_TEMP ? "IK" : "I", descr);
240 }
241
242 static int
243 aibs_attach_ggrp(struct aibs_softc *sc)
244 {
245         ACPI_STATUS             s;
246         ACPI_BUFFER             buf;
247         ACPI_HANDLE             h;
248         ACPI_OBJECT             id;
249         ACPI_OBJECT             *bp;
250         ACPI_OBJECT_LIST        arg;
251         int                     i;
252         int                     t, v, f;
253         int                     err;
254         int                     *s_idx;
255         const char              *name;
256         const char              *descr;
257         struct aibs_sensor      *sensor;
258         struct sysctl_oid       **so;
259
260         /* First see if GITM is available. */
261         s = AcpiGetHandle(sc->sc_ah, "GITM", &h);
262         if (ACPI_FAILURE(s)) {
263                 if (bootverbose)
264                         device_printf(sc->sc_dev, "GITM not found\n");
265                 return (ENXIO);
266         }
267
268         /*
269          * Now call GGRP with the appropriate argument to list sensors.
270          * The method lists different groups of entities depending on
271          * the argument.
272          */
273         id.Integer.Value = AIBS_GROUP_SENSORS;
274         id.Type = ACPI_TYPE_INTEGER;
275         arg.Count = 1;
276         arg.Pointer = &id;
277         buf.Length = ACPI_ALLOCATE_BUFFER;
278         buf.Pointer = NULL;
279         s = AcpiEvaluateObjectTyped(sc->sc_ah, "GGRP", &arg, &buf,
280             ACPI_TYPE_PACKAGE);
281         if (ACPI_FAILURE(s)) {
282                 device_printf(sc->sc_dev, "GGRP not found\n");
283                 return (ENXIO);
284         }
285
286         bp = buf.Pointer;
287         sc->sc_asens_all = malloc(sizeof(*sc->sc_asens_all) * bp->Package.Count,
288             M_DEVBUF, M_WAITOK | M_ZERO);
289         v = t = f = 0;
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,
293                     &descr);
294                 if (err != 0)
295                         continue;
296
297                 switch (sensor->t) {
298                 case AIBS_SENS_TYPE_VOLT:
299                         name = "volt";
300                         so = &sc->sc_volt_sysctl;
301                         s_idx = &v;
302                         break;
303                 case AIBS_SENS_TYPE_TEMP:
304                         name = "temp";
305                         so = &sc->sc_temp_sysctl;
306                         s_idx = &t;
307                         break;
308                 case AIBS_SENS_TYPE_FAN:
309                         name = "fan";
310                         so = &sc->sc_fan_sysctl;
311                         s_idx = &f;
312                         break;
313                 default:
314                         panic("add_sensor succeeded for unknown sensor type %d",
315                             sensor->t);
316                 }
317
318                 if (*so == NULL) {
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, NULL, NULL);
323                 }
324                 aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr);
325                 *s_idx += 1;
326         }
327
328         AcpiOsFree(buf.Pointer);
329         return (0);
330 }
331
332 static int
333 aibs_attach_sif(struct aibs_softc *sc, int st)
334 {
335         char                    name[] = "?SIF";
336         ACPI_STATUS             s;
337         ACPI_BUFFER             b;
338         ACPI_OBJECT             *bp, *o;
339         const char              *node;
340         struct aibs_sensor      *as;
341         struct sysctl_oid       **so;
342         int                     i, n;
343         int err;
344
345         switch (st) {
346         case AIBS_SENS_TYPE_VOLT:
347                 node = "volt";
348                 name[0] = 'V';
349                 so = &sc->sc_volt_sysctl;
350                 break;
351         case AIBS_SENS_TYPE_TEMP:
352                 node = "temp";
353                 name[0] = 'T';
354                 so = &sc->sc_temp_sysctl;
355                 break;
356         case AIBS_SENS_TYPE_FAN:
357                 node = "fan";
358                 name[0] = 'F';
359                 so = &sc->sc_fan_sysctl;
360                 break;
361         default:
362                 panic("Unsupported sensor type %d", st);
363         }
364
365         b.Length = ACPI_ALLOCATE_BUFFER;
366         s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
367             ACPI_TYPE_PACKAGE);
368         if (ACPI_FAILURE(s)) {
369                 device_printf(sc->sc_dev, "%s not found\n", name);
370                 return (ENXIO);
371         }
372
373         bp = b.Pointer;
374         o = bp->Package.Elements;
375         if (o[0].Type != ACPI_TYPE_INTEGER) {
376                 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
377                 AcpiOsFree(b.Pointer);
378                 return (ENXIO);
379         }
380
381         n = o[0].Integer.Value;
382         if (bp->Package.Count - 1 < n) {
383                 device_printf(sc->sc_dev, "%s: invalid package\n", name);
384                 AcpiOsFree(b.Pointer);
385                 return (ENXIO);
386         } else if (bp->Package.Count - 1 > n) {
387                 int on = n;
388
389 #ifdef AIBS_MORE_SENSORS
390                 n = bp->Package.Count - 1;
391 #endif
392                 device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
393                     ", assume %i\n", name, on, bp->Package.Count - 1, n);
394         }
395         if (n < 1) {
396                 device_printf(sc->sc_dev, "%s: no members in the package\n",
397                     name);
398                 AcpiOsFree(b.Pointer);
399                 return (ENXIO);
400         }
401
402         as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO);
403         switch (st) {
404         case AIBS_SENS_TYPE_VOLT:
405                 sc->sc_asens_volt = as;
406                 break;
407         case AIBS_SENS_TYPE_TEMP:
408                 sc->sc_asens_temp = as;
409                 break;
410         case AIBS_SENS_TYPE_FAN:
411                 sc->sc_asens_fan = as;
412                 break;
413         }
414
415         /* sysctl subtree for sensors of this type */
416         *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
417             SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
418             node, CTLFLAG_RD, NULL, NULL);
419
420         for (i = 0, o++; i < n; i++, o++) {
421                 const char      *descr;
422
423                 err = aibs_add_sensor(sc, o, &as[i], &descr);
424                 if (err == 0)
425                         aibs_sensor_added(sc, *so, node, i, &as[i], descr);
426         }
427
428         AcpiOsFree(b.Pointer);
429         return (0);
430 }
431
432 static int
433 aibs_detach(device_t dev)
434 {
435         struct aibs_softc       *sc = device_get_softc(dev);
436
437         if (sc->sc_asens_volt != NULL)
438                 free(sc->sc_asens_volt, M_DEVBUF);
439         if (sc->sc_asens_temp != NULL)
440                 free(sc->sc_asens_temp, M_DEVBUF);
441         if (sc->sc_asens_fan != NULL)
442                 free(sc->sc_asens_fan, M_DEVBUF);
443         if (sc->sc_asens_all != NULL)
444                 free(sc->sc_asens_all, M_DEVBUF);
445         return (0);
446 }
447
448 #ifdef AIBS_VERBOSE
449 #define ddevice_printf(x...) device_printf(x)
450 #else
451 #define ddevice_printf(x...)
452 #endif
453
454 static int
455 aibs_sysctl(SYSCTL_HANDLER_ARGS)
456 {
457         struct aibs_softc       *sc = arg1;
458         struct aibs_sensor      *sensor = (void *)(intptr_t)arg2;
459         int                     i = oidp->oid_number;
460         ACPI_STATUS             rs;
461         ACPI_OBJECT             p, *bp;
462         ACPI_OBJECT_LIST        mp;
463         ACPI_BUFFER             b;
464         char                    *name;
465         ACPI_INTEGER            v, l, h;
466         int                     so[3];
467
468         switch (sensor->t) {
469         case AIBS_SENS_TYPE_VOLT:
470                 name = "RVLT";
471                 break;
472         case AIBS_SENS_TYPE_TEMP:
473                 name = "RTMP";
474                 break;
475         case AIBS_SENS_TYPE_FAN:
476                 name = "RFAN";
477                 break;
478         default:
479                 return (ENOENT);
480         }
481         l = sensor->l;
482         h = sensor->h;
483         p.Type = ACPI_TYPE_INTEGER;
484         p.Integer.Value = sensor->i;
485         mp.Count = 1;
486         mp.Pointer = &p;
487         b.Length = ACPI_ALLOCATE_BUFFER;
488         ACPI_SERIAL_BEGIN(aibs);
489         rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
490             ACPI_TYPE_INTEGER);
491         if (ACPI_FAILURE(rs)) {
492                 ddevice_printf(sc->sc_dev,
493                     "%s: %i: evaluation failed\n",
494                     name, i);
495                 ACPI_SERIAL_END(aibs);
496                 return (EIO);
497         }
498         bp = b.Pointer;
499         v = bp->Integer.Value;
500         AcpiOsFree(b.Pointer);
501         ACPI_SERIAL_END(aibs);
502
503         switch (sensor->t) {
504         case AIBS_SENS_TYPE_VOLT:
505                 break;
506         case AIBS_SENS_TYPE_TEMP:
507                 v += 2731;
508                 l += 2731;
509                 h += 2731;
510                 break;
511         case AIBS_SENS_TYPE_FAN:
512                 break;
513         }
514         so[0] = v;
515         so[1] = l;
516         so[2] = h;
517         return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
518 }
519
520 static int
521 aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS)
522 {
523         struct aibs_softc       *sc = arg1;
524         struct aibs_sensor      *sensor = (void *)(intptr_t)arg2;
525         ACPI_STATUS             rs;
526         ACPI_OBJECT             p, *bp;
527         ACPI_OBJECT_LIST        arg;
528         ACPI_BUFFER             buf;
529         ACPI_INTEGER            v, l, h;
530         int                     so[3];
531         uint32_t                *ret;
532         uint32_t                cmd[3];
533
534         cmd[0] = sensor->i;
535         cmd[1] = 0;
536         cmd[2] = 0;
537         p.Type = ACPI_TYPE_BUFFER;
538         p.Buffer.Pointer = (void *)cmd;
539         p.Buffer.Length = sizeof(cmd);
540         arg.Count = 1;
541         arg.Pointer = &p;
542         buf.Pointer = NULL;
543         buf.Length = ACPI_ALLOCATE_BUFFER;
544         ACPI_SERIAL_BEGIN(aibs);
545         rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf,
546             ACPI_TYPE_BUFFER);
547         ACPI_SERIAL_END(aibs);
548         if (ACPI_FAILURE(rs)) {
549                 device_printf(sc->sc_dev, "GITM evaluation failed\n");
550                 return (EIO);
551         }
552         bp = buf.Pointer;
553         if (bp->Buffer.Length < 8) {
554                 device_printf(sc->sc_dev, "GITM returned short buffer\n");
555                 return (EIO);
556         }
557         ret = (uint32_t *)bp->Buffer.Pointer;
558         if (ret[0] == 0) {
559                 device_printf(sc->sc_dev, "GITM returned error status\n");
560                 return (EINVAL);
561         }
562         v = ret[1];
563         AcpiOsFree(buf.Pointer);
564
565         l = sensor->l;
566         h = sensor->h;
567
568         switch (sensor->t) {
569         case AIBS_SENS_TYPE_VOLT:
570                 break;
571         case AIBS_SENS_TYPE_TEMP:
572                 v += 2731;
573                 l += 2731;
574                 h += 2731;
575                 break;
576         case AIBS_SENS_TYPE_FAN:
577                 break;
578         }
579         so[0] = v;
580         so[1] = l;
581         so[2] = h;
582         return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
583 }