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