Annotation of sys/dev/acpi/acpiec.c, Revision 1.1.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