intr_machdep.c revision 279305
1/*-
2 * Copyright 2003-2011 Netlogic Microsystems (Netlogic). All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
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
13 *    the documentation and/or other materials provided with the
14 *    distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY Netlogic Microsystems ``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
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NETLOGIC OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * NETLOGIC_BSD */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/mips/nlm/intr_machdep.c 279305 2015-02-26 02:05:45Z jchandra $");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/bus.h>
36#include <sys/interrupt.h>
37#include <sys/kernel.h>
38#include <sys/module.h>
39
40#include <dev/ofw/ofw_bus.h>
41#include <dev/ofw/ofw_bus_subr.h>
42
43#include <machine/cpu.h>
44#include <machine/cpufunc.h>
45#include <machine/cpuinfo.h>
46#include <machine/cpuregs.h>
47#include <machine/frame.h>
48#include <machine/intr_machdep.h>
49#include <machine/md_var.h>
50#include <machine/trap.h>
51#include <machine/hwfunc.h>
52
53#include <mips/nlm/hal/haldefs.h>
54#include <mips/nlm/hal/iomap.h>
55#include <mips/nlm/hal/mips-extns.h>
56#include <mips/nlm/interrupt.h>
57#include <mips/nlm/hal/pic.h>
58#include <mips/nlm/xlp.h>
59
60struct xlp_intrsrc {
61	void (*busack)(int);		/* Additional ack */
62	struct intr_event *ie;		/* event corresponding to intr */
63	int irq;
64};
65
66static struct xlp_intrsrc xlp_interrupts[XLR_MAX_INTR];
67static mips_intrcnt_t mips_intr_counters[XLR_MAX_INTR];
68static int intrcnt_index;
69
70void
71xlp_enable_irq(int irq)
72{
73	uint64_t eimr;
74
75	eimr = nlm_read_c0_eimr();
76	nlm_write_c0_eimr(eimr | (1ULL << irq));
77}
78
79void
80cpu_establish_softintr(const char *name, driver_filter_t * filt,
81    void (*handler) (void *), void *arg, int irq, int flags,
82    void **cookiep)
83{
84
85	panic("Soft interrupts unsupported!\n");
86}
87
88void
89cpu_establish_hardintr(const char *name, driver_filter_t * filt,
90    void (*handler) (void *), void *arg, int irq, int flags,
91    void **cookiep)
92{
93
94	xlp_establish_intr(name, filt, handler, arg, irq, flags,
95	    cookiep, NULL);
96}
97
98static void
99xlp_post_filter(void *source)
100{
101	struct xlp_intrsrc *src = source;
102
103	if (src->busack)
104		src->busack(src->irq);
105	nlm_pic_ack(xlp_pic_base, xlp_irq_to_irt(src->irq));
106}
107
108static void
109xlp_pre_ithread(void *source)
110{
111	struct xlp_intrsrc *src = source;
112
113	if (src->busack)
114		src->busack(src->irq);
115}
116
117static void
118xlp_post_ithread(void *source)
119{
120	struct xlp_intrsrc *src = source;
121
122	nlm_pic_ack(xlp_pic_base, xlp_irq_to_irt(src->irq));
123}
124
125void
126xlp_establish_intr(const char *name, driver_filter_t filt,
127    driver_intr_t handler, void *arg, int irq, int flags,
128    void **cookiep, void (*busack)(int))
129{
130	struct intr_event *ie;	/* descriptor for the IRQ */
131	struct xlp_intrsrc *src = NULL;
132	int errcode;
133
134	if (irq < 0 || irq > XLR_MAX_INTR)
135		panic("%s called for unknown hard intr %d", __func__, irq);
136
137	/*
138	 * FIXME locking - not needed now, because we do this only on
139	 * startup from CPU0
140	 */
141	src = &xlp_interrupts[irq];
142	ie = src->ie;
143	if (ie == NULL) {
144		/*
145		 * PIC based interrupts need ack in PIC, and some SoC
146		 * components need additional acks (e.g. PCI)
147		 */
148		if (XLP_IRQ_IS_PICINTR(irq))
149			errcode = intr_event_create(&ie, src, 0, irq,
150			    xlp_pre_ithread, xlp_post_ithread, xlp_post_filter,
151			    NULL, "hard intr%d:", irq);
152		else {
153			if (filt == NULL)
154				panic("Unsupported non filter percpu intr %d", irq);
155			errcode = intr_event_create(&ie, src, 0, irq,
156			    NULL, NULL, NULL, NULL, "hard intr%d:", irq);
157		}
158		if (errcode) {
159			printf("Could not create event for intr %d\n", irq);
160			return;
161		}
162		src->irq = irq;
163		src->busack = busack;
164		src->ie = ie;
165	}
166	intr_event_add_handler(ie, name, filt, handler, arg,
167	    intr_priority(flags), flags, cookiep);
168	xlp_enable_irq(irq);
169}
170
171void
172cpu_intr(struct trapframe *tf)
173{
174	struct intr_event *ie;
175	uint64_t eirr, eimr;
176	int i;
177
178	critical_enter();
179
180	/* find a list of enabled interrupts */
181	eirr = nlm_read_c0_eirr();
182	eimr = nlm_read_c0_eimr();
183	eirr &= eimr;
184
185	if (eirr == 0) {
186		critical_exit();
187		return;
188	}
189	/*
190	 * No need to clear the EIRR here as the handler writes to
191	 * compare which ACKs the interrupt.
192	 */
193	if (eirr & (1 << IRQ_TIMER)) {
194		intr_event_handle(xlp_interrupts[IRQ_TIMER].ie, tf);
195		critical_exit();
196		return;
197	}
198
199	/* FIXME sched pin >? LOCK>? */
200	for (i = sizeof(eirr) * 8 - 1; i >= 0; i--) {
201		if ((eirr & (1ULL << i)) == 0)
202			continue;
203
204		ie = xlp_interrupts[i].ie;
205		/* Don't account special IRQs */
206		switch (i) {
207		case IRQ_IPI:
208		case IRQ_MSGRING:
209			break;
210		default:
211			mips_intrcnt_inc(mips_intr_counters[i]);
212		}
213
214		/* Ack the IRQ on the CPU */
215		nlm_write_c0_eirr(1ULL << i);
216		if (intr_event_handle(ie, tf) != 0) {
217			printf("stray interrupt %d\n", i);
218		}
219	}
220	critical_exit();
221}
222
223void
224mips_intrcnt_setname(mips_intrcnt_t counter, const char *name)
225{
226	int idx = counter - intrcnt;
227
228	KASSERT(counter != NULL, ("mips_intrcnt_setname: NULL counter"));
229
230	snprintf(intrnames + (MAXCOMLEN + 1) * idx,
231	    MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name);
232}
233
234mips_intrcnt_t
235mips_intrcnt_create(const char* name)
236{
237	mips_intrcnt_t counter = &intrcnt[intrcnt_index++];
238
239	mips_intrcnt_setname(counter, name);
240	return counter;
241}
242
243void
244cpu_init_interrupts()
245{
246	int i;
247	char name[MAXCOMLEN + 1];
248
249	/*
250	 * Initialize all available vectors so spare IRQ
251	 * would show up in systat output
252	 */
253	for (i = 0; i < XLR_MAX_INTR; i++) {
254		snprintf(name, MAXCOMLEN + 1, "int%d:", i);
255		mips_intr_counters[i] = mips_intrcnt_create(name);
256	}
257}
258
259static int	xlp_pic_probe(device_t);
260static int	xlp_pic_attach(device_t);
261
262static int
263xlp_pic_probe(device_t dev)
264{
265
266	if (!ofw_bus_is_compatible(dev, "netlogic,xlp-pic"))
267		return (ENXIO);
268	device_set_desc(dev, "XLP PIC");
269	return (0);
270}
271
272static int
273xlp_pic_attach(device_t dev)
274{
275
276	return (0);
277}
278
279static device_method_t xlp_pic_methods[] = {
280	DEVMETHOD(device_probe,		xlp_pic_probe),
281	DEVMETHOD(device_attach,	xlp_pic_attach),
282
283	DEVMETHOD_END
284};
285
286static driver_t xlp_pic_driver = {
287	"xlp_pic",
288	xlp_pic_methods,
289	1,		/* no softc */
290};
291
292static devclass_t xlp_pic_devclass;
293DRIVER_MODULE(xlp_pic, simplebus, xlp_pic_driver, xlp_pic_devclass, 0, 0);
294