gic.c revision 271595
1226031Sstas/*-
2226031Sstas * Copyright (c) 2011 The FreeBSD Foundation
3226031Sstas * All rights reserved.
4226031Sstas *
5226031Sstas * Developed by Damjan Marion <damjan.marion@gmail.com>
6226031Sstas *
7226031Sstas * Based on OMAP4 GIC code by Ben Gray
8226031Sstas *
9226031Sstas * Redistribution and use in source and binary forms, with or without
10226031Sstas * modification, are permitted provided that the following conditions
11226031Sstas * are met:
12226031Sstas * 1. Redistributions of source code must retain the above copyright
13226031Sstas *    notice, this list of conditions and the following disclaimer.
14226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
15226031Sstas *    notice, this list of conditions and the following disclaimer in the
16226031Sstas *    documentation and/or other materials provided with the distribution.
17226031Sstas * 3. The name of the company nor the name of the author may be used to
18226031Sstas *    endorse or promote products derived from this software without specific
19226031Sstas *    prior written permission.
20226031Sstas *
21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31226031Sstas * SUCH DAMAGE.
32226031Sstas */
33226031Sstas
34226031Sstas#include <sys/cdefs.h>
35226031Sstas__FBSDID("$FreeBSD: head/sys/arm/arm/gic.c 271595 2014-09-14 17:47:04Z ian $");
36226031Sstas
37226031Sstas#include <sys/param.h>
38226031Sstas#include <sys/systm.h>
39226031Sstas#include <sys/bus.h>
40226031Sstas#include <sys/kernel.h>
41226031Sstas#include <sys/ktr.h>
42226031Sstas#include <sys/module.h>
43226031Sstas#include <sys/rman.h>
44226031Sstas#include <sys/pcpu.h>
45226031Sstas#include <sys/proc.h>
46226031Sstas#include <sys/cpuset.h>
47226031Sstas#include <sys/lock.h>
48226031Sstas#include <sys/mutex.h>
49226031Sstas#include <machine/bus.h>
50226031Sstas#include <machine/intr.h>
51226031Sstas#include <machine/smp.h>
52226031Sstas
53226031Sstas#include <dev/fdt/fdt_common.h>
54226031Sstas#include <dev/ofw/openfirm.h>
55226031Sstas#include <dev/ofw/ofw_bus.h>
56226031Sstas#include <dev/ofw/ofw_bus_subr.h>
57226031Sstas
58226031Sstas/* We are using GICv2 register naming */
59226031Sstas
60226031Sstas/* Distributor Registers */
61226031Sstas#define GICD_CTLR		0x000			/* v1 ICDDCR */
62226031Sstas#define GICD_TYPER		0x004			/* v1 ICDICTR */
63226031Sstas#define GICD_IIDR		0x008			/* v1 ICDIIDR */
64226031Sstas#define GICD_IGROUPR(n)		(0x0080 + ((n) * 4))	/* v1 ICDISER */
65226031Sstas#define GICD_ISENABLER(n)	(0x0100 + ((n) * 4))	/* v1 ICDISER */
66226031Sstas#define GICD_ICENABLER(n)	(0x0180 + ((n) * 4))	/* v1 ICDICER */
67226031Sstas#define GICD_ISPENDR(n)		(0x0200 + ((n) * 4))	/* v1 ICDISPR */
68226031Sstas#define GICD_ICPENDR(n)		(0x0280 + ((n) * 4))	/* v1 ICDICPR */
69226031Sstas#define GICD_ICACTIVER(n)	(0x0380 + ((n) * 4))	/* v1 ICDABR */
70226031Sstas#define GICD_IPRIORITYR(n)	(0x0400 + ((n) * 4))	/* v1 ICDIPR */
71226031Sstas#define GICD_ITARGETSR(n)	(0x0800 + ((n) * 4))	/* v1 ICDIPTR */
72226031Sstas#define GICD_ICFGR(n)		(0x0C00 + ((n) * 4))	/* v1 ICDICFR */
73226031Sstas#define GICD_SGIR(n)		(0x0F00 + ((n) * 4))	/* v1 ICDSGIR */
74226031Sstas
75226031Sstas/* CPU Registers */
76226031Sstas#define GICC_CTLR		0x0000			/* v1 ICCICR */
77226031Sstas#define GICC_PMR		0x0004			/* v1 ICCPMR */
78226031Sstas#define GICC_BPR		0x0008			/* v1 ICCBPR */
79226031Sstas#define GICC_IAR		0x000C			/* v1 ICCIAR */
80226031Sstas#define GICC_EOIR		0x0010			/* v1 ICCEOIR */
81226031Sstas#define GICC_RPR		0x0014			/* v1 ICCRPR */
82226031Sstas#define GICC_HPPIR		0x0018			/* v1 ICCHPIR */
83226031Sstas#define GICC_ABPR		0x001C			/* v1 ICCABPR */
84226031Sstas#define GICC_IIDR		0x00FC			/* v1 ICCIIDR*/
85226031Sstas
86226031Sstas#define	GIC_LAST_IPI		15	/* Irqs 0-15 are IPIs. */
87226031Sstas
88226031Sstas/* First bit is a polarity bit (0 - low, 1 - high) */
89226031Sstas#define GICD_ICFGR_POL_LOW	(0 << 0)
90226031Sstas#define GICD_ICFGR_POL_HIGH	(1 << 0)
91226031Sstas#define GICD_ICFGR_POL_MASK	0x1
92226031Sstas/* Second bit is a trigger bit (0 - level, 1 - edge) */
93226031Sstas#define GICD_ICFGR_TRIG_LVL	(0 << 1)
94226031Sstas#define GICD_ICFGR_TRIG_EDGE	(1 << 1)
95226031Sstas#define GICD_ICFGR_TRIG_MASK	0x2
96226031Sstas
97226031Sstasstruct arm_gic_softc {
98226031Sstas	struct resource *	gic_res[3];
99226031Sstas	bus_space_tag_t		gic_c_bst;
100226031Sstas	bus_space_tag_t		gic_d_bst;
101226031Sstas	bus_space_handle_t	gic_c_bsh;
102226031Sstas	bus_space_handle_t	gic_d_bsh;
103226031Sstas	uint8_t			ver;
104226031Sstas	device_t		dev;
105226031Sstas	struct mtx		mutex;
106226031Sstas	uint32_t		nirqs;
107226031Sstas};
108226031Sstas
109226031Sstasstatic struct resource_spec arm_gic_spec[] = {
110226031Sstas	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },	/* Distributor registers */
111226031Sstas	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },	/* CPU Interrupt Intf. registers */
112226031Sstas	{ -1, 0 }
113226031Sstas};
114226031Sstas
115226031Sstasstatic struct arm_gic_softc *arm_gic_sc = NULL;
116226031Sstas
117226031Sstas#define	gic_c_read_4(reg)		\
118226031Sstas    bus_space_read_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg)
119226031Sstas#define	gic_c_write_4(reg, val)		\
120226031Sstas    bus_space_write_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg, val)
121226031Sstas#define	gic_d_read_4(reg)		\
122226031Sstas    bus_space_read_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg)
123226031Sstas#define	gic_d_write_4(reg, val)		\
124226031Sstas    bus_space_write_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg, val)
125226031Sstas
126226031Sstasstatic int gic_config_irq(int irq, enum intr_trigger trig,
127226031Sstas    enum intr_polarity pol);
128226031Sstasstatic void gic_post_filter(void *);
129226031Sstas
130226031Sstasstatic struct ofw_compat_data compat_data[] = {
131226031Sstas	{"arm,gic",		true},	/* Non-standard, used in FreeBSD dts. */
132226031Sstas	{"arm,gic-400",		true},
133226031Sstas	{"arm,cortex-a15-gic",	true},
134226031Sstas	{"arm,cortex-a9-gic",	true},
135226031Sstas	{"arm,cortex-a7-gic",	true},
136226031Sstas	{"arm,arm11mp-gic",	true},
137226031Sstas	{"brcm,brahma-b15-gic",	true},
138226031Sstas	{NULL,			false}
139226031Sstas};
140226031Sstas
141226031Sstasstatic int
142226031Sstasarm_gic_probe(device_t dev)
143226031Sstas{
144226031Sstas
145226031Sstas	if (!ofw_bus_status_okay(dev))
146226031Sstas		return (ENXIO);
147226031Sstas
148226031Sstas	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
149226031Sstas		return (ENXIO);
150226031Sstas	device_set_desc(dev, "ARM Generic Interrupt Controller");
151226031Sstas	return (BUS_PROBE_DEFAULT);
152226031Sstas}
153226031Sstas
154226031Sstasvoid
155226031Sstasgic_init_secondary(void)
156226031Sstas{
157226031Sstas	int i, nirqs;
158
159  	/* Get the number of interrupts */
160	nirqs = gic_d_read_4(GICD_TYPER);
161	nirqs = 32 * ((nirqs & 0x1f) + 1);
162
163	for (i = 0; i < nirqs; i += 4)
164		gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
165
166	/* Set all the interrupts to be in Group 0 (secure) */
167	for (i = 0; i < nirqs; i += 32) {
168		gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
169	}
170
171	/* Enable CPU interface */
172	gic_c_write_4(GICC_CTLR, 1);
173
174	/* Set priority mask register. */
175	gic_c_write_4(GICC_PMR, 0xff);
176
177	/* Enable interrupt distribution */
178	gic_d_write_4(GICD_CTLR, 0x01);
179
180	/*
181	 * Activate the timer interrupts: virtual, secure, and non-secure.
182	 */
183	gic_d_write_4(GICD_ISENABLER(27 >> 5), (1UL << (27 & 0x1F)));
184	gic_d_write_4(GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F)));
185	gic_d_write_4(GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F)));
186}
187
188static int
189arm_gic_attach(device_t dev)
190{
191	struct		arm_gic_softc *sc;
192	int		i;
193	uint32_t	icciidr;
194
195	if (arm_gic_sc)
196		return (ENXIO);
197
198	sc = device_get_softc(dev);
199	sc->dev = dev;
200
201	if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) {
202		device_printf(dev, "could not allocate resources\n");
203		return (ENXIO);
204	}
205
206	/* Initialize mutex */
207	mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN);
208
209	/* Distributor Interface */
210	sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]);
211	sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]);
212
213	/* CPU Interface */
214	sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]);
215	sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]);
216
217	arm_gic_sc = sc;
218
219	/* Disable interrupt forwarding to the CPU interface */
220	gic_d_write_4(GICD_CTLR, 0x00);
221
222	/* Get the number of interrupts */
223	sc->nirqs = gic_d_read_4(GICD_TYPER);
224	sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1);
225
226	/* Set up function pointers */
227	arm_post_filter = gic_post_filter;
228	arm_config_irq = gic_config_irq;
229
230	icciidr = gic_c_read_4(GICC_IIDR);
231	device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x sc->nirqs %u\n",
232			icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf,
233			(icciidr & 0xfff), sc->nirqs);
234
235	/* Set all global interrupts to be level triggered, active low. */
236	for (i = 32; i < sc->nirqs; i += 16) {
237		gic_d_write_4(GICD_ICFGR(i >> 4), 0x00000000);
238	}
239
240	/* Disable all interrupts. */
241	for (i = 32; i < sc->nirqs; i += 32) {
242		gic_d_write_4(GICD_ICENABLER(i >> 5), 0xFFFFFFFF);
243	}
244
245	for (i = 0; i < sc->nirqs; i += 4) {
246		gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
247		gic_d_write_4(GICD_ITARGETSR(i >> 2), 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24);
248	}
249
250	/* Set all the interrupts to be in Group 0 (secure) */
251	for (i = 0; i < sc->nirqs; i += 32) {
252		gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
253	}
254
255	/* Enable CPU interface */
256	gic_c_write_4(GICC_CTLR, 1);
257
258	/* Set priority mask register. */
259	gic_c_write_4(GICC_PMR, 0xff);
260
261	/* Enable interrupt distribution */
262	gic_d_write_4(GICD_CTLR, 0x01);
263
264	return (0);
265}
266
267static device_method_t arm_gic_methods[] = {
268	DEVMETHOD(device_probe,		arm_gic_probe),
269	DEVMETHOD(device_attach,	arm_gic_attach),
270	{ 0, 0 }
271};
272
273static driver_t arm_gic_driver = {
274	"gic",
275	arm_gic_methods,
276	sizeof(struct arm_gic_softc),
277};
278
279static devclass_t arm_gic_devclass;
280
281EARLY_DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0,
282    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
283EARLY_DRIVER_MODULE(gic, ofwbus, arm_gic_driver, arm_gic_devclass, 0, 0,
284    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
285
286static void
287gic_post_filter(void *arg)
288{
289	uintptr_t irq = (uintptr_t) arg;
290
291	if (irq > GIC_LAST_IPI)
292		arm_irq_memory_barrier(irq);
293	gic_c_write_4(GICC_EOIR, irq);
294}
295
296int
297arm_get_next_irq(int last_irq)
298{
299	uint32_t active_irq;
300
301	active_irq = gic_c_read_4(GICC_IAR);
302
303	/*
304	 * Immediatly EOIR the SGIs, because doing so requires the other
305	 * bits (ie CPU number), not just the IRQ number, and we do not
306	 * have this information later.
307	 */
308
309	if ((active_irq & 0x3ff) <= GIC_LAST_IPI)
310		gic_c_write_4(GICC_EOIR, active_irq);
311	active_irq &= 0x3FF;
312
313	if (active_irq == 0x3FF) {
314		if (last_irq == -1)
315			printf("Spurious interrupt detected\n");
316		return -1;
317	}
318
319	return active_irq;
320}
321
322void
323arm_mask_irq(uintptr_t nb)
324{
325
326	gic_d_write_4(GICD_ICENABLER(nb >> 5), (1UL << (nb & 0x1F)));
327	gic_c_write_4(GICC_EOIR, nb);
328}
329
330void
331arm_unmask_irq(uintptr_t nb)
332{
333
334	if (nb > GIC_LAST_IPI)
335		arm_irq_memory_barrier(nb);
336	gic_d_write_4(GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F)));
337}
338
339static int
340gic_config_irq(int irq, enum intr_trigger trig,
341    enum intr_polarity pol)
342{
343	uint32_t reg;
344	uint32_t mask;
345
346	/* Function is public-accessible, so validate input arguments */
347	if ((irq < 0) || (irq >= arm_gic_sc->nirqs))
348		goto invalid_args;
349	if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) &&
350	    (trig != INTR_TRIGGER_CONFORM))
351		goto invalid_args;
352	if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) &&
353	    (pol != INTR_POLARITY_CONFORM))
354		goto invalid_args;
355
356	mtx_lock_spin(&arm_gic_sc->mutex);
357
358	reg = gic_d_read_4(GICD_ICFGR(irq >> 4));
359	mask = (reg >> 2*(irq % 16)) & 0x3;
360
361	if (pol == INTR_POLARITY_LOW) {
362		mask &= ~GICD_ICFGR_POL_MASK;
363		mask |= GICD_ICFGR_POL_LOW;
364	} else if (pol == INTR_POLARITY_HIGH) {
365		mask &= ~GICD_ICFGR_POL_MASK;
366		mask |= GICD_ICFGR_POL_HIGH;
367	}
368
369	if (trig == INTR_TRIGGER_LEVEL) {
370		mask &= ~GICD_ICFGR_TRIG_MASK;
371		mask |= GICD_ICFGR_TRIG_LVL;
372	} else if (trig == INTR_TRIGGER_EDGE) {
373		mask &= ~GICD_ICFGR_TRIG_MASK;
374		mask |= GICD_ICFGR_TRIG_EDGE;
375	}
376
377	/* Set mask */
378	reg = reg & ~(0x3 << 2*(irq % 16));
379	reg = reg | (mask << 2*(irq % 16));
380	gic_d_write_4(GICD_ICFGR(irq >> 4), reg);
381
382	mtx_unlock_spin(&arm_gic_sc->mutex);
383
384	return (0);
385
386invalid_args:
387	device_printf(arm_gic_sc->dev, "gic_config_irg, invalid parameters\n");
388	return (EINVAL);
389}
390
391#ifdef SMP
392void
393pic_ipi_send(cpuset_t cpus, u_int ipi)
394{
395	uint32_t val = 0, i;
396
397	for (i = 0; i < MAXCPU; i++)
398		if (CPU_ISSET(i, &cpus))
399			val |= 1 << (16 + i);
400	gic_d_write_4(GICD_SGIR(0), val | ipi);
401
402}
403
404int
405pic_ipi_get(int i)
406{
407
408	if (i != -1) {
409		/*
410		 * The intr code will automagically give the frame pointer
411		 * if the interrupt argument is 0.
412		 */
413		if ((unsigned int)i > 16)
414			return (0);
415		return (i);
416	}
417	return (0x3ff);
418}
419
420void
421pic_ipi_clear(int ipi)
422{
423}
424#endif
425
426