1 /* $OpenBSD: acpiec.c,v 1.28 2009/03/11 20:37:46 jordan 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 44 u_int8_t acpiec_read_1(struct acpiec_softc *, u_int8_t); 45 void acpiec_write_1(struct acpiec_softc *, u_int8_t, u_int8_t); 46 47 void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 48 void acpiec_write(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 49 50 int acpiec_getcrs(struct acpiec_softc *, 51 struct acpi_attach_args *); 52 int acpiec_getregister(const u_int8_t *, int, int *, bus_size_t *); 53 54 void acpiec_wait(struct acpiec_softc *, u_int8_t, u_int8_t); 55 void acpiec_sci_event(struct acpiec_softc *); 56 57 void acpiec_get_events(struct acpiec_softc *); 58 59 int acpiec_gpehandler(struct acpi_softc *, int, void *); 60 61 struct aml_node *aml_find_name(struct acpi_softc *, struct aml_node *, 62 const char *); 63 64 /* EC Status bits */ 65 #define EC_STAT_SMI_EVT 0x40 /* SMI event pending */ 66 #define EC_STAT_SCI_EVT 0x20 /* SCI event pending */ 67 #define EC_STAT_BURST 0x10 /* Controller in burst mode */ 68 #define EC_STAT_CMD 0x08 /* data is command */ 69 #define EC_STAT_IBF 0x02 /* input buffer full */ 70 #define EC_STAT_OBF 0x01 /* output buffer full */ 71 72 /* EC Commands */ 73 #define EC_CMD_RD 0x80 /* Read */ 74 #define EC_CMD_WR 0x81 /* Write */ 75 #define EC_CMD_BE 0x82 /* Burst Enable */ 76 #define EC_CMD_BD 0x83 /* Burst Disable */ 77 #define EC_CMD_QR 0x84 /* Query */ 78 79 #define REG_TYPE_EC 3 80 81 int acpiec_reg(struct acpiec_softc *); 82 83 struct cfattach acpiec_ca = { 84 sizeof(struct acpiec_softc), acpiec_match, acpiec_attach 85 }; 86 87 struct cfdriver acpiec_cd = { 88 NULL, "acpiec", DV_DULL 89 }; 90 91 const char *acpiec_hids[] = { ACPI_DEV_ECD, 0 }; 92 93 void 94 acpiec_wait(struct acpiec_softc *sc, u_int8_t mask, u_int8_t val) 95 { 96 u_int8_t stat; 97 98 dnprintf(40, "%s: EC wait_ns for: %b == %02x\n", 99 DEVNAME(sc), (int)mask, 100 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF", (int)val); 101 102 while (((stat = acpiec_status(sc)) & mask) != val) { 103 if (stat & EC_STAT_SCI_EVT) 104 sc->sc_gotsci = 1; 105 if (cold) 106 delay(1); 107 else 108 tsleep(sc, PWAIT, "ecwait", 1); 109 } 110 111 dnprintf(40, "%s: EC wait_ns, stat: %b\n", DEVNAME(sc), (int)stat, 112 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF"); 113 } 114 115 u_int8_t 116 acpiec_status(struct acpiec_softc *sc) 117 { 118 return (bus_space_read_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0)); 119 } 120 121 void 122 acpiec_write_data(struct acpiec_softc *sc, u_int8_t val) 123 { 124 acpiec_wait(sc, EC_STAT_IBF, 0); 125 dnprintf(40, "acpiec: write_data -- %d\n", (int)val); 126 bus_space_write_1(sc->sc_data_bt, sc->sc_data_bh, 0, val); 127 } 128 129 void 130 acpiec_write_cmd(struct acpiec_softc *sc, u_int8_t val) 131 { 132 acpiec_wait(sc, EC_STAT_IBF, 0); 133 dnprintf(40, "acpiec: write_cmd -- %d\n", (int)val); 134 bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, val); 135 } 136 137 u_int8_t 138 acpiec_read_data(struct acpiec_softc *sc) 139 { 140 u_int8_t val; 141 142 acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF); 143 dnprintf(40, "acpiec: read_data\n", (int)val); 144 val = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0); 145 146 return (val); 147 } 148 149 void 150 acpiec_sci_event(struct acpiec_softc *sc) 151 { 152 u_int8_t evt; 153 154 sc->sc_gotsci = 0; 155 156 acpiec_wait(sc, EC_STAT_IBF, 0); 157 bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, EC_CMD_QR); 158 159 acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF); 160 evt = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0); 161 162 if (evt) { 163 dnprintf(10, "%s: sci_event: 0x%02x\n", DEVNAME(sc), (int)evt); 164 aml_evalnode(sc->sc_acpi, sc->sc_events[evt].event, 0, NULL, 165 NULL); 166 } 167 } 168 169 u_int8_t 170 acpiec_read_1(struct acpiec_softc *sc, u_int8_t addr) 171 { 172 u_int8_t val; 173 174 if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT) 175 sc->sc_gotsci = 1; 176 177 acpiec_write_cmd(sc, EC_CMD_RD); 178 acpiec_write_data(sc, addr); 179 180 val = acpiec_read_data(sc); 181 182 return (val); 183 } 184 185 void 186 acpiec_write_1(struct acpiec_softc *sc, u_int8_t addr, u_int8_t data) 187 { 188 if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT) 189 sc->sc_gotsci = 1; 190 191 acpiec_write_cmd(sc, EC_CMD_WR); 192 acpiec_write_data(sc, addr); 193 acpiec_write_data(sc, data); 194 } 195 196 void 197 acpiec_burst_enable(struct acpiec_softc *sc) 198 { 199 acpiec_write_cmd(sc, EC_CMD_BE); 200 acpiec_read_data(sc); 201 } 202 203 void 204 acpiec_read(struct acpiec_softc *sc, u_int8_t addr, int len, u_int8_t *buffer) 205 { 206 int reg; 207 208 /* 209 * this works because everything runs in the acpi thread context. 210 * at some point add a lock to deal with concurrency so that a 211 * transaction does not get interrupted. 212 */ 213 acpiec_burst_enable(sc); 214 dnprintf(20, "%s: read %d, %d\n", DEVNAME(sc), (int)addr, len); 215 216 for (reg = 0; reg < len; reg++) 217 buffer[reg] = acpiec_read_1(sc, addr + reg); 218 } 219 220 void 221 acpiec_write(struct acpiec_softc *sc, u_int8_t addr, int len, u_int8_t *buffer) 222 { 223 int reg; 224 225 /* 226 * this works because everything runs in the acpi thread context. 227 * at some point add a lock to deal with concurrency so that a 228 * transaction does not get interrupted. 229 */ 230 acpiec_burst_enable(sc); 231 dnprintf(20, "%s: write %d, %d\n", DEVNAME(sc), (int)addr, len); 232 for (reg = 0; reg < len; reg++) 233 acpiec_write_1(sc, addr + reg, buffer[reg]); 234 } 235 236 int 237 acpiec_match(struct device *parent, void *match, void *aux) 238 { 239 struct acpi_attach_args *aa = aux; 240 struct cfdata *cf = match; 241 242 /* sanity */ 243 return (acpi_matchhids(aa, acpiec_hids, cf->cf_driver->cd_name)); 244 } 245 246 void 247 acpiec_attach(struct device *parent, struct device *self, void *aux) 248 { 249 struct acpiec_softc *sc = (struct acpiec_softc *)self; 250 struct acpi_attach_args *aa = aux; 251 252 sc->sc_acpi = (struct acpi_softc *)parent; 253 sc->sc_devnode = aa->aaa_node; 254 255 if (sc->sc_acpi->sc_ec != NULL) { 256 printf(": Only single EC is supported\n"); 257 return; 258 } 259 sc->sc_acpi->sc_ec = sc; 260 261 if (acpiec_getcrs(sc, aa)) { 262 printf(": Failed to read resource settings\n"); 263 return; 264 } 265 266 if (acpiec_reg(sc)) { 267 printf(": Failed to register address space\n"); 268 return; 269 } 270 271 acpiec_get_events(sc); 272 273 dnprintf(10, "%s: GPE: %d\n", DEVNAME(sc), sc->sc_gpe); 274 275 #ifndef SMALL_KERNEL 276 acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler, 277 sc, "acpiec"); 278 #endif 279 280 printf("\n"); 281 } 282 283 void 284 acpiec_get_events(struct acpiec_softc *sc) 285 { 286 int idx; 287 char name[16]; 288 289 memset(sc->sc_events, 0, sizeof(sc->sc_events)); 290 for (idx = 0; idx < ACPIEC_MAX_EVENTS; idx++) { 291 snprintf(name, sizeof(name), "_Q%02X", idx); 292 sc->sc_events[idx].event = aml_searchname(sc->sc_devnode, name); 293 if (sc->sc_events[idx].event != NULL) 294 dnprintf(10, "%s: Found event %s\n", DEVNAME(sc), name); 295 } 296 } 297 298 int 299 acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg) 300 { 301 struct acpiec_softc *sc = arg; 302 u_int8_t mask, stat; 303 304 dnprintf(10, "ACPIEC: got gpe\n"); 305 306 /* Reset GPE event */ 307 mask = (1L << (gpe & 7)); 308 acpi_write_pmreg(acpi_sc, ACPIREG_GPE_STS, gpe>>3, mask); 309 acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, mask); 310 311 do { 312 if (sc->sc_gotsci) 313 acpiec_sci_event(sc); 314 315 stat = acpiec_status(sc); 316 dnprintf(40, "%s: EC interrupt, stat: %b\n", 317 DEVNAME(sc), (int)stat, 318 "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF"); 319 320 if (stat & EC_STAT_SCI_EVT) 321 sc->sc_gotsci = 1; 322 } while (sc->sc_gotsci); 323 324 return (0); 325 } 326 327 /* parse the resource buffer to get a 'register' value */ 328 int 329 acpiec_getregister(const u_int8_t *buf, int size, int *type, bus_size_t *addr) 330 { 331 int len, hlen; 332 333 #define RES_TYPE_MASK 0x80 334 #define RES_LENGTH_MASK 0x07 335 #define RES_TYPE_IOPORT 0x47 336 #define RES_TYPE_ENDTAG 0x79 337 338 if (size <= 0) 339 return (0); 340 341 if (*buf & RES_TYPE_MASK) { 342 /* large resource */ 343 if (size < 3) 344 return (1); 345 len = (int)buf[1] + 256 * (int)buf[2]; 346 hlen = 3; 347 } else { 348 /* small resource */ 349 len = buf[0] & RES_LENGTH_MASK; 350 hlen = 1; 351 } 352 353 /* XXX todo: decode other types */ 354 if (*buf != RES_TYPE_IOPORT) 355 return (0); 356 357 if (size < hlen + len) 358 return (0); 359 360 /* XXX validate? */ 361 *type = GAS_SYSTEM_IOSPACE; 362 *addr = (int)buf[2] + 256 * (int)buf[3]; 363 364 return (hlen + len); 365 } 366 367 int 368 acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa) 369 { 370 struct aml_value res; 371 bus_size_t ec_sc, ec_data; 372 int type1, type2; 373 char *buf; 374 int size, ret; 375 int64_t gpe; 376 377 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_GPE", 0, NULL, &gpe)) { 378 dnprintf(10, "%s: no _GPE\n", DEVNAME(sc)); 379 return (1); 380 } 381 382 sc->sc_gpe = gpe; 383 384 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) { 385 dnprintf(10, "%s: no _CRS\n", DEVNAME(sc)); 386 return (1); 387 } 388 389 /* Parse CRS to get control and data registers */ 390 391 if (res.type != AML_OBJTYPE_BUFFER) { 392 dnprintf(10, "%s: unknown _CRS type %d\n", 393 DEVNAME(sc), res.type); 394 aml_freevalue(&res); 395 return (1); 396 } 397 398 size = res.length; 399 buf = res.v_buffer; 400 401 ret = acpiec_getregister(buf, size, &type1, &ec_data); 402 if (ret <= 0) { 403 dnprintf(10, "%s: failed to read DATA from _CRS\n", 404 DEVNAME(sc)); 405 aml_freevalue(&res); 406 return (1); 407 } 408 409 buf += ret; 410 size -= ret; 411 412 ret = acpiec_getregister(buf, size, &type2, &ec_sc); 413 if (ret <= 0) { 414 dnprintf(10, "%s: failed to read S/C from _CRS\n", 415 DEVNAME(sc)); 416 aml_freevalue(&res); 417 return (1); 418 } 419 420 buf += ret; 421 size -= ret; 422 423 if (size != 2 || *buf != RES_TYPE_ENDTAG) { 424 dnprintf(10, "%s: no _CRS end tag\n", DEVNAME(sc)); 425 aml_freevalue(&res); 426 return (1); 427 } 428 aml_freevalue(&res); 429 430 /* XXX: todo - validate _CRS checksum? */ 431 432 dnprintf(10, "%s: Data: 0x%x, S/C: 0x%x\n", 433 DEVNAME(sc), ec_data, ec_sc); 434 435 if (type1 == GAS_SYSTEM_IOSPACE) 436 sc->sc_cmd_bt = aa->aaa_iot; 437 else 438 sc->sc_cmd_bt = aa->aaa_memt; 439 440 if (bus_space_map(sc->sc_cmd_bt, ec_sc, 1, 0, &sc->sc_cmd_bh)) { 441 dnprintf(10, "%s: failed to map S/C reg.\n", DEVNAME(sc)); 442 return (1); 443 } 444 445 if (type2 == GAS_SYSTEM_IOSPACE) 446 sc->sc_data_bt = aa->aaa_iot; 447 else 448 sc->sc_data_bt = aa->aaa_memt; 449 450 if (bus_space_map(sc->sc_data_bt, ec_data, 1, 0, &sc->sc_data_bh)) { 451 dnprintf(10, "%s: failed to map DATA reg.\n", DEVNAME(sc)); 452 bus_space_unmap(sc->sc_cmd_bt, sc->sc_cmd_bh, 1); 453 return (1); 454 } 455 456 return (0); 457 } 458 459 int 460 acpiec_reg(struct acpiec_softc *sc) 461 { 462 struct aml_value arg[2]; 463 464 memset(&arg, 0, sizeof(arg)); 465 arg[0].type = AML_OBJTYPE_INTEGER; 466 arg[0].v_integer = REG_TYPE_EC; 467 arg[1].type = AML_OBJTYPE_INTEGER; 468 arg[1].v_integer = 1; 469 470 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_REG", 2, 471 arg, NULL) != 0) { 472 dnprintf(10, "%s: eval method _REG failed\n", DEVNAME(sc)); 473 return (1); 474 } 475 476 return (0); 477 } 478