1 /* $NetBSD: vme_two_isr.c,v 1.18 2024/01/19 03:59:47 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Steve C. Woodford. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Split off from vme_two.c specifically to deal with hardware assisted 34 * soft interrupts when the user hasn't specified `vmetwo0' in the 35 * kernel config file (mvme1[67]2 only). 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: vme_two_isr.c,v 1.18 2024/01/19 03:59:47 thorpej Exp $"); 40 41 #include "vmetwo.h" 42 #include "opt_mvmeconf.h" 43 44 #include <sys/param.h> 45 #include <sys/kernel.h> 46 #include <sys/systm.h> 47 #include <sys/device.h> 48 #include <sys/malloc.h> 49 #include <sys/cpu.h> 50 #include <sys/bus.h> 51 52 #include <dev/vme/vmereg.h> 53 #include <dev/vme/vmevar.h> 54 55 #include <dev/mvme/mvmebus.h> 56 #include <dev/mvme/vme_tworeg.h> 57 #include <dev/mvme/vme_twovar.h> 58 59 /* 60 * Non-zero if there is no VMEChip2 on this board. 61 */ 62 int vmetwo_not_present; 63 64 /* 65 * Array of interrupt handlers registered with us for the non-VMEbus 66 * vectored interrupts. Eg. ABORT Switch, SYSFAIL etc. 67 * 68 * We can't just install a caller's handler directly, since these 69 * interrupts have to be manually cleared, so we have a trampoline 70 * which does the clearing automatically. 71 */ 72 static struct vme_two_handler { 73 int (*isr_hand)(void *); 74 void *isr_arg; 75 } vme_two_handlers[(VME2_VECTOR_LOCAL_MAX - VME2_VECTOR_LOCAL_MIN) + 1]; 76 77 #define VMETWO_HANDLERS_SZ (sizeof(vme_two_handlers) / \ 78 sizeof(struct vme_two_handler)) 79 80 static int vmetwo_local_isr_trampoline(void *); 81 #ifdef notyet 82 static void vmetwo_softintr_assert(void); 83 #endif 84 85 static struct vmetwo_softc *vmetwo_sc; 86 87 int 88 vmetwo_probe(bus_space_tag_t bt, bus_addr_t offset) 89 { 90 bus_space_handle_t bh; 91 92 bus_space_map(bt, offset + VME2REG_LCSR_OFFSET, VME2LCSR_SIZE, 0, &bh); 93 94 if (bus_space_peek_4(bt, bh, VME2LCSR_MISC_STATUS, NULL) != 0) { 95 #if defined(MVME162) || defined(MVME172) 96 #if defined(MVME167) || defined(MVME177) 97 if (machineid == MVME_162 || machineid == MVME_172) 98 #endif 99 { 100 /* 101 * No VMEChip2 on mvme162/172 is not too big a 102 * deal; we can fall back on timer4 in the 103 * mcchip for h/w assisted soft interrupts... 104 */ 105 extern void pcctwosoftintrinit(void); 106 bus_space_unmap(bt, bh, VME2LCSR_SIZE); 107 vmetwo_not_present = 1; 108 pcctwosoftintrinit(); 109 return (0); 110 } 111 #endif 112 #if defined(MVME167) || defined(MVME177) || defined(MVME88K) 113 /* 114 * No VMEChip2 on mvme167/177, however, is a Big Deal. 115 * In fact, it means the hardware's shot since the 116 * VMEChip2 is not a `build-option' on those boards. 117 */ 118 panic("VMEChip2 not responding! Faulty board?"); 119 /* NOTREACHED */ 120 #endif 121 #if defined(MVMEPPC) 122 /* 123 * No VMEChip2 on mvmeppc is no big deal. 124 */ 125 bus_space_unmap(bt, bh, VME2LCSR_SIZE); 126 vmetwo_not_present = 1; 127 return (0); 128 #endif 129 } 130 #if NVMETWO == 0 131 else { 132 /* 133 * The kernel config file has no `vmetwo0' device, but 134 * there is a VMEChip2 on the board. Fix up things 135 * just enough to hook VMEChip2 local interrupts. 136 */ 137 struct vmetwo_softc *sc; 138 139 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK); 140 sc->sc_mvmebus.sc_bust = bt; 141 sc->sc_lcrh = bh; 142 vmetwo_intr_init(sc); 143 return 0; 144 } 145 #else 146 bus_space_unmap(bt, bh, VME2LCSR_SIZE); 147 return (1); 148 #endif 149 } 150 151 void 152 vmetwo_intr_init(struct vmetwo_softc *sc) 153 { 154 u_int32_t reg; 155 int i; 156 157 vmetwo_sc = sc; 158 159 /* Clear out the ISR handler array */ 160 for (i = 0; i < VMETWO_HANDLERS_SZ; i++) 161 vme_two_handlers[i].isr_hand = NULL; 162 163 /* 164 * Initialize the chip. 165 * Firstly, disable all VMEChip2 Interrupts 166 */ 167 reg = vme2_lcsr_read(sc, VME2LCSR_MISC_STATUS) & ~VME2_MISC_STATUS_MIEN; 168 vme2_lcsr_write(sc, VME2LCSR_MISC_STATUS, reg); 169 vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, 0); 170 vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, 171 VME2_LOCAL_INTERRUPT_CLEAR_ALL); 172 173 /* Zap all the IRQ level registers */ 174 for (i = 0; i < VME2_NUM_IL_REGS; i++) 175 vme2_lcsr_write(sc, VME2LCSR_INTERRUPT_LEVEL_BASE + (i * 4), 0); 176 177 /* Disable the tick timers */ 178 reg = vme2_lcsr_read(sc, VME2LCSR_TIMER_CONTROL); 179 reg &= ~VME2_TIMER_CONTROL_EN(0); 180 reg &= ~VME2_TIMER_CONTROL_EN(1); 181 vme2_lcsr_write(sc, VME2LCSR_TIMER_CONTROL, reg); 182 183 /* Set the VMEChip2's vector base register to the required value */ 184 reg = vme2_lcsr_read(sc, VME2LCSR_VECTOR_BASE); 185 reg &= ~VME2_VECTOR_BASE_MASK; 186 reg |= VME2_VECTOR_BASE_REG_VALUE; 187 vme2_lcsr_write(sc, VME2LCSR_VECTOR_BASE, reg); 188 189 /* Set the Master Interrupt Enable bit now */ 190 reg = vme2_lcsr_read(sc, VME2LCSR_MISC_STATUS) | VME2_MISC_STATUS_MIEN; 191 vme2_lcsr_write(sc, VME2LCSR_MISC_STATUS, reg); 192 193 /* Allow the MD code the chance to do some initialising */ 194 vmetwo_md_intr_init(sc); 195 196 #if defined(MVME167) || defined(MVME177) 197 #if defined(MVME162) || defined(MVME172) 198 if (machineid != MVME_162 && machineid != MVME_172) 199 #endif 200 { 201 /* 202 * Let the NMI handler deal with level 7 ABORT switch 203 * interrupts 204 */ 205 vmetwo_intr_establish(sc, 7, 7, VME2_VEC_ABORT, 1, 206 nmihand, NULL, NULL); 207 } 208 #endif 209 210 /* Setup hardware assisted soft interrupts */ 211 #ifdef notyet 212 vmetwo_intr_establish(sc, 1, 1, VME2_VEC_SOFT0, 1, 213 (int (*)(void *))softintr_dispatch, NULL, NULL); 214 _softintr_chipset_assert = vmetwo_softintr_assert; 215 #endif 216 } 217 218 static int 219 vmetwo_local_isr_trampoline(void *arg) 220 { 221 struct vme_two_handler *isr; 222 int vec; 223 224 vec = (int) arg; /* 0x08 <= vec <= 0x1f */ 225 226 /* Clear the interrupt source */ 227 vme2_lcsr_write(vmetwo_sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, 228 VME2_LOCAL_INTERRUPT(vec)); 229 230 isr = &vme_two_handlers[vec - VME2_VECTOR_LOCAL_OFFSET]; 231 if (isr->isr_hand) 232 (void) (*isr->isr_hand) (isr->isr_arg); 233 else 234 printf("vmetwo: Spurious local interrupt, vector 0x%x\n", vec); 235 236 return (1); 237 } 238 239 void 240 vmetwo_local_intr_establish(int pri, int vec, int (*hand)(void *), void *arg, struct evcnt *evcnt) 241 { 242 243 vmetwo_intr_establish(vmetwo_sc, pri, pri, vec, 1, hand, arg, evcnt); 244 } 245 246 /* ARGSUSED */ 247 void 248 vmetwo_intr_establish(void *csc, int prior, int lvl, int vec, int first, int (*hand)(void *), void *arg, struct evcnt *evcnt) 249 { 250 struct vmetwo_softc *sc = csc; 251 u_int32_t reg; 252 int bitoff; 253 int iloffset, ilshift; 254 int s; 255 256 s = splhigh(); 257 258 #if NVMETWO > 0 259 /* 260 * Sort out interrupts generated locally by the VMEChip2 from 261 * those generated by VMEbus devices... 262 */ 263 if (vec >= VME2_VECTOR_LOCAL_MIN && vec <= VME2_VECTOR_LOCAL_MAX) { 264 #endif 265 /* 266 * Local interrupts need to be bounced through some 267 * trampoline code which acknowledges/clears them. 268 */ 269 vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_hand = hand; 270 vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_arg = arg; 271 hand = vmetwo_local_isr_trampoline; 272 arg = (void *) (vec - VME2_VECTOR_BASE); 273 274 /* 275 * Interrupt enable/clear bit offset is 0x08 - 0x1f 276 */ 277 bitoff = vec - VME2_VECTOR_BASE; 278 #if NVMETWO > 0 279 first = 1; /* Force the interrupt to be enabled */ 280 } else { 281 /* 282 * Interrupts originating from the VMEbus are 283 * controlled by an offset of 0x00 - 0x07 284 */ 285 bitoff = lvl - 1; 286 } 287 #endif 288 289 /* Hook the interrupt */ 290 (*sc->sc_isrlink)(sc->sc_isrcookie, hand, arg, prior, vec, evcnt); 291 292 /* 293 * Do we need to tell the VMEChip2 to let the interrupt through? 294 * (This is always true for locally-generated interrupts, but only 295 * needs doing once for each VMEbus interrupt level which is hooked) 296 */ 297 #if NVMETWO > 0 298 if (first) { 299 if (evcnt) 300 evcnt_attach_dynamic(evcnt, EVCNT_TYPE_INTR, 301 (*sc->sc_isrevcnt)(sc->sc_isrcookie, prior), 302 device_xname(sc->sc_mvmebus.sc_dev), 303 mvmebus_irq_name[lvl]); 304 #endif 305 iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) + 306 VME2LCSR_INTERRUPT_LEVEL_BASE; 307 ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff); 308 309 /* Program the specified interrupt to signal at 'prior' */ 310 reg = vme2_lcsr_read(sc, iloffset); 311 reg &= ~(VME2_INTERRUPT_LEVEL_MASK << ilshift); 312 reg |= (prior << ilshift); 313 vme2_lcsr_write(sc, iloffset, reg); 314 315 /* Clear it */ 316 vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, 317 VME2_LOCAL_INTERRUPT(bitoff)); 318 319 /* Enable it. */ 320 reg = vme2_lcsr_read(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE); 321 reg |= VME2_LOCAL_INTERRUPT(bitoff); 322 vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, reg); 323 #if NVMETWO > 0 324 } 325 #ifdef DIAGNOSTIC 326 else { 327 /* Verify the interrupt priority is the same */ 328 iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) + 329 VME2LCSR_INTERRUPT_LEVEL_BASE; 330 ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff); 331 332 reg = vme2_lcsr_read(sc, iloffset); 333 reg &= (VME2_INTERRUPT_LEVEL_MASK << ilshift); 334 335 if ((prior << ilshift) != reg) 336 panic("vmetwo_intr_establish: priority mismatch!"); 337 } 338 #endif 339 #endif 340 splx(s); 341 } 342 343 void 344 vmetwo_intr_disestablish(void *csc, int lvl, int vec, int last, struct evcnt *evcnt) 345 { 346 struct vmetwo_softc *sc = csc; 347 u_int32_t reg; 348 int iloffset, ilshift; 349 int bitoff; 350 int s; 351 352 s = splhigh(); 353 354 #if NVMETWO > 0 355 /* 356 * Sort out interrupts generated locally by the VMEChip2 from 357 * those generated by VMEbus devices... 358 */ 359 if (vec >= VME2_VECTOR_LOCAL_MIN && vec <= VME2_VECTOR_LOCAL_MAX) { 360 #endif 361 /* 362 * Interrupt enable/clear bit offset is 0x08 - 0x1f 363 */ 364 bitoff = vec - VME2_VECTOR_BASE; 365 vme_two_handlers[vec - VME2_VECTOR_LOCAL_MIN].isr_hand = NULL; 366 last = 1; /* Force the interrupt to be cleared */ 367 #if NVMETWO > 0 368 } else { 369 /* 370 * Interrupts originating from the VMEbus are 371 * controlled by an offset of 0x00 - 0x07 372 */ 373 bitoff = lvl - 1; 374 } 375 #endif 376 377 /* 378 * Do we need to tell the VMEChip2 to block the interrupt? 379 * (This is always true for locally-generated interrupts, but only 380 * needs doing once when the last VMEbus handler for any given level 381 * has been unhooked.) 382 */ 383 if (last) { 384 iloffset = VME2_ILOFFSET_FROM_VECTOR(bitoff) + 385 VME2LCSR_INTERRUPT_LEVEL_BASE; 386 ilshift = VME2_ILSHIFT_FROM_VECTOR(bitoff); 387 388 /* Disable it. */ 389 reg = vme2_lcsr_read(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE); 390 reg &= ~VME2_LOCAL_INTERRUPT(bitoff); 391 vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_ENABLE, reg); 392 393 /* Set the interrupt's level to zero */ 394 reg = vme2_lcsr_read(sc, iloffset); 395 reg &= ~(VME2_INTERRUPT_LEVEL_MASK << ilshift); 396 vme2_lcsr_write(sc, iloffset, reg); 397 398 /* Clear it */ 399 vme2_lcsr_write(sc, VME2LCSR_LOCAL_INTERRUPT_CLEAR, 400 VME2_LOCAL_INTERRUPT(vec)); 401 402 if (evcnt) 403 evcnt_detach(evcnt); 404 } 405 /* Un-hook it */ 406 (*sc->sc_isrunlink)(sc->sc_isrcookie, vec); 407 408 splx(s); 409 } 410 411 #ifdef notyet 412 static void 413 vmetwo_softintr_assert(void) 414 { 415 416 vme2_lcsr_write(vmetwo_sc, VME2LCSR_SOFTINT_SET, VME2_SOFTINT_SET(0)); 417 } 418 #endif 419