1 /* $OpenBSD: acpiec.c,v 1.46 2012/07/13 10:37:40 pirofti Exp $ */ 2 /* 3 * Copyright (c) 2006 Can Erkin Acar <canacar@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/proc.h> 20 #include <sys/signalvar.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <machine/bus.h> 26 27 #include <dev/acpi/acpireg.h> 28 #include <dev/acpi/acpivar.h> 29 #include <dev/acpi/acpidev.h> 30 #include <dev/acpi/amltypes.h> 31 #include <dev/acpi/dsdt.h> 32 33 #include <sys/sensors.h> 34 35 int acpiec_match(struct device *, void *, void *); 36 void acpiec_attach(struct device *, struct device *, void *); 37 38 u_int8_t acpiec_status(struct acpiec_softc *); 39 u_int8_t acpiec_read_data(struct acpiec_softc *); 40 void acpiec_write_cmd(struct acpiec_softc *, u_int8_t); 41 void acpiec_write_data(struct acpiec_softc *, u_int8_t); 42 void acpiec_burst_enable(struct acpiec_softc *sc); 43 void acpiec_burst_disable(struct acpiec_softc *sc); 44 45 u_int8_t acpiec_read_1(struct acpiec_softc *, u_int8_t); 46 void acpiec_write_1(struct acpiec_softc *, u_int8_t, u_int8_t); 47 48 void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 49 void acpiec_write(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 50 51 int acpiec_getcrs(struct acpiec_softc *, 52 struct acpi_attach_args *); 53 int acpiec_getregister(const u_int8_t *, int, int *, bus_size_t *); 54 55 void acpiec_wait(struct acpiec_softc *, u_int8_t, u_int8_t); 56 void acpiec_sci_event(struct acpiec_softc *); 57 58 void acpiec_get_events(struct acpiec_softc *); 59 60 int acpiec_gpehandler(struct acpi_softc *, int, void *); 61 62 /* EC Status bits */ 63 #define EC_STAT_SMI_EVT 0x40 /* SMI event pending */ 64 #define EC_STAT_SCI_EVT 0x20 /* SCI event pending */ 65 #define EC_STAT_BURST 0x10 /* Controller in burst mode */ 66 #define EC_STAT_CMD 0x08 /* data is command */ 67 #define EC_STAT_IBF 0x02 /* input buffer full */ 68 #define EC_STAT_OBF 0x01 /* output buffer full */ 69 70 /* EC Commands */ 71 #define EC_CMD_RD 0x80 /* Read */ 72 #define EC_CMD_WR 0x81 /* Write */ 73 #define EC_CMD_BE 0x82 /* Burst Enable */ 74 #define EC_CMD_BD 0x83 /* Burst Disable */ 75 #define EC_CMD_QR 0x84 /* Query */ 76 77 #define REG_TYPE_EC 3 78 79 int acpiec_reg(struct acpiec_softc *); 80 81 struct cfattach acpiec_ca = { 82 sizeof(struct acpiec_softc), acpiec_match, acpiec_attach 83 }; 84 85 struct cfdriver acpiec_cd = { 86 NULL, "acpiec", DV_DULL 87 }; 88 89 const char *acpiec_hids[] = { ACPI_DEV_ECD, 0 }; 90 91 void 92 acpiec_wait(struct acpiec_softc *sc, u_int8_t mask, u_int8_t val) 93 { 94 static int acpiecnowait; 95 u_int8_t stat; 96 97 dnprintf(40, "%s: EC wait_ns for: %b == %02x\n", 98 DEVNAME(sc), (int)mask, 99 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF", (int)val); 100 101 while (((stat = acpiec_status(sc)) & mask) != val) { 102 if (stat & EC_STAT_SCI_EVT) 103 sc->sc_gotsci = 1; 104 if (cold || (stat & EC_STAT_BURST)) 105 delay(1); 106 else 107 tsleep(&acpiecnowait, PWAIT, "acpiec", 1); 108 } 109 110 dnprintf(40, "%s: EC wait_ns, stat: %b\n", DEVNAME(sc), (int)stat, 111 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF"); 112 } 113 114 u_int8_t 115 acpiec_status(struct acpiec_softc *sc) 116 { 117 return (bus_space_read_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0)); 118 } 119 120 void 121 acpiec_write_data(struct acpiec_softc *sc, u_int8_t val) 122 { 123 acpiec_wait(sc, EC_STAT_IBF, 0); 124 dnprintf(40, "acpiec: write_data -- %d\n", (int)val); 125 bus_space_write_1(sc->sc_data_bt, sc->sc_data_bh, 0, val); 126 } 127 128 void 129 acpiec_write_cmd(struct acpiec_softc *sc, u_int8_t val) 130 { 131 acpiec_wait(sc, EC_STAT_IBF, 0); 132 dnprintf(40, "acpiec: write_cmd -- %d\n", (int)val); 133 bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, val); 134 } 135 136 u_int8_t 137 acpiec_read_data(struct acpiec_softc *sc) 138 { 139 u_int8_t val; 140 141 acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF); 142 dnprintf(40, "acpiec: read_data\n", (int)val); 143 val = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0); 144 145 return (val); 146 } 147 148 void 149 acpiec_sci_event(struct acpiec_softc *sc) 150 { 151 u_int8_t evt; 152 153 sc->sc_gotsci = 0; 154 155 acpiec_wait(sc, EC_STAT_IBF, 0); 156 bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, EC_CMD_QR); 157 158 acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF); 159 evt = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0); 160 161 if (evt) { 162 dnprintf(10, "%s: sci_event: 0x%02x\n", DEVNAME(sc), (int)evt); 163 aml_evalnode(sc->sc_acpi, sc->sc_events[evt].event, 0, NULL, 164 NULL); 165 } 166 } 167 168 u_int8_t 169 acpiec_read_1(struct acpiec_softc *sc, u_int8_t addr) 170 { 171 u_int8_t val; 172 173 if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT) 174 sc->sc_gotsci = 1; 175 176 acpiec_write_cmd(sc, EC_CMD_RD); 177 acpiec_write_data(sc, addr); 178 179 val = acpiec_read_data(sc); 180 181 return (val); 182 } 183 184 void 185 acpiec_write_1(struct acpiec_softc *sc, u_int8_t addr, u_int8_t data) 186 { 187 if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT) 188 sc->sc_gotsci = 1; 189 190 acpiec_write_cmd(sc, EC_CMD_WR); 191 acpiec_write_data(sc, addr); 192 acpiec_write_data(sc, data); 193 } 194 195 void 196 acpiec_burst_enable(struct acpiec_softc *sc) 197 { 198 acpiec_write_cmd(sc, EC_CMD_BE); 199 acpiec_read_data(sc); 200 } 201 202 void 203 acpiec_burst_disable(struct acpiec_softc *sc) 204 { 205 if ((acpiec_status(sc) & EC_STAT_BURST) == EC_STAT_BURST) 206 acpiec_write_cmd(sc, EC_CMD_BD); 207 } 208 209 void 210 acpiec_read(struct acpiec_softc *sc, u_int8_t addr, int len, u_int8_t *buffer) 211 { 212 int reg; 213 214 /* 215 * this works because everything runs in the acpi thread context. 216 * at some point add a lock to deal with concurrency so that a 217 * transaction does not get interrupted. 218 */ 219 dnprintf(20, "%s: read %d, %d\n", DEVNAME(sc), (int)addr, len); 220 sc->sc_ecbusy = 1; 221 acpiec_burst_enable(sc); 222 for (reg = 0; reg < len; reg++) 223 buffer[reg] = acpiec_read_1(sc, addr + reg); 224 acpiec_burst_disable(sc); 225 sc->sc_ecbusy = 0; 226 } 227 228 void 229 acpiec_write(struct acpiec_softc *sc, u_int8_t addr, int len, u_int8_t *buffer) 230 { 231 int reg; 232 233 /* 234 * this works because everything runs in the acpi thread context. 235 * at some point add a lock to deal with concurrency so that a 236 * transaction does not get interrupted. 237 */ 238 dnprintf(20, "%s: write %d, %d\n", DEVNAME(sc), (int)addr, len); 239 sc->sc_ecbusy = 1; 240 acpiec_burst_enable(sc); 241 for (reg = 0; reg < len; reg++) 242 acpiec_write_1(sc, addr + reg, buffer[reg]); 243 acpiec_burst_disable(sc); 244 sc->sc_ecbusy = 0; 245 } 246 247 int 248 acpiec_match(struct device *parent, void *match, void *aux) 249 { 250 struct acpi_attach_args *aa = aux; 251 struct cfdata *cf = match; 252 struct acpi_ecdt *ecdt = aa->aaa_table; 253 struct acpi_softc *acpisc = (struct acpi_softc *)parent; 254 255 /* Check for early ECDT table attach */ 256 if (ecdt && 257 !memcmp(ecdt->hdr.signature, ECDT_SIG, sizeof(ECDT_SIG) - 1)) 258 return (1); 259 if (acpisc->sc_ec) 260 return (0); 261 262 /* sanity */ 263 return (acpi_matchhids(aa, acpiec_hids, cf->cf_driver->cd_name)); 264 } 265 266 void 267 acpiec_attach(struct device *parent, struct device *self, void *aux) 268 { 269 struct acpiec_softc *sc = (struct acpiec_softc *)self; 270 struct acpi_attach_args *aa = aux; 271 struct aml_value res; 272 273 sc->sc_acpi = (struct acpi_softc *)parent; 274 sc->sc_devnode = aa->aaa_node; 275 276 if (acpiec_getcrs(sc, aa)) { 277 printf(": Failed to read resource settings\n"); 278 return; 279 } 280 281 sc->sc_acpi->sc_ec = sc; 282 283 if (acpiec_reg(sc)) { 284 printf(": Failed to register address space\n"); 285 return; 286 } 287 288 acpiec_get_events(sc); 289 290 dnprintf(10, "%s: GPE: %d\n", DEVNAME(sc), sc->sc_gpe); 291 292 #ifndef SMALL_KERNEL 293 acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler, 294 sc, 1); 295 #endif 296 297 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_GLK", 0, NULL, &res)) 298 sc->sc_glk = 0; 299 else if (res.type != AML_OBJTYPE_INTEGER) 300 sc->sc_glk = 0; 301 else 302 sc->sc_glk = res.v_integer ? 1 : 0; 303 304 printf("\n"); 305 } 306 307 void 308 acpiec_get_events(struct acpiec_softc *sc) 309 { 310 int idx; 311 char name[16]; 312 313 memset(sc->sc_events, 0, sizeof(sc->sc_events)); 314 for (idx = 0; idx < ACPIEC_MAX_EVENTS; idx++) { 315 snprintf(name, sizeof(name), "_Q%02X", idx); 316 sc->sc_events[idx].event = aml_searchname(sc->sc_devnode, name); 317 if (sc->sc_events[idx].event != NULL) 318 dnprintf(10, "%s: Found event %s\n", DEVNAME(sc), name); 319 } 320 } 321 322 int 323 acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg) 324 { 325 struct acpiec_softc *sc = arg; 326 u_int8_t mask, stat, en; 327 int s; 328 329 KASSERT(sc->sc_ecbusy == 0); 330 dnprintf(10, "ACPIEC: got gpe\n"); 331 332 do { 333 if (sc->sc_gotsci) 334 acpiec_sci_event(sc); 335 336 stat = acpiec_status(sc); 337 dnprintf(40, "%s: EC interrupt, stat: %b\n", 338 DEVNAME(sc), (int)stat, 339 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF"); 340 341 if (stat & EC_STAT_SCI_EVT) 342 sc->sc_gotsci = 1; 343 } while (sc->sc_gotsci); 344 345 /* Unmask the GPE which was blocked at interrupt time */ 346 s = spltty(); 347 mask = (1L << (gpe & 7)); 348 en = acpi_read_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3); 349 acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, en | mask); 350 splx(s); 351 352 return (0); 353 } 354 355 /* parse the resource buffer to get a 'register' value */ 356 int 357 acpiec_getregister(const u_int8_t *buf, int size, int *type, bus_size_t *addr) 358 { 359 int len, hlen; 360 361 #define RES_TYPE_MASK 0x80 362 #define RES_LENGTH_MASK 0x07 363 #define RES_TYPE_IOPORT 0x47 364 #define RES_TYPE_ENDTAG 0x79 365 366 if (size <= 0) 367 return (0); 368 369 if (*buf & RES_TYPE_MASK) { 370 /* large resource */ 371 if (size < 3) 372 return (1); 373 len = (int)buf[1] + 256 * (int)buf[2]; 374 hlen = 3; 375 } else { 376 /* small resource */ 377 len = buf[0] & RES_LENGTH_MASK; 378 hlen = 1; 379 } 380 381 /* XXX todo: decode other types */ 382 if (*buf != RES_TYPE_IOPORT) 383 return (0); 384 385 if (size < hlen + len) 386 return (0); 387 388 /* XXX validate? */ 389 *type = GAS_SYSTEM_IOSPACE; 390 *addr = (int)buf[2] + 256 * (int)buf[3]; 391 392 return (hlen + len); 393 } 394 395 int 396 acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa) 397 { 398 struct aml_value res; 399 bus_size_t ec_sc, ec_data; 400 int dtype, ctype; 401 char *buf; 402 int size, ret; 403 int64_t gpe; 404 struct acpi_ecdt *ecdt = aa->aaa_table; 405 extern struct aml_node aml_root; 406 407 /* Check if this is ECDT initialization */ 408 if (ecdt) { 409 /* Get GPE, Data and Control segments */ 410 sc->sc_gpe = ecdt->gpe_bit; 411 412 ctype = ecdt->ec_control.address_space_id; 413 ec_sc = ecdt->ec_control.address; 414 415 dtype = ecdt->ec_data.address_space_id; 416 ec_data = ecdt->ec_data.address; 417 418 /* Get devnode from header */ 419 sc->sc_devnode = aml_searchname(&aml_root, ecdt->ec_id); 420 421 goto ecdtdone; 422 } 423 424 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_GPE", 0, NULL, &gpe)) { 425 dnprintf(10, "%s: no _GPE\n", DEVNAME(sc)); 426 return (1); 427 } 428 429 sc->sc_gpe = gpe; 430 431 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) { 432 dnprintf(10, "%s: no _CRS\n", DEVNAME(sc)); 433 return (1); 434 } 435 436 /* Parse CRS to get control and data registers */ 437 438 if (res.type != AML_OBJTYPE_BUFFER) { 439 dnprintf(10, "%s: unknown _CRS type %d\n", 440 DEVNAME(sc), res.type); 441 aml_freevalue(&res); 442 return (1); 443 } 444 445 size = res.length; 446 buf = res.v_buffer; 447 448 ret = acpiec_getregister(buf, size, &dtype, &ec_data); 449 if (ret <= 0) { 450 dnprintf(10, "%s: failed to read DATA from _CRS\n", 451 DEVNAME(sc)); 452 aml_freevalue(&res); 453 return (1); 454 } 455 456 buf += ret; 457 size -= ret; 458 459 ret = acpiec_getregister(buf, size, &ctype, &ec_sc); 460 if (ret <= 0) { 461 dnprintf(10, "%s: failed to read S/C from _CRS\n", 462 DEVNAME(sc)); 463 aml_freevalue(&res); 464 return (1); 465 } 466 467 buf += ret; 468 size -= ret; 469 470 if (size != 2 || *buf != RES_TYPE_ENDTAG) { 471 dnprintf(10, "%s: no _CRS end tag\n", DEVNAME(sc)); 472 aml_freevalue(&res); 473 return (1); 474 } 475 aml_freevalue(&res); 476 477 /* XXX: todo - validate _CRS checksum? */ 478 ecdtdone: 479 480 dnprintf(10, "%s: Data: 0x%x, S/C: 0x%x\n", 481 DEVNAME(sc), ec_data, ec_sc); 482 483 if (ctype == GAS_SYSTEM_IOSPACE) 484 sc->sc_cmd_bt = aa->aaa_iot; 485 else 486 sc->sc_cmd_bt = aa->aaa_memt; 487 488 if (bus_space_map(sc->sc_cmd_bt, ec_sc, 1, 0, &sc->sc_cmd_bh)) { 489 dnprintf(10, "%s: failed to map S/C reg.\n", DEVNAME(sc)); 490 return (1); 491 } 492 493 if (dtype == GAS_SYSTEM_IOSPACE) 494 sc->sc_data_bt = aa->aaa_iot; 495 else 496 sc->sc_data_bt = aa->aaa_memt; 497 498 if (bus_space_map(sc->sc_data_bt, ec_data, 1, 0, &sc->sc_data_bh)) { 499 dnprintf(10, "%s: failed to map DATA reg.\n", DEVNAME(sc)); 500 bus_space_unmap(sc->sc_cmd_bt, sc->sc_cmd_bh, 1); 501 return (1); 502 } 503 504 return (0); 505 } 506 507 int 508 acpiec_reg(struct acpiec_softc *sc) 509 { 510 struct aml_value arg[2]; 511 struct aml_node *node; 512 513 memset(&arg, 0, sizeof(arg)); 514 arg[0].type = AML_OBJTYPE_INTEGER; 515 arg[0].v_integer = REG_TYPE_EC; 516 arg[1].type = AML_OBJTYPE_INTEGER; 517 arg[1].v_integer = 1; 518 519 node = aml_searchname(sc->sc_devnode, "_REG"); 520 if (node && aml_evalnode(sc->sc_acpi, node, 2, arg, NULL)) { 521 dnprintf(10, "%s: eval method _REG failed\n", DEVNAME(sc)); 522 printf("acpiec _REG failed, broken BIOS\n"); 523 } 524 525 return (0); 526 } 527