]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/acpi_support/atk0110.c
[arm64] Parse ACPI _PXM property on ARM64 platform
[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 | 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);
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 | CTLFLAG_MPSAFE,
323                             NULL, NULL);
324                 }
325                 aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr);
326                 *s_idx += 1;
327         }
328
329         AcpiOsFree(buf.Pointer);
330         return (0);
331 }
332
333 static int
334 aibs_attach_sif(struct aibs_softc *sc, int st)
335 {
336         char                    name[] = "?SIF";
337         ACPI_STATUS             s;
338         ACPI_BUFFER             b;
339         ACPI_OBJECT             *bp, *o;
340         const char              *node;
341         struct aibs_sensor      *as;
342         struct sysctl_oid       **so;
343         int                     i, n;
344         int err;
345
346         switch (st) {
347         case AIBS_SENS_TYPE_VOLT:
348                 node = "volt";
349                 name[0] = 'V';
350                 so = &sc->sc_volt_sysctl;
351                 break;
352         case AIBS_SENS_TYPE_TEMP:
353                 node = "temp";
354                 name[0] = 'T';
355                 so = &sc->sc_temp_sysctl;
356                 break;
357         case AIBS_SENS_TYPE_FAN:
358                 node = "fan";
359                 name[0] = 'F';
360                 so = &sc->sc_fan_sysctl;
361                 break;
362         default:
363                 panic("Unsupported sensor type %d", st);
364         }
365
366         b.Length = ACPI_ALLOCATE_BUFFER;
367         s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
368             ACPI_TYPE_PACKAGE);
369         if (ACPI_FAILURE(s)) {
370                 device_printf(sc->sc_dev, "%s not found\n", name);
371                 return (ENXIO);
372         }
373
374         bp = b.Pointer;
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);
379                 return (ENXIO);
380         }
381
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);
386                 return (ENXIO);
387         } else if (bp->Package.Count - 1 > n) {
388                 int on = n;
389
390 #ifdef AIBS_MORE_SENSORS
391                 n = bp->Package.Count - 1;
392 #endif
393                 device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
394                     ", assume %i\n", name, on, bp->Package.Count - 1, n);
395         }
396         if (n < 1) {
397                 device_printf(sc->sc_dev, "%s: no members in the package\n",
398                     name);
399                 AcpiOsFree(b.Pointer);
400                 return (ENXIO);
401         }
402
403         as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO);
404         switch (st) {
405         case AIBS_SENS_TYPE_VOLT:
406                 sc->sc_asens_volt = as;
407                 break;
408         case AIBS_SENS_TYPE_TEMP:
409                 sc->sc_asens_temp = as;
410                 break;
411         case AIBS_SENS_TYPE_FAN:
412                 sc->sc_asens_fan = as;
413                 break;
414         }
415
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);
420
421         for (i = 0, o++; i < n; i++, o++) {
422                 const char      *descr;
423
424                 err = aibs_add_sensor(sc, o, &as[i], &descr);
425                 if (err == 0)
426                         aibs_sensor_added(sc, *so, node, i, &as[i], descr);
427         }
428
429         AcpiOsFree(b.Pointer);
430         return (0);
431 }
432
433 static int
434 aibs_detach(device_t dev)
435 {
436         struct aibs_softc       *sc = device_get_softc(dev);
437
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);
446         return (0);
447 }
448
449 #ifdef AIBS_VERBOSE
450 #define ddevice_printf(x...) device_printf(x)
451 #else
452 #define ddevice_printf(x...)
453 #endif
454
455 static int
456 aibs_sysctl(SYSCTL_HANDLER_ARGS)
457 {
458         struct aibs_softc       *sc = arg1;
459         struct aibs_sensor      *sensor = (void *)(intptr_t)arg2;
460         int                     i = oidp->oid_number;
461         ACPI_STATUS             rs;
462         ACPI_OBJECT             p, *bp;
463         ACPI_OBJECT_LIST        mp;
464         ACPI_BUFFER             b;
465         char                    *name;
466         ACPI_INTEGER            v, l, h;
467         int                     so[3];
468
469         switch (sensor->t) {
470         case AIBS_SENS_TYPE_VOLT:
471                 name = "RVLT";
472                 break;
473         case AIBS_SENS_TYPE_TEMP:
474                 name = "RTMP";
475                 break;
476         case AIBS_SENS_TYPE_FAN:
477                 name = "RFAN";
478                 break;
479         default:
480                 return (ENOENT);
481         }
482         l = sensor->l;
483         h = sensor->h;
484         p.Type = ACPI_TYPE_INTEGER;
485         p.Integer.Value = sensor->i;
486         mp.Count = 1;
487         mp.Pointer = &p;
488         b.Length = ACPI_ALLOCATE_BUFFER;
489         ACPI_SERIAL_BEGIN(aibs);
490         rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
491             ACPI_TYPE_INTEGER);
492         if (ACPI_FAILURE(rs)) {
493                 ddevice_printf(sc->sc_dev,
494                     "%s: %i: evaluation failed\n",
495                     name, i);
496                 ACPI_SERIAL_END(aibs);
497                 return (EIO);
498         }
499         bp = b.Pointer;
500         v = bp->Integer.Value;
501         AcpiOsFree(b.Pointer);
502         ACPI_SERIAL_END(aibs);
503
504         switch (sensor->t) {
505         case AIBS_SENS_TYPE_VOLT:
506                 break;
507         case AIBS_SENS_TYPE_TEMP:
508                 v += 2731;
509                 l += 2731;
510                 h += 2731;
511                 break;
512         case AIBS_SENS_TYPE_FAN:
513                 break;
514         }
515         so[0] = v;
516         so[1] = l;
517         so[2] = h;
518         return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
519 }
520
521 static int
522 aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS)
523 {
524         struct aibs_softc       *sc = arg1;
525         struct aibs_sensor      *sensor = (void *)(intptr_t)arg2;
526         ACPI_STATUS             rs;
527         ACPI_OBJECT             p, *bp;
528         ACPI_OBJECT_LIST        arg;
529         ACPI_BUFFER             buf;
530         ACPI_INTEGER            v, l, h;
531         int                     so[3];
532         uint32_t                *ret;
533         uint32_t                cmd[3];
534
535         cmd[0] = sensor->i;
536         cmd[1] = 0;
537         cmd[2] = 0;
538         p.Type = ACPI_TYPE_BUFFER;
539         p.Buffer.Pointer = (void *)cmd;
540         p.Buffer.Length = sizeof(cmd);
541         arg.Count = 1;
542         arg.Pointer = &p;
543         buf.Pointer = NULL;
544         buf.Length = ACPI_ALLOCATE_BUFFER;
545         ACPI_SERIAL_BEGIN(aibs);
546         rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf,
547             ACPI_TYPE_BUFFER);
548         ACPI_SERIAL_END(aibs);
549         if (ACPI_FAILURE(rs)) {
550                 device_printf(sc->sc_dev, "GITM evaluation failed\n");
551                 return (EIO);
552         }
553         bp = buf.Pointer;
554         if (bp->Buffer.Length < 8) {
555                 device_printf(sc->sc_dev, "GITM returned short buffer\n");
556                 return (EIO);
557         }
558         ret = (uint32_t *)bp->Buffer.Pointer;
559         if (ret[0] == 0) {
560                 device_printf(sc->sc_dev, "GITM returned error status\n");
561                 return (EINVAL);
562         }
563         v = ret[1];
564         AcpiOsFree(buf.Pointer);
565
566         l = sensor->l;
567         h = sensor->h;
568
569         switch (sensor->t) {
570         case AIBS_SENS_TYPE_VOLT:
571                 break;
572         case AIBS_SENS_TYPE_TEMP:
573                 v += 2731;
574                 l += 2731;
575                 h += 2731;
576                 break;
577         case AIBS_SENS_TYPE_FAN:
578                 break;
579         }
580         so[0] = v;
581         so[1] = l;
582         so[2] = h;
583         return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
584 }