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