1/* $OpenBSD: acpiec.c,v 1.65 2022/08/10 16:58:16 patrick 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/signalvar.h>
20#include <sys/systm.h>
21#include <sys/device.h>
22
23#include <machine/bus.h>
24
25#include <dev/acpi/acpireg.h>
26#include <dev/acpi/acpivar.h>
27#include <dev/acpi/acpidev.h>
28#include <dev/acpi/amltypes.h>
29#include <dev/acpi/dsdt.h>
30
31#include <sys/sensors.h>
32
33int		acpiec_match(struct device *, void *, void *);
34void		acpiec_attach(struct device *, struct device *, void *);
35
36uint8_t		acpiec_status(struct acpiec_softc *);
37uint8_t		acpiec_read_data(struct acpiec_softc *);
38void		acpiec_write_cmd(struct acpiec_softc *, uint8_t);
39void		acpiec_write_data(struct acpiec_softc *, uint8_t);
40void		acpiec_burst_enable(struct acpiec_softc *sc);
41void		acpiec_burst_disable(struct acpiec_softc *sc);
42
43uint8_t		acpiec_read_1(struct acpiec_softc *, uint8_t);
44void		acpiec_write_1(struct acpiec_softc *, uint8_t, uint8_t);
45
46void		acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
47void		acpiec_write(struct acpiec_softc *, uint8_t, int, uint8_t *);
48
49int		acpiec_getcrs(struct acpiec_softc *,
50		    struct acpi_attach_args *);
51int		acpiec_parse_resources(int, union acpi_resource *, void *);
52
53void		acpiec_wait(struct acpiec_softc *, uint8_t, uint8_t);
54void		acpiec_sci_event(struct acpiec_softc *);
55
56void		acpiec_get_events(struct acpiec_softc *);
57
58int		acpiec_gpehandler(struct acpi_softc *, int, void *);
59
60/* EC Status bits */
61#define		EC_STAT_SMI_EVT	0x40	/* SMI event pending */
62#define		EC_STAT_SCI_EVT	0x20	/* SCI event pending */
63#define		EC_STAT_BURST	0x10	/* Controller in burst mode */
64#define		EC_STAT_CMD	0x08	/* data is command */
65#define		EC_STAT_IBF	0x02	/* input buffer full */
66#define		EC_STAT_OBF	0x01	/* output buffer full */
67
68/* EC Commands */
69#define		EC_CMD_RD	0x80	/* Read */
70#define		EC_CMD_WR	0x81	/* Write */
71#define		EC_CMD_BE	0x82	/* Burst Enable */
72#define		EC_CMD_BD	0x83	/* Burst Disable */
73#define		EC_CMD_QR	0x84	/* Query */
74
75int	acpiec_reg(struct acpiec_softc *);
76
77const struct cfattach acpiec_ca = {
78	sizeof(struct acpiec_softc), acpiec_match, acpiec_attach
79};
80
81struct cfdriver acpiec_cd = {
82	NULL, "acpiec", DV_DULL
83};
84
85const char *acpiec_hids[] = {
86	ACPI_DEV_ECD,
87	NULL
88};
89
90void
91acpiec_wait(struct acpiec_softc *sc, uint8_t mask, uint8_t val)
92{
93	static int acpiecnowait;
94	uint8_t		stat;
95
96	dnprintf(40, "%s: EC wait_ns for: %b == %02x\n",
97	    DEVNAME(sc), (int)mask,
98	    "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF", (int)val);
99
100	while (((stat = acpiec_status(sc)) & mask) != val) {
101		if (stat & EC_STAT_SCI_EVT)
102			sc->sc_gotsci = 1;
103		if (cold || (stat & EC_STAT_BURST))
104			delay(1);
105		else
106			tsleep(&acpiecnowait, PWAIT, "acpiec", 1);
107	}
108
109	dnprintf(40, "%s: EC wait_ns, stat: %b\n", DEVNAME(sc), (int)stat,
110	    "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF");
111}
112
113uint8_t
114acpiec_status(struct acpiec_softc *sc)
115{
116	return (bus_space_read_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0));
117}
118
119void
120acpiec_write_data(struct acpiec_softc *sc, uint8_t val)
121{
122	acpiec_wait(sc, EC_STAT_IBF, 0);
123	dnprintf(40, "acpiec: write_data -- %d\n", (int)val);
124	bus_space_write_1(sc->sc_data_bt, sc->sc_data_bh, 0, val);
125}
126
127void
128acpiec_write_cmd(struct acpiec_softc *sc, uint8_t val)
129{
130	acpiec_wait(sc, EC_STAT_IBF, 0);
131	dnprintf(40, "acpiec: write_cmd -- %d\n", (int)val);
132	bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, val);
133}
134
135uint8_t
136acpiec_read_data(struct acpiec_softc *sc)
137{
138	uint8_t		val;
139
140	acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF);
141	val = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0);
142
143	dnprintf(40, "acpiec: read_data %d\n", (int)val);
144
145	return (val);
146}
147
148void
149acpiec_sci_event(struct acpiec_softc *sc)
150{
151	uint8_t		evt;
152
153	sc->sc_gotsci = 0;
154
155	acpiec_wait(sc, EC_STAT_IBF, 0);
156	bus_space_write_1(sc->sc_cmd_bt, sc->sc_cmd_bh, 0, EC_CMD_QR);
157
158	acpiec_wait(sc, EC_STAT_OBF, EC_STAT_OBF);
159	evt = bus_space_read_1(sc->sc_data_bt, sc->sc_data_bh, 0);
160
161	if (evt) {
162		dnprintf(10, "%s: sci_event: 0x%02x\n", DEVNAME(sc), (int)evt);
163		aml_evalnode(sc->sc_acpi, sc->sc_events[evt].event, 0, NULL,
164		    NULL);
165	}
166}
167
168uint8_t
169acpiec_read_1(struct acpiec_softc *sc, uint8_t addr)
170{
171	uint8_t		val;
172
173	if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT)
174		sc->sc_gotsci = 1;
175
176	acpiec_write_cmd(sc, EC_CMD_RD);
177	acpiec_write_data(sc, addr);
178
179	val = acpiec_read_data(sc);
180
181	return (val);
182}
183
184void
185acpiec_write_1(struct acpiec_softc *sc, uint8_t addr, uint8_t data)
186{
187	if ((acpiec_status(sc) & EC_STAT_SCI_EVT) == EC_STAT_SCI_EVT)
188		sc->sc_gotsci = 1;
189
190	acpiec_write_cmd(sc, EC_CMD_WR);
191	acpiec_write_data(sc, addr);
192	acpiec_write_data(sc, data);
193}
194
195void
196acpiec_burst_enable(struct acpiec_softc *sc)
197{
198	if (sc->sc_cantburst)
199		return;
200
201	acpiec_write_cmd(sc, EC_CMD_BE);
202	acpiec_read_data(sc);
203}
204
205void
206acpiec_burst_disable(struct acpiec_softc *sc)
207{
208	if (sc->sc_cantburst)
209		return;
210
211	if ((acpiec_status(sc) & EC_STAT_BURST) == EC_STAT_BURST)
212		acpiec_write_cmd(sc, EC_CMD_BD);
213}
214
215void
216acpiec_read(struct acpiec_softc *sc, uint8_t addr, int len, uint8_t *buffer)
217{
218	int			reg;
219
220	/*
221	 * this works because everything runs in the acpi thread context.
222	 * at some point add a lock to deal with concurrency so that a
223	 * transaction does not get interrupted.
224	 */
225	dnprintf(20, "%s: read %d, %d\n", DEVNAME(sc), (int)addr, len);
226	sc->sc_ecbusy = 1;
227	acpiec_burst_enable(sc);
228	for (reg = 0; reg < len; reg++)
229		buffer[reg] = acpiec_read_1(sc, addr + reg);
230	acpiec_burst_disable(sc);
231	sc->sc_ecbusy = 0;
232}
233
234void
235acpiec_write(struct acpiec_softc *sc, uint8_t addr, int len, uint8_t *buffer)
236{
237	int			reg;
238
239	/*
240	 * this works because everything runs in the acpi thread context.
241	 * at some point add a lock to deal with concurrency so that a
242	 * transaction does not get interrupted.
243	 */
244	dnprintf(20, "%s: write %d, %d\n", DEVNAME(sc), (int)addr, len);
245	sc->sc_ecbusy = 1;
246	acpiec_burst_enable(sc);
247	for (reg = 0; reg < len; reg++)
248		acpiec_write_1(sc, addr + reg, buffer[reg]);
249	acpiec_burst_disable(sc);
250	sc->sc_ecbusy = 0;
251}
252
253int
254acpiec_match(struct device *parent, void *match, void *aux)
255{
256	struct acpi_attach_args	*aa = aux;
257	struct cfdata		*cf = match;
258	struct acpi_ecdt	*ecdt = aa->aaa_table;
259	struct acpi_softc	*acpisc = (struct acpi_softc *)parent;
260
261	/* Check for early ECDT table attach */
262	if (ecdt &&
263	    !memcmp(ecdt->hdr.signature, ECDT_SIG, sizeof(ECDT_SIG) - 1))
264		return (1);
265	if (acpisc->sc_ec)
266		return (0);
267
268	/* sanity */
269	return (acpi_matchhids(aa, acpiec_hids, cf->cf_driver->cd_name));
270}
271
272void
273acpiec_attach(struct device *parent, struct device *self, void *aux)
274{
275	struct acpiec_softc	*sc = (struct acpiec_softc *)self;
276	struct acpi_attach_args *aa = aux;
277	struct aml_value res;
278	int64_t st;
279
280	sc->sc_acpi = (struct acpi_softc *)parent;
281	sc->sc_devnode = aa->aaa_node;
282	sc->sc_cantburst = 0;
283
284	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
285		st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
286	if ((st & STA_PRESENT) == 0) {
287		printf(": not present\n");
288		return;
289	}
290
291	printf("\n");
292	if (acpiec_getcrs(sc, aa)) {
293		printf("%s: Failed to read resource settings\n", DEVNAME(sc));
294		return;
295	}
296
297	sc->sc_acpi->sc_ec = sc;
298
299	if (acpiec_reg(sc)) {
300		printf("%s: Failed to register address space\n", DEVNAME(sc));
301		return;
302	}
303
304	/*
305	 * Some Chromebooks using the Google EC do not support burst mode and
306	 * cause us to spin forever waiting for the acknowledgment.  Don't use
307	 * burst mode at all on these machines.
308	 */
309	if (hw_vendor != NULL && hw_prod != NULL &&
310	    strcmp(hw_vendor, "GOOGLE") == 0 &&
311	    strcmp(hw_prod, "Samus") == 0)
312		sc->sc_cantburst = 1;
313
314	acpiec_get_events(sc);
315
316	dnprintf(10, "%s: GPE: %d\n", DEVNAME(sc), sc->sc_gpe);
317
318#ifndef SMALL_KERNEL
319	acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler,
320	    sc, GPE_EDGE);
321#endif
322
323	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_GLK", 0, NULL, &res))
324		sc->sc_glk = 0;
325	else if (res.type != AML_OBJTYPE_INTEGER)
326		sc->sc_glk = 0;
327	else
328		sc->sc_glk = res.v_integer ? 1 : 0;
329}
330
331void
332acpiec_get_events(struct acpiec_softc *sc)
333{
334	int			idx;
335	char			name[16];
336
337	memset(sc->sc_events, 0, sizeof(sc->sc_events));
338	for (idx = 0; idx < ACPIEC_MAX_EVENTS; idx++) {
339		snprintf(name, sizeof(name), "_Q%02X", idx);
340		sc->sc_events[idx].event = aml_searchname(sc->sc_devnode, name);
341		if (sc->sc_events[idx].event != NULL)
342			dnprintf(10, "%s: Found event %s\n", DEVNAME(sc), name);
343	}
344}
345
346int
347acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg)
348{
349	struct acpiec_softc	*sc = arg;
350	uint8_t			mask, stat, en;
351	int			s;
352
353	KASSERT(sc->sc_ecbusy == 0);
354	dnprintf(10, "ACPIEC: got gpe\n");
355
356	do {
357		if (sc->sc_gotsci)
358			acpiec_sci_event(sc);
359
360		stat = acpiec_status(sc);
361		dnprintf(40, "%s: EC interrupt, stat: %b\n",
362		    DEVNAME(sc), (int)stat,
363		    "\20\x8IGN\x7SMI\x6SCI\05BURST\04CMD\03IGN\02IBF\01OBF");
364
365		if (stat & EC_STAT_SCI_EVT)
366			sc->sc_gotsci = 1;
367		else
368			sc->sc_gotsci = 0;
369	} while (sc->sc_gotsci);
370
371	/* Unmask the GPE which was blocked at interrupt time */
372	s = splbio();
373	mask = (1L << (gpe & 7));
374	en = acpi_read_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3);
375	acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, en | mask);
376	splx(s);
377
378	return (0);
379}
380
381int
382acpiec_parse_resources(int crsidx, union acpi_resource *crs, void *arg)
383{
384	struct acpiec_softc *sc = arg;
385	int type = AML_CRSTYPE(crs);
386
387	switch (crsidx) {
388	case 0:
389		if (type != SR_IOPORT) {
390			printf("%s: Unexpected resource #%d type %d\n",
391			    DEVNAME(sc), crsidx, type);
392			break;
393		}
394		sc->sc_data_bt = sc->sc_acpi->sc_iot;
395		sc->sc_ec_data = crs->sr_ioport._max;
396		break;
397	case 1:
398		if (type != SR_IOPORT) {
399			printf("%s: Unexpected resource #%d type %d\n",
400			    DEVNAME(sc), crsidx, type);
401			break;
402		}
403		sc->sc_cmd_bt = sc->sc_acpi->sc_iot;
404		sc->sc_ec_sc = crs->sr_ioport._max;
405		break;
406	case 2:
407		if (!sc->sc_acpi->sc_hw_reduced) {
408			printf("%s: Not running on HW-Reduced ACPI type %d\n",
409			    DEVNAME(sc), type);
410			break;
411		}
412		/* XXX: handle SCI GPIO  */
413		break;
414	default:
415		printf("%s: invalid resource #%d type %d\n",
416		    DEVNAME(sc), crsidx, type);
417	}
418
419	return 0;
420}
421
422int
423acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa)
424{
425	struct aml_value	res;
426	int64_t			gpe;
427	struct acpi_ecdt	*ecdt = aa->aaa_table;
428	int			rc;
429
430	/* Check if this is ECDT initialization */
431	if (ecdt) {
432		/* Get GPE, Data and Control segments */
433		sc->sc_gpe = ecdt->gpe_bit;
434
435		if (ecdt->ec_control.address_space_id == GAS_SYSTEM_IOSPACE)
436			sc->sc_cmd_bt = sc->sc_acpi->sc_iot;
437		else
438			sc->sc_cmd_bt = sc->sc_acpi->sc_memt;
439		sc->sc_ec_sc = ecdt->ec_control.address;
440
441		if (ecdt->ec_data.address_space_id == GAS_SYSTEM_IOSPACE)
442			sc->sc_data_bt = sc->sc_acpi->sc_iot;
443		else
444			sc->sc_data_bt = sc->sc_acpi->sc_memt;
445		sc->sc_ec_data = ecdt->ec_data.address;
446
447		/* Get devnode from header */
448		sc->sc_devnode = aml_searchname(sc->sc_acpi->sc_root,
449		    ecdt->ec_id);
450
451		goto ecdtdone;
452	}
453
454	rc = aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
455	    "_GPE", 0, NULL, &gpe);
456	if (rc) {
457		dnprintf(10, "%s: no _GPE\n", DEVNAME(sc));
458		return (1);
459	}
460
461	sc->sc_gpe = gpe;
462
463	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) {
464		dnprintf(10, "%s: no _CRS\n", DEVNAME(sc));
465		return (1);
466	}
467
468	/* Parse CRS to get control and data registers */
469
470	if (res.type != AML_OBJTYPE_BUFFER) {
471		dnprintf(10, "%s: unknown _CRS type %d\n",
472		    DEVNAME(sc), res.type);
473		aml_freevalue(&res);
474		return (1);
475	}
476
477	aml_parse_resource(&res, acpiec_parse_resources, sc);
478	aml_freevalue(&res);
479	if (sc->sc_ec_data == 0 || sc->sc_ec_sc == 0) {
480		printf("%s: failed to read from _CRS\n", DEVNAME(sc));
481		return (1);
482	}
483
484ecdtdone:
485
486	dnprintf(10, "%s: Data: 0x%lx, S/C: 0x%lx\n",
487	    DEVNAME(sc), sc->sc_ec_data, sc->sc_ec_sc);
488
489	if (bus_space_map(sc->sc_cmd_bt, sc->sc_ec_sc, 1, 0, &sc->sc_cmd_bh)) {
490		dnprintf(10, "%s: failed to map S/C reg.\n", DEVNAME(sc));
491		return (1);
492	}
493
494	rc = bus_space_map(sc->sc_data_bt, sc->sc_ec_data, 1, 0,
495	    &sc->sc_data_bh);
496	if (rc) {
497		dnprintf(10, "%s: failed to map DATA reg.\n", DEVNAME(sc));
498		bus_space_unmap(sc->sc_cmd_bt, sc->sc_cmd_bh, 1);
499		return (1);
500	}
501
502	return (0);
503}
504
505int
506acpiec_reg(struct acpiec_softc *sc)
507{
508	struct aml_value arg[2];
509	struct aml_node *node;
510
511	memset(&arg, 0, sizeof(arg));
512	arg[0].type = AML_OBJTYPE_INTEGER;
513	arg[0].v_integer = ACPI_OPREG_EC;
514	arg[1].type = AML_OBJTYPE_INTEGER;
515	arg[1].v_integer = 1;
516
517	node = aml_searchname(sc->sc_devnode, "_REG");
518	if (node && aml_evalnode(sc->sc_acpi, node, 2, arg, NULL)) {
519		dnprintf(10, "%s: eval method _REG failed\n", DEVNAME(sc));
520		printf("acpiec _REG failed, broken BIOS\n");
521	}
522
523	return (0);
524}
525