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 (0); 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, 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, NULL, NULL); 323 } 324 aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr); 325 *s_idx += 1; 326 } 327 328 AcpiOsFree(buf.Pointer); 329 return (0); 330 } 331 332 static int 333 aibs_attach_sif(struct aibs_softc *sc, int st) 334 { 335 char name[] = "?SIF"; 336 ACPI_STATUS s; 337 ACPI_BUFFER b; 338 ACPI_OBJECT *bp, *o; 339 const char *node; 340 struct aibs_sensor *as; 341 struct sysctl_oid **so; 342 int i, n; 343 int err; 344 345 switch (st) { 346 case AIBS_SENS_TYPE_VOLT: 347 node = "volt"; 348 name[0] = 'V'; 349 so = &sc->sc_volt_sysctl; 350 break; 351 case AIBS_SENS_TYPE_TEMP: 352 node = "temp"; 353 name[0] = 'T'; 354 so = &sc->sc_temp_sysctl; 355 break; 356 case AIBS_SENS_TYPE_FAN: 357 node = "fan"; 358 name[0] = 'F'; 359 so = &sc->sc_fan_sysctl; 360 break; 361 default: 362 panic("Unsupported sensor type %d", st); 363 } 364 365 b.Length = ACPI_ALLOCATE_BUFFER; 366 s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, 367 ACPI_TYPE_PACKAGE); 368 if (ACPI_FAILURE(s)) { 369 device_printf(sc->sc_dev, "%s not found\n", name); 370 return (ENXIO); 371 } 372 373 bp = b.Pointer; 374 o = bp->Package.Elements; 375 if (o[0].Type != ACPI_TYPE_INTEGER) { 376 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); 377 AcpiOsFree(b.Pointer); 378 return (ENXIO); 379 } 380 381 n = o[0].Integer.Value; 382 if (bp->Package.Count - 1 < n) { 383 device_printf(sc->sc_dev, "%s: invalid package\n", name); 384 AcpiOsFree(b.Pointer); 385 return (ENXIO); 386 } else if (bp->Package.Count - 1 > n) { 387 int on = n; 388 389 #ifdef AIBS_MORE_SENSORS 390 n = bp->Package.Count - 1; 391 #endif 392 device_printf(sc->sc_dev, "%s: malformed package: %i/%i" 393 ", assume %i\n", name, on, bp->Package.Count - 1, n); 394 } 395 if (n < 1) { 396 device_printf(sc->sc_dev, "%s: no members in the package\n", 397 name); 398 AcpiOsFree(b.Pointer); 399 return (ENXIO); 400 } 401 402 as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO); 403 switch (st) { 404 case AIBS_SENS_TYPE_VOLT: 405 sc->sc_asens_volt = as; 406 break; 407 case AIBS_SENS_TYPE_TEMP: 408 sc->sc_asens_temp = as; 409 break; 410 case AIBS_SENS_TYPE_FAN: 411 sc->sc_asens_fan = as; 412 break; 413 } 414 415 /* sysctl subtree for sensors of this type */ 416 *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), 417 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st, 418 node, CTLFLAG_RD, NULL, NULL); 419 420 for (i = 0, o++; i < n; i++, o++) { 421 const char *descr; 422 423 err = aibs_add_sensor(sc, o, &as[i], &descr); 424 if (err == 0) 425 aibs_sensor_added(sc, *so, node, i, &as[i], descr); 426 } 427 428 AcpiOsFree(b.Pointer); 429 return (0); 430 } 431 432 static int 433 aibs_detach(device_t dev) 434 { 435 struct aibs_softc *sc = device_get_softc(dev); 436 437 if (sc->sc_asens_volt != NULL) 438 free(sc->sc_asens_volt, M_DEVBUF); 439 if (sc->sc_asens_temp != NULL) 440 free(sc->sc_asens_temp, M_DEVBUF); 441 if (sc->sc_asens_fan != NULL) 442 free(sc->sc_asens_fan, M_DEVBUF); 443 if (sc->sc_asens_all != NULL) 444 free(sc->sc_asens_all, M_DEVBUF); 445 return (0); 446 } 447 448 #ifdef AIBS_VERBOSE 449 #define ddevice_printf(x...) device_printf(x) 450 #else 451 #define ddevice_printf(x...) 452 #endif 453 454 static int 455 aibs_sysctl(SYSCTL_HANDLER_ARGS) 456 { 457 struct aibs_softc *sc = arg1; 458 struct aibs_sensor *sensor = (void *)(intptr_t)arg2; 459 int i = oidp->oid_number; 460 ACPI_STATUS rs; 461 ACPI_OBJECT p, *bp; 462 ACPI_OBJECT_LIST mp; 463 ACPI_BUFFER b; 464 char *name; 465 ACPI_INTEGER v, l, h; 466 int so[3]; 467 468 switch (sensor->t) { 469 case AIBS_SENS_TYPE_VOLT: 470 name = "RVLT"; 471 break; 472 case AIBS_SENS_TYPE_TEMP: 473 name = "RTMP"; 474 break; 475 case AIBS_SENS_TYPE_FAN: 476 name = "RFAN"; 477 break; 478 default: 479 return (ENOENT); 480 } 481 l = sensor->l; 482 h = sensor->h; 483 p.Type = ACPI_TYPE_INTEGER; 484 p.Integer.Value = sensor->i; 485 mp.Count = 1; 486 mp.Pointer = &p; 487 b.Length = ACPI_ALLOCATE_BUFFER; 488 ACPI_SERIAL_BEGIN(aibs); 489 rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b, 490 ACPI_TYPE_INTEGER); 491 if (ACPI_FAILURE(rs)) { 492 ddevice_printf(sc->sc_dev, 493 "%s: %i: evaluation failed\n", 494 name, i); 495 ACPI_SERIAL_END(aibs); 496 return (EIO); 497 } 498 bp = b.Pointer; 499 v = bp->Integer.Value; 500 AcpiOsFree(b.Pointer); 501 ACPI_SERIAL_END(aibs); 502 503 switch (sensor->t) { 504 case AIBS_SENS_TYPE_VOLT: 505 break; 506 case AIBS_SENS_TYPE_TEMP: 507 v += 2731; 508 l += 2731; 509 h += 2731; 510 break; 511 case AIBS_SENS_TYPE_FAN: 512 break; 513 } 514 so[0] = v; 515 so[1] = l; 516 so[2] = h; 517 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req)); 518 } 519 520 static int 521 aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS) 522 { 523 struct aibs_softc *sc = arg1; 524 struct aibs_sensor *sensor = (void *)(intptr_t)arg2; 525 ACPI_STATUS rs; 526 ACPI_OBJECT p, *bp; 527 ACPI_OBJECT_LIST arg; 528 ACPI_BUFFER buf; 529 ACPI_INTEGER v, l, h; 530 int so[3]; 531 uint32_t *ret; 532 uint32_t cmd[3]; 533 534 cmd[0] = sensor->i; 535 cmd[1] = 0; 536 cmd[2] = 0; 537 p.Type = ACPI_TYPE_BUFFER; 538 p.Buffer.Pointer = (void *)cmd; 539 p.Buffer.Length = sizeof(cmd); 540 arg.Count = 1; 541 arg.Pointer = &p; 542 buf.Pointer = NULL; 543 buf.Length = ACPI_ALLOCATE_BUFFER; 544 ACPI_SERIAL_BEGIN(aibs); 545 rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf, 546 ACPI_TYPE_BUFFER); 547 ACPI_SERIAL_END(aibs); 548 if (ACPI_FAILURE(rs)) { 549 device_printf(sc->sc_dev, "GITM evaluation failed\n"); 550 return (EIO); 551 } 552 bp = buf.Pointer; 553 if (bp->Buffer.Length < 8) { 554 device_printf(sc->sc_dev, "GITM returned short buffer\n"); 555 return (EIO); 556 } 557 ret = (uint32_t *)bp->Buffer.Pointer; 558 if (ret[0] == 0) { 559 device_printf(sc->sc_dev, "GITM returned error status\n"); 560 return (EINVAL); 561 } 562 v = ret[1]; 563 AcpiOsFree(buf.Pointer); 564 565 l = sensor->l; 566 h = sensor->h; 567 568 switch (sensor->t) { 569 case AIBS_SENS_TYPE_VOLT: 570 break; 571 case AIBS_SENS_TYPE_TEMP: 572 v += 2731; 573 l += 2731; 574 h += 2731; 575 break; 576 case AIBS_SENS_TYPE_FAN: 577 break; 578 } 579 so[0] = v; 580 so[1] = l; 581 so[2] = h; 582 return (sysctl_handle_opaque(oidp, &so, sizeof(so), req)); 583 } 584