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