1 /* $OpenBSD: atk0110.c,v 1.20 2024/09/04 07:54:52 mglocker 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 methods */
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 *, const 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 const 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
aibs_match(struct device * parent,void * match,void * aux)130 aibs_match(struct device *parent, void *match, void *aux)
131 {
132 struct acpi_attach_args *aa = aux;
133 struct cfdata *cf = match;
134
135 return acpi_matchhids(aa, aibs_hids, cf->cf_driver->cd_name);
136 }
137
138 void
aibs_attach(struct device * parent,struct device * self,void * aux)139 aibs_attach(struct device *parent, struct device *self, void *aux)
140 {
141 struct aibs_softc *sc = (struct aibs_softc *)self;
142 struct acpi_attach_args *aa = aux;
143
144 sc->sc_acpi = (struct acpi_softc *)parent;
145 sc->sc_devnode = aa->aaa_node;
146
147 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
148 sizeof(sc->sc_sensordev.xname));
149 SIMPLEQ_INIT(&sc->sc_sensorlist);
150
151 aibs_probe(sc);
152 printf("\n");
153
154 if (sc->sc_mode)
155 aibs_attach_new(sc);
156 else {
157 aibs_attach_sif(sc, SENSOR_TEMP);
158 aibs_attach_sif(sc, SENSOR_FANRPM);
159 aibs_attach_sif(sc, SENSOR_VOLTS_DC);
160 }
161
162 if (sc->sc_sensordev.sensors_count == 0) {
163 printf("%s: no sensors found\n", DEVNAME(sc));
164 return;
165 }
166
167 sensordev_install(&sc->sc_sensordev);
168
169 aml_register_notify(sc->sc_devnode, aa->aaa_dev,
170 aibs_notify, sc, ACPIDEV_POLL);
171 }
172
173 void
aibs_attach_sif(struct aibs_softc * sc,enum sensor_type st)174 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st)
175 {
176 struct aml_value res;
177 struct aml_value **v;
178 int i, n;
179 char name[] = "?SIF";
180
181 switch (st) {
182 case SENSOR_TEMP:
183 name[0] = 'T';
184 break;
185 case SENSOR_FANRPM:
186 name[0] = 'F';
187 break;
188 case SENSOR_VOLTS_DC:
189 name[0] = 'V';
190 break;
191 default:
192 return;
193 }
194
195 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) {
196 printf("%s: %s not found\n", DEVNAME(sc), name);
197 aml_freevalue(&res);
198 return;
199 }
200 if (res.type != AML_OBJTYPE_PACKAGE) {
201 printf("%s: %s: not a package\n", DEVNAME(sc), name);
202 aml_freevalue(&res);
203 return;
204 }
205 v = res.v_package;
206 if (v[0]->type != AML_OBJTYPE_INTEGER) {
207 printf("%s: %s[0]: invalid type\n", DEVNAME(sc), name);
208 aml_freevalue(&res);
209 return;
210 }
211
212 n = v[0]->v_integer;
213 if (res.length - 1 < n) {
214 printf("%s: %s: invalid package\n", DEVNAME(sc), name);
215 aml_freevalue(&res);
216 return;
217 } else if (res.length - 1 > n) {
218 printf("%s: %s: malformed package: %i/%i",
219 DEVNAME(sc), name, n, res.length - 1);
220 #ifdef AIBS_MORE_SENSORS
221 n = res.length - 1;
222 #endif
223 printf(", assume %i\n", n);
224 }
225 if (n < 1) {
226 printf("%s: %s: no members in the package\n",
227 DEVNAME(sc), name);
228 aml_freevalue(&res);
229 return;
230 }
231
232 for (i = 0, v++; i < n; i++, v++) {
233 if(v[0]->type != AML_OBJTYPE_NAMEREF) {
234 printf("%s: %s: %i: not a nameref: %i type\n",
235 DEVNAME(sc), name, i, v[0]->type);
236 continue;
237 }
238 aibs_add_sensor(sc, aml_getname(v[0]->v_nameref));
239 }
240
241 aml_freevalue(&res);
242 }
243
244 void
aibs_attach_new(struct aibs_softc * sc)245 aibs_attach_new(struct aibs_softc *sc)
246 {
247 struct aml_value res;
248 int i;
249
250 if (aibs_getpack(sc, sc->sc_ggrpnode, ATK_ID_MUX_HWMON, &res)) {
251 printf("%s: GGRP: sensor enumeration failed\n", DEVNAME(sc));
252 return;
253 }
254
255 for (i = 0; i < res.length; i++) {
256 struct aml_value *r;
257 r = res.v_package[i];
258 if (r->type != AML_OBJTYPE_STRING) {
259 printf("%s: %s: %i: not a string (type %i)\n",
260 DEVNAME(sc), "GGRP", i, r->type);
261 continue;
262 }
263 aibs_add_sensor(sc, r->v_string);
264 }
265 aml_freevalue(&res);
266 }
267
268 void
aibs_add_sensor(struct aibs_softc * sc,const char * name)269 aibs_add_sensor(struct aibs_softc *sc, const char *name)
270 {
271 struct aml_value ri;
272 struct aibs_sensor *as;
273 int len, lim1, lim2, ena;
274
275 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name,
276 0, NULL, &ri)) {
277 printf("%s: aibs_add_sensor: %s not found\n",
278 DEVNAME(sc), name);
279 aml_freevalue(&ri);
280 return;
281 }
282 if (ri.type != AML_OBJTYPE_PACKAGE) {
283 printf("%s: aibs_add_sensor: %s: not a package\n",
284 DEVNAME(sc), name);
285 aml_freevalue(&ri);
286 return;
287 }
288 if (sc->sc_mode) {
289 len = 7;
290 lim1 = 4;
291 lim2 = 5;
292 ena = 6;
293 } else {
294 len = 5;
295 lim1 = 2;
296 lim2 = 3;
297 ena = 4;
298 }
299
300 if (ri.length != len ||
301 ri.v_package[0]->type != AML_OBJTYPE_INTEGER ||
302 ri.v_package[1]->type != AML_OBJTYPE_STRING ||
303 ri.v_package[lim1]->type != AML_OBJTYPE_INTEGER ||
304 ri.v_package[lim2]->type != AML_OBJTYPE_INTEGER ||
305 ri.v_package[ena]->type != AML_OBJTYPE_INTEGER) {
306 printf("%s: aibs_add_sensor: %s: invalid package\n",
307 DEVNAME(sc), name);
308 aml_freevalue(&ri);
309 return;
310 }
311 as = malloc(sizeof(*as), M_DEVBUF, M_NOWAIT | M_ZERO);
312 if (!as) {
313 printf("%s: aibs_add_sensor: %s: failed to allocate sensor\n",
314 DEVNAME(sc), name);
315 aml_freevalue(&ri);
316 return;
317 }
318 as->i = ri.v_package[0]->v_integer;
319 switch (ATK_TYPE(as->i)) {
320 case ATK_TYPE_VOLT:
321 as->s.type = SENSOR_VOLTS_DC;
322 break;
323 case ATK_TYPE_TEMP:
324 as->s.type = SENSOR_TEMP;
325 break;
326 case ATK_TYPE_FAN:
327 as->s.type = SENSOR_FANRPM;
328 break;
329 default:
330 printf("%s: aibs_add_sensor: %s: unknown sensor type %llx\n",
331 DEVNAME(sc), name, ri.v_package[0]->v_integer);
332 aml_freevalue(&ri);
333 free(as, M_DEVBUF, sizeof(*as));
334 return;
335 }
336 strlcpy(as->s.desc, ri.v_package[1]->v_string,
337 sizeof(as->s.desc));
338 as->l = ri.v_package[lim1]->v_integer;
339 if (sc->sc_mode)
340 /* the second limit is a actually a range */
341 as->h = as->l + ri.v_package[lim2]->v_integer;
342 else
343 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 SIMPLEQ_INSERT_TAIL(&sc->sc_sensorlist, as, entry);
350 sensor_attach(&sc->sc_sensordev, &as->s);
351 aml_freevalue(&ri);
352 return;
353 }
354
355 void
aibs_refresh(void * arg)356 aibs_refresh(void *arg)
357 {
358 struct aibs_softc *sc = arg;
359 struct aibs_sensor *as;
360
361 SIMPLEQ_FOREACH(as, &sc->sc_sensorlist, entry)
362 aibs_refresh_r(sc, as);
363 }
364
365 void
aibs_refresh_r(struct aibs_softc * sc,struct aibs_sensor * as)366 aibs_refresh_r(struct aibs_softc *sc, struct aibs_sensor *as)
367 {
368 struct ksensor *s = &as->s;
369 int64_t v;
370 const int64_t l = as->l, h = as->h;
371
372 if (aibs_getvalue(sc, as->i, &v)) {
373 s->flags |= SENSOR_FINVALID;
374 return;
375 }
376 switch (s->type) {
377 case SENSOR_TEMP:
378 s->value = v * 100 * 1000 + 273150000;
379 if (v == 0) {
380 s->status = SENSOR_S_UNKNOWN;
381 s->flags |= SENSOR_FINVALID;
382 } else {
383 if (v > h)
384 s->status = SENSOR_S_CRIT;
385 else if (v > l)
386 s->status = SENSOR_S_WARN;
387 else
388 s->status = SENSOR_S_OK;
389 s->flags &= ~SENSOR_FINVALID;
390 }
391 break;
392 case SENSOR_FANRPM:
393 s->value = v;
394 /* some boards have strange limits for fans */
395 if ((l != 0 && l < v && v < h) ||
396 (l == 0 && v > h))
397 s->status = SENSOR_S_OK;
398 else
399 s->status = SENSOR_S_WARN;
400 s->flags &= ~SENSOR_FINVALID;
401 break;
402 case SENSOR_VOLTS_DC:
403 s->value = v * 1000;
404 if (l < v && v < h)
405 s->status = SENSOR_S_OK;
406 else
407 s->status = SENSOR_S_WARN;
408 s->flags &= ~SENSOR_FINVALID;
409 break;
410 default:
411 /* NOTREACHED */
412 break;
413 }
414 }
415
416 int
aibs_getvalue(struct aibs_softc * sc,int64_t i,int64_t * v)417 aibs_getvalue(struct aibs_softc *sc, int64_t i, int64_t *v)
418 {
419 struct aml_node *n = sc->sc_gitmnode;
420 struct aml_value req, res;
421 struct aibs_cmd_buffer cmd;
422 struct aibs_ret_buffer ret;
423 enum aml_objecttype type;
424
425 if (sc->sc_mode) {
426 cmd.id = i;
427 cmd.param1 = 0;
428 cmd.param2 = 0;
429 type = req.type = AML_OBJTYPE_BUFFER;
430 req.v_buffer = (uint8_t *)&cmd;
431 req.length = sizeof(cmd);
432 } else {
433 switch (ATK_TYPE(i)) {
434 case ATK_TYPE_TEMP:
435 n = sc->sc_rtmpnode;
436 break;
437 case ATK_TYPE_FAN:
438 n = sc->sc_rfannode;
439 break;
440 case ATK_TYPE_VOLT:
441 n = sc->sc_rvltnode;
442 break;
443 default:
444 return (-1);
445 }
446 type = req.type = AML_OBJTYPE_INTEGER;
447 req.v_integer = i;
448 }
449
450 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 aml_freevalue(&res);
454 return (-1);
455 }
456 if (res.type != type) {
457 dprintf("%s: %s: %lld: not an integer: type %i\n",
458 DEVNAME(sc), n->name, i, res.type);
459 aml_freevalue(&res);
460 return (-1);
461 }
462
463 if (sc->sc_mode) {
464 if (res.length < sizeof(ret)) {
465 dprintf("%s: %s: %lld: result buffer too small\n",
466 DEVNAME(sc), n->name, i);
467 aml_freevalue(&res);
468 return (-1);
469 }
470 memcpy(&ret, res.v_buffer, sizeof(ret));
471 if (ret.flags == 0) {
472 dprintf("%s: %s: %lld: bad flags in result\n",
473 DEVNAME(sc), n->name, i);
474 aml_freevalue(&res);
475 return (-1);
476 }
477 *v = ret.value;
478 } else {
479 *v = res.v_integer;
480 }
481 aml_freevalue(&res);
482
483 return (0);
484 }
485
486 int
aibs_getpack(struct aibs_softc * sc,struct aml_node * n,int64_t i,struct aml_value * res)487 aibs_getpack(struct aibs_softc *sc, struct aml_node *n, int64_t i,
488 struct aml_value *res)
489 {
490 struct aml_value req;
491
492 req.type = AML_OBJTYPE_INTEGER;
493 req.v_integer = i;
494
495 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 aml_freevalue(res);
499 return (-1);
500 }
501 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 aml_freevalue(res);
505 return (-1);
506 }
507
508 return (0);
509 }
510
511 void
aibs_probe(struct aibs_softc * sc)512 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 aml_find_node(sc->sc_devnode, "RTMP", aibs_find_cb, &sc->sc_rtmpnode);
521 aml_find_node(sc->sc_devnode, "RVLT", aibs_find_cb, &sc->sc_rvltnode);
522 aml_find_node(sc->sc_devnode, "RFAN", aibs_find_cb, &sc->sc_rfannode);
523
524 aml_find_node(sc->sc_devnode, "GGRP", aibs_find_cb, &sc->sc_ggrpnode);
525 aml_find_node(sc->sc_devnode, "GITM", aibs_find_cb, &sc->sc_gitmnode);
526 aml_find_node(sc->sc_devnode, "SITM", aibs_find_cb, &sc->sc_sitmnode);
527
528 if (sc->sc_ggrpnode && sc->sc_gitmnode && sc->sc_sitmnode &&
529 !sc->sc_rtmpnode && !sc->sc_rvltnode && !sc->sc_rfannode)
530 sc->sc_mode = 1;
531 }
532
533 int
aibs_find_cb(struct aml_node * node,void * arg)534 aibs_find_cb(struct aml_node *node, void *arg)
535 {
536 struct aml_node **np = arg;
537
538 printf(" %s", node->name);
539 *np = node;
540 return (1);
541 }
542
543 int
aibs_notify(struct aml_node * node,int notify_type,void * arg)544 aibs_notify(struct aml_node *node, int notify_type, void *arg)
545 {
546 struct aibs_softc *sc = arg;
547
548 if (notify_type == 0x00) {
549 /* Poll sensors */
550 aibs_refresh(sc);
551 }
552 return (0);
553 }
554