1bbe4a97dSRui Paulo /* $NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $ */ 2bbe4a97dSRui Paulo /* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */ 3bbe4a97dSRui Paulo 4bbe4a97dSRui Paulo /* 5bbe4a97dSRui Paulo * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org> 6bbe4a97dSRui Paulo * 7bbe4a97dSRui Paulo * Permission to use, copy, modify, and distribute this software for any 8bbe4a97dSRui Paulo * purpose with or without fee is hereby granted, provided that the above 9bbe4a97dSRui Paulo * copyright notice and this permission notice appear in all copies. 10bbe4a97dSRui Paulo * 11bbe4a97dSRui Paulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12bbe4a97dSRui Paulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13bbe4a97dSRui Paulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14bbe4a97dSRui Paulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15bbe4a97dSRui Paulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16bbe4a97dSRui Paulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17bbe4a97dSRui Paulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18bbe4a97dSRui Paulo */ 19bbe4a97dSRui Paulo 20bbe4a97dSRui Paulo #include <sys/cdefs.h> 21bbe4a97dSRui Paulo __FBSDID("$FreeBSD$"); 22bbe4a97dSRui Paulo 23bbe4a97dSRui Paulo #include <machine/_inttypes.h> 24bbe4a97dSRui Paulo #include <sys/param.h> 25bbe4a97dSRui Paulo #include <sys/systm.h> 26bbe4a97dSRui Paulo #include <sys/kernel.h> 27bbe4a97dSRui Paulo #include <sys/bus.h> 28bbe4a97dSRui Paulo #include <sys/module.h> 29bbe4a97dSRui Paulo #include <sys/malloc.h> 30bbe4a97dSRui Paulo #include <sys/sysctl.h> 31bbe4a97dSRui Paulo 32bbe4a97dSRui Paulo #include <contrib/dev/acpica/include/acpi.h> 33bbe4a97dSRui Paulo #include <dev/acpica/acpivar.h> 34bbe4a97dSRui Paulo 35bbe4a97dSRui Paulo /* 36bbe4a97dSRui Paulo * ASUSTeK AI Booster (ACPI ASOC ATK0110). 37bbe4a97dSRui Paulo * 38bbe4a97dSRui Paulo * This code was originally written for OpenBSD after the techniques 39bbe4a97dSRui Paulo * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's 40bbe4a97dSRui Paulo * acpi_aiboost.c were verified to be accurate on the actual hardware kindly 41bbe4a97dSRui Paulo * provided by Sam Fourman Jr. It was subsequently ported from OpenBSD to 42bbe4a97dSRui Paulo * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9). 43bbe4a97dSRui Paulo * 44bbe4a97dSRui Paulo * -- Constantine A. Murenin <http://cnst.su/> 45bbe4a97dSRui Paulo */ 46bbe4a97dSRui Paulo 47bbe4a97dSRui Paulo #define _COMPONENT ACPI_OEM 48bbe4a97dSRui Paulo ACPI_MODULE_NAME("aibs"); 49bbe4a97dSRui Paulo ACPI_SERIAL_DECL(aibs, "aibs"); 50bbe4a97dSRui Paulo 51bbe4a97dSRui Paulo #define AIBS_MORE_SENSORS 52bbe4a97dSRui Paulo #define AIBS_VERBOSE 53bbe4a97dSRui Paulo 54bbe4a97dSRui Paulo enum aibs_type { 55bbe4a97dSRui Paulo AIBS_VOLT, 56bbe4a97dSRui Paulo AIBS_TEMP, 57bbe4a97dSRui Paulo AIBS_FAN 58bbe4a97dSRui Paulo }; 59bbe4a97dSRui Paulo 60bbe4a97dSRui Paulo struct aibs_sensor { 61bbe4a97dSRui Paulo ACPI_INTEGER v; 62bbe4a97dSRui Paulo ACPI_INTEGER i; 63bbe4a97dSRui Paulo ACPI_INTEGER l; 64bbe4a97dSRui Paulo ACPI_INTEGER h; 65bbe4a97dSRui Paulo enum aibs_type t; 66bbe4a97dSRui Paulo }; 67bbe4a97dSRui Paulo 68bbe4a97dSRui Paulo struct aibs_softc { 69*a8cd0393SAdrian Chadd device_t sc_dev; 70bbe4a97dSRui Paulo ACPI_HANDLE sc_ah; 71bbe4a97dSRui Paulo 72bbe4a97dSRui Paulo struct aibs_sensor *sc_asens_volt; 73bbe4a97dSRui Paulo struct aibs_sensor *sc_asens_temp; 74bbe4a97dSRui Paulo struct aibs_sensor *sc_asens_fan; 75bbe4a97dSRui Paulo }; 76bbe4a97dSRui Paulo 77bbe4a97dSRui Paulo static int aibs_probe(device_t); 78bbe4a97dSRui Paulo static int aibs_attach(device_t); 79bbe4a97dSRui Paulo static int aibs_detach(device_t); 80bbe4a97dSRui Paulo static int aibs_sysctl(SYSCTL_HANDLER_ARGS); 81bbe4a97dSRui Paulo 82bbe4a97dSRui Paulo static void aibs_attach_sif(struct aibs_softc *, enum aibs_type); 83bbe4a97dSRui Paulo 84bbe4a97dSRui Paulo static device_method_t aibs_methods[] = { 85bbe4a97dSRui Paulo DEVMETHOD(device_probe, aibs_probe), 86bbe4a97dSRui Paulo DEVMETHOD(device_attach, aibs_attach), 87bbe4a97dSRui Paulo DEVMETHOD(device_detach, aibs_detach), 88bbe4a97dSRui Paulo { NULL, NULL } 89bbe4a97dSRui Paulo }; 90bbe4a97dSRui Paulo 91bbe4a97dSRui Paulo static driver_t aibs_driver = { 92bbe4a97dSRui Paulo "aibs", 93bbe4a97dSRui Paulo aibs_methods, 94bbe4a97dSRui Paulo sizeof(struct aibs_softc) 95bbe4a97dSRui Paulo }; 96bbe4a97dSRui Paulo 97bbe4a97dSRui Paulo static devclass_t aibs_devclass; 98bbe4a97dSRui Paulo 99bbe4a97dSRui Paulo DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL); 100bbdf953eSKevin Lo MODULE_DEPEND(aibs, acpi, 1, 1, 1); 101bbe4a97dSRui Paulo 102bbe4a97dSRui Paulo static char* aibs_hids[] = { 103bbe4a97dSRui Paulo "ATK0110", 104bbe4a97dSRui Paulo NULL 105bbe4a97dSRui Paulo }; 106bbe4a97dSRui Paulo 107bbe4a97dSRui Paulo static int 108bbe4a97dSRui Paulo aibs_probe(device_t dev) 109bbe4a97dSRui Paulo { 110bbe4a97dSRui Paulo if (acpi_disabled("aibs") || 111bbe4a97dSRui Paulo ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL) 112bbe4a97dSRui Paulo return ENXIO; 113bbe4a97dSRui Paulo 114bbe4a97dSRui Paulo device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)"); 115bbe4a97dSRui Paulo return 0; 116bbe4a97dSRui Paulo } 117bbe4a97dSRui Paulo 118bbe4a97dSRui Paulo static int 119bbe4a97dSRui Paulo aibs_attach(device_t dev) 120bbe4a97dSRui Paulo { 121bbe4a97dSRui Paulo struct aibs_softc *sc = device_get_softc(dev); 122bbe4a97dSRui Paulo 123bbe4a97dSRui Paulo sc->sc_dev = dev; 124bbe4a97dSRui Paulo sc->sc_ah = acpi_get_handle(dev); 125bbe4a97dSRui Paulo 126bbe4a97dSRui Paulo aibs_attach_sif(sc, AIBS_VOLT); 127bbe4a97dSRui Paulo aibs_attach_sif(sc, AIBS_TEMP); 128bbe4a97dSRui Paulo aibs_attach_sif(sc, AIBS_FAN); 129bbe4a97dSRui Paulo 130bbe4a97dSRui Paulo return 0; 131bbe4a97dSRui Paulo } 132bbe4a97dSRui Paulo 133bbe4a97dSRui Paulo static void 134bbe4a97dSRui Paulo aibs_attach_sif(struct aibs_softc *sc, enum aibs_type st) 135bbe4a97dSRui Paulo { 136bbe4a97dSRui Paulo ACPI_STATUS s; 137bbe4a97dSRui Paulo ACPI_BUFFER b; 138bbe4a97dSRui Paulo ACPI_OBJECT *bp, *o; 139bbe4a97dSRui Paulo int i, n; 140bbe4a97dSRui Paulo const char *node; 141bbe4a97dSRui Paulo char name[] = "?SIF"; 142bbe4a97dSRui Paulo struct aibs_sensor *as; 143bbe4a97dSRui Paulo struct sysctl_oid *so; 144bbe4a97dSRui Paulo 145bbe4a97dSRui Paulo switch (st) { 146bbe4a97dSRui Paulo case AIBS_VOLT: 147bbe4a97dSRui Paulo node = "volt"; 148bbe4a97dSRui Paulo name[0] = 'V'; 149bbe4a97dSRui Paulo break; 150bbe4a97dSRui Paulo case AIBS_TEMP: 151bbe4a97dSRui Paulo node = "temp"; 152bbe4a97dSRui Paulo name[0] = 'T'; 153bbe4a97dSRui Paulo break; 154bbe4a97dSRui Paulo case AIBS_FAN: 155bbe4a97dSRui Paulo node = "fan"; 156bbe4a97dSRui Paulo name[0] = 'F'; 157bbe4a97dSRui Paulo break; 158bbe4a97dSRui Paulo default: 159bbe4a97dSRui Paulo return; 160bbe4a97dSRui Paulo } 161bbe4a97dSRui Paulo 162bbe4a97dSRui Paulo b.Length = ACPI_ALLOCATE_BUFFER; 163bbe4a97dSRui Paulo s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, 164bbe4a97dSRui Paulo ACPI_TYPE_PACKAGE); 165bbe4a97dSRui Paulo if (ACPI_FAILURE(s)) { 166bbe4a97dSRui Paulo device_printf(sc->sc_dev, "%s not found\n", name); 167bbe4a97dSRui Paulo return; 168bbe4a97dSRui Paulo } 169bbe4a97dSRui Paulo 170bbe4a97dSRui Paulo bp = b.Pointer; 171bbe4a97dSRui Paulo o = bp->Package.Elements; 172bbe4a97dSRui Paulo if (o[0].Type != ACPI_TYPE_INTEGER) { 173bbe4a97dSRui Paulo device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); 174bbe4a97dSRui Paulo AcpiOsFree(b.Pointer); 175bbe4a97dSRui Paulo return; 176bbe4a97dSRui Paulo } 177bbe4a97dSRui Paulo 178bbe4a97dSRui Paulo n = o[0].Integer.Value; 179bbe4a97dSRui Paulo if (bp->Package.Count - 1 < n) { 180bbe4a97dSRui Paulo device_printf(sc->sc_dev, "%s: invalid package\n", name); 181bbe4a97dSRui Paulo AcpiOsFree(b.Pointer); 182bbe4a97dSRui Paulo return; 183bbe4a97dSRui Paulo } else if (bp->Package.Count - 1 > n) { 184bbe4a97dSRui Paulo int on = n; 185bbe4a97dSRui Paulo 186bbe4a97dSRui Paulo #ifdef AIBS_MORE_SENSORS 187bbe4a97dSRui Paulo n = bp->Package.Count - 1; 188bbe4a97dSRui Paulo #endif 189bbe4a97dSRui Paulo device_printf(sc->sc_dev, "%s: malformed package: %i/%i" 190bbe4a97dSRui Paulo ", assume %i\n", name, on, bp->Package.Count - 1, n); 191bbe4a97dSRui Paulo } 192bbe4a97dSRui Paulo if (n < 1) { 193bbe4a97dSRui Paulo device_printf(sc->sc_dev, "%s: no members in the package\n", 194bbe4a97dSRui Paulo name); 195bbe4a97dSRui Paulo AcpiOsFree(b.Pointer); 196bbe4a97dSRui Paulo return; 197bbe4a97dSRui Paulo } 198bbe4a97dSRui Paulo 199bbe4a97dSRui Paulo as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO); 200bbe4a97dSRui Paulo if (as == NULL) { 201bbe4a97dSRui Paulo device_printf(sc->sc_dev, "%s: malloc fail\n", name); 202bbe4a97dSRui Paulo AcpiOsFree(b.Pointer); 203bbe4a97dSRui Paulo return; 204bbe4a97dSRui Paulo } 205bbe4a97dSRui Paulo switch (st) { 206bbe4a97dSRui Paulo case AIBS_VOLT: 207bbe4a97dSRui Paulo sc->sc_asens_volt = as; 208bbe4a97dSRui Paulo break; 209bbe4a97dSRui Paulo case AIBS_TEMP: 210bbe4a97dSRui Paulo sc->sc_asens_temp = as; 211bbe4a97dSRui Paulo break; 212bbe4a97dSRui Paulo case AIBS_FAN: 213bbe4a97dSRui Paulo sc->sc_asens_fan = as; 214bbe4a97dSRui Paulo break; 215bbe4a97dSRui Paulo } 216bbe4a97dSRui Paulo 217bbe4a97dSRui Paulo /* sysctl subtree for sensors of this type */ 218bbe4a97dSRui Paulo so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev), 219bbe4a97dSRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st, 220bbe4a97dSRui Paulo node, CTLFLAG_RD, NULL, NULL); 221bbe4a97dSRui Paulo 222bbe4a97dSRui Paulo for (i = 0, o++; i < n; i++, o++) { 223bbe4a97dSRui Paulo ACPI_OBJECT *oi; 224bbe4a97dSRui Paulo char si[3]; 225bbe4a97dSRui Paulo const char *desc; 226bbe4a97dSRui Paulo 227bbe4a97dSRui Paulo /* acpica5 automatically evaluates the referenced package */ 228bbe4a97dSRui Paulo if (o[0].Type != ACPI_TYPE_PACKAGE) { 229bbe4a97dSRui Paulo device_printf(sc->sc_dev, 230bbe4a97dSRui Paulo "%s: %i: not a package: %i type\n", 231bbe4a97dSRui Paulo name, i, o[0].Type); 232bbe4a97dSRui Paulo continue; 233bbe4a97dSRui Paulo } 234bbe4a97dSRui Paulo oi = o[0].Package.Elements; 235bbe4a97dSRui Paulo if (o[0].Package.Count != 5 || 236bbe4a97dSRui Paulo oi[0].Type != ACPI_TYPE_INTEGER || 237bbe4a97dSRui Paulo oi[1].Type != ACPI_TYPE_STRING || 238bbe4a97dSRui Paulo oi[2].Type != ACPI_TYPE_INTEGER || 239bbe4a97dSRui Paulo oi[3].Type != ACPI_TYPE_INTEGER || 240bbe4a97dSRui Paulo oi[4].Type != ACPI_TYPE_INTEGER) { 241bbe4a97dSRui Paulo device_printf(sc->sc_dev, 242bbe4a97dSRui Paulo "%s: %i: invalid package\n", 243bbe4a97dSRui Paulo name, i); 244bbe4a97dSRui Paulo continue; 245bbe4a97dSRui Paulo } 246bbe4a97dSRui Paulo as[i].i = oi[0].Integer.Value; 247bbe4a97dSRui Paulo desc = oi[1].String.Pointer; 248bbe4a97dSRui Paulo as[i].l = oi[2].Integer.Value; 249bbe4a97dSRui Paulo as[i].h = oi[3].Integer.Value; 250bbe4a97dSRui Paulo as[i].t = st; 251bbe4a97dSRui Paulo #ifdef AIBS_VERBOSE 252bbe4a97dSRui Paulo device_printf(sc->sc_dev, "%c%i: " 253bbe4a97dSRui Paulo "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64" " 254bbe4a97dSRui Paulo "0x%"PRIx64"\n", 255bbe4a97dSRui Paulo name[0], i, 2560c10b85aSJung-uk Kim (uint64_t)as[i].i, desc, (int64_t)as[i].l, 2570c10b85aSJung-uk Kim (int64_t)as[i].h, (uint64_t)oi[4].Integer.Value); 258bbe4a97dSRui Paulo #endif 259bbe4a97dSRui Paulo snprintf(si, sizeof(si), "%i", i); 260bbe4a97dSRui Paulo SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev), 261f4f04709SMatthew D Fleming SYSCTL_CHILDREN(so), i, si, CTLTYPE_INT | CTLFLAG_RD, 262bbe4a97dSRui Paulo sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc); 263bbe4a97dSRui Paulo } 264bbe4a97dSRui Paulo 265bbe4a97dSRui Paulo AcpiOsFree(b.Pointer); 266bbe4a97dSRui Paulo } 267bbe4a97dSRui Paulo 268bbe4a97dSRui Paulo static int 269bbe4a97dSRui Paulo aibs_detach(device_t dev) 270bbe4a97dSRui Paulo { 271bbe4a97dSRui Paulo struct aibs_softc *sc = device_get_softc(dev); 272bbe4a97dSRui Paulo 273bbe4a97dSRui Paulo if (sc->sc_asens_volt != NULL) 274bbe4a97dSRui Paulo free(sc->sc_asens_volt, M_DEVBUF); 275bbe4a97dSRui Paulo if (sc->sc_asens_temp != NULL) 276bbe4a97dSRui Paulo free(sc->sc_asens_temp, M_DEVBUF); 277bbe4a97dSRui Paulo if (sc->sc_asens_fan != NULL) 278bbe4a97dSRui Paulo free(sc->sc_asens_fan, M_DEVBUF); 279bbe4a97dSRui Paulo return 0; 280bbe4a97dSRui Paulo } 281bbe4a97dSRui Paulo 282bbe4a97dSRui Paulo #ifdef AIBS_VERBOSE 283bbe4a97dSRui Paulo #define ddevice_printf(x...) device_printf(x) 284bbe4a97dSRui Paulo #else 285bbe4a97dSRui Paulo #define ddevice_printf(x...) 286bbe4a97dSRui Paulo #endif 287bbe4a97dSRui Paulo 288bbe4a97dSRui Paulo static int 289bbe4a97dSRui Paulo aibs_sysctl(SYSCTL_HANDLER_ARGS) 290bbe4a97dSRui Paulo { 291bbe4a97dSRui Paulo struct aibs_softc *sc = arg1; 292bbe4a97dSRui Paulo enum aibs_type st = arg2; 293bbe4a97dSRui Paulo int i = oidp->oid_number; 294bbe4a97dSRui Paulo ACPI_STATUS rs; 295bbe4a97dSRui Paulo ACPI_OBJECT p, *bp; 296bbe4a97dSRui Paulo ACPI_OBJECT_LIST mp; 297bbe4a97dSRui Paulo ACPI_BUFFER b; 298bbe4a97dSRui Paulo char *name; 299bbe4a97dSRui Paulo struct aibs_sensor *as; 300bbe4a97dSRui Paulo ACPI_INTEGER v, l, h; 301bbe4a97dSRui Paulo int so[3]; 302bbe4a97dSRui Paulo 303bbe4a97dSRui Paulo switch (st) { 304bbe4a97dSRui Paulo case AIBS_VOLT: 305bbe4a97dSRui Paulo name = "RVLT"; 306bbe4a97dSRui Paulo as = sc->sc_asens_volt; 307bbe4a97dSRui Paulo break; 308bbe4a97dSRui Paulo case AIBS_TEMP: 309bbe4a97dSRui Paulo name = "RTMP"; 310bbe4a97dSRui Paulo as = sc->sc_asens_temp; 311bbe4a97dSRui Paulo break; 312bbe4a97dSRui Paulo case AIBS_FAN: 313bbe4a97dSRui Paulo name = "RFAN"; 314bbe4a97dSRui Paulo as = sc->sc_asens_fan; 315bbe4a97dSRui Paulo break; 316bbe4a97dSRui Paulo default: 317bbe4a97dSRui Paulo return ENOENT; 318bbe4a97dSRui Paulo } 319bbe4a97dSRui Paulo if (as == NULL) 320bbe4a97dSRui Paulo return ENOENT; 321bbe4a97dSRui Paulo l = as[i].l; 322bbe4a97dSRui Paulo h = as[i].h; 323bbe4a97dSRui Paulo p.Type = ACPI_TYPE_INTEGER; 324bbe4a97dSRui Paulo p.Integer.Value = as[i].i; 325bbe4a97dSRui Paulo mp.Count = 1; 326bbe4a97dSRui Paulo mp.Pointer = &p; 327bbe4a97dSRui Paulo b.Length = ACPI_ALLOCATE_BUFFER; 328bbe4a97dSRui Paulo ACPI_SERIAL_BEGIN(aibs); 329bbe4a97dSRui Paulo rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b, 330bbe4a97dSRui Paulo ACPI_TYPE_INTEGER); 331bbe4a97dSRui Paulo if (ACPI_FAILURE(rs)) { 332bbe4a97dSRui Paulo ddevice_printf(sc->sc_dev, 333bbe4a97dSRui Paulo "%s: %i: evaluation failed\n", 334bbe4a97dSRui Paulo name, i); 335bbe4a97dSRui Paulo ACPI_SERIAL_END(aibs); 336bbe4a97dSRui Paulo return EIO; 337bbe4a97dSRui Paulo } 338bbe4a97dSRui Paulo bp = b.Pointer; 339bbe4a97dSRui Paulo v = bp->Integer.Value; 340bbe4a97dSRui Paulo AcpiOsFree(b.Pointer); 341bbe4a97dSRui Paulo ACPI_SERIAL_END(aibs); 342bbe4a97dSRui Paulo 343bbe4a97dSRui Paulo switch (st) { 344bbe4a97dSRui Paulo case AIBS_VOLT: 345bbe4a97dSRui Paulo break; 346bbe4a97dSRui Paulo case AIBS_TEMP: 347bbe4a97dSRui Paulo v += 2732; 348bbe4a97dSRui Paulo l += 2732; 349bbe4a97dSRui Paulo h += 2732; 350bbe4a97dSRui Paulo break; 351bbe4a97dSRui Paulo case AIBS_FAN: 352bbe4a97dSRui Paulo break; 353bbe4a97dSRui Paulo } 354bbe4a97dSRui Paulo so[0] = v; 355bbe4a97dSRui Paulo so[1] = l; 356bbe4a97dSRui Paulo so[2] = h; 357bbe4a97dSRui Paulo return sysctl_handle_opaque(oidp, &so, sizeof(so), req); 358bbe4a97dSRui Paulo } 359