]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powerpc/intr_machdep.c
Merge llvm, clang, compiler-rt, libc++, lld, and lldb release_80 branch
[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[INTR_VECTORS];
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[INTR_VECTORS];
134 char intrnames[INTR_VECTORS * (MAXCOMLEN + 1)];
135 size_t sintrcnt = sizeof(intrcnt);
136 size_t sintrnames = sizeof(intrnames);
137
138 device_t root_pic;
139
140 #ifdef SMP
141 static void *ipi_cookie;
142 #endif
143
144 static void
145 intr_init(void *dummy __unused)
146 {
147
148         mtx_init(&intr_table_lock, "intr sources lock", NULL, MTX_DEF);
149 }
150 SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL);
151
152 #ifdef SMP
153 static void
154 smp_intr_init(void *dummy __unused)
155 {
156         struct powerpc_intr *i;
157         int vector;
158
159         for (vector = 0; vector < nvectors; vector++) {
160                 i = powerpc_intrs[vector];
161                 if (i != NULL && i->event != NULL && i->pic == root_pic)
162                         PIC_BIND(i->pic, i->intline, i->cpu, &i->priv);
163         }
164 }
165 SYSINIT(smp_intr_init, SI_SUB_SMP, SI_ORDER_ANY, smp_intr_init, NULL);
166 #endif
167
168 static void
169 intrcnt_setname(const char *name, int index)
170 {
171
172         snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s",
173             MAXCOMLEN, name);
174 }
175
176 void
177 intrcnt_add(const char *name, u_long **countp)
178 {
179         int idx;
180
181         idx = atomic_fetchadd_int(&intrcnt_index, 1);
182         KASSERT(idx < INTR_VECTORS, ("intrcnt_add: Interrupt counter index "
183             "reached INTR_VECTORS"));
184         *countp = &intrcnt[idx];
185         intrcnt_setname(name, idx);
186 }
187
188 static struct powerpc_intr *
189 intr_lookup(u_int irq)
190 {
191         char intrname[16];
192         struct powerpc_intr *i, *iscan;
193         int vector;
194
195         mtx_lock(&intr_table_lock);
196         for (vector = 0; vector < nvectors; vector++) {
197                 i = powerpc_intrs[vector];
198                 if (i != NULL && i->irq == irq) {
199                         mtx_unlock(&intr_table_lock);
200                         return (i);
201                 }
202         }
203
204         i = malloc(sizeof(*i), M_INTR, M_NOWAIT);
205         if (i == NULL) {
206                 mtx_unlock(&intr_table_lock);
207                 return (NULL);
208         }
209
210         i->event = NULL;
211         i->cntp = NULL;
212         i->priv = NULL;
213         i->trig = INTR_TRIGGER_CONFORM;
214         i->pol = INTR_POLARITY_CONFORM;
215         i->irq = irq;
216         i->pic = NULL;
217         i->vector = -1;
218         i->fwcode = 0;
219         i->ipi = 0;
220
221 #ifdef SMP
222         i->cpu = all_cpus;
223 #else
224         CPU_SETOF(0, &i->cpu);
225 #endif
226
227         for (vector = 0; vector < INTR_VECTORS && vector <= nvectors;
228             vector++) {
229                 iscan = powerpc_intrs[vector];
230                 if (iscan != NULL && iscan->irq == irq)
231                         break;
232                 if (iscan == NULL && i->vector == -1)
233                         i->vector = vector;
234                 iscan = NULL;
235         }
236
237         if (iscan == NULL && i->vector != -1) {
238                 powerpc_intrs[i->vector] = i;
239                 i->cntindex = atomic_fetchadd_int(&intrcnt_index, 1);
240                 i->cntp = &intrcnt[i->cntindex];
241                 sprintf(intrname, "irq%u:", i->irq);
242                 intrcnt_setname(intrname, i->cntindex);
243                 nvectors++;
244         }
245         mtx_unlock(&intr_table_lock);
246
247         if (iscan != NULL || i->vector == -1) {
248                 free(i, M_INTR);
249                 i = iscan;
250         }
251
252         return (i);
253 }
254
255 static int
256 powerpc_map_irq(struct powerpc_intr *i)
257 {
258         struct pic *p;
259         u_int cnt;
260         int idx;
261
262         for (idx = 0; idx < npics; idx++) {
263                 p = &piclist[idx];
264                 cnt = p->irqs + p->ipis;
265                 if (i->irq >= p->base && i->irq < p->base + cnt)
266                         break;
267         }
268         if (idx == npics)
269                 return (EINVAL);
270
271         i->intline = i->irq - p->base;
272         i->pic = p->dev;
273
274         /* Try a best guess if that failed */
275         if (i->pic == NULL)
276                 i->pic = root_pic;
277
278         return (0);
279 }
280
281 static void
282 powerpc_intr_eoi(void *arg)
283 {
284         struct powerpc_intr *i = arg;
285
286         PIC_EOI(i->pic, i->intline, i->priv);
287 }
288
289 static void
290 powerpc_intr_pre_ithread(void *arg)
291 {
292         struct powerpc_intr *i = arg;
293
294         PIC_MASK(i->pic, i->intline, i->priv);
295         PIC_EOI(i->pic, i->intline, i->priv);
296 }
297
298 static void
299 powerpc_intr_post_ithread(void *arg)
300 {
301         struct powerpc_intr *i = arg;
302
303         PIC_UNMASK(i->pic, i->intline, i->priv);
304 }
305
306 static int
307 powerpc_assign_intr_cpu(void *arg, int cpu)
308 {
309 #ifdef SMP
310         struct powerpc_intr *i = arg;
311
312         if (cpu == NOCPU)
313                 i->cpu = all_cpus;
314         else
315                 CPU_SETOF(cpu, &i->cpu);
316
317         if (!cold && i->pic != NULL && i->pic == root_pic)
318                 PIC_BIND(i->pic, i->intline, i->cpu, &i->priv);
319
320         return (0);
321 #else
322         return (EOPNOTSUPP);
323 #endif
324 }
325
326 u_int
327 powerpc_register_pic(device_t dev, uint32_t node, u_int irqs, u_int ipis,
328     u_int atpic)
329 {
330         struct pic *p;
331         u_int irq;
332         int idx;
333
334         mtx_lock(&intr_table_lock);
335
336         /* XXX see powerpc_get_irq(). */
337         for (idx = 0; idx < npics; idx++) {
338                 p = &piclist[idx];
339                 if (p->node != node)
340                         continue;
341                 if (node != 0 || p->dev == dev)
342                         break;
343         }
344         p = &piclist[idx];
345
346         p->dev = dev;
347         p->node = node;
348         p->irqs = irqs;
349         p->ipis = ipis;
350         if (idx == npics) {
351 #ifdef DEV_ISA
352                 p->base = (atpic) ? 0 : nirqs;
353 #else
354                 p->base = nirqs;
355 #endif
356                 irq = p->base + irqs + ipis;
357                 nirqs = MAX(nirqs, irq);
358                 npics++;
359         }
360
361         KASSERT(npics < MAX_PICS,
362             ("Number of PICs exceeds maximum (%d)", MAX_PICS));
363
364         mtx_unlock(&intr_table_lock);
365
366         return (p->base);
367 }
368
369 u_int
370 powerpc_get_irq(uint32_t node, u_int pin)
371 {
372         int idx;
373
374         if (node == 0)
375                 return (pin);
376
377         mtx_lock(&intr_table_lock);
378         for (idx = 0; idx < npics; idx++) {
379                 if (piclist[idx].node == node) {
380                         mtx_unlock(&intr_table_lock);
381                         return (piclist[idx].base + pin);
382                 }
383         }
384
385         /*
386          * XXX we should never encounter an unregistered PIC, but that
387          * can only be done when we properly support bus enumeration
388          * using multiple passes. Until then, fake an entry and give it
389          * some adhoc maximum number of IRQs and IPIs.
390          */
391         piclist[idx].dev = NULL;
392         piclist[idx].node = node;
393         piclist[idx].irqs = 124;
394         piclist[idx].ipis = 4;
395         piclist[idx].base = nirqs;
396         nirqs += (1 << 25);
397         npics++;
398
399         KASSERT(npics < MAX_PICS,
400             ("Number of PICs exceeds maximum (%d)", MAX_PICS));
401
402         mtx_unlock(&intr_table_lock);
403
404         return (piclist[idx].base + pin);
405 }
406
407 int
408 powerpc_enable_intr(void)
409 {
410         struct powerpc_intr *i;
411         int error, vector;
412 #ifdef SMP
413         int n;
414 #endif
415
416         if (npics == 0)
417                 panic("no PIC detected\n");
418
419         if (root_pic == NULL)
420                 root_pic = piclist[0].dev;
421
422 #ifdef SMP
423         /* Install an IPI handler. */
424         if (mp_ncpus > 1) {
425                 for (n = 0; n < npics; n++) {
426                         if (piclist[n].dev != root_pic)
427                                 continue;
428
429                         KASSERT(piclist[n].ipis != 0,
430                             ("%s: SMP root PIC does not supply any IPIs",
431                             __func__));
432                         error = powerpc_setup_intr("IPI",
433                             MAP_IRQ(piclist[n].node, piclist[n].irqs),
434                             powerpc_ipi_handler, NULL, NULL,
435                             INTR_TYPE_MISC | INTR_EXCL, &ipi_cookie);
436                         if (error) {
437                                 printf("unable to setup IPI handler\n");
438                                 return (error);
439                         }
440
441                         /*
442                          * Some subterfuge: disable late EOI and mark this
443                          * as an IPI to the dispatch layer.
444                          */
445                         i = intr_lookup(MAP_IRQ(piclist[n].node,
446                             piclist[n].irqs));
447                         i->event->ie_post_filter = NULL;
448                         i->ipi = 1;
449                 }
450         }
451 #endif
452
453         for (vector = 0; vector < nvectors; vector++) {
454                 i = powerpc_intrs[vector];
455                 if (i == NULL)
456                         continue;
457
458                 error = powerpc_map_irq(i);
459                 if (error)
460                         continue;
461
462                 if (i->trig == INTR_TRIGGER_INVALID)
463                         PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode,
464                             &i->trig, &i->pol);
465                 if (i->trig != INTR_TRIGGER_CONFORM ||
466                     i->pol != INTR_POLARITY_CONFORM)
467                         PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
468
469                 if (i->event != NULL)
470                         PIC_ENABLE(i->pic, i->intline, vector, &i->priv);
471         }
472
473         return (0);
474 }
475
476 int
477 powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter,
478     driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep)
479 {
480         struct powerpc_intr *i;
481         int error, enable = 0;
482
483         i = intr_lookup(irq);
484         if (i == NULL)
485                 return (ENOMEM);
486
487         if (i->event == NULL) {
488                 error = intr_event_create(&i->event, (void *)i, 0, irq,
489                     powerpc_intr_pre_ithread, powerpc_intr_post_ithread,
490                     powerpc_intr_eoi, powerpc_assign_intr_cpu, "irq%u:", irq);
491                 if (error)
492                         return (error);
493
494                 enable = 1;
495         }
496
497         error = intr_event_add_handler(i->event, name, filter, handler, arg,
498             intr_priority(flags), flags, cookiep);
499
500         mtx_lock(&intr_table_lock);
501         intrcnt_setname(i->event->ie_fullname, i->cntindex);
502         mtx_unlock(&intr_table_lock);
503
504         if (!cold) {
505                 error = powerpc_map_irq(i);
506
507                 if (!error) {
508                         if (i->trig == INTR_TRIGGER_INVALID)
509                                 PIC_TRANSLATE_CODE(i->pic, i->intline,
510                                     i->fwcode, &i->trig, &i->pol);
511         
512                         if (i->trig != INTR_TRIGGER_CONFORM ||
513                             i->pol != INTR_POLARITY_CONFORM)
514                                 PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
515
516                         if (i->pic == root_pic)
517                                 PIC_BIND(i->pic, i->intline, i->cpu, &i->priv);
518
519                         if (enable)
520                                 PIC_ENABLE(i->pic, i->intline, i->vector,
521                                     &i->priv);
522                 }
523         }
524         return (error);
525 }
526
527 int
528 powerpc_teardown_intr(void *cookie)
529 {
530
531         return (intr_event_remove_handler(cookie));
532 }
533
534 #ifdef SMP
535 int
536 powerpc_bind_intr(u_int irq, u_char cpu)
537 {
538         struct powerpc_intr *i;
539
540         i = intr_lookup(irq);
541         if (i == NULL)
542                 return (ENOMEM);
543
544         return (intr_event_bind(i->event, cpu));
545 }
546 #endif
547
548 int
549 powerpc_fw_config_intr(int irq, int sense_code)
550 {
551         struct powerpc_intr *i;
552
553         i = intr_lookup(irq);
554         if (i == NULL)
555                 return (ENOMEM);
556
557         i->trig = INTR_TRIGGER_INVALID;
558         i->pol = INTR_POLARITY_CONFORM;
559         i->fwcode = sense_code;
560
561         if (!cold && i->pic != NULL) {
562                 PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig,
563                     &i->pol);
564                 PIC_CONFIG(i->pic, i->intline, i->trig, i->pol);
565         }
566
567         return (0);
568 }
569
570 int
571 powerpc_config_intr(int irq, enum intr_trigger trig, enum intr_polarity pol)
572 {
573         struct powerpc_intr *i;
574
575         i = intr_lookup(irq);
576         if (i == NULL)
577                 return (ENOMEM);
578
579         i->trig = trig;
580         i->pol = pol;
581
582         if (!cold && i->pic != NULL)
583                 PIC_CONFIG(i->pic, i->intline, trig, pol);
584
585         return (0);
586 }
587
588 void
589 powerpc_dispatch_intr(u_int vector, struct trapframe *tf)
590 {
591         struct powerpc_intr *i;
592         struct intr_event *ie;
593
594         i = powerpc_intrs[vector];
595         if (i == NULL)
596                 goto stray;
597
598         (*i->cntp)++;
599
600         ie = i->event;
601         KASSERT(ie != NULL, ("%s: interrupt without an event", __func__));
602
603         /*
604          * IPIs are magical and need to be EOI'ed before filtering.
605          * This prevents races in IPI handling.
606          */
607         if (i->ipi)
608                 PIC_EOI(i->pic, i->intline, i->priv);
609
610         if (intr_event_handle(ie, tf) != 0) {
611                 goto stray;
612         }
613         return;
614
615 stray:
616         stray_count++;
617         if (stray_count <= MAX_STRAY_LOG) {
618                 printf("stray irq %d\n", i ? i->irq : -1);
619                 if (stray_count >= MAX_STRAY_LOG) {
620                         printf("got %d stray interrupts, not logging anymore\n",
621                             MAX_STRAY_LOG);
622                 }
623         }
624         if (i != NULL)
625                 PIC_MASK(i->pic, i->intline, i->priv);
626 }
627
628 void
629 powerpc_intr_mask(u_int irq)
630 {
631         struct powerpc_intr *i;
632
633         i = intr_lookup(irq);
634         if (i == NULL || i->pic == NULL)
635                 return;
636
637         PIC_MASK(i->pic, i->intline, i->priv);
638 }
639
640 void
641 powerpc_intr_unmask(u_int irq)
642 {
643         struct powerpc_intr *i;
644
645         i = intr_lookup(irq);
646         if (i == NULL || i->pic == NULL)
647                 return;
648
649         PIC_UNMASK(i->pic, i->intline, i->priv);
650 }