1178172Simp/*-
2195162Simp * Copyright (c) 2006 Oleksandr Tymoshenko
3178172Simp * Copyright (c) 2002-2004 Juli Mallett <jmallett@FreeBSD.org>
4178172Simp * All rights reserved.
5178172Simp *
6178172Simp * Redistribution and use in source and binary forms, with or without
7178172Simp * modification, are permitted provided that the following conditions
8178172Simp * are met:
9178172Simp * 1. Redistributions of source code must retain the above copyright
10178172Simp *    notice, this list of conditions, and the following disclaimer,
11178172Simp *    without modification, immediately at the beginning of the file.
12178172Simp * 2. The name of the author may not be used to endorse or promote products
13178172Simp *    derived from this software without specific prior written permission.
14178172Simp *
15178172Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16178172Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17178172Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18178172Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19178172Simp * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20178172Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21178172Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22178172Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23178172Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24178172Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25178172Simp * SUCH DAMAGE.
26178172Simp *
27178172Simp */
28178172Simp
29178172Simp#include <sys/cdefs.h>
30178172Simp__FBSDID("$FreeBSD$");
31178172Simp
32233318Sgonzo#include "opt_hwpmc_hooks.h"
33233318Sgonzo
34178172Simp#include <sys/param.h>
35178172Simp#include <sys/systm.h>
36178172Simp#include <sys/bus.h>
37178172Simp#include <sys/interrupt.h>
38233318Sgonzo#include <sys/pmc.h>
39233318Sgonzo#include <sys/pmckern.h>
40178172Simp
41178172Simp#include <machine/clock.h>
42178172Simp#include <machine/cpu.h>
43178172Simp#include <machine/cpufunc.h>
44178172Simp#include <machine/cpuinfo.h>
45178172Simp#include <machine/cpuregs.h>
46178172Simp#include <machine/frame.h>
47178172Simp#include <machine/intr_machdep.h>
48178172Simp#include <machine/md_var.h>
49178172Simp#include <machine/trap.h>
50178172Simp
51178172Simpstatic struct intr_event *hardintr_events[NHARD_IRQS];
52178172Simpstatic struct intr_event *softintr_events[NSOFT_IRQS];
53202046Simpstatic mips_intrcnt_t mips_intr_counters[NSOFT_IRQS + NHARD_IRQS];
54178172Simp
55202046Simpstatic int intrcnt_index;
56178172Simp
57203697Sneelstatic cpu_intr_mask_t		hardintr_mask_func;
58203697Sneelstatic cpu_intr_unmask_t	hardintr_unmask_func;
59203697Sneel
60202046Simpmips_intrcnt_t
61202046Simpmips_intrcnt_create(const char* name)
62202046Simp{
63202046Simp	mips_intrcnt_t counter = &intrcnt[intrcnt_index++];
64202046Simp
65202046Simp	mips_intrcnt_setname(counter, name);
66202046Simp	return counter;
67202046Simp}
68202046Simp
69182765Sobrienvoid
70202046Simpmips_intrcnt_setname(mips_intrcnt_t counter, const char *name)
71178172Simp{
72202046Simp	int idx = counter - intrcnt;
73178172Simp
74202046Simp	KASSERT(counter != NULL, ("mips_intrcnt_setname: NULL counter"));
75202046Simp
76202046Simp	snprintf(intrnames + (MAXCOMLEN + 1) * idx,
77202046Simp	    MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name);
78178172Simp}
79178172Simp
80202046Simpstatic void
81202046Simpmips_mask_hard_irq(void *source)
82202046Simp{
83202046Simp	uintptr_t irq = (uintptr_t)source;
84202046Simp
85202046Simp	mips_wr_status(mips_rd_status() & ~(((1 << irq) << 8) << 2));
86202046Simp}
87202046Simp
88202046Simpstatic void
89202046Simpmips_unmask_hard_irq(void *source)
90202046Simp{
91202046Simp	uintptr_t irq = (uintptr_t)source;
92202046Simp
93202046Simp	mips_wr_status(mips_rd_status() | (((1 << irq) << 8) << 2));
94202046Simp}
95202046Simp
96202046Simpstatic void
97202046Simpmips_mask_soft_irq(void *source)
98202046Simp{
99202046Simp	uintptr_t irq = (uintptr_t)source;
100202046Simp
101202046Simp	mips_wr_status(mips_rd_status() & ~((1 << irq) << 8));
102202046Simp}
103202046Simp
104202046Simpstatic void
105202046Simpmips_unmask_soft_irq(void *source)
106202046Simp{
107202046Simp	uintptr_t irq = (uintptr_t)source;
108202046Simp
109202046Simp	mips_wr_status(mips_rd_status() | ((1 << irq) << 8));
110202046Simp}
111202046Simp
112202046Simp/*
113202046Simp * Perform initialization of interrupts prior to setting
114202046Simp * handlings
115202046Simp */
116182765Sobrienvoid
117202046Simpcpu_init_interrupts()
118178172Simp{
119202046Simp	int i;
120202046Simp	char name[MAXCOMLEN + 1];
121178172Simp
122202046Simp	/*
123202046Simp	 * Initialize all available vectors so spare IRQ
124202046Simp	 * would show up in systat output
125202046Simp	 */
126202046Simp	for (i = 0; i < NSOFT_IRQS; i++) {
127202046Simp		snprintf(name, MAXCOMLEN + 1, "sint%d:", i);
128202046Simp		mips_intr_counters[i] = mips_intrcnt_create(name);
129202046Simp	}
130202046Simp
131202046Simp	for (i = 0; i < NHARD_IRQS; i++) {
132202046Simp		snprintf(name, MAXCOMLEN + 1, "int%d:", i);
133202046Simp		mips_intr_counters[NSOFT_IRQS + i] = mips_intrcnt_create(name);
134202046Simp	}
135178172Simp}
136178172Simp
137178172Simpvoid
138203697Sneelcpu_set_hardintr_mask_func(cpu_intr_mask_t func)
139203697Sneel{
140203697Sneel
141203697Sneel	hardintr_mask_func = func;
142203697Sneel}
143203697Sneel
144203697Sneelvoid
145203697Sneelcpu_set_hardintr_unmask_func(cpu_intr_unmask_t func)
146203697Sneel{
147203697Sneel
148203697Sneel	hardintr_unmask_func = func;
149203697Sneel}
150203697Sneel
151203697Sneelvoid
152182765Sobriencpu_establish_hardintr(const char *name, driver_filter_t *filt,
153182765Sobrien    void (*handler)(void*), void *arg, int irq, int flags, void **cookiep)
154178172Simp{
155182765Sobrien	struct intr_event *event;
156182765Sobrien	int error;
157178172Simp
158178172Simp	/*
159178172Simp	 * We have 6 levels, but thats 0 - 5 (not including 6)
160178172Simp	 */
161178172Simp	if (irq < 0 || irq >= NHARD_IRQS)
162178172Simp		panic("%s called for unknown hard intr %d", __func__, irq);
163178172Simp
164203697Sneel	if (hardintr_mask_func == NULL)
165203697Sneel		hardintr_mask_func = mips_mask_hard_irq;
166203697Sneel
167203697Sneel	if (hardintr_unmask_func == NULL)
168203697Sneel		hardintr_unmask_func = mips_unmask_hard_irq;
169203697Sneel
170182765Sobrien	event = hardintr_events[irq];
171182765Sobrien	if (event == NULL) {
172202046Simp		error = intr_event_create(&event, (void *)(uintptr_t)irq, 0,
173203697Sneel		    irq, hardintr_mask_func, hardintr_unmask_func,
174202046Simp		    NULL, NULL, "int%d", irq);
175182765Sobrien		if (error)
176182765Sobrien			return;
177182765Sobrien		hardintr_events[irq] = event;
178203697Sneel		mips_unmask_hard_irq((void*)(uintptr_t)irq);
179182765Sobrien	}
180178172Simp
181182765Sobrien	intr_event_add_handler(event, name, filt, handler, arg,
182182765Sobrien	    intr_priority(flags), flags, cookiep);
183182765Sobrien
184203697Sneel	mips_intrcnt_setname(mips_intr_counters[NSOFT_IRQS + irq],
185203697Sneel			     event->ie_fullname);
186178172Simp}
187178172Simp
188178172Simpvoid
189182765Sobriencpu_establish_softintr(const char *name, driver_filter_t *filt,
190182765Sobrien    void (*handler)(void*), void *arg, int irq, int flags,
191178172Simp    void **cookiep)
192178172Simp{
193182765Sobrien	struct intr_event *event;
194182765Sobrien	int error;
195178172Simp
196202046Simp#if 0
197182765Sobrien	printf("Establish SOFT IRQ %d: filt %p handler %p arg %p\n",
198182765Sobrien	    irq, filt, handler, arg);
199202046Simp#endif
200178172Simp	if (irq < 0 || irq > NSOFT_IRQS)
201178172Simp		panic("%s called for unknown hard intr %d", __func__, irq);
202178172Simp
203182765Sobrien	event = softintr_events[irq];
204182765Sobrien	if (event == NULL) {
205202046Simp		error = intr_event_create(&event, (void *)(uintptr_t)irq, 0,
206202046Simp		    irq, mips_mask_soft_irq, mips_unmask_soft_irq,
207202046Simp		    NULL, NULL, "sint%d:", irq);
208182765Sobrien		if (error)
209182765Sobrien			return;
210182765Sobrien		softintr_events[irq] = event;
211203697Sneel		mips_unmask_soft_irq((void*)(uintptr_t)irq);
212182765Sobrien	}
213178172Simp
214182765Sobrien	intr_event_add_handler(event, name, filt, handler, arg,
215182765Sobrien	    intr_priority(flags), flags, cookiep);
216178172Simp
217202046Simp	mips_intrcnt_setname(mips_intr_counters[irq], event->ie_fullname);
218178172Simp}
219178172Simp
220178172Simpvoid
221178172Simpcpu_intr(struct trapframe *tf)
222178172Simp{
223178172Simp	struct intr_event *event;
224202046Simp	register_t cause, status;
225183174Simp	int hard, i, intr;
226178172Simp
227178172Simp	critical_enter();
228178172Simp
229178172Simp	cause = mips_rd_cause();
230202046Simp	status = mips_rd_status();
231178172Simp	intr = (cause & MIPS_INT_MASK) >> 8;
232202046Simp	/*
233202046Simp	 * Do not handle masked interrupts. They were masked by
234202046Simp	 * pre_ithread function (mips_mask_XXX_intr) and will be
235202046Simp	 * unmasked once ithread is through with handler
236202046Simp	 */
237202046Simp	intr &= (status & MIPS_INT_MASK) >> 8;
238178172Simp	while ((i = fls(intr)) != 0) {
239178172Simp		intr &= ~(1 << (i - 1));
240178172Simp		switch (i) {
241178172Simp		case 1: case 2:
242178172Simp			/* Software interrupt. */
243178172Simp			i--; /* Get a 0-offset interrupt. */
244178172Simp			hard = 0;
245178172Simp			event = softintr_events[i];
246202046Simp			mips_intrcnt_inc(mips_intr_counters[i]);
247178172Simp			break;
248178172Simp		default:
249178172Simp			/* Hardware interrupt. */
250178172Simp			i -= 2; /* Trim software interrupt bits. */
251178172Simp			i--; /* Get a 0-offset interrupt. */
252178172Simp			hard = 1;
253178172Simp			event = hardintr_events[i];
254202046Simp			mips_intrcnt_inc(mips_intr_counters[NSOFT_IRQS + i]);
255178172Simp			break;
256178172Simp		}
257178172Simp
258182765Sobrien		if (!event || TAILQ_EMPTY(&event->ie_handlers)) {
259182765Sobrien			printf("stray %s interrupt %d\n",
260182765Sobrien			    hard ? "hard" : "soft", i);
261182765Sobrien			continue;
262178172Simp		}
263178172Simp
264183174Simp		if (intr_event_handle(event, tf) != 0) {
265183174Simp			printf("stray %s interrupt %d\n",
266183174Simp			    hard ? "hard" : "soft", i);
267178172Simp		}
268178172Simp	}
269178172Simp
270178172Simp	KASSERT(i == 0, ("all interrupts handled"));
271178172Simp
272178172Simp	critical_exit();
273233318Sgonzo
274233318Sgonzo#ifdef HWPMC_HOOKS
275233318Sgonzo	if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN))
276233318Sgonzo		pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf);
277233318Sgonzo#endif
278178172Simp}
279