xref: /netbsd-src/sys/arch/sparc64/sparc64/intr.c (revision b83b86c8e464940ef6c185a042f73a405c9e5b58)
1 /*	$NetBSD: intr.c,v 1.72 2024/01/15 08:13:45 andvar Exp $ */
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *	This product includes software developed by the University of
14  *	California, Lawrence Berkeley Laboratory.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT OT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	@(#)intr.c	8.3 (Berkeley) 11/11/93
41  */
42 
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.72 2024/01/15 08:13:45 andvar Exp $");
45 
46 #include "opt_ddb.h"
47 #include "opt_multiprocessor.h"
48 
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/kernel.h>
52 #include <sys/kmem.h>
53 
54 #include <dev/cons.h>
55 
56 #include <machine/cpu.h>
57 #include <machine/ctlreg.h>
58 #include <machine/instr.h>
59 #include <machine/trap.h>
60 
61 #ifdef DEBUG
62 #define INTRDB_ESTABLISH   0x01
63 #define INTRDB_REUSE       0x02
64 static int sparc_intr_debug = 0x0;
65 #define DPRINTF(l, s)   do { if (sparc_intr_debug & l) printf s; } while (0)
66 #else
67 #define DPRINTF(l, s)
68 #endif
69 
70 /*
71  * The following array is to used by locore.s to map interrupt packets
72  * to the proper IPL to send ourselves a softint.  It should be filled
73  * in as the devices are probed.  We should eventually change this to a
74  * vector table and call these things directly.
75  */
76 struct intrhand *intrlev[MAXINTNUM];
77 
78 void	strayintr(const struct trapframe64 *, int);
79 int	intr_list_handler(void *);
80 
81 extern struct evcnt intr_evcnts[];
82 
83 /*
84  * Stray interrupt handler.  Clear it if possible.
85  * If not, and if we get 10 interrupts in 10 seconds, panic.
86  */
87 int ignore_stray = 1;
88 int straycnt[16];
89 
90 void
strayintr(const struct trapframe64 * fp,int vectored)91 strayintr(const struct trapframe64 *fp, int vectored)
92 {
93 	static int straytime, nstray;
94 	int timesince;
95 	char buf[256];
96 #if 0
97 	extern int swallow_zsintrs;
98 #endif
99 
100 	if (fp->tf_pil < 16)
101 		straycnt[(int)fp->tf_pil]++;
102 
103 	if (ignore_stray)
104 		return;
105 
106 	/* If we're in polled mode ignore spurious interrupts */
107 	if ((fp->tf_pil == PIL_SER) /* && swallow_zsintrs */) return;
108 
109 	snprintb(buf, sizeof(buf), PSTATE_BITS,
110 	    (fp->tf_tstate>>TSTATE_PSTATE_SHIFT));
111 	printf("stray interrupt ipl %u pc=%llx npc=%llx pstate=%s vectored=%d\n",
112 	    fp->tf_pil, (unsigned long long)fp->tf_pc,
113 	    (unsigned long long)fp->tf_npc,  buf, vectored);
114 
115 	timesince = time_second - straytime;
116 	if (timesince <= 10) {
117 		if (++nstray > 500)
118 			panic("crazy interrupts");
119 	} else {
120 		straytime = time_second;
121 		nstray = 1;
122 	}
123 #ifdef DDB
124 	Debugger();
125 #endif
126 }
127 
128 /*
129  * PCI devices can share interrupts so we need to have
130  * a handler to hand out interrupts.
131  */
132 int
intr_list_handler(void * arg)133 intr_list_handler(void *arg)
134 {
135 	int claimed = 0;
136 	struct intrhand *ih = (struct intrhand *)arg;
137 
138 	if (!arg) panic("intr_list_handler: no handlers!");
139 	while (ih && !claimed) {
140 		claimed = (*ih->ih_fun)(ih->ih_arg);
141 #ifdef DEBUG
142 		{
143 			extern int intrdebug;
144 			if (intrdebug & 1)
145 				printf("intr %p %x arg %p %s\n",
146 					ih, ih->ih_number, ih->ih_arg,
147 					claimed ? "claimed" : "");
148 		}
149 #endif
150 		ih = ih->ih_next;
151 	}
152 	return (claimed);
153 }
154 
155 #ifdef MULTIPROCESSOR
156 static int intr_biglock_wrapper(void *);
157 
158 static int
intr_biglock_wrapper(void * vp)159 intr_biglock_wrapper(void *vp)
160 {
161 	struct intrhand *ih = vp;
162 	int ret;
163 
164 	KERNEL_LOCK(1, NULL);
165 	ret = (*ih->ih_realfun)(ih->ih_realarg);
166 	KERNEL_UNLOCK_ONE(NULL);
167 
168 	return ret;
169 }
170 #endif
171 
172 /*
173  * Allocate memory for interrupt handler.
174  * The allocated memory is initialized with zeros so
175  * e.g. pointers in the intrhand structure are properly initialized.
176  * A valid pointer is always returned by the function.
177  */
178 struct intrhand*
intrhand_alloc(void)179 intrhand_alloc(void)
180 {
181 	struct intrhand *ih = kmem_zalloc(sizeof(struct intrhand), KM_NOSLEEP);
182 	if (ih == NULL)
183 		panic("%s: failed to allocate intrhand", __func__);
184 	return ih;
185 }
186 
187 /*
188  * Attach an interrupt handler to the vector chain for the given level.
189  * This is not possible if it has been taken away as a fast vector.
190  */
191 void
intr_establish(int level,bool mpsafe,struct intrhand * ih)192 intr_establish(int level, bool mpsafe, struct intrhand *ih)
193 {
194 	struct intrhand *q = NULL;
195 	int s;
196 #ifdef DEBUG
197 	int opil = ih->ih_pil;
198 #endif
199 
200 	/*
201 	 * This is O(N^2) for long chains, but chains are never long
202 	 * and we do want to preserve order.
203 	 */
204 #ifdef DIAGNOSTIC
205 	if (ih->ih_pil != level)
206 		printf("%s: caller %p did not pre-set ih_pil\n",
207 		    __func__, __builtin_return_address(0));
208 	if (ih->ih_pending != 0)
209 		printf("%s: caller %p did not pre-set ih_pending to zero\n",
210 		    __func__, __builtin_return_address(0));
211 #endif
212 	ih->ih_pil = level; /* XXXX caller should have done this before */
213 	ih->ih_pending = 0; /* XXXX caller should have done this before */
214 	ih->ih_next = NULL;
215 
216 	/*
217 	 * no need for a separate counter if ivec == 0, in that case there's
218 	 * either only one device using the interrupt level and there's already
219 	 * a counter for it or it's something special like psycho's error
220 	 * interrupts
221 	 */
222 	if (ih->ih_ivec != 0 && intrlev[ih->ih_number] == NULL) {
223 		snprintf(ih->ih_name, sizeof(ih->ih_name), "%x", ih->ih_ivec);
224 		evcnt_attach_dynamic(&ih->ih_cnt, EVCNT_TYPE_INTR,
225 		    &intr_evcnts[level], "ivec", ih->ih_name);
226 	}
227 
228 	/* opil because we overwrote it above with level */
229 	DPRINTF(INTRDB_ESTABLISH,
230 	    ("%s: level %x ivec %x inumber %x pil %x\n",
231 	     __func__, level, ih->ih_ivec, ih->ih_number, opil));
232 
233 #ifdef MULTIPROCESSOR
234 	if (!mpsafe) {
235 		ih->ih_realarg = ih->ih_arg;
236 		ih->ih_realfun = ih->ih_fun;
237 		ih->ih_arg = ih;
238 		ih->ih_fun = intr_biglock_wrapper;
239 	}
240 #endif
241 
242 	/* XXXSMP */
243 	s = splhigh();
244 	/*
245 	 * Store in fast lookup table
246 	 */
247 #ifdef NOT_DEBUG
248 	if (!ih->ih_number) {
249 		printf("\nintr_establish: NULL vector fun %p arg %p pil %p\n",
250 			  ih->ih_fun, ih->ih_arg, ih->ih_number, ih->ih_pil);
251 		Debugger();
252 	}
253 #endif
254 	if (ih->ih_number < MAXINTNUM && ih->ih_number >= 0) {
255 		if ((q = intrlev[ih->ih_number])) {
256 			struct intrhand *nih;
257 			/*
258 			 * Interrupt is already there.  We need to create a
259 			 * new interrupt handler and interpose it.
260 			 */
261 			DPRINTF(INTRDB_REUSE,
262 			    ("intr_establish: intr reused %x\n",
263 			     ih->ih_number));
264 			if (q->ih_fun != intr_list_handler) {
265 				nih = intrhand_alloc();
266 				/* Point the old IH at the new handler */
267 				*nih = *q;
268 				nih->ih_next = NULL;
269 				q->ih_arg = (void *)nih;
270 				q->ih_fun = intr_list_handler;
271 			}
272 			/* Add the ih to the head of the list */
273 			ih->ih_next = (struct intrhand *)q->ih_arg;
274 			q->ih_arg = (void *)ih;
275 		} else {
276 			intrlev[ih->ih_number] = ih;
277 		}
278 #ifdef NOT_DEBUG
279 		printf("\nintr_establish: vector %x pil %x mapintr %p "
280 			"clrintr %p fun %p arg %p\n",
281 			ih->ih_number, ih->ih_pil, (void *)ih->ih_map,
282 			(void *)ih->ih_clr, (void *)ih->ih_fun,
283 			(void *)ih->ih_arg);
284 		/*Debugger();*/
285 #endif
286 	} else
287 		panic("intr_establish: bad intr number %x", ih->ih_number);
288 
289 	splx(s);
290 }
291 
292 /*
293  * Prepare an interrupt handler used for send_softint.
294  */
295 void *
sparc_softintr_establish(int pil,int (* fun)(void *),void * arg)296 sparc_softintr_establish(int pil, int (*fun)(void *), void *arg)
297 {
298 	struct intrhand *ih;
299 
300 	ih = intrhand_alloc();
301 	ih->ih_fun = fun;
302 	ih->ih_pil = pil;
303 	ih->ih_arg = arg;
304 	return ih;
305 }
306 
307 void
sparc_softintr_disestablish(void * cookie)308 sparc_softintr_disestablish(void *cookie)
309 {
310 
311 	kmem_free(cookie, sizeof(struct intrhand));
312 }
313 
314 void
sparc_softintr_schedule(void * cookie)315 sparc_softintr_schedule(void *cookie)
316 {
317 	struct intrhand *ih = (struct intrhand *)cookie;
318 
319 	send_softint(-1, ih->ih_pil, ih);
320 }
321 
322 #ifdef __HAVE_FAST_SOFTINTS
323 /*
324  * MD implementation of FAST software interrupt framework
325  */
326 
327 int softint_fastintr(void *);
328 
329 void
softint_init_md(lwp_t * l,u_int level,uintptr_t * machdep)330 softint_init_md(lwp_t *l, u_int level, uintptr_t *machdep)
331 {
332 	struct intrhand *ih;
333 	int pil;
334 
335 	switch (level) {
336 	case SOFTINT_BIO:
337 		pil = IPL_SOFTBIO;
338 		break;
339 	case SOFTINT_NET:
340 		pil = IPL_SOFTNET;
341 		break;
342 	case SOFTINT_SERIAL:
343 		pil = IPL_SOFTSERIAL;
344 		break;
345 	case SOFTINT_CLOCK:
346 		pil = IPL_SOFTCLOCK;
347 		break;
348 	default:
349 		panic("softint_init_md");
350 	}
351 
352 	ih = sparc_softintr_establish(pil, softint_fastintr, l);
353 	*machdep = (uintptr_t)ih;
354 }
355 
356 void
softint_trigger(uintptr_t machdep)357 softint_trigger(uintptr_t machdep)
358 {
359 	struct intrhand *ih = (struct intrhand *)machdep;
360 
361 	send_softint(-1, ih->ih_pil, ih);
362 }
363 #endif /* __HAVE_FAST_SOFTINTS */
364 
365 #ifdef SUN4V
366 
367 #include <machine/hypervisor.h>
368 
369 uint64_t sun4v_group_interrupt_major;
370 
371 int64_t
sun4v_intr_devino_to_sysino(uint64_t devhandle,uint64_t devino,uint64_t * ino)372 sun4v_intr_devino_to_sysino(uint64_t devhandle, uint64_t devino, uint64_t *ino)
373 {
374 	if (sun4v_group_interrupt_major < 3)
375 		return hv_intr_devino_to_sysino(devhandle, devino, ino);
376 
377 	*ino = devino;
378 	return H_EOK;
379 }
380 
381 int64_t
sun4v_intr_setcookie(uint64_t devhandle,uint64_t ino,uint64_t cookie_value)382 sun4v_intr_setcookie(uint64_t devhandle, uint64_t ino, uint64_t cookie_value)
383 {
384 	if (sun4v_group_interrupt_major < 3)
385 		return H_EOK;
386 
387 	return hv_vintr_setcookie(devhandle, ino, cookie_value);
388 }
389 
390 int64_t
sun4v_intr_setenabled(uint64_t devhandle,uint64_t ino,uint64_t intr_enabled)391 sun4v_intr_setenabled(uint64_t devhandle, uint64_t ino, uint64_t intr_enabled)
392 {
393 	if (sun4v_group_interrupt_major < 3)
394 		return hv_intr_setenabled(ino, intr_enabled);
395 
396 	return hv_vintr_setenabled(devhandle, ino, intr_enabled);
397 }
398 
399 int64_t
sun4v_intr_setstate(uint64_t devhandle,uint64_t ino,uint64_t intr_state)400 sun4v_intr_setstate(uint64_t devhandle, uint64_t ino, uint64_t intr_state)
401 {
402 	if (sun4v_group_interrupt_major < 3)
403 		return hv_intr_setstate(ino, intr_state);
404 
405 	return hv_vintr_setstate(devhandle, ino, intr_state);
406 }
407 
408 int64_t
sun4v_intr_settarget(uint64_t devhandle,uint64_t ino,uint64_t cpuid)409 sun4v_intr_settarget(uint64_t devhandle, uint64_t ino, uint64_t cpuid)
410 {
411 	if (sun4v_group_interrupt_major < 3)
412 		return hv_intr_settarget(ino, cpuid);
413 
414 	return hv_vintr_settarget(devhandle, ino, cpuid);
415 }
416 
417 #endif
418