intr_machdep.c revision 331722
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: stable/11/sys/mips/nlm/intr_machdep.c 331722 2018-03-29 02:50:57Z eadler $");
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 (*bus_ack)(int, void *);	/* Additional ack */
62	void *bus_ack_arg;		/* arg for additional ack */
63	struct intr_event *ie;		/* event corresponding to intr */
64	int irq;
65	int irt;
66};
67
68static struct xlp_intrsrc xlp_interrupts[XLR_MAX_INTR];
69static mips_intrcnt_t mips_intr_counters[XLR_MAX_INTR];
70static int intrcnt_index;
71
72int
73xlp_irq_to_irt(int irq)
74{
75	uint32_t offset;
76
77	switch (irq) {
78	case PIC_UART_0_IRQ:
79	case PIC_UART_1_IRQ:
80		offset =  XLP_IO_UART_OFFSET(0, irq - PIC_UART_0_IRQ);
81		return (xlp_socdev_irt(offset));
82	case PIC_PCIE_0_IRQ:
83	case PIC_PCIE_1_IRQ:
84	case PIC_PCIE_2_IRQ:
85	case PIC_PCIE_3_IRQ:
86		offset = XLP_IO_PCIE_OFFSET(0, irq - PIC_PCIE_0_IRQ);
87		return (xlp_socdev_irt(offset));
88	case PIC_USB_0_IRQ:
89	case PIC_USB_1_IRQ:
90	case PIC_USB_2_IRQ:
91	case PIC_USB_3_IRQ:
92	case PIC_USB_4_IRQ:
93		offset = XLP_IO_USB_OFFSET(0, irq - PIC_USB_0_IRQ);
94		return (xlp_socdev_irt(offset));
95	case PIC_I2C_0_IRQ:
96	case PIC_I2C_1_IRQ:
97		offset = XLP_IO_I2C0_OFFSET(0);
98		return (xlp_socdev_irt(offset) + irq - PIC_I2C_0_IRQ);
99	default:
100		printf("ERROR: %s: unknown irq %d\n", __func__, irq);
101		return (-1);
102	}
103}
104
105void
106xlp_enable_irq(int irq)
107{
108	uint64_t eimr;
109
110	eimr = nlm_read_c0_eimr();
111	nlm_write_c0_eimr(eimr | (1ULL << irq));
112}
113
114void
115cpu_establish_softintr(const char *name, driver_filter_t * filt,
116    void (*handler) (void *), void *arg, int irq, int flags,
117    void **cookiep)
118{
119
120	panic("Soft interrupts unsupported!\n");
121}
122
123static void
124xlp_post_filter(void *source)
125{
126	struct xlp_intrsrc *src = source;
127
128	if (src->bus_ack)
129		src->bus_ack(src->irq, src->bus_ack_arg);
130	nlm_pic_ack(xlp_pic_base, src->irt);
131}
132
133static void
134xlp_pre_ithread(void *source)
135{
136	struct xlp_intrsrc *src = source;
137
138	if (src->bus_ack)
139		src->bus_ack(src->irq, src->bus_ack_arg);
140}
141
142static void
143xlp_post_ithread(void *source)
144{
145	struct xlp_intrsrc *src = source;
146
147	nlm_pic_ack(xlp_pic_base, src->irt);
148}
149
150void
151xlp_set_bus_ack(int irq, void (*ack)(int, void *), void *arg)
152{
153	struct xlp_intrsrc *src;
154
155	KASSERT(irq > 0 && irq <= XLR_MAX_INTR,
156	    ("%s called for bad hard intr %d", __func__, irq));
157
158	/* no locking needed - this will called early in boot */
159	src = &xlp_interrupts[irq];
160	KASSERT(src->ie != NULL,
161	    ("%s called after IRQ enable for %d.", __func__, irq));
162	src->bus_ack_arg = arg;
163	src->bus_ack = ack;
164}
165
166void
167cpu_establish_hardintr(const char *name, driver_filter_t * filt,
168    void (*handler) (void *), void *arg, int irq, int flags,
169    void **cookiep)
170{
171	struct intr_event *ie;	/* descriptor for the IRQ */
172	struct xlp_intrsrc *src = NULL;
173	int errcode;
174
175	KASSERT(irq > 0 && irq <= XLR_MAX_INTR ,
176	    ("%s called for bad hard intr %d", __func__, irq));
177
178	/*
179	 * Locking - not needed now, because we do this only on
180	 * startup from CPU0
181	 */
182	src = &xlp_interrupts[irq];
183	ie = src->ie;
184	if (ie == NULL) {
185		/*
186		 * PIC based interrupts need ack in PIC, and some SoC
187		 * components need additional acks (e.g. PCI)
188		 */
189		if (XLP_IRQ_IS_PICINTR(irq))
190			errcode = intr_event_create(&ie, src, 0, irq,
191			    xlp_pre_ithread, xlp_post_ithread, xlp_post_filter,
192			    NULL, "hard intr%d:", irq);
193		else {
194			if (filt == NULL)
195				panic("Unsupported non filter percpu intr %d", irq);
196			errcode = intr_event_create(&ie, src, 0, irq,
197			    NULL, NULL, NULL, NULL, "hard intr%d:", irq);
198		}
199		if (errcode) {
200			printf("Could not create event for intr %d\n", irq);
201			return;
202		}
203		src->irq = irq;
204		src->ie = ie;
205	}
206	if (XLP_IRQ_IS_PICINTR(irq)) {
207		/* Set all irqs to CPU 0 for now */
208		src->irt = xlp_irq_to_irt(irq);
209		nlm_pic_write_irt_direct(xlp_pic_base, src->irt, 1, 0,
210		    PIC_LOCAL_SCHEDULING, irq, 0);
211	}
212
213	intr_event_add_handler(ie, name, filt, handler, arg,
214	    intr_priority(flags), flags, cookiep);
215	xlp_enable_irq(irq);
216}
217
218void
219cpu_intr(struct trapframe *tf)
220{
221	struct intr_event *ie;
222	uint64_t eirr, eimr;
223	int i;
224
225	critical_enter();
226
227	/* find a list of enabled interrupts */
228	eirr = nlm_read_c0_eirr();
229	eimr = nlm_read_c0_eimr();
230	eirr &= eimr;
231
232	if (eirr == 0) {
233		critical_exit();
234		return;
235	}
236	/*
237	 * No need to clear the EIRR here as the handler writes to
238	 * compare which ACKs the interrupt.
239	 */
240	if (eirr & (1 << IRQ_TIMER)) {
241		intr_event_handle(xlp_interrupts[IRQ_TIMER].ie, tf);
242		critical_exit();
243		return;
244	}
245
246	/* FIXME sched pin >? LOCK>? */
247	for (i = sizeof(eirr) * 8 - 1; i >= 0; i--) {
248		if ((eirr & (1ULL << i)) == 0)
249			continue;
250
251		ie = xlp_interrupts[i].ie;
252		/* Don't account special IRQs */
253		switch (i) {
254		case IRQ_IPI:
255		case IRQ_MSGRING:
256			break;
257		default:
258			mips_intrcnt_inc(mips_intr_counters[i]);
259		}
260
261		/* Ack the IRQ on the CPU */
262		nlm_write_c0_eirr(1ULL << i);
263		if (intr_event_handle(ie, tf) != 0) {
264			printf("stray interrupt %d\n", i);
265		}
266	}
267	critical_exit();
268}
269
270void
271mips_intrcnt_setname(mips_intrcnt_t counter, const char *name)
272{
273	int idx = counter - intrcnt;
274
275	KASSERT(counter != NULL, ("mips_intrcnt_setname: NULL counter"));
276
277	snprintf(intrnames + (MAXCOMLEN + 1) * idx,
278	    MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name);
279}
280
281mips_intrcnt_t
282mips_intrcnt_create(const char* name)
283{
284	mips_intrcnt_t counter = &intrcnt[intrcnt_index++];
285
286	mips_intrcnt_setname(counter, name);
287	return counter;
288}
289
290void
291cpu_init_interrupts()
292{
293	int i;
294	char name[MAXCOMLEN + 1];
295
296	/*
297	 * Initialize all available vectors so spare IRQ
298	 * would show up in systat output
299	 */
300	for (i = 0; i < XLR_MAX_INTR; i++) {
301		snprintf(name, MAXCOMLEN + 1, "int%d:", i);
302		mips_intr_counters[i] = mips_intrcnt_create(name);
303	}
304}
305
306static int	xlp_pic_probe(device_t);
307static int	xlp_pic_attach(device_t);
308
309static int
310xlp_pic_probe(device_t dev)
311{
312
313	if (!ofw_bus_is_compatible(dev, "netlogic,xlp-pic"))
314		return (ENXIO);
315	device_set_desc(dev, "XLP PIC");
316	return (0);
317}
318
319static int
320xlp_pic_attach(device_t dev)
321{
322
323	return (0);
324}
325
326static device_method_t xlp_pic_methods[] = {
327	DEVMETHOD(device_probe,		xlp_pic_probe),
328	DEVMETHOD(device_attach,	xlp_pic_attach),
329
330	DEVMETHOD_END
331};
332
333static driver_t xlp_pic_driver = {
334	"xlp_pic",
335	xlp_pic_methods,
336	1,		/* no softc */
337};
338
339static devclass_t xlp_pic_devclass;
340DRIVER_MODULE(xlp_pic, simplebus, xlp_pic_driver, xlp_pic_devclass, 0, 0);
341