1/*-
2 * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org>
3 * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29#include "opt_platform.h"
30
31#include <sys/param.h>
32#include <sys/bus.h>
33#include <sys/cpuset.h>
34#include <sys/kernel.h>
35#include <sys/ktr.h>
36#include <sys/lock.h>
37#include <sys/module.h>
38#include <sys/mutex.h>
39#include <sys/param.h>
40#include <sys/pcpu.h>
41#include <sys/proc.h>
42#include <sys/rman.h>
43#include <sys/smp.h>
44#include <sys/systm.h>
45#include <sys/sched.h>
46#include <machine/bus.h>
47#include <machine/intr.h>
48
49#include <dev/ofw/openfirm.h>
50#include <dev/ofw/ofw_bus.h>
51#include <dev/ofw/ofw_bus_subr.h>
52
53#include "pic_if.h"
54
55/**
56 * Interrupt controller registers
57 *
58 */
59#define	SW_INT_VECTOR_REG		0x00
60#define	SW_INT_BASE_ADR_REG		0x04
61#define	SW_INT_PROTECTION_REG		0x08
62#define	SW_INT_NMI_CTRL_REG		0x0c
63
64#define	SW_INT_IRQ_PENDING_REG0		0x10
65#define	SW_INT_IRQ_PENDING_REG1		0x14
66#define	SW_INT_IRQ_PENDING_REG2		0x18
67
68#define	SW_INT_FIQ_PENDING_REG0		0x20
69#define	SW_INT_FIQ_PENDING_REG1		0x24
70#define	SW_INT_FIQ_PENDING_REG2		0x28
71
72#define	SW_INT_SELECT_REG0		0x30
73#define	SW_INT_SELECT_REG1		0x34
74#define	SW_INT_SELECT_REG2		0x38
75
76#define	SW_INT_ENABLE_REG0		0x40
77#define	SW_INT_ENABLE_REG1		0x44
78#define	SW_INT_ENABLE_REG2		0x48
79
80#define	SW_INT_MASK_REG0		0x50
81#define	SW_INT_MASK_REG1		0x54
82#define	SW_INT_MASK_REG2		0x58
83
84#define	SW_INT_IRQNO_ENMI		0
85
86#define	A10_INTR_MAX_NIRQS		81
87
88#define	SW_INT_IRQ_PENDING_REG(_b)	(0x10 + ((_b) * 4))
89#define	SW_INT_FIQ_PENDING_REG(_b)	(0x20 + ((_b) * 4))
90#define	SW_INT_SELECT_REG(_b)		(0x30 + ((_b) * 4))
91#define	SW_INT_ENABLE_REG(_b)		(0x40 + ((_b) * 4))
92#define	SW_INT_MASK_REG(_b)		(0x50 + ((_b) * 4))
93
94struct a10_intr_irqsrc {
95	struct intr_irqsrc	isrc;
96	u_int			irq;
97};
98
99struct a10_aintc_softc {
100	device_t		sc_dev;
101	struct resource *	aintc_res;
102	bus_space_tag_t		aintc_bst;
103	bus_space_handle_t	aintc_bsh;
104	struct mtx		mtx;
105	struct a10_intr_irqsrc	isrcs[A10_INTR_MAX_NIRQS];
106};
107
108#define	aintc_read_4(sc, reg)						\
109	bus_space_read_4(sc->aintc_bst, sc->aintc_bsh, reg)
110#define	aintc_write_4(sc, reg, val)					\
111	bus_space_write_4(sc->aintc_bst, sc->aintc_bsh, reg, val)
112
113static __inline void
114a10_intr_eoi(struct a10_aintc_softc *sc, u_int irq)
115{
116
117	if (irq != SW_INT_IRQNO_ENMI)
118		return;
119	mtx_lock_spin(&sc->mtx);
120	aintc_write_4(sc, SW_INT_IRQ_PENDING_REG(0),
121	    (1 << SW_INT_IRQNO_ENMI));
122	mtx_unlock_spin(&sc->mtx);
123}
124
125static void
126a10_intr_unmask(struct a10_aintc_softc *sc, u_int irq)
127{
128	uint32_t bit, block, value;
129
130	bit = (irq % 32);
131	block = (irq / 32);
132
133	mtx_lock_spin(&sc->mtx);
134	value = aintc_read_4(sc, SW_INT_ENABLE_REG(block));
135	value |= (1 << bit);
136	aintc_write_4(sc, SW_INT_ENABLE_REG(block), value);
137
138	value = aintc_read_4(sc, SW_INT_MASK_REG(block));
139	value &= ~(1 << bit);
140	aintc_write_4(sc, SW_INT_MASK_REG(block), value);
141	mtx_unlock_spin(&sc->mtx);
142}
143
144static void
145a10_intr_mask(struct a10_aintc_softc *sc, u_int irq)
146{
147	uint32_t bit, block, value;
148
149	bit = (irq % 32);
150	block = (irq / 32);
151
152	mtx_lock_spin(&sc->mtx);
153	value = aintc_read_4(sc, SW_INT_ENABLE_REG(block));
154	value &= ~(1 << bit);
155	aintc_write_4(sc, SW_INT_ENABLE_REG(block), value);
156
157	value = aintc_read_4(sc, SW_INT_MASK_REG(block));
158	value |= (1 << bit);
159	aintc_write_4(sc, SW_INT_MASK_REG(block), value);
160	mtx_unlock_spin(&sc->mtx);
161}
162
163static int
164a10_pending_irq(struct a10_aintc_softc *sc)
165{
166	uint32_t value;
167	int i, b;
168
169	for (i = 0; i < 3; i++) {
170		value = aintc_read_4(sc, SW_INT_IRQ_PENDING_REG(i));
171		if (value == 0)
172			continue;
173		for (b = 0; b < 32; b++)
174			if (value & (1 << b)) {
175				return (i * 32 + b);
176			}
177	}
178
179	return (-1);
180}
181
182static int
183a10_intr(void *arg)
184{
185	struct a10_aintc_softc *sc = arg;
186	u_int irq;
187
188	irq = a10_pending_irq(sc);
189	if (irq == -1 || irq > A10_INTR_MAX_NIRQS) {
190		device_printf(sc->sc_dev, "Spurious interrupt %d\n", irq);
191		return (FILTER_HANDLED);
192	}
193
194	while (irq != -1) {
195		if (irq > A10_INTR_MAX_NIRQS) {
196			device_printf(sc->sc_dev, "Spurious interrupt %d\n",
197			    irq);
198			return (FILTER_HANDLED);
199		}
200		if (intr_isrc_dispatch(&sc->isrcs[irq].isrc,
201		    curthread->td_intr_frame) != 0) {
202			a10_intr_mask(sc, irq);
203			a10_intr_eoi(sc, irq);
204			device_printf(sc->sc_dev,
205			    "Stray interrupt %d disabled\n", irq);
206		}
207
208		arm_irq_memory_barrier(irq);
209		irq = a10_pending_irq(sc);
210	}
211
212	return (FILTER_HANDLED);
213}
214
215static int
216a10_intr_pic_attach(struct a10_aintc_softc *sc)
217{
218	struct intr_pic *pic;
219	int error;
220	uint32_t irq;
221	const char *name;
222	intptr_t xref;
223
224	name = device_get_nameunit(sc->sc_dev);
225	for (irq = 0; irq < A10_INTR_MAX_NIRQS; irq++) {
226		sc->isrcs[irq].irq = irq;
227
228		error = intr_isrc_register(&sc->isrcs[irq].isrc,
229		    sc->sc_dev, 0, "%s,%u", name, irq);
230		if (error != 0)
231			return (error);
232	}
233
234	xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev));
235	pic = intr_pic_register(sc->sc_dev, xref);
236	if (pic == NULL)
237		return (ENXIO);
238
239	return (intr_pic_claim_root(sc->sc_dev, xref, a10_intr, sc));
240}
241
242static void
243a10_intr_enable_intr(device_t dev, struct intr_irqsrc *isrc)
244{
245	struct a10_aintc_softc *sc;
246	u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
247
248	sc = device_get_softc(dev);
249	arm_irq_memory_barrier(irq);
250	a10_intr_unmask(sc, irq);
251}
252
253static void
254a10_intr_disable_intr(device_t dev, struct intr_irqsrc *isrc)
255{
256	struct a10_aintc_softc *sc;
257	u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
258
259	sc = device_get_softc(dev);
260	a10_intr_mask(sc, irq);
261}
262
263static int
264a10_intr_map_intr(device_t dev, struct intr_map_data *data,
265    struct intr_irqsrc **isrcp)
266{
267	struct intr_map_data_fdt *daf;
268	struct a10_aintc_softc *sc;
269
270	if (data->type != INTR_MAP_DATA_FDT)
271		return (ENOTSUP);
272
273	daf = (struct intr_map_data_fdt *)data;
274	if (daf->ncells != 1 || daf->cells[0] >= A10_INTR_MAX_NIRQS)
275		return (EINVAL);
276
277	sc = device_get_softc(dev);
278	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
279	return (0);
280}
281
282static void
283a10_intr_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
284{
285	struct a10_aintc_softc *sc = device_get_softc(dev);
286	u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
287
288	a10_intr_mask(sc, irq);
289	a10_intr_eoi(sc, irq);
290}
291
292static void
293a10_intr_post_ithread(device_t dev, struct intr_irqsrc *isrc)
294{
295
296	a10_intr_enable_intr(dev, isrc);
297}
298
299static void
300a10_intr_post_filter(device_t dev, struct intr_irqsrc *isrc)
301{
302	struct a10_aintc_softc *sc = device_get_softc(dev);
303	u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
304
305	a10_intr_eoi(sc, irq);
306}
307
308static int
309a10_aintc_probe(device_t dev)
310{
311
312	if (!ofw_bus_status_okay(dev))
313		return (ENXIO);
314
315	if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ic"))
316		return (ENXIO);
317	device_set_desc(dev, "A10 AINTC Interrupt Controller");
318	return (BUS_PROBE_DEFAULT);
319}
320
321static int
322a10_aintc_attach(device_t dev)
323{
324	struct a10_aintc_softc *sc = device_get_softc(dev);
325	int rid = 0;
326	int i;
327	sc->sc_dev = dev;
328
329	sc->aintc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
330	    &rid, RF_ACTIVE);
331	if (!sc->aintc_res) {
332		device_printf(dev, "could not allocate resource\n");
333		goto error;
334	}
335
336	sc->aintc_bst = rman_get_bustag(sc->aintc_res);
337	sc->aintc_bsh = rman_get_bushandle(sc->aintc_res);
338
339	mtx_init(&sc->mtx, "A10 AINTC lock", "", MTX_SPIN);
340
341	/* Disable & clear all interrupts */
342	for (i = 0; i < 3; i++) {
343		aintc_write_4(sc, SW_INT_ENABLE_REG(i), 0);
344		aintc_write_4(sc, SW_INT_MASK_REG(i), 0xffffffff);
345	}
346	/* enable protection mode*/
347	aintc_write_4(sc, SW_INT_PROTECTION_REG, 0x01);
348
349	/* config the external interrupt source type*/
350	aintc_write_4(sc, SW_INT_NMI_CTRL_REG, 0x00);
351
352	if (a10_intr_pic_attach(sc) != 0) {
353		device_printf(dev, "could not attach PIC\n");
354		return (ENXIO);
355	}
356
357	return (0);
358
359error:
360	bus_release_resource(dev, SYS_RES_MEMORY, rid,
361	    sc->aintc_res);
362	return (ENXIO);
363}
364
365static device_method_t a10_aintc_methods[] = {
366	DEVMETHOD(device_probe,		a10_aintc_probe),
367	DEVMETHOD(device_attach,	a10_aintc_attach),
368
369	/* Interrupt controller interface */
370	DEVMETHOD(pic_disable_intr,	a10_intr_disable_intr),
371	DEVMETHOD(pic_enable_intr,	a10_intr_enable_intr),
372	DEVMETHOD(pic_map_intr,		a10_intr_map_intr),
373	DEVMETHOD(pic_post_filter,	a10_intr_post_filter),
374	DEVMETHOD(pic_post_ithread,	a10_intr_post_ithread),
375	DEVMETHOD(pic_pre_ithread,	a10_intr_pre_ithread),
376	{ 0, 0 }
377};
378
379static driver_t a10_aintc_driver = {
380	"aintc",
381	a10_aintc_methods,
382	sizeof(struct a10_aintc_softc),
383};
384
385EARLY_DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, 0, 0,
386    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST);
387