xref: /openbsd-src/sys/dev/acpi/acpiec.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /* $OpenBSD: acpiec.c,v 1.25 2008/06/13 09:13:56 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 #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;
283 
284 	if (sc->sc_acpi->sc_ec != NULL) {
285 		printf(": Only single EC is supported\n");
286 		return;
287 	}
288 	sc->sc_acpi->sc_ec = sc;
289 
290 	if (acpiec_getcrs(sc, aa)) {
291 		printf(": Failed to read resource settings\n");
292 		return;
293 	}
294 
295 	if (acpiec_reg(sc)) {
296 		printf(": Failed to register address space\n");
297 		return;
298 	}
299 
300 	acpiec_get_events(sc);
301 
302 	dnprintf(10, "%s: GPE: %d\n", DEVNAME(sc), sc->sc_gpe);
303 
304 #ifndef SMALL_KERNEL
305 	acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler,
306 	    sc, "acpiec");
307 #endif
308 
309 	printf("\n");
310 }
311 
312 void
313 acpiec_get_events(struct acpiec_softc *sc)
314 {
315 	int			idx;
316 	char			name[16];
317 
318 	memset(sc->sc_events, 0, sizeof(sc->sc_events));
319 	for (idx = 0; idx < ACPIEC_MAX_EVENTS; idx++) {
320 		snprintf(name, sizeof(name), "_Q%02X", idx);
321 		sc->sc_events[idx].event = aml_searchname(sc->sc_devnode, name);
322 		if (sc->sc_events[idx].event != NULL)
323 			dnprintf(10, "%s: Found event %s\n", DEVNAME(sc), name);
324 	}
325 }
326 
327 int
328 acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg)
329 {
330 	struct acpiec_softc	*sc = arg;
331 	u_int8_t		mask, stat;
332 
333 	dnprintf(10, "ACPIEC: got gpe\n");
334 
335 	/* Reset GPE event */
336 	mask = (1L << (gpe & 7));
337 	acpi_write_pmreg(acpi_sc, ACPIREG_GPE_STS, gpe>>3, mask);
338 	acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN,  gpe>>3, mask);
339 
340 	do {
341 		if (sc->sc_gotsci)
342 			acpiec_sci_event(sc);
343 
344 		stat = acpiec_status(sc);
345 		dnprintf(40, "%s: EC interrupt, stat: %b\n",
346 		    DEVNAME(sc), (int)stat,
347 		    "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF");
348 
349 		if (stat & EC_STAT_SCI_EVT)
350 			sc->sc_gotsci = 1;
351 	} while (sc->sc_gotsci);
352 
353 	return (0);
354 }
355 
356 /* parse the resource buffer to get a 'register' value */
357 int
358 acpiec_getregister(const u_int8_t *buf, int size, int *type, bus_size_t *addr)
359 {
360 	int			len, hlen;
361 
362 #define RES_TYPE_MASK 0x80
363 #define RES_LENGTH_MASK 0x07
364 #define RES_TYPE_IOPORT	0x47
365 #define RES_TYPE_ENDTAG	0x79
366 
367 	if (size <= 0)
368 		return (0);
369 
370 	if (*buf & RES_TYPE_MASK) {
371 		/* large resource */
372 		if (size < 3)
373 			return (1);
374 		len = (int)buf[1] + 256 * (int)buf[2];
375 		hlen = 3;
376 	} else {
377 		/* small resource */
378 		len = buf[0] & RES_LENGTH_MASK;
379 		hlen = 1;
380 	}
381 
382 	/* XXX todo: decode other types */
383 	if (*buf != RES_TYPE_IOPORT)
384 		return (0);
385 
386 	if (size < hlen + len)
387 		return (0);
388 
389 	/* XXX validate? */
390 	*type = GAS_SYSTEM_IOSPACE;
391 	*addr = (int)buf[2] + 256 * (int)buf[3];
392 
393 	return (hlen + len);
394 }
395 
396 int
397 acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa)
398 {
399 	struct aml_value	res;
400 	bus_size_t		ec_sc, ec_data;
401 	int			type1, type2;
402 	char			*buf;
403 	int			size, ret;
404 
405 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_GPE", 0, NULL, &res)) {
406 		dnprintf(10, "%s: no _GPE\n", DEVNAME(sc));
407 		return (1);
408 	}
409 
410 	sc->sc_gpe = aml_val2int(&res);
411 	aml_freevalue(&res);
412 
413 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) {
414 		dnprintf(10, "%s: no _CRS\n", DEVNAME(sc));
415 		return (1);
416 	}
417 
418 	/* Parse CRS to get control and data registers */
419 
420 	if (res.type != AML_OBJTYPE_BUFFER) {
421 		dnprintf(10, "%s: unknown _CRS type %d\n",
422 		    DEVNAME(sc), res.type);
423 		aml_freevalue(&res);
424 		return (1);
425 	}
426 
427 	size = res.length;
428 	buf = res.v_buffer;
429 
430 	ret = acpiec_getregister(buf, size, &type1, &ec_data);
431 	if (ret <= 0) {
432 		dnprintf(10, "%s: failed to read DATA from _CRS\n",
433 		    DEVNAME(sc));
434 		aml_freevalue(&res);
435 		return (1);
436 	}
437 
438 	buf += ret;
439 	size -= ret;
440 
441 	ret = acpiec_getregister(buf, size, &type2,  &ec_sc);
442 	if (ret <= 0) {
443 		dnprintf(10, "%s: failed to read S/C from _CRS\n",
444 		    DEVNAME(sc));
445 		aml_freevalue(&res);
446 		return (1);
447 	}
448 
449 	buf += ret;
450 	size -= ret;
451 
452 	if (size != 2 || *buf != RES_TYPE_ENDTAG) {
453 		dnprintf(10, "%s: no _CRS end tag\n", DEVNAME(sc));
454 		aml_freevalue(&res);
455 		return (1);
456 	}
457 	aml_freevalue(&res);
458 
459 	/* XXX: todo - validate _CRS checksum? */
460 
461 	dnprintf(10, "%s: Data: 0x%x, S/C: 0x%x\n",
462 	    DEVNAME(sc), ec_data, ec_sc);
463 
464 	if (type1 == GAS_SYSTEM_IOSPACE)
465 		sc->sc_cmd_bt = aa->aaa_iot;
466 	else
467 		sc->sc_cmd_bt = aa->aaa_memt;
468 
469 	if (bus_space_map(sc->sc_cmd_bt, ec_sc, 1, 0, &sc->sc_cmd_bh)) {
470 		dnprintf(10, "%s: failed to map S/C reg.\n", DEVNAME(sc));
471 		return (1);
472 	}
473 
474 	if (type2 == GAS_SYSTEM_IOSPACE)
475 		sc->sc_data_bt = aa->aaa_iot;
476 	else
477 		sc->sc_data_bt = aa->aaa_memt;
478 
479 	if (bus_space_map(sc->sc_data_bt, ec_data, 1, 0, &sc->sc_data_bh)) {
480 		dnprintf(10, "%s: failed to map DATA reg.\n", DEVNAME(sc));
481 		bus_space_unmap(sc->sc_cmd_bt, sc->sc_cmd_bh, 1);
482 		return (1);
483 	}
484 
485 	return (0);
486 }
487 
488 int
489 acpiec_reg(struct acpiec_softc *sc)
490 {
491 	struct aml_value arg[2];
492 
493 	memset(&arg, 0, sizeof(arg));
494 	arg[0].type = AML_OBJTYPE_INTEGER;
495 	arg[0].v_integer = REG_TYPE_EC;
496 	arg[1].type = AML_OBJTYPE_INTEGER;
497 	arg[1].v_integer = 1;
498 
499 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_REG", 2,
500 	    arg, NULL) != 0) {
501 		dnprintf(10, "%s: eval method _REG failed\n", DEVNAME(sc));
502 		return (1);
503 	}
504 
505 	return (0);
506 }
507