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