Line data Source code
1 : /* $OpenBSD: atk0110.c,v 1.15 2018/06/29 17:39:18 kettenis Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2009 Constantine A. Murenin <cnst+openbsd@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/param.h>
20 : #include <sys/systm.h>
21 : #include <sys/device.h>
22 : #include <sys/malloc.h>
23 : #include <sys/sensors.h>
24 :
25 : #include <dev/acpi/acpireg.h>
26 : #include <dev/acpi/acpivar.h>
27 : #include <dev/acpi/acpidev.h>
28 : #include <dev/acpi/amltypes.h>
29 : #include <dev/acpi/dsdt.h>
30 :
31 : /*
32 : * ASUSTeK AI Booster (ACPI ATK0110).
33 : *
34 : * The driver was inspired by Takanori Watanabe's acpi_aiboost driver.
35 : * http://cvsweb.freebsd.org/src/sys/dev/acpi_support/acpi_aiboost.c
36 : *
37 : * Special thanks goes to Sam Fourman Jr. for providing access to several
38 : * ASUS boxes where the driver could be tested.
39 : *
40 : * -- cnst.su.
41 : */
42 :
43 : #define ATK_ID_MUX_HWMON 0x00000006
44 :
45 : #define ATK_CLASS(x) (((x) >> 24) & 0xff)
46 : #define ATK_CLASS_FREQ_CTL 3
47 : #define ATK_CLASS_FAN_CTL 4
48 : #define ATK_CLASS_HWMON 6
49 : #define ATK_CLASS_MGMT 17
50 :
51 : #define ATK_TYPE(x) (((x) >> 16) & 0xff)
52 : #define ATK_TYPE_VOLT 2
53 : #define ATK_TYPE_TEMP 3
54 : #define ATK_TYPE_FAN 4
55 :
56 : #define AIBS_MORE_SENSORS
57 : /* #define AIBS_VERBOSE */
58 :
59 : struct aibs_sensor {
60 : struct ksensor s;
61 : int64_t i;
62 : int64_t l;
63 : int64_t h;
64 : SIMPLEQ_ENTRY(aibs_sensor) entry;
65 : };
66 :
67 : struct aibs_softc {
68 : struct device sc_dev;
69 :
70 : struct acpi_softc *sc_acpi;
71 : struct aml_node *sc_devnode;
72 :
73 : struct aml_node *sc_ggrpnode;
74 : struct aml_node *sc_gitmnode;
75 : struct aml_node *sc_sitmnode;
76 : struct aml_node *sc_rtmpnode;
77 : struct aml_node *sc_rvltnode;
78 : struct aml_node *sc_rfannode;
79 :
80 : SIMPLEQ_HEAD(, aibs_sensor) sc_sensorlist;
81 : struct ksensordev sc_sensordev;
82 :
83 : int sc_mode; /* 1 = new, 0 = old */
84 : };
85 :
86 : /* Command buffer used for GITM and SITM methods */
87 : struct aibs_cmd_buffer {
88 : uint32_t id;
89 : uint32_t param1;
90 : uint32_t param2;
91 : };
92 :
93 : /* Return buffer used by the GITM and SITM mehtods */
94 : struct aibs_ret_buffer {
95 : uint32_t flags;
96 : uint32_t value;
97 : /* there is more stuff that is unknown */
98 : };
99 :
100 : int aibs_match(struct device *, void *, void *);
101 : void aibs_attach(struct device *, struct device *, void *);
102 : int aibs_notify(struct aml_node *, int, void *);
103 : void aibs_refresh(void *);
104 :
105 : void aibs_attach_sif(struct aibs_softc *, enum sensor_type);
106 : void aibs_attach_new(struct aibs_softc *);
107 : void aibs_add_sensor(struct aibs_softc *, char *);
108 : void aibs_refresh_r(struct aibs_softc *, struct aibs_sensor *);
109 : int aibs_getvalue(struct aibs_softc *, int64_t, int64_t *);
110 : int aibs_getpack(struct aibs_softc *, struct aml_node *, int64_t,
111 : struct aml_value *);
112 : void aibs_probe(struct aibs_softc *);
113 : int aibs_find_cb(struct aml_node *, void *);
114 :
115 :
116 : struct cfattach aibs_ca = {
117 : sizeof(struct aibs_softc), aibs_match, aibs_attach
118 : };
119 :
120 : struct cfdriver aibs_cd = {
121 : NULL, "aibs", DV_DULL
122 : };
123 :
124 : static const char* aibs_hids[] = {
125 : "ATK0110",
126 : NULL
127 : };
128 :
129 : int
130 0 : aibs_match(struct device *parent, void *match, void *aux)
131 : {
132 0 : struct acpi_attach_args *aa = aux;
133 0 : struct cfdata *cf = match;
134 :
135 0 : return acpi_matchhids(aa, aibs_hids, cf->cf_driver->cd_name);
136 : }
137 :
138 : void
139 0 : aibs_attach(struct device *parent, struct device *self, void *aux)
140 : {
141 0 : struct aibs_softc *sc = (struct aibs_softc *)self;
142 0 : struct acpi_attach_args *aa = aux;
143 :
144 0 : sc->sc_acpi = (struct acpi_softc *)parent;
145 0 : sc->sc_devnode = aa->aaa_node;
146 :
147 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
148 : sizeof(sc->sc_sensordev.xname));
149 0 : SIMPLEQ_INIT(&sc->sc_sensorlist);
150 :
151 0 : aibs_probe(sc);
152 0 : printf("\n");
153 :
154 0 : if (sc->sc_mode)
155 0 : aibs_attach_new(sc);
156 : else {
157 0 : aibs_attach_sif(sc, SENSOR_TEMP);
158 0 : aibs_attach_sif(sc, SENSOR_FANRPM);
159 0 : aibs_attach_sif(sc, SENSOR_VOLTS_DC);
160 : }
161 :
162 0 : if (sc->sc_sensordev.sensors_count == 0) {
163 0 : printf("%s: no sensors found\n", DEVNAME(sc));
164 0 : return;
165 : }
166 :
167 0 : sensordev_install(&sc->sc_sensordev);
168 :
169 0 : aml_register_notify(sc->sc_devnode, aa->aaa_dev,
170 0 : aibs_notify, sc, ACPIDEV_POLL);
171 0 : }
172 :
173 : void
174 0 : aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
175 : {
176 0 : struct aml_value res;
177 : struct aml_value **v;
178 : int i, n;
179 0 : char name[] = "?SIF";
180 :
181 0 : switch (st) {
182 : case SENSOR_TEMP:
183 0 : name[0] = 'T';
184 0 : break;
185 : case SENSOR_FANRPM:
186 0 : name[0] = 'F';
187 0 : break;
188 : case SENSOR_VOLTS_DC:
189 0 : name[0] = 'V';
190 0 : break;
191 : default:
192 0 : return;
193 : }
194 :
195 0 : if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) {
196 0 : printf("%s: %s not found\n", DEVNAME(sc), name);
197 0 : aml_freevalue(&res);
198 0 : return;
199 : }
200 0 : if (res.type != AML_OBJTYPE_PACKAGE) {
201 0 : printf("%s: %s: not a package\n", DEVNAME(sc), name);
202 0 : aml_freevalue(&res);
203 0 : return;
204 : }
205 0 : v = res.v_package;
206 0 : if (v[0]->type != AML_OBJTYPE_INTEGER) {
207 0 : printf("%s: %s[0]: invalid type\n", DEVNAME(sc), name);
208 0 : aml_freevalue(&res);
209 0 : return;
210 : }
211 :
212 0 : n = v[0]->v_integer;
213 0 : if (res.length - 1 < n) {
214 0 : printf("%s: %s: invalid package\n", DEVNAME(sc), name);
215 0 : aml_freevalue(&res);
216 0 : return;
217 0 : } else if (res.length - 1 > n) {
218 0 : printf("%s: %s: misformed package: %i/%i",
219 0 : DEVNAME(sc), name, n, res.length - 1);
220 : #ifdef AIBS_MORE_SENSORS
221 0 : n = res.length - 1;
222 : #endif
223 0 : printf(", assume %i\n", n);
224 0 : }
225 0 : if (n < 1) {
226 0 : printf("%s: %s: no members in the package\n",
227 0 : DEVNAME(sc), name);
228 0 : aml_freevalue(&res);
229 0 : return;
230 : }
231 :
232 0 : for (i = 0, v++; i < n; i++, v++) {
233 0 : if(v[0]->type != AML_OBJTYPE_STRING) {
234 0 : printf("%s: %s: %i: not a string: %i type\n",
235 0 : DEVNAME(sc), name, i, v[0]->type);
236 0 : continue;
237 : }
238 0 : aibs_add_sensor(sc, v[0]->v_string);
239 0 : }
240 :
241 0 : aml_freevalue(&res);
242 0 : }
243 :
244 : void
245 0 : aibs_attach_new(struct aibs_softc *sc)
246 : {
247 0 : struct aml_value res;
248 : int i;
249 :
250 0 : if (aibs_getpack(sc, sc->sc_ggrpnode, ATK_ID_MUX_HWMON, &res)) {
251 0 : printf("%s: GGRP: sensor enumeration failed\n", DEVNAME(sc));
252 0 : return;
253 : }
254 :
255 0 : for (i = 0; i < res.length; i++) {
256 : struct aml_value *r;
257 0 : r = res.v_package[i];
258 0 : if (r->type != AML_OBJTYPE_STRING) {
259 0 : printf("%s: %s: %i: not a string (type %i)\n",
260 0 : DEVNAME(sc), "GGRP", i, r->type);
261 0 : continue;
262 : }
263 0 : aibs_add_sensor(sc, r->v_string);
264 0 : }
265 0 : aml_freevalue(&res);
266 0 : }
267 :
268 : void
269 0 : aibs_add_sensor(struct aibs_softc *sc, char *name)
270 : {
271 0 : struct aml_value ri;
272 : struct aibs_sensor *as;
273 : int len, lim1, lim2, ena;
274 :
275 0 : if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
276 : 0, NULL, &ri)) {
277 0 : printf("%s: aibs_add_sensor: %s not found\n",
278 0 : DEVNAME(sc), name);
279 0 : aml_freevalue(&ri);
280 0 : return;
281 : }
282 0 : if (ri.type != AML_OBJTYPE_PACKAGE) {
283 0 : printf("%s: aibs_add_sensor: %s: not a package\n",
284 0 : DEVNAME(sc), name);
285 0 : aml_freevalue(&ri);
286 0 : return;
287 : }
288 0 : if (sc->sc_mode) {
289 : len = 7;
290 : lim1 = 4;
291 : lim2 = 5;
292 : ena = 6;
293 0 : } else {
294 : len = 5;
295 : lim1 = 2;
296 : lim2 = 3;
297 : ena = 4;
298 : }
299 :
300 0 : if (ri.length != len ||
301 0 : ri.v_package[0]->type != AML_OBJTYPE_INTEGER ||
302 0 : ri.v_package[1]->type != AML_OBJTYPE_STRING ||
303 0 : ri.v_package[lim1]->type != AML_OBJTYPE_INTEGER ||
304 0 : ri.v_package[lim2]->type != AML_OBJTYPE_INTEGER ||
305 0 : ri.v_package[ena]->type != AML_OBJTYPE_INTEGER) {
306 0 : printf("%s: aibs_add_sensor: %s: invalid package\n",
307 0 : DEVNAME(sc), name);
308 0 : aml_freevalue(&ri);
309 0 : return;
310 : }
311 0 : as = malloc(sizeof(*as), M_DEVBUF, M_NOWAIT | M_ZERO);
312 0 : if (!as) {
313 0 : printf("%s: aibs_add_sensor: %s: failed to allocate sensor\n",
314 0 : DEVNAME(sc), name);
315 0 : aml_freevalue(&ri);
316 0 : return;
317 : }
318 0 : as->i = ri.v_package[0]->v_integer;
319 0 : switch (ATK_TYPE(as->i)) {
320 : case ATK_TYPE_VOLT:
321 0 : as->s.type = SENSOR_VOLTS_DC;
322 0 : break;
323 : case ATK_TYPE_TEMP:
324 0 : as->s.type = SENSOR_TEMP;
325 0 : break;
326 : case ATK_TYPE_FAN:
327 0 : as->s.type = SENSOR_FANRPM;
328 0 : break;
329 : default:
330 0 : printf("%s: aibs_add_sensor: %s: unknown sensor type %llx\n",
331 0 : DEVNAME(sc), name, ri.v_package[0]->v_integer);
332 0 : aml_freevalue(&ri);
333 0 : free(as, M_DEVBUF, sizeof(*as));
334 0 : return;
335 : }
336 0 : strlcpy(as->s.desc, ri.v_package[1]->v_string,
337 : sizeof(as->s.desc));
338 0 : as->l = ri.v_package[lim1]->v_integer;
339 0 : if (sc->sc_mode)
340 : /* the second limit is a actually a range */
341 0 : as->h = as->l + ri.v_package[lim2]->v_integer;
342 : else
343 0 : as->h = ri.v_package[lim2]->v_integer;
344 : #ifdef AIBS_VERBOSE
345 : printf("%s: %4s: %s 0x%08llx %5lli / %5lli 0x%llx\n",
346 : DEVNAME(sc), name, as->s.desc, as->i, as->l, as->h,
347 : ri.v_package[ena]->v_integer);
348 : #endif
349 0 : SIMPLEQ_INSERT_TAIL(&sc->sc_sensorlist, as, entry);
350 0 : sensor_attach(&sc->sc_sensordev, &as->s);
351 0 : aml_freevalue(&ri);
352 0 : return;
353 0 : }
354 :
355 : void
356 0 : aibs_refresh(void *arg)
357 : {
358 0 : struct aibs_softc *sc = arg;
359 : struct aibs_sensor *as;
360 :
361 0 : SIMPLEQ_FOREACH(as, &sc->sc_sensorlist, entry)
362 0 : aibs_refresh_r(sc, as);
363 0 : }
364 :
365 : void
366 0 : aibs_refresh_r(struct aibs_softc *sc, struct aibs_sensor *as)
367 : {
368 0 : struct ksensor *s = &as->s;
369 0 : int64_t v;
370 0 : const int64_t l = as->l, h = as->h;
371 :
372 0 : if (aibs_getvalue(sc, as->i, &v)) {
373 0 : s->flags |= SENSOR_FINVALID;
374 0 : return;
375 : }
376 0 : switch (s->type) {
377 : case SENSOR_TEMP:
378 0 : s->value = v * 100 * 1000 + 273150000;
379 0 : if (v == 0) {
380 0 : s->status = SENSOR_S_UNKNOWN;
381 0 : s->flags |= SENSOR_FINVALID;
382 0 : } else {
383 0 : if (v > h)
384 0 : s->status = SENSOR_S_CRIT;
385 0 : else if (v > l)
386 0 : s->status = SENSOR_S_WARN;
387 : else
388 0 : s->status = SENSOR_S_OK;
389 0 : s->flags &= ~SENSOR_FINVALID;
390 : }
391 : break;
392 : case SENSOR_FANRPM:
393 0 : s->value = v;
394 : /* some boards have strange limits for fans */
395 0 : if ((l != 0 && l < v && v < h) ||
396 0 : (l == 0 && v > h))
397 0 : s->status = SENSOR_S_OK;
398 : else
399 0 : s->status = SENSOR_S_WARN;
400 0 : s->flags &= ~SENSOR_FINVALID;
401 0 : break;
402 : case SENSOR_VOLTS_DC:
403 0 : s->value = v * 1000;
404 0 : if (l < v && v < h)
405 0 : s->status = SENSOR_S_OK;
406 : else
407 0 : s->status = SENSOR_S_WARN;
408 0 : s->flags &= ~SENSOR_FINVALID;
409 0 : break;
410 : default:
411 : /* NOTREACHED */
412 : break;
413 : }
414 0 : }
415 :
416 : int
417 0 : aibs_getvalue(struct aibs_softc *sc, int64_t i, int64_t *v)
418 : {
419 0 : struct aml_node *n = sc->sc_gitmnode;
420 0 : struct aml_value req, res;
421 0 : struct aibs_cmd_buffer cmd;
422 : struct aibs_ret_buffer ret;
423 : enum aml_objecttype type;
424 :
425 0 : if (sc->sc_mode) {
426 0 : cmd.id = i;
427 0 : cmd.param1 = 0;
428 0 : cmd.param2 = 0;
429 0 : type = req.type = AML_OBJTYPE_BUFFER;
430 0 : req.v_buffer = (uint8_t *)&cmd;
431 0 : req.length = sizeof(cmd);
432 0 : } else {
433 0 : switch (ATK_TYPE(i)) {
434 : case ATK_TYPE_TEMP:
435 0 : n = sc->sc_rtmpnode;
436 0 : break;
437 : case ATK_TYPE_FAN:
438 0 : n = sc->sc_rfannode;
439 0 : break;
440 : case ATK_TYPE_VOLT:
441 0 : n = sc->sc_rvltnode;
442 0 : break;
443 : default:
444 0 : return (-1);
445 : }
446 0 : type = req.type = AML_OBJTYPE_INTEGER;
447 0 : req.v_integer = i;
448 : }
449 :
450 0 : if (aml_evalnode(sc->sc_acpi, n, 1, &req, &res)) {
451 : dprintf("%s: %s: %lld: evaluation failed\n",
452 : DEVNAME(sc), n->name, i);
453 0 : aml_freevalue(&res);
454 0 : return (-1);
455 : }
456 0 : if (res.type != type) {
457 : dprintf("%s: %s: %lld: not an integer: type %i\n",
458 : DEVNAME(sc), n->name, i, res.type);
459 0 : aml_freevalue(&res);
460 0 : return (-1);
461 : }
462 :
463 0 : if (sc->sc_mode) {
464 0 : if (res.length < sizeof(ret)) {
465 : dprintf("%s: %s: %lld: result buffer too small\n",
466 : DEVNAME(sc), n->name, i);
467 0 : aml_freevalue(&res);
468 0 : return (-1);
469 : }
470 0 : memcpy(&ret, res.v_buffer, sizeof(ret));
471 0 : if (ret.flags == 0) {
472 : dprintf("%s: %s: %lld: bad flags in result\n",
473 : DEVNAME(sc), n->name, i);
474 0 : aml_freevalue(&res);
475 0 : return (-1);
476 : }
477 0 : *v = ret.value;
478 0 : } else {
479 0 : *v = res.v_integer;
480 : }
481 0 : aml_freevalue(&res);
482 :
483 0 : return (0);
484 0 : }
485 :
486 : int
487 0 : aibs_getpack(struct aibs_softc *sc, struct aml_node *n, int64_t i,
488 : struct aml_value *res)
489 : {
490 0 : struct aml_value req;
491 :
492 0 : req.type = AML_OBJTYPE_INTEGER;
493 0 : req.v_integer = i;
494 :
495 0 : if (aml_evalnode(sc->sc_acpi, n, 1, &req, res)) {
496 : dprintf("%s: %s: %lld: evaluation failed\n",
497 : DEVNAME(sc), n->name, i);
498 0 : aml_freevalue(res);
499 0 : return (-1);
500 : }
501 0 : if (res->type != AML_OBJTYPE_PACKAGE) {
502 : dprintf("%s: %s: %lld: not a package: type %i\n",
503 : DEVNAME(sc), n->name, i, res->type);
504 0 : aml_freevalue(res);
505 0 : return (-1);
506 : }
507 :
508 0 : return (0);
509 0 : }
510 :
511 : void
512 0 : aibs_probe(struct aibs_softc *sc)
513 : {
514 : /*
515 : * Old mode uses TSIF, VSIF, and FSIF to enumerate sensors and
516 : * RTMP, RVLT, and RFAN are used to get the values.
517 : * New mode uses GGRP for enumeration and GITM and SITM as accessor.
518 : * If the new methods are available use them else default to old mode.
519 : */
520 0 : aml_find_node(sc->sc_devnode, "RTMP", aibs_find_cb, &sc->sc_rtmpnode);
521 0 : aml_find_node(sc->sc_devnode, "RVLT", aibs_find_cb, &sc->sc_rvltnode);
522 0 : aml_find_node(sc->sc_devnode, "RFAN", aibs_find_cb, &sc->sc_rfannode);
523 :
524 0 : aml_find_node(sc->sc_devnode, "GGRP", aibs_find_cb, &sc->sc_ggrpnode);
525 0 : aml_find_node(sc->sc_devnode, "GITM", aibs_find_cb, &sc->sc_gitmnode);
526 0 : aml_find_node(sc->sc_devnode, "SITM", aibs_find_cb, &sc->sc_sitmnode);
527 :
528 0 : if (sc->sc_ggrpnode && sc->sc_gitmnode && sc->sc_sitmnode &&
529 0 : !sc->sc_rtmpnode && !sc->sc_rvltnode && !sc->sc_rfannode)
530 0 : sc->sc_mode = 1;
531 0 : }
532 :
533 : int
534 0 : aibs_find_cb(struct aml_node *node, void *arg)
535 : {
536 0 : struct aml_node **np = arg;
537 :
538 0 : printf(" %s", node->name);
539 0 : *np = node;
540 0 : return (1);
541 : }
542 :
543 : int
544 0 : aibs_notify(struct aml_node *node, int notify_type, void *arg)
545 : {
546 0 : struct aibs_softc *sc = arg;
547 :
548 0 : if (notify_type == 0x00) {
549 : /* Poll sensors */
550 0 : aibs_refresh(sc);
551 0 : }
552 0 : return (0);
553 : }
|