xref: /NextBSD/sys/arm64/arm64/intr_machdep.c (revision 4557fabb34e865d7f40be64b39c9e34fa41dbb60)
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