1 /* $OpenBSD: acpitz.c,v 1.32 2009/10/15 19:00:53 jordan Exp $ */ 2 /* 3 * Copyright (c) 2006 Can Erkin Acar <canacar@openbsd.org> 4 * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> 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 <sys/param.h> 20 #include <sys/proc.h> 21 #include <sys/signalvar.h> 22 #include <sys/systm.h> 23 #include <sys/device.h> 24 #include <sys/malloc.h> 25 #include <sys/kernel.h> 26 27 #include <machine/bus.h> 28 29 #include <dev/acpi/acpireg.h> 30 #include <dev/acpi/acpivar.h> 31 #include <dev/acpi/acpidev.h> 32 #include <dev/acpi/amltypes.h> 33 #include <dev/acpi/dsdt.h> 34 35 #include <sys/sensors.h> 36 37 #define KTOC(k) ((k - 2732) / 10) 38 #define ACPITZ_MAX_AC (10) 39 #define ACPITZ_TMP_RETRY (3) 40 41 struct acpitz_softc { 42 struct device sc_dev; 43 44 struct acpi_softc *sc_acpi; 45 struct aml_node *sc_devnode; 46 47 int sc_tmp; 48 int sc_crt; 49 int sc_hot; 50 int sc_ac[ACPITZ_MAX_AC]; 51 int sc_ac_stat[ACPITZ_MAX_AC]; 52 int sc_pse; 53 int sc_psv; 54 int sc_tc1; 55 int sc_tc2; 56 int sc_lasttmp; 57 58 struct ksensor sc_sens; 59 struct ksensordev sc_sensdev; 60 61 struct acpi_devlist_head sc_psl; 62 struct acpi_devlist_head sc_alx[ACPITZ_MAX_AC]; 63 }; 64 65 int acpitz_match(struct device *, void *, void *); 66 void acpitz_attach(struct device *, struct device *, void *); 67 68 struct cfattach acpitz_ca = { 69 sizeof(struct acpitz_softc), acpitz_match, acpitz_attach 70 }; 71 72 struct cfdriver acpitz_cd = { 73 NULL, "acpitz", DV_DULL 74 }; 75 76 void acpitz_monitor(struct acpitz_softc *); 77 void acpitz_refresh(void *); 78 int acpitz_notify(struct aml_node *, int, void *); 79 int acpitz_gettempreading(struct acpitz_softc *, char *); 80 int acpitz_getreading(struct acpitz_softc *, char *); 81 int acpitz_setfan(struct acpitz_softc *, int, char *); 82 void acpitz_init(struct acpitz_softc *, int); 83 #if 0 84 int acpitz_setcpu(struct acpitz_softc *, int); 85 #endif 86 87 extern void (*cpu_setperf)(int); 88 extern int perflevel; 89 #define PERFSTEP 10 90 91 #define ACPITZ_TRIPS (1L << 0) 92 #define ACPITZ_DEVLIST (1L << 1) 93 #define ACPITZ_INIT ACPITZ_TRIPS+ACPITZ_DEVLIST 94 95 extern struct aml_node aml_root; 96 97 void 98 acpitz_init(struct acpitz_softc *sc, int flag) 99 { 100 int i; 101 char name[5]; 102 struct aml_value res; 103 104 /* Read trip points */ 105 if (flag & ACPITZ_TRIPS) { 106 sc->sc_psv = acpitz_getreading(sc, "_PSV"); 107 for (i=0; i<ACPITZ_MAX_AC; i++) { 108 snprintf(name, sizeof(name), "_AC%d", i); 109 sc->sc_ac[i] = acpitz_getreading(sc, name); 110 sc->sc_ac_stat[i] = -1; 111 } 112 } 113 114 /* Read device lists */ 115 if (flag & ACPITZ_DEVLIST) { 116 if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL", 117 0, NULL, &res)) { 118 acpi_freedevlist(&sc->sc_psl); 119 acpi_getdevlist(&sc->sc_psl, sc->sc_devnode, &res, 0); 120 aml_freevalue(&res); 121 } 122 for (i=0; i<ACPITZ_MAX_AC; i++) { 123 snprintf(name, sizeof(name), "_AL%d", i); 124 if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 125 0, NULL, &res)) { 126 acpi_freedevlist(&sc->sc_alx[i]); 127 acpi_getdevlist(&sc->sc_alx[i], 128 sc->sc_devnode, &res, 0); 129 aml_freevalue(&res); 130 } 131 } 132 } 133 } 134 135 int 136 acpitz_match(struct device *parent, void *match, void *aux) 137 { 138 struct acpi_attach_args *aa = aux; 139 struct cfdata *cf = match; 140 141 /* sanity */ 142 if (aa->aaa_name == NULL || 143 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 || 144 aa->aaa_table != NULL) 145 return (0); 146 147 if (aa->aaa_node->value->type != AML_OBJTYPE_THERMZONE) 148 return (0); 149 150 return (1); 151 } 152 153 void 154 acpitz_attach(struct device *parent, struct device *self, void *aux) 155 { 156 struct acpitz_softc *sc = (struct acpitz_softc *)self; 157 struct acpi_attach_args *aa = aux; 158 int i; 159 160 sc->sc_acpi = (struct acpi_softc *)parent; 161 sc->sc_devnode = aa->aaa_node; 162 163 TAILQ_INIT(&sc->sc_psl); 164 for (i=0; i<ACPITZ_MAX_AC; i++) 165 TAILQ_INIT(&sc->sc_alx[i]); 166 167 sc->sc_lasttmp = -1; 168 if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) { 169 printf(": failed to read _TMP\n"); 170 return; 171 } 172 173 if ((sc->sc_crt = acpitz_gettempreading(sc, "_CRT")) == -1) 174 printf(": no critical temperature defined\n"); 175 else 176 printf(": critical temperature %d degC\n", KTOC(sc->sc_crt)); 177 178 sc->sc_hot = acpitz_gettempreading(sc, "_HOT"); 179 sc->sc_tc1 = acpitz_getreading(sc, "_TC1"); 180 sc->sc_tc2 = acpitz_getreading(sc, "_TC2"); 181 182 /* get _PSL, _ALx */ 183 acpitz_init(sc, ACPITZ_INIT); 184 185 dnprintf(10, "%s: _HOT: %d _TC1: %d _TC2: %d _PSV: %d _TMP: %d " 186 "_CRT: %d\n", DEVNAME(sc), sc->sc_hot, sc->sc_tc1, sc->sc_tc2, 187 sc->sc_psv, sc->sc_tmp, sc->sc_crt); 188 189 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 190 sizeof(sc->sc_sensdev.xname)); 191 strlcpy(sc->sc_sens.desc, "zone temperature", 192 sizeof(sc->sc_sens.desc)); 193 sc->sc_sens.type = SENSOR_TEMP; 194 sensor_attach(&sc->sc_sensdev, &sc->sc_sens); 195 sensordev_install(&sc->sc_sensdev); 196 197 aml_register_notify(sc->sc_devnode, NULL, 198 acpitz_notify, sc, ACPIDEV_POLL); 199 } 200 201 #if 0 202 int 203 acpitz_setcpu(struct acpitz_softc *sc, int perc) 204 { 205 struct aml_value res0, *ref; 206 int x; 207 208 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL", 0, NULL, &res0)) { 209 printf("%s: _PSL failed\n", DEVNAME(sc)); 210 goto out; 211 } 212 if (res0.type != AML_OBJTYPE_PACKAGE) { 213 printf("%s: not a package\n", DEVNAME(sc)); 214 goto out; 215 } 216 for (x = 0; x < res0.length; x++) { 217 if (res0.v_package[x]->type != AML_OBJTYPE_OBJREF) { 218 printf("%s: _PSL[%d] not a object ref\n", 219 DEVNAME(sc), x); 220 continue; 221 } 222 ref = res0.v_package[x]->v_objref.ref; 223 if (ref->type != AML_OBJTYPE_PROCESSOR) 224 printf("%s: _PSL[%d] not a CPU\n", DEVNAME(sc), x); 225 } 226 out: 227 aml_freevalue(&res0); 228 return (0); 229 } 230 #endif 231 232 int 233 acpitz_setfan(struct acpitz_softc *sc, int i, char *method) 234 { 235 struct aml_node *node; 236 struct aml_value res1, *ref; 237 char name[8]; 238 int rv = 1, x, y; 239 int64_t sta; 240 struct acpi_devlist *dl; 241 242 dnprintf(20, "%s: acpitz_setfan(%d, %s)\n", DEVNAME(sc), i, method); 243 244 TAILQ_FOREACH(dl, &sc->sc_alx[i], dev_link) { 245 if (aml_evalname(sc->sc_acpi, dl->dev_node, "_PR0",0 , NULL, 246 &res1)) { 247 printf("%s: %s[%d] _PR0 failed\n", DEVNAME(sc), 248 name, x); 249 aml_freevalue(&res1); 250 continue; 251 } 252 if (res1.type != AML_OBJTYPE_PACKAGE) { 253 printf("%s: %s[%d] _PR0 not a package\n", DEVNAME(sc), 254 name, x); 255 aml_freevalue(&res1); 256 continue; 257 } 258 for (y = 0; y < res1.length; y++) { 259 ref = res1.v_package[y]; 260 if (ref->type == AML_OBJTYPE_STRING) { 261 node = aml_searchrel(sc->sc_devnode, 262 ref->v_string); 263 if (node == NULL) { 264 printf("%s: %s[%d.%d] _PRO" 265 " not a valid device\n", 266 DEVNAME(sc), name, x, y); 267 continue; 268 } 269 ref = node->value; 270 } 271 if (ref->type == AML_OBJTYPE_OBJREF) { 272 ref = ref->v_objref.ref; 273 } 274 if (ref->type != AML_OBJTYPE_DEVICE && 275 ref->type != AML_OBJTYPE_POWERRSRC) { 276 printf("%s: %s[%d.%d] _PRO not a package\n", 277 DEVNAME(sc), name, x, y); 278 continue; 279 } 280 if (aml_evalname(sc->sc_acpi, ref->node, method, 0, 281 NULL, NULL)) 282 printf("%s: %s[%d.%d] %s fails\n", 283 DEVNAME(sc), name, x, y, method); 284 285 /* save off status of fan */ 286 if (aml_evalinteger(sc->sc_acpi, ref->node, "_STA", 0, 287 NULL, &sta)) 288 printf("%s: %s[%d.%d] _STA fails\n", 289 DEVNAME(sc), name, x, y); 290 else { 291 sc->sc_ac_stat[i] = sta; 292 } 293 } 294 aml_freevalue(&res1); 295 } 296 rv = 0; 297 return (rv); 298 } 299 300 void 301 acpitz_refresh(void *arg) 302 { 303 struct acpitz_softc *sc = arg; 304 int i, trend, nperf; 305 306 dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc), 307 sc->sc_devnode->name); 308 309 /* get _TMP and debounce the value */ 310 if (-1 == (sc->sc_tmp = acpitz_gettempreading(sc, "_TMP"))) { 311 printf("%s: %s: failed to read temp\n", DEVNAME(sc), 312 sc->sc_devnode->name); 313 return; 314 } 315 /* critical trip points */ 316 if (sc->sc_crt != -1 && sc->sc_crt <= sc->sc_tmp) { 317 /* do critical shutdown */ 318 printf("%s: Critical temperature, shutting down\n", 319 DEVNAME(sc)); 320 psignal(initproc, SIGUSR2); 321 } 322 if (sc->sc_hot != -1 && sc->sc_hot <= sc->sc_tmp) { 323 printf("%s: _HOT temperature\n", DEVNAME(sc)); 324 /* XXX go to S4, until then cool as hard as we can */ 325 } 326 327 /* passive cooling */ 328 if (sc->sc_lasttmp != -1 && sc->sc_tc1 != -1 && sc->sc_tc2 != -1 && 329 sc->sc_psv != -1) { 330 dnprintf(30, "%s: passive cooling: lasttmp: %d tc1: %d " 331 "tc2: %d psv: %d\n", DEVNAME(sc), sc->sc_lasttmp, 332 sc->sc_tc1, sc->sc_tc2, sc->sc_psv); 333 334 nperf = perflevel; 335 if (sc->sc_psv <= sc->sc_tmp) { 336 /* Passive cooling enabled */ 337 dnprintf(1, "%s: enabling passive %d %d\n", 338 DEVNAME(sc), sc->sc_tmp, sc->sc_psv); 339 if (!sc->sc_pse) 340 sc->sc_acpi->sc_pse++; 341 sc->sc_pse = 1; 342 343 trend = sc->sc_tc1 * (sc->sc_tmp - sc->sc_lasttmp) + 344 sc->sc_tc2 * (sc->sc_tmp - sc->sc_psv); 345 346 /* Depending on trend, slow down/speed up */ 347 if (trend > 0) 348 nperf -= PERFSTEP; 349 else 350 nperf += PERFSTEP; 351 } 352 else { 353 /* Passive cooling disabled, increase % */ 354 dnprintf(1, "%s: disabling passive %d %d\n", 355 DEVNAME(sc), sc->sc_tmp, sc->sc_psv); 356 if (sc->sc_pse) 357 sc->sc_acpi->sc_pse--; 358 sc->sc_pse = 0; 359 nperf += PERFSTEP; 360 } 361 if (nperf < 0) 362 nperf = 0; 363 else if (nperf > 100) 364 nperf = 100; 365 366 /* Perform CPU setperf */ 367 if (cpu_setperf && nperf != perflevel) { 368 perflevel = nperf; 369 cpu_setperf(nperf); 370 } 371 } 372 sc->sc_lasttmp = sc->sc_tmp; 373 374 /* active cooling */ 375 for (i = 0; i < ACPITZ_MAX_AC; i++) { 376 if (sc->sc_ac[i] != -1 && sc->sc_ac[i] <= sc->sc_tmp) { 377 /* turn on fan i */ 378 if (sc->sc_ac_stat[i] <= 0) 379 acpitz_setfan(sc, i, "_ON_"); 380 } else if (sc->sc_ac[i] != -1) { 381 /* turn off fan i */ 382 if (sc->sc_ac_stat[i] > 0) 383 acpitz_setfan(sc, i, "_OFF"); 384 } 385 } 386 sc->sc_sens.value = sc->sc_tmp * 100000 - 50000; 387 } 388 389 int 390 acpitz_getreading(struct acpitz_softc *sc, char *name) 391 { 392 struct aml_value res, *ref; 393 int rv = -1; 394 395 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) { 396 dnprintf(10, "%s: acpitz_getreading: no %s\n", DEVNAME(sc), 397 name); 398 goto out; 399 } 400 if (res.type == AML_OBJTYPE_STRING) { 401 struct aml_node *node; 402 node = aml_searchrel(sc->sc_devnode, res.v_string); 403 if (node == NULL) 404 goto out; 405 ref = node->value; 406 } else 407 ref = &res; 408 if (ref->type == AML_OBJTYPE_OBJREF) { 409 ref = ref->v_objref.ref; 410 } 411 rv = aml_val2int(ref); 412 out: 413 aml_freevalue(&res); 414 return (rv); 415 } 416 417 int 418 acpitz_gettempreading(struct acpitz_softc *sc, char *name) 419 { 420 int rv = -1, tmp = -1, i; 421 422 for (i = 0; i < ACPITZ_TMP_RETRY; i++) { 423 tmp = acpitz_getreading(sc, name); 424 if (tmp == -1) 425 goto out; 426 if (KTOC(tmp) > 0) { 427 rv = tmp; 428 break; 429 } else { 430 dnprintf(20, "%s: %d invalid reading on %s, " 431 "debouncing\n", DEVNAME(sc), tmp, name); 432 } 433 434 /* debounce value */ 435 if (cold) 436 delay(1000000); 437 else 438 while (tsleep(sc, PWAIT, "tzsleep", hz) != 439 EWOULDBLOCK); 440 } 441 if (i >= ACPITZ_TMP_RETRY) { 442 printf("%s: %s: failed to read %s\n", DEVNAME(sc), 443 sc->sc_devnode->name, name); 444 goto out; 445 } 446 out: 447 dnprintf(30, "%s: name: %s tmp: %dK => %dC, rv: %d\n", DEVNAME(sc), 448 name, tmp, KTOC(tmp), rv); 449 return (rv); 450 } 451 452 int 453 acpitz_notify(struct aml_node *node, int notify_type, void *arg) 454 { 455 struct acpitz_softc *sc = arg; 456 457 dnprintf(10, "%s notify: %.2x %s\n", DEVNAME(sc), notify_type, 458 sc->sc_devnode->name); 459 460 switch (notify_type) { 461 case 0x80: /* hardware notifications */ 462 break; 463 case 0x81: /* operating Points changed */ 464 acpitz_init(sc, ACPITZ_TRIPS); 465 break; 466 case 0x82: /* re-evaluate thermal device list */ 467 acpitz_init(sc, ACPITZ_DEVLIST); 468 break; 469 default: 470 break; 471 } 472 473 acpitz_refresh(sc); 474 return (0); 475 } 476