]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/ia64/ia64/interrupt.c
This commit was generated by cvs2svn to compensate for changes in r171577,
[FreeBSD/FreeBSD.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/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_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(TRAPF_USERMODE(tf), TRAPF_PC(tf));
177                         else
178                                 hardclock_cpu(TRAPF_USERMODE(tf));
179                         if (profprocs != 0)
180                                 profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
181                         statclock(TRAPF_USERMODE(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_PTR(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_filter_t filter, 
287                 driver_intr_t handler, void *arg, enum intr_type flags, 
288                 void **cookiep, volatile long *cntp)            
289 {
290         struct ia64_intr *i;
291         int errcode;
292         intptr_t vector = irq + IA64_HARDWARE_IRQ_BASE;
293         char *intrname;
294
295         /*
296          * XXX - Can we have more than one device on a vector?  If so, we have
297          * a race condition here that needs to be worked around similar to
298          * the fashion done in the i386 inthand_add() function.
299          */
300         
301         /* First, check for an existing hash table entry for this vector. */
302         mtx_lock_spin(&ia64_intrs_lock);
303         i = ia64_intrs[vector];
304         mtx_unlock_spin(&ia64_intrs_lock);
305
306         if (i == NULL) {
307                 /* None was found, so create an entry. */
308                 i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT);
309                 if (i == NULL)
310                         return ENOMEM;
311                 if (cntp == NULL)
312                         i->cntp = intrcnt + irq + INTRCNT_ISA_IRQ;
313                 else
314                         i->cntp = cntp;
315                 if (name != NULL && *name != '\0') {
316                         /* XXX needs abstraction. Too error phrone. */
317                         intrname = intrnames + (irq + INTRCNT_ISA_IRQ) *
318                             INTRNAME_LEN;
319                         memset(intrname, ' ', INTRNAME_LEN - 1);
320                         bcopy(name, intrname, strlen(name));
321                 }
322                 errcode = intr_event_create(&i->event, (void *)vector, 0,
323                     (void (*)(void *))ia64_send_eoi, "intr:");
324                 if (errcode) {
325                         free(i, M_DEVBUF);
326                         return errcode;
327                 }
328
329                 mtx_lock_spin(&ia64_intrs_lock);
330                 ia64_intrs[vector] = i;
331                 mtx_unlock_spin(&ia64_intrs_lock);
332         }
333
334         /* Second, add this handler. */
335         errcode = intr_event_add_handler(i->event, name, filter, handler, arg,
336             intr_priority(flags), flags, cookiep);
337         if (errcode)
338                 return errcode;
339
340         return (sapic_enable(irq, vector));
341 }
342
343 int
344 ia64_teardown_intr(void *cookie)
345 {
346
347         return (intr_event_remove_handler(cookie));
348 }
349
350 void
351 ia64_dispatch_intr(void *frame, unsigned long vector)
352 {
353         struct ia64_intr *i;
354         struct intr_event *ie;                  /* our interrupt event */
355         struct intr_handler *ih;
356         int error, thread, ret;
357
358         /*
359          * Find the interrupt thread for this vector.
360          */
361         i = ia64_intrs[vector];
362         if (i == NULL)
363                 return;                 /* no event for this vector */
364
365         if (i->cntp)
366                 atomic_add_long(i->cntp, 1);
367
368         ie = i->event;
369         KASSERT(ie != NULL, ("interrupt vector without an event"));
370
371         /*
372          * As an optimization, if an event has no handlers, don't
373          * schedule it to run.
374          */
375         if (TAILQ_EMPTY(&ie->ie_handlers))
376                 return;
377
378         /*
379          * Execute all fast interrupt handlers directly without Giant.  Note
380          * that this means that any fast interrupt handler must be MP safe.
381          */
382         ret = 0;
383         thread = 0;
384         critical_enter();
385         TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
386                 if (ih->ih_filter == NULL) {
387                         thread = 1;
388                         continue;
389                 }
390                 CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__,
391                     ih->ih_filter, ih->ih_argument, ih->ih_name);
392                 ret = ih->ih_filter(ih->ih_argument);
393                 /*
394                  * Wrapper handler special case: see
395                  * i386/intr_machdep.c::intr_execute_handlers()
396                  */
397                 if (!thread) {
398                         if (ret == FILTER_SCHEDULE_THREAD)
399                                 thread = 1;
400                 }
401         }
402         critical_exit();
403
404         if (thread) {
405                 error = intr_event_schedule_thread(ie);
406                 KASSERT(error == 0, ("got an impossible stray interrupt"));
407         } else
408                 ia64_send_eoi(vector);
409 }
410
411 #ifdef DDB
412
413 static void
414 db_show_vector(int vector)
415 {
416         int irq, i;
417
418         irq = vector - IA64_HARDWARE_IRQ_BASE;
419         for (i = 0; i < ia64_sapic_count; i++) {
420                 struct sapic *sa = ia64_sapics[i];
421                 if (irq >= sa->sa_base && irq <= sa->sa_limit)
422                         sapic_print(sa, irq - sa->sa_base);
423         }
424 }
425
426 DB_SHOW_COMMAND(irq, db_show_irq)
427 {
428         int vector;
429
430         if (have_addr) {
431                 vector = ((addr >> 4) % 16) * 10 + (addr % 16);
432                 db_show_vector(vector);
433         } else {
434                 for (vector = IA64_HARDWARE_IRQ_BASE;
435                      vector < IA64_HARDWARE_IRQ_BASE + 64; vector++)
436                         db_show_vector(vector);
437         }
438 }
439
440 #endif