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 struct device *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(struct device *); 67 static int aibs_attach(struct device *); 68 static int aibs_detach(struct device *); 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(struct device *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(struct device *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 if (sensor_task_register(sc, aibs_refresh, 5)) { 132 device_printf(dev, "unable to register update task\n"); 133 return ENXIO; 134 } 135 136 sensordev_install(&sc->sc_sensordev); 137 return 0; 138 } 139 140 static void 141 aibs_attach_sif(struct aibs_softc *sc, enum sensor_type st) 142 { 143 ACPI_STATUS s; 144 ACPI_BUFFER b; 145 ACPI_OBJECT *bp, *o; 146 int i, n; 147 char name[] = "?SIF"; 148 struct aibs_sensor *as; 149 150 switch (st) { 151 case SENSOR_TEMP: 152 name[0] = 'T'; 153 break; 154 case SENSOR_FANRPM: 155 name[0] = 'F'; 156 break; 157 case SENSOR_VOLTS_DC: 158 name[0] = 'V'; 159 break; 160 default: 161 return; 162 } 163 164 b.Length = ACPI_ALLOCATE_BUFFER; 165 s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b, 166 ACPI_TYPE_PACKAGE); 167 if (ACPI_FAILURE(s)) { 168 device_printf(sc->sc_dev, "%s not found\n", name); 169 return; 170 } 171 172 bp = b.Pointer; 173 o = bp->Package.Elements; 174 if (o[0].Type != ACPI_TYPE_INTEGER) { 175 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name); 176 AcpiOsFree(b.Pointer); 177 return; 178 } 179 180 n = o[0].Integer.Value; 181 if (bp->Package.Count - 1 < n) { 182 device_printf(sc->sc_dev, "%s: invalid package\n", name); 183 AcpiOsFree(b.Pointer); 184 return; 185 } else if (bp->Package.Count - 1 > n) { 186 int on = n; 187 188 #ifdef AIBS_MORE_SENSORS 189 n = bp->Package.Count - 1; 190 #endif 191 device_printf(sc->sc_dev, "%s: malformed package: %i/%i" 192 ", assume %i\n", name, on, bp->Package.Count - 1, n); 193 } 194 if (n < 1) { 195 device_printf(sc->sc_dev, "%s: no members in the package\n", 196 name); 197 AcpiOsFree(b.Pointer); 198 return; 199 } 200 201 as = kmalloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO); 202 if (as == NULL) { 203 device_printf(sc->sc_dev, "%s: malloc fail\n", name); 204 AcpiOsFree(b.Pointer); 205 return; 206 } 207 208 switch (st) { 209 case SENSOR_TEMP: 210 sc->sc_asens_temp = as; 211 break; 212 case SENSOR_FANRPM: 213 sc->sc_asens_fan = as; 214 break; 215 case SENSOR_VOLTS_DC: 216 sc->sc_asens_volt = as; 217 break; 218 default: 219 /* NOTREACHED */ 220 return; 221 } 222 223 for (i = 0, o++; i < n; i++, o++) { 224 ACPI_OBJECT *oi; 225 226 /* acpica automatically evaluates the referenced package */ 227 if (o[0].Type != ACPI_TYPE_PACKAGE) { 228 device_printf(sc->sc_dev, 229 "%s: %i: not a package: %i type\n", 230 name, i, o[0].Type); 231 continue; 232 } 233 oi = o[0].Package.Elements; 234 if (o[0].Package.Count != 5 || 235 oi[0].Type != ACPI_TYPE_INTEGER || 236 oi[1].Type != ACPI_TYPE_STRING || 237 oi[2].Type != ACPI_TYPE_INTEGER || 238 oi[3].Type != ACPI_TYPE_INTEGER || 239 oi[4].Type != ACPI_TYPE_INTEGER) { 240 device_printf(sc->sc_dev, 241 "%s: %i: invalid package\n", 242 name, i); 243 continue; 244 } 245 as[i].i = oi[0].Integer.Value; 246 strlcpy(as[i].s.desc, oi[1].String.Pointer, 247 sizeof(as[i].s.desc)); 248 as[i].l = oi[2].Integer.Value; 249 as[i].h = oi[3].Integer.Value; 250 as[i].s.type = st; 251 #ifdef AIBS_VERBOSE 252 device_printf(sc->sc_dev, "%c%i: " 253 "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64" " 254 "0x%"PRIx64"\n", 255 name[0], i, 256 as[i].i, as[i].s.desc, (int64_t)as[i].l, (int64_t)as[i].h, 257 oi[4].Integer.Value); 258 #endif 259 sensor_attach(&sc->sc_sensordev, &as[i].s); 260 } 261 262 AcpiOsFree(b.Pointer); 263 return; 264 } 265 266 static int 267 aibs_detach(struct device *dev) 268 { 269 struct aibs_softc *sc = device_get_softc(dev); 270 271 sensordev_deinstall(&sc->sc_sensordev); 272 sensor_task_unregister(sc); 273 if (sc->sc_asens_volt != NULL) 274 kfree(sc->sc_asens_volt, M_DEVBUF); 275 if (sc->sc_asens_temp != NULL) 276 kfree(sc->sc_asens_temp, M_DEVBUF); 277 if (sc->sc_asens_fan != NULL) 278 kfree(sc->sc_asens_fan, M_DEVBUF); 279 return 0; 280 } 281 282 #ifdef AIBS_VERBOSE 283 #define ddevice_printf(x...) device_printf(x) 284 #else 285 #define ddevice_printf(x...) 286 #endif 287 288 static void 289 aibs_refresh(void *arg) 290 { 291 struct aibs_softc *sc = arg; 292 293 aibs_refresh_r(sc, SENSOR_VOLTS_DC); 294 aibs_refresh_r(sc, SENSOR_TEMP); 295 aibs_refresh_r(sc, SENSOR_FANRPM); 296 } 297 298 static void 299 aibs_refresh_r(struct aibs_softc *sc, enum sensor_type st) 300 { 301 ACPI_STATUS rs; 302 ACPI_HANDLE rh; 303 int i, n = sc->sc_sensordev.maxnumt[st]; 304 char *name; 305 struct aibs_sensor *as; 306 307 switch (st) { 308 case SENSOR_TEMP: 309 name = "RTMP"; 310 as = sc->sc_asens_temp; 311 break; 312 case SENSOR_FANRPM: 313 name = "RFAN"; 314 as = sc->sc_asens_fan; 315 break; 316 case SENSOR_VOLTS_DC: 317 name = "RVLT"; 318 as = sc->sc_asens_volt; 319 break; 320 default: 321 return; 322 } 323 324 if (as == NULL) 325 return; 326 327 rs = AcpiGetHandle(sc->sc_ah, name, &rh); 328 if (ACPI_FAILURE(rs)) { 329 ddevice_printf(sc->sc_dev, "%s: method handle not found\n", 330 name); 331 for (i = 0; i < n; i++) 332 as[i].s.flags |= SENSOR_FINVALID; 333 return; 334 } 335 336 for (i = 0; i < n; i++) { 337 ACPI_OBJECT p, *bp; 338 ACPI_OBJECT_LIST mp; 339 ACPI_BUFFER b; 340 UINT64 v; 341 struct ksensor *s = &as[i].s; 342 const UINT64 l = as[i].l, h = as[i].h; 343 344 p.Type = ACPI_TYPE_INTEGER; 345 p.Integer.Value = as[i].i; 346 mp.Count = 1; 347 mp.Pointer = &p; 348 b.Length = ACPI_ALLOCATE_BUFFER; 349 rs = AcpiEvaluateObjectTyped(rh, NULL, &mp, &b, 350 ACPI_TYPE_INTEGER); 351 if (ACPI_FAILURE(rs)) { 352 ddevice_printf(sc->sc_dev, 353 "%s: %i: evaluation failed\n", 354 name, i); 355 s->flags |= SENSOR_FINVALID; 356 continue; 357 } 358 bp = b.Pointer; 359 v = bp->Integer.Value; 360 AcpiOsFree(b.Pointer); 361 362 switch (st) { 363 case SENSOR_TEMP: 364 s->value = v * 100 * 1000 + 273150000; 365 if (v == 0) { 366 s->status = SENSOR_S_UNKNOWN; 367 s->flags |= SENSOR_FINVALID; 368 } else { 369 if (v > h) 370 s->status = SENSOR_S_CRIT; 371 else if (v > l) 372 s->status = SENSOR_S_WARN; 373 else 374 s->status = SENSOR_S_OK; 375 s->flags &= ~SENSOR_FINVALID; 376 } 377 break; 378 case SENSOR_FANRPM: 379 s->value = v; 380 /* some boards have strange limits for fans */ 381 if ((l != 0 && l < v && v < h) || 382 (l == 0 && v > h)) 383 s->status = SENSOR_S_OK; 384 else 385 s->status = SENSOR_S_WARN; 386 s->flags &= ~SENSOR_FINVALID; 387 break; 388 case SENSOR_VOLTS_DC: 389 s->value = v * 1000; 390 if (l < v && v < h) 391 s->status = SENSOR_S_OK; 392 else 393 s->status = SENSOR_S_WARN; 394 s->flags &= ~SENSOR_FINVALID; 395 break; 396 default: 397 /* NOTREACHED */ 398 break; 399 } 400 } 401 402 return; 403 } 404