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