pchgpio.c revision 1.7
1/*	$OpenBSD: pchgpio.c,v 1.7 2021/09/21 14:59:13 kettenis Exp $	*/
2/*
3 * Copyright (c) 2020 Mark Kettenis
4 * Copyright (c) 2020 James Hastings
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/malloc.h>
21#include <sys/systm.h>
22
23#include <dev/acpi/acpireg.h>
24#include <dev/acpi/acpivar.h>
25#include <dev/acpi/acpidev.h>
26#include <dev/acpi/amltypes.h>
27#include <dev/acpi/dsdt.h>
28
29#define PCHGPIO_MAXCOM		4
30
31#define PCHGPIO_CONF_TXSTATE		0x00000001
32#define PCHGPIO_CONF_RXSTATE		0x00000002
33#define PCHGPIO_CONF_RXINV		0x00800000
34#define PCHGPIO_CONF_RXEV_EDGE		0x02000000
35#define PCHGPIO_CONF_RXEV_ZERO		0x04000000
36#define PCHGPIO_CONF_RXEV_MASK		0x06000000
37#define PCHGPIO_CONF_PADRSTCFG_MASK	0xc0000000
38
39#define PCHGPIO_PADBAR		0x00c
40
41struct pchgpio_group {
42	uint8_t		bar;
43	uint8_t		bank;
44	uint16_t	base;
45	uint16_t	limit;
46	int16_t		gpiobase;
47};
48
49struct pchgpio_device {
50	uint16_t	pad_size;
51	uint16_t	gpi_is;
52	uint16_t	gpi_ie;
53	const struct pchgpio_group *groups;
54	int		ngroups;
55	int		npins;
56};
57
58struct pchgpio_match {
59	const char	*hid;
60	const struct pchgpio_device *device;
61};
62
63struct pchgpio_pincfg {
64	uint32_t	pad_cfg_dw0;
65	uint32_t	pad_cfg_dw1;
66	int		gpi_ie;
67};
68
69struct pchgpio_intrhand {
70	int (*ih_func)(void *);
71	void *ih_arg;
72};
73
74struct pchgpio_softc {
75	struct device sc_dev;
76	struct acpi_softc *sc_acpi;
77	struct aml_node *sc_node;
78
79	bus_space_tag_t sc_memt[PCHGPIO_MAXCOM];
80	bus_space_handle_t sc_memh[PCHGPIO_MAXCOM];
81	void *sc_ih;
82	int sc_naddr;
83
84	const struct pchgpio_device *sc_device;
85	uint16_t sc_padbar[PCHGPIO_MAXCOM];
86	uint16_t sc_padbase[PCHGPIO_MAXCOM];
87	int sc_padsize;
88
89	int sc_npins;
90	struct pchgpio_pincfg *sc_pin_cfg;
91	struct pchgpio_intrhand *sc_pin_ih;
92
93	struct acpi_gpio sc_gpio;
94};
95
96int	pchgpio_match(struct device *, void *, void *);
97void	pchgpio_attach(struct device *, struct device *, void *);
98int	pchgpio_activate(struct device *, int);
99
100struct cfattach pchgpio_ca = {
101	sizeof(struct pchgpio_softc), pchgpio_match, pchgpio_attach,
102	NULL, pchgpio_activate
103};
104
105struct cfdriver pchgpio_cd = {
106	NULL, "pchgpio", DV_DULL
107};
108
109const char *pchgpio_hids[] = {
110	"INT34BB",
111	"INT34C5",
112	NULL
113};
114
115const struct pchgpio_group cnl_lp_groups[] =
116{
117	/* Community 0 */
118	{ 0, 0, 0, 24, 0 },		/* GPP_A */
119	{ 0, 1, 25, 50, 32 },		/* GPP_B */
120	{ 0, 2, 51, 58, 64 },		/* GPP_G */
121
122	/* Community 1 */
123	{ 1, 0, 68, 92, 96 },		/* GPP_D */
124	{ 1, 1, 93, 116, 128 },		/* GPP_F */
125	{ 1, 2, 117, 140, 160 },	/* GPP_H */
126
127	/* Community 4 */
128	{ 2, 0, 181, 204, 256 },	/* GPP_C */
129	{ 2, 1, 205, 228, 288 },	/* GPP_E */
130};
131
132const struct pchgpio_device cnl_lp_device =
133{
134	.pad_size = 16,
135	.gpi_is = 0x100,
136	.gpi_ie = 0x120,
137	.groups = cnl_lp_groups,
138	.ngroups = nitems(cnl_lp_groups),
139	.npins = 320,
140};
141
142const struct pchgpio_group tgl_lp_groups[] =
143{
144	/* Community 0 */
145	{ 0, 0, 0, 25, 0 },		/* GPP_B */
146	{ 0, 1, 26, 41, 32 },		/* GPP_T */
147	{ 0, 2, 42, 66, 64 },		/* GPP_A */
148
149	/* Community 1 */
150	{ 1, 0, 67, 74, 96 },		/* GPP_S */
151	{ 1, 1, 75, 98, 128 },		/* GPP_H */
152	{ 1, 2, 99, 119, 160 },		/* GPP_D */
153	{ 1, 3, 120, 143, 192 },	/* GPP_U */
154
155	/* Community 4 */
156	{ 2, 0, 171, 194, 256 },	/* GPP_C */
157	{ 2, 1, 195, 219, 288 },	/* GPP_F */
158	{ 2, 3, 226, 250, 320 },	/* GPP_E */
159
160	/* Community 5 */
161	{ 3, 0, 260, 267, 352 },	/* GPP_R */
162};
163
164const struct pchgpio_device tgl_lp_device =
165{
166	.pad_size = 16,
167	.gpi_is = 0x100,
168	.gpi_ie = 0x120,
169	.groups = tgl_lp_groups,
170	.ngroups = nitems(tgl_lp_groups),
171	.npins = 360,
172};
173
174struct pchgpio_match pchgpio_devices[] = {
175	{ "INT34BB", &cnl_lp_device },
176	{ "INT34C5", &tgl_lp_device },
177};
178
179int	pchgpio_read_pin(void *, int);
180void	pchgpio_write_pin(void *, int, int);
181void	pchgpio_intr_establish(void *, int, int, int (*)(void *), void *);
182int	pchgpio_intr(void *);
183void	pchgpio_save(struct pchgpio_softc *);
184void	pchgpio_restore(struct pchgpio_softc *);
185
186int
187pchgpio_match(struct device *parent, void *match, void *aux)
188{
189	struct acpi_attach_args *aaa = aux;
190	struct cfdata *cf = match;
191
192	return acpi_matchhids(aaa, pchgpio_hids, cf->cf_driver->cd_name);
193}
194
195void
196pchgpio_attach(struct device *parent, struct device *self, void *aux)
197{
198	struct pchgpio_softc *sc = (struct pchgpio_softc *)self;
199	struct acpi_attach_args *aaa = aux;
200	uint16_t bar;
201	int i;
202
203	sc->sc_acpi = (struct acpi_softc *)parent;
204	sc->sc_node = aaa->aaa_node;
205	printf(" %s", sc->sc_node->name);
206
207	if (aaa->aaa_naddr < 1) {
208		printf(": no registers\n");
209		return;
210	}
211
212	if (aaa->aaa_nirq < 1) {
213		printf(": no interrupt\n");
214		return;
215	}
216
217	printf(" addr");
218
219	for (i = 0; i < aaa->aaa_naddr; i++) {
220		printf(" 0x%llx/0x%llx", aaa->aaa_addr[i], aaa->aaa_size[i]);
221
222		sc->sc_memt[i] = aaa->aaa_bst[i];
223		if (bus_space_map(sc->sc_memt[i], aaa->aaa_addr[i],
224		    aaa->aaa_size[i], 0, &sc->sc_memh[i])) {
225			printf(": can't map registers\n");
226			goto unmap;
227		}
228
229		sc->sc_padbar[i] = bus_space_read_4(sc->sc_memt[i],
230		    sc->sc_memh[i], PCHGPIO_PADBAR);
231		sc->sc_naddr++;
232	}
233
234	printf(" irq %d", aaa->aaa_irq[0]);
235
236	for (i = 0; i < nitems(pchgpio_devices); i++) {
237		if (strcmp(pchgpio_devices[i].hid, aaa->aaa_dev) == 0) {
238			sc->sc_device = pchgpio_devices[i].device;
239			break;
240		}
241	}
242	KASSERT(sc->sc_device);
243
244	/* Figure out the first pin for each community. */
245	bar = -1;
246	for (i = 0; i < sc->sc_device->ngroups; i++) {
247		if (sc->sc_device->groups[i].bar != bar) {
248			bar = sc->sc_device->groups[i].bar;
249			sc->sc_padbase[bar] = sc->sc_device->groups[i].base;
250		}
251	}
252
253	sc->sc_padsize = sc->sc_device->pad_size;
254	sc->sc_npins = sc->sc_device->npins;
255	sc->sc_pin_cfg = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_cfg),
256	    M_DEVBUF, M_WAITOK);
257	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
258	    M_DEVBUF, M_WAITOK | M_ZERO);
259
260	sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], aaa->aaa_irq_flags[0],
261	    IPL_BIO, pchgpio_intr, sc, sc->sc_dev.dv_xname);
262	if (sc->sc_ih == NULL) {
263		printf(": can't establish interrupt\n");
264		goto unmap;
265	}
266
267	sc->sc_gpio.cookie = sc;
268	sc->sc_gpio.read_pin = pchgpio_read_pin;
269	sc->sc_gpio.write_pin = pchgpio_write_pin;
270	sc->sc_gpio.intr_establish = pchgpio_intr_establish;
271	sc->sc_node->gpio = &sc->sc_gpio;
272
273	printf(", %d pins\n", sc->sc_npins);
274
275	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
276	return;
277
278unmap:
279	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
280	free(sc->sc_pin_cfg, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_cfg));
281	for (i = 0; i < sc->sc_naddr; i++)
282		bus_space_unmap(sc->sc_memt[i], sc->sc_memh[i],
283		    aaa->aaa_size[i]);
284}
285
286int
287pchgpio_activate(struct device *self, int act)
288{
289	struct pchgpio_softc *sc = (struct pchgpio_softc *)self;
290
291	switch (act) {
292	case DVACT_SUSPEND:
293		pchgpio_save(sc);
294		break;
295	case DVACT_RESUME:
296		pchgpio_restore(sc);
297		break;
298	}
299
300	return 0;
301}
302
303const struct pchgpio_group *
304pchgpio_find_group(struct pchgpio_softc *sc, int pin)
305{
306	int i, npads;
307
308	for (i = 0; i < sc->sc_device->ngroups; i++) {
309		npads = 1 + sc->sc_device->groups[i].limit -
310		    sc->sc_device->groups[i].base;
311
312		if (pin >= sc->sc_device->groups[i].gpiobase &&
313		    pin < sc->sc_device->groups[i].gpiobase + npads)
314			return &sc->sc_device->groups[i];
315	}
316	return NULL;
317}
318
319int
320pchgpio_read_pin(void *cookie, int pin)
321{
322	struct pchgpio_softc *sc = cookie;
323	const struct pchgpio_group *group;
324	uint32_t reg;
325	uint16_t pad;
326	uint8_t bar;
327
328	group = pchgpio_find_group(sc, pin);
329	bar = group->bar;
330	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
331
332	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
333	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
334
335	return !!(reg & PCHGPIO_CONF_RXSTATE);
336}
337
338void
339pchgpio_write_pin(void *cookie, int pin, int value)
340{
341	struct pchgpio_softc *sc = cookie;
342	const struct pchgpio_group *group;
343	uint32_t reg;
344	uint16_t pad;
345	uint8_t bar;
346
347	group = pchgpio_find_group(sc, pin);
348	bar = group->bar;
349	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
350
351	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
352	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
353	if (value)
354		reg |= PCHGPIO_CONF_TXSTATE;
355	else
356		reg &= ~PCHGPIO_CONF_TXSTATE;
357	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
358	    sc->sc_padbar[bar] + pad * sc->sc_padsize, reg);
359}
360
361void
362pchgpio_intr_establish(void *cookie, int pin, int flags,
363    int (*func)(void *), void *arg)
364{
365	struct pchgpio_softc *sc = cookie;
366	const struct pchgpio_group *group;
367	uint32_t reg;
368	uint16_t pad;
369	uint8_t bank, bar;
370
371	KASSERT(pin >= 0);
372
373	group = pchgpio_find_group(sc, pin);
374	if (group == NULL)
375		return;
376
377	bar = group->bar;
378	bank = group->bank;
379	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
380
381	sc->sc_pin_ih[pin].ih_func = func;
382	sc->sc_pin_ih[pin].ih_arg = arg;
383
384	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
385	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
386	reg &= ~(PCHGPIO_CONF_RXEV_MASK | PCHGPIO_CONF_RXINV);
387	if ((flags & LR_GPIO_MODE) == 1)
388		reg |= PCHGPIO_CONF_RXEV_EDGE;
389	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTLO)
390		reg |= PCHGPIO_CONF_RXINV;
391	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTBOTH)
392		reg |= PCHGPIO_CONF_RXEV_EDGE | PCHGPIO_CONF_RXEV_ZERO;
393	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
394	    sc->sc_padbar[bar] + pad * sc->sc_padsize, reg);
395
396	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
397	    sc->sc_device->gpi_ie + bank * 4);
398	reg |= (1 << (pin - group->gpiobase));
399	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
400	    sc->sc_device->gpi_ie + bank * 4, reg);
401}
402
403int
404pchgpio_intr(void *arg)
405{
406	struct pchgpio_softc *sc = arg;
407	uint32_t status, enable;
408	int gpiobase, group, bit, pin, handled = 0;
409	uint16_t base, limit;
410	uint8_t bank, bar;
411
412	for (group = 0; group < sc->sc_device->ngroups; group++) {
413		bar = sc->sc_device->groups[group].bar;
414		bank = sc->sc_device->groups[group].bank;
415		base = sc->sc_device->groups[group].base;
416		limit = sc->sc_device->groups[group].limit;
417		gpiobase = sc->sc_device->groups[group].gpiobase;
418
419		status = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
420		    sc->sc_device->gpi_is + bank * 4);
421		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
422		    sc->sc_device->gpi_is + bank * 4, status);
423		enable = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
424		    sc->sc_device->gpi_ie + bank * 4);
425		status &= enable;
426		if (status == 0)
427			continue;
428
429		for (bit = 0; bit <= (limit - base); bit++) {
430			pin = gpiobase + bit;
431			if (status & (1 << bit) && sc->sc_pin_ih[pin].ih_func)
432				sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
433			handled = 1;
434		}
435	}
436
437	return handled;
438}
439
440void
441pchgpio_save_pin(struct pchgpio_softc *sc, int pin)
442{
443	const struct pchgpio_group *group;
444	uint32_t gpi_ie;
445	uint16_t pad;
446	uint8_t bank, bar;
447
448	group = pchgpio_find_group(sc, pin);
449	if (group == NULL)
450		return;
451
452	bar = group->bar;
453	bank = group->bank;
454	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
455
456	sc->sc_pin_cfg[pin].pad_cfg_dw0 =
457	    bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
458		sc->sc_padbar[bar] + pad * sc->sc_padsize);
459	sc->sc_pin_cfg[pin].pad_cfg_dw1 =
460	    bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
461		sc->sc_padbar[bar] + pad * sc->sc_padsize + 4);
462
463	gpi_ie = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
464	    sc->sc_device->gpi_ie + bank * 4);
465	sc->sc_pin_cfg[pin].gpi_ie = (gpi_ie & (1 << (pin - group->gpiobase)));
466}
467
468void
469pchgpio_save(struct pchgpio_softc *sc)
470{
471	int pin;
472
473	for (pin = 0; pin < sc->sc_npins; pin++)
474		pchgpio_save_pin(sc, pin);
475}
476
477void
478pchgpio_restore_pin(struct pchgpio_softc *sc, int pin)
479{
480	const struct pchgpio_group *group;
481	int restore = 0;
482	uint32_t pad_cfg_dw0, gpi_ie;
483	uint16_t pad;
484	uint8_t bank, bar;
485
486	group = pchgpio_find_group(sc, pin);
487	if (group == NULL)
488		return;
489
490	bar = group->bar;
491	bank = group->bank;
492	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
493
494	pad_cfg_dw0 = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
495	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
496
497	if (sc->sc_pin_ih[pin].ih_func)
498		restore = 1;
499
500	/*
501	 * The BIOS on Lenovo Thinkpads based on Intel's Tiger Lake
502	 * platform have a bug where the GPIO pin that is used for the
503	 * touchpad interrupt gets reset when entering S3 and isn't
504	 * properly restored upon resume.  We detect this issue by
505	 * comparing the bits in the PAD_CFG_DW0 register PADRSTCFG
506	 * field before suspend and after resume and restore the pin
507	 * configuration if the bits don't match.
508	 */
509	if ((sc->sc_pin_cfg[pin].pad_cfg_dw0 & PCHGPIO_CONF_PADRSTCFG_MASK) !=
510	    (pad_cfg_dw0 & PCHGPIO_CONF_PADRSTCFG_MASK))
511		restore = 1;
512
513	if (restore) {
514		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
515		    sc->sc_padbar[bar] + pad * sc->sc_padsize,
516		    sc->sc_pin_cfg[pin].pad_cfg_dw0);
517		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
518		    sc->sc_padbar[bar] + pad * sc->sc_padsize + 4,
519		    sc->sc_pin_cfg[pin].pad_cfg_dw1);
520
521		gpi_ie = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
522		    sc->sc_device->gpi_ie + bank * 4);
523		if (sc->sc_pin_cfg[pin].gpi_ie)
524			gpi_ie |= (1 << (pin - group->gpiobase));
525		else
526			gpi_ie &= ~(1 << (pin - group->gpiobase));
527		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
528		    sc->sc_device->gpi_ie + bank * 4, gpi_ie);
529	}
530}
531
532void
533pchgpio_restore(struct pchgpio_softc *sc)
534{
535	int pin;
536
537	for (pin = 0; pin < sc->sc_npins; pin++)
538		pchgpio_restore_pin(sc, pin);
539}
540