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