xref: /openbsd-src/sys/dev/acpi/acpiec.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
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