atk0110.c revision 252276
1209523Srpaulo/* $NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $ */ 2209523Srpaulo/* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */ 3209523Srpaulo 4209523Srpaulo/* 5209523Srpaulo * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org> 6209523Srpaulo * 7209523Srpaulo * Permission to use, copy, modify, and distribute this software for any 8209523Srpaulo * purpose with or without fee is hereby granted, provided that the above 9209523Srpaulo * copyright notice and this permission notice appear in all copies. 10209523Srpaulo * 11209523Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12209523Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13209523Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14209523Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15209523Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16209523Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17209523Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18209523Srpaulo */ 19209523Srpaulo 20209523Srpaulo#include <sys/cdefs.h> 21209523Srpaulo__FBSDID("$FreeBSD: head/sys/dev/acpi_support/atk0110.c 252276 2013-06-26 23:52:10Z jkim $"); 22209523Srpaulo 23209523Srpaulo#include <machine/_inttypes.h> 24209523Srpaulo#include <sys/param.h> 25209523Srpaulo#include <sys/systm.h> 26209523Srpaulo#include <sys/kernel.h> 27209523Srpaulo#include <sys/bus.h> 28209523Srpaulo#include <sys/module.h> 29209523Srpaulo#include <sys/malloc.h> 30209523Srpaulo#include <sys/sysctl.h> 31209523Srpaulo 32209523Srpaulo#include <contrib/dev/acpica/include/acpi.h> 33209523Srpaulo#include <dev/acpica/acpivar.h> 34209523Srpaulo 35209523Srpaulo/* 36209523Srpaulo * ASUSTeK AI Booster (ACPI ASOC ATK0110). 37209523Srpaulo * 38209523Srpaulo * This code was originally written for OpenBSD after the techniques 39209523Srpaulo * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's 40209523Srpaulo * acpi_aiboost.c were verified to be accurate on the actual hardware kindly 41209523Srpaulo * provided by Sam Fourman Jr. It was subsequently ported from OpenBSD to 42209523Srpaulo * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9). 43209523Srpaulo * 44209523Srpaulo * -- Constantine A. Murenin <http://cnst.su/> 45209523Srpaulo */ 46209523Srpaulo 47209523Srpaulo#define _COMPONENT ACPI_OEM 48209523SrpauloACPI_MODULE_NAME("aibs"); 49209523SrpauloACPI_SERIAL_DECL(aibs, "aibs"); 50209523Srpaulo 51209523Srpaulo#define AIBS_MORE_SENSORS 52209523Srpaulo#define AIBS_VERBOSE 53209523Srpaulo 54209523Srpauloenum aibs_type { 55209523Srpaulo AIBS_VOLT, 56209523Srpaulo AIBS_TEMP, 57209523Srpaulo AIBS_FAN 58209523Srpaulo}; 59209523Srpaulo 60209523Srpaulostruct aibs_sensor { 61209523Srpaulo ACPI_INTEGER v; 62209523Srpaulo ACPI_INTEGER i; 63209523Srpaulo ACPI_INTEGER l; 64209523Srpaulo ACPI_INTEGER h; 65209523Srpaulo enum aibs_type t; 66209523Srpaulo}; 67209523Srpaulo 68209523Srpaulostruct aibs_softc { 69209523Srpaulo struct device *sc_dev; 70209523Srpaulo ACPI_HANDLE sc_ah; 71209523Srpaulo 72209523Srpaulo struct aibs_sensor *sc_asens_volt; 73209523Srpaulo struct aibs_sensor *sc_asens_temp; 74209523Srpaulo struct aibs_sensor *sc_asens_fan; 75209523Srpaulo}; 76209523Srpaulo 77209523Srpaulostatic int aibs_probe(device_t); 78209523Srpaulostatic int aibs_attach(device_t); 79209523Srpaulostatic int aibs_detach(device_t); 80209523Srpaulostatic int aibs_sysctl(SYSCTL_HANDLER_ARGS); 81209523Srpaulo 82209523Srpaulostatic void aibs_attach_sif(struct aibs_softc *, enum aibs_type); 83209523Srpaulo 84209523Srpaulostatic device_method_t aibs_methods[] = { 85209523Srpaulo DEVMETHOD(device_probe, aibs_probe), 86209523Srpaulo DEVMETHOD(device_attach, aibs_attach), 87209523Srpaulo DEVMETHOD(device_detach, aibs_detach), 88209523Srpaulo { NULL, NULL } 89209523Srpaulo}; 90209523Srpaulo 91209523Srpaulostatic driver_t aibs_driver = { 92209523Srpaulo "aibs", 93209523Srpaulo aibs_methods, 94209523Srpaulo sizeof(struct aibs_softc) 95209523Srpaulo}; 96209523Srpaulo 97209523Srpaulostatic devclass_t aibs_devclass; 98209523Srpaulo 99209523SrpauloDRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL); 100232256SkevloMODULE_DEPEND(aibs, acpi, 1, 1, 1); 101209523Srpaulo 102209523Srpaulostatic char* aibs_hids[] = { 103209523Srpaulo "ATK0110", 104209523Srpaulo NULL 105209523Srpaulo}; 106209523Srpaulo 107209523Srpaulostatic int 108209523Srpauloaibs_probe(device_t dev) 109209523Srpaulo{ 110209523Srpaulo if (acpi_disabled("aibs") || 111209523Srpaulo ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL) 112209523Srpaulo return ENXIO; 113209523Srpaulo 114209523Srpaulo device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)"); 115209523Srpaulo return 0; 116209523Srpaulo} 117209523Srpaulo 118209523Srpaulostatic int 119209523Srpauloaibs_attach(device_t dev) 120209523Srpaulo{ 121209523Srpaulo struct aibs_softc *sc = device_get_softc(dev); 122209523Srpaulo 123209523Srpaulo sc->sc_dev = dev; 124209523Srpaulo sc->sc_ah = acpi_get_handle(dev); 125209523Srpaulo 126209523Srpaulo aibs_attach_sif(sc, AIBS_VOLT); 127209523Srpaulo aibs_attach_sif(sc, AIBS_TEMP); 128209523Srpaulo aibs_attach_sif(sc, AIBS_FAN); 129209523Srpaulo 130209523Srpaulo return 0; 131209523Srpaulo} 132209523Srpaulo 133209523Srpaulostatic void 134209523Srpauloaibs_attach_sif(struct aibs_softc *sc, enum aibs_type st) 135209523Srpaulo{ 136209523Srpaulo ACPI_STATUS s; 137209523Srpaulo ACPI_BUFFER b; 138209523Srpaulo ACPI_OBJECT *bp, *o; 139209523Srpaulo int i, n; 140209523Srpaulo const char *node; 141209523Srpaulo char name[] = "?SIF"; 142209523Srpaulo struct aibs_sensor *as; 143209523Srpaulo struct sysctl_oid *so; 144209523Srpaulo 145209523Srpaulo switch (st) { 146209523Srpaulo case AIBS_VOLT: 147209523Srpaulo node = "volt"; 148209523Srpaulo name[0] = 'V'; 149209523Srpaulo break; 150209523Srpaulo case AIBS_TEMP: 151209523Srpaulo node = "temp"; 152209523Srpaulo name[0] = 'T'; 153209523Srpaulo break; 154209523Srpaulo case AIBS_FAN: 155209523Srpaulo node = "fan"; 156209523Srpaulo name[0] = 'F'; 157209523Srpaulo break; 158209523Srpaulo default: 159209523Srpaulo return; 160209523Srpaulo } 161209523Srpaulo 162209523Srpaulo b.Length = ACPI_ALLOCATE_BUFFER; 163209523Srpaulo s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, 164209523Srpaulo ACPI_TYPE_PACKAGE); 165209523Srpaulo if (ACPI_FAILURE(s)) { 166209523Srpaulo device_printf(sc->sc_dev, "%s not found\n", name); 167209523Srpaulo return; 168209523Srpaulo } 169209523Srpaulo 170209523Srpaulo bp = b.Pointer; 171209523Srpaulo o = bp->Package.Elements; 172209523Srpaulo if (o[0].Type != ACPI_TYPE_INTEGER) { 173209523Srpaulo device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); 174209523Srpaulo AcpiOsFree(b.Pointer); 175209523Srpaulo return; 176209523Srpaulo } 177209523Srpaulo 178209523Srpaulo n = o[0].Integer.Value; 179209523Srpaulo if (bp->Package.Count - 1 < n) { 180209523Srpaulo device_printf(sc->sc_dev, "%s: invalid package\n", name); 181209523Srpaulo AcpiOsFree(b.Pointer); 182209523Srpaulo return; 183209523Srpaulo } else if (bp->Package.Count - 1 > n) { 184209523Srpaulo int on = n; 185209523Srpaulo 186209523Srpaulo#ifdef AIBS_MORE_SENSORS 187209523Srpaulo n = bp->Package.Count - 1; 188209523Srpaulo#endif 189209523Srpaulo device_printf(sc->sc_dev, "%s: malformed package: %i/%i" 190209523Srpaulo ", assume %i\n", name, on, bp->Package.Count - 1, n); 191209523Srpaulo } 192209523Srpaulo if (n < 1) { 193209523Srpaulo device_printf(sc->sc_dev, "%s: no members in the package\n", 194209523Srpaulo name); 195209523Srpaulo AcpiOsFree(b.Pointer); 196209523Srpaulo return; 197209523Srpaulo } 198209523Srpaulo 199209523Srpaulo as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO); 200209523Srpaulo if (as == NULL) { 201209523Srpaulo device_printf(sc->sc_dev, "%s: malloc fail\n", name); 202209523Srpaulo AcpiOsFree(b.Pointer); 203209523Srpaulo return; 204209523Srpaulo } 205209523Srpaulo switch (st) { 206209523Srpaulo case AIBS_VOLT: 207209523Srpaulo sc->sc_asens_volt = as; 208209523Srpaulo break; 209209523Srpaulo case AIBS_TEMP: 210209523Srpaulo sc->sc_asens_temp = as; 211209523Srpaulo break; 212209523Srpaulo case AIBS_FAN: 213209523Srpaulo sc->sc_asens_fan = as; 214209523Srpaulo break; 215209523Srpaulo } 216209523Srpaulo 217209523Srpaulo /* sysctl subtree for sensors of this type */ 218209523Srpaulo so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), 219209523Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st, 220209523Srpaulo node, CTLFLAG_RD, NULL, NULL); 221209523Srpaulo 222209523Srpaulo for (i = 0, o++; i < n; i++, o++) { 223209523Srpaulo ACPI_OBJECT *oi; 224209523Srpaulo char si[3]; 225209523Srpaulo const char *desc; 226209523Srpaulo 227209523Srpaulo /* acpica5 automatically evaluates the referenced package */ 228209523Srpaulo if (o[0].Type != ACPI_TYPE_PACKAGE) { 229209523Srpaulo device_printf(sc->sc_dev, 230209523Srpaulo "%s: %i: not a package: %i type\n", 231209523Srpaulo name, i, o[0].Type); 232209523Srpaulo continue; 233209523Srpaulo } 234209523Srpaulo oi = o[0].Package.Elements; 235209523Srpaulo if (o[0].Package.Count != 5 || 236209523Srpaulo oi[0].Type != ACPI_TYPE_INTEGER || 237209523Srpaulo oi[1].Type != ACPI_TYPE_STRING || 238209523Srpaulo oi[2].Type != ACPI_TYPE_INTEGER || 239209523Srpaulo oi[3].Type != ACPI_TYPE_INTEGER || 240209523Srpaulo oi[4].Type != ACPI_TYPE_INTEGER) { 241209523Srpaulo device_printf(sc->sc_dev, 242209523Srpaulo "%s: %i: invalid package\n", 243209523Srpaulo name, i); 244209523Srpaulo continue; 245209523Srpaulo } 246209523Srpaulo as[i].i = oi[0].Integer.Value; 247209523Srpaulo desc = oi[1].String.Pointer; 248209523Srpaulo as[i].l = oi[2].Integer.Value; 249209523Srpaulo as[i].h = oi[3].Integer.Value; 250209523Srpaulo as[i].t = st; 251209523Srpaulo#ifdef AIBS_VERBOSE 252209523Srpaulo device_printf(sc->sc_dev, "%c%i: " 253209523Srpaulo "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64" " 254209523Srpaulo "0x%"PRIx64"\n", 255209523Srpaulo name[0], i, 256252276Sjkim (uint64_t)as[i].i, desc, (int64_t)as[i].l, 257252276Sjkim (int64_t)as[i].h, (uint64_t)oi[4].Integer.Value); 258209523Srpaulo#endif 259209523Srpaulo snprintf(si, sizeof(si), "%i", i); 260209523Srpaulo SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev), 261217566Smdf SYSCTL_CHILDREN(so), i, si, CTLTYPE_INT | CTLFLAG_RD, 262209523Srpaulo sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc); 263209523Srpaulo } 264209523Srpaulo 265209523Srpaulo AcpiOsFree(b.Pointer); 266209523Srpaulo} 267209523Srpaulo 268209523Srpaulostatic int 269209523Srpauloaibs_detach(device_t dev) 270209523Srpaulo{ 271209523Srpaulo struct aibs_softc *sc = device_get_softc(dev); 272209523Srpaulo 273209523Srpaulo if (sc->sc_asens_volt != NULL) 274209523Srpaulo free(sc->sc_asens_volt, M_DEVBUF); 275209523Srpaulo if (sc->sc_asens_temp != NULL) 276209523Srpaulo free(sc->sc_asens_temp, M_DEVBUF); 277209523Srpaulo if (sc->sc_asens_fan != NULL) 278209523Srpaulo free(sc->sc_asens_fan, M_DEVBUF); 279209523Srpaulo return 0; 280209523Srpaulo} 281209523Srpaulo 282209523Srpaulo#ifdef AIBS_VERBOSE 283209523Srpaulo#define ddevice_printf(x...) device_printf(x) 284209523Srpaulo#else 285209523Srpaulo#define ddevice_printf(x...) 286209523Srpaulo#endif 287209523Srpaulo 288209523Srpaulostatic int 289209523Srpauloaibs_sysctl(SYSCTL_HANDLER_ARGS) 290209523Srpaulo{ 291209523Srpaulo struct aibs_softc *sc = arg1; 292209523Srpaulo enum aibs_type st = arg2; 293209523Srpaulo int i = oidp->oid_number; 294209523Srpaulo ACPI_STATUS rs; 295209523Srpaulo ACPI_OBJECT p, *bp; 296209523Srpaulo ACPI_OBJECT_LIST mp; 297209523Srpaulo ACPI_BUFFER b; 298209523Srpaulo char *name; 299209523Srpaulo struct aibs_sensor *as; 300209523Srpaulo ACPI_INTEGER v, l, h; 301209523Srpaulo int so[3]; 302209523Srpaulo 303209523Srpaulo switch (st) { 304209523Srpaulo case AIBS_VOLT: 305209523Srpaulo name = "RVLT"; 306209523Srpaulo as = sc->sc_asens_volt; 307209523Srpaulo break; 308209523Srpaulo case AIBS_TEMP: 309209523Srpaulo name = "RTMP"; 310209523Srpaulo as = sc->sc_asens_temp; 311209523Srpaulo break; 312209523Srpaulo case AIBS_FAN: 313209523Srpaulo name = "RFAN"; 314209523Srpaulo as = sc->sc_asens_fan; 315209523Srpaulo break; 316209523Srpaulo default: 317209523Srpaulo return ENOENT; 318209523Srpaulo } 319209523Srpaulo if (as == NULL) 320209523Srpaulo return ENOENT; 321209523Srpaulo l = as[i].l; 322209523Srpaulo h = as[i].h; 323209523Srpaulo p.Type = ACPI_TYPE_INTEGER; 324209523Srpaulo p.Integer.Value = as[i].i; 325209523Srpaulo mp.Count = 1; 326209523Srpaulo mp.Pointer = &p; 327209523Srpaulo b.Length = ACPI_ALLOCATE_BUFFER; 328209523Srpaulo ACPI_SERIAL_BEGIN(aibs); 329209523Srpaulo rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b, 330209523Srpaulo ACPI_TYPE_INTEGER); 331209523Srpaulo if (ACPI_FAILURE(rs)) { 332209523Srpaulo ddevice_printf(sc->sc_dev, 333209523Srpaulo "%s: %i: evaluation failed\n", 334209523Srpaulo name, i); 335209523Srpaulo ACPI_SERIAL_END(aibs); 336209523Srpaulo return EIO; 337209523Srpaulo } 338209523Srpaulo bp = b.Pointer; 339209523Srpaulo v = bp->Integer.Value; 340209523Srpaulo AcpiOsFree(b.Pointer); 341209523Srpaulo ACPI_SERIAL_END(aibs); 342209523Srpaulo 343209523Srpaulo switch (st) { 344209523Srpaulo case AIBS_VOLT: 345209523Srpaulo break; 346209523Srpaulo case AIBS_TEMP: 347209523Srpaulo v += 2732; 348209523Srpaulo l += 2732; 349209523Srpaulo h += 2732; 350209523Srpaulo break; 351209523Srpaulo case AIBS_FAN: 352209523Srpaulo break; 353209523Srpaulo } 354209523Srpaulo so[0] = v; 355209523Srpaulo so[1] = l; 356209523Srpaulo so[2] = h; 357209523Srpaulo return sysctl_handle_opaque(oidp, &so, sizeof(so), req); 358209523Srpaulo} 359