xref: /netbsd-src/sys/dev/mvme/vme_two_isr.c (revision d3ce0206640f3452ed48ee76b3e4afe4033aeab6)
1*d3ce0206Sthorpej /*	$NetBSD: vme_two_isr.c,v 1.18 2024/01/19 03:59:47 thorpej Exp $	*/
208bde987Sscw 
308bde987Sscw /*-
408bde987Sscw  * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
508bde987Sscw  * All rights reserved.
608bde987Sscw  *
708bde987Sscw  * This code is derived from software contributed to The NetBSD Foundation
808bde987Sscw  * by Steve C. Woodford.
908bde987Sscw  *
1008bde987Sscw  * Redistribution and use in source and binary forms, with or without
1108bde987Sscw  * modification, are permitted provided that the following conditions
1208bde987Sscw  * are met:
1308bde987Sscw  * 1. Redistributions of source code must retain the above copyright
1408bde987Sscw  *    notice, this list of conditions and the following disclaimer.
1508bde987Sscw  * 2. Redistributions in binary form must reproduce the above copyright
1608bde987Sscw  *    notice, this list of conditions and the following disclaimer in the
1708bde987Sscw  *    documentation and/or other materials provided with the distribution.
1808bde987Sscw  *
1908bde987Sscw  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2008bde987Sscw  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2108bde987Sscw  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2208bde987Sscw  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2308bde987Sscw  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2408bde987Sscw  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2508bde987Sscw  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2608bde987Sscw  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2708bde987Sscw  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2808bde987Sscw  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2908bde987Sscw  * POSSIBILITY OF SUCH DAMAGE.
3008bde987Sscw  */
3108bde987Sscw 
3208bde987Sscw /*
3308bde987Sscw  * Split off from vme_two.c specifically to deal with hardware assisted
3408bde987Sscw  * soft interrupts when the user hasn't specified `vmetwo0' in the
3508bde987Sscw  * kernel config file (mvme1[67]2 only).
3608bde987Sscw  */
3708bde987Sscw 
38365cbd94Slukem #include <sys/cdefs.h>
39*d3ce0206Sthorpej __KERNEL_RCSID(0, "$NetBSD: vme_two_isr.c,v 1.18 2024/01/19 03:59:47 thorpej Exp $");
40365cbd94Slukem 
4108bde987Sscw #include "vmetwo.h"
42*d3ce0206Sthorpej #include "opt_mvmeconf.h"
4308bde987Sscw 
4408bde987Sscw #include <sys/param.h>
4508bde987Sscw #include <sys/kernel.h>
4608bde987Sscw #include <sys/systm.h>
4708bde987Sscw #include <sys/device.h>
4808bde987Sscw #include <sys/malloc.h>
49a2a38285Sad #include <sys/cpu.h>
50a2a38285Sad #include <sys/bus.h>
5108bde987Sscw 
5208bde987Sscw #include <dev/vme/vmereg.h>
5308bde987Sscw #include <dev/vme/vmevar.h>
5408bde987Sscw 
5508bde987Sscw #include <dev/mvme/mvmebus.h>
5608bde987Sscw #include <dev/mvme/vme_tworeg.h>
5708bde987Sscw #include <dev/mvme/vme_twovar.h>
5808bde987Sscw 
5908bde987Sscw /*
6008bde987Sscw  * Non-zero if there is no VMEChip2 on this board.
6108bde987Sscw  */
6208bde987Sscw int vmetwo_not_present;
6308bde987Sscw 
6408bde987Sscw /*
6508bde987Sscw  * Array of interrupt handlers registered with us for the non-VMEbus
6608bde987Sscw  * vectored interrupts. Eg. ABORT Switch, SYSFAIL etc.
6708bde987Sscw  *
6808bde987Sscw  * We can't just install a caller's handler directly, since these
6908bde987Sscw  * interrupts have to be manually cleared, so we have a trampoline
7008bde987Sscw  * which does the clearing automatically.
7108bde987Sscw  */
7208bde987Sscw static struct vme_two_handler {
7318db93c7Sperry 	int (*isr_hand)(void *);
7408bde987Sscw 	void *isr_arg;
7508bde987Sscw } vme_two_handlers[(VME2_VECTOR_LOCAL_MAX - VME2_VECTOR_LOCAL_MIN) + 1];
7608bde987Sscw 
7708bde987Sscw #define VMETWO_HANDLERS_SZ	(sizeof(vme_two_handlers) /	\
7808bde987Sscw 				 sizeof(struct vme_two_handler))
7908bde987Sscw 
8008bde987Sscw static	int  vmetwo_local_isr_trampoline(void *);
814b293a84Sad #ifdef notyet
8208bde987Sscw static	void vmetwo_softintr_assert(void);
834b293a84Sad #endif
8408bde987Sscw 
8508bde987Sscw static	struct vmetwo_softc *vmetwo_sc;
8608bde987Sscw 
8708bde987Sscw int
vmetwo_probe(bus_space_tag_t bt,bus_addr_t offset)8808bde987Sscw vmetwo_probe(bus_space_tag_t bt, bus_addr_t offset)
8908bde987Sscw {
9008bde987Sscw 	bus_space_handle_t bh;
9108bde987Sscw 
9208bde987Sscw 	bus_space_map(bt, offset + VME2REG_LCSR_OFFSET, VME2LCSR_SIZE, 0, &bh);
9308bde987Sscw 
9408bde987Sscw 	if (bus_space_peek_4(bt, bh, VME2LCSR_MISC_STATUS, NULL) != 0) {
9508bde987Sscw #if defined(MVME162) || defined(MVME172)
9608bde987Sscw #if defined(MVME167) || defined(MVME177)
9708bde987Sscw 		if (machineid == MVME_162 || machineid == MVME_172)
9808bde987Sscw #endif
9908bde987Sscw 		{
10008bde987Sscw 			/*
10108bde987Sscw 			 * No VMEChip2 on mvme162/172 is not too big a
10208bde987Sscw 			 * deal; we can fall back on timer4 in the
10308bde987Sscw 			 * mcchip for h/w assisted soft interrupts...
10408bde987Sscw 			 */
10508bde987Sscw 			extern void pcctwosoftintrinit(void);
10608bde987Sscw 			bus_space_unmap(bt, bh, VME2LCSR_SIZE);
10708bde987Sscw 			vmetwo_not_present = 1;
10808bde987Sscw 			pcctwosoftintrinit();
10908bde987Sscw 			return (0);
11008bde987Sscw 		}
11108bde987Sscw #endif
11208bde987Sscw #if defined(MVME167) || defined(MVME177) || defined(MVME88K)
11308bde987Sscw 		/*
11408bde987Sscw 		 * No VMEChip2 on mvme167/177, however, is a Big Deal.
11508bde987Sscw 		 * In fact, it means the hardware's shot since the
11608bde987Sscw 		 * VMEChip2 is not a `build-option' on those boards.
11708bde987Sscw 		 */
11808bde987Sscw 		panic("VMEChip2 not responding! Faulty board?");
11908bde987Sscw 		/* NOTREACHED */
12008bde987Sscw #endif
12108bde987Sscw #if defined(MVMEPPC)
12208bde987Sscw 		/*
12308bde987Sscw 		 * No VMEChip2 on mvmeppc is no big deal.
12408bde987Sscw 		 */
12508bde987Sscw 		bus_space_unmap(bt, bh, VME2LCSR_SIZE);
12608bde987Sscw 		vmetwo_not_present = 1;
12708bde987Sscw 		return (0);
12808bde987Sscw #endif
12908bde987Sscw 	}
13008bde987Sscw #if NVMETWO == 0
13108bde987Sscw 	else {
13208bde987Sscw 		/*
13308bde987Sscw 		 * The kernel config file has no `vmetwo0' device, but
13408bde987Sscw 		 * there is a VMEChip2 on the board. Fix up things
13508bde987Sscw 		 * just enough to hook VMEChip2 local interrupts.
13608bde987Sscw 		 */
13708bde987Sscw 		struct vmetwo_softc *sc;
13808bde987Sscw 
139d47bcd29Schs 		sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK);
14008bde987Sscw 		sc->sc_mvmebus.sc_bust = bt;
14108bde987Sscw 		sc->sc_lcrh = bh;
14208bde987Sscw 		vmetwo_intr_init(sc);
14308bde987Sscw 		return 0;
14408bde987Sscw 	}
14508bde987Sscw #else
14608bde987Sscw 	bus_space_unmap(bt, bh, VME2LCSR_SIZE);
14708bde987Sscw 	return (1);
14808bde987Sscw #endif
14908bde987Sscw }
15008bde987Sscw 
15108bde987Sscw void
vmetwo_intr_init(struct vmetwo_softc * sc)15208bde987Sscw vmetwo_intr_init(struct vmetwo_softc *sc)
15308bde987Sscw {
15408bde987Sscw 	u_int32_t reg;
15508bde987Sscw 	int i;
15608bde987Sscw 
15708bde987Sscw 	vmetwo_sc = sc;
15808bde987Sscw 
15908bde987Sscw 	/* Clear out the ISR handler array */
16008bde987Sscw 	for (i = 0; i < VMETWO_HANDLERS_SZ; i++)
16108bde987Sscw 		vme_two_handlers[i].isr_hand = NULL;
16208bde987Sscw 
16308bde987Sscw 	/*
16408bde987Sscw 	 * Initialize the chip.
16508bde987Sscw 	 * Firstly, disable all VMEChip2 Interrupts
16608bde987Sscw 	 */
16708bde987Sscw 	reg = vme2_lcsr_read(sc, VME2LCSR_MISC_STATUS) & ~VME2_MISC_STATUS_MIEN;
16808bde987Sscw 	vme2_lcsr_write(sc, VME2LCSR_MISC_STATUS, reg);
16908bde987Sscw 	vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, 0);
17008bde987Sscw 	vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR,
17108bde987Sscw 	    VME2_LOCAL_INTERRUPT_CLEAR_ALL);
17208bde987Sscw 
17308bde987Sscw 	/* Zap all the IRQ level registers */
17408bde987Sscw 	for (i = 0; i < VME2_NUM_IL_REGS; i++)
17508bde987Sscw 		vme2_lcsr_write(sc, VME2LCSR_INTERRUPT_LEVEL_BASE + (i * 4), 0);
17608bde987Sscw 
17708bde987Sscw 	/* Disable the tick timers */
17808bde987Sscw 	reg = vme2_lcsr_read(sc, VME2LCSR_TIMER_CONTROL);
17908bde987Sscw 	reg &= ~VME2_TIMER_CONTROL_EN(0);
18008bde987Sscw 	reg &= ~VME2_TIMER_CONTROL_EN(1);
18108bde987Sscw 	vme2_lcsr_write(sc, VME2LCSR_TIMER_CONTROL, reg);
18208bde987Sscw 
18308bde987Sscw 	/* Set the VMEChip2's vector base register to the required value */
18408bde987Sscw 	reg = vme2_lcsr_read(sc, VME2LCSR_VECTOR_BASE);
18508bde987Sscw 	reg &= ~VME2_VECTOR_BASE_MASK;
18608bde987Sscw 	reg |= VME2_VECTOR_BASE_REG_VALUE;
18708bde987Sscw 	vme2_lcsr_write(sc, VME2LCSR_VECTOR_BASE, reg);
18808bde987Sscw 
18908bde987Sscw 	/* Set the Master Interrupt Enable bit now */
19008bde987Sscw 	reg = vme2_lcsr_read(sc, VME2LCSR_MISC_STATUS) | VME2_MISC_STATUS_MIEN;
19108bde987Sscw 	vme2_lcsr_write(sc, VME2LCSR_MISC_STATUS, reg);
19208bde987Sscw 
1937e69c325Sscw 	/* Allow the MD code the chance to do some initialising */
1947e69c325Sscw 	vmetwo_md_intr_init(sc);
1957e69c325Sscw 
19608bde987Sscw #if defined(MVME167) || defined(MVME177)
19708bde987Sscw #if defined(MVME162) || defined(MVME172)
19808bde987Sscw 	if (machineid != MVME_162 && machineid != MVME_172)
19908bde987Sscw #endif
20008bde987Sscw 	{
20108bde987Sscw 		/*
20208bde987Sscw 		 * Let the NMI handler deal with level 7 ABORT switch
20308bde987Sscw 		 * interrupts
20408bde987Sscw 		 */
20508bde987Sscw 		vmetwo_intr_establish(sc, 7, 7, VME2_VEC_ABORT, 1,
20608bde987Sscw 		    nmihand, NULL, NULL);
20708bde987Sscw 	}
20808bde987Sscw #endif
20908bde987Sscw 
21008bde987Sscw 	/* Setup hardware assisted soft interrupts */
2114b293a84Sad #ifdef notyet
21208bde987Sscw 	vmetwo_intr_establish(sc, 1, 1, VME2_VEC_SOFT0, 1,
21308bde987Sscw 	    (int (*)(void *))softintr_dispatch, NULL, NULL);
21408bde987Sscw 	_softintr_chipset_assert = vmetwo_softintr_assert;
2154b293a84Sad #endif
21608bde987Sscw }
21708bde987Sscw 
21808bde987Sscw static int
vmetwo_local_isr_trampoline(void * arg)219454af1c0Sdsl vmetwo_local_isr_trampoline(void *arg)
22008bde987Sscw {
22108bde987Sscw 	struct vme_two_handler *isr;
22208bde987Sscw 	int vec;
22308bde987Sscw 
22408bde987Sscw 	vec = (int) arg;	/* 0x08 <= vec <= 0x1f */
22508bde987Sscw 
22608bde987Sscw 	/* Clear the interrupt source */
22708bde987Sscw 	vme2_lcsr_write(vmetwo_sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR,
22808bde987Sscw 	    VME2_LOCAL_INTERRUPT(vec));
22908bde987Sscw 
23008bde987Sscw 	isr = &vme_two_handlers[vec - VME2_VECTOR_LOCAL_OFFSET];
23108bde987Sscw 	if (isr->isr_hand)
23208bde987Sscw 		(void) (*isr->isr_hand) (isr->isr_arg);
23308bde987Sscw 	else
23408bde987Sscw 		printf("vmetwo: Spurious local interrupt, vector 0x%x\n", vec);
23508bde987Sscw 
23608bde987Sscw 	return (1);
23708bde987Sscw }
23808bde987Sscw 
23908bde987Sscw void
vmetwo_local_intr_establish(int pri,int vec,int (* hand)(void *),void * arg,struct evcnt * evcnt)2407cc9af7dSdsl vmetwo_local_intr_establish(int pri, int vec, int (*hand)(void *), void *arg, struct evcnt *evcnt)
24108bde987Sscw {
24208bde987Sscw 
24308bde987Sscw 	vmetwo_intr_establish(vmetwo_sc, pri, pri, vec, 1, hand, arg, evcnt);
24408bde987Sscw }
24508bde987Sscw 
24608bde987Sscw /* ARGSUSED */
24708bde987Sscw void
vmetwo_intr_establish(void * csc,int prior,int lvl,int vec,int first,int (* hand)(void *),void * arg,struct evcnt * evcnt)2487cc9af7dSdsl vmetwo_intr_establish(void *csc, int prior, int lvl, int vec, int first, int (*hand)(void *), void *arg, struct evcnt *evcnt)
24908bde987Sscw {
25008bde987Sscw 	struct vmetwo_softc *sc = csc;
25108bde987Sscw 	u_int32_t reg;
25208bde987Sscw 	int bitoff;
25308bde987Sscw 	int iloffset, ilshift;
25408bde987Sscw 	int s;
25508bde987Sscw 
25608bde987Sscw 	s = splhigh();
25708bde987Sscw 
25808bde987Sscw #if NVMETWO > 0
25908bde987Sscw 	/*
26008bde987Sscw 	 * Sort out interrupts generated locally by the VMEChip2 from
26108bde987Sscw 	 * those generated by VMEbus devices...
26208bde987Sscw 	 */
26308bde987Sscw 	if (vec >= VME2_VECTOR_LOCAL_MIN && vec <= VME2_VECTOR_LOCAL_MAX) {
26408bde987Sscw #endif
26508bde987Sscw 		/*
26608bde987Sscw 		 * Local interrupts need to be bounced through some
26708bde987Sscw 		 * trampoline code which acknowledges/clears them.
26808bde987Sscw 		 */
26908bde987Sscw 		vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_hand = hand;
27008bde987Sscw 		vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_arg = arg;
27108bde987Sscw 		hand = vmetwo_local_isr_trampoline;
27208bde987Sscw 		arg = (void *) (vec - VME2_VECTOR_BASE);
27308bde987Sscw 
27408bde987Sscw 		/*
27508bde987Sscw 		 * Interrupt enable/clear bit offset is 0x08 - 0x1f
27608bde987Sscw 		 */
27708bde987Sscw 		bitoff = vec - VME2_VECTOR_BASE;
27808bde987Sscw #if NVMETWO > 0
27908bde987Sscw 		first = 1;	/* Force the interrupt to be enabled */
28008bde987Sscw 	} else {
28108bde987Sscw 		/*
28208bde987Sscw 		 * Interrupts originating from the VMEbus are
28308bde987Sscw 		 * controlled by an offset of 0x00 - 0x07
28408bde987Sscw 		 */
28508bde987Sscw 		bitoff = lvl - 1;
28608bde987Sscw 	}
28708bde987Sscw #endif
28808bde987Sscw 
28908bde987Sscw 	/* Hook the interrupt */
29008bde987Sscw 	(*sc->sc_isrlink)(sc->sc_isrcookie, hand, arg, prior, vec, evcnt);
29108bde987Sscw 
29208bde987Sscw 	/*
29308bde987Sscw 	 * Do we need to tell the VMEChip2 to let the interrupt through?
29408bde987Sscw 	 * (This is always true for locally-generated interrupts, but only
29508bde987Sscw 	 * needs doing once for each VMEbus interrupt level which is hooked)
29608bde987Sscw 	 */
29708bde987Sscw #if NVMETWO > 0
29808bde987Sscw 	if (first) {
29908bde987Sscw 		if (evcnt)
30008bde987Sscw 			evcnt_attach_dynamic(evcnt, EVCNT_TYPE_INTR,
30108bde987Sscw 			    (*sc->sc_isrevcnt)(sc->sc_isrcookie, prior),
302cbab9cadSchs 			    device_xname(sc->sc_mvmebus.sc_dev),
30308bde987Sscw 			    mvmebus_irq_name[lvl]);
30408bde987Sscw #endif
30508bde987Sscw 		iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) +
30608bde987Sscw 		    VME2LCSR_INTERRUPT_LEVEL_BASE;
30708bde987Sscw 		ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff);
30808bde987Sscw 
30908bde987Sscw 		/* Program the specified interrupt to signal at 'prior' */
31008bde987Sscw 		reg = vme2_lcsr_read(sc, iloffset);
31108bde987Sscw 		reg &= ~(VME2_INTERRUPT_LEVEL_MASK << ilshift);
31208bde987Sscw 		reg |= (prior << ilshift);
31308bde987Sscw 		vme2_lcsr_write(sc, iloffset, reg);
31408bde987Sscw 
31508bde987Sscw 		/* Clear it */
31608bde987Sscw 		vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR,
31708bde987Sscw 		    VME2_LOCAL_INTERRUPT(bitoff));
31808bde987Sscw 
31908bde987Sscw 		/* Enable it. */
32008bde987Sscw 		reg = vme2_lcsr_read(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE);
32108bde987Sscw 		reg |= VME2_LOCAL_INTERRUPT(bitoff);
32208bde987Sscw 		vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, reg);
32308bde987Sscw #if NVMETWO > 0
32408bde987Sscw 	}
32508bde987Sscw #ifdef DIAGNOSTIC
32608bde987Sscw 	else {
32708bde987Sscw 		/* Verify the interrupt priority is the same */
32808bde987Sscw 		iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) +
32908bde987Sscw 		    VME2LCSR_INTERRUPT_LEVEL_BASE;
33008bde987Sscw 		ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff);
33108bde987Sscw 
33208bde987Sscw 		reg = vme2_lcsr_read(sc, iloffset);
33308bde987Sscw 		reg &= (VME2_INTERRUPT_LEVEL_MASK << ilshift);
33408bde987Sscw 
33508bde987Sscw 		if ((prior << ilshift) != reg)
33608bde987Sscw 			panic("vmetwo_intr_establish: priority mismatch!");
33708bde987Sscw 	}
33808bde987Sscw #endif
33908bde987Sscw #endif
34008bde987Sscw 	splx(s);
34108bde987Sscw }
34208bde987Sscw 
34308bde987Sscw void
vmetwo_intr_disestablish(void * csc,int lvl,int vec,int last,struct evcnt * evcnt)34482357f6dSdsl vmetwo_intr_disestablish(void *csc, int lvl, int vec, int last, struct evcnt *evcnt)
34508bde987Sscw {
34608bde987Sscw 	struct vmetwo_softc *sc = csc;
34708bde987Sscw 	u_int32_t reg;
34808bde987Sscw 	int iloffset, ilshift;
34908bde987Sscw 	int bitoff;
35008bde987Sscw 	int s;
35108bde987Sscw 
35208bde987Sscw 	s = splhigh();
35308bde987Sscw 
35408bde987Sscw #if NVMETWO > 0
35508bde987Sscw 	/*
35608bde987Sscw 	 * Sort out interrupts generated locally by the VMEChip2 from
35708bde987Sscw 	 * those generated by VMEbus devices...
35808bde987Sscw 	 */
35908bde987Sscw 	if (vec >= VME2_VECTOR_LOCAL_MIN && vec <= VME2_VECTOR_LOCAL_MAX) {
36008bde987Sscw #endif
36108bde987Sscw 		/*
36208bde987Sscw 		 * Interrupt enable/clear bit offset is 0x08 - 0x1f
36308bde987Sscw 		 */
36408bde987Sscw 		bitoff = vec - VME2_VECTOR_BASE;
36508bde987Sscw 		vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_hand = NULL;
36608bde987Sscw 		last = 1; /* Force the interrupt to be cleared */
36708bde987Sscw #if NVMETWO > 0
36808bde987Sscw 	} else {
36908bde987Sscw 		/*
37008bde987Sscw 		 * Interrupts originating from the VMEbus are
37108bde987Sscw 		 * controlled by an offset of 0x00 - 0x07
37208bde987Sscw 		 */
37308bde987Sscw 		bitoff = lvl - 1;
37408bde987Sscw 	}
37508bde987Sscw #endif
37608bde987Sscw 
37708bde987Sscw 	/*
37808bde987Sscw 	 * Do we need to tell the VMEChip2 to block the interrupt?
37908bde987Sscw 	 * (This is always true for locally-generated interrupts, but only
38008bde987Sscw 	 * needs doing once when the last VMEbus handler for any given level
38108bde987Sscw 	 * has been unhooked.)
38208bde987Sscw 	 */
38308bde987Sscw 	if (last) {
38408bde987Sscw 		iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) +
38508bde987Sscw 		    VME2LCSR_INTERRUPT_LEVEL_BASE;
38608bde987Sscw 		ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff);
38708bde987Sscw 
38808bde987Sscw 		/* Disable it. */
38908bde987Sscw 		reg = vme2_lcsr_read(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE);
39008bde987Sscw 		reg &= ~VME2_LOCAL_INTERRUPT(bitoff);
39108bde987Sscw 		vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, reg);
39208bde987Sscw 
39308bde987Sscw 		/* Set the interrupt's level to zero */
39408bde987Sscw 		reg = vme2_lcsr_read(sc, iloffset);
39508bde987Sscw 		reg &= ~(VME2_INTERRUPT_LEVEL_MASK << ilshift);
39608bde987Sscw 		vme2_lcsr_write(sc, iloffset, reg);
39708bde987Sscw 
39808bde987Sscw 		/* Clear it */
39908bde987Sscw 		vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR,
40008bde987Sscw 		    VME2_LOCAL_INTERRUPT(vec));
40108bde987Sscw 
40208bde987Sscw 		if (evcnt)
40308bde987Sscw 			evcnt_detach(evcnt);
40408bde987Sscw 	}
40508bde987Sscw 	/* Un-hook it */
40608bde987Sscw 	(*sc->sc_isrunlink)(sc->sc_isrcookie, vec);
40708bde987Sscw 
40808bde987Sscw 	splx(s);
40908bde987Sscw }
41008bde987Sscw 
4114b293a84Sad #ifdef notyet
41208bde987Sscw static void
vmetwo_softintr_assert(void)41308bde987Sscw vmetwo_softintr_assert(void)
41408bde987Sscw {
41508bde987Sscw 
41608bde987Sscw 	vme2_lcsr_write(vmetwo_sc, VME2LCSR_SOFTINT_SET, VME2_SOFTINT_SET(0));
41708bde987Sscw }
4184b293a84Sad #endif
419