]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/ia64/ia64/interrupt.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / 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/malloc.h>
47 #include <sys/ktr.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/smp.h>
51 #include <sys/sysctl.h>
52
53 #include <machine/clock.h>
54 #include <machine/cpu.h>
55 #include <machine/fpu.h>
56 #include <machine/frame.h>
57 #include <machine/intr.h>
58 #include <machine/md_var.h>
59 #include <machine/pcb.h>
60 #include <machine/reg.h>
61 #include <machine/sapicvar.h>
62 #include <machine/smp.h>
63
64 #ifdef EVCNT_COUNTERS
65 struct evcnt clock_intr_evcnt;  /* event counter for clock intrs. */
66 #else
67 #include <sys/interrupt.h>
68 #include <machine/intrcnt.h>
69 #endif
70
71 #ifdef DDB
72 #include <ddb/ddb.h>
73 #endif
74
75 #ifdef SMP
76 extern int mp_ipi_test;
77 #endif
78
79 volatile int mc_expected, mc_received;
80
81 static void 
82 dummy_perf(unsigned long vector, struct trapframe *tf)  
83 {
84         printf("performance interrupt!\n");
85 }
86
87 void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf;
88
89 static unsigned int ints[MAXCPU];
90 SYSCTL_OPAQUE(_debug, OID_AUTO, ints, CTLFLAG_RW, &ints, sizeof(ints), "IU",
91     "");
92
93 static unsigned int clks[MAXCPU];
94 #ifdef SMP
95 SYSCTL_OPAQUE(_debug, OID_AUTO, clks, CTLFLAG_RW, &clks, sizeof(clks), "IU",
96     "");
97 #else
98 SYSCTL_INT(_debug, OID_AUTO, clks, CTLFLAG_RW, clks, 0, "");
99 #endif
100
101 #ifdef SMP
102 static unsigned int asts[MAXCPU];
103 SYSCTL_OPAQUE(_debug, OID_AUTO, asts, CTLFLAG_RW, &asts, sizeof(asts), "IU",
104     "");
105
106 static unsigned int rdvs[MAXCPU];
107 SYSCTL_OPAQUE(_debug, OID_AUTO, rdvs, CTLFLAG_RW, &rdvs, sizeof(rdvs), "IU",
108     "");
109 #endif
110
111 SYSCTL_NODE(_debug, OID_AUTO, clock, CTLFLAG_RW, 0, "clock statistics");
112
113 static int adjust_edges = 0;
114 SYSCTL_INT(_debug_clock, OID_AUTO, adjust_edges, CTLFLAG_RD,
115     &adjust_edges, 0, "Number of times ITC got more than 12.5% behind");
116
117 static int adjust_excess = 0;
118 SYSCTL_INT(_debug_clock, OID_AUTO, adjust_excess, CTLFLAG_RD,
119     &adjust_excess, 0, "Total number of ignored ITC interrupts");
120
121 static int adjust_lost = 0;
122 SYSCTL_INT(_debug_clock, OID_AUTO, adjust_lost, CTLFLAG_RD,
123     &adjust_lost, 0, "Total number of lost ITC interrupts");
124
125 static int adjust_ticks = 0;
126 SYSCTL_INT(_debug_clock, OID_AUTO, adjust_ticks, CTLFLAG_RD,
127     &adjust_ticks, 0, "Total number of ITC interrupts with adjustment");
128
129 int
130 interrupt(u_int64_t vector, struct trapframe *tf)
131 {
132         struct thread *td;
133         volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
134         uint64_t adj, clk, itc;
135         int64_t delta;
136         int count;
137
138         ia64_set_fpsr(IA64_FPSR_DEFAULT);
139
140         td = curthread;
141         atomic_add_int(&td->td_intr_nesting_level, 1);
142
143         /*
144          * Handle ExtINT interrupts by generating an INTA cycle to
145          * read the vector.
146          */
147         if (vector == 0) {
148                 vector = ib->ib_inta;
149                 printf("ExtINT interrupt: vector=%ld\n", vector);
150                 if (vector == 15)
151                         goto stray;
152         }
153
154         if (vector == CLOCK_VECTOR) {/* clock interrupt */
155                 /* CTR0(KTR_INTR, "clock interrupt"); */
156
157                 PCPU_LAZY_INC(cnt.v_intr);
158 #ifdef EVCNT_COUNTERS
159                 clock_intr_evcnt.ev_count++;
160 #else
161                 intrcnt[INTRCNT_CLOCK]++;
162 #endif
163                 clks[PCPU_GET(cpuid)]++;
164
165                 critical_enter();
166
167                 adj = PCPU_GET(clockadj);
168                 itc = ia64_get_itc();
169                 ia64_set_itm(itc + ia64_clock_reload - adj);
170                 clk = PCPU_GET(clock);
171                 delta = itc - clk;
172                 count = 0;
173                 while (delta >= ia64_clock_reload) {
174                         /* Only the BSP runs the real clock */
175                         if (PCPU_GET(cpuid) == 0)
176                                 hardclock((struct clockframe *)tf);
177                         else
178                                 hardclock_process((struct clockframe *)tf);
179                         if (profprocs != 0)
180                                 profclock((struct clockframe *)tf);
181                         statclock((struct clockframe *)tf);
182                         delta -= ia64_clock_reload;
183                         clk += ia64_clock_reload;
184                         if (adj != 0)
185                                 adjust_ticks++;
186                         count++;
187                 }
188                 if (count > 0) {
189                         adjust_lost += count - 1;
190                         if (delta > (ia64_clock_reload >> 3)) {
191                                 if (adj == 0)
192                                         adjust_edges++;
193                                 adj = ia64_clock_reload >> 4;
194                         } else
195                                 adj = 0;
196                 } else {
197                         adj = 0;
198                         adjust_excess++;
199                 }
200                 PCPU_SET(clock, clk);
201                 PCPU_SET(clockadj, adj);
202
203                 critical_exit();
204
205 #ifdef SMP
206         } else if (vector == ipi_vector[IPI_AST]) {
207                 asts[PCPU_GET(cpuid)]++;
208                 CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid));
209         } else if (vector == ipi_vector[IPI_HIGH_FP]) {
210                 struct thread *thr = PCPU_GET(fpcurthread);
211                 if (thr != NULL) {
212                         mtx_lock_spin(&thr->td_md.md_highfp_mtx);
213                         save_high_fp(&thr->td_pcb->pcb_high_fp);
214                         thr->td_pcb->pcb_fpcpu = NULL;
215                         PCPU_SET(fpcurthread, NULL);
216                         mtx_unlock_spin(&thr->td_md.md_highfp_mtx);
217                 }
218         } else if (vector == ipi_vector[IPI_RENDEZVOUS]) {
219                 rdvs[PCPU_GET(cpuid)]++;
220                 CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid));
221                 smp_rendezvous_action();
222         } else if (vector == ipi_vector[IPI_STOP]) {
223                 register_t intr;
224                 cpumask_t mybit = PCPU_GET(cpumask);
225
226                 intr = intr_disable();
227                 savectx(PCPU_GET(pcb));
228                 atomic_set_int(&stopped_cpus, mybit);
229                 while ((started_cpus & mybit) == 0)
230                         /* spin */;
231                 atomic_clear_int(&started_cpus, mybit);
232                 atomic_clear_int(&stopped_cpus, mybit);
233                 intr_restore(intr);
234         } else if (vector == ipi_vector[IPI_TEST]) {
235                 CTR1(KTR_SMP, "IPI_TEST, cpuid=%d", PCPU_GET(cpuid));
236                 mp_ipi_test++;
237 #endif
238         } else {
239                 ints[PCPU_GET(cpuid)]++;
240                 ia64_dispatch_intr(tf, vector);
241         }
242
243 stray:
244         atomic_subtract_int(&td->td_intr_nesting_level, 1);
245         return (TRAPF_USERMODE(tf));
246 }
247
248 /*
249  * Hardware irqs have vectors starting at this offset.
250  */
251 #define IA64_HARDWARE_IRQ_BASE  0x20
252
253 struct ia64_intr {
254     struct intr_event   *event; /* interrupt event */
255     volatile long       *cntp;  /* interrupt counter */
256 };
257
258 static struct mtx ia64_intrs_lock;
259 static struct ia64_intr *ia64_intrs[256];
260
261 extern struct sapic *ia64_sapics[];
262 extern int ia64_sapic_count;
263
264 static void
265 ithds_init(void *dummy)
266 {
267
268         mtx_init(&ia64_intrs_lock, "intr table", NULL, MTX_SPIN);
269 }
270 SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL);
271
272 static void
273 ia64_send_eoi(uintptr_t vector)
274 {
275         int irq, i;
276
277         irq = vector - IA64_HARDWARE_IRQ_BASE;
278         for (i = 0; i < ia64_sapic_count; i++) {
279                 struct sapic *sa = ia64_sapics[i];
280                 if (irq >= sa->sa_base && irq <= sa->sa_limit)
281                         sapic_eoi(sa, vector);
282         }
283 }
284
285 int
286 ia64_setup_intr(const char *name, int irq, driver_intr_t handler, void *arg,
287                 enum intr_type flags, void **cookiep, volatile long *cntp)
288 {
289         struct ia64_intr *i;
290         int errcode;
291         intptr_t vector = irq + IA64_HARDWARE_IRQ_BASE;
292         char *intrname;
293
294         /*
295          * XXX - Can we have more than one device on a vector?  If so, we have
296          * a race condition here that needs to be worked around similar to
297          * the fashion done in the i386 inthand_add() function.
298          */
299         
300         /* First, check for an existing hash table entry for this vector. */
301         mtx_lock_spin(&ia64_intrs_lock);
302         i = ia64_intrs[vector];
303         mtx_unlock_spin(&ia64_intrs_lock);
304
305         if (i == NULL) {
306                 /* None was found, so create an entry. */
307                 i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT);
308                 if (i == NULL)
309                         return ENOMEM;
310                 if (cntp == NULL)
311                         i->cntp = intrcnt + irq + INTRCNT_ISA_IRQ;
312                 else
313                         i->cntp = cntp;
314                 if (name != NULL && *name != '\0') {
315                         /* XXX needs abstraction. Too error phrone. */
316                         intrname = intrnames + (irq + INTRCNT_ISA_IRQ) *
317                             INTRNAME_LEN;
318                         memset(intrname, ' ', INTRNAME_LEN - 1);
319                         bcopy(name, intrname, strlen(name));
320                 }
321                 errcode = intr_event_create(&i->event, (void *)vector, 0,
322                     (void (*)(void *))ia64_send_eoi, "intr:");
323                 if (errcode) {
324                         free(i, M_DEVBUF);
325                         return errcode;
326                 }
327
328                 mtx_lock_spin(&ia64_intrs_lock);
329                 ia64_intrs[vector] = i;
330                 mtx_unlock_spin(&ia64_intrs_lock);
331         }
332
333         /* Second, add this handler. */
334         errcode = intr_event_add_handler(i->event, name, handler, arg,
335             intr_priority(flags), flags, cookiep);
336         if (errcode)
337                 return errcode;
338
339         return (sapic_enable(irq, vector));
340 }
341
342 int
343 ia64_teardown_intr(void *cookie)
344 {
345
346         return (intr_event_remove_handler(cookie));
347 }
348
349 void
350 ia64_dispatch_intr(void *frame, unsigned long vector)
351 {
352         struct ia64_intr *i;
353         struct intr_event *ie;                  /* our interrupt event */
354         struct intr_handler *ih;
355         int error, thread;
356
357         /*
358          * Find the interrupt thread for this vector.
359          */
360         i = ia64_intrs[vector];
361         if (i == NULL)
362                 return;                 /* no event for this vector */
363
364         if (i->cntp)
365                 atomic_add_long(i->cntp, 1);
366
367         ie = i->event;
368         KASSERT(ie != NULL, ("interrupt vector without an event"));
369
370         /*
371          * As an optimization, if an event has no handlers, don't
372          * schedule it to run.
373          */
374         if (TAILQ_EMPTY(&ie->ie_handlers))
375                 return;
376
377         /*
378          * Execute all fast interrupt handlers directly without Giant.  Note
379          * that this means that any fast interrupt handler must be MP safe.
380          */
381         thread = 0;
382         critical_enter();
383         TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
384                 if (!(ih->ih_flags & IH_FAST)) {
385                         thread = 1;
386                         continue;
387                 }
388                 CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__,
389                     ih->ih_handler, ih->ih_argument, ih->ih_name);
390                 ih->ih_handler(ih->ih_argument);
391         }
392
393         if (thread) {
394                 error = intr_event_schedule_thread(ie);
395                 KASSERT(error == 0, ("got an impossible stray interrupt"));
396         } else
397                 ia64_send_eoi(vector);
398         critical_exit();
399 }
400
401 #ifdef DDB
402
403 static void
404 db_show_vector(int vector)
405 {
406         int irq, i;
407
408         irq = vector - IA64_HARDWARE_IRQ_BASE;
409         for (i = 0; i < ia64_sapic_count; i++) {
410                 struct sapic *sa = ia64_sapics[i];
411                 if (irq >= sa->sa_base && irq <= sa->sa_limit)
412                         sapic_print(sa, irq - sa->sa_base);
413         }
414 }
415
416 DB_SHOW_COMMAND(irq, db_show_irq)
417 {
418         int vector;
419
420         if (have_addr) {
421                 vector = ((addr >> 4) % 16) * 10 + (addr % 16);
422                 db_show_vector(vector);
423         } else {
424                 for (vector = IA64_HARDWARE_IRQ_BASE;
425                      vector < IA64_HARDWARE_IRQ_BASE + 64; vector++)
426                         db_show_vector(vector);
427         }
428 }
429
430 #endif