atk0110.c revision 256281
159191Skris/* $NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $ */ 259191Skris/* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */ 355714Skris 4296341Sdelphij/* 5296341Sdelphij * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org> 6296341Sdelphij * 7296341Sdelphij * Permission to use, copy, modify, and distribute this software for any 8296341Sdelphij * purpose with or without fee is hereby granted, provided that the above 9296341Sdelphij * copyright notice and this permission notice appear in all copies. 10296341Sdelphij * 11296341Sdelphij * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12296341Sdelphij * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13296341Sdelphij * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14296341Sdelphij * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15296341Sdelphij * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16296341Sdelphij * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17296341Sdelphij * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18296341Sdelphij */ 19296341Sdelphij 20296341Sdelphij#include <sys/cdefs.h> 21296341Sdelphij__FBSDID("$FreeBSD: stable/10/sys/dev/acpi_support/atk0110.c 252276 2013-06-26 23:52:10Z jkim $"); 22296341Sdelphij 23296341Sdelphij#include <machine/_inttypes.h> 24296341Sdelphij#include <sys/param.h> 25296341Sdelphij#include <sys/systm.h> 26296341Sdelphij#include <sys/kernel.h> 27296341Sdelphij#include <sys/bus.h> 28296341Sdelphij#include <sys/module.h> 29296341Sdelphij#include <sys/malloc.h> 30296341Sdelphij#include <sys/sysctl.h> 31296341Sdelphij 32296341Sdelphij#include <contrib/dev/acpica/include/acpi.h> 33296341Sdelphij#include <dev/acpica/acpivar.h> 34296341Sdelphij 35296341Sdelphij/* 36296341Sdelphij * ASUSTeK AI Booster (ACPI ASOC ATK0110). 37296341Sdelphij * 38296341Sdelphij * This code was originally written for OpenBSD after the techniques 39296341Sdelphij * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's 40296341Sdelphij * acpi_aiboost.c were verified to be accurate on the actual hardware kindly 41296341Sdelphij * provided by Sam Fourman Jr. It was subsequently ported from OpenBSD to 42296341Sdelphij * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9). 43296341Sdelphij * 44296341Sdelphij * -- Constantine A. Murenin <http://cnst.su/> 45296341Sdelphij */ 46296341Sdelphij 47296341Sdelphij#define _COMPONENT ACPI_OEM 48296341SdelphijACPI_MODULE_NAME("aibs"); 49296341SdelphijACPI_SERIAL_DECL(aibs, "aibs"); 5055714Skris 51296341Sdelphij#define AIBS_MORE_SENSORS 52296341Sdelphij#define AIBS_VERBOSE 53296341Sdelphij 54296341Sdelphijenum aibs_type { 55296341Sdelphij AIBS_VOLT, 56296341Sdelphij AIBS_TEMP, 5755714Skris AIBS_FAN 5855714Skris}; 59296341Sdelphij 60296341Sdelphijstruct aibs_sensor { 61296341Sdelphij ACPI_INTEGER v; 62296341Sdelphij ACPI_INTEGER i; 63238405Sjkim ACPI_INTEGER l; 6455714Skris ACPI_INTEGER h; 6555714Skris enum aibs_type t; 66296341Sdelphij}; 67296341Sdelphij 68296341Sdelphijstruct aibs_softc { 69296341Sdelphij struct device *sc_dev; 70109998Smarkm ACPI_HANDLE sc_ah; 71296341Sdelphij 7255714Skris struct aibs_sensor *sc_asens_volt; 73109998Smarkm struct aibs_sensor *sc_asens_temp; 74296341Sdelphij struct aibs_sensor *sc_asens_fan; 7559191Skris}; 76296341Sdelphij 77296341Sdelphijstatic int aibs_probe(device_t); 78109998Smarkmstatic int aibs_attach(device_t); 79296341Sdelphijstatic int aibs_detach(device_t); 8055714Skrisstatic int aibs_sysctl(SYSCTL_HANDLER_ARGS); 81296341Sdelphij 82296341Sdelphijstatic void aibs_attach_sif(struct aibs_softc *, enum aibs_type); 83296341Sdelphij 84109998Smarkmstatic device_method_t aibs_methods[] = { 85296341Sdelphij DEVMETHOD(device_probe, aibs_probe), 8655714Skris DEVMETHOD(device_attach, aibs_attach), 87109998Smarkm DEVMETHOD(device_detach, aibs_detach), 88296341Sdelphij { NULL, NULL } 8968651Skris}; 90109998Smarkm 91296341Sdelphijstatic driver_t aibs_driver = { 9255714Skris "aibs", 93109998Smarkm aibs_methods, 94296341Sdelphij sizeof(struct aibs_softc) 9555714Skris}; 96160814Ssimon 97296341Sdelphijstatic devclass_t aibs_devclass; 98160814Ssimon 99160814SsimonDRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL); 100296341SdelphijMODULE_DEPEND(aibs, acpi, 1, 1, 1); 101160814Ssimon 102296341Sdelphijstatic char* aibs_hids[] = { 103109998Smarkm "ATK0110", 104296341Sdelphij NULL 10555714Skris}; 106109998Smarkm 107296341Sdelphijstatic int 10855714Skrisaibs_probe(device_t dev) 109296341Sdelphij{ 110269686Sjkim if (acpi_disabled("aibs") || 111296341Sdelphij ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL) 11255714Skris return ENXIO; 113269686Sjkim 114296341Sdelphij device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)"); 11555714Skris return 0; 116111147Snectar} 117296341Sdelphij 118111147Snectarstatic int 119269686Sjkimaibs_attach(device_t dev) 120296341Sdelphij{ 12155714Skris struct aibs_softc *sc = device_get_softc(dev); 122296341Sdelphij 123296341Sdelphij sc->sc_dev = dev; 124194206Ssimon sc->sc_ah = acpi_get_handle(dev); 125296341Sdelphij 126194206Ssimon aibs_attach_sif(sc, AIBS_VOLT); 127296341Sdelphij aibs_attach_sif(sc, AIBS_TEMP); 128296341Sdelphij aibs_attach_sif(sc, AIBS_FAN); 129269686Sjkim 130296341Sdelphij return 0; 13155714Skris} 132296341Sdelphij 133109998Smarkmstatic void 134296341Sdelphijaibs_attach_sif(struct aibs_softc *sc, enum aibs_type st) 13555714Skris{ 136296341Sdelphij ACPI_STATUS s; 137296341Sdelphij ACPI_BUFFER b; 138296341Sdelphij ACPI_OBJECT *bp, *o; 139296341Sdelphij int i, n; 140296341Sdelphij const char *node; 141296341Sdelphij char name[] = "?SIF"; 142296341Sdelphij struct aibs_sensor *as; 143111147Snectar struct sysctl_oid *so; 144296341Sdelphij 145111147Snectar switch (st) { 146238405Sjkim case AIBS_VOLT: 147296341Sdelphij node = "volt"; 148238405Sjkim name[0] = 'V'; 149296341Sdelphij break; 150296341Sdelphij case AIBS_TEMP: 151238405Sjkim node = "temp"; 152296341Sdelphij name[0] = 'T'; 153238405Sjkim break; 154109998Smarkm case AIBS_FAN: 155296341Sdelphij node = "fan"; 156109998Smarkm name[0] = 'F'; 157109998Smarkm break; 158296341Sdelphij default: 159109998Smarkm return; 160109998Smarkm } 161296341Sdelphij 162109998Smarkm b.Length = ACPI_ALLOCATE_BUFFER; 163109998Smarkm s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, 164296341Sdelphij ACPI_TYPE_PACKAGE); 165109998Smarkm if (ACPI_FAILURE(s)) { 166109998Smarkm device_printf(sc->sc_dev, "%s not found\n", name); 167296341Sdelphij return; 168109998Smarkm } 169109998Smarkm 170296341Sdelphij bp = b.Pointer; 171109998Smarkm o = bp->Package.Elements; 172109998Smarkm if (o[0].Type != ACPI_TYPE_INTEGER) { 173296341Sdelphij device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); 174109998Smarkm AcpiOsFree(b.Pointer); 175109998Smarkm return; 176296341Sdelphij } 177109998Smarkm 178109998Smarkm n = o[0].Integer.Value; 179296341Sdelphij if (bp->Package.Count - 1 < n) { 180109998Smarkm device_printf(sc->sc_dev, "%s: invalid package\n", name); 181109998Smarkm AcpiOsFree(b.Pointer); 182296341Sdelphij return; 183109998Smarkm } else if (bp->Package.Count - 1 > n) { 184109998Smarkm int on = n; 185296341Sdelphij 186109998Smarkm#ifdef AIBS_MORE_SENSORS 187109998Smarkm n = bp->Package.Count - 1; 188296341Sdelphij#endif 189109998Smarkm device_printf(sc->sc_dev, "%s: malformed package: %i/%i" 190109998Smarkm ", assume %i\n", name, on, bp->Package.Count - 1, n); 191296341Sdelphij } 192109998Smarkm if (n < 1) { 193162911Ssimon device_printf(sc->sc_dev, "%s: no members in the package\n", 194296341Sdelphij name); 195162911Ssimon AcpiOsFree(b.Pointer); 196162911Ssimon return; 197296341Sdelphij } 198162911Ssimon 199162911Ssimon as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO); 200296341Sdelphij if (as == NULL) { 201162911Ssimon device_printf(sc->sc_dev, "%s: malloc fail\n", name); 202162911Ssimon AcpiOsFree(b.Pointer); 203296341Sdelphij return; 204162911Ssimon } 205162911Ssimon switch (st) { 206296341Sdelphij case AIBS_VOLT: 207162911Ssimon sc->sc_asens_volt = as; 208162911Ssimon break; 209296341Sdelphij case AIBS_TEMP: 210162911Ssimon sc->sc_asens_temp = as; 211296341Sdelphij break; 212238405Sjkim case AIBS_FAN: 213296341Sdelphij sc->sc_asens_fan = as; 214238405Sjkim break; 215109998Smarkm } 216296341Sdelphij 21755714Skris /* sysctl subtree for sensors of this type */ 218109998Smarkm so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), 219296341Sdelphij SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st, 22055714Skris node, CTLFLAG_RD, NULL, NULL); 221109998Smarkm 222296341Sdelphij for (i = 0, o++; i < n; i++, o++) { 22355714Skris ACPI_OBJECT *oi; 224109998Smarkm char si[3]; 225296341Sdelphij const char *desc; 22655714Skris 227194206Ssimon /* acpica5 automatically evaluates the referenced package */ 228296341Sdelphij if (o[0].Type != ACPI_TYPE_PACKAGE) { 229194206Ssimon device_printf(sc->sc_dev, 230109998Smarkm "%s: %i: not a package: %i type\n", 231296341Sdelphij name, i, o[0].Type); 23255714Skris continue; 233109998Smarkm } 234296341Sdelphij oi = o[0].Package.Elements; 23559191Skris if (o[0].Package.Count != 5 || 236109998Smarkm oi[0].Type != ACPI_TYPE_INTEGER || 237296341Sdelphij oi[1].Type != ACPI_TYPE_STRING || 23855714Skris oi[2].Type != ACPI_TYPE_INTEGER || 239109998Smarkm oi[3].Type != ACPI_TYPE_INTEGER || 240296341Sdelphij oi[4].Type != ACPI_TYPE_INTEGER) { 24155714Skris device_printf(sc->sc_dev, 242109998Smarkm "%s: %i: invalid package\n", 243296341Sdelphij name, i); 24455714Skris continue; 245109998Smarkm } 246296341Sdelphij as[i].i = oi[0].Integer.Value; 24755714Skris desc = oi[1].String.Pointer; 248109998Smarkm as[i].l = oi[2].Integer.Value; 249296341Sdelphij as[i].h = oi[3].Integer.Value; 25055714Skris as[i].t = st; 251109998Smarkm#ifdef AIBS_VERBOSE 252296341Sdelphij device_printf(sc->sc_dev, "%c%i: " 25355714Skris "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64" " 254109998Smarkm "0x%"PRIx64"\n", 255296341Sdelphij name[0], i, 25655714Skris (uint64_t)as[i].i, desc, (int64_t)as[i].l, 257109998Smarkm (int64_t)as[i].h, (uint64_t)oi[4].Integer.Value); 258296341Sdelphij#endif 25955714Skris snprintf(si, sizeof(si), "%i", i); 260109998Smarkm SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev), 261296341Sdelphij SYSCTL_CHILDREN(so), i, si, CTLTYPE_INT | CTLFLAG_RD, 26255714Skris sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc); 263109998Smarkm } 264296341Sdelphij 26555714Skris AcpiOsFree(b.Pointer); 266109998Smarkm} 267296341Sdelphij 26855714Skrisstatic int 269109998Smarkmaibs_detach(device_t dev) 270296341Sdelphij{ 27155714Skris struct aibs_softc *sc = device_get_softc(dev); 272109998Smarkm 273296341Sdelphij if (sc->sc_asens_volt != NULL) 27455714Skris free(sc->sc_asens_volt, M_DEVBUF); 275109998Smarkm if (sc->sc_asens_temp != NULL) 276296341Sdelphij free(sc->sc_asens_temp, M_DEVBUF); 27755714Skris if (sc->sc_asens_fan != NULL) 278109998Smarkm free(sc->sc_asens_fan, M_DEVBUF); 279296341Sdelphij return 0; 28055714Skris} 281109998Smarkm 282296341Sdelphij#ifdef AIBS_VERBOSE 28355714Skris#define ddevice_printf(x...) device_printf(x) 284109998Smarkm#else 285296341Sdelphij#define ddevice_printf(x...) 28655714Skris#endif 287109998Smarkm 288296341Sdelphijstatic int 28955714Skrisaibs_sysctl(SYSCTL_HANDLER_ARGS) 290109998Smarkm{ 291296341Sdelphij struct aibs_softc *sc = arg1; 29255714Skris enum aibs_type st = arg2; 293109998Smarkm int i = oidp->oid_number; 294296341Sdelphij ACPI_STATUS rs; 29555714Skris ACPI_OBJECT p, *bp; 296194206Ssimon ACPI_OBJECT_LIST mp; 297296341Sdelphij ACPI_BUFFER b; 298194206Ssimon char *name; 299194206Ssimon struct aibs_sensor *as; 300296341Sdelphij ACPI_INTEGER v, l, h; 301194206Ssimon int so[3]; 302194206Ssimon 303296341Sdelphij switch (st) { 304194206Ssimon case AIBS_VOLT: 305194206Ssimon name = "RVLT"; 306296341Sdelphij as = sc->sc_asens_volt; 307194206Ssimon break; 308109998Smarkm case AIBS_TEMP: 309296341Sdelphij name = "RTMP"; 31055714Skris as = sc->sc_asens_temp; 311109998Smarkm break; 312296341Sdelphij case AIBS_FAN: 31355714Skris name = "RFAN"; 314109998Smarkm as = sc->sc_asens_fan; 315296341Sdelphij break; 31655714Skris default: 317109998Smarkm return ENOENT; 318296341Sdelphij } 31955714Skris if (as == NULL) 320109998Smarkm return ENOENT; 321296341Sdelphij l = as[i].l; 32259191Skris h = as[i].h; 323109998Smarkm p.Type = ACPI_TYPE_INTEGER; 324296341Sdelphij p.Integer.Value = as[i].i; 32559191Skris mp.Count = 1; 326109998Smarkm mp.Pointer = &p; 327296341Sdelphij b.Length = ACPI_ALLOCATE_BUFFER; 32855714Skris ACPI_SERIAL_BEGIN(aibs); 329109998Smarkm rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b, 330296341Sdelphij ACPI_TYPE_INTEGER); 33155714Skris if (ACPI_FAILURE(rs)) { 332109998Smarkm ddevice_printf(sc->sc_dev, 333296341Sdelphij "%s: %i: evaluation failed\n", 33455714Skris name, i); 335109998Smarkm ACPI_SERIAL_END(aibs); 336296341Sdelphij return EIO; 33755714Skris } 338109998Smarkm bp = b.Pointer; 339296341Sdelphij v = bp->Integer.Value; 34055714Skris AcpiOsFree(b.Pointer); 341109998Smarkm ACPI_SERIAL_END(aibs); 342296341Sdelphij 34355714Skris switch (st) { 344109998Smarkm case AIBS_VOLT: 345296341Sdelphij break; 34655714Skris case AIBS_TEMP: 347109998Smarkm v += 2732; 348296341Sdelphij l += 2732; 34955714Skris h += 2732; 350109998Smarkm break; 351296341Sdelphij case AIBS_FAN: 35255714Skris break; 353109998Smarkm } 354296341Sdelphij so[0] = v; 35555714Skris so[1] = l; 356109998Smarkm so[2] = h; 357296341Sdelphij return sysctl_handle_opaque(oidp, &so, sizeof(so), req); 35855714Skris} 359109998Smarkm