2 * Copyright (c) 1991 The Regents of the University of California.
3 * Copyright (c) 2002 Benno Rice.
4 * Copyright (c) 2014 The FreeBSD Foundation
7 * This software was developed by Semihalf under
8 * the sponsorship of the FreeBSD Foundation.
10 * This code is derived from software contributed by
11 * William Jolitz (Berkeley) and Benno Rice.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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.
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
34 * form: src/sys/powerpc/powerpc/intr_machdep.c, r271712 2014/09/17
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
40 #include <sys/param.h>
41 #include <sys/systm.h>
43 #include <sys/kernel.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>
53 #include <machine/cpufunc.h>
54 #include <machine/intr.h>
57 #include <machine/smp.h>
62 #define MAX_STRAY_LOG 5
63 #define INTRNAME_LEN (MAXCOMLEN + 1)
65 #define NIRQS 1024 /* Maximum number of interrupts in the system */
67 static MALLOC_DEFINE(M_INTR, "intr", "Interrupt Services");
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.
74 static SLIST_HEAD(, arm64_intr_entry) irq_slist_head =
75 SLIST_HEAD_INITIALIZER(irq_slist_head);
77 struct arm64_intr_entry {
78 SLIST_ENTRY(arm64_intr_entry) entries;
79 struct intr_event *i_event;
81 enum intr_trigger i_trig;
82 enum intr_polarity i_pol;
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 */
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);
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;
105 intr_init(void *dummy __unused)
108 mtx_init(&intr_list_lock, "intr sources lock", NULL, MTX_DEF);
110 SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL);
116 /* Set interrupt name for statistics */
118 intrcnt_setname(const char *name, u_int idx)
121 snprintf(&intrnames[idx * INTRNAME_LEN], INTRNAME_LEN, "%-*s",
122 INTRNAME_LEN - 1, name);
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[]).
131 static struct arm64_intr_entry *
132 intr_acquire(u_int hw_irq)
134 struct arm64_intr_entry *intr;
136 mtx_lock(&intr_list_lock);
138 SLIST_FOREACH(intr, &irq_slist_head, entries) {
139 if (intr->i_hw_irq == hw_irq) {
146 /* Do not alloc another intr when max number of IRQs has been reached */
147 if (intrcntidx >= NIRQS)
150 intr = malloc(sizeof(*intr), M_INTR, M_NOWAIT);
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);
163 mtx_unlock(&intr_list_lock);
168 intr_pre_ithread(void *arg)
170 struct arm64_intr_entry *intr = arg;
172 PIC_PRE_ITHREAD(root_pic, intr->i_hw_irq);
176 intr_post_ithread(void *arg)
178 struct arm64_intr_entry *intr = arg;
180 PIC_POST_ITHREAD(root_pic, intr->i_hw_irq);
184 intr_post_filter(void *arg)
186 struct arm64_intr_entry *intr = arg;
188 PIC_POST_FILTER(root_pic, intr->i_hw_irq);
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
200 arm_register_root_pic(device_t dev, u_int nirq)
203 KASSERT(root_pic == NULL, ("Unable to set the pic twice"));
204 KASSERT(nirq <= NIRQS, ("PIC is trying to handle too many IRQs"));
206 arm64_nintrs = NIRQS; /* Number of IRQs limited only by array size */
210 /* Register device which allocates MSI interrupts */
212 arm_register_msi_pic(device_t dev)
215 KASSERT(msi_pic == NULL, ("Unable to set msi_pic twice"));
220 arm_alloc_msi(device_t pci_dev, int count, int *irqs)
223 return PIC_ALLOC_MSI(msi_pic, pci_dev, count, irqs);
227 arm_release_msi(device_t pci_dev, int count, int *irqs)
230 return PIC_RELEASE_MSI(msi_pic, pci_dev, count, irqs);
234 arm_map_msi(device_t pci_dev, int irq, uint64_t *addr, uint32_t *data)
237 return PIC_MAP_MSI(msi_pic, pci_dev, irq, addr, data);
241 arm_alloc_msix(device_t pci_dev, int *irq)
244 return PIC_ALLOC_MSIX(msi_pic, pci_dev, irq);
248 arm_release_msix(device_t pci_dev, int irq)
251 return PIC_RELEASE_MSIX(msi_pic, pci_dev, irq);
256 arm_map_msix(device_t pci_dev, int irq, uint64_t *addr, uint32_t *data)
259 return PIC_MAP_MSIX(msi_pic, pci_dev, irq, addr, data);
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.
268 arm_enable_intr(void)
270 struct arm64_intr_entry *intr;
272 if (root_pic == NULL)
273 panic("Cannot enable interrupts. No PIC configured");
276 * Iterate through all possible interrupts and perform
277 * configuration if the interrupt is registered.
279 SLIST_FOREACH(intr, &irq_slist_head, entries) {
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.
285 * This can happen only when calling bus_setup_intr()
286 * before the interrupt controller is attached.
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",
296 PIC_MASK(root_pic, intr->i_hw_irq);
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);
305 if (intr->i_handlers > 0)
306 PIC_UNMASK(root_pic, intr->i_hw_irq);
309 /* Enable interrupt reception on this CPU */
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)
319 struct arm64_intr_entry *intr;
322 intr = intr_acquire(hw_irq);
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()).
331 if (intr->i_cntidx >= NIRQS)
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);
342 error = intr_event_add_handler(intr->i_event, name, filt, handler, arg,
343 intr_priority(flags), flags, cookiep);
346 mtx_lock(&intr_list_lock);
347 intrcnt_setname(intr->i_event->ie_fullname, intr->i_cntidx);
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,
357 PIC_UNMASK(root_pic, intr->i_hw_irq);
359 mtx_unlock(&intr_list_lock);
366 arm_teardown_intr(void *cookie)
368 struct arm64_intr_entry *intr;
371 intr = intr_handler_source(cookie);
372 error = intr_event_remove_handler(cookie);
374 mtx_lock(&intr_list_lock);
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);
386 arm_config_intr(u_int hw_irq, enum intr_trigger trig, enum intr_polarity pol)
388 struct arm64_intr_entry *intr;
390 intr = intr_acquire(hw_irq);
397 if (!cold && root_pic != NULL)
398 PIC_CONFIG(root_pic, intr->i_hw_irq, trig, pol);
404 arm_dispatch_intr(u_int hw_irq, struct trapframe *tf)
406 struct arm64_intr_entry *intr;
408 SLIST_FOREACH(intr, &irq_slist_head, entries) {
409 if (intr->i_hw_irq == hw_irq) {
419 if (!intr_event_handle(intr->i_event, tf))
423 if (arm64_nstray < MAX_STRAY_LOG) {
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",
433 PIC_MASK(root_pic, intr->i_hw_irq);
435 if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN))
436 pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf);
441 arm_cpu_intr(struct trapframe *tf)
445 PIC_DISPATCH(root_pic, tf);
451 arm_setup_ipihandler(driver_filter_t *filt, u_int ipi)
454 arm_setup_intr("ipi", filt, NULL, (void *)((uintptr_t)ipi | 1<<16), ipi,
455 INTR_TYPE_MISC | INTR_EXCL, NULL);
460 arm_unmask_ipi(u_int ipi)
463 PIC_UNMASK(root_pic, ipi);
467 arm_init_secondary(void)
470 PIC_INIT_SECONDARY(root_pic);
475 ipi_all_but_self(u_int ipi)
479 other_cpus = all_cpus;
480 CPU_CLR(PCPU_GET(cpuid), &other_cpus);
482 /* ARM64TODO: This will be fixed with arm_intrng */
485 CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
486 PIC_IPI_SEND(root_pic, other_cpus, ipi);
490 ipi_cpu(int cpu, u_int ipi)
497 CTR2(KTR_SMP, "ipi_cpu: cpu: %d, ipi: %x", cpu, ipi);
498 PIC_IPI_SEND(root_pic, cpus, ipi);
502 ipi_selected(cpuset_t cpus, u_int ipi)
505 CTR1(KTR_SMP, "ipi_selected: ipi: %x", ipi);
506 PIC_IPI_SEND(root_pic, cpus, ipi);