1 /* $OpenBSD: acpitz.c,v 1.43 2011/06/15 00:15:54 marco 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 69 struct cfattach acpitz_ca = { 70 sizeof(struct acpitz_softc), acpitz_match, acpitz_attach 71 }; 72 73 struct cfdriver acpitz_cd = { 74 NULL, "acpitz", DV_DULL 75 }; 76 77 void acpitz_init_perf(void *); 78 void acpitz_setperf(int); 79 void acpitz_monitor(struct acpitz_softc *); 80 void acpitz_refresh(void *); 81 int acpitz_notify(struct aml_node *, int, void *); 82 int acpitz_gettempreading(struct acpitz_softc *, char *); 83 int acpitz_getreading(struct acpitz_softc *, char *); 84 int acpitz_setfan(struct acpitz_softc *, int, char *); 85 void acpitz_init(struct acpitz_softc *, int); 86 87 void (*acpitz_cpu_setperf)(int); 88 int acpitz_perflevel = -1; 89 extern void (*cpu_setperf)(int); 90 extern int perflevel; 91 #define PERFSTEP 10 92 93 #define ACPITZ_TRIPS (1L << 0) 94 #define ACPITZ_DEVLIST (1L << 1) 95 #define ACPITZ_INIT ACPITZ_TRIPS+ACPITZ_DEVLIST 96 97 extern struct aml_node aml_root; 98 99 void 100 acpitz_init_perf(void *arg) 101 { 102 if (acpitz_perflevel == -1) 103 acpitz_perflevel = perflevel; 104 105 if (cpu_setperf != acpitz_setperf) { 106 acpitz_cpu_setperf = cpu_setperf; 107 cpu_setperf = acpitz_setperf; 108 } 109 } 110 111 void 112 acpitz_setperf(int level) 113 { 114 extern struct acpi_softc *acpi_softc; 115 116 if (level < 0 || level > 100) 117 return; 118 119 if (acpi_softc == NULL) 120 return; 121 if (acpi_softc->sc_pse && level > acpitz_perflevel) 122 return; 123 124 if (acpitz_cpu_setperf) 125 acpitz_cpu_setperf(level); 126 } 127 128 void 129 acpitz_init(struct acpitz_softc *sc, int flag) 130 { 131 int i; 132 char name[5]; 133 struct aml_value res; 134 135 /* Read trip points */ 136 if (flag & ACPITZ_TRIPS) { 137 sc->sc_psv = acpitz_getreading(sc, "_PSV"); 138 for (i = 0; i < ACPITZ_MAX_AC; i++) { 139 snprintf(name, sizeof(name), "_AC%d", i); 140 sc->sc_ac[i] = acpitz_getreading(sc, name); 141 sc->sc_ac_stat[i] = -1; 142 } 143 } 144 145 /* Read device lists */ 146 if (flag & ACPITZ_DEVLIST) { 147 if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL", 148 0, NULL, &res)) { 149 acpi_freedevlist(&sc->sc_psl); 150 acpi_getdevlist(&sc->sc_psl, sc->sc_devnode, &res, 0); 151 aml_freevalue(&res); 152 } 153 for (i = 0; i < ACPITZ_MAX_AC; i++) { 154 snprintf(name, sizeof(name), "_AL%d", i); 155 if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 156 0, NULL, &res)) { 157 acpi_freedevlist(&sc->sc_alx[i]); 158 acpi_getdevlist(&sc->sc_alx[i], 159 sc->sc_devnode, &res, 0); 160 aml_freevalue(&res); 161 } 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 191 sc->sc_acpi = (struct acpi_softc *)parent; 192 sc->sc_devnode = aa->aaa_node; 193 194 TAILQ_INIT(&sc->sc_psl); 195 for (i = 0; i < ACPITZ_MAX_AC; i++) 196 TAILQ_INIT(&sc->sc_alx[i]); 197 198 sc->sc_lasttmp = -1; 199 if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) { 200 dnprintf(10, ": failed to read _TMP\n"); 201 return; 202 } 203 204 if ((sc->sc_crt = acpitz_gettempreading(sc, "_CRT")) == -1) 205 printf(": no critical temperature defined\n"); 206 else 207 printf(": critical temperature is %d degC\n", KTOC(sc->sc_crt)); 208 209 sc->sc_hot = acpitz_gettempreading(sc, "_HOT"); 210 sc->sc_tc1 = acpitz_getreading(sc, "_TC1"); 211 sc->sc_tc2 = acpitz_getreading(sc, "_TC2"); 212 213 /* get _PSL, _ALx */ 214 acpitz_init(sc, ACPITZ_INIT); 215 216 dnprintf(10, "%s: _HOT: %d _TC1: %d _TC2: %d _PSV: %d _TMP: %d " 217 "_CRT: %d\n", DEVNAME(sc), sc->sc_hot, sc->sc_tc1, sc->sc_tc2, 218 sc->sc_psv, sc->sc_tmp, sc->sc_crt); 219 220 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 221 sizeof(sc->sc_sensdev.xname)); 222 strlcpy(sc->sc_sens.desc, "zone temperature", 223 sizeof(sc->sc_sens.desc)); 224 sc->sc_sens.type = SENSOR_TEMP; 225 sensor_attach(&sc->sc_sensdev, &sc->sc_sens); 226 sensordev_install(&sc->sc_sensdev); 227 228 aml_register_notify(sc->sc_devnode, NULL, 229 acpitz_notify, sc, ACPIDEV_POLL); 230 231 /* 232 * XXX use kthread_create_deferred to ensure we are the very last 233 * piece of code that touches this pointer after all CPUs have been 234 * fully attached 235 */ 236 kthread_create_deferred(acpitz_init_perf, sc); 237 } 238 239 int 240 acpitz_setfan(struct acpitz_softc *sc, int i, char *method) 241 { 242 struct aml_node *node; 243 struct aml_value res1, *ref; 244 char name[8]; 245 int rv = 1, x, y; 246 int64_t sta; 247 struct acpi_devlist *dl; 248 249 dnprintf(20, "%s: acpitz_setfan(%d, %s)\n", DEVNAME(sc), i, method); 250 251 x = 0; 252 snprintf(name, sizeof(name), "_AL%d", i); 253 TAILQ_FOREACH(dl, &sc->sc_alx[i], dev_link) { 254 if (aml_evalname(sc->sc_acpi, dl->dev_node, "_PR0",0 , NULL, 255 &res1)) { 256 printf("%s: %s[%d] _PR0 failed\n", DEVNAME(sc), 257 name, x); 258 aml_freevalue(&res1); 259 x++; 260 continue; 261 } 262 if (res1.type != AML_OBJTYPE_PACKAGE) { 263 printf("%s: %s[%d] _PR0 not a package\n", DEVNAME(sc), 264 name, x); 265 aml_freevalue(&res1); 266 x++; 267 continue; 268 } 269 for (y = 0; y < res1.length; y++) { 270 ref = res1.v_package[y]; 271 if (ref->type == AML_OBJTYPE_STRING) { 272 node = aml_searchrel(sc->sc_devnode, 273 ref->v_string); 274 if (node == NULL) { 275 printf("%s: %s[%d.%d] _PRO" 276 " not a valid device\n", 277 DEVNAME(sc), name, x, y); 278 continue; 279 } 280 ref = node->value; 281 } 282 if (ref->type == AML_OBJTYPE_OBJREF) { 283 ref = ref->v_objref.ref; 284 } 285 if (ref->type != AML_OBJTYPE_DEVICE && 286 ref->type != AML_OBJTYPE_POWERRSRC) { 287 printf("%s: %s[%d.%d] _PRO not a package\n", 288 DEVNAME(sc), name, x, y); 289 continue; 290 } 291 if (aml_evalname(sc->sc_acpi, ref->node, method, 0, 292 NULL, NULL)) 293 printf("%s: %s[%d.%d] %s fails\n", 294 DEVNAME(sc), name, x, y, method); 295 296 /* save off status of fan */ 297 if (aml_evalinteger(sc->sc_acpi, ref->node, "_STA", 0, 298 NULL, &sta)) 299 printf("%s: %s[%d.%d] _STA fails\n", 300 DEVNAME(sc), name, x, y); 301 else { 302 sc->sc_ac_stat[i] = sta; 303 } 304 } 305 aml_freevalue(&res1); 306 x++; 307 } 308 rv = 0; 309 return (rv); 310 } 311 312 void 313 acpitz_refresh(void *arg) 314 { 315 struct acpitz_softc *sc = arg; 316 int i, trend, nperf; 317 318 dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc), 319 sc->sc_devnode->name); 320 321 /* get _TMP and debounce the value */ 322 if (-1 == (sc->sc_tmp = acpitz_gettempreading(sc, "_TMP"))) { 323 printf("%s: %s: failed to read temp\n", DEVNAME(sc), 324 sc->sc_devnode->name); 325 return; 326 } 327 /* critical trip points */ 328 if (sc->sc_crt != -1 && sc->sc_crt <= sc->sc_tmp) { 329 /* do critical shutdown */ 330 printf("%s: critical temperature exceeded %dC (%dK), shutting " 331 "down\n", 332 DEVNAME(sc), KTOC(sc->sc_tmp), sc->sc_tmp); 333 psignal(initproc, SIGUSR2); 334 } 335 if (sc->sc_hot != -1 && sc->sc_hot <= sc->sc_tmp) { 336 printf("%s: _HOT temperature\n", DEVNAME(sc)); 337 /* XXX go to S4, until then cool as hard as we can */ 338 } 339 340 /* passive cooling */ 341 if (sc->sc_lasttmp != -1 && sc->sc_tc1 != -1 && sc->sc_tc2 != -1 && 342 sc->sc_psv != -1) { 343 dnprintf(30, "%s: passive cooling: lasttmp: %d tc1: %d " 344 "tc2: %d psv: %d\n", DEVNAME(sc), sc->sc_lasttmp, 345 sc->sc_tc1, sc->sc_tc2, sc->sc_psv); 346 347 nperf = acpitz_perflevel; 348 if (sc->sc_psv <= sc->sc_tmp) { 349 /* Passive cooling enabled */ 350 dnprintf(1, "%s: enabling passive %d %d\n", 351 DEVNAME(sc), sc->sc_tmp, sc->sc_psv); 352 if (!sc->sc_pse) 353 sc->sc_acpi->sc_pse++; 354 sc->sc_pse = 1; 355 356 trend = sc->sc_tc1 * (sc->sc_tmp - sc->sc_lasttmp) + 357 sc->sc_tc2 * (sc->sc_tmp - sc->sc_psv); 358 359 /* Depending on trend, slow down/speed up */ 360 if (trend > 0) 361 nperf -= PERFSTEP; 362 else 363 nperf += PERFSTEP; 364 } 365 else { 366 /* Passive cooling disabled, increase % */ 367 dnprintf(1, "%s: disabling passive %d %d\n", 368 DEVNAME(sc), sc->sc_tmp, sc->sc_psv); 369 if (sc->sc_pse) 370 sc->sc_acpi->sc_pse--; 371 sc->sc_pse = 0; 372 nperf += PERFSTEP; 373 } 374 if (nperf < 0) 375 nperf = 0; 376 else if (nperf > 100) 377 nperf = 100; 378 379 /* clamp passive cooling request */ 380 if (nperf > perflevel) 381 nperf = perflevel; 382 383 /* Perform CPU setperf */ 384 if (acpitz_cpu_setperf && nperf != acpitz_perflevel) { 385 acpitz_perflevel = nperf; 386 acpitz_cpu_setperf(nperf); 387 } 388 } 389 sc->sc_lasttmp = sc->sc_tmp; 390 391 /* active cooling */ 392 for (i = 0; i < ACPITZ_MAX_AC; i++) { 393 if (sc->sc_ac[i] != -1 && sc->sc_ac[i] <= sc->sc_tmp) { 394 /* turn on fan i */ 395 if (sc->sc_ac_stat[i] <= 0) 396 acpitz_setfan(sc, i, "_ON_"); 397 } else if (sc->sc_ac[i] != -1) { 398 /* turn off fan i */ 399 if (sc->sc_ac_stat[i] > 0) 400 acpitz_setfan(sc, i, "_OFF"); 401 } 402 } 403 sc->sc_sens.value = sc->sc_tmp * 100000 - 50000; 404 } 405 406 int 407 acpitz_getreading(struct acpitz_softc *sc, char *name) 408 { 409 u_int64_t val; 410 411 if (!aml_evalinteger(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &val)) 412 return (val); 413 414 return (-1); 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 acpi_sleep(1000, "acpitz"); /* debounce: 1000 msec */ 435 } 436 if (i >= ACPITZ_TMP_RETRY) { 437 printf("%s: %s: failed to read %s\n", DEVNAME(sc), 438 sc->sc_devnode->name, name); 439 goto out; 440 } 441 out: 442 dnprintf(30, "%s: name: %s tmp: %dK => %dC, rv: %d\n", DEVNAME(sc), 443 name, tmp, KTOC(tmp), rv); 444 return (rv); 445 } 446 447 int 448 acpitz_notify(struct aml_node *node, int notify_type, void *arg) 449 { 450 struct acpitz_softc *sc = arg; 451 452 dnprintf(10, "%s notify: %.2x %s\n", DEVNAME(sc), notify_type, 453 sc->sc_devnode->name); 454 455 switch (notify_type) { 456 case 0x80: /* hardware notifications */ 457 break; 458 case 0x81: /* operating Points changed */ 459 acpitz_init(sc, ACPITZ_TRIPS); 460 break; 461 case 0x82: /* re-evaluate thermal device list */ 462 acpitz_init(sc, ACPITZ_DEVLIST); 463 break; 464 default: 465 break; 466 } 467 468 acpitz_refresh(sc); 469 return (0); 470 } 471