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