aibs: Add ASUSTeK AI Booster ACPI ATK0110 sensors
[dragonfly.git] / sys / dev / acpica5 / aibs / atk0110.c
blob9a2978293aaa415179055335b962c59951b1c0b6
1 /* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */
3 /*
4 * Copyright (c) 2009 Constantine A. Murenin <cnst+dfly@bugmail.mojo.ru>
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.
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.
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>
27 #include <sys/sensors.h>
29 #include "acpi.h"
30 #include "acpivar.h"
33 * ASUSTeK AI Booster (ACPI ATK0110).
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.
40 * -- Constantine A. Murenin <http://cnst.su/>
43 #define AIBS_MORE_SENSORS
44 #define AIBS_VERBOSE
46 struct aibs_sensor {
47 struct ksensor s;
48 int64_t i;
49 int64_t l;
50 int64_t h;
53 struct aibs_softc {
54 struct device *sc_dev;
55 ACPI_HANDLE sc_ah;
57 struct aibs_sensor *sc_asens_volt;
58 struct aibs_sensor *sc_asens_temp;
59 struct aibs_sensor *sc_asens_fan;
61 struct ksensordev sc_sensordev;
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 *);
70 static void aibs_attach_sif(struct aibs_softc *, enum sensor_type);
71 static void aibs_refresh_r(struct aibs_softc *, enum sensor_type);
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 }
81 static driver_t aibs_driver = {
82 "aibs",
83 aibs_methods,
84 sizeof(struct aibs_softc)
87 static devclass_t aibs_devclass;
89 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
92 static char* aibs_hids[] = {
93 "ATK0110",
94 NULL
97 static int
98 aibs_probe(struct device *dev)
101 if (acpi_disabled("aibs") ||
102 ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
103 return ENXIO;
105 device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
106 return 0;
109 static int
110 aibs_attach(struct device *dev)
112 struct aibs_softc *sc;
114 sc = device_get_softc(dev);
115 sc->sc_dev = dev;
116 sc->sc_ah = acpi_get_handle(dev);
118 strlcpy(sc->sc_sensordev.xname, device_get_nameunit(dev),
119 sizeof(sc->sc_sensordev.xname));
121 aibs_attach_sif(sc, SENSOR_VOLTS_DC);
122 aibs_attach_sif(sc, SENSOR_TEMP);
123 aibs_attach_sif(sc, SENSOR_FANRPM);
125 if (sc->sc_sensordev.sensors_count == 0) {
126 device_printf(dev, "no sensors found\n");
127 return ENXIO;
130 if (sensor_task_register(sc, aibs_refresh, 5)) {
131 device_printf(dev, "unable to register update task\n");
132 return ENXIO;
135 sensordev_install(&sc->sc_sensordev);
136 return 0;
139 static void
140 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
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;
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;
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;
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;
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;
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);
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;
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;
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;
222 for (i = 0, o++; i < n; i++, o++) {
223 ACPI_OBJECT *oi;
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;
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;
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);
260 AcpiOsFree(b.Pointer);
261 return;
264 static int
265 aibs_detach(struct device *dev)
267 struct aibs_softc *sc = device_get_softc(dev);
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;
280 #ifdef AIBS_VERBOSE
281 #define ddevice_printf(x...) device_printf(x)
282 #else
283 #define ddevice_printf(x...)
284 #endif
286 static void
287 aibs_refresh(void *arg)
289 struct aibs_softc *sc = arg;
291 aibs_refresh_r(sc, SENSOR_VOLTS_DC);
292 aibs_refresh_r(sc, SENSOR_TEMP);
293 aibs_refresh_r(sc, SENSOR_FANRPM);
296 static void
297 aibs_refresh_r(struct aibs_softc *sc, enum sensor_type st)
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;
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;
322 if (as == NULL)
323 return;
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;
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;
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;
356 bp = b.Pointer;
357 v = bp->Integer.Value;
358 AcpiOsFree(b.Pointer);
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;
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;
400 return;