pchgpio.c revision 1.4
1/*	$OpenBSD: pchgpio.c,v 1.4 2021/08/24 16:18:50 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
38#define PCHGPIO_PADBAR		0x00c
39
40struct pchgpio_group {
41	uint8_t		bar;
42	uint8_t		bank;
43	uint16_t	base;
44	uint16_t	limit;
45	uint16_t	offset;
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	struct pchgpio_group *groups;
54	int		ngroups;
55	int		npins;
56};
57
58struct pchgpio_match {
59	const char	*hid;
60	struct pchgpio_device *device;
61};
62
63struct pchgpio_intrhand {
64	int (*ih_func)(void *);
65	void *ih_arg;
66};
67
68struct pchgpio_softc {
69	struct device sc_dev;
70	struct acpi_softc *sc_acpi;
71	struct aml_node *sc_node;
72
73	bus_space_tag_t sc_memt[PCHGPIO_MAXCOM];
74	bus_space_handle_t sc_memh[PCHGPIO_MAXCOM];
75	void *sc_ih;
76	int sc_naddr;
77
78	struct pchgpio_device *sc_device;
79	uint16_t sc_padbar[PCHGPIO_MAXCOM];
80	int sc_padsize;
81
82	int sc_npins;
83	struct pchgpio_intrhand *sc_pin_ih;
84
85	struct acpi_gpio sc_gpio;
86};
87
88int	pchgpio_match(struct device *, void *, void *);
89void	pchgpio_attach(struct device *, struct device *, void *);
90
91struct cfattach pchgpio_ca = {
92	sizeof(struct pchgpio_softc), pchgpio_match, pchgpio_attach
93};
94
95struct cfdriver pchgpio_cd = {
96	NULL, "pchgpio", DV_DULL
97};
98
99const char *pchgpio_hids[] = {
100	"INT34BB",
101	"INT34C5",
102	NULL
103};
104
105struct pchgpio_group cnl_lp_groups[] =
106{
107	/* Community 0 */
108	{ 0, 0, 0, 24, 0, 0 },		/* GPP_A */
109	{ 0, 1, 25, 50, 25, 32 },	/* GPP_B */
110	{ 0, 2, 51, 58, 51, 64 },	/* GPP_G */
111
112	/* Community 1 */
113	{ 1, 0, 68, 92, 0, 96 },	/* GPP_D */
114	{ 1, 1, 93, 116, 25, 128 },	/* GPP_F */
115	{ 1, 2, 117, 140, 49, 160 },	/* GPP_H */
116
117	/* Community 4 */
118	{ 2, 0, 181, 204, 0, 256 },	/* GPP_C */
119	{ 2, 1, 205, 228, 24, 288 },	/* GPP_E */
120};
121
122struct pchgpio_device cnl_lp_device =
123{
124	.pad_size = 16,
125	.gpi_is = 0x100,
126	.gpi_ie = 0x120,
127	.groups = cnl_lp_groups,
128	.ngroups = nitems(cnl_lp_groups),
129	.npins = 320,
130};
131
132struct pchgpio_group tgl_lp_groups[] =
133{
134	/* Community 0 */
135	{ 0, 0, 0, 25, 0, 0 },		/* GPP_B */
136	{ 0, 1, 26, 41, 26, 32 },	/* GPP_T */
137	{ 0, 2, 42, 66, 42, 64 },	/* GPP_A */
138
139	/* Community 1 */
140	{ 1, 0, 67, 74, 0, 96 },	/* GPP_S */
141	{ 1, 1, 75, 98, 8, 128 },	/* GPP_H */
142	{ 1, 2, 99, 119, 32, 160 },	/* GPP_D */
143	{ 1, 3, 120, 143, 53, 192 },	/* GPP_U */
144
145	/* Community 4 */
146	{ 2, 0, 171, 194, 0, 256 },	/* GPP_C */
147	{ 2, 1, 195, 219, 24, 288 },	/* GPP_F */
148	{ 2, 3, 226, 250, 55, 320 },	/* GPP_E */
149
150	/* Community 5 */
151	{ 3, 0, 260, 267, 0, 352 },	/* GPP_R */
152};
153
154struct pchgpio_device tgl_lp_device =
155{
156	.pad_size = 16,
157	.gpi_is = 0x100,
158	.gpi_ie = 0x120,
159	.groups = tgl_lp_groups,
160	.ngroups = nitems(tgl_lp_groups),
161	.npins = 360,
162};
163
164struct pchgpio_match pchgpio_devices[] = {
165	{ "INT34BB", &cnl_lp_device },
166	{ "INT34C5", &tgl_lp_device },
167};
168
169int	pchgpio_read_pin(void *, int);
170void	pchgpio_write_pin(void *, int, int);
171void	pchgpio_intr_establish(void *, int, int, int (*)(void *), void *);
172int	pchgpio_intr(void *);
173
174int
175pchgpio_match(struct device *parent, void *match, void *aux)
176{
177	struct acpi_attach_args *aaa = aux;
178	struct cfdata *cf = match;
179
180	return acpi_matchhids(aaa, pchgpio_hids, cf->cf_driver->cd_name);
181}
182
183void
184pchgpio_attach(struct device *parent, struct device *self, void *aux)
185{
186	struct pchgpio_softc *sc = (struct pchgpio_softc *)self;
187	struct acpi_attach_args *aaa = aux;
188	int i;
189
190	sc->sc_acpi = (struct acpi_softc *)parent;
191	sc->sc_node = aaa->aaa_node;
192	printf(" %s", sc->sc_node->name);
193
194	if (aaa->aaa_naddr < 1) {
195		printf(": no registers\n");
196		return;
197	}
198
199	if (aaa->aaa_nirq < 1) {
200		printf(": no interrupt\n");
201		return;
202	}
203
204	printf(" addr");
205
206	for (i = 0; i < aaa->aaa_naddr; i++) {
207		printf(" 0x%llx/0x%llx", aaa->aaa_addr[i], aaa->aaa_size[i]);
208
209		sc->sc_memt[i] = aaa->aaa_bst[i];
210		if (bus_space_map(sc->sc_memt[i], aaa->aaa_addr[i],
211		    aaa->aaa_size[i], 0, &sc->sc_memh[i])) {
212			printf(": can't map registers\n");
213			goto unmap;
214		}
215
216		sc->sc_padbar[i] = bus_space_read_4(sc->sc_memt[i],
217		    sc->sc_memh[i], PCHGPIO_PADBAR);
218		sc->sc_naddr++;
219	}
220
221	printf(" irq %d", aaa->aaa_irq[0]);
222
223	for (i = 0; i < nitems(pchgpio_devices); i++) {
224		if (strcmp(pchgpio_devices[i].hid, aaa->aaa_dev) == 0) {
225			sc->sc_device = pchgpio_devices[i].device;
226			break;
227		}
228	}
229	KASSERT(sc->sc_device);
230
231	sc->sc_padsize = sc->sc_device->pad_size;
232	sc->sc_npins = sc->sc_device->npins;
233	sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
234	    M_DEVBUF, M_WAITOK | M_ZERO);
235
236	sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], aaa->aaa_irq_flags[0],
237	    IPL_BIO, pchgpio_intr, sc, sc->sc_dev.dv_xname);
238	if (sc->sc_ih == NULL) {
239		printf(": can't establish interrupt\n");
240		goto unmap;
241	}
242
243	sc->sc_gpio.cookie = sc;
244	sc->sc_gpio.read_pin = pchgpio_read_pin;
245	sc->sc_gpio.write_pin = pchgpio_write_pin;
246	sc->sc_gpio.intr_establish = pchgpio_intr_establish;
247	sc->sc_node->gpio = &sc->sc_gpio;
248
249	printf(", %d pins\n", sc->sc_npins);
250
251	acpi_register_gpio(sc->sc_acpi, sc->sc_node);
252	return;
253
254unmap:
255	free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
256	for (i = 0; i < sc->sc_naddr; i++)
257		bus_space_unmap(sc->sc_memt[i], sc->sc_memh[i],
258		    aaa->aaa_size[i]);
259}
260
261struct pchgpio_group *
262pchgpio_find_group(struct pchgpio_softc *sc, int pin)
263{
264	int i, npads;
265
266	for (i = 0; i < sc->sc_device->ngroups; i++) {
267		npads = 1 + sc->sc_device->groups[i].limit -
268		    sc->sc_device->groups[i].base;
269
270		if (pin >= sc->sc_device->groups[i].gpiobase &&
271		    pin < sc->sc_device->groups[i].gpiobase + npads)
272			return &sc->sc_device->groups[i];
273	}
274	return NULL;
275}
276
277int
278pchgpio_read_pin(void *cookie, int pin)
279{
280	struct pchgpio_softc *sc = cookie;
281	struct pchgpio_group *group;
282	uint32_t reg;
283	uint16_t offset;
284	uint8_t bar;
285
286	group = pchgpio_find_group(sc, pin);
287	offset = group->offset + (pin - group->gpiobase);
288	bar = group->bar;
289
290	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
291	    sc->sc_padbar[bar] + offset * sc->sc_padsize);
292
293	return !!(reg & PCHGPIO_CONF_RXSTATE);
294}
295
296void
297pchgpio_write_pin(void *cookie, int pin, int value)
298{
299	struct pchgpio_softc *sc = cookie;
300	struct pchgpio_group *group;
301	uint32_t reg;
302	uint16_t offset;
303	uint8_t bar;
304
305	group = pchgpio_find_group(sc, pin);
306	offset = group->offset + (pin - group->gpiobase);
307	bar = group->bar;
308
309	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
310	    sc->sc_padbar[bar] + offset * sc->sc_padsize);
311	if (value)
312		reg |= PCHGPIO_CONF_TXSTATE;
313	else
314		reg &= ~PCHGPIO_CONF_TXSTATE;
315	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
316	    sc->sc_padbar[bar] + offset * sc->sc_padsize, reg);
317}
318
319void
320pchgpio_intr_establish(void *cookie, int pin, int flags,
321    int (*func)(void *), void *arg)
322{
323	struct pchgpio_softc *sc = cookie;
324	struct pchgpio_group *group;
325	uint32_t reg;
326	uint16_t offset;
327	uint8_t bank, bar;
328
329	KASSERT(pin >= 0);
330
331	if ((group = pchgpio_find_group(sc, pin)) == NULL)
332		return;
333
334	offset = group->offset + (pin - group->gpiobase);
335	bar = group->bar;
336	bank = group->bank;
337
338	sc->sc_pin_ih[pin].ih_func = func;
339	sc->sc_pin_ih[pin].ih_arg = arg;
340
341	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
342	    sc->sc_padbar[bar] + offset * sc->sc_padsize);
343	reg &= ~(PCHGPIO_CONF_RXEV_MASK | PCHGPIO_CONF_RXINV);
344	if ((flags & LR_GPIO_MODE) == 1)
345		reg |= PCHGPIO_CONF_RXEV_EDGE;
346	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTLO)
347		reg |= PCHGPIO_CONF_RXINV;
348	if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTBOTH)
349		reg |= PCHGPIO_CONF_RXEV_EDGE | PCHGPIO_CONF_RXEV_ZERO;
350	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
351	    sc->sc_padbar[bar] + offset * sc->sc_padsize, reg);
352
353	reg = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
354	    sc->sc_device->gpi_ie + bank * 4);
355	reg |= (1 << (pin - group->gpiobase));
356	bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
357	    sc->sc_device->gpi_ie + bank * 4, reg);
358}
359
360int
361pchgpio_intr(void *arg)
362{
363	struct pchgpio_softc *sc = arg;
364	uint32_t status, enable;
365	int gpiobase, group, bit, pin, handled = 0;
366	uint16_t base, limit;
367	uint16_t offset;
368	uint8_t bank, bar;
369
370	for (group = 0; group < sc->sc_device->ngroups; group++) {
371		bar = sc->sc_device->groups[group].bar;
372		bank = sc->sc_device->groups[group].bank;
373		base = sc->sc_device->groups[group].base;
374		limit = sc->sc_device->groups[group].limit;
375		offset = sc->sc_device->groups[group].offset;
376		gpiobase = sc->sc_device->groups[group].gpiobase;
377
378		status = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
379		    sc->sc_device->gpi_is + bank * 4);
380		bus_space_write_4(sc->sc_memt[bar], sc->sc_memh[bar],
381		    sc->sc_device->gpi_is + bank * 4, status);
382		enable = bus_space_read_4(sc->sc_memt[bar], sc->sc_memh[bar],
383		    sc->sc_device->gpi_ie + bank * 4);
384		status &= enable;
385		if (status == 0)
386			continue;
387
388		for (bit = 0; bit <= (limit - base); bit++) {
389			pin = gpiobase + bit;
390			if (status & (1 << bit) && sc->sc_pin_ih[pin].ih_func)
391				sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
392			handled = 1;
393		}
394	}
395
396	return handled;
397}
398