1 /* $OpenBSD: acpitz.c,v 1.31 2009/04/26 02:20:58 cnst 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 146 aml_register_notify(sc->sc_devnode, NULL, 147 acpitz_notify, sc, ACPIDEV_POLL); 148 } 149 150 #if 0 151 int 152 acpitz_setcpu(struct acpitz_softc *sc, int perc) 153 { 154 struct aml_value res0, *ref; 155 int x; 156 157 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSL", 0, NULL, &res0)) { 158 printf("%s: _PSL failed\n", DEVNAME(sc)); 159 goto out; 160 } 161 if (res0.type != AML_OBJTYPE_PACKAGE) { 162 printf("%s: not a package\n", DEVNAME(sc)); 163 goto out; 164 } 165 for (x = 0; x < res0.length; x++) { 166 if (res0.v_package[x]->type != AML_OBJTYPE_OBJREF) { 167 printf("%s: _PSL[%d] not a object ref\n", 168 DEVNAME(sc), x); 169 continue; 170 } 171 ref = res0.v_package[x]->v_objref.ref; 172 if (ref->type != AML_OBJTYPE_PROCESSOR) 173 printf("%s: _PSL[%d] not a CPU\n", DEVNAME(sc), x); 174 } 175 out: 176 aml_freevalue(&res0); 177 return (0); 178 } 179 #endif 180 181 int 182 acpitz_setfan(struct acpitz_softc *sc, int i, char *method) 183 { 184 struct aml_node *node; 185 struct aml_value res0, res1, *ref; 186 char name[8]; 187 int rv = 1, x, y; 188 int64_t sta; 189 190 dnprintf(20, "%s: acpitz_setfan(%d, %s)\n", DEVNAME(sc), i, method); 191 192 snprintf(name, sizeof name, "_AL%d", i); 193 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res0)) { 194 dnprintf(20, "%s: %s failed\n", DEVNAME(sc), name); 195 goto out; 196 } 197 198 if (res0.type != AML_OBJTYPE_PACKAGE) { 199 printf("%s: %s not a package\n", DEVNAME(sc), name); 200 goto out; 201 } 202 203 for (x = 0; x < res0.length; x++) { 204 ref = res0.v_package[x]; 205 if (ref->type == AML_OBJTYPE_STRING) { 206 node = aml_searchrel(sc->sc_devnode, ref->v_string); 207 if (node == NULL) { 208 printf("%s: %s[%d] not a valid device\n", 209 DEVNAME(sc), name, x); 210 continue; 211 } 212 ref = node->value; 213 } 214 if (ref->type == AML_OBJTYPE_OBJREF) { 215 ref = ref->v_objref.ref; 216 } 217 if (ref->type != AML_OBJTYPE_DEVICE) { 218 printf("%s: %s[%d] not a valid object\n", DEVNAME(sc), 219 name, x); 220 continue; 221 } 222 if (aml_evalname(sc->sc_acpi, ref->node, "_PR0",0 , NULL, 223 &res1)) { 224 printf("%s: %s[%d] _PR0 failed\n", DEVNAME(sc), 225 name, x); 226 aml_freevalue(&res1); 227 continue; 228 } 229 if (res1.type != AML_OBJTYPE_PACKAGE) { 230 printf("%s: %s[%d] _PR0 not a package\n", DEVNAME(sc), 231 name, x); 232 aml_freevalue(&res1); 233 continue; 234 } 235 for (y = 0; y < res1.length; y++) { 236 ref = res1.v_package[y]; 237 if (ref->type == AML_OBJTYPE_STRING) { 238 node = aml_searchrel(sc->sc_devnode, 239 ref->v_string); 240 if (node == NULL) { 241 printf("%s: %s[%d.%d] _PRO" 242 " not a valid device\n", 243 DEVNAME(sc), name, x, y); 244 continue; 245 } 246 ref = node->value; 247 } 248 if (ref->type == AML_OBJTYPE_OBJREF) { 249 ref = ref->v_objref.ref; 250 } 251 if (ref->type != AML_OBJTYPE_DEVICE && 252 ref->type != AML_OBJTYPE_POWERRSRC) { 253 printf("%s: %s[%d.%d] _PRO not a package\n", 254 DEVNAME(sc), name, x, y); 255 continue; 256 } 257 if (aml_evalname(sc->sc_acpi, ref->node, method, 0, 258 NULL, NULL)) 259 printf("%s: %s[%d.%d] %s fails\n", 260 DEVNAME(sc), name, x, y, method); 261 262 /* save off status of fan */ 263 if (aml_evalinteger(sc->sc_acpi, ref->node, "_STA", 0, 264 NULL, &sta)) 265 printf("%s: %s[%d.%d] _STA fails\n", 266 DEVNAME(sc), name, x, y); 267 else { 268 sc->sc_ac_stat[i] = sta; 269 } 270 } 271 aml_freevalue(&res1); 272 } 273 rv = 0; 274 out: 275 aml_freevalue(&res0); 276 return (rv); 277 } 278 279 void 280 acpitz_refresh(void *arg) 281 { 282 struct acpitz_softc *sc = arg; 283 int i, perc; 284 285 dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc), 286 sc->sc_devnode->name); 287 288 /* get _TMP and debounce the value */ 289 if (-1 == (sc->sc_tmp = acpitz_gettempreading(sc, "_TMP"))) { 290 printf("%s: %s: failed to read temp\n", DEVNAME(sc), 291 sc->sc_devnode->name); 292 return; 293 } 294 /* critical trip points */ 295 if (sc->sc_crt != -1 && sc->sc_crt <= sc->sc_tmp) { 296 /* do critical shutdown */ 297 printf("%s: Critical temperature, shutting down\n", 298 DEVNAME(sc)); 299 psignal(initproc, SIGUSR2); 300 } 301 if (sc->sc_hot != -1 && sc->sc_hot <= sc->sc_tmp) { 302 printf("%s: _HOT temperature\n", DEVNAME(sc)); 303 /* XXX go to S4, until then cool as hard as we can */ 304 } 305 306 /* passive cooling */ 307 if (sc->sc_lasttmp != -1 && sc->sc_tc1 != -1 && sc->sc_tc2 != -1 && 308 sc->sc_psv != -1) { 309 dnprintf(30, "%s: passive cooling: lasttmp: %d tc1: %d " 310 "tc2: %d psv: %d\n", DEVNAME(sc), sc->sc_lasttmp, 311 sc->sc_tc1, sc->sc_tc2, sc->sc_psv); 312 if (sc->sc_psv <= sc->sc_tmp) { 313 sc->sc_pse = 1; 314 perc = sc->sc_tc1 * (sc->sc_tmp - sc->sc_lasttmp) + 315 sc->sc_tc2 * (sc->sc_tmp - sc->sc_psv); 316 perc /= 10; 317 if (perc < 0) 318 perc = 0; 319 else if (perc > 100) 320 perc = 100; 321 /* printf("_TZ perc = %d\n", perc); */ 322 } else if (sc->sc_pse) 323 sc->sc_pse = 0; 324 } 325 sc->sc_lasttmp = sc->sc_tmp; 326 327 /* active cooling */ 328 for (i = 0; i < ACPITZ_MAX_AC; i++) { 329 if (sc->sc_ac[i] != -1 && sc->sc_ac[i] <= sc->sc_tmp) { 330 /* turn on fan i */ 331 if (sc->sc_ac_stat[i] <= 0) 332 acpitz_setfan(sc, i, "_ON_"); 333 } else if (sc->sc_ac[i] != -1) { 334 /* turn off fan i */ 335 if (sc->sc_ac_stat[i] > 0) 336 acpitz_setfan(sc, i, "_OFF"); 337 } 338 } 339 sc->sc_sens.value = sc->sc_tmp * 100000 - 50000; 340 } 341 342 int 343 acpitz_getreading(struct acpitz_softc *sc, char *name) 344 { 345 struct aml_value res, *ref; 346 int rv = -1; 347 348 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, name, 0, NULL, &res)) { 349 dnprintf(10, "%s: acpitz_getreading: no %s\n", DEVNAME(sc), 350 name); 351 goto out; 352 } 353 if (res.type == AML_OBJTYPE_STRING) { 354 struct aml_node *node; 355 node = aml_searchrel(sc->sc_devnode, res.v_string); 356 if (node == NULL) 357 goto out; 358 ref = node->value; 359 } else 360 ref = &res; 361 if (ref->type == AML_OBJTYPE_OBJREF) { 362 ref = ref->v_objref.ref; 363 } 364 rv = aml_val2int(ref); 365 out: 366 aml_freevalue(&res); 367 return (rv); 368 } 369 370 int 371 acpitz_gettempreading(struct acpitz_softc *sc, char *name) 372 { 373 int rv = -1, tmp = -1, i; 374 375 for (i = 0; i < ACPITZ_TMP_RETRY; i++) { 376 tmp = acpitz_getreading(sc, name); 377 if (tmp == -1) 378 goto out; 379 if (KTOC(tmp) > 0) { 380 rv = tmp; 381 break; 382 } else { 383 dnprintf(20, "%s: %d invalid reading on %s, " 384 "debouncing\n", DEVNAME(sc), tmp, name); 385 } 386 387 /* debounce value */ 388 if (cold) 389 delay(1000000); 390 else 391 while (tsleep(sc, PWAIT, "tzsleep", hz) != 392 EWOULDBLOCK); 393 } 394 if (i >= ACPITZ_TMP_RETRY) { 395 printf("%s: %s: failed to read %s\n", DEVNAME(sc), 396 sc->sc_devnode->name, name); 397 goto out; 398 } 399 out: 400 dnprintf(30, "%s: name: %s tmp: %dK => %dC, rv: %d\n", DEVNAME(sc), 401 name, tmp, KTOC(tmp), rv); 402 return (rv); 403 } 404 405 int 406 acpitz_notify(struct aml_node *node, int notify_type, void *arg) 407 { 408 struct acpitz_softc *sc = arg; 409 int crt; 410 411 dnprintf(10, "%s notify: %.2x %s\n", DEVNAME(sc), notify_type, 412 sc->sc_devnode->name); 413 414 switch (notify_type) { 415 case 0x80: /* hardware notifications */ 416 break; 417 case 0x81: /* operating Points changed */ 418 sc->sc_psv = acpitz_gettempreading(sc, "_PSV"); 419 crt = sc->sc_crt; 420 sc->sc_crt = acpitz_gettempreading(sc, "_CRT"); 421 if (crt != sc->sc_crt) 422 printf("%s: new critical temperature: %u degC", 423 DEVNAME(sc), KTOC(sc->sc_crt)); 424 break; 425 case 0x82: /* re-evaluate thermal device list */ 426 break; 427 default: 428 break; 429 } 430 431 acpitz_refresh(sc); 432 return (0); 433 } 434