1 /* $OpenBSD: atk0110.c,v 1.17 2021/03/10 21:49:55 patrick 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 *, 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 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 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 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 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: misformed 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 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 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 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 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 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 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 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 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 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