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
84static device_t
85pl061_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
93static int
94pl061_pin_max(device_t dev, int *maxpin)
95{
96	*maxpin = PL061_NUM_GPIO - 1;
97	return (0);
98}
99
100static int
101pl061_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
112static int
113pl061_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
134static int
135pl061_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
145static void
146mask_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
157static int
158pl061_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
182static int
183pl061_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
201static int
202pl061_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
218static int
219pl061_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
236static void
237pl061_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
253static void
254pl061_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
269static int
270pl061_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
294static int
295pl061_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
353static int
354pl061_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
375static void
376pl061_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
388static void
389pl061_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
402static void
403pl061_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
404{
405	pl061_pic_disable_intr(dev, isrc);
406}
407
408static int
409pl061_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
435int
436pl061_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
497free_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);
505free_pic:
506        /*
507	 * XXX intr_pic_deregister: not implemented
508         * intr_pic_deregister(dev, 0);
509         */
510
511free_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
519int
520pl061_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
542static 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
576DEFINE_CLASS_0(gpio, pl061_driver, pl061_methods, sizeof(struct pl061_softc));
577