Home | History | Annotate | Line # | Download | only in sparc64
      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
     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
    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
    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*
    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
    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 *
    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
    308 sparc_softintr_disestablish(void *cookie)
    309 {
    310 
    311 	kmem_free(cookie, sizeof(struct intrhand));
    312 }
    313 
    314 void
    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
    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
    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
    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
    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
    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
    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
    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