9a2978293aaa415179055335b962c59951b1c0b6
[dragonfly.git] / sys / dev / acpica5 / aibs / atk0110.c
1 /*      $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $        */
2
3 /*
4  * Copyright (c) 2009 Constantine A. Murenin <cnst+dfly@bugmail.mojo.ru>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <sys/cdefs.h>
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/kernel.h>
23 #include <sys/bus.h>
24 #include <sys/module.h>
25 #include <sys/malloc.h>
26
27 #include <sys/sensors.h>
28
29 #include "acpi.h"
30 #include "acpivar.h"
31
32 /*
33  * ASUSTeK AI Booster (ACPI ATK0110).
34  *
35  * This code was originally written for OpenBSD after the techniques
36  * described in the Linux's asus_atk0110.c and FreeBSD's acpi_aiboost.c
37  * were verified to be accurate on the actual hardware kindly provided by
38  * Sam Fourman Jr.  It was subsequently ported from OpenBSD to DragonFly BSD.
39  *
40  *                                -- Constantine A. Murenin <http://cnst.su/>
41  */
42
43 #define AIBS_MORE_SENSORS
44 #define AIBS_VERBOSE
45
46 struct aibs_sensor {
47         struct ksensor  s;
48         int64_t         i;
49         int64_t         l;
50         int64_t         h;
51 };
52
53 struct aibs_softc {
54         struct device           *sc_dev;
55         ACPI_HANDLE             sc_ah;
56
57         struct aibs_sensor      *sc_asens_volt;
58         struct aibs_sensor      *sc_asens_temp;
59         struct aibs_sensor      *sc_asens_fan;
60
61         struct ksensordev       sc_sensordev;
62 };
63
64
65 static int aibs_probe(struct device *);
66 static int aibs_attach(struct device *);
67 static int aibs_detach(struct device *);
68 static void aibs_refresh(void *);
69
70 static void aibs_attach_sif(struct aibs_softc *, enum sensor_type);
71 static void aibs_refresh_r(struct aibs_softc *, enum sensor_type);
72
73
74 static device_method_t aibs_methods[] = {
75         DEVMETHOD(device_probe,         aibs_probe),
76         DEVMETHOD(device_attach,        aibs_attach),
77         DEVMETHOD(device_detach,        aibs_detach),
78         { NULL, NULL }
79 };
80
81 static driver_t aibs_driver = {
82         "aibs",
83         aibs_methods,
84         sizeof(struct aibs_softc)
85 };
86
87 static devclass_t aibs_devclass;
88
89 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
90
91
92 static char* aibs_hids[] = {
93         "ATK0110",
94         NULL
95 };
96
97 static int
98 aibs_probe(struct device *dev)
99 {
100
101         if (acpi_disabled("aibs") ||
102             ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
103                 return ENXIO;
104
105         device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
106         return 0;
107 }
108
109 static int
110 aibs_attach(struct device *dev)
111 {
112         struct aibs_softc       *sc;
113
114         sc = device_get_softc(dev);
115         sc->sc_dev = dev;
116         sc->sc_ah = acpi_get_handle(dev);
117
118         strlcpy(sc->sc_sensordev.xname, device_get_nameunit(dev),
119             sizeof(sc->sc_sensordev.xname));
120
121         aibs_attach_sif(sc, SENSOR_VOLTS_DC);
122         aibs_attach_sif(sc, SENSOR_TEMP);
123         aibs_attach_sif(sc, SENSOR_FANRPM);
124
125         if (sc->sc_sensordev.sensors_count == 0) {
126                 device_printf(dev, "no sensors found\n");
127                 return ENXIO;
128         }
129
130         if (sensor_task_register(sc, aibs_refresh, 5)) {
131                 device_printf(dev, "unable to register update task\n");
132                 return ENXIO;
133         }
134
135         sensordev_install(&sc->sc_sensordev);
136         return 0;
137 }
138
139 static void
140 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
141 {
142         ACPI_STATUS             s;
143         ACPI_BUFFER             b;
144         ACPI_OBJECT             *bp, *o;
145         int                     i, n;
146         char                    name[] = "?SIF";
147         struct aibs_sensor      *as;
148
149         switch (st) {
150         case SENSOR_TEMP:
151                 name[0] = 'T';
152                 break;
153         case SENSOR_FANRPM:
154                 name[0] = 'F';
155                 break;
156         case SENSOR_VOLTS_DC:
157                 name[0] = 'V';
158                 break;
159         default:
160                 return;
161         }
162
163         b.Length = ACPI_ALLOCATE_BUFFER;
164         s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
165             ACPI_TYPE_PACKAGE);
166         if (ACPI_FAILURE(s)) {
167                 device_printf(sc->sc_dev, "%s not found\n", name);
168                 return;
169         }
170
171         bp = b.Pointer;
172         o = bp->Package.Elements;
173         if (o[0].Type != ACPI_TYPE_INTEGER) {
174                 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
175                 AcpiOsFree(b.Pointer);
176                 return;
177         }
178
179         n = o[0].Integer.Value;
180         if (bp->Package.Count - 1 < n) {
181                 device_printf(sc->sc_dev, "%s: invalid package\n", name);
182                 AcpiOsFree(b.Pointer);
183                 return;
184         } else if (bp->Package.Count - 1 > n) {
185                 int on = n;
186
187 #ifdef AIBS_MORE_SENSORS
188                 n = bp->Package.Count - 1;
189 #endif
190                 device_printf(sc->sc_dev, "%s: misformed package: %i/%i"
191                     ", assume %i\n", name, on, bp->Package.Count - 1, n);
192         }
193         if (n < 1) {
194                 device_printf(sc->sc_dev, "%s: no members in the package\n",
195                     name);
196                 AcpiOsFree(b.Pointer);
197                 return;
198         }
199
200         as = kmalloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
201         if (as == NULL) {
202                 device_printf(sc->sc_dev, "%s: malloc fail\n", name);
203                 AcpiOsFree(b.Pointer);
204                 return;
205         }
206
207         switch (st) {
208         case SENSOR_TEMP:
209                 sc->sc_asens_temp = as;
210                 break;
211         case SENSOR_FANRPM:
212                 sc->sc_asens_fan = as;
213                 break;
214         case SENSOR_VOLTS_DC:
215                 sc->sc_asens_volt = as;
216                 break;
217         default:
218                 /* NOTREACHED */
219                 return;
220         }
221
222         for (i = 0, o++; i < n; i++, o++) {
223                 ACPI_OBJECT     *oi;
224
225                 /* acpica5 automatically evaluates the referenced package */
226                 if(o[0].Type != ACPI_TYPE_PACKAGE) {
227                         device_printf(sc->sc_dev,
228                             "%s: %i: not a package: %i type\n",
229                             name, i, o[0].Type);
230                         continue;
231                 }
232                 oi = o[0].Package.Elements;
233                 if (o[0].Package.Count != 5 ||
234                     oi[0].Type != ACPI_TYPE_INTEGER ||
235                     oi[1].Type != ACPI_TYPE_STRING ||
236                     oi[2].Type != ACPI_TYPE_INTEGER ||
237                     oi[3].Type != ACPI_TYPE_INTEGER ||
238                     oi[4].Type != ACPI_TYPE_INTEGER) {
239                         device_printf(sc->sc_dev,
240                             "%s: %i: invalid package\n",
241                             name, i);
242                         continue;
243                 }
244                 as[i].i = oi[0].Integer.Value;
245                 strlcpy(as[i].s.desc, oi[1].String.Pointer,
246                     sizeof(as[i].s.desc));
247                 as[i].l = oi[2].Integer.Value;
248                 as[i].h = oi[3].Integer.Value;
249                 as[i].s.type = st;
250 #ifdef AIBS_VERBOSE
251                 device_printf(sc->sc_dev, "%c%i: "
252                     "0x%08llx %20s %5lli / %5lli  0x%llx\n",
253                     name[0], i,
254                     as[i].i, as[i].s.desc, as[i].l, as[i].h,
255                     oi[4].Integer.Value);
256 #endif
257                 sensor_attach(&sc->sc_sensordev, &as[i].s);
258         }
259
260         AcpiOsFree(b.Pointer);
261         return;
262 }
263
264 static int
265 aibs_detach(struct device *dev)
266 {
267         struct aibs_softc       *sc = device_get_softc(dev);
268
269         sensordev_deinstall(&sc->sc_sensordev);
270         sensor_task_unregister(sc);
271         if (sc->sc_asens_volt != NULL)
272                 kfree(sc->sc_asens_volt, M_DEVBUF);
273         if (sc->sc_asens_temp != NULL)
274                 kfree(sc->sc_asens_temp, M_DEVBUF);
275         if (sc->sc_asens_fan != NULL)
276                 kfree(sc->sc_asens_fan, M_DEVBUF);
277         return 0;
278 }
279
280 #ifdef AIBS_VERBOSE
281 #define ddevice_printf(x...) device_printf(x)
282 #else
283 #define ddevice_printf(x...)
284 #endif
285
286 static void
287 aibs_refresh(void *arg)
288 {
289         struct aibs_softc *sc = arg;
290
291         aibs_refresh_r(sc, SENSOR_VOLTS_DC);
292         aibs_refresh_r(sc, SENSOR_TEMP);
293         aibs_refresh_r(sc, SENSOR_FANRPM);
294 }
295
296 static void
297 aibs_refresh_r(struct aibs_softc *sc, enum sensor_type st)
298 {
299         ACPI_STATUS             rs;
300         ACPI_HANDLE             rh;
301         int                     i, n = sc->sc_sensordev.maxnumt[st];
302         char                    *name;
303         struct aibs_sensor      *as;
304
305         switch (st) {
306         case SENSOR_TEMP:
307                 name = "RTMP";
308                 as = sc->sc_asens_temp;
309                 break;
310         case SENSOR_FANRPM:
311                 name = "RFAN";
312                 as = sc->sc_asens_fan;
313                 break;
314         case SENSOR_VOLTS_DC:
315                 name = "RVLT";
316                 as = sc->sc_asens_volt;
317                 break;
318         default:
319                 return;
320         }
321
322         if (as == NULL)
323                 return;
324
325         rs = AcpiGetHandle(sc->sc_ah, name, &rh);
326         if (ACPI_FAILURE(rs)) {
327                 ddevice_printf(sc->sc_dev, "%s: method handle not found\n",
328                     name);
329                 for (i = 0; i < n; i++)
330                         as[i].s.flags |= SENSOR_FINVALID;
331                 return;
332         }
333
334         for (i = 0; i < n; i++) {
335                 ACPI_OBJECT             p, *bp;
336                 ACPI_OBJECT_LIST        mp;
337                 ACPI_BUFFER             b;
338                 int64_t                 v;
339                 struct ksensor          *s = &as[i].s;
340                 const int64_t           l = as[i].l, h = as[i].h;
341
342                 p.Type = ACPI_TYPE_INTEGER;
343                 p.Integer.Value = as[i].i;
344                 mp.Count = 1;
345                 mp.Pointer = &p;
346                 b.Length = ACPI_ALLOCATE_BUFFER;
347                 rs = AcpiEvaluateObjectTyped(rh, NULL, &mp, &b,
348                     ACPI_TYPE_INTEGER);
349                 if (ACPI_FAILURE(rs)) {
350                         ddevice_printf(sc->sc_dev,
351                             "%s: %i: evaluation failed\n",
352                             name, i);
353                         s->flags |= SENSOR_FINVALID;
354                         continue;
355                 }
356                 bp = b.Pointer;
357                 v = bp->Integer.Value;
358                 AcpiOsFree(b.Pointer);
359
360                 switch (st) {
361                 case SENSOR_TEMP:
362                         s->value = v * 100 * 1000 + 273150000;
363                         if (v == 0) {
364                                 s->status = SENSOR_S_UNKNOWN;
365                                 s->flags |= SENSOR_FINVALID;
366                         } else {
367                                 if (v > h)
368                                         s->status = SENSOR_S_CRIT;
369                                 else if (v > l)
370                                         s->status = SENSOR_S_WARN;
371                                 else
372                                         s->status = SENSOR_S_OK;
373                                 s->flags &= ~SENSOR_FINVALID;
374                         }
375                         break;
376                 case SENSOR_FANRPM:
377                         s->value = v;
378                         /* some boards have strange limits for fans */
379                         if ((l != 0 && l < v && v < h) ||
380                             (l == 0 && v > h))
381                                 s->status = SENSOR_S_OK;
382                         else
383                                 s->status = SENSOR_S_WARN;
384                         s->flags &= ~SENSOR_FINVALID;
385                         break;
386                 case SENSOR_VOLTS_DC:
387                         s->value = v * 1000;
388                         if (l < v && v < h)
389                                 s->status = SENSOR_S_OK;
390                         else
391                                 s->status = SENSOR_S_WARN;
392                         s->flags &= ~SENSOR_FINVALID;
393                         break;
394                 default:
395                         /* NOTREACHED */
396                         break;
397                 }
398         }
399
400         return;
401 }