1 /* $NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $ */ 2 /* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */ 3 4 /* 5 * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 __FBSDID("$FreeBSD$"); 22 23 #include <machine/_inttypes.h> 24 #include <sys/param.h> 25 #include <sys/systm.h> 26 #include <sys/kernel.h> 27 #include <sys/bus.h> 28 #include <sys/module.h> 29 #include <sys/malloc.h> 30 #include <sys/sysctl.h> 31 #include <sys/stdint.h> 32 33 #include <contrib/dev/acpica/include/acpi.h> 34 #include <dev/acpica/acpivar.h> 35 36 /* 37 * ASUSTeK AI Booster (ACPI ASOC ATK0110). 38 * 39 * This code was originally written for OpenBSD after the techniques 40 * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's 41 * acpi_aiboost.c were verified to be accurate on the actual hardware kindly 42 * provided by Sam Fourman Jr. It was subsequently ported from OpenBSD to 43 * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9). 44 * 45 * -- Constantine A. Murenin <http://cnst.su/> 46 */ 47 48 #define _COMPONENT ACPI_OEM 49 ACPI_MODULE_NAME("aibs"); 50 ACPI_SERIAL_DECL(aibs, "aibs"); 51 52 #define AIBS_MORE_SENSORS 53 #define AIBS_VERBOSE 54 55 #define AIBS_GROUP_SENSORS 0x06 56 57 #define AIBS_SENS_TYPE(x) (((x) >> 16) & 0xff) 58 #define AIBS_SENS_TYPE_VOLT 2 59 #define AIBS_SENS_TYPE_TEMP 3 60 #define AIBS_SENS_TYPE_FAN 4 61 62 #define AIBS_SENS_TYPE_VOLT_NAME "volt" 63 #define AIBS_SENS_TYPE_VOLT_TEMP "temp" 64 #define AIBS_SENS_TYPE_VOLT_FAN "fan" 65 66 struct aibs_sensor { 67 ACPI_INTEGER v; 68 ACPI_INTEGER i; 69 ACPI_INTEGER l; 70 ACPI_INTEGER h; 71 int t; 72 }; 73 74 struct aibs_softc { 75 device_t sc_dev; 76 ACPI_HANDLE sc_ah; 77 78 struct aibs_sensor *sc_asens_volt; 79 struct aibs_sensor *sc_asens_temp; 80 struct aibs_sensor *sc_asens_fan; 81 struct aibs_sensor *sc_asens_all; 82 83 struct sysctl_oid *sc_volt_sysctl; 84 struct sysctl_oid *sc_temp_sysctl; 85 struct sysctl_oid *sc_fan_sysctl; 86 87 bool sc_ggrp_method; 88 }; 89 90 static int aibs_probe(device_t); 91 static int aibs_attach(device_t); 92 static int aibs_detach(device_t); 93 static int aibs_sysctl(SYSCTL_HANDLER_ARGS); 94 static int aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS); 95 96 static int aibs_attach_ggrp(struct aibs_softc *); 97 static int aibs_attach_sif(struct aibs_softc *, int); 98 99 static device_method_t aibs_methods[] = { 100 DEVMETHOD(device_probe, aibs_probe), 101 DEVMETHOD(device_attach, aibs_attach), 102 DEVMETHOD(device_detach, aibs_detach), 103 { NULL, NULL } 104 }; 105 106 static driver_t aibs_driver = { 107 "aibs", 108 aibs_methods, 109 sizeof(struct aibs_softc) 110 }; 111 112 static devclass_t aibs_devclass; 113 114 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL); 115 MODULE_DEPEND(aibs, acpi, 1, 1, 1); 116 117 static char* aibs_hids[] = { 118 "ATK0110", 119 NULL 120 }; 121 122 static int 123 aibs_probe(device_t dev) 124 { 125 int rv; 126 127 if (acpi_disabled("aibs")) 128 return (ENXIO); 129 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids, NULL); 130 if (rv <= 0 ) 131 device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)"); 132 return (rv); 133 } 134 135 static int 136 aibs_attach(device_t dev) 137 { 138 struct aibs_softc *sc = device_get_softc(dev); 139 int err; 140 141 sc->sc_dev = dev; 142 sc->sc_ah = acpi_get_handle(dev); 143 144 sc->sc_ggrp_method = false; 145 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_VOLT); 146 if (err == 0) 147 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_TEMP); 148 if (err == 0) 149 err = aibs_attach_sif(sc, AIBS_SENS_TYPE_FAN); 150 151 if (err == 0) 152 return (0); 153 154 /* Clean up whatever was allocated earlier. */ 155 if (sc->sc_volt_sysctl != NULL) 156 sysctl_remove_oid(sc->sc_volt_sysctl, true, true); 157 if (sc->sc_temp_sysctl != NULL) 158 sysctl_remove_oid(sc->sc_temp_sysctl, true, true); 159 if (sc->sc_fan_sysctl != NULL) 160 sysctl_remove_oid(sc->sc_fan_sysctl, true, true); 161 aibs_detach(dev); 162 163 sc->sc_ggrp_method = true; 164 err = aibs_attach_ggrp(sc); 165 return (err); 166 } 167 168 static int 169 aibs_add_sensor(struct aibs_softc *sc, ACPI_OBJECT *o, 170 struct aibs_sensor* sensor, const char ** descr) 171 { 172 int off; 173 174 /* 175 * Packages for the old and new methods are quite 176 * similar except that the new package has two 177 * new (unknown / unused) fields after the name field. 178 */ 179 if (sc->sc_ggrp_method) 180 off = 4; 181 else 182 off = 2; 183 184 if (o->Type != ACPI_TYPE_PACKAGE) { 185 device_printf(sc->sc_dev, 186 "sensor object is not a package: %i type\n", 187 o->Type); 188 return (ENXIO); 189 } 190 if (o[0].Package.Count != (off + 3) || 191 o->Package.Elements[0].Type != ACPI_TYPE_INTEGER || 192 o->Package.Elements[1].Type != ACPI_TYPE_STRING || 193 o->Package.Elements[off].Type != ACPI_TYPE_INTEGER || 194 o->Package.Elements[off + 1].Type != ACPI_TYPE_INTEGER || 195 o->Package.Elements[off + 2].Type != ACPI_TYPE_INTEGER) { 196 device_printf(sc->sc_dev, "unexpected package content\n"); 197 return (ENXIO); 198 } 199 200 sensor->i = o->Package.Elements[0].Integer.Value; 201 *descr = o->Package.Elements[1].String.Pointer; 202 sensor->l = o->Package.Elements[off].Integer.Value; 203 sensor->h = o->Package.Elements[off + 1].Integer.Value; 204 /* For the new method the second value is a range size. */ 205 if (sc->sc_ggrp_method) 206 sensor->h += sensor->l; 207 sensor->t = AIBS_SENS_TYPE(sensor->i); 208 209 switch (sensor->t) { 210 case AIBS_SENS_TYPE_VOLT: 211 case AIBS_SENS_TYPE_TEMP: 212 case AIBS_SENS_TYPE_FAN: 213 return (0); 214 default: 215 device_printf(sc->sc_dev, "unknown sensor type 0x%x", 216 sensor->t); 217 return (ENXIO); 218 } 219 } 220 221 static void 222 aibs_sensor_added(struct aibs_softc *sc, struct sysctl_oid *so, 223 const char *type_name, int idx, struct aibs_sensor *sensor, 224 const char *descr) 225 { 226 char sysctl_name[8]; 227 228 snprintf(sysctl_name, sizeof(sysctl_name), "%i", idx); 229 #ifdef AIBS_VERBOSE 230 device_printf(sc->sc_dev, "%c%i: 0x%08jx %20s %5jd / %5jd\n", 231 type_name[0], idx, 232 (uintmax_t)sensor->i, descr, (intmax_t)sensor->l, 233 (intmax_t)sensor->h); 234 #endif 235 SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev), 236 SYSCTL_CHILDREN(so), idx, sysctl_name, 237 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, (uintptr_t)sensor, 238 sc->sc_ggrp_method ? aibs_sysctl_ggrp : aibs_sysctl, 239 sensor->t == AIBS_SENS_TYPE_TEMP ? "IK" : "I", descr); 240 } 241 242 static int 243 aibs_attach_ggrp(struct aibs_softc *sc) 244 { 245 ACPI_STATUS s; 246 ACPI_BUFFER buf; 247 ACPI_HANDLE h; 248 ACPI_OBJECT id; 249 ACPI_OBJECT *bp; 250 ACPI_OBJECT_LIST arg; 251 int i; 252 int t, v, f; 253 int err; 254 int *s_idx; 255 const char *name; 256 const char *descr; 257 struct aibs_sensor *sensor; 258 struct sysctl_oid **so; 259 260 /* First see if GITM is available. */ 261 s = AcpiGetHandle(sc->sc_ah, "GITM", &h); 262 if (ACPI_FAILURE(s)) { 263 if (bootverbose) 264 device_printf(sc->sc_dev, "GITM not found\n"); 265 return (ENXIO); 266 } 267 268 /* 269 * Now call GGRP with the appropriate argument to list sensors. 270 * The method lists different groups of entities depending on 271 * the argument. 272 */ 273 id.Integer.Value = AIBS_GROUP_SENSORS; 274 id.Type = ACPI_TYPE_INTEGER; 275 arg.Count = 1; 276 arg.Pointer = &id; 277 buf.Length = ACPI_ALLOCATE_BUFFER; 278 buf.Pointer = NULL; 279 s = AcpiEvaluateObjectTyped(sc->sc_ah, "GGRP", &arg, &buf, 280 ACPI_TYPE_PACKAGE); 281 if (ACPI_FAILURE(s)) { 282 device_printf(sc->sc_dev, "GGRP not found\n"); 283 return (ENXIO); 284 } 285 286 bp = buf.Pointer; 287 sc->sc_asens_all = malloc(sizeof(*sc->sc_asens_all) * bp->Package.Count, 288 M_DEVBUF, M_WAITOK | M_ZERO); 289 v = t = f = 0; 290 for (i = 0; i < bp->Package.Count; i++) { 291 sensor = &sc->sc_asens_all[i]; 292 err = aibs_add_sensor(sc, &bp->Package.Elements[i], sensor, 293 &descr); 294 if (err != 0) 295 continue; 296 297 switch (sensor->t) { 298 case AIBS_SENS_TYPE_VOLT: 299 name = "volt"; 300 so = &sc->sc_volt_sysctl; 301 s_idx = &v; 302 break; 303 case AIBS_SENS_TYPE_TEMP: 304 name = "temp"; 305 so = &sc->sc_temp_sysctl; 306 s_idx = &t; 307 break; 308 case AIBS_SENS_TYPE_FAN: 309 name = "fan"; 310 so = &sc->sc_fan_sysctl; 311 s_idx = &f; 312 break; 313 default: 314 panic("add_sensor succeeded for unknown sensor type %d", 315 sensor->t); 316 } 317 318 if (*so == NULL) { 319 /* sysctl subtree for sensors of this type */ 320 *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), 321 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), 322 sensor->t, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 323 NULL, NULL); 324 } 325 aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr); 326 *s_idx += 1; 327 } 328 329 AcpiOsFree(buf.Pointer); 330 return (0); 331 } 332 333 static int 334 aibs_attach_sif(struct aibs_softc *sc, int st) 335 { 336 char name[] = "?SIF"; 337 ACPI_STATUS s; 338 ACPI_BUFFER b; 339 ACPI_OBJECT *bp, *o; 340 const char *node; 341 struct aibs_sensor *as; 342 struct sysctl_oid **so; 343 int i, n; 344 int err; 345 346 switch (st) { 347 case AIBS_SENS_TYPE_VOLT: 348 node = "volt"; 349 name[0] = 'V'; 350 so = &sc->sc_volt_sysctl; 351 break; 352 case AIBS_SENS_TYPE_TEMP: 353 node = "temp"; 354 name[0] = 'T'; 355 so = &sc->sc_temp_sysctl; 356 break; 357 case AIBS_SENS_TYPE_FAN: 358 node = "fan"; 359 name[0] = 'F'; 360 so = &sc->sc_fan_sysctl; 361 break; 362 default: 363 panic("Unsupported sensor type %d", st); 364 } 365 366 b.Length = ACPI_ALLOCATE_BUFFER; 367 s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, 368 ACPI_TYPE_PACKAGE); 369 if (ACPI_FAILURE(s)) { 370 device_printf(sc->sc_dev, "%s not found\n", name); 371 return (ENXIO); 372 } 373 374 bp = b.Pointer; 375 o = bp->Package.Elements; 376 if (o[0].Type != ACPI_TYPE_INTEGER) { 377 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); 378 AcpiOsFree(b.Pointer); 379 return (ENXIO); 380 } 381 382 n = o[0].Integer.Value; 383 if (bp->Package.Count - 1 < n) { 384 device_printf(sc->sc_dev, "%s: invalid package\n", name); 385 AcpiOsFree(b.Pointer); 386 return (ENXIO); 387 } else if (bp->Package.Count - 1 > n) { 388 int on = n; 389 390 #ifdef AIBS_MORE_SENSORS 391 n = bp->Package.Count - 1; 392 #endif 393 device_printf(sc->sc_dev, "%s: malformed package: %i/%i" 394 ", assume %i\n", name, on, bp->Package.Count - 1, n); 395 } 396 if (n < 1) { 397 device_printf(sc->sc_dev, "%s: no members in the package\n", 398 name); 399 AcpiOsFree(b.Pointer); 400 return (ENXIO); 401 } 402 403 as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO); 404 switch (st) { 405 case AIBS_SENS_TYPE_VOLT: 406 sc->sc_asens_volt = as; 407 break; 408 case AIBS_SENS_TYPE_TEMP: 409 sc->sc_asens_temp = as; 410 break; 411 case AIBS_SENS_TYPE_FAN: 412 sc->sc_asens_fan = as; 413 break; 414 } 415 416 /* sysctl subtree for sensors of this type */ 417 *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), 418 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st, 419 node, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, NULL); 420 421 for (i = 0, o++; i < n; i++, o++) { 422 const char *descr; 423 424 err = aibs_add_sensor(sc, o, &as[i], &descr); 425 if (err == 0) 426 aibs_sensor_added(sc, *so, node, i, &as[i], descr); 427 } 428 429 AcpiOsFree(b.Pointer); 430 return (0); 431 } 432 433 static int 434 aibs_detach(device_t dev) 435 { 436 struct aibs_softc *sc = device_get_softc(dev); 437 438 if (sc->sc_asens_volt != NULL) 439 free(sc->sc_asens_volt, M_DEVBUF); 440 if (sc->sc_asens_temp != NULL) 441 free(sc->sc_asens_temp, M_DEVBUF); 442 if (sc->sc_asens_fan != NULL) 443 free(sc->sc_asens_fan, M_DEVBUF); 444 if (sc->sc_asens_all != NULL) 445 free(sc->sc_asens_all, M_DEVBUF); 446 return (0); 447 } 448 449 #ifdef AIBS_VERBOSE 450 #define ddevice_printf(x...) device_printf(x) 451 #else 452 #define ddevice_printf(x...) 453 #endif 454 455 static int 456 aibs_sysctl(SYSCTL_HANDLER_ARGS) 457 { 458 struct aibs_softc *sc = arg1; 459 struct aibs_sensor *sensor = (void *)(intptr_t)arg2; 460 int i = oidp->oid_number; 461 ACPI_STATUS rs; 462 ACPI_OBJECT p, *bp; 463 ACPI_OBJECT_LIST mp; 464 ACPI_BUFFER b; 465 char *name; 466 ACPI_INTEGER v, l, h; 467 int so[3]; 468 469 switch (sensor->t) { 470 case AIBS_SENS_TYPE_VOLT: 471 name = "RVLT"; 472 break; 473 case AIBS_SENS_TYPE_TEMP: 474 name = "RTMP"; 475 break; 476 case AIBS_SENS_TYPE_FAN: 477 name = "RFAN"; 478 break; 479 default: 480 return (ENOENT); 481 } 482 l = sensor->l; 483 h = sensor->h; 484 p.Type = ACPI_TYPE_INTEGER; 485 p.Integer.Value = sensor->i; 486 mp.Count = 1; 487 mp.Pointer = &p; 488 b.Length = ACPI_ALLOCATE_BUFFER; 489 ACPI_SERIAL_BEGIN(aibs); 490 rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b, 491 ACPI_TYPE_INTEGER); 492 if (ACPI_FAILURE(rs)) { 493 ddevice_printf(sc->sc_dev, 494 "%s: %i: evaluation failed\n", 495 name, i); 496 ACPI_SERIAL_END(aibs); 497 return (EIO); 498 } 499 bp = b.Pointer; 500 v = bp->Integer.Value; 501 AcpiOsFree(b.Pointer); 502 ACPI_SERIAL_END(aibs); 503 504 switch (sensor->t) { 505 case AIBS_SENS_TYPE_VOLT: 506 break; 507 case AIBS_SENS_TYPE_TEMP: 508 v += 2731; 509 l += 2731; 510 h += 2731; 511 break; 512 case AIBS_SENS_TYPE_FAN: 513 break; 514 } 515 so[0] = v; 516 so[1] = l; 517 so[2] = h; 518 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req)); 519 } 520 521 static int 522 aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS) 523 { 524 struct aibs_softc *sc = arg1; 525 struct aibs_sensor *sensor = (void *)(intptr_t)arg2; 526 ACPI_STATUS rs; 527 ACPI_OBJECT p, *bp; 528 ACPI_OBJECT_LIST arg; 529 ACPI_BUFFER buf; 530 ACPI_INTEGER v, l, h; 531 int so[3]; 532 uint32_t *ret; 533 uint32_t cmd[3]; 534 535 cmd[0] = sensor->i; 536 cmd[1] = 0; 537 cmd[2] = 0; 538 p.Type = ACPI_TYPE_BUFFER; 539 p.Buffer.Pointer = (void *)cmd; 540 p.Buffer.Length = sizeof(cmd); 541 arg.Count = 1; 542 arg.Pointer = &p; 543 buf.Pointer = NULL; 544 buf.Length = ACPI_ALLOCATE_BUFFER; 545 ACPI_SERIAL_BEGIN(aibs); 546 rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf, 547 ACPI_TYPE_BUFFER); 548 ACPI_SERIAL_END(aibs); 549 if (ACPI_FAILURE(rs)) { 550 device_printf(sc->sc_dev, "GITM evaluation failed\n"); 551 return (EIO); 552 } 553 bp = buf.Pointer; 554 if (bp->Buffer.Length < 8) { 555 device_printf(sc->sc_dev, "GITM returned short buffer\n"); 556 return (EIO); 557 } 558 ret = (uint32_t *)bp->Buffer.Pointer; 559 if (ret[0] == 0) { 560 device_printf(sc->sc_dev, "GITM returned error status\n"); 561 return (EINVAL); 562 } 563 v = ret[1]; 564 AcpiOsFree(buf.Pointer); 565 566 l = sensor->l; 567 h = sensor->h; 568 569 switch (sensor->t) { 570 case AIBS_SENS_TYPE_VOLT: 571 break; 572 case AIBS_SENS_TYPE_TEMP: 573 v += 2731; 574 l += 2731; 575 h += 2731; 576 break; 577 case AIBS_SENS_TYPE_FAN: 578 break; 579 } 580 so[0] = v; 581 so[1] = l; 582 so[2] = h; 583 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req)); 584 } 585