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