]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powerpc/intr_machdep.c
powerpc: Scale intrcnt by mp_ncpus
[FreeBSD/FreeBSD.git] / sys / powerpc / powerpc / intr_machdep.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1991 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * William Jolitz.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*-
35  * Copyright (c) 2002 Benno Rice.
36  * All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  *
59  *      from: @(#)isa.c 7.2 (Berkeley) 5/13/91
60  *      form: src/sys/i386/isa/intr_machdep.c,v 1.57 2001/07/20
61  *
62  * $FreeBSD$
63  */
64
65 #include "opt_isa.h"
66
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/queue.h>
71 #include <sys/bus.h>
72 #include <sys/cpuset.h>
73 #include <sys/interrupt.h>
74 #include <sys/ktr.h>
75 #include <sys/lock.h>
76 #include <sys/malloc.h>
77 #include <sys/mutex.h>
78 #include <sys/pcpu.h>
79 #include <sys/smp.h>
80 #include <sys/syslog.h>
81 #include <sys/vmmeter.h>
82 #include <sys/proc.h>
83
84 #include <machine/frame.h>
85 #include <machine/intr_machdep.h>
86 #include <machine/md_var.h>
87 #include <machine/smp.h>
88 #include <machine/trap.h>
89
90 #include "pic_if.h"
91
92 #define MAX_STRAY_LOG   5
93
94 static MALLOC_DEFINE(M_INTR, "intr", "interrupt handler data");
95
96 struct powerpc_intr {
97         struct intr_event *event;
98         long    *cntp;
99         void    *priv;          /* PIC-private data */
100         u_int   irq;
101         device_t pic;
102         u_int   intline;
103         u_int   vector;
104         u_int   cntindex;
105         cpuset_t cpu;
106         enum intr_trigger trig;
107         enum intr_polarity pol;
108         int     fwcode;
109         int     ipi;
110 };
111
112 struct pic {
113         device_t dev;
114         uint32_t node;
115         u_int   irqs;
116         u_int   ipis;
117         int     base;
118 };
119
120 static u_int intrcnt_index = 0;
121 static struct mtx intr_table_lock;
122 static struct powerpc_intr **powerpc_intrs;
123 static struct pic piclist[MAX_PICS];
124 static u_int nvectors;          /* Allocated vectors */
125 static u_int npics;             /* PICs registered */
126 #ifdef DEV_ISA
127 static u_int nirqs = 16;        /* Allocated IRQS (ISA pre-allocated). */
128 #else
129 static u_int nirqs = 0;         /* Allocated IRQs. */
130 #endif
131 static u_int stray_count;
132
133 u_long *intrcnt;
134 char *intrnames;
135 size_t sintrcnt = sizeof(intrcnt);
136 size_t sintrnames = sizeof(intrnames);
137 int nintrcnt;
138
139 /*
140  * Just to start
141  */
142 #ifdef __powerpc64__
143 u_int num_io_irqs = 768;
144 #else
145 u_int num_io_irqs = 256;
146 #endif
147
148 device_t root_pic;
149
150 #ifdef SMP
151 static void *ipi_cookie;
152 #endif
153
154 static void
155 intrcnt_setname(const char *name, int index)
156 {
157
158         snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s",
159             MAXCOMLEN, name);
160 }
161
162 static void
163 intr_init(void *dummy __unused)
164 {
165
166         mtx_init(&intr_table_lock, "intr sources lock", NULL, MTX_DEF);
167 }
168 SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL);
169
170 static void
171 intr_init_sources(void *arg __unused)
172 {
173
174         powerpc_intrs = mallocarray(num_io_irqs, sizeof(*powerpc_intrs),
175             M_INTR, M_WAITOK | M_ZERO);
176         nintrcnt = 1 + num_io_irqs * 2 + mp_ncpus * 2;
177 #ifdef COUNT_IPIS
178         if (mp_ncpus > 1)
179                 nintrcnt += 8 * mp_ncpus;
180 #endif
181         intrcnt = mallocarray(nintrcnt, sizeof(u_long), M_INTR, M_WAITOK |
182             M_ZERO);
183         intrnames = mallocarray(nintrcnt, MAXCOMLEN + 1, M_INTR, M_WAITOK |
184             M_ZERO);
185         sintrcnt = nintrcnt * sizeof(u_long);
186         sintrnames = nintrcnt * (MAXCOMLEN + 1);
187
188         intrcnt_setname("???", 0);
189         intrcnt_index = 1;
190 }
191 /*
192  * This needs to happen before SI_SUB_CPU
193  */
194 SYSINIT(intr_init_sources, SI_SUB_KLD, SI_ORDER_ANY, intr_init_sources, NULL);
195
196 #ifdef SMP
197 static void
198 smp_intr_init(void *dummy __unused)
199 {
200         struct powerpc_intr *i;
201         int vector;
202
203         for (vector = 0; vector < nvectors; vector++) {
204                 i = powerpc_intrs[vector];
205                 if (i != NULL && i->event != NULL && i->pic == root_pic)
206                         PIC_BIND(i->pic, i->intline, i->cpu, &i->priv);
207         }
208 }
209 SYSINIT(smp_intr_init, SI_SUB_SMP, SI_ORDER_ANY, smp_intr_init, NULL);
210 #endif
211
212 void
213 intrcnt_add(const char *name, u_long **countp)
214 {
215         int idx;
216
217         idx = atomic_fetchadd_int(&intrcnt_index, 1);
218         KASSERT(idx < nintrcnt, ("intrcnt_add: Interrupt counter index %d/%d"
219                 "reached nintrcnt : %d", intrcnt_index, idx, nintrcnt));
220         *countp = &intrcnt[idx];
221         intrcnt_setname(name, idx);
222 }
223
224 extern void kdb_backtrace(void);
225 static struct powerpc_intr *
226 intr_lookup(u_int irq)
227 {
228         char intrname[16];
229         struct powerpc_intr *i, *iscan;
230         int vector;
231
232         mtx_lock(&intr_table_lock);
233         for (vector = 0; vector < nvectors; vector++) {
234                 i = powerpc_intrs[vector];
235                 if (i != NULL && i->irq == irq) {
236                         mtx_unlock(&intr_table_lock);
237                         return (i);
238                 }
239         }
240
241         i = malloc(sizeof(*i), M_INTR, M_NOWAIT);
242         if (i == NULL) {
243                 mtx_unlock(&intr_table_lock);
244                 return (NULL);
245         }
246
247         i->event = NULL;
248         i->cntp = NULL;
249         i->priv = NULL;
250         i->trig = INTR_TRIGGER_CONFORM;
251         i->pol = INTR_POLARITY_CONFORM;
252         i->irq = irq;
253         i->pic = NULL;
254         i->vector = -1;
255         i->fwcode = 0;
256         i->ipi = 0;
257
258 #ifdef SMP
259         i->cpu = all_cpus;
260 #else
261         CPU_SETOF(0, &i->cpu);
262 #endif
263
264         for (vector = 0; vector < num_io_irqs && vector <= nvectors;
265             vector++) {
266                 iscan = powerpc_intrs[vector];
267                 if (iscan != NULL && iscan->irq == irq)
268                         break;
269                 if (iscan == NULL && i->vector == -1)
270                         i->vector = vector;
271                 iscan = NULL;
272         }
273
274         if (iscan == NULL && i->vector != -1) {
275                 powerpc_intrs[i->vector] = i;
276                 i->cntindex = atomic_fetchadd_int(&intrcnt_index, 1);
277                 i->cntp = &intrcnt[i->cntindex];
278                 sprintf(intrname, "irq%u:", i->irq);
279                 intrcnt_setname(intrname, i->cntindex);
280                 nvectors++;
281         }
282         mtx_unlock(&intr_table_lock);
283
284         if (iscan != NULL || i->vector == -1) {
285                 free(i, M_INTR);
286                 i = iscan;
287         }
288
289         return (i);
290 }
291
292 static int
293 powerpc_map_irq(struct powerpc_intr *i)
294 {
295         struct pic *p;
296         u_int cnt;
297         int idx;
298
299         for (idx = 0; idx < npics; idx++) {
300                 p = &piclist[idx];
301                 cnt = p->irqs + p->ipis;
302                 if (i->irq >= p->base && i->irq < p->base + cnt)
303                         break;
304         }
305         if (idx == npics)
306                 return (EINVAL);
307
308         i->intline = i->irq - p->base;
309         i->pic = p->dev;
310
311         /* Try a best guess if that failed */
312         if (i->pic == NULL)
313                 i->pic = root_pic;
314
315         return (0);
316 }
317
318 static void
319 powerpc_intr_eoi(void *arg)
320 {
321         struct powerpc_intr *i = arg;
322
323         PIC_EOI(i->pic, i->intline, i->priv);
324 }
325
326 static void
327 powerpc_intr_pre_ithread(void *arg)
328 {
329         struct powerpc_intr *i = arg;
330
331         PIC_MASK(i->pic, i->intline, i->priv);
332         PIC_EOI(i->pic, i->intline, i->priv);
333 }
334
335 static void
336 powerpc_intr_post_ithread(void *arg)
337 {
338         struct powerpc_intr *i = arg;
339
340         PIC_UNMASK(i->pic, i->intline, i->priv);
341 }
342
343 static int
344 powerpc_assign_intr_cpu(void *arg, int cpu)
345 {
346 #ifdef SMP
347         struct powerpc_intr *i = arg;
348
349         if (cpu == NOCPU)
350                 i->cpu = all_cpus;
351         else
352                 CPU_SETOF(cpu, &i->cpu);
353
354         if (!cold && i->pic != NULL && i->pic == root_pic)
355                 PIC_BIND(i->pic, i->intline, i->cpu, &i->priv);
356
357         return (0);
358 #else
359         return (EOPNOTSUPP);
360 #endif
361 }
362
363 u_int
364 powerpc_register_pic(device_t dev, uint32_t node, u_int irqs, u_int ipis,
365     u_int atpic)
366 {
367         struct pic *p;
368         u_int irq;
369         int idx;
370
371         mtx_lock(&intr_table_lock);
372
373         /* XXX see powerpc_get_irq(). */
374         for (idx = 0; idx < npics; idx++) {
375                 p = &piclist[idx];
376                 if (p->node != node)
377                         continue;
378                 if (node != 0 || p->dev == dev)
379                         break;
380         }
381         p = &piclist[idx];
382
383         p->dev = dev;
384         p->node = node;
385         p->irqs = irqs;
386         p->ipis = ipis;
387         if (idx == npics) {
388 #ifdef DEV_ISA
389                 p->base = (atpic) ? 0 : nirqs;
390 #else
391                 p->base = nirqs;
392 #endif
393                 irq = p->base + irqs + ipis;
394                 nirqs = MAX(nirqs, irq);
395                 npics++;
396         }
397
398         KASSERT(npics < MAX_PICS,
399             ("Number of PICs exceeds maximum (%d)", MAX_PICS));
400
401         mtx_unlock(&intr_table_lock);
402
403         return (p->base);
404 }
405
406 u_int
407 powerpc_get_irq(uint32_t node, u_int pin)
408 {
409         int idx;
410
411         if (node == 0)
412                 return (pin);
413
414         mtx_lock(&intr_table_lock);
415         for (idx = 0; idx < npics; idx++) {
416                 if (piclist[idx].node == node) {
417                         mtx_unlock(&intr_table_lock);
418                         return (piclist[idx].base + pin);
419                 }
420         }
421
422         /*
423          * XXX we should never encounter an unregistered PIC, but that
424          * can only be done when we properly support bus enumeration
425          * using multiple passes. Until then, fake an entry and give it
426          * some adhoc maximum number of IRQs and IPIs.
427          */
428         piclist[idx].dev = NULL;
429         piclist[idx].node = node;
430         piclist[idx].irqs = 124;
431         piclist[idx].ipis = 4;
432         piclist[idx].base = nirqs;
433         nirqs += (1 << 25);
434         npics++;
435
436         KASSERT(npics < MAX_PICS,
437             ("Number of PICs exceeds maximum (%d)", MAX_PICS));
438
439         mtx_unlock(&intr_table_lock);
440
441         return (piclist[idx].base + pin);
442 }
443
444 int
445 powerpc_enable_intr(void)
446 {
447         struct powerpc_intr *i;
448         int error, vector;
449 #ifdef SMP
450         int n;
451 #endif
452
453         if (npics == 0)
454                 panic("no PIC detected\n");
455
456         if (root_pic == NULL)
457                 root_pic = piclist[0].dev;
458
459 #ifdef SMP
460         /* Install an IPI handler. */
461         if (mp_ncpus > 1) {
462                 for (n = 0; n < npics; n++) {
463                         if (piclist[n].dev != root_pic)
464                                 continue;
465
466                         KASSERT(piclist[n].ipis != 0,
467                             ("%s: SMP root PIC does not supply any IPIs",
468                             __func__));
469                         error = powerpc_setup_intr("IPI",
470                             MAP_IRQ(piclist[n].node, piclist[n].irqs),
471                             powerpc_ipi_handler, NULL, NULL,
472                             INTR_TYPE_MISC | INTR_EXCL, &ipi_cookie);
473                         if (error) {
474                                 printf("unable to setup IPI handler\n");
475                                 return (error);
476                         }
477
478                         /*
479                          * Some subterfuge: disable late EOI and mark this
480                          * as an IPI to the dispatch layer.
481                          */
482                         i = intr_lookup(MAP_IRQ(piclist[n].node,
483                             piclist[n].irqs));
484                         i->event->ie_post_filter = NULL;
485                         i->ipi = 1;
486                 }
487         }
488 #endif
489
490         for (vector = 0; vector < nvectors; vector++) {
491                 i = powerpc_intrs[vector];
492                 if (i == NULL)
493                         continue;
494
495                 error = powerpc_map_irq(i);
496                 if (error)
497                         continue;
498
499                 if (i->trig == INTR_TRIGGER_INVALID)
500                         PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode,
501                             &i->trig, &i->pol);
502                 if (i->trig != INTR_TRIGGER_CONFORM ||
503                     i->pol != INTR_POLARITY_CONFORM)
504                         PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
505
506                 if (i->event != NULL)
507                         PIC_ENABLE(i->pic, i->intline, vector, &i->priv);
508         }
509
510         return (0);
511 }
512
513 int
514 powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter,
515     driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep)
516 {
517         struct powerpc_intr *i;
518         int error, enable = 0;
519
520         i = intr_lookup(irq);
521         if (i == NULL)
522                 return (ENOMEM);
523
524         if (i->event == NULL) {
525                 error = intr_event_create(&i->event, (void *)i, 0, irq,
526                     powerpc_intr_pre_ithread, powerpc_intr_post_ithread,
527                     powerpc_intr_eoi, powerpc_assign_intr_cpu, "irq%u:", irq);
528                 if (error)
529                         return (error);
530
531                 enable = 1;
532         }
533
534         error = intr_event_add_handler(i->event, name, filter, handler, arg,
535             intr_priority(flags), flags, cookiep);
536
537         mtx_lock(&intr_table_lock);
538         intrcnt_setname(i->event->ie_fullname, i->cntindex);
539         mtx_unlock(&intr_table_lock);
540
541         if (!cold) {
542                 error = powerpc_map_irq(i);
543
544                 if (!error) {
545                         if (i->trig == INTR_TRIGGER_INVALID)
546                                 PIC_TRANSLATE_CODE(i->pic, i->intline,
547                                     i->fwcode, &i->trig, &i->pol);
548         
549                         if (i->trig != INTR_TRIGGER_CONFORM ||
550                             i->pol != INTR_POLARITY_CONFORM)
551                                 PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
552
553                         if (i->pic == root_pic)
554                                 PIC_BIND(i->pic, i->intline, i->cpu, &i->priv);
555
556                         if (enable)
557                                 PIC_ENABLE(i->pic, i->intline, i->vector,
558                                     &i->priv);
559                 }
560         }
561         return (error);
562 }
563
564 int
565 powerpc_teardown_intr(void *cookie)
566 {
567
568         return (intr_event_remove_handler(cookie));
569 }
570
571 #ifdef SMP
572 int
573 powerpc_bind_intr(u_int irq, u_char cpu)
574 {
575         struct powerpc_intr *i;
576
577         i = intr_lookup(irq);
578         if (i == NULL)
579                 return (ENOMEM);
580
581         return (intr_event_bind(i->event, cpu));
582 }
583 #endif
584
585 int
586 powerpc_fw_config_intr(int irq, int sense_code)
587 {
588         struct powerpc_intr *i;
589
590         i = intr_lookup(irq);
591         if (i == NULL)
592                 return (ENOMEM);
593
594         i->trig = INTR_TRIGGER_INVALID;
595         i->pol = INTR_POLARITY_CONFORM;
596         i->fwcode = sense_code;
597
598         if (!cold && i->pic != NULL) {
599                 PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig,
600                     &i->pol);
601                 PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
602         }
603
604         return (0);
605 }
606
607 int
608 powerpc_config_intr(int irq, enum intr_trigger trig, enum intr_polarity pol)
609 {
610         struct powerpc_intr *i;
611
612         i = intr_lookup(irq);
613         if (i == NULL)
614                 return (ENOMEM);
615
616         i->trig = trig;
617         i->pol = pol;
618
619         if (!cold && i->pic != NULL)
620                 PIC_CONFIG(i->pic, i->intline, trig, pol);
621
622         return (0);
623 }
624
625 void
626 powerpc_dispatch_intr(u_int vector, struct trapframe *tf)
627 {
628         struct powerpc_intr *i;
629         struct intr_event *ie;
630
631         i = powerpc_intrs[vector];
632         if (i == NULL)
633                 goto stray;
634
635         (*i->cntp)++;
636
637         ie = i->event;
638         KASSERT(ie != NULL, ("%s: interrupt without an event", __func__));
639
640         /*
641          * IPIs are magical and need to be EOI'ed before filtering.
642          * This prevents races in IPI handling.
643          */
644         if (i->ipi)
645                 PIC_EOI(i->pic, i->intline, i->priv);
646
647         if (intr_event_handle(ie, tf) != 0) {
648                 goto stray;
649         }
650         return;
651
652 stray:
653         stray_count++;
654         if (stray_count <= MAX_STRAY_LOG) {
655                 printf("stray irq %d\n", i ? i->irq : -1);
656                 if (stray_count >= MAX_STRAY_LOG) {
657                         printf("got %d stray interrupts, not logging anymore\n",
658                             MAX_STRAY_LOG);
659                 }
660         }
661         if (i != NULL)
662                 PIC_MASK(i->pic, i->intline, i->priv);
663 }
664
665 void
666 powerpc_intr_mask(u_int irq)
667 {
668         struct powerpc_intr *i;
669
670         i = intr_lookup(irq);
671         if (i == NULL || i->pic == NULL)
672                 return;
673
674         PIC_MASK(i->pic, i->intline, i->priv);
675 }
676
677 void
678 powerpc_intr_unmask(u_int irq)
679 {
680         struct powerpc_intr *i;
681
682         i = intr_lookup(irq);
683         if (i == NULL || i->pic == NULL)
684                 return;
685
686         PIC_UNMASK(i->pic, i->intline, i->priv);
687 }