xref: /freebsd/sys/dev/gpio/pl061.c (revision c1d255d3)
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 	struct pl061_softc *sc;
107 
108 	sc = device_get_softc(dev);
109 	if (pin >= PL061_NUM_GPIO)
110 		return (EINVAL);
111 
112 	snprintf(name, GPIOMAXNAME, "p%u", pin);
113 	name[GPIOMAXNAME - 1] = '\0';
114 
115 	return (0);
116 }
117 
118 static int
119 pl061_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
120 {
121 	struct pl061_softc *sc;
122 	uint8_t mask = 1 << pin;
123 
124 	sc = device_get_softc(dev);
125 	if (pin >= PL061_NUM_GPIO)
126 		return (EINVAL);
127 
128 	PL061_LOCK(sc);
129 	*flags = 0;
130 
131 	if (mask & bus_read_1(sc->sc_mem_res, PL061_DIR))
132 		*flags |= GPIO_PIN_OUTPUT;
133 	else
134 		*flags |= GPIO_PIN_INPUT;
135 
136 	PL061_UNLOCK(sc);
137 	return (0);
138 }
139 
140 static int
141 pl061_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
142 {
143 	struct pl061_softc *sc;
144 
145 	sc = device_get_softc(dev);
146 	if (pin >= PL061_NUM_GPIO)
147 		return (EINVAL);
148 
149 	*caps = PL061_ALLOWED_CAPS;
150 
151 	return (0);
152 }
153 
154 static void
155 mask_and_set(struct pl061_softc *sc, long a, uint8_t m, uint8_t b)
156 {
157 	uint8_t tmp;
158 
159 	tmp = bus_read_1(sc->sc_mem_res, a);
160 	tmp &= ~m;
161 	tmp |= b;
162 	bus_write_1(sc->sc_mem_res, a, tmp);
163 	dprintf("%s: writing %#x to register %#lx\n", __func__, tmp, a);
164 }
165 
166 static int
167 pl061_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
168 {
169 	struct pl061_softc *sc;
170 	uint8_t mask = 1 << pin;
171 	const uint32_t in_out = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
172 
173 	sc = device_get_softc(dev);
174 	if (pin >= PL061_NUM_GPIO)
175 		return (EINVAL);
176 
177 	if (flags & ~PL061_ALLOWED_CAPS)
178 		return (EINVAL);
179 
180 	/* can't be both input and output */
181 	if ((flags & in_out) == in_out)
182 		return (EINVAL);
183 
184 
185 	PL061_LOCK(sc);
186 	mask_and_set(sc, PL061_DIR, mask, flags & GPIO_PIN_OUTPUT ? mask : 0);
187 	PL061_UNLOCK(sc);
188 	return (0);
189 }
190 
191 static int
192 pl061_pin_get(device_t dev, uint32_t pin, uint32_t *value)
193 {
194 	struct pl061_softc *sc;
195 
196 	sc = device_get_softc(dev);
197 	if (pin >= PL061_NUM_GPIO)
198 		return (EINVAL);
199 
200 	PL061_LOCK(sc);
201 	if (bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin)))
202 		*value = GPIO_PIN_HIGH;
203 	else
204 		*value = GPIO_PIN_LOW;
205 	PL061_UNLOCK(sc);
206 
207 	return (0);
208 }
209 
210 static int
211 pl061_pin_set(device_t dev, uint32_t pin, uint32_t value)
212 {
213 	struct pl061_softc *sc;
214 	uint8_t d = (value == GPIO_PIN_HIGH) ? 0xff : 0x00;
215 
216 	sc = device_get_softc(dev);
217 	if (pin >= PL061_NUM_GPIO)
218 		return (EINVAL);
219 
220 	PL061_LOCK(sc);
221 	bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
222 	PL061_UNLOCK(sc);
223 
224 	return (0);
225 }
226 
227 static int
228 pl061_pin_toggle(device_t dev, uint32_t pin)
229 {
230 	struct pl061_softc *sc;
231 	uint8_t d;
232 
233 	sc = device_get_softc(dev);
234 	if (pin >= PL061_NUM_GPIO)
235 		return (EINVAL);
236 
237 	PL061_LOCK(sc);
238 	d = ~bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin));
239 	bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
240 	PL061_UNLOCK(sc);
241 
242 	return (0);
243 }
244 
245 static void
246 pl061_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
247 {
248 	struct pl061_softc *sc;
249 	uint8_t mask;
250 
251 	sc = device_get_softc(dev);
252 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
253 
254 	dprintf("%s: calling disable interrupt %#x\n", __func__, mask);
255 	PL061_LOCK(sc);
256 	mask_and_set(sc, PL061_INTMASK, mask, 0);
257 	PL061_UNLOCK(sc);
258 }
259 
260 
261 
262 static void
263 pl061_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
264 {
265 	struct pl061_softc *sc;
266 	uint8_t mask;
267 
268 	sc = device_get_softc(dev);
269 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
270 
271 
272 	dprintf("%s: calling enable interrupt %#x\n", __func__, mask);
273 	PL061_LOCK(sc);
274 	mask_and_set(sc, PL061_INTMASK, mask, mask);
275 	PL061_UNLOCK(sc);
276 }
277 
278 static int
279 pl061_pic_map_intr(device_t dev, struct intr_map_data *data,
280 	struct intr_irqsrc **isrcp)
281 {
282 	struct pl061_softc *sc;
283 	struct intr_map_data_gpio *gdata;
284 	uint32_t irq;
285 
286 	sc = device_get_softc(dev);
287 	if (data->type != INTR_MAP_DATA_GPIO)
288 		return (ENOTSUP);
289 
290 	gdata = (struct intr_map_data_gpio *)data;
291 	irq = gdata->gpio_pin_num;
292 	if (irq >= PL061_NUM_GPIO) {
293 		device_printf(dev, "invalid interrupt number %u\n", irq);
294 		return (EINVAL);
295 	}
296 
297 	dprintf("%s: calling map interrupt %u\n", __func__, irq);
298 	*isrcp = PIC_INTR_ISRC(sc, irq);
299 
300 	return (0);
301 }
302 
303 static int
304 pl061_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
305 	struct resource *res, struct intr_map_data *data)
306 {
307 	struct pl061_softc *sc;
308 	struct intr_map_data_gpio *gdata;
309 	struct pl061_pin_irqsrc *irqsrc;
310 	uint32_t mode;
311 	uint8_t mask;
312 
313 	if (data == NULL)
314 		return (ENOTSUP);
315 
316 	sc = device_get_softc(dev);
317 	gdata = (struct intr_map_data_gpio *)data;
318 	irqsrc = (struct pl061_pin_irqsrc *)isrc;
319 
320 	mode = gdata->gpio_intr_mode;
321 	mask = 1 << gdata->gpio_pin_num;
322 
323 	dprintf("%s: calling setup interrupt %u mode %#x\n", __func__,
324 	    irqsrc->irq, mode);
325 	if (irqsrc->irq != gdata->gpio_pin_num) {
326 		dprintf("%s: interrupts don't match\n", __func__);
327 		return (EINVAL);
328 	}
329 
330 	if (isrc->isrc_handlers != 0) {
331 		dprintf("%s: handler already attached\n", __func__);
332 		return (irqsrc->mode == mode ? 0 : EINVAL);
333 	}
334 	irqsrc->mode = mode;
335 
336 	PL061_LOCK(sc);
337 
338 	if (mode & GPIO_INTR_EDGE_BOTH) {
339 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, mask);
340 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
341 	} else if (mode & GPIO_INTR_EDGE_RISING) {
342 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
343 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
344 		mask_and_set(sc, PL061_INTEVENT, mask, mask);
345 	} else if (mode & GPIO_INTR_EDGE_FALLING) {
346 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
347 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
348 		mask_and_set(sc, PL061_INTEVENT, mask, 0);
349 	} else if (mode & GPIO_INTR_LEVEL_HIGH) {
350 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
351 		mask_and_set(sc, PL061_INTSENSE, mask, mask);
352 		mask_and_set(sc, PL061_INTEVENT, mask, mask);
353 	} else if (mode & GPIO_INTR_LEVEL_LOW) {
354 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
355 		mask_and_set(sc, PL061_INTSENSE, mask, mask);
356 		mask_and_set(sc, PL061_INTEVENT, mask, 0);
357 	}
358 	PL061_UNLOCK(sc);
359 	return (0);
360 }
361 
362 static int
363 pl061_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
364 	struct resource *res, struct intr_map_data *data)
365 {
366 	struct pl061_softc *sc;
367 	struct pl061_pin_irqsrc *irqsrc;
368 	uint8_t mask;
369 
370 	irqsrc = (struct pl061_pin_irqsrc *)isrc;
371 	mask = 1 << irqsrc->irq;
372 	dprintf("%s: calling teardown interrupt %#x\n", __func__, mask);
373 
374 	sc = device_get_softc(dev);
375 	if (isrc->isrc_handlers == 0) {
376 		irqsrc->mode = GPIO_INTR_CONFORM;
377 		PL061_LOCK(sc);
378 		mask_and_set(sc, PL061_INTMASK, mask, 0);
379 		PL061_UNLOCK(sc);
380 	}
381 	return (0);
382 }
383 
384 static void
385 pl061_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
386 {
387 	struct pl061_softc *sc;
388 	uint8_t mask;
389 
390 	sc = device_get_softc(dev);
391 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
392 	dprintf("%s: calling post filter %#x\n", __func__, mask);
393 
394 	bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
395 }
396 
397 static void
398 pl061_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
399 {
400 	struct pl061_softc *sc;
401 	uint8_t mask;
402 
403 	sc = device_get_softc(dev);
404 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
405 	dprintf("%s: calling post ithread %#x\n", __func__, mask);
406 	bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
407 
408 	pl061_pic_enable_intr(dev, isrc);
409 }
410 
411 static void
412 pl061_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
413 {
414 	pl061_pic_disable_intr(dev, isrc);
415 }
416 
417 static int
418 pl061_intr(void *arg)
419 {
420 	struct pl061_softc *sc;
421 	struct trapframe *tf;
422 	uint8_t status;
423 	int pin;
424 
425 	sc = (struct pl061_softc *)arg;
426 	tf = curthread->td_intr_frame;
427 
428 	status = bus_read_1(sc->sc_mem_res, PL061_STATUS);
429 
430 	while (status != 0) {
431 		pin = ffs(status) - 1;
432 		status &= ~(1 << pin);
433 
434 		if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, pin), tf) != 0)
435 			device_printf(sc->sc_dev, "spurious interrupt %d\n",
436 			    pin);
437 
438 		dprintf("got IRQ on %d\n", pin);
439 
440 	}
441 	return (FILTER_HANDLED);
442 }
443 
444 int
445 pl061_attach(device_t dev)
446 {
447 	struct pl061_softc *sc;
448 	int ret;
449 	int irq;
450 	const char *name;
451 
452 	sc = device_get_softc(dev);
453 	sc->sc_dev = dev;
454 
455 	sc->sc_mem_rid = 0;
456 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
457 	    &sc->sc_mem_rid, RF_ACTIVE);
458 	if (sc->sc_mem_res == NULL) {
459 		device_printf(dev, "can't allocate memory resource\n");
460 		return (ENXIO);
461 	}
462 
463 	sc->sc_irq_rid = 0;
464 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
465 	    &sc->sc_irq_rid, RF_ACTIVE);
466 
467 	if (sc->sc_irq_res == NULL) {
468 		device_printf(dev, "can't allocate IRQ resource\n");
469 		goto free_mem;
470 	}
471 
472 	ret = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
473 	    pl061_intr, NULL, sc, &sc->sc_irq_hdlr);
474 	if (ret) {
475 		device_printf(dev, "can't setup IRQ\n");
476 		goto free_pic;
477 	}
478 
479 	name = device_get_nameunit(dev);
480 
481 	for (irq = 0; irq < PL061_NUM_GPIO; irq++) {
482 		if (bootverbose) {
483 			device_printf(dev,
484 			    "trying to register pin %d name %s\n", irq, name);
485 		}
486 		sc->sc_isrcs[irq].irq = irq;
487 		sc->sc_isrcs[irq].mode = GPIO_INTR_CONFORM;
488 		ret = intr_isrc_register(PIC_INTR_ISRC(sc, irq), dev, 0,
489 		    "%s", name);
490 		if (ret) {
491 			device_printf(dev, "can't register isrc %d\n", ret);
492 			goto free_isrc;
493 		}
494 	}
495 
496 	sc->sc_busdev = gpiobus_attach_bus(dev);
497 	if (sc->sc_busdev == NULL) {
498 		device_printf(dev, "couldn't attach gpio bus\n");
499 		goto free_isrc;
500 	}
501 
502 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "pl061", MTX_SPIN);
503 
504 	return (0);
505 
506 free_isrc:
507 	/*
508 	 * XXX isrc_release_counters() not implemented
509 	 * for (irq = 0; irq < PL061_NUM_GPIO; irq++)
510 	 *	intr_isrc_deregister(PIC_INTR_ISRC(sc, irq));
511 	*/
512 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
513 	    sc->sc_irq_res);
514 free_pic:
515         /*
516 	 * XXX intr_pic_deregister: not implemented
517          * intr_pic_deregister(dev, 0);
518          */
519 
520 free_mem:
521 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
522 	    sc->sc_mem_res);
523 
524 	return (ENXIO);
525 
526 }
527 
528 int
529 pl061_detach(device_t dev)
530 {
531 	struct pl061_softc *sc;
532 	sc = device_get_softc(dev);
533 
534 	if (sc->sc_busdev)
535 		gpiobus_detach_bus(dev);
536 
537 	if (sc->sc_irq_hdlr != NULL)
538 		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdlr);
539 
540 	if (sc->sc_irq_res != NULL)
541 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
542 		    sc->sc_irq_res);
543 
544 	if (sc->sc_mem_res != NULL)
545 		bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
546 		    sc->sc_mem_res);
547 	PL061_LOCK_DESTROY(sc);
548 	return (0);
549 }
550 
551 static device_method_t pl061_methods[] = {
552 	/* Device interface */
553 	DEVMETHOD(device_attach,	pl061_attach),
554 	DEVMETHOD(device_detach,	pl061_detach),
555 
556 	/* Bus interface */
557 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
558 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
559 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
560 
561 	/* GPIO protocol */
562 	DEVMETHOD(gpio_get_bus,		pl061_get_bus),
563 	DEVMETHOD(gpio_pin_max,		pl061_pin_max),
564 	DEVMETHOD(gpio_pin_getname,	pl061_pin_getname),
565 	DEVMETHOD(gpio_pin_getflags,	pl061_pin_getflags),
566 	DEVMETHOD(gpio_pin_getcaps,	pl061_pin_getcaps),
567 	DEVMETHOD(gpio_pin_setflags,	pl061_pin_setflags),
568 	DEVMETHOD(gpio_pin_get,		pl061_pin_get),
569 	DEVMETHOD(gpio_pin_set,		pl061_pin_set),
570 	DEVMETHOD(gpio_pin_toggle,	pl061_pin_toggle),
571 
572 	/* Interrupt controller interface */
573 	DEVMETHOD(pic_disable_intr,	pl061_pic_disable_intr),
574 	DEVMETHOD(pic_enable_intr,	pl061_pic_enable_intr),
575 	DEVMETHOD(pic_map_intr,		pl061_pic_map_intr),
576 	DEVMETHOD(pic_setup_intr,	pl061_pic_setup_intr),
577 	DEVMETHOD(pic_teardown_intr,	pl061_pic_teardown_intr),
578 	DEVMETHOD(pic_post_filter,	pl061_pic_post_filter),
579 	DEVMETHOD(pic_post_ithread,	pl061_pic_post_ithread),
580 	DEVMETHOD(pic_pre_ithread,	pl061_pic_pre_ithread),
581 
582 	DEVMETHOD_END
583 };
584 
585 DEFINE_CLASS_0(gpio, pl061_driver, pl061_methods, sizeof(struct pl061_softc));
586