]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/nlm/intr_machdep.c
Optionally bind ktls threads to NUMA domains
[FreeBSD/FreeBSD.git] / sys / mips / nlm / intr_machdep.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 2003-2011 Netlogic Microsystems (Netlogic). All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY Netlogic Microsystems ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NETLOGIC OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * NETLOGIC_BSD */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/interrupt.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44
45 #include <machine/cpu.h>
46 #include <machine/cpufunc.h>
47 #include <machine/cpuinfo.h>
48 #include <machine/cpuregs.h>
49 #include <machine/frame.h>
50 #include <machine/intr_machdep.h>
51 #include <machine/md_var.h>
52 #include <machine/trap.h>
53 #include <machine/hwfunc.h>
54
55 #include <mips/nlm/hal/haldefs.h>
56 #include <mips/nlm/hal/iomap.h>
57 #include <mips/nlm/hal/mips-extns.h>
58 #include <mips/nlm/interrupt.h>
59 #include <mips/nlm/hal/pic.h>
60 #include <mips/nlm/xlp.h>
61
62 struct xlp_intrsrc {
63         void (*bus_ack)(int, void *);   /* Additional ack */
64         void *bus_ack_arg;              /* arg for additional ack */
65         struct intr_event *ie;          /* event corresponding to intr */
66         int irq;
67         int irt;
68 };
69
70 static struct xlp_intrsrc xlp_interrupts[XLR_MAX_INTR];
71 static mips_intrcnt_t mips_intr_counters[XLR_MAX_INTR];
72 static int intrcnt_index;
73
74 int
75 xlp_irq_to_irt(int irq)
76 {
77         uint32_t offset;
78
79         switch (irq) {
80         case PIC_UART_0_IRQ:
81         case PIC_UART_1_IRQ:
82                 offset =  XLP_IO_UART_OFFSET(0, irq - PIC_UART_0_IRQ);
83                 return (xlp_socdev_irt(offset));
84         case PIC_PCIE_0_IRQ:
85         case PIC_PCIE_1_IRQ:
86         case PIC_PCIE_2_IRQ:
87         case PIC_PCIE_3_IRQ:
88                 offset = XLP_IO_PCIE_OFFSET(0, irq - PIC_PCIE_0_IRQ);
89                 return (xlp_socdev_irt(offset));
90         case PIC_USB_0_IRQ:
91         case PIC_USB_1_IRQ:
92         case PIC_USB_2_IRQ:
93         case PIC_USB_3_IRQ:
94         case PIC_USB_4_IRQ:
95                 offset = XLP_IO_USB_OFFSET(0, irq - PIC_USB_0_IRQ);
96                 return (xlp_socdev_irt(offset));
97         case PIC_I2C_0_IRQ:
98         case PIC_I2C_1_IRQ:
99                 offset = XLP_IO_I2C0_OFFSET(0);
100                 return (xlp_socdev_irt(offset) + irq - PIC_I2C_0_IRQ);
101         default:
102                 printf("ERROR: %s: unknown irq %d\n", __func__, irq);
103                 return (-1);
104         }
105 }
106
107 void
108 xlp_enable_irq(int irq)
109 {
110         uint64_t eimr;
111
112         eimr = nlm_read_c0_eimr();
113         nlm_write_c0_eimr(eimr | (1ULL << irq));
114 }
115
116 void
117 cpu_establish_softintr(const char *name, driver_filter_t * filt,
118     void (*handler) (void *), void *arg, int irq, int flags,
119     void **cookiep)
120 {
121
122         panic("Soft interrupts unsupported!\n");
123 }
124
125 static void
126 xlp_post_filter(void *source)
127 {
128         struct xlp_intrsrc *src = source;
129
130         if (src->bus_ack)
131                 src->bus_ack(src->irq, src->bus_ack_arg);
132         nlm_pic_ack(xlp_pic_base, src->irt);
133 }
134
135 static void
136 xlp_pre_ithread(void *source)
137 {
138         struct xlp_intrsrc *src = source;
139
140         if (src->bus_ack)
141                 src->bus_ack(src->irq, src->bus_ack_arg);
142 }
143
144 static void
145 xlp_post_ithread(void *source)
146 {
147         struct xlp_intrsrc *src = source;
148
149         nlm_pic_ack(xlp_pic_base, src->irt);
150 }
151
152 void
153 xlp_set_bus_ack(int irq, void (*ack)(int, void *), void *arg)
154 {
155         struct xlp_intrsrc *src;
156
157         KASSERT(irq > 0 && irq <= XLR_MAX_INTR,
158             ("%s called for bad hard intr %d", __func__, irq));
159
160         /* no locking needed - this will called early in boot */
161         src = &xlp_interrupts[irq];
162         KASSERT(src->ie != NULL,
163             ("%s called after IRQ enable for %d.", __func__, irq));
164         src->bus_ack_arg = arg;
165         src->bus_ack = ack;
166 }
167
168 void
169 cpu_establish_hardintr(const char *name, driver_filter_t * filt,
170     void (*handler) (void *), void *arg, int irq, int flags,
171     void **cookiep)
172 {
173         struct intr_event *ie;  /* descriptor for the IRQ */
174         struct xlp_intrsrc *src = NULL;
175         int errcode;
176
177         KASSERT(irq > 0 && irq <= XLR_MAX_INTR ,
178             ("%s called for bad hard intr %d", __func__, irq));
179
180         /*
181          * Locking - not needed now, because we do this only on
182          * startup from CPU0
183          */
184         src = &xlp_interrupts[irq];
185         ie = src->ie;
186         if (ie == NULL) {
187                 /*
188                  * PIC based interrupts need ack in PIC, and some SoC
189                  * components need additional acks (e.g. PCI)
190                  */
191                 if (XLP_IRQ_IS_PICINTR(irq))
192                         errcode = intr_event_create(&ie, src, 0, irq,
193                             xlp_pre_ithread, xlp_post_ithread, xlp_post_filter,
194                             NULL, "hard intr%d:", irq);
195                 else {
196                         if (filt == NULL)
197                                 panic("Unsupported non filter percpu intr %d", irq);
198                         errcode = intr_event_create(&ie, src, 0, irq,
199                             NULL, NULL, NULL, NULL, "hard intr%d:", irq);
200                 }
201                 if (errcode) {
202                         printf("Could not create event for intr %d\n", irq);
203                         return;
204                 }
205                 src->irq = irq;
206                 src->ie = ie;
207         }
208         if (XLP_IRQ_IS_PICINTR(irq)) {
209                 /* Set all irqs to CPU 0 for now */
210                 src->irt = xlp_irq_to_irt(irq);
211                 nlm_pic_write_irt_direct(xlp_pic_base, src->irt, 1, 0,
212                     PIC_LOCAL_SCHEDULING, irq, 0);
213         }
214
215         intr_event_add_handler(ie, name, filt, handler, arg,
216             intr_priority(flags), flags, cookiep);
217         xlp_enable_irq(irq);
218 }
219
220 void
221 cpu_intr(struct trapframe *tf)
222 {
223         struct intr_event *ie;
224         uint64_t eirr, eimr;
225         int i;
226
227         critical_enter();
228
229         /* find a list of enabled interrupts */
230         eirr = nlm_read_c0_eirr();
231         eimr = nlm_read_c0_eimr();
232         eirr &= eimr;
233
234         if (eirr == 0) {
235                 critical_exit();
236                 return;
237         }
238         /*
239          * No need to clear the EIRR here as the handler writes to
240          * compare which ACKs the interrupt.
241          */
242         if (eirr & (1 << IRQ_TIMER)) {
243                 intr_event_handle(xlp_interrupts[IRQ_TIMER].ie, tf);
244                 critical_exit();
245                 return;
246         }
247
248         /* FIXME sched pin >? LOCK>? */
249         for (i = sizeof(eirr) * 8 - 1; i >= 0; i--) {
250                 if ((eirr & (1ULL << i)) == 0)
251                         continue;
252
253                 ie = xlp_interrupts[i].ie;
254                 /* Don't account special IRQs */
255                 switch (i) {
256                 case IRQ_IPI:
257                 case IRQ_MSGRING:
258                         break;
259                 default:
260                         mips_intrcnt_inc(mips_intr_counters[i]);
261                 }
262
263                 /* Ack the IRQ on the CPU */
264                 nlm_write_c0_eirr(1ULL << i);
265                 if (intr_event_handle(ie, tf) != 0) {
266                         printf("stray interrupt %d\n", i);
267                 }
268         }
269         critical_exit();
270 }
271
272 void
273 mips_intrcnt_setname(mips_intrcnt_t counter, const char *name)
274 {
275         int idx = counter - intrcnt;
276
277         KASSERT(counter != NULL, ("mips_intrcnt_setname: NULL counter"));
278
279         snprintf(intrnames + (MAXCOMLEN + 1) * idx,
280             MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name);
281 }
282
283 mips_intrcnt_t
284 mips_intrcnt_create(const char* name)
285 {
286         mips_intrcnt_t counter = &intrcnt[intrcnt_index++];
287
288         mips_intrcnt_setname(counter, name);
289         return counter;
290 }
291
292 void
293 cpu_init_interrupts()
294 {
295         int i;
296         char name[MAXCOMLEN + 1];
297
298         /*
299          * Initialize all available vectors so spare IRQ
300          * would show up in systat output
301          */
302         for (i = 0; i < XLR_MAX_INTR; i++) {
303                 snprintf(name, MAXCOMLEN + 1, "int%d:", i);
304                 mips_intr_counters[i] = mips_intrcnt_create(name);
305         }
306 }
307
308 static int      xlp_pic_probe(device_t);
309 static int      xlp_pic_attach(device_t);
310
311 static int
312 xlp_pic_probe(device_t dev)
313 {
314
315         if (!ofw_bus_is_compatible(dev, "netlogic,xlp-pic"))
316                 return (ENXIO);
317         device_set_desc(dev, "XLP PIC");
318         return (0);
319 }
320
321 static int
322 xlp_pic_attach(device_t dev)
323 {
324
325         return (0);
326 }
327
328 static device_method_t xlp_pic_methods[] = {
329         DEVMETHOD(device_probe,         xlp_pic_probe),
330         DEVMETHOD(device_attach,        xlp_pic_attach),
331
332         DEVMETHOD_END
333 };
334
335 static driver_t xlp_pic_driver = {
336         "xlp_pic",
337         xlp_pic_methods,
338         1,              /* no softc */
339 };
340
341 static devclass_t xlp_pic_devclass;
342 DRIVER_MODULE(xlp_pic, simplebus, xlp_pic_driver, xlp_pic_devclass, 0, 0);