1/*-
2 * Copyright (c) 2016 Stanislav Galabov
3 * Copyright (c) 2015 Alexander Kabaev
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 *    without modification, immediately at the beginning of the file.
12 * 2. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
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 FOR
19 * 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
29#include "opt_platform.h"
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/bus.h>
37#include <sys/kernel.h>
38#include <sys/ktr.h>
39#include <sys/module.h>
40#include <sys/malloc.h>
41#include <sys/rman.h>
42#include <sys/pcpu.h>
43#include <sys/proc.h>
44#include <sys/cpuset.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47#include <sys/smp.h>
48#include <sys/sched.h>
49#include <machine/bus.h>
50#include <machine/intr.h>
51#include <machine/smp.h>
52
53#include <dev/fdt/fdt_common.h>
54#include <dev/ofw/openfirm.h>
55#include <dev/ofw/ofw_bus.h>
56#include <dev/ofw/ofw_bus_subr.h>
57
58#include "pic_if.h"
59
60#define	MTK_NIRQS	64	/* We'll only use 64 for now */
61
62#define MTK_INTPOL		0x0100
63#define MTK_INTTRIG		0x0180
64#define MTK_INTDIS		0x0300
65#define MTK_INTENA		0x0380
66#define MTK_INTMASK		0x0400
67#define MTK_INTSTAT		0x0480
68#define MTK_MAPPIN(_i)		(0x0500 + (4 * (_i)))
69#define MTK_MAPVPE(_i, _v)	(0x2000 + (32 * (_i)) + (((_v) / 32) * 4))
70
71#define MTK_INTPOL_POS		1
72#define MTK_INTPOL_NEG		0
73#define MTK_INTTRIG_EDGE	1
74#define MTK_INTTRIG_LEVEL	0
75#define MTK_PIN_BITS(_i)	((1 << 31) | (_i))
76#define MTK_VPE_BITS(_v)	(1 << ((_v) % 32))
77
78static int mtk_gic_intr(void *);
79
80struct mtk_gic_irqsrc {
81	struct intr_irqsrc	isrc;
82	u_int			irq;
83};
84
85struct mtk_gic_softc {
86	device_t		gic_dev;
87	void *                  gic_intrhand;
88	struct resource *       gic_res[2];
89	struct mtk_gic_irqsrc	gic_irqs[MTK_NIRQS];
90	struct mtx		mutex;
91	uint32_t		nirqs;
92};
93
94#define GIC_INTR_ISRC(sc, irq)	(&(sc)->gic_irqs[(irq)].isrc)
95
96static struct resource_spec mtk_gic_spec[] = {
97	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },	/* Registers */
98	{ -1, 0 }
99};
100
101static struct ofw_compat_data compat_data[] = {
102	{ "mti,gic",	1 },
103	{ NULL,		0 }
104};
105
106#define READ4(_sc, _reg)	bus_read_4((_sc)->gic_res[0], (_reg))
107#define WRITE4(_sc, _reg, _val)	bus_write_4((_sc)->gic_res[0], (_reg), (_val))
108
109static int
110mtk_gic_probe(device_t dev)
111{
112
113	if (!ofw_bus_status_okay(dev))
114		return (ENXIO);
115
116	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
117		return (ENXIO);
118
119	device_set_desc(dev, "MTK Interrupt Controller (GIC)");
120	return (BUS_PROBE_DEFAULT);
121}
122
123static inline void
124gic_irq_unmask(struct mtk_gic_softc *sc, u_int irq)
125{
126
127	WRITE4(sc, MTK_INTENA, (1u << (irq)));
128}
129
130static inline void
131gic_irq_mask(struct mtk_gic_softc *sc, u_int irq)
132{
133
134	WRITE4(sc, MTK_INTDIS, (1u << (irq)));
135}
136
137static inline intptr_t
138gic_xref(device_t dev)
139{
140
141	return (OF_xref_from_node(ofw_bus_get_node(dev)));
142}
143
144static int
145mtk_gic_register_isrcs(struct mtk_gic_softc *sc)
146{
147	int error;
148	uint32_t irq;
149	struct intr_irqsrc *isrc;
150	const char *name;
151
152	name = device_get_nameunit(sc->gic_dev);
153	for (irq = 0; irq < sc->nirqs; irq++) {
154		sc->gic_irqs[irq].irq = irq;
155		isrc = GIC_INTR_ISRC(sc, irq);
156		error = intr_isrc_register(isrc, sc->gic_dev, 0, "%s", name);
157		if (error != 0) {
158			/* XXX call intr_isrc_deregister */
159			device_printf(sc->gic_dev, "%s failed", __func__);
160			return (error);
161		}
162	}
163
164	return (0);
165}
166
167static int
168mtk_gic_attach(device_t dev)
169{
170	struct mtk_gic_softc *sc;
171	intptr_t xref = gic_xref(dev);
172	int i;
173
174	sc = device_get_softc(dev);
175
176	if (bus_alloc_resources(dev, mtk_gic_spec, sc->gic_res)) {
177		device_printf(dev, "could not allocate resources\n");
178		return (ENXIO);
179	}
180
181	sc->gic_dev = dev;
182
183	/* Initialize mutex */
184	mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN);
185
186	/* Set the number of interrupts */
187	sc->nirqs = nitems(sc->gic_irqs);
188
189	/* Mask all interrupts */
190	WRITE4(sc, MTK_INTDIS, 0xFFFFFFFF);
191
192	/* All interrupts are of type level */
193	WRITE4(sc, MTK_INTTRIG, 0x00000000);
194
195	/* All interrupts are of positive polarity */
196	WRITE4(sc, MTK_INTPOL, 0xFFFFFFFF);
197
198	/*
199	 * Route all interrupts to pin 0 on VPE 0;
200	 */
201	for (i = 0; i < 32; i++) {
202		WRITE4(sc, MTK_MAPPIN(i), MTK_PIN_BITS(0));
203		WRITE4(sc, MTK_MAPVPE(i, 0), MTK_VPE_BITS(0));
204	}
205
206	/* Register the interrupts */
207	if (mtk_gic_register_isrcs(sc) != 0) {
208		device_printf(dev, "could not register GIC ISRCs\n");
209		goto cleanup;
210	}
211
212	/*
213	 * Now, when everything is initialized, it's right time to
214	 * register interrupt controller to interrupt framefork.
215	 */
216	if (intr_pic_register(dev, xref) == NULL) {
217		device_printf(dev, "could not register PIC\n");
218		goto cleanup;
219	}
220
221	cpu_establish_hardintr("gic", mtk_gic_intr, NULL, sc, 0, INTR_TYPE_CLK,
222	    NULL);
223
224	return (0);
225
226cleanup:
227	bus_release_resources(dev, mtk_gic_spec, sc->gic_res);
228	return(ENXIO);
229}
230
231static int
232mtk_gic_intr(void *arg)
233{
234	struct mtk_gic_softc *sc = arg;
235	struct thread *td;
236	uint32_t i, intr;
237
238	td = curthread;
239	/* Workaround: do not inflate intr nesting level */
240	td->td_intr_nesting_level--;
241
242	intr = READ4(sc, MTK_INTSTAT) & READ4(sc, MTK_INTMASK);
243	while ((i = fls(intr)) != 0) {
244		i--;
245		intr &= ~(1u << i);
246
247		if (intr_isrc_dispatch(GIC_INTR_ISRC(sc, i),
248		    curthread->td_intr_frame) != 0) {
249			device_printf(sc->gic_dev,
250				"Stray interrupt %u detected\n", i);
251			gic_irq_mask(sc, i);
252			continue;
253		}
254	}
255
256	KASSERT(i == 0, ("all interrupts handled"));
257
258	td->td_intr_nesting_level++;
259
260	return (FILTER_HANDLED);
261}
262
263static int
264mtk_gic_map_intr(device_t dev, struct intr_map_data *data,
265    struct intr_irqsrc **isrcp)
266{
267#ifdef FDT
268	struct intr_map_data_fdt *daf;
269	struct mtk_gic_softc *sc;
270
271	if (data->type != INTR_MAP_DATA_FDT)
272		return (ENOTSUP);
273
274	sc = device_get_softc(dev);
275	daf = (struct intr_map_data_fdt *)data;
276
277	if (daf->ncells != 3 || daf->cells[1] >= sc->nirqs)
278		return (EINVAL);
279
280	*isrcp = GIC_INTR_ISRC(sc, daf->cells[1]);
281	return (0);
282#else
283	return (ENOTSUP);
284#endif
285}
286
287static void
288mtk_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
289{
290	u_int irq;
291
292	irq = ((struct mtk_gic_irqsrc *)isrc)->irq;
293	gic_irq_unmask(device_get_softc(dev), irq);
294}
295
296static void
297mtk_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
298{
299	u_int irq;
300
301	irq = ((struct mtk_gic_irqsrc *)isrc)->irq;
302	gic_irq_mask(device_get_softc(dev), irq);
303}
304
305static void
306mtk_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
307{
308
309	mtk_gic_disable_intr(dev, isrc);
310}
311
312static void
313mtk_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
314{
315
316	mtk_gic_enable_intr(dev, isrc);
317}
318
319static void
320mtk_gic_post_filter(device_t dev, struct intr_irqsrc *isrc)
321{
322}
323
324#ifdef SMP
325static int
326mtk_gic_bind(device_t dev, struct intr_irqsrc *isrc)
327{
328	return (EOPNOTSUPP);
329}
330
331static void
332mtk_gic_init_secondary(device_t dev)
333{
334}
335
336static void
337mtk_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus)
338{
339}
340#endif
341
342static device_method_t mtk_gic_methods[] = {
343	/* Device interface */
344	DEVMETHOD(device_probe,		mtk_gic_probe),
345	DEVMETHOD(device_attach,	mtk_gic_attach),
346	/* Interrupt controller interface */
347	DEVMETHOD(pic_disable_intr,	mtk_gic_disable_intr),
348	DEVMETHOD(pic_enable_intr,	mtk_gic_enable_intr),
349	DEVMETHOD(pic_map_intr,		mtk_gic_map_intr),
350	DEVMETHOD(pic_post_filter,	mtk_gic_post_filter),
351	DEVMETHOD(pic_post_ithread,	mtk_gic_post_ithread),
352	DEVMETHOD(pic_pre_ithread,	mtk_gic_pre_ithread),
353#ifdef SMP
354	DEVMETHOD(pic_bind,		mtk_gic_bind),
355	DEVMETHOD(pic_init_secondary,	mtk_gic_init_secondary),
356	DEVMETHOD(pic_ipi_send,		mtk_gic_ipi_send),
357#endif
358	{ 0, 0 }
359};
360
361static driver_t mtk_gic_driver = {
362	"intc",
363	mtk_gic_methods,
364	sizeof(struct mtk_gic_softc),
365};
366
367static devclass_t mtk_gic_devclass;
368
369EARLY_DRIVER_MODULE(intc_gic, simplebus, mtk_gic_driver, mtk_gic_devclass, 0, 0,
370    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
371