xref: /freebsd/sys/dev/gpio/pl061.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 Amazon.com, Inc. or its affiliates.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/proc.h>
38 #include <sys/rman.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/gpio.h>
42 #include <sys/interrupt.h>
43 
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 #include <machine/resource.h>
47 
48 #include <dev/gpio/gpiobusvar.h>
49 
50 #include "pl061.h"
51 
52 #include "gpio_if.h"
53 #include "pic_if.h"
54 
55 #define	PL061_LOCK(_sc)			mtx_lock_spin(&(_sc)->sc_mtx)
56 #define	PL061_UNLOCK(_sc)		mtx_unlock_spin(&(_sc)->sc_mtx)
57 #define	PL061_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->sc_mtx)
58 #define	PL061_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
59 #define	PL061_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
60 
61 #if 0
62 #define dprintf(fmt, args...) do { 	\
63 	printf(fmt, ##args);	  	\
64 } while (0)
65 #else
66 #define dprintf(fmt, args...)
67 #endif
68 
69 #define PL061_PIN_TO_ADDR(pin)  (1 << (pin + 2))
70 #define PL061_DATA		0x3FC
71 #define PL061_DIR		0x400
72 #define PL061_INTSENSE  	0x404
73 #define PL061_INTBOTHEDGES	0x408
74 #define PL061_INTEVENT		0x40C
75 #define PL061_INTMASK		0x410
76 #define PL061_RAWSTATUS		0x414
77 #define PL061_STATUS		0x418
78 #define PL061_INTCLR		0x41C
79 #define PL061_MODECTRL		0x420
80 
81 #define PL061_ALLOWED_CAPS     (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_INTR_EDGE_BOTH | \
82 				GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | \
83 				GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW )
84 
85 #define PIC_INTR_ISRC(sc, irq) (&(sc->sc_isrcs[irq].isrc))
86 
87 static device_t
88 pl061_get_bus(device_t dev)
89 {
90 	struct pl061_softc *sc;
91 
92 	sc = device_get_softc(dev);
93 	return (sc->sc_busdev);
94 }
95 
96 static int
97 pl061_pin_max(device_t dev, int *maxpin)
98 {
99 	*maxpin = PL061_NUM_GPIO - 1;
100 	return (0);
101 }
102 
103 static int
104 pl061_pin_getname(device_t dev, uint32_t pin, char *name)
105 {
106 	if (pin >= PL061_NUM_GPIO)
107 		return (EINVAL);
108 
109 	snprintf(name, GPIOMAXNAME, "p%u", pin);
110 	name[GPIOMAXNAME - 1] = '\0';
111 
112 	return (0);
113 }
114 
115 static int
116 pl061_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
117 {
118 	struct pl061_softc *sc;
119 	uint8_t mask = 1 << pin;
120 
121 	sc = device_get_softc(dev);
122 	if (pin >= PL061_NUM_GPIO)
123 		return (EINVAL);
124 
125 	PL061_LOCK(sc);
126 	*flags = 0;
127 
128 	if (mask & bus_read_1(sc->sc_mem_res, PL061_DIR))
129 		*flags |= GPIO_PIN_OUTPUT;
130 	else
131 		*flags |= GPIO_PIN_INPUT;
132 
133 	PL061_UNLOCK(sc);
134 	return (0);
135 }
136 
137 static int
138 pl061_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
139 {
140 	if (pin >= PL061_NUM_GPIO)
141 		return (EINVAL);
142 
143 	*caps = PL061_ALLOWED_CAPS;
144 
145 	return (0);
146 }
147 
148 static void
149 mask_and_set(struct pl061_softc *sc, long a, uint8_t m, uint8_t b)
150 {
151 	uint8_t tmp;
152 
153 	tmp = bus_read_1(sc->sc_mem_res, a);
154 	tmp &= ~m;
155 	tmp |= b;
156 	bus_write_1(sc->sc_mem_res, a, tmp);
157 	dprintf("%s: writing %#x to register %#lx\n", __func__, tmp, a);
158 }
159 
160 static int
161 pl061_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
162 {
163 	struct pl061_softc *sc;
164 	uint8_t mask = 1 << pin;
165 	const uint32_t in_out = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
166 
167 	sc = device_get_softc(dev);
168 	if (pin >= PL061_NUM_GPIO)
169 		return (EINVAL);
170 
171 	if (flags & ~PL061_ALLOWED_CAPS)
172 		return (EINVAL);
173 
174 	/* can't be both input and output */
175 	if ((flags & in_out) == in_out)
176 		return (EINVAL);
177 
178 
179 	PL061_LOCK(sc);
180 	mask_and_set(sc, PL061_DIR, mask, flags & GPIO_PIN_OUTPUT ? mask : 0);
181 	PL061_UNLOCK(sc);
182 	return (0);
183 }
184 
185 static int
186 pl061_pin_get(device_t dev, uint32_t pin, uint32_t *value)
187 {
188 	struct pl061_softc *sc;
189 
190 	sc = device_get_softc(dev);
191 	if (pin >= PL061_NUM_GPIO)
192 		return (EINVAL);
193 
194 	PL061_LOCK(sc);
195 	if (bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin)))
196 		*value = GPIO_PIN_HIGH;
197 	else
198 		*value = GPIO_PIN_LOW;
199 	PL061_UNLOCK(sc);
200 
201 	return (0);
202 }
203 
204 static int
205 pl061_pin_set(device_t dev, uint32_t pin, uint32_t value)
206 {
207 	struct pl061_softc *sc;
208 	uint8_t d = (value == GPIO_PIN_HIGH) ? 0xff : 0x00;
209 
210 	sc = device_get_softc(dev);
211 	if (pin >= PL061_NUM_GPIO)
212 		return (EINVAL);
213 
214 	PL061_LOCK(sc);
215 	bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
216 	PL061_UNLOCK(sc);
217 
218 	return (0);
219 }
220 
221 static int
222 pl061_pin_toggle(device_t dev, uint32_t pin)
223 {
224 	struct pl061_softc *sc;
225 	uint8_t d;
226 
227 	sc = device_get_softc(dev);
228 	if (pin >= PL061_NUM_GPIO)
229 		return (EINVAL);
230 
231 	PL061_LOCK(sc);
232 	d = ~bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin));
233 	bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
234 	PL061_UNLOCK(sc);
235 
236 	return (0);
237 }
238 
239 static void
240 pl061_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
241 {
242 	struct pl061_softc *sc;
243 	uint8_t mask;
244 
245 	sc = device_get_softc(dev);
246 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
247 
248 	dprintf("%s: calling disable interrupt %#x\n", __func__, mask);
249 	PL061_LOCK(sc);
250 	mask_and_set(sc, PL061_INTMASK, mask, 0);
251 	PL061_UNLOCK(sc);
252 }
253 
254 
255 
256 static void
257 pl061_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
258 {
259 	struct pl061_softc *sc;
260 	uint8_t mask;
261 
262 	sc = device_get_softc(dev);
263 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
264 
265 
266 	dprintf("%s: calling enable interrupt %#x\n", __func__, mask);
267 	PL061_LOCK(sc);
268 	mask_and_set(sc, PL061_INTMASK, mask, mask);
269 	PL061_UNLOCK(sc);
270 }
271 
272 static int
273 pl061_pic_map_intr(device_t dev, struct intr_map_data *data,
274 	struct intr_irqsrc **isrcp)
275 {
276 	struct pl061_softc *sc;
277 	struct intr_map_data_gpio *gdata;
278 	uint32_t irq;
279 
280 	sc = device_get_softc(dev);
281 	if (data->type != INTR_MAP_DATA_GPIO)
282 		return (ENOTSUP);
283 
284 	gdata = (struct intr_map_data_gpio *)data;
285 	irq = gdata->gpio_pin_num;
286 	if (irq >= PL061_NUM_GPIO) {
287 		device_printf(dev, "invalid interrupt number %u\n", irq);
288 		return (EINVAL);
289 	}
290 
291 	dprintf("%s: calling map interrupt %u\n", __func__, irq);
292 	*isrcp = PIC_INTR_ISRC(sc, irq);
293 
294 	return (0);
295 }
296 
297 static int
298 pl061_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
299 	struct resource *res, struct intr_map_data *data)
300 {
301 	struct pl061_softc *sc;
302 	struct intr_map_data_gpio *gdata;
303 	struct pl061_pin_irqsrc *irqsrc;
304 	uint32_t mode;
305 	uint8_t mask;
306 
307 	if (data == NULL)
308 		return (ENOTSUP);
309 
310 	sc = device_get_softc(dev);
311 	gdata = (struct intr_map_data_gpio *)data;
312 	irqsrc = (struct pl061_pin_irqsrc *)isrc;
313 
314 	mode = gdata->gpio_intr_mode;
315 	mask = 1 << gdata->gpio_pin_num;
316 
317 	dprintf("%s: calling setup interrupt %u mode %#x\n", __func__,
318 	    irqsrc->irq, mode);
319 	if (irqsrc->irq != gdata->gpio_pin_num) {
320 		dprintf("%s: interrupts don't match\n", __func__);
321 		return (EINVAL);
322 	}
323 
324 	if (isrc->isrc_handlers != 0) {
325 		dprintf("%s: handler already attached\n", __func__);
326 		return (irqsrc->mode == mode ? 0 : EINVAL);
327 	}
328 	irqsrc->mode = mode;
329 
330 	PL061_LOCK(sc);
331 
332 	if (mode & GPIO_INTR_EDGE_BOTH) {
333 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, mask);
334 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
335 	} else if (mode & GPIO_INTR_EDGE_RISING) {
336 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
337 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
338 		mask_and_set(sc, PL061_INTEVENT, mask, mask);
339 	} else if (mode & GPIO_INTR_EDGE_FALLING) {
340 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
341 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
342 		mask_and_set(sc, PL061_INTEVENT, mask, 0);
343 	} else if (mode & GPIO_INTR_LEVEL_HIGH) {
344 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
345 		mask_and_set(sc, PL061_INTSENSE, mask, mask);
346 		mask_and_set(sc, PL061_INTEVENT, mask, mask);
347 	} else if (mode & GPIO_INTR_LEVEL_LOW) {
348 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
349 		mask_and_set(sc, PL061_INTSENSE, mask, mask);
350 		mask_and_set(sc, PL061_INTEVENT, mask, 0);
351 	}
352 	PL061_UNLOCK(sc);
353 	return (0);
354 }
355 
356 static int
357 pl061_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
358 	struct resource *res, struct intr_map_data *data)
359 {
360 	struct pl061_softc *sc;
361 	struct pl061_pin_irqsrc *irqsrc;
362 	uint8_t mask;
363 
364 	irqsrc = (struct pl061_pin_irqsrc *)isrc;
365 	mask = 1 << irqsrc->irq;
366 	dprintf("%s: calling teardown interrupt %#x\n", __func__, mask);
367 
368 	sc = device_get_softc(dev);
369 	if (isrc->isrc_handlers == 0) {
370 		irqsrc->mode = GPIO_INTR_CONFORM;
371 		PL061_LOCK(sc);
372 		mask_and_set(sc, PL061_INTMASK, mask, 0);
373 		PL061_UNLOCK(sc);
374 	}
375 	return (0);
376 }
377 
378 static void
379 pl061_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
380 {
381 	struct pl061_softc *sc;
382 	uint8_t mask;
383 
384 	sc = device_get_softc(dev);
385 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
386 	dprintf("%s: calling post filter %#x\n", __func__, mask);
387 
388 	bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
389 }
390 
391 static void
392 pl061_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
393 {
394 	struct pl061_softc *sc;
395 	uint8_t mask;
396 
397 	sc = device_get_softc(dev);
398 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
399 	dprintf("%s: calling post ithread %#x\n", __func__, mask);
400 	bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
401 
402 	pl061_pic_enable_intr(dev, isrc);
403 }
404 
405 static void
406 pl061_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
407 {
408 	pl061_pic_disable_intr(dev, isrc);
409 }
410 
411 static int
412 pl061_intr(void *arg)
413 {
414 	struct pl061_softc *sc;
415 	struct trapframe *tf;
416 	uint8_t status;
417 	int pin;
418 
419 	sc = (struct pl061_softc *)arg;
420 	tf = curthread->td_intr_frame;
421 
422 	status = bus_read_1(sc->sc_mem_res, PL061_STATUS);
423 
424 	while (status != 0) {
425 		pin = ffs(status) - 1;
426 		status &= ~(1 << pin);
427 
428 		if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, pin), tf) != 0)
429 			device_printf(sc->sc_dev, "spurious interrupt %d\n",
430 			    pin);
431 
432 		dprintf("got IRQ on %d\n", pin);
433 
434 	}
435 	return (FILTER_HANDLED);
436 }
437 
438 int
439 pl061_attach(device_t dev)
440 {
441 	struct pl061_softc *sc;
442 	int ret;
443 	int irq;
444 	const char *name;
445 
446 	sc = device_get_softc(dev);
447 	sc->sc_dev = dev;
448 
449 	sc->sc_mem_rid = 0;
450 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
451 	    &sc->sc_mem_rid, RF_ACTIVE);
452 	if (sc->sc_mem_res == NULL) {
453 		device_printf(dev, "can't allocate memory resource\n");
454 		return (ENXIO);
455 	}
456 
457 	sc->sc_irq_rid = 0;
458 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
459 	    &sc->sc_irq_rid, RF_ACTIVE);
460 
461 	if (sc->sc_irq_res == NULL) {
462 		device_printf(dev, "can't allocate IRQ resource\n");
463 		goto free_mem;
464 	}
465 
466 	ret = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
467 	    pl061_intr, NULL, sc, &sc->sc_irq_hdlr);
468 	if (ret) {
469 		device_printf(dev, "can't setup IRQ\n");
470 		goto free_pic;
471 	}
472 
473 	name = device_get_nameunit(dev);
474 
475 	for (irq = 0; irq < PL061_NUM_GPIO; irq++) {
476 		if (bootverbose) {
477 			device_printf(dev,
478 			    "trying to register pin %d name %s\n", irq, name);
479 		}
480 		sc->sc_isrcs[irq].irq = irq;
481 		sc->sc_isrcs[irq].mode = GPIO_INTR_CONFORM;
482 		ret = intr_isrc_register(PIC_INTR_ISRC(sc, irq), dev, 0,
483 		    "%s", name);
484 		if (ret) {
485 			device_printf(dev, "can't register isrc %d\n", ret);
486 			goto free_isrc;
487 		}
488 	}
489 
490 	sc->sc_busdev = gpiobus_attach_bus(dev);
491 	if (sc->sc_busdev == NULL) {
492 		device_printf(dev, "couldn't attach gpio bus\n");
493 		goto free_isrc;
494 	}
495 
496 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "pl061", MTX_SPIN);
497 
498 	return (0);
499 
500 free_isrc:
501 	/*
502 	 * XXX isrc_release_counters() not implemented
503 	 * for (irq = 0; irq < PL061_NUM_GPIO; irq++)
504 	 *	intr_isrc_deregister(PIC_INTR_ISRC(sc, irq));
505 	*/
506 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
507 	    sc->sc_irq_res);
508 free_pic:
509         /*
510 	 * XXX intr_pic_deregister: not implemented
511          * intr_pic_deregister(dev, 0);
512          */
513 
514 free_mem:
515 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
516 	    sc->sc_mem_res);
517 
518 	return (ENXIO);
519 
520 }
521 
522 int
523 pl061_detach(device_t dev)
524 {
525 	struct pl061_softc *sc;
526 	sc = device_get_softc(dev);
527 
528 	if (sc->sc_busdev)
529 		gpiobus_detach_bus(dev);
530 
531 	if (sc->sc_irq_hdlr != NULL)
532 		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdlr);
533 
534 	if (sc->sc_irq_res != NULL)
535 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
536 		    sc->sc_irq_res);
537 
538 	if (sc->sc_mem_res != NULL)
539 		bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
540 		    sc->sc_mem_res);
541 	PL061_LOCK_DESTROY(sc);
542 	return (0);
543 }
544 
545 static device_method_t pl061_methods[] = {
546 	/* Device interface */
547 	DEVMETHOD(device_attach,	pl061_attach),
548 	DEVMETHOD(device_detach,	pl061_detach),
549 
550 	/* Bus interface */
551 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
552 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
553 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
554 
555 	/* GPIO protocol */
556 	DEVMETHOD(gpio_get_bus,		pl061_get_bus),
557 	DEVMETHOD(gpio_pin_max,		pl061_pin_max),
558 	DEVMETHOD(gpio_pin_getname,	pl061_pin_getname),
559 	DEVMETHOD(gpio_pin_getflags,	pl061_pin_getflags),
560 	DEVMETHOD(gpio_pin_getcaps,	pl061_pin_getcaps),
561 	DEVMETHOD(gpio_pin_setflags,	pl061_pin_setflags),
562 	DEVMETHOD(gpio_pin_get,		pl061_pin_get),
563 	DEVMETHOD(gpio_pin_set,		pl061_pin_set),
564 	DEVMETHOD(gpio_pin_toggle,	pl061_pin_toggle),
565 
566 	/* Interrupt controller interface */
567 	DEVMETHOD(pic_disable_intr,	pl061_pic_disable_intr),
568 	DEVMETHOD(pic_enable_intr,	pl061_pic_enable_intr),
569 	DEVMETHOD(pic_map_intr,		pl061_pic_map_intr),
570 	DEVMETHOD(pic_setup_intr,	pl061_pic_setup_intr),
571 	DEVMETHOD(pic_teardown_intr,	pl061_pic_teardown_intr),
572 	DEVMETHOD(pic_post_filter,	pl061_pic_post_filter),
573 	DEVMETHOD(pic_post_ithread,	pl061_pic_post_ithread),
574 	DEVMETHOD(pic_pre_ithread,	pl061_pic_pre_ithread),
575 
576 	DEVMETHOD_END
577 };
578 
579 DEFINE_CLASS_0(gpio, pl061_driver, pl061_methods, sizeof(struct pl061_softc));
580