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
intr_init(void * dummy __unused)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
intrcnt_setname(const char * name,u_int idx)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 *
intr_acquire(u_int hw_irq)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
intr_pre_ithread(void * arg)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
intr_post_ithread(void * arg)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
intr_post_filter(void * arg)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
arm_register_root_pic(device_t dev,u_int nirq)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
arm_register_msi_pic(device_t dev)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
arm_alloc_msi(device_t pci,device_t child,int count,int maxcount,int * irqs)220 arm_alloc_msi(device_t pci, device_t child, int count, int maxcount, int *irqs)
221 {
222
223 return (PIC_ALLOC_MSI(msi_pic, child, count, irqs));
224 }
225
226 int
arm_release_msi(device_t pci,device_t child,int count,int * irqs)227 arm_release_msi(device_t pci, device_t child, int count, int *irqs)
228 {
229
230 return (PIC_RELEASE_MSI(msi_pic, child, count, irqs));
231 }
232
233 int
arm_map_msi(device_t pci,device_t child,int irq,uint64_t * addr,uint32_t * data)234 arm_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data)
235 {
236
237 return (PIC_MAP_MSI(msi_pic, child, irq, addr, data));
238 }
239
240 int
arm_alloc_msix(device_t pci,device_t child,int * irq)241 arm_alloc_msix(device_t pci, device_t child, int *irq)
242 {
243
244 return (PIC_ALLOC_MSIX(msi_pic, child, irq));
245 }
246
247 int
arm_release_msix(device_t pci,device_t child,int irq)248 arm_release_msix(device_t pci, device_t child, int irq)
249 {
250
251 return (PIC_RELEASE_MSIX(msi_pic, child, irq));
252 }
253
254
255 /*
256 * Finalize interrupts bring-up (should be called from configure_final()).
257 * Enables all interrupts registered by bus_setup_intr() during boot
258 * as well as unlocks interrups reception on primary CPU.
259 */
260 int
arm_enable_intr(void)261 arm_enable_intr(void)
262 {
263 struct arm64_intr_entry *intr;
264
265 if (root_pic == NULL)
266 panic("Cannot enable interrupts. No PIC configured");
267
268 /*
269 * Iterate through all possible interrupts and perform
270 * configuration if the interrupt is registered.
271 */
272 SLIST_FOREACH(intr, &irq_slist_head, entries) {
273 /*
274 * XXX: In case we allowed to set up interrupt whose number
275 * exceeds maximum number of interrupts for the root PIC
276 * disable it and print proper error message.
277 *
278 * This can happen only when calling bus_setup_intr()
279 * before the interrupt controller is attached.
280 */
281 if (intr->i_cntidx >= arm64_nintrs) {
282 /* Better fail when IVARIANTS enabled */
283 KASSERT(0, ("%s: Interrupt %u cannot be handled by the "
284 "registered PIC. Max interrupt number: %u", __func__,
285 intr->i_cntidx, arm64_nintrs - 1));
286 /* Print message and disable otherwise */
287 printf("ERROR: Cannot enable irq %u. Disabling.\n",
288 intr->i_cntidx);
289 PIC_MASK(root_pic, intr->i_hw_irq);
290 }
291
292 if (intr->i_trig != INTR_TRIGGER_CONFORM ||
293 intr->i_pol != INTR_POLARITY_CONFORM) {
294 PIC_CONFIG(root_pic, intr->i_hw_irq,
295 intr->i_trig, intr->i_pol);
296 }
297
298 if (intr->i_handlers > 0)
299 PIC_UNMASK(root_pic, intr->i_hw_irq);
300
301 }
302 /* Enable interrupt reception on this CPU */
303 intr_enable();
304
305 return (0);
306 }
307
308 int
arm_setup_intr(const char * name,driver_filter_t * filt,driver_intr_t handler,void * arg,u_int hw_irq,enum intr_type flags,void ** cookiep)309 arm_setup_intr(const char *name, driver_filter_t *filt, driver_intr_t handler,
310 void *arg, u_int hw_irq, enum intr_type flags, void **cookiep)
311 {
312 struct arm64_intr_entry *intr;
313 int error;
314
315 intr = intr_acquire(hw_irq);
316 if (intr == NULL)
317 return (ENOMEM);
318
319 /*
320 * Watch out for interrupts' numbers.
321 * If this is a system boot then don't allow to overfill interrupts
322 * table (the interrupts will be deconfigured in arm_enable_intr()).
323 */
324 if (intr->i_cntidx >= NIRQS)
325 return (EINVAL);
326
327 if (intr->i_event == NULL) {
328 error = intr_event_create(&intr->i_event, (void *)intr, 0,
329 hw_irq, intr_pre_ithread, intr_post_ithread,
330 intr_post_filter, NULL, "irq%u", hw_irq);
331 if (error)
332 return (error);
333 }
334
335 error = intr_event_add_handler(intr->i_event, name, filt, handler, arg,
336 intr_priority(flags), flags, cookiep);
337
338 if (!error) {
339 mtx_lock(&intr_list_lock);
340 intrcnt_setname(intr->i_event->ie_fullname, intr->i_cntidx);
341 intr->i_handlers++;
342
343 if (!cold && intr->i_handlers == 1) {
344 if (intr->i_trig != INTR_TRIGGER_CONFORM ||
345 intr->i_pol != INTR_POLARITY_CONFORM) {
346 PIC_CONFIG(root_pic, intr->i_hw_irq, intr->i_trig,
347 intr->i_pol);
348 }
349
350 PIC_UNMASK(root_pic, intr->i_hw_irq);
351 }
352 mtx_unlock(&intr_list_lock);
353 }
354
355 return (error);
356 }
357
358 int
arm_teardown_intr(void * cookie)359 arm_teardown_intr(void *cookie)
360 {
361 struct arm64_intr_entry *intr;
362 int error;
363
364 intr = intr_handler_source(cookie);
365 error = intr_event_remove_handler(cookie);
366 if (!error) {
367 mtx_lock(&intr_list_lock);
368 intr->i_handlers--;
369 if (intr->i_handlers == 0)
370 PIC_MASK(root_pic, intr->i_hw_irq);
371 intrcnt_setname(intr->i_event->ie_fullname, intr->i_cntidx);
372 mtx_unlock(&intr_list_lock);
373 }
374
375 return (error);
376 }
377
378 int
arm_config_intr(u_int hw_irq,enum intr_trigger trig,enum intr_polarity pol)379 arm_config_intr(u_int hw_irq, enum intr_trigger trig, enum intr_polarity pol)
380 {
381 struct arm64_intr_entry *intr;
382
383 intr = intr_acquire(hw_irq);
384 if (intr == NULL)
385 return (ENOMEM);
386
387 intr->i_trig = trig;
388 intr->i_pol = pol;
389
390 if (!cold && root_pic != NULL)
391 PIC_CONFIG(root_pic, intr->i_hw_irq, trig, pol);
392
393 return (0);
394 }
395
396 void
arm_dispatch_intr(u_int hw_irq,struct trapframe * tf)397 arm_dispatch_intr(u_int hw_irq, struct trapframe *tf)
398 {
399 struct arm64_intr_entry *intr;
400
401 SLIST_FOREACH(intr, &irq_slist_head, entries) {
402 if (intr->i_hw_irq == hw_irq) {
403 break;
404 }
405 }
406
407 if (intr == NULL)
408 goto stray;
409
410 (*intr->i_cntp)++;
411
412 if (!intr_event_handle(intr->i_event, tf))
413 return;
414
415 stray:
416 if (arm64_nstray < MAX_STRAY_LOG) {
417 arm64_nstray++;
418 printf("Stray IRQ %u\n", hw_irq);
419 if (arm64_nstray >= MAX_STRAY_LOG) {
420 printf("Got %d stray IRQs. Not logging anymore.\n",
421 MAX_STRAY_LOG);
422 }
423 }
424
425 if (intr != NULL)
426 PIC_MASK(root_pic, intr->i_hw_irq);
427 #ifdef HWPMC_HOOKS
428 if (pmc_hook && (PCPU_GET(curthread)->td_pflags & TDP_CALLCHAIN))
429 pmc_hook(PCPU_GET(curthread), PMC_FN_USER_CALLCHAIN, tf);
430 #endif
431 }
432
433 void
arm_cpu_intr(struct trapframe * tf)434 arm_cpu_intr(struct trapframe *tf)
435 {
436
437 critical_enter();
438 PIC_DISPATCH(root_pic, tf);
439 critical_exit();
440 }
441
442 #ifdef SMP
443 void
arm_setup_ipihandler(driver_filter_t * filt,u_int ipi)444 arm_setup_ipihandler(driver_filter_t *filt, u_int ipi)
445 {
446
447 arm_setup_intr("ipi", filt, NULL, (void *)((uintptr_t)ipi | 1<<16), ipi,
448 INTR_TYPE_MISC | INTR_EXCL, NULL);
449 arm_unmask_ipi(ipi);
450 }
451
452 void
arm_unmask_ipi(u_int ipi)453 arm_unmask_ipi(u_int ipi)
454 {
455
456 PIC_UNMASK(root_pic, ipi);
457 }
458
459 void
arm_init_secondary(void)460 arm_init_secondary(void)
461 {
462
463 PIC_INIT_SECONDARY(root_pic);
464 }
465
466 /* Sending IPI */
467 void
ipi_all_but_self(u_int ipi)468 ipi_all_but_self(u_int ipi)
469 {
470 cpuset_t other_cpus;
471
472 other_cpus = all_cpus;
473 CPU_CLR(PCPU_GET(cpuid), &other_cpus);
474
475 /* ARM64TODO: This will be fixed with arm_intrng */
476 ipi += 16;
477
478 CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
479 PIC_IPI_SEND(root_pic, other_cpus, ipi);
480 }
481
482 void
ipi_cpu(int cpu,u_int ipi)483 ipi_cpu(int cpu, u_int ipi)
484 {
485 cpuset_t cpus;
486
487 CPU_ZERO(&cpus);
488 CPU_SET(cpu, &cpus);
489
490 CTR2(KTR_SMP, "ipi_cpu: cpu: %d, ipi: %x", cpu, ipi);
491 PIC_IPI_SEND(root_pic, cpus, ipi);
492 }
493
494 void
ipi_selected(cpuset_t cpus,u_int ipi)495 ipi_selected(cpuset_t cpus, u_int ipi)
496 {
497
498 CTR1(KTR_SMP, "ipi_selected: ipi: %x", ipi);
499 PIC_IPI_SEND(root_pic, cpus, ipi);
500 }
501
502 #endif
503