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