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