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