]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/acpi_support/atk0110.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
32 #include <contrib/dev/acpica/include/acpi.h>
33 #include <dev/acpica/acpivar.h>
34
35 /*
36  * ASUSTeK AI Booster (ACPI ASOC ATK0110).
37  *
38  * This code was originally written for OpenBSD after the techniques
39  * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's
40  * acpi_aiboost.c were verified to be accurate on the actual hardware kindly
41  * provided by Sam Fourman Jr.  It was subsequently ported from OpenBSD to
42  * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9).
43  *
44  *                                -- Constantine A. Murenin <http://cnst.su/>
45  */
46
47 #define _COMPONENT      ACPI_OEM
48 ACPI_MODULE_NAME("aibs");
49 ACPI_SERIAL_DECL(aibs, "aibs");
50
51 #define AIBS_MORE_SENSORS
52 #define AIBS_VERBOSE
53
54 enum aibs_type {
55         AIBS_VOLT,
56         AIBS_TEMP,
57         AIBS_FAN
58 };
59
60 struct aibs_sensor {
61         ACPI_INTEGER    v;
62         ACPI_INTEGER    i;
63         ACPI_INTEGER    l;
64         ACPI_INTEGER    h;
65         enum aibs_type  t;
66 };
67
68 struct aibs_softc {
69         struct device           *sc_dev;
70         ACPI_HANDLE             sc_ah;
71
72         struct aibs_sensor      *sc_asens_volt;
73         struct aibs_sensor      *sc_asens_temp;
74         struct aibs_sensor      *sc_asens_fan;
75 };
76
77 static int aibs_probe(device_t);
78 static int aibs_attach(device_t);
79 static int aibs_detach(device_t);
80 static int aibs_sysctl(SYSCTL_HANDLER_ARGS);
81
82 static void aibs_attach_sif(struct aibs_softc *, enum aibs_type);
83
84 static device_method_t aibs_methods[] = {
85         DEVMETHOD(device_probe,         aibs_probe),
86         DEVMETHOD(device_attach,        aibs_attach),
87         DEVMETHOD(device_detach,        aibs_detach),
88         { NULL, NULL }
89 };
90
91 static driver_t aibs_driver = {
92         "aibs",
93         aibs_methods,
94         sizeof(struct aibs_softc)
95 };
96
97 static devclass_t aibs_devclass;
98
99 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
100 MODULE_DEPEND(aibs, acpi, 1, 1, 1);
101
102 static char* aibs_hids[] = {
103         "ATK0110",
104         NULL
105 };
106
107 static int
108 aibs_probe(device_t dev)
109 {
110         if (acpi_disabled("aibs") ||
111             ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
112                 return ENXIO;
113
114         device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
115         return 0;
116 }
117
118 static int
119 aibs_attach(device_t dev)
120 {
121         struct aibs_softc *sc = device_get_softc(dev);
122
123         sc->sc_dev = dev;
124         sc->sc_ah = acpi_get_handle(dev);
125
126         aibs_attach_sif(sc, AIBS_VOLT);
127         aibs_attach_sif(sc, AIBS_TEMP);
128         aibs_attach_sif(sc, AIBS_FAN);
129
130         return 0;
131 }
132
133 static void
134 aibs_attach_sif(struct aibs_softc *sc, enum aibs_type st)
135 {
136         ACPI_STATUS             s;
137         ACPI_BUFFER             b;
138         ACPI_OBJECT             *bp, *o;
139         int                     i, n;
140         const char              *node;
141         char                    name[] = "?SIF";
142         struct aibs_sensor      *as;
143         struct sysctl_oid       *so;
144
145         switch (st) {
146         case AIBS_VOLT:
147                 node = "volt";
148                 name[0] = 'V';
149                 break;
150         case AIBS_TEMP:
151                 node = "temp";
152                 name[0] = 'T';
153                 break;
154         case AIBS_FAN:
155                 node = "fan";
156                 name[0] = 'F';
157                 break;
158         default:
159                 return;
160         }
161
162         b.Length = ACPI_ALLOCATE_BUFFER;
163         s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
164             ACPI_TYPE_PACKAGE);
165         if (ACPI_FAILURE(s)) {
166                 device_printf(sc->sc_dev, "%s not found\n", name);
167                 return;
168         }
169
170         bp = b.Pointer;
171         o = bp->Package.Elements;
172         if (o[0].Type != ACPI_TYPE_INTEGER) {
173                 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
174                 AcpiOsFree(b.Pointer);
175                 return;
176         }
177
178         n = o[0].Integer.Value;
179         if (bp->Package.Count - 1 < n) {
180                 device_printf(sc->sc_dev, "%s: invalid package\n", name);
181                 AcpiOsFree(b.Pointer);
182                 return;
183         } else if (bp->Package.Count - 1 > n) {
184                 int on = n;
185
186 #ifdef AIBS_MORE_SENSORS
187                 n = bp->Package.Count - 1;
188 #endif
189                 device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
190                     ", assume %i\n", name, on, bp->Package.Count - 1, n);
191         }
192         if (n < 1) {
193                 device_printf(sc->sc_dev, "%s: no members in the package\n",
194                     name);
195                 AcpiOsFree(b.Pointer);
196                 return;
197         }
198
199         as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
200         if (as == NULL) {
201                 device_printf(sc->sc_dev, "%s: malloc fail\n", name);
202                 AcpiOsFree(b.Pointer);
203                 return;
204         }
205         switch (st) {
206         case AIBS_VOLT:
207                 sc->sc_asens_volt = as;
208                 break;
209         case AIBS_TEMP:
210                 sc->sc_asens_temp = as;
211                 break;
212         case AIBS_FAN:
213                 sc->sc_asens_fan = as;
214                 break;
215         }
216
217         /* sysctl subtree for sensors of this type */
218         so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
219             SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
220             node, CTLFLAG_RD, NULL, NULL);
221
222         for (i = 0, o++; i < n; i++, o++) {
223                 ACPI_OBJECT     *oi;
224                 char            si[3];
225                 const char      *desc;
226
227                 /* acpica5 automatically evaluates the referenced package */
228                 if (o[0].Type != ACPI_TYPE_PACKAGE) {
229                         device_printf(sc->sc_dev,
230                             "%s: %i: not a package: %i type\n",
231                             name, i, o[0].Type);
232                         continue;
233                 }
234                 oi = o[0].Package.Elements;
235                 if (o[0].Package.Count != 5 ||
236                     oi[0].Type != ACPI_TYPE_INTEGER ||
237                     oi[1].Type != ACPI_TYPE_STRING ||
238                     oi[2].Type != ACPI_TYPE_INTEGER ||
239                     oi[3].Type != ACPI_TYPE_INTEGER ||
240                     oi[4].Type != ACPI_TYPE_INTEGER) {
241                         device_printf(sc->sc_dev,
242                             "%s: %i: invalid package\n",
243                             name, i);
244                         continue;
245                 }
246                 as[i].i = oi[0].Integer.Value;
247                 desc = oi[1].String.Pointer;
248                 as[i].l = oi[2].Integer.Value;
249                 as[i].h = oi[3].Integer.Value;
250                 as[i].t = st;
251 #ifdef AIBS_VERBOSE
252                 device_printf(sc->sc_dev, "%c%i: "
253                     "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64"  "
254                     "0x%"PRIx64"\n",
255                     name[0], i,
256                     (uint64_t)as[i].i, desc, (int64_t)as[i].l,
257                     (int64_t)as[i].h, (uint64_t)oi[4].Integer.Value);
258 #endif
259                 snprintf(si, sizeof(si), "%i", i);
260                 SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
261                     SYSCTL_CHILDREN(so), i, si, CTLTYPE_INT | CTLFLAG_RD,
262                     sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc);
263         }
264
265         AcpiOsFree(b.Pointer);
266 }
267
268 static int
269 aibs_detach(device_t dev)
270 {
271         struct aibs_softc       *sc = device_get_softc(dev);
272
273         if (sc->sc_asens_volt != NULL)
274                 free(sc->sc_asens_volt, M_DEVBUF);
275         if (sc->sc_asens_temp != NULL)
276                 free(sc->sc_asens_temp, M_DEVBUF);
277         if (sc->sc_asens_fan != NULL)
278                 free(sc->sc_asens_fan, M_DEVBUF);
279         return 0;
280 }
281
282 #ifdef AIBS_VERBOSE
283 #define ddevice_printf(x...) device_printf(x)
284 #else
285 #define ddevice_printf(x...)
286 #endif
287
288 static int
289 aibs_sysctl(SYSCTL_HANDLER_ARGS)
290 {
291         struct aibs_softc       *sc = arg1;
292         enum aibs_type          st = arg2;
293         int                     i = oidp->oid_number;
294         ACPI_STATUS             rs;
295         ACPI_OBJECT             p, *bp;
296         ACPI_OBJECT_LIST        mp;
297         ACPI_BUFFER             b;
298         char                    *name;
299         struct aibs_sensor      *as;
300         ACPI_INTEGER            v, l, h;
301         int                     so[3];
302
303         switch (st) {
304         case AIBS_VOLT:
305                 name = "RVLT";
306                 as = sc->sc_asens_volt;
307                 break;
308         case AIBS_TEMP:
309                 name = "RTMP";
310                 as = sc->sc_asens_temp;
311                 break;
312         case AIBS_FAN:
313                 name = "RFAN";
314                 as = sc->sc_asens_fan;
315                 break;
316         default:
317                 return ENOENT;
318         }
319         if (as == NULL)
320                 return ENOENT;
321         l = as[i].l;
322         h = as[i].h;
323         p.Type = ACPI_TYPE_INTEGER;
324         p.Integer.Value = as[i].i;
325         mp.Count = 1;
326         mp.Pointer = &p;
327         b.Length = ACPI_ALLOCATE_BUFFER;
328         ACPI_SERIAL_BEGIN(aibs);
329         rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
330             ACPI_TYPE_INTEGER);
331         if (ACPI_FAILURE(rs)) {
332                 ddevice_printf(sc->sc_dev,
333                     "%s: %i: evaluation failed\n",
334                     name, i);
335                 ACPI_SERIAL_END(aibs);
336                 return EIO;
337         }
338         bp = b.Pointer;
339         v = bp->Integer.Value;
340         AcpiOsFree(b.Pointer);
341         ACPI_SERIAL_END(aibs);
342
343         switch (st) {
344         case AIBS_VOLT:
345                 break;
346         case AIBS_TEMP:
347                 v += 2732;
348                 l += 2732;
349                 h += 2732;
350                 break;
351         case AIBS_FAN:
352                 break;
353         }
354         so[0] = v;
355         so[1] = l;
356         so[2] = h;
357         return sysctl_handle_opaque(oidp, &so, sizeof(so), req);
358 }