1 /* $OpenBSD: acpitz.c,v 1.29 2009/03/11 21:54:15 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 struct ksensor sc_sens; 58 struct ksensordev sc_sensdev; 59 }; 60 61 int acpitz_match(struct device *, void *, void *); 62 void acpitz_attach(struct device *, struct device *, void *); 63 64 struct cfattach acpitz_ca = { 65 sizeof(struct acpitz_softc), acpitz_match, acpitz_attach 66 }; 67 68 struct cfdriver acpitz_cd = { 69 NULL, "acpitz", DV_DULL 70 }; 71 72 void acpitz_monitor(struct acpitz_softc *); 73 void acpitz_refresh(void *); 74 int acpitz_notify(struct aml_node *, int, void *); 75 int acpitz_gettempreading(struct acpitz_softc *, char *); 76 int acpitz_getreading(struct acpitz_softc *, char *); 77 int acpitz_setfan(struct acpitz_softc *, int, char *); 78 #if 0 79 int acpitz_setcpu(struct acpitz_softc *, int); 80 #endif 81 82 int 83 acpitz_match(struct device *parent, void *match, void *aux) 84 { 85 struct acpi_attach_args *aa = aux; 86 struct cfdata *cf = match; 87 88 /* sanity */ 89 if (aa->aaa_name == NULL || 90 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 || 91 aa->aaa_table != NULL) 92 return (0); 93 94 if (aa->aaa_node->value->type != AML_OBJTYPE_THERMZONE) 95 return (0); 96 97 return (1); 98 } 99 100 void 101 acpitz_attach(struct device *parent, struct device *self, void *aux) 102 { 103 struct acpitz_softc *sc = (struct acpitz_softc *)self; 104 struct acpi_attach_args *aa = aux; 105 int i; 106 char name[8]; 107 108 sc->sc_acpi = (struct acpi_softc *)parent; 109 sc->sc_devnode = aa->aaa_node; 110 111 sc->sc_lasttmp = -1; 112 if ((sc->sc_tmp = acpitz_gettempreading(sc, "_TMP")) == -1) { 113 printf(": failed to read _TMP\n"); 114 return; 115 } 116 117 if ((sc->sc_crt = acpitz_gettempreading(sc, "_CRT")) == -1) 118 printf(": no critical temperature defined\n"); 119 else 120 printf(": critical temperature %d degC\n", KTOC(sc->sc_crt)); 121 122 for (i = 0; i < ACPITZ_MAX_AC; i++) { 123 snprintf(name, sizeof name, "_AC%d", i); 124 sc->sc_ac[i] = acpitz_gettempreading(sc, name); 125 sc->sc_ac_stat[0] = -1; 126 } 127 128 sc->sc_hot = acpitz_gettempreading(sc, "_HOT"); 129 sc->sc_tc1 = acpitz_getreading(sc, "_TC1"); 130 sc->sc_tc2 = acpitz_getreading(sc, "_TC2"); 131 sc->sc_psv = acpitz_gettempreading(sc, "_PSV"); 132 dnprintf(10, "%s: _HOT: %d _TC1: %d _TC2: %d _PSV: %d _TMP: %d " 133 "_CRT: %d\n", DEVNAME(sc), sc->sc_hot, sc->sc_tc1, sc->sc_tc2, 134 sc->sc_psv, sc->sc_tmp, sc->sc_crt); 135 136 /* get _PSL */ 137 138 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 139 sizeof(sc->sc_sensdev.xname)); 140 strlcpy(sc->sc_sens.desc, "zone temperature", 141 sizeof(sc->sc_sens.desc)); 142 sc->sc_sens.type = SENSOR_TEMP; 143 sensor_attach(&sc->sc_sensdev, &sc->sc_sens); 144 sensordev_install(&sc->sc_sensdev); 145 sc->sc_sens.value = 0; 146 147 aml_register_notify(sc->sc_devnode, NULL, 148 acpitz_notify, sc, ACPIDEV_POLL); 149 } 150 151 #if 0 152 int 153 acpitz_setcpu(struct acpitz_softc *sc, int perc) 154 { 155 struct aml_value res0, *ref; 156 int x; 157 158 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL", 0, NULL, &res0)) { 159 printf("%s: _PSL failed\n", DEVNAME(sc)); 160 goto out; 161 } 162 if (res0.type != AML_OBJTYPE_PACKAGE) { 163 printf("%s: not a package\n", DEVNAME(sc)); 164 goto out; 165 } 166 for (x = 0; x < res0.length; x++) { 167 if (res0.v_package[x]->type != AML_OBJTYPE_OBJREF) { 168 printf("%s: _PSL[%d] not a object ref\n", 169 DEVNAME(sc), x); 170 continue; 171 } 172 ref = res0.v_package[x]->v_objref.ref; 173 if (ref->type != AML_OBJTYPE_PROCESSOR) 174 printf("%s: _PSL[%d] not a CPU\n", DEVNAME(sc), x); 175 } 176 out: 177 aml_freevalue(&res0); 178 return (0); 179 } 180 #endif 181 182 int 183 acpitz_setfan(struct acpitz_softc *sc, int i, char *method) 184 { 185 struct aml_node *node; 186 struct aml_value res0, res1, *ref; 187 char name[8]; 188 int rv = 1, x, y; 189 int64_t sta; 190 191 dnprintf(20, "%s: acpitz_setfan(%d, %s)\n", DEVNAME(sc), i, method); 192 193 snprintf(name, sizeof name, "_AL%d", i); 194 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res0)) { 195 dnprintf(20, "%s: %s failed\n", DEVNAME(sc), name); 196 goto out; 197 } 198 199 if (res0.type != AML_OBJTYPE_PACKAGE) { 200 printf("%s: %s not a package\n", DEVNAME(sc), name); 201 goto out; 202 } 203 204 for (x = 0; x < res0.length; x++) { 205 ref = res0.v_package[x]; 206 if (ref->type == AML_OBJTYPE_STRING) { 207 node = aml_searchrel(sc->sc_devnode, ref->v_string); 208 if (node == NULL) { 209 printf("%s: %s[%d] not a valid device\n", 210 DEVNAME(sc), name, x); 211 continue; 212 } 213 ref = node->value; 214 } 215 if (ref->type == AML_OBJTYPE_OBJREF) { 216 ref = ref->v_objref.ref; 217 } 218 if (ref->type != AML_OBJTYPE_DEVICE) { 219 printf("%s: %s[%d] not a valid object\n", DEVNAME(sc), 220 name, x); 221 continue; 222 } 223 if (aml_evalname(sc->sc_acpi, ref->node, "_PR0",0 , NULL, 224 &res1)) { 225 printf("%s: %s[%d] _PR0 failed\n", DEVNAME(sc), 226 name, x); 227 aml_freevalue(&res1); 228 continue; 229 } 230 if (res1.type != AML_OBJTYPE_PACKAGE) { 231 printf("%s: %s[%d] _PR0 not a package\n", DEVNAME(sc), 232 name, x); 233 aml_freevalue(&res1); 234 continue; 235 } 236 for (y = 0; y < res1.length; y++) { 237 ref = res1.v_package[y]; 238 if (ref->type == AML_OBJTYPE_STRING) { 239 node = aml_searchrel(sc->sc_devnode, 240 ref->v_string); 241 if (node == NULL) { 242 printf("%s: %s[%d.%d] _PRO" 243 " not a valid device\n", 244 DEVNAME(sc), name, x, y); 245 continue; 246 } 247 ref = node->value; 248 } 249 if (ref->type == AML_OBJTYPE_OBJREF) { 250 ref = ref->v_objref.ref; 251 } 252 if (ref->type != AML_OBJTYPE_DEVICE && 253 ref->type != AML_OBJTYPE_POWERRSRC) { 254 printf("%s: %s[%d.%d] _PRO not a package\n", 255 DEVNAME(sc), name, x, y); 256 continue; 257 } 258 if (aml_evalname(sc->sc_acpi, ref->node, method, 0, 259 NULL, NULL)) 260 printf("%s: %s[%d.%d] %s fails\n", 261 DEVNAME(sc), name, x, y, method); 262 263 /* save off status of fan */ 264 if (aml_evalinteger(sc->sc_acpi, ref->node, "_STA", 0, 265 NULL, &sta)) 266 printf("%s: %s[%d.%d] _STA fails\n", 267 DEVNAME(sc), name, x, y); 268 else { 269 sc->sc_ac_stat[i] = sta; 270 } 271 } 272 aml_freevalue(&res1); 273 } 274 rv = 0; 275 out: 276 aml_freevalue(&res0); 277 return (rv); 278 } 279 280 void 281 acpitz_refresh(void *arg) 282 { 283 struct acpitz_softc *sc = arg; 284 int i, perc; 285 286 dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc), 287 sc->sc_devnode->name); 288 289 /* get _TMP and debounce the value */ 290 if (-1 == (sc->sc_tmp = acpitz_gettempreading(sc, "_TMP"))) { 291 printf("%s: %s: failed to read temp\n", DEVNAME(sc), 292 sc->sc_devnode->name); 293 return; 294 } 295 /* critical trip points */ 296 if (sc->sc_crt != -1 && sc->sc_crt <= sc->sc_tmp) { 297 /* do critical shutdown */ 298 printf("%s: Critical temperature, shutting down\n", 299 DEVNAME(sc)); 300 psignal(initproc, SIGUSR2); 301 } 302 if (sc->sc_hot != -1 && sc->sc_hot <= sc->sc_tmp) { 303 printf("%s: _HOT temperature\n", DEVNAME(sc)); 304 /* XXX go to S4, until then cool as hard as we can */ 305 } 306 307 /* passive cooling */ 308 if (sc->sc_lasttmp != -1 && sc->sc_tc1 != -1 && sc->sc_tc2 != -1 && 309 sc->sc_psv != -1) { 310 dnprintf(30, "%s: passive cooling: lasttmp: %d tc1: %d " 311 "tc2: %d psv: %d\n", DEVNAME(sc), sc->sc_lasttmp, 312 sc->sc_tc1, sc->sc_tc2, sc->sc_psv); 313 if (sc->sc_psv <= sc->sc_tmp) { 314 sc->sc_pse = 1; 315 perc = sc->sc_tc1 * (sc->sc_tmp - sc->sc_lasttmp) + 316 sc->sc_tc2 * (sc->sc_tmp - sc->sc_psv); 317 perc /= 10; 318 if (perc < 0) 319 perc = 0; 320 else if (perc > 100) 321 perc = 100; 322 /* printf("_TZ perc = %d\n", perc); */ 323 } else if (sc->sc_pse) 324 sc->sc_pse = 0; 325 } 326 sc->sc_lasttmp = sc->sc_tmp; 327 328 /* active cooling */ 329 for (i = 0; i < ACPITZ_MAX_AC; i++) { 330 if (sc->sc_ac[i] != -1 && sc->sc_ac[i] <= sc->sc_tmp) { 331 /* turn on fan i */ 332 if (sc->sc_ac_stat[i] <= 0) 333 acpitz_setfan(sc, i, "_ON_"); 334 } else if (sc->sc_ac[i] != -1) { 335 /* turn off fan i */ 336 if (sc->sc_ac_stat[i] > 0) 337 acpitz_setfan(sc, i, "_OFF"); 338 } 339 } 340 sc->sc_sens.value = sc->sc_tmp * 100000; 341 } 342 343 int 344 acpitz_getreading(struct acpitz_softc *sc, char *name) 345 { 346 struct aml_value res, *ref; 347 int rv = -1; 348 349 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) { 350 dnprintf(10, "%s: acpitz_getreading: no %s\n", DEVNAME(sc), 351 name); 352 goto out; 353 } 354 if (res.type == AML_OBJTYPE_STRING) { 355 struct aml_node *node; 356 node = aml_searchrel(sc->sc_devnode, res.v_string); 357 if (node == NULL) 358 goto out; 359 ref = node->value; 360 } else 361 ref = &res; 362 if (ref->type == AML_OBJTYPE_OBJREF) { 363 ref = ref->v_objref.ref; 364 } 365 rv = aml_val2int(ref); 366 out: 367 aml_freevalue(&res); 368 return (rv); 369 } 370 371 int 372 acpitz_gettempreading(struct acpitz_softc *sc, char *name) 373 { 374 int rv = -1, tmp = -1, i; 375 376 for (i = 0; i < ACPITZ_TMP_RETRY; i++) { 377 tmp = acpitz_getreading(sc, name); 378 if (tmp == -1) 379 goto out; 380 if (KTOC(tmp) > 0) { 381 rv = tmp; 382 break; 383 } else { 384 dnprintf(20, "%s: %d invalid reading on %s, " 385 "debouncing\n", DEVNAME(sc), tmp, name); 386 } 387 388 /* debounce value */ 389 if (cold) 390 delay(1000000); 391 else 392 while (tsleep(sc, PWAIT, "tzsleep", hz) != 393 EWOULDBLOCK); 394 } 395 if (i >= ACPITZ_TMP_RETRY) { 396 printf("%s: %s: failed to read %s\n", DEVNAME(sc), 397 sc->sc_devnode->name, name); 398 goto out; 399 } 400 out: 401 dnprintf(30, "%s: name: %s tmp: %dK => %dC, rv: %d\n", DEVNAME(sc), 402 name, tmp, KTOC(tmp), rv); 403 return (rv); 404 } 405 406 int 407 acpitz_notify(struct aml_node *node, int notify_type, void *arg) 408 { 409 struct acpitz_softc *sc = arg; 410 int crt; 411 412 dnprintf(10, "%s notify: %.2x %s\n", DEVNAME(sc), notify_type, 413 sc->sc_devnode->name); 414 415 switch (notify_type) { 416 case 0x80: /* hardware notifications */ 417 break; 418 case 0x81: /* operating Points changed */ 419 sc->sc_psv = acpitz_gettempreading(sc, "_PSV"); 420 crt = sc->sc_crt; 421 sc->sc_crt = acpitz_gettempreading(sc, "_CRT"); 422 if (crt != sc->sc_crt) 423 printf("%s: new critical temperature: %u degC", 424 DEVNAME(sc), KTOC(sc->sc_crt)); 425 break; 426 case 0x82: /* re-evaluate thermal device list */ 427 break; 428 default: 429 break; 430 } 431 432 acpitz_refresh(sc); 433 return (0); 434 } 435