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