1 /* $OpenBSD: acpiec.c,v 1.65 2022/08/10 16:58:16 patrick 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 uint8_t acpiec_status(struct acpiec_softc *); 37 uint8_t acpiec_read_data(struct acpiec_softc *); 38 void acpiec_write_cmd(struct acpiec_softc *, uint8_t); 39 void acpiec_write_data(struct acpiec_softc *, uint8_t); 40 void acpiec_burst_enable(struct acpiec_softc *sc); 41 void acpiec_burst_disable(struct acpiec_softc *sc); 42 43 uint8_t acpiec_read_1(struct acpiec_softc *, uint8_t); 44 void acpiec_write_1(struct acpiec_softc *, uint8_t, uint8_t); 45 46 void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *); 47 void acpiec_write(struct acpiec_softc *, uint8_t, int, uint8_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 *, uint8_t, uint8_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 /* EC Status bits */ 61 #define EC_STAT_SMI_EVT 0x40 /* SMI event pending */ 62 #define EC_STAT_SCI_EVT 0x20 /* SCI event pending */ 63 #define EC_STAT_BURST 0x10 /* Controller in burst mode */ 64 #define EC_STAT_CMD 0x08 /* data is command */ 65 #define EC_STAT_IBF 0x02 /* input buffer full */ 66 #define EC_STAT_OBF 0x01 /* output buffer full */ 67 68 /* EC Commands */ 69 #define EC_CMD_RD 0x80 /* Read */ 70 #define EC_CMD_WR 0x81 /* Write */ 71 #define EC_CMD_BE 0x82 /* Burst Enable */ 72 #define EC_CMD_BD 0x83 /* Burst Disable */ 73 #define EC_CMD_QR 0x84 /* Query */ 74 75 int acpiec_reg(struct acpiec_softc *); 76 77 const struct cfattach acpiec_ca = { 78 sizeof(struct acpiec_softc), acpiec_match, acpiec_attach 79 }; 80 81 struct cfdriver acpiec_cd = { 82 NULL, "acpiec", DV_DULL 83 }; 84 85 const char *acpiec_hids[] = { 86 ACPI_DEV_ECD, 87 NULL 88 }; 89 90 void 91 acpiec_wait(struct acpiec_softc *sc, uint8_t mask, uint8_t val) 92 { 93 static int acpiecnowait; 94 uint8_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 uint8_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, uint8_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, uint8_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 uint8_t 136 acpiec_read_data(struct acpiec_softc *sc) 137 { 138 uint8_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 uint8_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 uint8_t 169 acpiec_read_1(struct acpiec_softc *sc, uint8_t addr) 170 { 171 uint8_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, uint8_t addr, uint8_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 if (sc->sc_cantburst) 199 return; 200 201 acpiec_write_cmd(sc, EC_CMD_BE); 202 acpiec_read_data(sc); 203 } 204 205 void 206 acpiec_burst_disable(struct acpiec_softc *sc) 207 { 208 if (sc->sc_cantburst) 209 return; 210 211 if ((acpiec_status(sc) & EC_STAT_BURST) == EC_STAT_BURST) 212 acpiec_write_cmd(sc, EC_CMD_BD); 213 } 214 215 void 216 acpiec_read(struct acpiec_softc *sc, uint8_t addr, int len, uint8_t *buffer) 217 { 218 int reg; 219 220 /* 221 * this works because everything runs in the acpi thread context. 222 * at some point add a lock to deal with concurrency so that a 223 * transaction does not get interrupted. 224 */ 225 dnprintf(20, "%s: read %d, %d\n", DEVNAME(sc), (int)addr, len); 226 sc->sc_ecbusy = 1; 227 acpiec_burst_enable(sc); 228 for (reg = 0; reg < len; reg++) 229 buffer[reg] = acpiec_read_1(sc, addr + reg); 230 acpiec_burst_disable(sc); 231 sc->sc_ecbusy = 0; 232 } 233 234 void 235 acpiec_write(struct acpiec_softc *sc, uint8_t addr, int len, uint8_t *buffer) 236 { 237 int reg; 238 239 /* 240 * this works because everything runs in the acpi thread context. 241 * at some point add a lock to deal with concurrency so that a 242 * transaction does not get interrupted. 243 */ 244 dnprintf(20, "%s: write %d, %d\n", DEVNAME(sc), (int)addr, len); 245 sc->sc_ecbusy = 1; 246 acpiec_burst_enable(sc); 247 for (reg = 0; reg < len; reg++) 248 acpiec_write_1(sc, addr + reg, buffer[reg]); 249 acpiec_burst_disable(sc); 250 sc->sc_ecbusy = 0; 251 } 252 253 int 254 acpiec_match(struct device *parent, void *match, void *aux) 255 { 256 struct acpi_attach_args *aa = aux; 257 struct cfdata *cf = match; 258 struct acpi_ecdt *ecdt = aa->aaa_table; 259 struct acpi_softc *acpisc = (struct acpi_softc *)parent; 260 261 /* Check for early ECDT table attach */ 262 if (ecdt && 263 !memcmp(ecdt->hdr.signature, ECDT_SIG, sizeof(ECDT_SIG) - 1)) 264 return (1); 265 if (acpisc->sc_ec) 266 return (0); 267 268 /* sanity */ 269 return (acpi_matchhids(aa, acpiec_hids, cf->cf_driver->cd_name)); 270 } 271 272 void 273 acpiec_attach(struct device *parent, struct device *self, void *aux) 274 { 275 struct acpiec_softc *sc = (struct acpiec_softc *)self; 276 struct acpi_attach_args *aa = aux; 277 struct aml_value res; 278 int64_t st; 279 280 sc->sc_acpi = (struct acpi_softc *)parent; 281 sc->sc_devnode = aa->aaa_node; 282 sc->sc_cantburst = 0; 283 284 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st)) 285 st = STA_PRESENT | STA_ENABLED | STA_DEV_OK; 286 if ((st & STA_PRESENT) == 0) { 287 printf(": not present\n"); 288 return; 289 } 290 291 printf("\n"); 292 if (acpiec_getcrs(sc, aa)) { 293 printf("%s: Failed to read resource settings\n", DEVNAME(sc)); 294 return; 295 } 296 297 sc->sc_acpi->sc_ec = sc; 298 299 if (acpiec_reg(sc)) { 300 printf("%s: Failed to register address space\n", DEVNAME(sc)); 301 return; 302 } 303 304 /* 305 * Some Chromebooks using the Google EC do not support burst mode and 306 * cause us to spin forever waiting for the acknowledgment. Don't use 307 * burst mode at all on these machines. 308 */ 309 if (hw_vendor != NULL && hw_prod != NULL && 310 strcmp(hw_vendor, "GOOGLE") == 0 && 311 strcmp(hw_prod, "Samus") == 0) 312 sc->sc_cantburst = 1; 313 314 acpiec_get_events(sc); 315 316 dnprintf(10, "%s: GPE: %d\n", DEVNAME(sc), sc->sc_gpe); 317 318 #ifndef SMALL_KERNEL 319 acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler, 320 sc, GPE_EDGE); 321 #endif 322 323 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_GLK", 0, NULL, &res)) 324 sc->sc_glk = 0; 325 else if (res.type != AML_OBJTYPE_INTEGER) 326 sc->sc_glk = 0; 327 else 328 sc->sc_glk = res.v_integer ? 1 : 0; 329 } 330 331 void 332 acpiec_get_events(struct acpiec_softc *sc) 333 { 334 int idx; 335 char name[16]; 336 337 memset(sc->sc_events, 0, sizeof(sc->sc_events)); 338 for (idx = 0; idx < ACPIEC_MAX_EVENTS; idx++) { 339 snprintf(name, sizeof(name), "_Q%02X", idx); 340 sc->sc_events[idx].event = aml_searchname(sc->sc_devnode, name); 341 if (sc->sc_events[idx].event != NULL) 342 dnprintf(10, "%s: Found event %s\n", DEVNAME(sc), name); 343 } 344 } 345 346 int 347 acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg) 348 { 349 struct acpiec_softc *sc = arg; 350 uint8_t mask, stat, en; 351 int s; 352 353 KASSERT(sc->sc_ecbusy == 0); 354 dnprintf(10, "ACPIEC: got gpe\n"); 355 356 do { 357 if (sc->sc_gotsci) 358 acpiec_sci_event(sc); 359 360 stat = acpiec_status(sc); 361 dnprintf(40, "%s: EC interrupt, stat: %b\n", 362 DEVNAME(sc), (int)stat, 363 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF"); 364 365 if (stat & EC_STAT_SCI_EVT) 366 sc->sc_gotsci = 1; 367 else 368 sc->sc_gotsci = 0; 369 } while (sc->sc_gotsci); 370 371 /* Unmask the GPE which was blocked at interrupt time */ 372 s = splbio(); 373 mask = (1L << (gpe & 7)); 374 en = acpi_read_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3); 375 acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, en | mask); 376 splx(s); 377 378 return (0); 379 } 380 381 int 382 acpiec_parse_resources(int crsidx, union acpi_resource *crs, void *arg) 383 { 384 struct acpiec_softc *sc = arg; 385 int type = AML_CRSTYPE(crs); 386 387 switch (crsidx) { 388 case 0: 389 if (type != SR_IOPORT) { 390 printf("%s: Unexpected resource #%d type %d\n", 391 DEVNAME(sc), crsidx, type); 392 break; 393 } 394 sc->sc_data_bt = sc->sc_acpi->sc_iot; 395 sc->sc_ec_data = crs->sr_ioport._max; 396 break; 397 case 1: 398 if (type != SR_IOPORT) { 399 printf("%s: Unexpected resource #%d type %d\n", 400 DEVNAME(sc), crsidx, type); 401 break; 402 } 403 sc->sc_cmd_bt = sc->sc_acpi->sc_iot; 404 sc->sc_ec_sc = crs->sr_ioport._max; 405 break; 406 case 2: 407 if (!sc->sc_acpi->sc_hw_reduced) { 408 printf("%s: Not running on HW-Reduced ACPI type %d\n", 409 DEVNAME(sc), type); 410 break; 411 } 412 /* XXX: handle SCI GPIO */ 413 break; 414 default: 415 printf("%s: invalid resource #%d type %d\n", 416 DEVNAME(sc), crsidx, type); 417 } 418 419 return 0; 420 } 421 422 int 423 acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa) 424 { 425 struct aml_value res; 426 int64_t gpe; 427 struct acpi_ecdt *ecdt = aa->aaa_table; 428 int rc; 429 430 /* Check if this is ECDT initialization */ 431 if (ecdt) { 432 /* Get GPE, Data and Control segments */ 433 sc->sc_gpe = ecdt->gpe_bit; 434 435 if (ecdt->ec_control.address_space_id == GAS_SYSTEM_IOSPACE) 436 sc->sc_cmd_bt = sc->sc_acpi->sc_iot; 437 else 438 sc->sc_cmd_bt = sc->sc_acpi->sc_memt; 439 sc->sc_ec_sc = ecdt->ec_control.address; 440 441 if (ecdt->ec_data.address_space_id == GAS_SYSTEM_IOSPACE) 442 sc->sc_data_bt = sc->sc_acpi->sc_iot; 443 else 444 sc->sc_data_bt = sc->sc_acpi->sc_memt; 445 sc->sc_ec_data = ecdt->ec_data.address; 446 447 /* Get devnode from header */ 448 sc->sc_devnode = aml_searchname(sc->sc_acpi->sc_root, 449 ecdt->ec_id); 450 451 goto ecdtdone; 452 } 453 454 rc = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 455 "_GPE", 0, NULL, &gpe); 456 if (rc) { 457 dnprintf(10, "%s: no _GPE\n", DEVNAME(sc)); 458 return (1); 459 } 460 461 sc->sc_gpe = gpe; 462 463 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) { 464 dnprintf(10, "%s: no _CRS\n", DEVNAME(sc)); 465 return (1); 466 } 467 468 /* Parse CRS to get control and data registers */ 469 470 if (res.type != AML_OBJTYPE_BUFFER) { 471 dnprintf(10, "%s: unknown _CRS type %d\n", 472 DEVNAME(sc), res.type); 473 aml_freevalue(&res); 474 return (1); 475 } 476 477 aml_parse_resource(&res, acpiec_parse_resources, sc); 478 aml_freevalue(&res); 479 if (sc->sc_ec_data == 0 || sc->sc_ec_sc == 0) { 480 printf("%s: failed to read from _CRS\n", DEVNAME(sc)); 481 return (1); 482 } 483 484 ecdtdone: 485 486 dnprintf(10, "%s: Data: 0x%lx, S/C: 0x%lx\n", 487 DEVNAME(sc), sc->sc_ec_data, sc->sc_ec_sc); 488 489 if (bus_space_map(sc->sc_cmd_bt, sc->sc_ec_sc, 1, 0, &sc->sc_cmd_bh)) { 490 dnprintf(10, "%s: failed to map S/C reg.\n", DEVNAME(sc)); 491 return (1); 492 } 493 494 rc = bus_space_map(sc->sc_data_bt, sc->sc_ec_data, 1, 0, 495 &sc->sc_data_bh); 496 if (rc) { 497 dnprintf(10, "%s: failed to map DATA reg.\n", DEVNAME(sc)); 498 bus_space_unmap(sc->sc_cmd_bt, sc->sc_cmd_bh, 1); 499 return (1); 500 } 501 502 return (0); 503 } 504 505 int 506 acpiec_reg(struct acpiec_softc *sc) 507 { 508 struct aml_value arg[2]; 509 struct aml_node *node; 510 511 memset(&arg, 0, sizeof(arg)); 512 arg[0].type = AML_OBJTYPE_INTEGER; 513 arg[0].v_integer = ACPI_OPREG_EC; 514 arg[1].type = AML_OBJTYPE_INTEGER; 515 arg[1].v_integer = 1; 516 517 node = aml_searchname(sc->sc_devnode, "_REG"); 518 if (node && aml_evalnode(sc->sc_acpi, node, 2, arg, NULL)) { 519 dnprintf(10, "%s: eval method _REG failed\n", DEVNAME(sc)); 520 printf("acpiec _REG failed, broken BIOS\n"); 521 } 522 523 return (0); 524 } 525