1 /* $OpenBSD: acpiec.c,v 1.55 2016/10/25 06:55:59 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/signalvar.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 23 #include <machine/bus.h> 24 25 #include <dev/acpi/acpireg.h> 26 #include <dev/acpi/acpivar.h> 27 #include <dev/acpi/acpidev.h> 28 #include <dev/acpi/amltypes.h> 29 #include <dev/acpi/dsdt.h> 30 31 #include <sys/sensors.h> 32 33 int acpiec_match(struct device *, void *, void *); 34 void acpiec_attach(struct device *, struct device *, void *); 35 36 u_int8_t acpiec_status(struct acpiec_softc *); 37 u_int8_t acpiec_read_data(struct acpiec_softc *); 38 void acpiec_write_cmd(struct acpiec_softc *, u_int8_t); 39 void acpiec_write_data(struct acpiec_softc *, u_int8_t); 40 void acpiec_burst_enable(struct acpiec_softc *sc); 41 void acpiec_burst_disable(struct acpiec_softc *sc); 42 43 u_int8_t acpiec_read_1(struct acpiec_softc *, u_int8_t); 44 void acpiec_write_1(struct acpiec_softc *, u_int8_t, u_int8_t); 45 46 void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 47 void acpiec_write(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 48 49 int acpiec_getcrs(struct acpiec_softc *, 50 struct acpi_attach_args *); 51 int acpiec_parse_resources(int, union acpi_resource *, void *); 52 53 void acpiec_wait(struct acpiec_softc *, u_int8_t, u_int8_t); 54 void acpiec_sci_event(struct acpiec_softc *); 55 56 void acpiec_get_events(struct acpiec_softc *); 57 58 int acpiec_gpehandler(struct acpi_softc *, int, void *); 59 60 void acpiec_lock(struct acpiec_softc *); 61 void acpiec_unlock(struct acpiec_softc *); 62 63 /* EC Status bits */ 64 #define EC_STAT_SMI_EVT 0x40 /* SMI event pending */ 65 #define EC_STAT_SCI_EVT 0x20 /* SCI event pending */ 66 #define EC_STAT_BURST 0x10 /* Controller in burst mode */ 67 #define EC_STAT_CMD 0x08 /* data is command */ 68 #define EC_STAT_IBF 0x02 /* input buffer full */ 69 #define EC_STAT_OBF 0x01 /* output buffer full */ 70 71 /* EC Commands */ 72 #define EC_CMD_RD 0x80 /* Read */ 73 #define EC_CMD_WR 0x81 /* Write */ 74 #define EC_CMD_BE 0x82 /* Burst Enable */ 75 #define EC_CMD_BD 0x83 /* Burst Disable */ 76 #define EC_CMD_QR 0x84 /* Query */ 77 78 int acpiec_reg(struct acpiec_softc *); 79 80 struct cfattach acpiec_ca = { 81 sizeof(struct acpiec_softc), acpiec_match, acpiec_attach 82 }; 83 84 struct cfdriver acpiec_cd = { 85 NULL, "acpiec", DV_DULL 86 }; 87 88 const char *acpiec_hids[] = { ACPI_DEV_ECD, 0 }; 89 90 void 91 acpiec_wait(struct acpiec_softc *sc, u_int8_t mask, u_int8_t val) 92 { 93 static int acpiecnowait; 94 u_int8_t stat; 95 96 dnprintf(40, "%s: EC wait_ns for: %b == %02x\n", 97 DEVNAME(sc), (int)mask, 98 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF", (int)val); 99 100 while (((stat = acpiec_status(sc)) & mask) != val) { 101 if (stat & EC_STAT_SCI_EVT) 102 sc->sc_gotsci = 1; 103 if (cold || (stat & EC_STAT_BURST)) 104 delay(1); 105 else 106 tsleep(&acpiecnowait, PWAIT, "acpiec", 1); 107 } 108 109 dnprintf(40, "%s: EC wait_ns, stat: %b\n", DEVNAME(sc), (int)stat, 110 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF"); 111 } 112 113 u_int8_t 114 acpiec_status(struct acpiec_softc *sc) 115 { 116 return (bus_space_read_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0)); 117 } 118 119 void 120 acpiec_write_data(struct acpiec_softc *sc, u_int8_t val) 121 { 122 acpiec_wait(sc, EC_STAT_IBF, 0); 123 dnprintf(40, "acpiec: write_data -- %d\n", (int)val); 124 bus_space_write_1(sc->sc_data_bt, sc->sc_data_bh, 0, val); 125 } 126 127 void 128 acpiec_write_cmd(struct acpiec_softc *sc, u_int8_t val) 129 { 130 acpiec_wait(sc, EC_STAT_IBF, 0); 131 dnprintf(40, "acpiec: write_cmd -- %d\n", (int)val); 132 bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, val); 133 } 134 135 u_int8_t 136 acpiec_read_data(struct acpiec_softc *sc) 137 { 138 u_int8_t val; 139 140 acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF); 141 val = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0); 142 143 dnprintf(40, "acpiec: read_data %d\n", (int)val); 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 if (len > 1) 222 acpiec_burst_enable(sc); 223 for (reg = 0; reg < len; reg++) 224 buffer[reg] = acpiec_read_1(sc, addr + reg); 225 if (len > 1) 226 acpiec_burst_disable(sc); 227 sc->sc_ecbusy = 0; 228 } 229 230 void 231 acpiec_write(struct acpiec_softc *sc, u_int8_t addr, int len, u_int8_t *buffer) 232 { 233 int reg; 234 235 /* 236 * this works because everything runs in the acpi thread context. 237 * at some point add a lock to deal with concurrency so that a 238 * transaction does not get interrupted. 239 */ 240 dnprintf(20, "%s: write %d, %d\n", DEVNAME(sc), (int)addr, len); 241 sc->sc_ecbusy = 1; 242 if (len > 1) 243 acpiec_burst_enable(sc); 244 for (reg = 0; reg < len; reg++) 245 acpiec_write_1(sc, addr + reg, buffer[reg]); 246 if (len > 1) 247 acpiec_burst_disable(sc); 248 sc->sc_ecbusy = 0; 249 } 250 251 int 252 acpiec_match(struct device *parent, void *match, void *aux) 253 { 254 struct acpi_attach_args *aa = aux; 255 struct cfdata *cf = match; 256 struct acpi_ecdt *ecdt = aa->aaa_table; 257 struct acpi_softc *acpisc = (struct acpi_softc *)parent; 258 259 /* Check for early ECDT table attach */ 260 if (ecdt && 261 !memcmp(ecdt->hdr.signature, ECDT_SIG, sizeof(ECDT_SIG) - 1)) 262 return (1); 263 if (acpisc->sc_ec) 264 return (0); 265 266 /* sanity */ 267 return (acpi_matchhids(aa, acpiec_hids, cf->cf_driver->cd_name)); 268 } 269 270 void 271 acpiec_attach(struct device *parent, struct device *self, void *aux) 272 { 273 struct acpiec_softc *sc = (struct acpiec_softc *)self; 274 struct acpi_attach_args *aa = aux; 275 struct aml_value res; 276 int64_t st; 277 278 sc->sc_acpi = (struct acpi_softc *)parent; 279 sc->sc_devnode = aa->aaa_node; 280 281 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st)) 282 st = STA_PRESENT | STA_ENABLED | STA_DEV_OK; 283 if ((st & STA_PRESENT) == 0) { 284 printf(": not present\n"); 285 return; 286 } 287 288 printf("\n"); 289 if (acpiec_getcrs(sc, aa)) { 290 printf("%s: Failed to read resource settings\n", DEVNAME(sc)); 291 return; 292 } 293 294 sc->sc_acpi->sc_ec = sc; 295 296 if (acpiec_reg(sc)) { 297 printf("%s: Failed to register address space\n", DEVNAME(sc)); 298 return; 299 } 300 301 acpiec_get_events(sc); 302 303 dnprintf(10, "%s: GPE: %d\n", DEVNAME(sc), sc->sc_gpe); 304 305 #ifndef SMALL_KERNEL 306 acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler, 307 sc, 1); 308 #endif 309 310 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_GLK", 0, NULL, &res)) 311 sc->sc_glk = 0; 312 else if (res.type != AML_OBJTYPE_INTEGER) 313 sc->sc_glk = 0; 314 else 315 sc->sc_glk = res.v_integer ? 1 : 0; 316 } 317 318 void 319 acpiec_get_events(struct acpiec_softc *sc) 320 { 321 int idx; 322 char name[16]; 323 324 memset(sc->sc_events, 0, sizeof(sc->sc_events)); 325 for (idx = 0; idx < ACPIEC_MAX_EVENTS; idx++) { 326 snprintf(name, sizeof(name), "_Q%02X", idx); 327 sc->sc_events[idx].event = aml_searchname(sc->sc_devnode, name); 328 if (sc->sc_events[idx].event != NULL) 329 dnprintf(10, "%s: Found event %s\n", DEVNAME(sc), name); 330 } 331 } 332 333 int 334 acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg) 335 { 336 struct acpiec_softc *sc = arg; 337 u_int8_t mask, stat, en; 338 int s; 339 340 KASSERT(sc->sc_ecbusy == 0); 341 dnprintf(10, "ACPIEC: got gpe\n"); 342 343 do { 344 if (sc->sc_gotsci) 345 acpiec_sci_event(sc); 346 347 stat = acpiec_status(sc); 348 dnprintf(40, "%s: EC interrupt, stat: %b\n", 349 DEVNAME(sc), (int)stat, 350 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF"); 351 352 if (stat & EC_STAT_SCI_EVT) 353 sc->sc_gotsci = 1; 354 else 355 sc->sc_gotsci = 0; 356 } while (sc->sc_gotsci); 357 358 /* Unmask the GPE which was blocked at interrupt time */ 359 s = spltty(); 360 mask = (1L << (gpe & 7)); 361 en = acpi_read_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3); 362 acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, en | mask); 363 splx(s); 364 365 return (0); 366 } 367 368 int 369 acpiec_parse_resources(int crsidx, union acpi_resource *crs, void *arg) 370 { 371 struct acpiec_softc *sc = arg; 372 int type = AML_CRSTYPE(crs); 373 374 switch (crsidx) { 375 case 0: 376 if (type != SR_IOPORT) { 377 printf("%s: Unexpected resource #%d type %d\n", 378 DEVNAME(sc), crsidx, type); 379 break; 380 } 381 sc->sc_data_bt = sc->sc_acpi->sc_iot; 382 sc->sc_ec_data = crs->sr_ioport._max; 383 break; 384 case 1: 385 if (type != SR_IOPORT) { 386 printf("%s: Unexpected resource #%d type %d\n", 387 DEVNAME(sc), crsidx, type); 388 break; 389 } 390 sc->sc_cmd_bt = sc->sc_acpi->sc_iot; 391 sc->sc_ec_sc = crs->sr_ioport._max; 392 break; 393 case 2: 394 if (!sc->sc_acpi->sc_hw_reduced) { 395 printf("%s: Not running on HW-Reduced ACPI type %d\n", 396 DEVNAME(sc), type); 397 break; 398 } 399 /* XXX: handle SCI GPIO */ 400 break; 401 default: 402 printf("%s: invalid resource #%d type %d\n", 403 DEVNAME(sc), crsidx, type); 404 } 405 406 return 0; 407 } 408 409 int 410 acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa) 411 { 412 struct aml_value res; 413 int64_t gpe; 414 struct acpi_ecdt *ecdt = aa->aaa_table; 415 extern struct aml_node aml_root; 416 int rc; 417 418 /* Check if this is ECDT initialization */ 419 if (ecdt) { 420 /* Get GPE, Data and Control segments */ 421 sc->sc_gpe = ecdt->gpe_bit; 422 423 if (ecdt->ec_control.address_space_id == GAS_SYSTEM_IOSPACE) 424 sc->sc_cmd_bt = sc->sc_acpi->sc_iot; 425 else 426 sc->sc_cmd_bt = sc->sc_acpi->sc_memt; 427 sc->sc_ec_sc = ecdt->ec_control.address; 428 429 if (ecdt->ec_data.address_space_id == GAS_SYSTEM_IOSPACE) 430 sc->sc_data_bt = sc->sc_acpi->sc_iot; 431 else 432 sc->sc_data_bt = sc->sc_acpi->sc_memt; 433 sc->sc_ec_data = ecdt->ec_data.address; 434 435 /* Get devnode from header */ 436 sc->sc_devnode = aml_searchname(&aml_root, ecdt->ec_id); 437 438 goto ecdtdone; 439 } 440 441 rc = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 442 "_GPE", 0, NULL, &gpe); 443 if (rc) { 444 dnprintf(10, "%s: no _GPE\n", DEVNAME(sc)); 445 return (1); 446 } 447 448 sc->sc_gpe = gpe; 449 450 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) { 451 dnprintf(10, "%s: no _CRS\n", DEVNAME(sc)); 452 return (1); 453 } 454 455 /* Parse CRS to get control and data registers */ 456 457 if (res.type != AML_OBJTYPE_BUFFER) { 458 dnprintf(10, "%s: unknown _CRS type %d\n", 459 DEVNAME(sc), res.type); 460 aml_freevalue(&res); 461 return (1); 462 } 463 464 aml_parse_resource(&res, acpiec_parse_resources, sc); 465 aml_freevalue(&res); 466 if (sc->sc_ec_data == 0 || sc->sc_ec_sc == 0) { 467 printf("%s: failed to read from _CRS\n", DEVNAME(sc)); 468 return (1); 469 } 470 471 ecdtdone: 472 473 dnprintf(10, "%s: Data: 0x%lx, S/C: 0x%lx\n", 474 DEVNAME(sc), sc->sc_ec_data, sc->sc_ec_sc); 475 476 if (bus_space_map(sc->sc_cmd_bt, sc->sc_ec_sc, 1, 0, &sc->sc_cmd_bh)) { 477 dnprintf(10, "%s: failed to map S/C reg.\n", DEVNAME(sc)); 478 return (1); 479 } 480 481 rc = bus_space_map(sc->sc_data_bt, sc->sc_ec_data, 1, 0, 482 &sc->sc_data_bh); 483 if (rc) { 484 dnprintf(10, "%s: failed to map DATA reg.\n", DEVNAME(sc)); 485 bus_space_unmap(sc->sc_cmd_bt, sc->sc_cmd_bh, 1); 486 return (1); 487 } 488 489 return (0); 490 } 491 492 int 493 acpiec_reg(struct acpiec_softc *sc) 494 { 495 struct aml_value arg[2]; 496 struct aml_node *node; 497 498 memset(&arg, 0, sizeof(arg)); 499 arg[0].type = AML_OBJTYPE_INTEGER; 500 arg[0].v_integer = ACPI_OPREG_EC; 501 arg[1].type = AML_OBJTYPE_INTEGER; 502 arg[1].v_integer = 1; 503 504 node = aml_searchname(sc->sc_devnode, "_REG"); 505 if (node && aml_evalnode(sc->sc_acpi, node, 2, arg, NULL)) { 506 dnprintf(10, "%s: eval method _REG failed\n", DEVNAME(sc)); 507 printf("acpiec _REG failed, broken BIOS\n"); 508 } 509 510 return (0); 511 } 512 513 void 514 acpiec_lock(struct acpiec_softc *sc) 515 { 516 KASSERT(sc->sc_ecbusy == 0); 517 518 sc->sc_ecbusy = 1; 519 520 if (sc->sc_glk) { 521 acpi_glk_enter(); 522 } 523 } 524 525 void 526 acpiec_unlock(struct acpiec_softc *sc) 527 { 528 KASSERT(sc->sc_ecbusy == 1); 529 530 if (sc->sc_glk) { 531 acpi_glk_leave(); 532 } 533 534 sc->sc_ecbusy = 0; 535 } 536