interrupt.c revision 268198
1/*-
2 * Copyright (c) 2010-2011 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include "opt_ddb.h"
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/10/sys/ia64/ia64/interrupt.c 268198 2014-07-02 23:33:07Z marcel $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/proc.h>
36#include <sys/vmmeter.h>
37#include <sys/bus.h>
38#include <sys/interrupt.h>
39#include <sys/malloc.h>
40#include <sys/ktr.h>
41#include <sys/lock.h>
42#include <sys/mutex.h>
43#include <sys/sched.h>
44#include <sys/smp.h>
45#include <sys/sysctl.h>
46#include <sys/syslog.h>
47
48#include <machine/cpu.h>
49#include <machine/fpu.h>
50#include <machine/frame.h>
51#include <machine/intr.h>
52#include <machine/intrcnt.h>
53#include <machine/md_var.h>
54#include <machine/pcb.h>
55#include <machine/reg.h>
56#include <machine/smp.h>
57
58#ifdef DDB
59#include <ddb/ddb.h>
60#endif
61
62struct ia64_intr {
63	struct intr_event *event;	/* interrupt event */
64	volatile long *cntp;		/* interrupt counter */
65	struct sapic *sapic;
66	u_int	irq;
67};
68
69ia64_ihtype *ia64_handler[IA64_NXIVS];
70
71static enum ia64_xiv_use ia64_xiv[IA64_NXIVS];
72static struct ia64_intr *ia64_intrs[IA64_NXIVS];
73
74static ia64_ihtype ia64_ih_invalid;
75static ia64_ihtype ia64_ih_irq;
76
77void
78ia64_xiv_init(void)
79{
80	u_int xiv;
81
82	for (xiv = 0; xiv < IA64_NXIVS; xiv++) {
83		ia64_handler[xiv] = ia64_ih_invalid;
84		ia64_xiv[xiv] = IA64_XIV_FREE;
85		ia64_intrs[xiv] = NULL;
86	}
87	(void)ia64_xiv_reserve(15, IA64_XIV_ARCH, NULL);
88}
89
90int
91ia64_xiv_free(u_int xiv, enum ia64_xiv_use what)
92{
93
94	if (xiv >= IA64_NXIVS)
95		return (EINVAL);
96	if (what == IA64_XIV_FREE || what == IA64_XIV_ARCH)
97		return (EINVAL);
98	if (ia64_xiv[xiv] != what)
99		return (ENXIO);
100	ia64_xiv[xiv] = IA64_XIV_FREE;
101	ia64_handler[xiv] = ia64_ih_invalid;
102	return (0);
103}
104
105int
106ia64_xiv_reserve(u_int xiv, enum ia64_xiv_use what, ia64_ihtype ih)
107{
108
109	if (xiv >= IA64_NXIVS)
110		return (EINVAL);
111	if (what == IA64_XIV_FREE)
112		return (EINVAL);
113	if (ia64_xiv[xiv] != IA64_XIV_FREE)
114		return (EBUSY);
115	ia64_xiv[xiv] = what;
116	ia64_handler[xiv] = (ih == NULL) ? ia64_ih_invalid: ih;
117	if (bootverbose)
118		printf("XIV %u: use=%u, IH=%p\n", xiv, what, ih);
119	return (0);
120}
121
122u_int
123ia64_xiv_alloc(u_int prio, enum ia64_xiv_use what, ia64_ihtype ih)
124{
125	u_int hwprio;
126	u_int xiv0, xiv;
127
128	hwprio = prio >> 2;
129	if (hwprio > IA64_MAX_HWPRIO)
130		hwprio = IA64_MAX_HWPRIO;
131
132	xiv0 = IA64_NXIVS - (hwprio + 1) * 16;
133
134	KASSERT(xiv0 >= IA64_MIN_XIV, ("%s: min XIV", __func__));
135	KASSERT(xiv0 < IA64_NXIVS, ("%s: max XIV", __func__));
136
137	xiv = xiv0;
138	while (xiv < IA64_NXIVS && ia64_xiv_reserve(xiv, what, ih))
139		xiv++;
140
141	if (xiv < IA64_NXIVS)
142		return (xiv);
143
144	xiv = xiv0;
145	while (xiv >= IA64_MIN_XIV && ia64_xiv_reserve(xiv, what, ih))
146		xiv--;
147
148	return ((xiv >= IA64_MIN_XIV) ? xiv : 0);
149}
150
151static void
152ia64_intr_eoi(void *arg)
153{
154	u_int xiv = (uintptr_t)arg;
155	struct ia64_intr *i;
156
157	i = ia64_intrs[xiv];
158	KASSERT(i != NULL, ("%s", __func__));
159	sapic_eoi(i->sapic, xiv);
160}
161
162static void
163ia64_intr_mask(void *arg)
164{
165	u_int xiv = (uintptr_t)arg;
166	struct ia64_intr *i;
167
168	i = ia64_intrs[xiv];
169	KASSERT(i != NULL, ("%s", __func__));
170	sapic_mask(i->sapic, i->irq);
171	sapic_eoi(i->sapic, xiv);
172}
173
174static void
175ia64_intr_unmask(void *arg)
176{
177	u_int xiv = (uintptr_t)arg;
178	struct ia64_intr *i;
179
180	i = ia64_intrs[xiv];
181	KASSERT(i != NULL, ("%s", __func__));
182	sapic_unmask(i->sapic, i->irq);
183}
184
185int
186ia64_setup_intr(const char *name, int irq, driver_filter_t filter,
187    driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep)
188{
189	struct ia64_intr *i;
190	struct sapic *sa;
191	char *intrname;
192	u_int prio, xiv;
193	int error;
194
195	prio = intr_priority(flags);
196	if (prio > PRI_MAX_ITHD)
197		return (EINVAL);
198
199	/* XXX lock */
200
201	/* Get the I/O SAPIC and XIV that corresponds to the IRQ. */
202	sa = sapic_lookup(irq, &xiv);
203	if (sa == NULL) {
204		/* XXX unlock */
205		return (EINVAL);
206	}
207
208	if (xiv == 0) {
209		/* XXX unlock */
210		i = malloc(sizeof(struct ia64_intr), M_DEVBUF,
211		    M_ZERO | M_WAITOK);
212		/* XXX lock */
213		sa = sapic_lookup(irq, &xiv);
214		KASSERT(sa != NULL, ("sapic_lookup"));
215		if (xiv != 0)
216			free(i, M_DEVBUF);
217	}
218
219	/*
220	 * If the IRQ has no XIV assigned to it yet, assign one based
221	 * on the priority.
222	 */
223	if (xiv == 0) {
224		xiv = ia64_xiv_alloc(prio, IA64_XIV_IRQ, ia64_ih_irq);
225		if (xiv == 0) {
226			/* XXX unlock */
227			free(i, M_DEVBUF);
228			return (ENOSPC);
229		}
230
231		error = intr_event_create(&i->event, (void *)(uintptr_t)xiv,
232		    0, irq, ia64_intr_mask, ia64_intr_unmask, ia64_intr_eoi,
233		    NULL, "irq%u:", irq);
234		if (error) {
235			ia64_xiv_free(xiv, IA64_XIV_IRQ);
236			/* XXX unlock */
237			free(i, M_DEVBUF);
238			return (error);
239		}
240
241		i->sapic = sa;
242		i->irq = irq;
243		i->cntp = intrcnt + xiv;
244		ia64_intrs[xiv] = i;
245
246		/* XXX unlock */
247
248		sapic_enable(sa, irq, xiv);
249
250		if (name != NULL && *name != '\0') {
251			/* XXX needs abstraction. Too error prone. */
252			intrname = intrnames + xiv * INTRNAME_LEN;
253			memset(intrname, ' ', INTRNAME_LEN - 1);
254			bcopy(name, intrname, strlen(name));
255		}
256	} else {
257		i = ia64_intrs[xiv];
258		/* XXX unlock */
259	}
260
261	KASSERT(i != NULL, ("XIV mapping bug"));
262
263	error = intr_event_add_handler(i->event, name, filter, handler, arg,
264	    prio, flags, cookiep);
265	return (error);
266}
267
268int
269ia64_teardown_intr(void *cookie)
270{
271
272	return (intr_event_remove_handler(cookie));
273}
274
275void
276ia64_bind_intr(void)
277{
278	struct ia64_intr *i;
279	struct pcpu *pc;
280	u_int xiv;
281	int cpu;
282
283	cpu = MAXCPU;
284	for (xiv = IA64_NXIVS - 1; xiv >= IA64_MIN_XIV; xiv--) {
285		if (ia64_xiv[xiv] != IA64_XIV_IRQ)
286			continue;
287		i = ia64_intrs[xiv];
288		do {
289			cpu = (cpu == 0) ? MAXCPU - 1 : cpu - 1;
290			pc = cpuid_to_pcpu[cpu];
291		} while (pc == NULL || !pc->pc_md.awake);
292		sapic_bind_intr(i->irq, pc);
293	}
294}
295
296/*
297 * Interrupt handlers.
298 */
299
300void
301ia64_handle_intr(struct trapframe *tf)
302{
303	struct thread *td;
304	u_int xiv;
305
306	td = curthread;
307	ia64_set_fpsr(IA64_FPSR_DEFAULT);
308	PCPU_INC(cnt.v_intr);
309
310	xiv = ia64_get_ivr();
311	ia64_srlz_d();
312	if (xiv == 15) {
313		PCPU_INC(md.stats.pcs_nstrays);
314		goto out;
315	}
316
317	critical_enter();
318	do {
319		CTR3(KTR_INTR, "INTR: XIV=%u, #%u: frame=%p", xiv,
320		    PCPU_GET(cnt.v_intr), tf);
321		if (!(ia64_handler[xiv])(td, xiv, tf)) {
322			ia64_set_eoi(0);
323			ia64_srlz_d();
324		}
325		xiv = ia64_get_ivr();
326		ia64_srlz_d();
327	} while (xiv != 15);
328	critical_exit();
329
330 out:
331	if (TRAPF_USERMODE(tf)) {
332		while (td->td_flags & (TDF_ASTPENDING|TDF_NEEDRESCHED)) {
333			ia64_enable_intr();
334			ast(tf);
335			ia64_disable_intr();
336		}
337	}
338}
339
340static u_int
341ia64_ih_invalid(struct thread *td, u_int xiv, struct trapframe *tf)
342{
343
344	panic("invalid XIV: %u", xiv);
345	return (0);
346}
347
348static u_int
349ia64_ih_irq(struct thread *td, u_int xiv, struct trapframe *tf)
350{
351	struct ia64_intr *i;
352	struct intr_event *ie;			/* our interrupt event */
353
354	PCPU_INC(md.stats.pcs_nhwints);
355
356	/* Find the interrupt thread for this XIV. */
357	i = ia64_intrs[xiv];
358	KASSERT(i != NULL, ("%s: unassigned XIV", __func__));
359
360	(*i->cntp)++;
361
362	ie = i->event;
363	KASSERT(ie != NULL, ("%s: interrupt without event", __func__));
364
365	if (intr_event_handle(ie, tf) != 0) {
366		ia64_intr_mask((void *)(uintptr_t)xiv);
367		log(LOG_ERR, "stray irq%u\n", i->irq);
368	}
369
370	return (0);
371}
372
373#ifdef DDB
374
375static void
376db_print_xiv(u_int xiv, int always)
377{
378	struct ia64_intr *i;
379
380	i = ia64_intrs[xiv];
381	if (i != NULL) {
382		db_printf("XIV %u (%p): ", xiv, i);
383		sapic_print(i->sapic, i->irq);
384	} else if (always)
385		db_printf("XIV %u: unassigned\n", xiv);
386}
387
388DB_SHOW_COMMAND(xiv, db_show_xiv)
389{
390	u_int xiv;
391
392	if (have_addr) {
393		xiv = ((addr >> 4) % 16) * 10 + (addr % 16);
394		if (xiv >= IA64_NXIVS)
395			db_printf("error: XIV %u not in range [0..%u]\n",
396			    xiv, IA64_NXIVS - 1);
397		else
398			db_print_xiv(xiv, 1);
399	} else {
400		for (xiv = 0; xiv < IA64_NXIVS; xiv++)
401			db_print_xiv(xiv, 0);
402	}
403}
404
405#endif
406