1198155Srrs/*-
2198484Srrs * Copyright (c) 2006-2009 RMI Corporation
3198155Srrs * Copyright (c) 2002-2004 Juli Mallett <jmallett@FreeBSD.org>
4198155Srrs * All rights reserved.
5198155Srrs *
6198155Srrs * Redistribution and use in source and binary forms, with or without
7198155Srrs * modification, are permitted provided that the following conditions
8198155Srrs * are met:
9198155Srrs * 1. Redistributions of source code must retain the above copyright
10198155Srrs *    notice, this list of conditions, and the following disclaimer,
11198155Srrs *    without modification, immediately at the beginning of the file.
12198155Srrs * 2. The name of the author may not be used to endorse or promote products
13198155Srrs *    derived from this software without specific prior written permission.
14198155Srrs *
15198155Srrs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16198155Srrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17198155Srrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18198155Srrs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19198155Srrs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20198155Srrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21198155Srrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22198155Srrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23198155Srrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24198155Srrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25198155Srrs * SUCH DAMAGE.
26198155Srrs *
27198155Srrs */
28198155Srrs
29198155Srrs#include <sys/cdefs.h>
30198155Srrs__FBSDID("$FreeBSD$");
31198155Srrs
32198155Srrs#include <sys/param.h>
33198155Srrs#include <sys/systm.h>
34198155Srrs#include <sys/bus.h>
35198155Srrs#include <sys/interrupt.h>
36198155Srrs#include <sys/kernel.h>
37198155Srrs
38198155Srrs#include <machine/cpu.h>
39198155Srrs#include <machine/cpufunc.h>
40198155Srrs#include <machine/cpuinfo.h>
41198155Srrs#include <machine/cpuregs.h>
42198155Srrs#include <machine/frame.h>
43198155Srrs#include <machine/intr_machdep.h>
44198155Srrs#include <machine/md_var.h>
45198155Srrs#include <machine/trap.h>
46198155Srrs#include <machine/hwfunc.h>
47211994Sjchandra
48211994Sjchandra#include <mips/rmi/rmi_mips_exts.h>
49198607Srrs#include <mips/rmi/interrupt.h>
50199203Srrs#include <mips/rmi/pic.h>
51198155Srrs
52211893Sjchandrastruct xlr_intrsrc {
53211893Sjchandra	void (*busack)(int);		/* Additional ack */
54211893Sjchandra	struct intr_event *ie;		/* event corresponding to intr */
55211893Sjchandra	int irq;
56211893Sjchandra};
57211893Sjchandra
58211893Sjchandrastatic struct xlr_intrsrc xlr_interrupts[XLR_MAX_INTR];
59200901Srrsstatic mips_intrcnt_t mips_intr_counters[XLR_MAX_INTR];
60200901Srrsstatic int intrcnt_index;
61198155Srrs
62204130Srrsvoid
63211893Sjchandraxlr_enable_irq(int irq)
64198155Srrs{
65212102Sjchandra	uint64_t eimr;
66198155Srrs
67212102Sjchandra	eimr = read_c0_eimr64();
68212102Sjchandra	write_c0_eimr64(eimr | (1ULL << irq));
69198155Srrs}
70198155Srrs
71204130Srrsvoid
72211893Sjchandracpu_establish_softintr(const char *name, driver_filter_t * filt,
73211893Sjchandra    void (*handler) (void *), void *arg, int irq, int flags,
74211893Sjchandra    void **cookiep)
75198155Srrs{
76198155Srrs
77211893Sjchandra	panic("Soft interrupts unsupported!\n");
78198155Srrs}
79198155Srrs
80198155Srrsvoid
81211893Sjchandracpu_establish_hardintr(const char *name, driver_filter_t * filt,
82211893Sjchandra    void (*handler) (void *), void *arg, int irq, int flags,
83211893Sjchandra    void **cookiep)
84198155Srrs{
85211893Sjchandra
86211893Sjchandra	xlr_establish_intr(name, filt, handler, arg, irq, flags,
87211893Sjchandra	    cookiep, NULL);
88211893Sjchandra}
89211893Sjchandra
90211893Sjchandrastatic void
91211893Sjchandraxlr_post_filter(void *source)
92211893Sjchandra{
93211893Sjchandra	struct xlr_intrsrc *src = source;
94211893Sjchandra
95211893Sjchandra	if (src->busack)
96211893Sjchandra		src->busack(src->irq);
97211893Sjchandra	pic_ack(PIC_IRQ_TO_INTR(src->irq));
98211893Sjchandra}
99211893Sjchandra
100211893Sjchandrastatic void
101211893Sjchandraxlr_pre_ithread(void *source)
102211893Sjchandra{
103211893Sjchandra	struct xlr_intrsrc *src = source;
104211893Sjchandra
105211893Sjchandra	if (src->busack)
106211893Sjchandra		src->busack(src->irq);
107211893Sjchandra}
108211893Sjchandra
109211893Sjchandrastatic void
110211893Sjchandraxlr_post_ithread(void *source)
111211893Sjchandra{
112211893Sjchandra	struct xlr_intrsrc *src = source;
113211893Sjchandra
114211893Sjchandra	pic_ack(PIC_IRQ_TO_INTR(src->irq));
115211893Sjchandra}
116211893Sjchandra
117211893Sjchandravoid
118211893Sjchandraxlr_establish_intr(const char *name, driver_filter_t filt,
119211893Sjchandra    driver_intr_t handler, void *arg, int irq, int flags,
120211893Sjchandra    void **cookiep, void (*busack)(int))
121211893Sjchandra{
122198625Srrs	struct intr_event *ie;	/* descriptor for the IRQ */
123211893Sjchandra	struct xlr_intrsrc *src = NULL;
124198155Srrs	int errcode;
125198625Srrs
126198607Srrs	if (irq < 0 || irq > XLR_MAX_INTR)
127198607Srrs		panic("%s called for unknown hard intr %d", __func__, irq);
128198155Srrs
129198625Srrs	/*
130198625Srrs	 * FIXME locking - not needed now, because we do this only on
131198625Srrs	 * startup from CPU0
132198625Srrs	 */
133211893Sjchandra	src = &xlr_interrupts[irq];
134211893Sjchandra	ie = src->ie;
135198155Srrs	if (ie == NULL) {
136211893Sjchandra		/*
137211893Sjchandra		 * PIC based interrupts need ack in PIC, and some SoC
138211893Sjchandra		 * components need additional acks (e.g. PCI)
139211893Sjchandra		 */
140211893Sjchandra		if (PIC_IRQ_IS_PICINTR(irq))
141211893Sjchandra			errcode = intr_event_create(&ie, src, 0, irq,
142211893Sjchandra			    xlr_pre_ithread, xlr_post_ithread, xlr_post_filter,
143211893Sjchandra			    NULL, "hard intr%d:", irq);
144211893Sjchandra		else {
145211893Sjchandra			if (filt == NULL)
146211893Sjchandra				panic("Not supported - non filter percpu intr");
147211893Sjchandra			errcode = intr_event_create(&ie, src, 0, irq,
148211893Sjchandra			    NULL, NULL, NULL, NULL, "hard intr%d:", irq);
149211893Sjchandra		}
150198155Srrs		if (errcode) {
151198155Srrs			printf("Could not create event for intr %d\n", irq);
152198155Srrs			return;
153198155Srrs		}
154211893Sjchandra		src->irq = irq;
155211893Sjchandra		src->busack = busack;
156211893Sjchandra		src->ie = ie;
157198155Srrs	}
158198607Srrs	intr_event_add_handler(ie, name, filt, handler, arg,
159198155Srrs	    intr_priority(flags), flags, cookiep);
160211893Sjchandra	xlr_enable_irq(irq);
161198155Srrs}
162198155Srrs
163204130Srrsvoid
164198155Srrscpu_intr(struct trapframe *tf)
165198155Srrs{
166198155Srrs	struct intr_event *ie;
167204130Srrs	uint64_t eirr, eimr;
168198607Srrs	int i;
169198155Srrs
170198155Srrs	critical_enter();
171204130Srrs
172204130Srrs	/* find a list of enabled interrupts */
173198155Srrs	eirr = read_c0_eirr64();
174204130Srrs	eimr = read_c0_eimr64();
175204130Srrs	eirr &= eimr;
176204130Srrs
177204130Srrs	if (eirr == 0) {
178198155Srrs		critical_exit();
179198155Srrs		return;
180198155Srrs	}
181198625Srrs	/*
182210528Sjchandra	 * No need to clear the EIRR here as the handler writes to
183210528Sjchandra	 * compare which ACKs the interrupt.
184198155Srrs	 */
185198155Srrs	if (eirr & (1 << IRQ_TIMER)) {
186211893Sjchandra		intr_event_handle(xlr_interrupts[IRQ_TIMER].ie, tf);
187198155Srrs		critical_exit();
188198155Srrs		return;
189198155Srrs	}
190199098Srrs
191198155Srrs	/* FIXME sched pin >? LOCK>? */
192198625Srrs	for (i = sizeof(eirr) * 8 - 1; i >= 0; i--) {
193199098Srrs		if ((eirr & (1ULL << i)) == 0)
194198155Srrs			continue;
195208165Srrs
196211893Sjchandra		ie = xlr_interrupts[i].ie;
197208165Srrs		/* Don't account special IRQs */
198208165Srrs		switch (i) {
199208165Srrs		case IRQ_IPI:
200208165Srrs		case IRQ_MSGRING:
201208165Srrs			break;
202208165Srrs		default:
203208165Srrs			mips_intrcnt_inc(mips_intr_counters[i]);
204198155Srrs		}
205211893Sjchandra
206211893Sjchandra		/* Ack the IRQ on the CPU */
207198155Srrs		write_c0_eirr64(1ULL << i);
208198155Srrs		if (intr_event_handle(ie, tf) != 0) {
209198625Srrs			printf("stray interrupt %d\n", i);
210198155Srrs		}
211198155Srrs	}
212198155Srrs	critical_exit();
213198155Srrs}
214200901Srrs
215200901Srrsvoid
216200901Srrsmips_intrcnt_setname(mips_intrcnt_t counter, const char *name)
217200901Srrs{
218200901Srrs	int idx = counter - intrcnt;
219200901Srrs
220200901Srrs	KASSERT(counter != NULL, ("mips_intrcnt_setname: NULL counter"));
221200901Srrs
222200901Srrs	snprintf(intrnames + (MAXCOMLEN + 1) * idx,
223200901Srrs	    MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name);
224200901Srrs}
225200901Srrs
226200901Srrsmips_intrcnt_t
227200901Srrsmips_intrcnt_create(const char* name)
228200901Srrs{
229200901Srrs	mips_intrcnt_t counter = &intrcnt[intrcnt_index++];
230200901Srrs
231200901Srrs	mips_intrcnt_setname(counter, name);
232200901Srrs	return counter;
233200901Srrs}
234200901Srrs
235200901Srrsvoid
236200901Srrscpu_init_interrupts()
237200901Srrs{
238200901Srrs	int i;
239200901Srrs	char name[MAXCOMLEN + 1];
240200901Srrs
241200901Srrs	/*
242200901Srrs	 * Initialize all available vectors so spare IRQ
243200901Srrs	 * would show up in systat output
244200901Srrs	 */
245200901Srrs	for (i = 0; i < XLR_MAX_INTR; i++) {
246200901Srrs		snprintf(name, MAXCOMLEN + 1, "int%d:", i);
247200901Srrs		mips_intr_counters[i] = mips_intrcnt_create(name);
248200901Srrs	}
249200901Srrs}
250