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