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