pchgpio.c revision 1.12
1/*	$OpenBSD: pchgpio.c,v 1.12 2022/04/06 18:59:27 naddy 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		5
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
100const struct 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	"INT344B",
111	"INT3450",
112	"INT3451",
113	"INT345D",
114	"INT34BB",
115	"INT34C5",
116	"INT34C6",
117	NULL
118};
119
120/* Sunrisepoint-LP */
121
122const struct pchgpio_group spt_lp_groups[] =
123{
124	/* Community 0 */
125	{ 0, 0, 0, 23, 0 },		/* GPP_A */
126	{ 0, 1, 24, 47, 24 },		/* GPP_B */
127
128	/* Community 1 */
129	{ 1, 0, 48, 71, 48 },		/* GPP_C */
130	{ 1, 1, 72, 95, 72 },		/* GPP_D */
131	{ 1, 2, 96, 119, 96 },		/* GPP_E */
132
133	/* Community 3 */
134	{ 2, 0, 120, 143, 120 },	/* GPP_F */
135	{ 2, 1, 144, 151, 144 },	/* GPP_G */
136};
137
138const struct pchgpio_device spt_lp_device =
139{
140	.pad_size = 8,
141	.gpi_is = 0x100,
142	.gpi_ie = 0x120,
143	.groups = spt_lp_groups,
144	.ngroups = nitems(spt_lp_groups),
145	.npins = 176,
146};
147
148/* Sunrisepoint-H */
149
150const struct pchgpio_group spt_h_groups[] =
151{
152	/* Community 0 */
153	{ 0, 0, 0, 23, 0 },		/* GPP_A */
154	{ 0, 1, 24, 47, 24 },		/* GPP_B */
155
156	/* Community 1 */
157	{ 1, 0, 48, 71, 48 },		/* GPP_C */
158	{ 1, 1, 72, 95, 72 },		/* GPP_D */
159	{ 1, 2, 96, 108, 96 },		/* GPP_E */
160	{ 1, 3, 109, 132, 120 },	/* GPP_F */
161	{ 1, 4, 133, 156, 144 },	/* GPP_G */
162	{ 1, 5, 157, 180, 168 },	/* GPP_H */
163
164	/* Community 3 */
165	{ 2, 0, 181, 191, 192 },	/* GPP_I */
166};
167
168const struct pchgpio_device spt_h_device =
169{
170	.pad_size = 8,
171	.gpi_is = 0x100,
172	.gpi_ie = 0x120,
173	.groups = spt_h_groups,
174	.ngroups = nitems(spt_h_groups),
175	.npins = 224,
176};
177
178/* Cannon Lake-H */
179
180const struct pchgpio_group cnl_h_groups[] =
181{
182	/* Community 0 */
183	{ 0, 0, 0, 24, 0 },		/* GPP_A */
184	{ 0, 1, 25, 50, 32 },		/* GPP_B */
185
186	/* Community 1 */
187	{ 1, 0, 51, 74, 64 },		/* GPP_C */
188	{ 1, 1, 75, 98, 96 },		/* GPP_D */
189	{ 1, 2, 99, 106, 128 },		/* GPP_G */
190
191	/* Community 3 */
192	{ 2, 0, 155, 178, 192 },	/* GPP_K */
193	{ 2, 1, 179, 202, 224 },	/* GPP_H */
194	{ 2, 2, 203, 215, 256 },	/* GPP_E */
195	{ 2, 3, 216, 239, 288 },	/* GPP_F */
196
197	/* Community 4 */
198	{ 3, 2, 269, 286, 320 },	/* GPP_I */
199	{ 3, 3, 287, 298, 352 },	/* GPP_J */
200};
201
202const struct pchgpio_device cnl_h_device =
203{
204	.pad_size = 16,
205	.gpi_is = 0x100,
206	.gpi_ie = 0x120,
207	.groups = cnl_h_groups,
208	.ngroups = nitems(cnl_h_groups),
209	.npins = 384,
210};
211
212/* Cannon Lake-LP */
213
214const struct pchgpio_group cnl_lp_groups[] =
215{
216	/* Community 0 */
217	{ 0, 0, 0, 24, 0 },		/* GPP_A */
218	{ 0, 1, 25, 50, 32 },		/* GPP_B */
219	{ 0, 2, 51, 58, 64 },		/* GPP_G */
220
221	/* Community 1 */
222	{ 1, 0, 68, 92, 96 },		/* GPP_D */
223	{ 1, 1, 93, 116, 128 },		/* GPP_F */
224	{ 1, 2, 117, 140, 160 },	/* GPP_H */
225
226	/* Community 4 */
227	{ 2, 0, 181, 204, 256 },	/* GPP_C */
228	{ 2, 1, 205, 228, 288 },	/* GPP_E */
229};
230
231const struct pchgpio_device cnl_lp_device =
232{
233	.pad_size = 16,
234	.gpi_is = 0x100,
235	.gpi_ie = 0x120,
236	.groups = cnl_lp_groups,
237	.ngroups = nitems(cnl_lp_groups),
238	.npins = 320,
239};
240
241/* Tiger Lake-LP */
242
243const struct pchgpio_group tgl_lp_groups[] =
244{
245	/* Community 0 */
246	{ 0, 0, 0, 25, 0 },		/* GPP_B */
247	{ 0, 1, 26, 41, 32 },		/* GPP_T */
248	{ 0, 2, 42, 66, 64 },		/* GPP_A */
249
250	/* Community 1 */
251	{ 1, 0, 67, 74, 96 },		/* GPP_S */
252	{ 1, 1, 75, 98, 128 },		/* GPP_H */
253	{ 1, 2, 99, 119, 160 },		/* GPP_D */
254	{ 1, 3, 120, 143, 192 },	/* GPP_U */
255
256	/* Community 4 */
257	{ 2, 0, 171, 194, 256 },	/* GPP_C */
258	{ 2, 1, 195, 219, 288 },	/* GPP_F */
259	{ 2, 3, 226, 250, 320 },	/* GPP_E */
260
261	/* Community 5 */
262	{ 3, 0, 260, 267, 352 },	/* GPP_R */
263};
264
265const struct pchgpio_device tgl_lp_device =
266{
267	.pad_size = 16,
268	.gpi_is = 0x100,
269	.gpi_ie = 0x120,
270	.groups = tgl_lp_groups,
271	.ngroups = nitems(tgl_lp_groups),
272	.npins = 360,
273};
274
275/* Tiger Lake-H */
276
277const struct pchgpio_group tgl_h_groups[] =
278{
279	/* Community 0 */
280	{ 0, 0, 0, 24, 0 },		/* GPP_A */
281	{ 0, 1, 25, 44, 32 },		/* GPP_R */
282	{ 0, 2, 45, 70, 64 },		/* GPP_B */
283
284	/* Community 1 */
285	{ 1, 0, 79, 104, 128 },		/* GPP_D */
286	{ 1, 1, 105, 128, 160 },	/* GPP_C */
287	{ 1, 2, 129, 136, 192 },	/* GPP_S */
288	{ 1, 3, 137, 153, 224 },	/* GPP_G */
289
290	/* Community 3 */
291	{ 2, 0, 181, 193, 288 },	/* GPP_E */
292	{ 2, 1, 194, 217, 320 },	/* GPP_F */
293
294	/* Community 4 */
295	{ 2, 0, 218, 241, 352 },	/* GPP_H */
296	{ 2, 1, 242, 251, 384 },	/* GPP_J */
297	{ 2, 2, 252, 266, 416 },	/* GPP_K */
298
299	/* Community 5 */
300	{ 3, 0, 267, 281, 448 },	/* GPP_I */
301};
302
303const struct pchgpio_device tgl_h_device =
304{
305	.pad_size = 16,
306	.gpi_is = 0x100,
307	.gpi_ie = 0x120,
308	.groups = tgl_h_groups,
309	.ngroups = nitems(tgl_h_groups),
310	.npins = 480,
311};
312
313struct pchgpio_match pchgpio_devices[] = {
314	{ "INT344B", &spt_lp_device },
315	{ "INT3450", &cnl_h_device },
316	{ "INT3451", &spt_h_device },
317	{ "INT345D", &spt_h_device },
318	{ "INT34BB", &cnl_lp_device },
319	{ "INT34C5", &tgl_lp_device },
320	{ "INT34C6", &tgl_h_device },
321};
322
323int	pchgpio_read_pin(void *, int);
324void	pchgpio_write_pin(void *, int, int);
325void	pchgpio_intr_establish(void *, int, int, int (*)(void *), void *);
326int	pchgpio_intr(void *);
327void	pchgpio_save(struct pchgpio_softc *);
328void	pchgpio_restore(struct pchgpio_softc *);
329
330int
331pchgpio_match(struct device *parent, void *match, void *aux)
332{
333	struct acpi_attach_args *aaa = aux;
334	struct cfdata *cf = match;
335
336	if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1)
337		return 0;
338	return acpi_matchhids(aaa, pchgpio_hids, cf->cf_driver->cd_name);
339}
340
341void
342pchgpio_attach(struct device *parent, struct device *self, void *aux)
343{
344	struct pchgpio_softc *sc = (struct pchgpio_softc *)self;
345	struct acpi_attach_args *aaa = aux;
346	uint16_t bar;
347	int i;
348
349	sc->sc_acpi = (struct acpi_softc *)parent;
350	sc->sc_node = aaa->aaa_node;
351	printf(" %s", sc->sc_node->name);
352
353	printf(" addr");
354
355	for (i = 0; i < aaa->aaa_naddr; i++) {
356		printf(" 0x%llx/0x%llx", aaa->aaa_addr[i], aaa->aaa_size[i]);
357
358		sc->sc_memt[i] = aaa->aaa_bst[i];
359		if (bus_space_map(sc->sc_memt[i], aaa->aaa_addr[i],
360		    aaa->aaa_size[i], 0, &sc->sc_memh[i])) {
361			printf(": can't map registers\n");
362			goto unmap;
363		}
364
365		sc->sc_padbar[i] = bus_space_read_4(sc->sc_memt[i],
366		    sc->sc_memh[i], PCHGPIO_PADBAR);
367		sc->sc_naddr++;
368	}
369
370	printf(" irq %d", aaa->aaa_irq[0]);
371
372	for (i = 0; i < nitems(pchgpio_devices); i++) {
373		if (strcmp(pchgpio_devices[i].hid, aaa->aaa_dev) == 0) {
374			sc->sc_device = pchgpio_devices[i].device;
375			break;
376		}
377	}
378	KASSERT(sc->sc_device);
379
380	/* Figure out the first pin for each community. */
381	bar = -1;
382	for (i = 0; i < sc->sc_device->ngroups; i++) {
383		if (sc->sc_device->groups[i].bar != bar) {
384			bar = sc->sc_device->groups[i].bar;
385			sc->sc_padbase[bar] = sc->sc_device->groups[i].base;
386		}
387	}
388
389	sc->sc_padsize = sc->sc_device->pad_size;
390	sc->sc_npins = sc->sc_device->npins;
391	sc->sc_pin_cfg = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_cfg),
392	    M_DEVBUF, M_WAITOK);
393	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
394	    M_DEVBUF, M_WAITOK | M_ZERO);
395
396	sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], aaa->aaa_irq_flags[0],
397	    IPL_BIO, pchgpio_intr, sc, sc->sc_dev.dv_xname);
398	if (sc->sc_ih == NULL) {
399		printf(": can't establish interrupt\n");
400		goto unmap;
401	}
402
403	sc->sc_gpio.cookie = sc;
404	sc->sc_gpio.read_pin = pchgpio_read_pin;
405	sc->sc_gpio.write_pin = pchgpio_write_pin;
406	sc->sc_gpio.intr_establish = pchgpio_intr_establish;
407	sc->sc_node->gpio = &sc->sc_gpio;
408
409	printf(", %d pins\n", sc->sc_npins);
410
411	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
412	return;
413
414unmap:
415	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
416	free(sc->sc_pin_cfg, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_cfg));
417	for (i = 0; i < sc->sc_naddr; i++)
418		bus_space_unmap(sc->sc_memt[i], sc->sc_memh[i],
419		    aaa->aaa_size[i]);
420}
421
422int
423pchgpio_activate(struct device *self, int act)
424{
425	struct pchgpio_softc *sc = (struct pchgpio_softc *)self;
426
427	switch (act) {
428	case DVACT_SUSPEND:
429		pchgpio_save(sc);
430		break;
431	case DVACT_RESUME:
432		pchgpio_restore(sc);
433		break;
434	}
435
436	return 0;
437}
438
439const struct pchgpio_group *
440pchgpio_find_group(struct pchgpio_softc *sc, int pin)
441{
442	int i, npads;
443
444	for (i = 0; i < sc->sc_device->ngroups; i++) {
445		npads = 1 + sc->sc_device->groups[i].limit -
446		    sc->sc_device->groups[i].base;
447
448		if (pin >= sc->sc_device->groups[i].gpiobase &&
449		    pin < sc->sc_device->groups[i].gpiobase + npads)
450			return &sc->sc_device->groups[i];
451	}
452	return NULL;
453}
454
455int
456pchgpio_read_pin(void *cookie, int pin)
457{
458	struct pchgpio_softc *sc = cookie;
459	const struct pchgpio_group *group;
460	uint32_t reg;
461	uint16_t pad;
462	uint8_t bar;
463
464	group = pchgpio_find_group(sc, pin);
465	bar = group->bar;
466	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
467
468	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
469	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
470
471	return !!(reg & PCHGPIO_CONF_RXSTATE);
472}
473
474void
475pchgpio_write_pin(void *cookie, int pin, int value)
476{
477	struct pchgpio_softc *sc = cookie;
478	const struct pchgpio_group *group;
479	uint32_t reg;
480	uint16_t pad;
481	uint8_t bar;
482
483	group = pchgpio_find_group(sc, pin);
484	bar = group->bar;
485	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
486
487	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
488	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
489	if (value)
490		reg |= PCHGPIO_CONF_TXSTATE;
491	else
492		reg &= ~PCHGPIO_CONF_TXSTATE;
493	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
494	    sc->sc_padbar[bar] + pad * sc->sc_padsize, reg);
495}
496
497void
498pchgpio_intr_establish(void *cookie, int pin, int flags,
499    int (*func)(void *), void *arg)
500{
501	struct pchgpio_softc *sc = cookie;
502	const struct pchgpio_group *group;
503	uint32_t reg;
504	uint16_t pad;
505	uint8_t bank, bar;
506
507	KASSERT(pin >= 0);
508
509	group = pchgpio_find_group(sc, pin);
510	if (group == NULL)
511		return;
512
513	bar = group->bar;
514	bank = group->bank;
515	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
516
517	sc->sc_pin_ih[pin].ih_func = func;
518	sc->sc_pin_ih[pin].ih_arg = arg;
519
520	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
521	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
522	reg &= ~(PCHGPIO_CONF_RXEV_MASK | PCHGPIO_CONF_RXINV);
523	if ((flags & LR_GPIO_MODE) == 1)
524		reg |= PCHGPIO_CONF_RXEV_EDGE;
525	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTLO)
526		reg |= PCHGPIO_CONF_RXINV;
527	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTBOTH)
528		reg |= PCHGPIO_CONF_RXEV_EDGE | PCHGPIO_CONF_RXEV_ZERO;
529	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
530	    sc->sc_padbar[bar] + pad * sc->sc_padsize, reg);
531
532	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
533	    sc->sc_device->gpi_ie + bank * 4);
534	reg |= (1 << (pin - group->gpiobase));
535	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
536	    sc->sc_device->gpi_ie + bank * 4, reg);
537}
538
539int
540pchgpio_intr_handle(struct pchgpio_softc *sc, int group, int bit)
541{
542	uint32_t enable;
543	int gpiobase, pin, handled = 0;
544	uint8_t bank, bar;
545
546	bar = sc->sc_device->groups[group].bar;
547	bank = sc->sc_device->groups[group].bank;
548	gpiobase = sc->sc_device->groups[group].gpiobase;
549
550	pin = gpiobase + bit;
551	if (sc->sc_pin_ih[pin].ih_func) {
552		sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
553		handled = 1;
554	} else {
555		/* Mask unhandled interrupt */
556		enable = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
557		    sc->sc_device->gpi_ie + bank * 4);
558		enable &= ~(1 << bit);
559		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
560		    sc->sc_device->gpi_ie + bank * 4, enable);
561	}
562
563	return handled;
564}
565
566int
567pchgpio_intr(void *arg)
568{
569	struct pchgpio_softc *sc = arg;
570	uint32_t status, enable;
571	int group, bit, handled = 0;
572	uint16_t base, limit;
573	uint8_t bank, bar;
574
575	for (group = 0; group < sc->sc_device->ngroups; group++) {
576		bar = sc->sc_device->groups[group].bar;
577		bank = sc->sc_device->groups[group].bank;
578		base = sc->sc_device->groups[group].base;
579		limit = sc->sc_device->groups[group].limit;
580
581		status = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
582		    sc->sc_device->gpi_is + bank * 4);
583		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
584		    sc->sc_device->gpi_is + bank * 4, status);
585		enable = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
586		    sc->sc_device->gpi_ie + bank * 4);
587		status &= enable;
588		if (status == 0)
589			continue;
590
591		for (bit = 0; bit <= (limit - base); bit++) {
592			if (status & (1 << bit))
593				handled |= pchgpio_intr_handle(sc, group, bit);
594		}
595	}
596
597	return handled;
598}
599
600void
601pchgpio_save_pin(struct pchgpio_softc *sc, int pin)
602{
603	const struct pchgpio_group *group;
604	uint32_t gpi_ie;
605	uint16_t pad;
606	uint8_t bank, bar;
607
608	group = pchgpio_find_group(sc, pin);
609	if (group == NULL)
610		return;
611
612	bar = group->bar;
613	bank = group->bank;
614	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
615
616	sc->sc_pin_cfg[pin].pad_cfg_dw0 =
617	    bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
618		sc->sc_padbar[bar] + pad * sc->sc_padsize);
619	sc->sc_pin_cfg[pin].pad_cfg_dw1 =
620	    bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
621		sc->sc_padbar[bar] + pad * sc->sc_padsize + 4);
622
623	gpi_ie = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
624	    sc->sc_device->gpi_ie + bank * 4);
625	sc->sc_pin_cfg[pin].gpi_ie = (gpi_ie & (1 << (pin - group->gpiobase)));
626}
627
628void
629pchgpio_save(struct pchgpio_softc *sc)
630{
631	int pin;
632
633	for (pin = 0; pin < sc->sc_npins; pin++)
634		pchgpio_save_pin(sc, pin);
635}
636
637void
638pchgpio_restore_pin(struct pchgpio_softc *sc, int pin)
639{
640	const struct pchgpio_group *group;
641	int restore = 0;
642	uint32_t pad_cfg_dw0, gpi_ie;
643	uint16_t pad;
644	uint8_t bank, bar;
645
646	group = pchgpio_find_group(sc, pin);
647	if (group == NULL)
648		return;
649
650	bar = group->bar;
651	bank = group->bank;
652	pad = group->base + (pin - group->gpiobase) - sc->sc_padbase[bar];
653
654	pad_cfg_dw0 = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
655	    sc->sc_padbar[bar] + pad * sc->sc_padsize);
656
657	if (sc->sc_pin_ih[pin].ih_func)
658		restore = 1;
659
660	/*
661	 * The BIOS on Lenovo Thinkpads based on Intel's Tiger Lake
662	 * platform have a bug where the GPIO pin that is used for the
663	 * touchpad interrupt gets reset when entering S3 and isn't
664	 * properly restored upon resume.  We detect this issue by
665	 * comparing the bits in the PAD_CFG_DW0 register PADRSTCFG
666	 * field before suspend and after resume and restore the pin
667	 * configuration if the bits don't match.
668	 */
669	if ((sc->sc_pin_cfg[pin].pad_cfg_dw0 & PCHGPIO_CONF_PADRSTCFG_MASK) !=
670	    (pad_cfg_dw0 & PCHGPIO_CONF_PADRSTCFG_MASK))
671		restore = 1;
672
673	if (restore) {
674		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
675		    sc->sc_padbar[bar] + pad * sc->sc_padsize,
676		    sc->sc_pin_cfg[pin].pad_cfg_dw0);
677		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
678		    sc->sc_padbar[bar] + pad * sc->sc_padsize + 4,
679		    sc->sc_pin_cfg[pin].pad_cfg_dw1);
680
681		gpi_ie = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
682		    sc->sc_device->gpi_ie + bank * 4);
683		if (sc->sc_pin_cfg[pin].gpi_ie)
684			gpi_ie |= (1 << (pin - group->gpiobase));
685		else
686			gpi_ie &= ~(1 << (pin - group->gpiobase));
687		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
688		    sc->sc_device->gpi_ie + bank * 4, gpi_ie);
689	}
690}
691
692void
693pchgpio_restore(struct pchgpio_softc *sc)
694{
695	int pin;
696
697	for (pin = 0; pin < sc->sc_npins; pin++)
698		pchgpio_restore_pin(sc, pin);
699}
700