]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/ia64/ia64/interrupt.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / ia64 / ia64 / interrupt.c
1 /* $FreeBSD$ */
2 /* $NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $ */
3
4 /*-
5  * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
6  * All rights reserved.
7  *
8  * Authors: Keith Bostic, Chris G. Demetriou
9  * 
10  * Permission to use, copy, modify and distribute this software and
11  * its documentation is hereby granted, provided that both the copyright
12  * notice and this permission notice appear in all copies of the
13  * software, derivative works or modified versions, and any portions
14  * thereof, and that both notices appear in supporting documentation.
15  * 
16  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 
17  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 
18  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
19  * 
20  * Carnegie Mellon requests users of this software to return to
21  *
22  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
23  *  School of Computer Science
24  *  Carnegie Mellon University
25  *  Pittsburgh PA 15213-3890
26  *
27  * any improvements or extensions that they make and grant Carnegie the
28  * rights to redistribute these changes.
29  */
30 /*-
31  * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center.
32  * Redistribute and modify at will, leaving only this additional copyright
33  * notice.
34  */
35
36 #include "opt_ddb.h"
37
38 #include <sys/cdefs.h>                  /* RCS ID & Copyright macro defns */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/proc.h>
44 #include <sys/vmmeter.h>
45 #include <sys/bus.h>
46 #include <sys/interrupt.h>
47 #include <sys/malloc.h>
48 #include <sys/ktr.h>
49 #include <sys/lock.h>
50 #include <sys/mutex.h>
51 #include <sys/sched.h>
52 #include <sys/smp.h>
53 #include <sys/sysctl.h>
54 #include <sys/syslog.h>
55
56 #include <machine/cpu.h>
57 #include <machine/fpu.h>
58 #include <machine/frame.h>
59 #include <machine/intr.h>
60 #include <machine/intrcnt.h>
61 #include <machine/md_var.h>
62 #include <machine/pcb.h>
63 #include <machine/reg.h>
64 #include <machine/smp.h>
65
66 #ifdef DDB
67 #include <ddb/ddb.h>
68 #endif
69
70 struct ia64_intr {
71         struct intr_event *event;       /* interrupt event */
72         volatile long *cntp;            /* interrupt counter */
73         struct sapic *sapic;
74         u_int   irq;
75 };
76
77 ia64_ihtype *ia64_handler[IA64_NXIVS];
78
79 static enum ia64_xiv_use ia64_xiv[IA64_NXIVS];
80 static struct ia64_intr *ia64_intrs[IA64_NXIVS];
81
82 static ia64_ihtype ia64_ih_invalid;
83 static ia64_ihtype ia64_ih_irq;
84
85 void
86 ia64_xiv_init(void)
87 {
88         u_int xiv;
89
90         for (xiv = 0; xiv < IA64_NXIVS; xiv++) {
91                 ia64_handler[xiv] = ia64_ih_invalid;
92                 ia64_xiv[xiv] = IA64_XIV_FREE;
93                 ia64_intrs[xiv] = NULL;
94         }
95         (void)ia64_xiv_reserve(15, IA64_XIV_ARCH, NULL);
96 }
97
98 int
99 ia64_xiv_free(u_int xiv, enum ia64_xiv_use what)
100 {
101
102         if (xiv >= IA64_NXIVS)
103                 return (EINVAL);
104         if (what == IA64_XIV_FREE || what == IA64_XIV_ARCH)
105                 return (EINVAL);
106         if (ia64_xiv[xiv] != what)
107                 return (ENXIO);
108         ia64_xiv[xiv] = IA64_XIV_FREE;
109         ia64_handler[xiv] = ia64_ih_invalid;
110         return (0);
111 }
112
113 int
114 ia64_xiv_reserve(u_int xiv, enum ia64_xiv_use what, ia64_ihtype ih)
115 {
116
117         if (xiv >= IA64_NXIVS)
118                 return (EINVAL);
119         if (what == IA64_XIV_FREE)
120                 return (EINVAL);
121         if (ia64_xiv[xiv] != IA64_XIV_FREE)
122                 return (EBUSY);
123         ia64_xiv[xiv] = what;
124         ia64_handler[xiv] = (ih == NULL) ? ia64_ih_invalid: ih;
125         if (bootverbose)
126                 printf("XIV %u: use=%u, IH=%p\n", xiv, what, ih);
127         return (0);
128 }
129
130 u_int
131 ia64_xiv_alloc(u_int prio, enum ia64_xiv_use what, ia64_ihtype ih)
132 {
133         u_int hwprio;
134         u_int xiv0, xiv;
135
136         hwprio = prio >> 2;
137         if (hwprio > IA64_MAX_HWPRIO)
138                 hwprio = IA64_MAX_HWPRIO;
139
140         xiv0 = IA64_NXIVS - (hwprio + 1) * 16;
141
142         KASSERT(xiv0 >= IA64_MIN_XIV, ("%s: min XIV", __func__));
143         KASSERT(xiv0 < IA64_NXIVS, ("%s: max XIV", __func__));
144
145         xiv = xiv0;
146         while (xiv < IA64_NXIVS && ia64_xiv_reserve(xiv, what, ih))
147                 xiv++;
148
149         if (xiv < IA64_NXIVS)
150                 return (xiv);
151
152         xiv = xiv0;
153         while (xiv >= IA64_MIN_XIV && ia64_xiv_reserve(xiv, what, ih))
154                 xiv--;
155
156         return ((xiv >= IA64_MIN_XIV) ? xiv : 0);
157 }
158
159 static void
160 ia64_intr_eoi(void *arg)
161 {
162         u_int xiv = (uintptr_t)arg;
163         struct ia64_intr *i;
164
165         i = ia64_intrs[xiv];
166         KASSERT(i != NULL, ("%s", __func__));
167         sapic_eoi(i->sapic, xiv);
168 }
169
170 static void
171 ia64_intr_mask(void *arg)
172 {
173         u_int xiv = (uintptr_t)arg;
174         struct ia64_intr *i;
175
176         i = ia64_intrs[xiv];
177         KASSERT(i != NULL, ("%s", __func__));
178         sapic_mask(i->sapic, i->irq);
179         sapic_eoi(i->sapic, xiv);
180 }
181
182 static void
183 ia64_intr_unmask(void *arg)
184 {
185         u_int xiv = (uintptr_t)arg;
186         struct ia64_intr *i;
187
188         i = ia64_intrs[xiv];
189         KASSERT(i != NULL, ("%s", __func__));
190         sapic_unmask(i->sapic, i->irq);
191 }
192
193 int
194 ia64_setup_intr(const char *name, int irq, driver_filter_t filter,
195     driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep)
196 {
197         struct ia64_intr *i;
198         struct sapic *sa;
199         char *intrname;
200         u_int prio, xiv;
201         int error;
202
203         prio = intr_priority(flags);
204         if (prio > PRI_MAX_ITHD)
205                 return (EINVAL);
206
207         /* XXX lock */
208
209         /* Get the I/O SAPIC and XIV that corresponds to the IRQ. */
210         sa = sapic_lookup(irq, &xiv);
211         if (sa == NULL) {
212                 /* XXX unlock */
213                 return (EINVAL);
214         }
215
216         if (xiv == 0) {
217                 /* XXX unlock */
218                 i = malloc(sizeof(struct ia64_intr), M_DEVBUF,
219                     M_ZERO | M_WAITOK);
220                 /* XXX lock */
221                 sa = sapic_lookup(irq, &xiv);
222                 KASSERT(sa != NULL, ("sapic_lookup"));
223                 if (xiv != 0)
224                         free(i, M_DEVBUF);
225         }
226
227         /*
228          * If the IRQ has no XIV assigned to it yet, assign one based
229          * on the priority.
230          */
231         if (xiv == 0) {
232                 xiv = ia64_xiv_alloc(prio, IA64_XIV_IRQ, ia64_ih_irq);
233                 if (xiv == 0) {
234                         /* XXX unlock */
235                         free(i, M_DEVBUF);
236                         return (ENOSPC);
237                 }
238
239                 error = intr_event_create(&i->event, (void *)(uintptr_t)xiv,
240                     0, irq, ia64_intr_mask, ia64_intr_unmask, ia64_intr_eoi,
241                     NULL, "irq%u:", irq);
242                 if (error) {
243                         ia64_xiv_free(xiv, IA64_XIV_IRQ);
244                         /* XXX unlock */
245                         free(i, M_DEVBUF);
246                         return (error);
247                 }
248
249                 i->sapic = sa;
250                 i->irq = irq;
251                 i->cntp = intrcnt + xiv;
252                 ia64_intrs[xiv] = i;
253
254                 /* XXX unlock */
255
256                 sapic_enable(sa, irq, xiv);
257
258                 if (name != NULL && *name != '\0') {
259                         /* XXX needs abstraction. Too error prone. */
260                         intrname = intrnames + xiv * INTRNAME_LEN;
261                         memset(intrname, ' ', INTRNAME_LEN - 1);
262                         bcopy(name, intrname, strlen(name));
263                 }
264         } else {
265                 i = ia64_intrs[xiv];
266                 /* XXX unlock */
267         }
268
269         KASSERT(i != NULL, ("XIV mapping bug"));
270
271         error = intr_event_add_handler(i->event, name, filter, handler, arg,
272             prio, flags, cookiep);
273         return (error);
274 }
275
276 int
277 ia64_teardown_intr(void *cookie)
278 {
279
280         return (intr_event_remove_handler(cookie));
281 }
282
283 void
284 ia64_bind_intr(void)
285 {
286         struct ia64_intr *i;
287         struct pcpu *pc;
288         u_int xiv;
289         int cpu;
290
291         cpu = MAXCPU;
292         for (xiv = IA64_NXIVS - 1; xiv >= IA64_MIN_XIV; xiv--) {
293                 if (ia64_xiv[xiv] != IA64_XIV_IRQ)
294                         continue;
295                 i = ia64_intrs[xiv];
296                 do {
297                         cpu = (cpu == 0) ? MAXCPU - 1 : cpu - 1;
298                         pc = cpuid_to_pcpu[cpu];
299                 } while (pc == NULL || !pc->pc_md.awake);
300                 sapic_bind_intr(i->irq, pc);
301         }
302 }
303
304 /*
305  * Interrupt handlers.
306  */
307
308 void
309 ia64_handle_intr(struct trapframe *tf)
310 {
311         struct thread *td;
312         u_int xiv;
313
314         td = curthread;
315         ia64_set_fpsr(IA64_FPSR_DEFAULT);
316         PCPU_INC(cnt.v_intr);
317
318         xiv = ia64_get_ivr();
319         ia64_srlz_d();
320         if (xiv == 15) {
321                 PCPU_INC(md.stats.pcs_nstrays);
322                 goto out;
323         }
324
325         critical_enter();
326
327         do {
328                 CTR2(KTR_INTR, "INTR: ITC=%u, XIV=%u",
329                     (u_int)tf->tf_special.ifa, xiv);
330                 (ia64_handler[xiv])(td, xiv, tf);
331                 ia64_set_eoi(0);
332                 ia64_srlz_d();
333                 xiv = ia64_get_ivr();
334                 ia64_srlz_d();
335         } while (xiv != 15);
336
337         critical_exit();
338
339  out:
340         if (TRAPF_USERMODE(tf)) {
341                 while (td->td_flags & (TDF_ASTPENDING|TDF_NEEDRESCHED)) {
342                         ia64_enable_intr();
343                         ast(tf);
344                         ia64_disable_intr();
345                 }
346         }
347 }
348
349 static u_int
350 ia64_ih_invalid(struct thread *td, u_int xiv, struct trapframe *tf)
351 {
352
353         panic("invalid XIV: %u", xiv);
354         return (0);
355 }
356
357 static u_int
358 ia64_ih_irq(struct thread *td, u_int xiv, struct trapframe *tf)
359 {
360         struct ia64_intr *i;
361         struct intr_event *ie;                  /* our interrupt event */
362
363         PCPU_INC(md.stats.pcs_nhwints);
364
365         /* Find the interrupt thread for this XIV. */
366         i = ia64_intrs[xiv];
367         KASSERT(i != NULL, ("%s: unassigned XIV", __func__));
368
369         (*i->cntp)++;
370
371         ie = i->event;
372         KASSERT(ie != NULL, ("%s: interrupt without event", __func__));
373
374         if (intr_event_handle(ie, tf) != 0) {
375                 ia64_intr_mask((void *)(uintptr_t)xiv);
376                 log(LOG_ERR, "stray irq%u\n", i->irq);
377         }
378
379         return (0);
380 }
381
382 #ifdef DDB
383
384 static void
385 db_print_xiv(u_int xiv, int always)
386 {
387         struct ia64_intr *i;
388
389         i = ia64_intrs[xiv];
390         if (i != NULL) {
391                 db_printf("XIV %u (%p): ", xiv, i);
392                 sapic_print(i->sapic, i->irq);
393         } else if (always)
394                 db_printf("XIV %u: unassigned\n", xiv);
395 }
396
397 DB_SHOW_COMMAND(xiv, db_show_xiv)
398 {
399         u_int xiv;
400
401         if (have_addr) {
402                 xiv = ((addr >> 4) % 16) * 10 + (addr % 16);
403                 if (xiv >= IA64_NXIVS)
404                         db_printf("error: XIV %u not in range [0..%u]\n",
405                             xiv, IA64_NXIVS - 1);
406                 else
407                         db_print_xiv(xiv, 1);
408         } else {
409                 for (xiv = 0; xiv < IA64_NXIVS; xiv++)
410                         db_print_xiv(xiv, 0);
411         }
412 }
413
414 #endif