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