]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/intr_machdep.c
Update hostapd/wpa_supplicant to version 2.5.
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / intr_machdep.c
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * Copyright (c) 2002 Benno Rice.
4  * Copyright (c) 2014 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Semihalf under
8  * the sponsorship of the FreeBSD Foundation.
9  * 
10  * This code is derived from software contributed by
11  * William Jolitz (Berkeley) and Benno Rice.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  * notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in the
20  * documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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  *      form: src/sys/powerpc/powerpc/intr_machdep.c, r271712 2014/09/17
35  */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/bus.h>
43 #include <sys/kernel.h>
44 #include <sys/ktr.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/mutex.h>
48 #include <sys/cpuset.h>
49 #include <sys/interrupt.h>
50 #include <sys/queue.h>
51 #include <sys/smp.h>
52
53 #include <machine/cpufunc.h>
54 #include <machine/intr.h>
55
56 #ifdef SMP
57 #include <machine/smp.h>
58 #endif
59
60 #include "pic_if.h"
61
62 #define MAX_STRAY_LOG   5
63 #define INTRNAME_LEN    (MAXCOMLEN + 1)
64
65 #define NIRQS   1024    /* Maximum number of interrupts in the system */
66
67 static MALLOC_DEFINE(M_INTR, "intr", "Interrupt Services");
68
69 /*
70  * Linked list of interrupts that have been set-up.
71  * Each element holds the interrupt description
72  * and has to be allocated and freed dynamically.
73  */
74 static SLIST_HEAD(, arm64_intr_entry) irq_slist_head =
75     SLIST_HEAD_INITIALIZER(irq_slist_head);
76
77 struct arm64_intr_entry {
78         SLIST_ENTRY(arm64_intr_entry) entries;
79         struct intr_event       *i_event;
80
81         enum intr_trigger       i_trig;
82         enum intr_polarity      i_pol;
83
84         u_int                   i_hw_irq;       /* Physical interrupt number */
85         u_int                   i_cntidx;       /* Index in intrcnt table */
86         u_int                   i_handlers;     /* Allocated handlers */
87         u_long                  *i_cntp;        /* Interrupt hit counter */
88 };
89
90 /* Counts and names for statistics - see sys/sys/interrupt.h */
91 /* Tables are indexed by i_cntidx */
92 u_long intrcnt[NIRQS];
93 char intrnames[NIRQS * INTRNAME_LEN];
94 size_t sintrcnt = sizeof(intrcnt);
95 size_t sintrnames = sizeof(intrnames);
96
97 static u_int intrcntidx;        /* Current index into intrcnt table */
98 static u_int arm64_nintrs;      /* Max interrupts number of the root PIC */
99 static u_int arm64_nstray;      /* Number of received stray interrupts */
100 static device_t root_pic;       /* PIC device for all incoming interrupts */
101 static device_t msi_pic;        /* Device which handles MSI/MSI-X interrupts */
102 static struct mtx intr_list_lock;
103
104 static void
105 intr_init(void *dummy __unused)
106 {
107
108         mtx_init(&intr_list_lock, "intr sources lock", NULL, MTX_DEF);
109 }
110 SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL);
111
112 /*
113  * Helper routines.
114  */
115
116 /* Set interrupt name for statistics */
117 static void
118 intrcnt_setname(const char *name, u_int idx)
119 {
120
121         snprintf(&intrnames[idx * INTRNAME_LEN], INTRNAME_LEN, "%-*s",
122             INTRNAME_LEN - 1, name);
123 }
124
125 /*
126  * Get intr structure for the given interrupt number.
127  * Allocate one if this is the first time.
128  * (Similar to ppc's intr_lookup() but without actual
129  * lookup since irq number is an index in arm64_intrs[]).
130  */
131 static struct arm64_intr_entry *
132 intr_acquire(u_int hw_irq)
133 {
134         struct arm64_intr_entry *intr;
135
136         mtx_lock(&intr_list_lock);
137
138         SLIST_FOREACH(intr, &irq_slist_head, entries) {
139                 if (intr->i_hw_irq == hw_irq) {
140                         break;
141                 }
142         }
143         if (intr != NULL)
144                 goto out;
145
146         /* Do not alloc another intr when max number of IRQs has been reached */
147         if (intrcntidx >= NIRQS)
148                 goto out;
149
150         intr = malloc(sizeof(*intr), M_INTR, M_NOWAIT);
151         if (intr == NULL)
152                 goto out;
153
154         intr->i_event = NULL;
155         intr->i_handlers = 0;
156         intr->i_trig = INTR_TRIGGER_CONFORM;
157         intr->i_pol = INTR_POLARITY_CONFORM;
158         intr->i_cntidx = atomic_fetchadd_int(&intrcntidx, 1);
159         intr->i_cntp = &intrcnt[intr->i_cntidx];
160         intr->i_hw_irq = hw_irq;
161         SLIST_INSERT_HEAD(&irq_slist_head, intr, entries);
162 out:
163         mtx_unlock(&intr_list_lock);
164         return intr;
165 }
166
167 static void
168 intr_pre_ithread(void *arg)
169 {
170         struct arm64_intr_entry *intr = arg;
171
172         PIC_PRE_ITHREAD(root_pic, intr->i_hw_irq);
173 }
174
175 static void
176 intr_post_ithread(void *arg)
177 {
178         struct arm64_intr_entry *intr = arg;
179
180         PIC_POST_ITHREAD(root_pic, intr->i_hw_irq);
181 }
182
183 static void
184 intr_post_filter(void *arg)
185 {
186         struct arm64_intr_entry *intr = arg;
187
188         PIC_POST_FILTER(root_pic, intr->i_hw_irq);
189 }
190
191 /*
192  * Register PIC driver.
193  * This is intended to be called by the very first PIC driver
194  * at the end of the successful attach.
195  * Note that during boot this can be called after first references
196  * to bus_setup_intr() so it is required to not use root_pic if it
197  * is not 100% safe.
198  */
199 void
200 arm_register_root_pic(device_t dev, u_int nirq)
201 {
202
203         KASSERT(root_pic == NULL, ("Unable to set the pic twice"));
204         KASSERT(nirq <= NIRQS, ("PIC is trying to handle too many IRQs"));
205
206         arm64_nintrs = NIRQS; /* Number of IRQs limited only by array size */
207         root_pic = dev;
208 }
209
210 /* Register device which allocates MSI interrupts */
211 void
212 arm_register_msi_pic(device_t dev)
213 {
214
215         KASSERT(msi_pic == NULL, ("Unable to set msi_pic twice"));
216         msi_pic = dev;
217 }
218
219 int
220 arm_alloc_msi(device_t pci_dev, int count, int *irqs)
221 {
222
223         return PIC_ALLOC_MSI(msi_pic, pci_dev, count, irqs);
224 }
225
226 int
227 arm_release_msi(device_t pci_dev, int count, int *irqs)
228 {
229
230         return PIC_RELEASE_MSI(msi_pic, pci_dev, count, irqs);
231 }
232
233 int
234 arm_map_msi(device_t pci_dev, int irq, uint64_t *addr, uint32_t *data)
235 {
236
237         return PIC_MAP_MSI(msi_pic, pci_dev, irq, addr, data);
238 }
239
240 int
241 arm_alloc_msix(device_t pci_dev, int *irq)
242 {
243
244         return PIC_ALLOC_MSIX(msi_pic, pci_dev, irq);
245 }
246
247 int
248 arm_release_msix(device_t pci_dev, int irq)
249 {
250
251         return PIC_RELEASE_MSIX(msi_pic, pci_dev, irq);
252 }
253
254
255 int
256 arm_map_msix(device_t pci_dev, int irq, uint64_t *addr, uint32_t *data)
257 {
258
259         return PIC_MAP_MSIX(msi_pic, pci_dev, irq, addr, data);
260 }
261
262 /*
263  * Finalize interrupts bring-up (should be called from configure_final()).
264  * Enables all interrupts registered by bus_setup_intr() during boot
265  * as well as unlocks interrups reception on primary CPU.
266  */
267 int
268 arm_enable_intr(void)
269 {
270         struct arm64_intr_entry *intr;
271
272         if (root_pic == NULL)
273                 panic("Cannot enable interrupts. No PIC configured");
274
275         /*
276          * Iterate through all possible interrupts and perform
277          * configuration if the interrupt is registered.
278          */
279         SLIST_FOREACH(intr, &irq_slist_head, entries) {
280                 /*
281                  * XXX: In case we allowed to set up interrupt whose number
282                  *      exceeds maximum number of interrupts for the root PIC
283                  *      disable it and print proper error message.
284                  *
285                  *      This can happen only when calling bus_setup_intr()
286                  *      before the interrupt controller is attached.
287                  */
288                 if (intr->i_cntidx >= arm64_nintrs) {
289                         /* Better fail when IVARIANTS enabled */
290                         KASSERT(0, ("%s: Interrupt %u cannot be handled by the "
291                             "registered PIC. Max interrupt number: %u", __func__,
292                             intr->i_cntidx, arm64_nintrs - 1));
293                         /* Print message and disable otherwise */
294                         printf("ERROR: Cannot enable irq %u. Disabling.\n",
295                             intr->i_cntidx);
296                         PIC_MASK(root_pic, intr->i_hw_irq);
297                 }
298
299                 if (intr->i_trig != INTR_TRIGGER_CONFORM ||
300                     intr->i_pol != INTR_POLARITY_CONFORM) {
301                         PIC_CONFIG(root_pic, intr->i_hw_irq,
302                             intr->i_trig, intr->i_pol);
303                 }
304
305                 if (intr->i_handlers > 0)
306                         PIC_UNMASK(root_pic, intr->i_hw_irq);
307
308         }
309         /* Enable interrupt reception on this CPU */
310         intr_enable();
311
312         return (0);
313 }
314
315 int
316 arm_setup_intr(const char *name, driver_filter_t *filt, driver_intr_t handler,
317     void *arg, u_int hw_irq, enum intr_type flags, void **cookiep)
318 {
319         struct arm64_intr_entry *intr;
320         int error;
321
322         intr = intr_acquire(hw_irq);
323         if (intr == NULL)
324                 return (ENOMEM);
325
326         /*
327          * Watch out for interrupts' numbers.
328          * If this is a system boot then don't allow to overfill interrupts
329          * table (the interrupts will be deconfigured in arm_enable_intr()).
330          */
331         if (intr->i_cntidx >= NIRQS)
332                 return (EINVAL);
333
334         if (intr->i_event == NULL) {
335                 error = intr_event_create(&intr->i_event, (void *)intr, 0,
336                     hw_irq, intr_pre_ithread, intr_post_ithread,
337                     intr_post_filter, NULL, "irq%u", hw_irq);
338                 if (error)
339                         return (error);
340         }
341
342         error = intr_event_add_handler(intr->i_event, name, filt, handler, arg,
343             intr_priority(flags), flags, cookiep);
344
345         if (!error) {
346                 mtx_lock(&intr_list_lock);
347                 intrcnt_setname(intr->i_event->ie_fullname, intr->i_cntidx);
348                 intr->i_handlers++;
349
350                 if (!cold && intr->i_handlers == 1) {
351                         if (intr->i_trig != INTR_TRIGGER_CONFORM ||
352                             intr->i_pol != INTR_POLARITY_CONFORM) {
353                                 PIC_CONFIG(root_pic, intr->i_hw_irq, intr->i_trig,
354                                     intr->i_pol);
355                         }
356
357                         PIC_UNMASK(root_pic, intr->i_hw_irq);
358                 }
359                 mtx_unlock(&intr_list_lock);
360         }
361
362         return (error);
363 }
364
365 int
366 arm_teardown_intr(void *cookie)
367 {
368         struct arm64_intr_entry *intr;
369         int error;
370
371         intr = intr_handler_source(cookie);
372         error = intr_event_remove_handler(cookie);
373         if (!error) {
374                 mtx_lock(&intr_list_lock);
375                 intr->i_handlers--;
376                 if (intr->i_handlers == 0)
377                         PIC_MASK(root_pic, intr->i_hw_irq);
378                 intrcnt_setname(intr->i_event->ie_fullname, intr->i_cntidx);
379                 mtx_unlock(&intr_list_lock);
380         }
381
382         return (error);
383 }
384
385 int
386 arm_config_intr(u_int hw_irq, enum intr_trigger trig, enum intr_polarity pol)
387 {
388         struct arm64_intr_entry *intr;
389
390         intr = intr_acquire(hw_irq);
391         if (intr == NULL)
392                 return (ENOMEM);
393
394         intr->i_trig = trig;
395         intr->i_pol = pol;
396
397         if (!cold && root_pic != NULL)
398                 PIC_CONFIG(root_pic, intr->i_hw_irq, trig, pol);
399
400         return (0);
401 }
402
403 void
404 arm_dispatch_intr(u_int hw_irq, struct trapframe *tf)
405 {
406         struct arm64_intr_entry *intr;
407
408         SLIST_FOREACH(intr, &irq_slist_head, entries) {
409                 if (intr->i_hw_irq == hw_irq) {
410                         break;
411                 }
412         }
413
414         if (intr == NULL)
415                 goto stray;
416
417         (*intr->i_cntp)++;
418
419         if (!intr_event_handle(intr->i_event, tf))
420                 return;
421
422 stray:
423         if (arm64_nstray < MAX_STRAY_LOG) {
424                 arm64_nstray++;
425                 printf("Stray IRQ %u\n", hw_irq);
426                 if (arm64_nstray >= MAX_STRAY_LOG) {
427                         printf("Got %d stray IRQs. Not logging anymore.\n",
428                             MAX_STRAY_LOG);
429                 }
430         }
431
432         if (intr != NULL)
433                 PIC_MASK(root_pic, intr->i_hw_irq);
434 #ifdef HWPMC_HOOKS
435         if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN))
436                 pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf);
437 #endif
438 }
439
440 void
441 arm_cpu_intr(struct trapframe *tf)
442 {
443
444         critical_enter();
445         PIC_DISPATCH(root_pic, tf);
446         critical_exit();
447 }
448
449 #ifdef SMP
450 void
451 arm_setup_ipihandler(driver_filter_t *filt, u_int ipi)
452 {
453
454         arm_setup_intr("ipi", filt, NULL, (void *)((uintptr_t)ipi | 1<<16), ipi,
455             INTR_TYPE_MISC | INTR_EXCL, NULL);
456         arm_unmask_ipi(ipi);
457 }
458
459 void
460 arm_unmask_ipi(u_int ipi)
461 {
462
463         PIC_UNMASK(root_pic, ipi);
464 }
465
466 void
467 arm_init_secondary(void)
468 {
469
470         PIC_INIT_SECONDARY(root_pic);
471 }
472
473 /* Sending IPI */
474 void
475 ipi_all_but_self(u_int ipi)
476 {
477         cpuset_t other_cpus;
478
479         other_cpus = all_cpus;
480         CPU_CLR(PCPU_GET(cpuid), &other_cpus);
481
482         /* ARM64TODO: This will be fixed with arm_intrng */
483         ipi += 16;
484
485         CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
486         PIC_IPI_SEND(root_pic, other_cpus, ipi);
487 }
488
489 void
490 ipi_cpu(int cpu, u_int ipi)
491 {
492         cpuset_t cpus;
493
494         CPU_ZERO(&cpus);
495         CPU_SET(cpu, &cpus);
496
497         CTR2(KTR_SMP, "ipi_cpu: cpu: %d, ipi: %x", cpu, ipi);
498         PIC_IPI_SEND(root_pic, cpus, ipi);
499 }
500
501 void
502 ipi_selected(cpuset_t cpus, u_int ipi)
503 {
504
505         CTR1(KTR_SMP, "ipi_selected: ipi: %x", ipi);
506         PIC_IPI_SEND(root_pic, cpus, ipi);
507 }
508
509 #endif