1 /* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Constantine A. Murenin <cnst+dfly@bugmail.mojo.ru> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <machine/inttypes.h> 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/kernel.h> 24 #include <sys/bus.h> 25 #include <sys/module.h> 26 #include <sys/malloc.h> 27 28 #include <sys/sensors.h> 29 30 #include "acpi.h" 31 #include "acpivar.h" 32 33 /* 34 * ASUSTeK AI Booster (ACPI ATK0110). 35 * 36 * This code was originally written for OpenBSD after the techniques 37 * described in the Linux's asus_atk0110.c and FreeBSD's acpi_aiboost.c 38 * were verified to be accurate on the actual hardware kindly provided by 39 * Sam Fourman Jr. It was subsequently ported from OpenBSD to DragonFly BSD. 40 * 41 * -- Constantine A. Murenin <http://cnst.su/> 42 */ 43 44 #define AIBS_MORE_SENSORS 45 #define AIBS_VERBOSE 46 47 struct aibs_sensor { 48 struct ksensor s; 49 UINT64 i; 50 UINT64 l; 51 UINT64 h; 52 }; 53 54 struct aibs_softc { 55 device_t sc_dev; 56 ACPI_HANDLE sc_ah; 57 58 struct aibs_sensor *sc_asens_volt; 59 struct aibs_sensor *sc_asens_temp; 60 struct aibs_sensor *sc_asens_fan; 61 62 struct ksensordev sc_sensordev; 63 }; 64 65 66 static int aibs_probe(device_t); 67 static int aibs_attach(device_t); 68 static int aibs_detach(device_t); 69 static void aibs_refresh(void *); 70 71 static void aibs_attach_sif(struct aibs_softc *, enum sensor_type); 72 static void aibs_refresh_r(struct aibs_softc *, enum sensor_type); 73 74 75 static device_method_t aibs_methods[] = { 76 DEVMETHOD(device_probe, aibs_probe), 77 DEVMETHOD(device_attach, aibs_attach), 78 DEVMETHOD(device_detach, aibs_detach), 79 { NULL, NULL } 80 }; 81 82 static driver_t aibs_driver = { 83 "aibs", 84 aibs_methods, 85 sizeof(struct aibs_softc) 86 }; 87 88 static devclass_t aibs_devclass; 89 90 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL); 91 MODULE_DEPEND(aibs, acpi, 1, 1, 1); 92 93 static char* aibs_hids[] = { 94 "ATK0110", 95 NULL 96 }; 97 98 static int 99 aibs_probe(device_t dev) 100 { 101 102 if (acpi_disabled("aibs") || 103 ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL) 104 return ENXIO; 105 106 device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)"); 107 return 0; 108 } 109 110 static int 111 aibs_attach(device_t dev) 112 { 113 struct aibs_softc *sc; 114 115 sc = device_get_softc(dev); 116 sc->sc_dev = dev; 117 sc->sc_ah = acpi_get_handle(dev); 118 119 strlcpy(sc->sc_sensordev.xname, device_get_nameunit(dev), 120 sizeof(sc->sc_sensordev.xname)); 121 122 aibs_attach_sif(sc, SENSOR_VOLTS_DC); 123 aibs_attach_sif(sc, SENSOR_TEMP); 124 aibs_attach_sif(sc, SENSOR_FANRPM); 125 126 if (sc->sc_sensordev.sensors_count == 0) { 127 device_printf(dev, "no sensors found\n"); 128 return ENXIO; 129 } 130 131 sensor_task_register(sc, aibs_refresh, 5); 132 133 sensordev_install(&sc->sc_sensordev); 134 return 0; 135 } 136 137 static void 138 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st) 139 { 140 ACPI_STATUS s; 141 ACPI_BUFFER b; 142 ACPI_OBJECT *bp, *o; 143 int i, n; 144 char name[] = "?SIF"; 145 struct aibs_sensor *as; 146 147 switch (st) { 148 case SENSOR_TEMP: 149 name[0] = 'T'; 150 break; 151 case SENSOR_FANRPM: 152 name[0] = 'F'; 153 break; 154 case SENSOR_VOLTS_DC: 155 name[0] = 'V'; 156 break; 157 default: 158 return; 159 } 160 161 b.Length = ACPI_ALLOCATE_BUFFER; 162 s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, 163 ACPI_TYPE_PACKAGE); 164 if (ACPI_FAILURE(s)) { 165 device_printf(sc->sc_dev, "%s not found\n", name); 166 return; 167 } 168 169 bp = b.Pointer; 170 o = bp->Package.Elements; 171 if (o[0].Type != ACPI_TYPE_INTEGER) { 172 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); 173 AcpiOsFree(b.Pointer); 174 return; 175 } 176 177 n = o[0].Integer.Value; 178 if (bp->Package.Count - 1 < n) { 179 device_printf(sc->sc_dev, "%s: invalid package\n", name); 180 AcpiOsFree(b.Pointer); 181 return; 182 } else if (bp->Package.Count - 1 > n) { 183 int on = n; 184 185 #ifdef AIBS_MORE_SENSORS 186 n = bp->Package.Count - 1; 187 #endif 188 device_printf(sc->sc_dev, "%s: malformed package: %i/%i" 189 ", assume %i\n", name, on, bp->Package.Count - 1, n); 190 } 191 if (n < 1) { 192 device_printf(sc->sc_dev, "%s: no members in the package\n", 193 name); 194 AcpiOsFree(b.Pointer); 195 return; 196 } 197 198 as = kmalloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO); 199 if (as == NULL) { 200 device_printf(sc->sc_dev, "%s: malloc fail\n", name); 201 AcpiOsFree(b.Pointer); 202 return; 203 } 204 205 switch (st) { 206 case SENSOR_TEMP: 207 sc->sc_asens_temp = as; 208 break; 209 case SENSOR_FANRPM: 210 sc->sc_asens_fan = as; 211 break; 212 case SENSOR_VOLTS_DC: 213 sc->sc_asens_volt = as; 214 break; 215 default: 216 /* NOTREACHED */ 217 return; 218 } 219 220 for (i = 0, o++; i < n; i++, o++) { 221 ACPI_OBJECT *oi; 222 223 /* acpica automatically evaluates the referenced package */ 224 if (o[0].Type != ACPI_TYPE_PACKAGE) { 225 device_printf(sc->sc_dev, 226 "%s: %i: not a package: %i type\n", 227 name, i, o[0].Type); 228 continue; 229 } 230 oi = o[0].Package.Elements; 231 if (o[0].Package.Count != 5 || 232 oi[0].Type != ACPI_TYPE_INTEGER || 233 oi[1].Type != ACPI_TYPE_STRING || 234 oi[2].Type != ACPI_TYPE_INTEGER || 235 oi[3].Type != ACPI_TYPE_INTEGER || 236 oi[4].Type != ACPI_TYPE_INTEGER) { 237 device_printf(sc->sc_dev, 238 "%s: %i: invalid package\n", 239 name, i); 240 continue; 241 } 242 as[i].i = oi[0].Integer.Value; 243 strlcpy(as[i].s.desc, oi[1].String.Pointer, 244 sizeof(as[i].s.desc)); 245 as[i].l = oi[2].Integer.Value; 246 as[i].h = oi[3].Integer.Value; 247 as[i].s.type = st; 248 #ifdef AIBS_VERBOSE 249 device_printf(sc->sc_dev, "%c%i: " 250 "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64" " 251 "0x%"PRIx64"\n", 252 name[0], i, 253 as[i].i, as[i].s.desc, (int64_t)as[i].l, (int64_t)as[i].h, 254 oi[4].Integer.Value); 255 #endif 256 sensor_attach(&sc->sc_sensordev, &as[i].s); 257 } 258 259 AcpiOsFree(b.Pointer); 260 return; 261 } 262 263 static int 264 aibs_detach(device_t dev) 265 { 266 struct aibs_softc *sc = device_get_softc(dev); 267 268 sensordev_deinstall(&sc->sc_sensordev); 269 sensor_task_unregister(sc); 270 if (sc->sc_asens_volt != NULL) 271 kfree(sc->sc_asens_volt, M_DEVBUF); 272 if (sc->sc_asens_temp != NULL) 273 kfree(sc->sc_asens_temp, M_DEVBUF); 274 if (sc->sc_asens_fan != NULL) 275 kfree(sc->sc_asens_fan, M_DEVBUF); 276 return 0; 277 } 278 279 #ifdef AIBS_VERBOSE 280 #define ddevice_printf(x...) device_printf(x) 281 #else 282 #define ddevice_printf(x...) 283 #endif 284 285 static void 286 aibs_refresh(void *arg) 287 { 288 struct aibs_softc *sc = arg; 289 290 aibs_refresh_r(sc, SENSOR_VOLTS_DC); 291 aibs_refresh_r(sc, SENSOR_TEMP); 292 aibs_refresh_r(sc, SENSOR_FANRPM); 293 } 294 295 static void 296 aibs_refresh_r(struct aibs_softc *sc, enum sensor_type st) 297 { 298 ACPI_STATUS rs; 299 ACPI_HANDLE rh; 300 int i, n = sc->sc_sensordev.maxnumt[st]; 301 char *name; 302 struct aibs_sensor *as; 303 304 switch (st) { 305 case SENSOR_TEMP: 306 name = "RTMP"; 307 as = sc->sc_asens_temp; 308 break; 309 case SENSOR_FANRPM: 310 name = "RFAN"; 311 as = sc->sc_asens_fan; 312 break; 313 case SENSOR_VOLTS_DC: 314 name = "RVLT"; 315 as = sc->sc_asens_volt; 316 break; 317 default: 318 return; 319 } 320 321 if (as == NULL) 322 return; 323 324 rs = AcpiGetHandle(sc->sc_ah, name, &rh); 325 if (ACPI_FAILURE(rs)) { 326 ddevice_printf(sc->sc_dev, "%s: method handle not found\n", 327 name); 328 for (i = 0; i < n; i++) 329 as[i].s.flags |= SENSOR_FINVALID; 330 return; 331 } 332 333 for (i = 0; i < n; i++) { 334 ACPI_OBJECT p, *bp; 335 ACPI_OBJECT_LIST mp; 336 ACPI_BUFFER b; 337 UINT64 v; 338 struct ksensor *s = &as[i].s; 339 const UINT64 l = as[i].l, h = as[i].h; 340 341 p.Type = ACPI_TYPE_INTEGER; 342 p.Integer.Value = as[i].i; 343 mp.Count = 1; 344 mp.Pointer = &p; 345 b.Length = ACPI_ALLOCATE_BUFFER; 346 rs = AcpiEvaluateObjectTyped(rh, NULL, &mp, &b, 347 ACPI_TYPE_INTEGER); 348 if (ACPI_FAILURE(rs)) { 349 ddevice_printf(sc->sc_dev, 350 "%s: %i: evaluation failed\n", 351 name, i); 352 s->flags |= SENSOR_FINVALID; 353 continue; 354 } 355 bp = b.Pointer; 356 v = bp->Integer.Value; 357 AcpiOsFree(b.Pointer); 358 359 switch (st) { 360 case SENSOR_TEMP: 361 s->value = v * 100 * 1000 + 273150000; 362 if (v == 0) { 363 s->status = SENSOR_S_UNKNOWN; 364 s->flags |= SENSOR_FINVALID; 365 } else { 366 if (v > h) 367 s->status = SENSOR_S_CRIT; 368 else if (v > l) 369 s->status = SENSOR_S_WARN; 370 else 371 s->status = SENSOR_S_OK; 372 s->flags &= ~SENSOR_FINVALID; 373 } 374 break; 375 case SENSOR_FANRPM: 376 s->value = v; 377 /* some boards have strange limits for fans */ 378 if ((l != 0 && l < v && v < h) || 379 (l == 0 && v > h)) 380 s->status = SENSOR_S_OK; 381 else 382 s->status = SENSOR_S_WARN; 383 s->flags &= ~SENSOR_FINVALID; 384 break; 385 case SENSOR_VOLTS_DC: 386 s->value = v * 1000; 387 if (l < v && v < h) 388 s->status = SENSOR_S_OK; 389 else 390 s->status = SENSOR_S_WARN; 391 s->flags &= ~SENSOR_FINVALID; 392 break; 393 default: 394 /* NOTREACHED */ 395 break; 396 } 397 } 398 399 return; 400 } 401