1 /* $OpenBSD: macintr.c,v 1.55 2019/09/03 17:51:52 deraadt Exp $ */ 2 3 /*- 4 * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org> 5 * Copyright (c) 1995 Per Fogelstrom 6 * Copyright (c) 1993, 1994 Charles M. Hannum. 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * William Jolitz and Don Ahn. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * @(#)isa.c 7.2 (Berkeley) 5/12/91 38 */ 39 40 #include <sys/param.h> 41 #include <sys/device.h> 42 #include <sys/systm.h> 43 #include <sys/malloc.h> 44 45 #include <uvm/uvm_extern.h> 46 #include <ddb/db_var.h> 47 48 #include <machine/atomic.h> 49 #include <machine/autoconf.h> 50 #include <machine/intr.h> 51 #include <machine/psl.h> 52 #include <machine/pio.h> 53 54 #include <dev/ofw/openfirm.h> 55 56 #define ICU_LEN 64 57 #define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN)) 58 59 int macintr_ienable_l[IPL_NUM], macintr_ienable_h[IPL_NUM]; 60 int macintr_pri_share[IPL_NUM]; 61 62 struct intrq macintr_handler[ICU_LEN]; 63 64 void macintr_calc_mask(void); 65 void macintr_eoi(int irq); 66 int macintr_read_irq(void); 67 68 extern u_int32_t *heathrow_FCR; 69 70 #define INT_STATE_REG0 (interrupt_reg + 0x20) 71 #define INT_ENABLE_REG0 (interrupt_reg + 0x24) 72 #define INT_CLEAR_REG0 (interrupt_reg + 0x28) 73 #define INT_LEVEL_REG0 (interrupt_reg + 0x2c) 74 #define INT_STATE_REG1 (INT_STATE_REG0 - 0x10) 75 #define INT_ENABLE_REG1 (INT_ENABLE_REG0 - 0x10) 76 #define INT_CLEAR_REG1 (INT_CLEAR_REG0 - 0x10) 77 #define INT_LEVEL_REG1 (INT_LEVEL_REG0 - 0x10) 78 79 struct macintr_softc { 80 struct device sc_dev; 81 }; 82 83 int macintr_match(struct device *parent, void *cf, void *aux); 84 void macintr_attach(struct device *, struct device *, void *); 85 void mac_ext_intr(void); 86 void macintr_collect_preconf_intr(void); 87 void macintr_setipl(int ipl); 88 89 struct cfattach macintr_ca = { 90 sizeof(struct macintr_softc), 91 macintr_match, 92 macintr_attach 93 }; 94 95 struct cfdriver macintr_cd = { 96 NULL, "macintr", DV_DULL 97 }; 98 99 int 100 macintr_match(struct device *parent, void *cf, void *aux) 101 { 102 struct confargs *ca = aux; 103 char type[40]; 104 105 /* 106 * Match entry according to "present" openfirmware entry. 107 */ 108 if (strcmp(ca->ca_name, "interrupt-controller") == 0 ) { 109 OF_getprop(ca->ca_node, "device_type", type, sizeof(type)); 110 if (strcmp(type, "interrupt-controller") == 0) 111 return 1; 112 } 113 114 /* 115 * Check name for legacy interrupt controller, this is 116 * faked to allow old firmware which does not have an entry 117 * to attach to this device. 118 */ 119 if (strcmp(ca->ca_name, "legacy-interrupt-controller") == 0 ) 120 return 1; 121 return 0; 122 } 123 124 u_int8_t *interrupt_reg; 125 typedef void (void_f) (void); 126 int macintr_prog_button (void *arg); 127 128 intr_establish_t macintr_establish; 129 intr_disestablish_t macintr_disestablish; 130 131 ppc_splraise_t macintr_splraise; 132 ppc_spllower_t macintr_spllower; 133 ppc_splx_t macintr_splx; 134 135 136 int 137 macintr_splraise(int newcpl) 138 { 139 struct cpu_info *ci = curcpu(); 140 int ocpl = ci->ci_cpl; 141 int s; 142 143 newcpl = macintr_pri_share[newcpl]; 144 if (ocpl > newcpl) 145 newcpl = ocpl; 146 147 s = ppc_intr_disable(); 148 macintr_setipl(newcpl); 149 ppc_intr_enable(s); 150 151 return ocpl; 152 } 153 154 int 155 macintr_spllower(int newcpl) 156 { 157 struct cpu_info *ci = curcpu(); 158 int ocpl = ci->ci_cpl; 159 160 macintr_splx(newcpl); 161 162 return ocpl; 163 } 164 165 void 166 macintr_splx(int newcpl) 167 { 168 struct cpu_info *ci = curcpu(); 169 int intr, s; 170 171 intr = ppc_intr_disable(); 172 macintr_setipl(newcpl); 173 if ((newcpl < IPL_SOFTTTY && ci->ci_ipending & ppc_smask[newcpl])) { 174 s = splsofttty(); 175 dosoftint(newcpl); 176 macintr_setipl(s); /* no-overhead splx */ 177 } 178 ppc_intr_enable(intr); 179 } 180 181 void 182 macintr_attach(struct device *parent, struct device *self, void *aux) 183 { 184 struct cpu_info *ci = curcpu(); 185 struct confargs *ca = aux; 186 extern intr_establish_t *intr_establish_func; 187 extern intr_disestablish_t *intr_disestablish_func; 188 struct intrq *iq; 189 int i; 190 191 interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */ 192 193 for (i = 0; i < ICU_LEN; i++) { 194 iq = &macintr_handler[i]; 195 TAILQ_INIT(&iq->iq_list); 196 } 197 ppc_smask_init(); 198 199 install_extint(mac_ext_intr); 200 intr_establish_func = macintr_establish; 201 intr_disestablish_func = macintr_disestablish; 202 203 ppc_intr_func.raise = macintr_splraise; 204 ppc_intr_func.lower = macintr_spllower; 205 ppc_intr_func.x = macintr_splx; 206 207 ci->ci_flags = 0; 208 209 macintr_collect_preconf_intr(); 210 211 mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH, 212 macintr_prog_button, (void *)0x14, "progbutton"); 213 214 ppc_intr_enable(1); 215 printf("\n"); 216 } 217 218 void 219 macintr_collect_preconf_intr(void) 220 { 221 int i; 222 for (i = 0; i < ppc_configed_intr_cnt; i++) { 223 #ifdef DEBUG 224 printf("\n\t%s irq %d level %d fun %p arg %p", 225 ppc_configed_intr[i].ih_what, 226 ppc_configed_intr[i].ih_irq, 227 ppc_configed_intr[i].ih_level, 228 ppc_configed_intr[i].ih_fun, 229 ppc_configed_intr[i].ih_arg 230 ); 231 #endif 232 macintr_establish(NULL, 233 ppc_configed_intr[i].ih_irq, 234 IST_LEVEL, 235 ppc_configed_intr[i].ih_level, 236 ppc_configed_intr[i].ih_fun, 237 ppc_configed_intr[i].ih_arg, 238 ppc_configed_intr[i].ih_what); 239 } 240 } 241 242 243 /* 244 * programmer_button function to fix args to Debugger. 245 * deal with any enables/disables, if necessary. 246 */ 247 int 248 macintr_prog_button (void *arg) 249 { 250 #ifdef DDB 251 if (db_console) 252 db_enter(); 253 #else 254 printf("programmer button pressed, debugger not available\n"); 255 #endif 256 return 1; 257 } 258 259 /* Must be called with interrupt disable. */ 260 void 261 macintr_setipl(int ipl) 262 { 263 struct cpu_info *ci = curcpu(); 264 265 ci->ci_cpl = ipl; 266 if (heathrow_FCR) 267 out32rb(INT_ENABLE_REG1, 268 macintr_ienable_h[macintr_pri_share[ipl]]); 269 270 out32rb(INT_ENABLE_REG0, macintr_ienable_l[macintr_pri_share[ipl]]); 271 } 272 273 /* 274 * Register an interrupt handler. 275 */ 276 void * 277 macintr_establish(void * lcv, int irq, int type, int level, 278 int (*ih_fun)(void *), void *ih_arg, const char *name) 279 { 280 struct cpu_info *ci = curcpu(); 281 struct intrq *iq; 282 struct intrhand *ih; 283 int s, flags; 284 285 if (!LEGAL_IRQ(irq) || type == IST_NONE) { 286 printf("%s: bogus irq %d or type %d", __func__, irq, type); 287 return (NULL); 288 } 289 290 /* no point in sleeping unless someone can free memory. */ 291 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 292 if (ih == NULL) 293 panic("intr_establish: can't malloc handler info"); 294 295 iq = &macintr_handler[irq]; 296 switch (iq->iq_ist) { 297 case IST_NONE: 298 iq->iq_ist = type; 299 break; 300 case IST_EDGE: 301 intr_shared_edge = 1; 302 /* FALLTHROUGH */ 303 case IST_LEVEL: 304 if (type == iq->iq_ist) 305 break; 306 case IST_PULSE: 307 if (type != IST_NONE) 308 panic("intr_establish: can't share %s with %s", 309 ppc_intr_typename(iq->iq_ist), 310 ppc_intr_typename(type)); 311 break; 312 } 313 314 flags = level & IPL_MPSAFE; 315 level &= ~IPL_MPSAFE; 316 317 KASSERT(level <= IPL_TTY || level >= IPL_CLOCK || flags & IPL_MPSAFE); 318 319 ih->ih_fun = ih_fun; 320 ih->ih_arg = ih_arg; 321 ih->ih_level = level; 322 ih->ih_flags = flags; 323 ih->ih_irq = irq; 324 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 325 326 /* 327 * Append handler to end of list 328 */ 329 s = ppc_intr_disable(); 330 331 TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list); 332 macintr_calc_mask(); 333 334 macintr_setipl(ci->ci_cpl); 335 ppc_intr_enable(s); 336 337 return (ih); 338 } 339 340 /* 341 * Deregister an interrupt handler. 342 */ 343 void 344 macintr_disestablish(void *lcp, void *arg) 345 { 346 struct cpu_info *ci = curcpu(); 347 struct intrhand *ih = arg; 348 int irq = ih->ih_irq; 349 int s; 350 struct intrq *iq; 351 352 if (!LEGAL_IRQ(irq)) { 353 printf("%s: bogus irq %d", __func__, irq); 354 return; 355 } 356 357 /* 358 * Remove the handler from the chain. 359 */ 360 361 iq = &macintr_handler[irq]; 362 s = ppc_intr_disable(); 363 364 TAILQ_REMOVE(&iq->iq_list, ih, ih_list); 365 macintr_calc_mask(); 366 367 macintr_setipl(ci->ci_cpl); 368 ppc_intr_enable(s); 369 370 evcount_detach(&ih->ih_count); 371 free(ih, M_DEVBUF, sizeof *ih); 372 373 if (TAILQ_EMPTY(&iq->iq_list)) 374 iq->iq_ist = IST_NONE; 375 } 376 377 /* 378 * Recalculate the interrupt masks from scratch. 379 * We could code special registry and deregistry versions of this function that 380 * would be faster, but the code would be nastier, and we don't expect this to 381 * happen very much anyway. 382 */ 383 void 384 macintr_calc_mask(void) 385 { 386 int irq; 387 struct intrhand *ih; 388 int i; 389 390 for (i = IPL_NONE; i < IPL_NUM; i++) { 391 macintr_pri_share[i] = i; 392 } 393 394 for (irq = 0; irq < ICU_LEN; irq++) { 395 int maxipl = IPL_NONE; 396 int minipl = IPL_HIGH; 397 struct intrq *iq = &macintr_handler[irq]; 398 399 TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 400 if (ih->ih_level > maxipl) 401 maxipl = ih->ih_level; 402 if (ih->ih_level < minipl) 403 minipl = ih->ih_level; 404 } 405 406 iq->iq_ipl = maxipl; 407 408 if (maxipl == IPL_NONE) { 409 minipl = IPL_NONE; /* Interrupt not enabled */ 410 } else { 411 for (i = minipl; i < maxipl; i++) 412 macintr_pri_share[i] = 413 macintr_pri_share[maxipl]; 414 } 415 416 /* Enable interrupts at lower levels */ 417 418 if (irq < 32) { 419 for (i = IPL_NONE; i < minipl; i++) 420 macintr_ienable_l[i] |= (1 << irq); 421 for (; i <= IPL_HIGH; i++) 422 macintr_ienable_l[i] &= ~(1 << irq); 423 } else { 424 for (i = IPL_NONE; i < minipl; i++) 425 macintr_ienable_h[i] |= (1 << (irq-32)); 426 for (; i <= IPL_HIGH; i++) 427 macintr_ienable_h[i] &= ~(1 << (irq-32)); 428 } 429 } 430 431 #if 0 432 for (i = 0; i < IPL_NUM; i++) 433 printf("imask[%d] %x %x\n", i, macintr_ienable_l[i], 434 macintr_ienable_h[i]); 435 #endif 436 } 437 438 /* 439 * external interrupt handler 440 */ 441 void 442 mac_ext_intr(void) 443 { 444 int irq = 0; 445 int pcpl, ret; 446 struct cpu_info *ci = curcpu(); 447 struct intrq *iq; 448 struct intrhand *ih; 449 450 pcpl = ci->ci_cpl; /* Turn off all */ 451 452 irq = macintr_read_irq(); 453 while (irq != 255) { 454 iq = &macintr_handler[irq]; 455 macintr_setipl(iq->iq_ipl); 456 457 TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 458 ppc_intr_enable(1); 459 ret = ((*ih->ih_fun)(ih->ih_arg)); 460 if (ret) { 461 ih->ih_count.ec_count++; 462 if (intr_shared_edge == 0 && ret == 1) 463 break; 464 } 465 (void)ppc_intr_disable(); 466 } 467 macintr_eoi(irq); 468 macintr_setipl(pcpl); 469 470 uvmexp.intrs++; 471 472 irq = macintr_read_irq(); 473 } 474 475 macintr_splx(pcpl); /* Process pendings. */ 476 } 477 478 void 479 macintr_eoi(int irq) 480 { 481 u_int32_t state0, state1; 482 483 if (irq < 32) { 484 state0 = 1 << irq; 485 out32rb(INT_CLEAR_REG0, state0); 486 } else { 487 if (heathrow_FCR) { /* has heathrow? */ 488 state1 = 1 << (irq - 32); 489 out32rb(INT_CLEAR_REG1, state1); 490 } 491 } 492 } 493 494 int 495 macintr_read_irq(void) 496 { 497 struct cpu_info *ci = curcpu(); 498 u_int32_t state0, state1, irq_mask; 499 int ipl, irq; 500 501 state0 = in32rb(INT_STATE_REG0); 502 503 if (heathrow_FCR) /* has heathrow? */ 504 state1 = in32rb(INT_STATE_REG1); 505 else 506 state1 = 0; 507 508 for (ipl = IPL_HIGH; ipl >= ci->ci_cpl; ipl --) { 509 irq_mask = state0 & macintr_ienable_l[ipl]; 510 if (irq_mask) { 511 irq = ffs(irq_mask) - 1; 512 return irq; 513 } 514 irq_mask = state1 & macintr_ienable_h[ipl]; 515 if (irq_mask) { 516 irq = ffs(irq_mask) + 31; 517 return irq; 518 } 519 } 520 return 255; 521 } 522