xref: /freebsd/sys/dev/gpio/intel/intelgpio.c (revision 5349a46babc03453b0e6679bf867272e7ef7ea22)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2026 Beckhoff Automation GmbH & Co. KG
5  */
6 
7 #include <sys/param.h>
8 #include <sys/systm.h>
9 #include <sys/bus.h>
10 #include <sys/gpio.h>
11 #include <sys/kernel.h>
12 #include <sys/module.h>
13 #include <sys/rman.h>
14 
15 #include <machine/bus.h>
16 #include <machine/resource.h>
17 
18 #include <contrib/dev/acpica/include/acpi.h>
19 #include <contrib/dev/acpica/include/accommon.h>
20 
21 #include <dev/acpica/acpivar.h>
22 #include <dev/gpio/gpiobusvar.h>
23 
24 #include "intelgpio.h"
25 
26 #include "gpio_if.h"
27 #include "opt_acpi.h"
28 
29 #define INTELGPIO_LOCK(_sc)   mtx_lock_spin(&(_sc)->sc_mtx)
30 #define INTELGPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
31 #define INTELGPIO_LOCK_INIT(_sc)                                     \
32 	mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
33 	    "intelgpio", MTX_SPIN)
34 #define INTELGPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
35 
36 static int
intelgpio_gpio_to_pad(struct intelgpio_softc * sc,uint32_t pin,struct resource ** res,bus_size_t * pad_off,const struct intelgpio_padgroup ** pgp)37 intelgpio_gpio_to_pad(struct intelgpio_softc *sc, uint32_t pin,
38     struct resource **res, bus_size_t *pad_off,
39     const struct intelgpio_padgroup **pgp)
40 {
41 	const struct intelgpio_platform *plat = sc->sc_plat;
42 	const struct intelgpio_community *com;
43 	const struct intelgpio_padgroup *pg;
44 	int i, j, offset, local_pad;
45 
46 	for (i = 0; i < plat->ncommunities; i++) {
47 		com = &plat->communities[i];
48 		for (j = 0; j < com->ngroups; j++) {
49 			pg = &com->groups[j];
50 			if (pg->gpio_base == INTELGPIO_GPIO_NOMAP)
51 				continue;
52 			if (pin >= pg->gpio_base &&
53 			    pin < pg->gpio_base + pg->npads) {
54 				if (res != NULL)
55 					*res = sc->sc_mem_res[i];
56 				if (pad_off != NULL) {
57 					offset = pin - pg->gpio_base;
58 					local_pad = (pg->first_pad + offset) -
59 					    com->groups[0].first_pad;
60 					*pad_off = sc->sc_padbar[i] +
61 					    (local_pad * INTELGPIO_PAD_SIZE);
62 				}
63 				if (pgp != NULL)
64 					*pgp = pg;
65 				return (0);
66 			}
67 		}
68 	}
69 	return (-1);
70 }
71 
72 static device_t
intelgpio_get_bus(device_t dev)73 intelgpio_get_bus(device_t dev)
74 {
75 	struct intelgpio_softc *sc;
76 
77 	sc = device_get_softc(dev);
78 	return (sc->sc_busdev);
79 }
80 
81 static int
intelgpio_pin_max(device_t dev,int * maxpin)82 intelgpio_pin_max(device_t dev, int *maxpin)
83 {
84 	struct intelgpio_softc *sc;
85 	const struct intelgpio_platform *plat;
86 	const struct intelgpio_community *com;
87 	const struct intelgpio_padgroup *pg;
88 	int i, j;
89 
90 	if (maxpin == NULL)
91 		return (EINVAL);
92 
93 	sc = device_get_softc(dev);
94 	plat = sc->sc_plat;
95 
96 	/* Find the last group with a valid gpio_base, skipping NOMAP */
97 	*maxpin = 0;
98 	for (i = 0; i < plat->ncommunities; i++) {
99 		com = &plat->communities[i];
100 		for (j = 0; j < com->ngroups; j++) {
101 			pg = &com->groups[j];
102 			if (pg->gpio_base == INTELGPIO_GPIO_NOMAP)
103 				continue;
104 			*maxpin = pg->gpio_base + (pg->npads - 1);
105 		}
106 	}
107 	return (0);
108 }
109 
110 static int
intelgpio_pin_getname(device_t dev,uint32_t pin,char * name)111 intelgpio_pin_getname(device_t dev, uint32_t pin, char *name)
112 {
113 	struct intelgpio_softc *sc;
114 	const struct intelgpio_padgroup *pg;
115 	int offset;
116 
117 	sc = device_get_softc(dev);
118 	if (intelgpio_gpio_to_pad(sc, pin, NULL, NULL, &pg) != 0)
119 		return (EINVAL);
120 
121 	offset = pin - pg->gpio_base;
122 	snprintf(name, GPIOMAXNAME, "%s%d", pg->name, offset);
123 	return (0);
124 }
125 
126 static int
intelgpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)127 intelgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
128 {
129 	struct intelgpio_softc *sc;
130 
131 	sc = device_get_softc(dev);
132 
133 	/*
134 	 * The getcaps function is expected to return the capabilities
135 	 * of the pin. Validate that the pin number maps to an existing
136 	 * pad first.
137 	 */
138 	if (intelgpio_gpio_to_pad(sc, pin, NULL, NULL, NULL) != 0)
139 		return (EINVAL);
140 
141 	*caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
142 	return (0);
143 }
144 
145 static int
intelgpio_pin_getflags(device_t dev,uint32_t pin,uint32_t * flags)146 intelgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
147 {
148 	struct intelgpio_softc *sc;
149 	struct resource *res;
150 	bus_size_t pad_off;
151 	uint32_t val;
152 
153 	sc = device_get_softc(dev);
154 	if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
155 		return (EINVAL);
156 	if (res == NULL)
157 		return (EINVAL);
158 
159 	*flags = 0;
160 
161 	INTELGPIO_LOCK(sc);
162 	val = bus_read_4(res, pad_off);
163 	INTELGPIO_UNLOCK(sc);
164 
165 	/*
166 	 * If the pad is not in GPIO mode (i.e. configured for a native
167 	 * function like PCIe, UART, I2C, etc.), report no flags. The pin
168 	 * cannot be used as GPIO without first switching it via setflags.
169 	 */
170 	if ((val & INTELGPIO_PADCFG0_PMODE_MASK) !=
171 	    INTELGPIO_PADCFG0_PMODE_GPIO)
172 		return (0);
173 
174 	/*
175 	 * Report OUTPUT only if TX is explicitly enabled. Default to INPUT
176 	 * otherwise: a non-driving pin is considered an input from the user's
177 	 * perspective.
178 	 */
179 	if (!(val & INTELGPIO_PADCFG0_GPIOTXDIS))
180 		*flags = GPIO_PIN_OUTPUT;
181 	else
182 		*flags = GPIO_PIN_INPUT;
183 
184 	return (0);
185 }
186 
187 static int
intelgpio_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)188 intelgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
189 {
190 	struct intelgpio_softc *sc;
191 	struct resource *res;
192 	bus_size_t pad_off;
193 	uint32_t val;
194 
195 	sc = device_get_softc(dev);
196 	if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
197 		return (EINVAL);
198 	if (res == NULL)
199 		return (EINVAL);
200 
201 	/*
202 	 * We currently only support input and output, not both simultaneously
203 	 */
204 	if (flags & ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
205 		return (EINVAL);
206 	if ((flags & GPIO_PIN_INPUT) && (flags & GPIO_PIN_OUTPUT))
207 		return (EINVAL);
208 
209 	INTELGPIO_LOCK(sc);
210 	val = bus_read_4(res, pad_off);
211 
212 	/*
213 	 * Switch the pad to GPIO mode. Intel pads can operate in different
214 	 * modes (native function 1-15 or GPIO). Since setflags is only called
215 	 * when userspace or a consumer explicitly wants to use the pin as a
216 	 * GPIO, we force the pad into GPIO mode here so the direction bits
217 	 * (TXDIS/RXDIS) below take effect.
218 	 */
219 	val &= ~INTELGPIO_PADCFG0_PMODE_MASK;
220 	val |= INTELGPIO_PADCFG0_PMODE_GPIO;
221 
222 	if (flags & GPIO_PIN_INPUT)
223 		val &= ~INTELGPIO_PADCFG0_GPIORXDIS;
224 	else
225 		val |= INTELGPIO_PADCFG0_GPIORXDIS;
226 
227 	if (flags & GPIO_PIN_OUTPUT)
228 		val &= ~INTELGPIO_PADCFG0_GPIOTXDIS;
229 	else
230 		val |= INTELGPIO_PADCFG0_GPIOTXDIS;
231 
232 	bus_write_4(res, pad_off, val);
233 	INTELGPIO_UNLOCK(sc);
234 
235 	return (0);
236 }
237 
238 static int
intelgpio_pin_get(device_t dev,uint32_t pin,unsigned int * value)239 intelgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
240 {
241 	struct intelgpio_softc *sc;
242 	struct resource *res;
243 	bus_size_t pad_off;
244 	uint32_t val;
245 
246 	sc = device_get_softc(dev);
247 	if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
248 		return (EINVAL);
249 	if (res == NULL)
250 		return (EINVAL);
251 
252 	INTELGPIO_LOCK(sc);
253 	val = bus_read_4(res, pad_off);
254 	INTELGPIO_UNLOCK(sc);
255 
256 	*value = (val & INTELGPIO_PADCFG0_GPIORXSTATE) ?
257 	    GPIO_PIN_HIGH : GPIO_PIN_LOW;
258 
259 	return (0);
260 }
261 
262 static int
intelgpio_pin_set(device_t dev,uint32_t pin,unsigned int value)263 intelgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
264 {
265 	struct intelgpio_softc *sc;
266 	struct resource *res;
267 	bus_size_t pad_off;
268 	uint32_t val;
269 
270 	sc = device_get_softc(dev);
271 	if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
272 		return (EINVAL);
273 	if (res == NULL)
274 		return (EINVAL);
275 
276 	INTELGPIO_LOCK(sc);
277 	val = bus_read_4(res, pad_off);
278 	if (value == GPIO_PIN_LOW)
279 		val &= ~INTELGPIO_PADCFG0_GPIOTXSTATE;
280 	else
281 		val |= INTELGPIO_PADCFG0_GPIOTXSTATE;
282 	bus_write_4(res, pad_off, val);
283 	INTELGPIO_UNLOCK(sc);
284 
285 	return (0);
286 }
287 
288 static int
intelgpio_pin_toggle(device_t dev,uint32_t pin)289 intelgpio_pin_toggle(device_t dev, uint32_t pin)
290 {
291 	struct intelgpio_softc *sc;
292 	struct resource *res;
293 	bus_size_t pad_off;
294 	uint32_t val;
295 
296 	sc = device_get_softc(dev);
297 	if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
298 		return (EINVAL);
299 	if (res == NULL)
300 		return (EINVAL);
301 
302 	INTELGPIO_LOCK(sc);
303 	val = bus_read_4(res, pad_off);
304 	val ^= INTELGPIO_PADCFG0_GPIOTXSTATE;
305 	bus_write_4(res, pad_off, val);
306 	INTELGPIO_UNLOCK(sc);
307 
308 	return (0);
309 }
310 
311 int
intelgpio_probe(device_t dev,const struct intelgpio_platform * plat)312 intelgpio_probe(device_t dev, const struct intelgpio_platform *plat)
313 {
314 	int rv;
315 
316 	if (acpi_disabled("intelgpio"))
317 		return (ENXIO);
318 
319 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, plat->hids, NULL);
320 	if (rv <= 0)
321 		device_set_desc(dev, plat->desc);
322 	return (rv);
323 }
324 
325 static int
intelgpio_detach(device_t dev)326 intelgpio_detach(device_t dev)
327 {
328 	struct intelgpio_softc *sc;
329 	int i;
330 
331 	sc = device_get_softc(dev);
332 
333 	if (sc->sc_busdev != NULL)
334 		gpiobus_detach_bus(dev);
335 
336 	for (i = 0; i < sc->sc_plat->ncommunities; i++) {
337 		if (sc->sc_mem_res[i] != NULL)
338 			bus_release_resource(dev, SYS_RES_MEMORY, i,
339 			    sc->sc_mem_res[i]);
340 	}
341 
342 	INTELGPIO_LOCK_DESTROY(sc);
343 	return (0);
344 }
345 
346 int
intelgpio_attach(device_t dev,const struct intelgpio_platform * plat)347 intelgpio_attach(device_t dev, const struct intelgpio_platform *plat)
348 {
349 	struct intelgpio_softc *sc;
350 	bool have_res;
351 	int i;
352 
353 	sc = device_get_softc(dev);
354 	sc->sc_dev = dev;
355 	sc->sc_plat = plat;
356 
357 	KASSERT(plat->ncommunities <= INTELGPIO_MAX_COMMUNITIES,
358 	    ("too many communities: %d > %d", plat->ncommunities,
359 		INTELGPIO_MAX_COMMUNITIES));
360 
361 	INTELGPIO_LOCK_INIT(sc);
362 
363 	have_res = false;
364 	for (i = 0; i < plat->ncommunities; i++) {
365 		sc->sc_mem_res[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
366 		    &i, RF_ACTIVE);
367 		if (sc->sc_mem_res[i] == NULL) {
368 			device_printf(dev,
369 			    "failed to allocate memory resource "
370 			    "for community %d\n",
371 			    i);
372 			continue;
373 		}
374 		sc->sc_padbar[i] = bus_read_4(sc->sc_mem_res[i],
375 		    INTELGPIO_PADBAR_REG);
376 		have_res = true;
377 	}
378 
379 	if (!have_res) {
380 		device_printf(dev, "no memory resources found\n");
381 		INTELGPIO_LOCK_DESTROY(sc);
382 		return (ENXIO);
383 	}
384 
385 	sc->sc_busdev = gpiobus_add_bus(dev);
386 	if (sc->sc_busdev == NULL) {
387 		device_printf(dev, "failed to add gpiobus\n");
388 		intelgpio_detach(dev);
389 		return (ENXIO);
390 	}
391 
392 	bus_attach_children(dev);
393 
394 	return (0);
395 }
396 
397 
398 static device_method_t intelgpio_methods[] = {
399 	DEVMETHOD(device_detach, intelgpio_detach),
400 
401 	DEVMETHOD(gpio_get_bus, intelgpio_get_bus),
402 	DEVMETHOD(gpio_pin_max, intelgpio_pin_max),
403 	DEVMETHOD(gpio_pin_getname, intelgpio_pin_getname),
404 	DEVMETHOD(gpio_pin_getcaps, intelgpio_pin_getcaps),
405 	DEVMETHOD(gpio_pin_getflags, intelgpio_pin_getflags),
406 	DEVMETHOD(gpio_pin_setflags, intelgpio_pin_setflags),
407 	DEVMETHOD(gpio_pin_get, intelgpio_pin_get),
408 	DEVMETHOD(gpio_pin_set, intelgpio_pin_set),
409 	DEVMETHOD(gpio_pin_toggle, intelgpio_pin_toggle),
410 
411 	DEVMETHOD_END
412 };
413 
414 DEFINE_CLASS_0(intelgpio, intelgpio_driver, intelgpio_methods,
415     sizeof(struct intelgpio_softc));
416