1 /* $OpenBSD: macintr.c,v 1.57 2022/07/24 00:28:09 cheloha 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 const 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 (ci->ci_dec_deferred && newcpl < IPL_CLOCK) { 174 ppc_mtdec(0); 175 ppc_mtdec(UINT32_MAX); /* raise DEC exception */ 176 } 177 if ((newcpl < IPL_SOFTTTY && ci->ci_ipending & ppc_smask[newcpl])) { 178 s = splsofttty(); 179 dosoftint(newcpl); 180 macintr_setipl(s); /* no-overhead splx */ 181 } 182 ppc_intr_enable(intr); 183 } 184 185 void 186 macintr_attach(struct device *parent, struct device *self, void *aux) 187 { 188 struct cpu_info *ci = curcpu(); 189 struct confargs *ca = aux; 190 extern intr_establish_t *intr_establish_func; 191 extern intr_disestablish_t *intr_disestablish_func; 192 struct intrq *iq; 193 int i; 194 195 interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */ 196 197 for (i = 0; i < ICU_LEN; i++) { 198 iq = &macintr_handler[i]; 199 TAILQ_INIT(&iq->iq_list); 200 } 201 ppc_smask_init(); 202 203 install_extint(mac_ext_intr); 204 intr_establish_func = macintr_establish; 205 intr_disestablish_func = macintr_disestablish; 206 207 ppc_intr_func.raise = macintr_splraise; 208 ppc_intr_func.lower = macintr_spllower; 209 ppc_intr_func.x = macintr_splx; 210 211 ci->ci_flags = 0; 212 213 macintr_collect_preconf_intr(); 214 215 mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH, 216 macintr_prog_button, (void *)0x14, "progbutton"); 217 218 ppc_intr_enable(1); 219 printf("\n"); 220 } 221 222 void 223 macintr_collect_preconf_intr(void) 224 { 225 int i; 226 for (i = 0; i < ppc_configed_intr_cnt; i++) { 227 #ifdef DEBUG 228 printf("\n\t%s irq %d level %d fun %p arg %p", 229 ppc_configed_intr[i].ih_what, 230 ppc_configed_intr[i].ih_irq, 231 ppc_configed_intr[i].ih_level, 232 ppc_configed_intr[i].ih_fun, 233 ppc_configed_intr[i].ih_arg 234 ); 235 #endif 236 macintr_establish(NULL, 237 ppc_configed_intr[i].ih_irq, 238 IST_LEVEL, 239 ppc_configed_intr[i].ih_level, 240 ppc_configed_intr[i].ih_fun, 241 ppc_configed_intr[i].ih_arg, 242 ppc_configed_intr[i].ih_what); 243 } 244 } 245 246 247 /* 248 * programmer_button function to fix args to Debugger. 249 * deal with any enables/disables, if necessary. 250 */ 251 int 252 macintr_prog_button (void *arg) 253 { 254 #ifdef DDB 255 if (db_console) 256 db_enter(); 257 #else 258 printf("programmer button pressed, debugger not available\n"); 259 #endif 260 return 1; 261 } 262 263 /* Must be called with interrupt disable. */ 264 void 265 macintr_setipl(int ipl) 266 { 267 struct cpu_info *ci = curcpu(); 268 269 ci->ci_cpl = ipl; 270 if (heathrow_FCR) 271 out32rb(INT_ENABLE_REG1, 272 macintr_ienable_h[macintr_pri_share[ipl]]); 273 274 out32rb(INT_ENABLE_REG0, macintr_ienable_l[macintr_pri_share[ipl]]); 275 } 276 277 /* 278 * Register an interrupt handler. 279 */ 280 void * 281 macintr_establish(void * lcv, int irq, int type, int level, 282 int (*ih_fun)(void *), void *ih_arg, const char *name) 283 { 284 struct cpu_info *ci = curcpu(); 285 struct intrq *iq; 286 struct intrhand *ih; 287 int s, flags; 288 289 if (!LEGAL_IRQ(irq) || type == IST_NONE) { 290 printf("%s: bogus irq %d or type %d", __func__, irq, type); 291 return (NULL); 292 } 293 294 /* no point in sleeping unless someone can free memory. */ 295 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 296 if (ih == NULL) 297 panic("intr_establish: can't malloc handler info"); 298 299 iq = &macintr_handler[irq]; 300 switch (iq->iq_ist) { 301 case IST_NONE: 302 iq->iq_ist = type; 303 break; 304 case IST_EDGE: 305 intr_shared_edge = 1; 306 /* FALLTHROUGH */ 307 case IST_LEVEL: 308 if (type == iq->iq_ist) 309 break; 310 case IST_PULSE: 311 if (type != IST_NONE) 312 panic("intr_establish: can't share %s with %s", 313 ppc_intr_typename(iq->iq_ist), 314 ppc_intr_typename(type)); 315 break; 316 } 317 318 flags = level & IPL_MPSAFE; 319 level &= ~IPL_MPSAFE; 320 321 KASSERT(level <= IPL_TTY || level >= IPL_CLOCK || flags & IPL_MPSAFE); 322 323 ih->ih_fun = ih_fun; 324 ih->ih_arg = ih_arg; 325 ih->ih_level = level; 326 ih->ih_flags = flags; 327 ih->ih_irq = irq; 328 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 329 330 /* 331 * Append handler to end of list 332 */ 333 s = ppc_intr_disable(); 334 335 TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list); 336 macintr_calc_mask(); 337 338 macintr_setipl(ci->ci_cpl); 339 ppc_intr_enable(s); 340 341 return (ih); 342 } 343 344 /* 345 * Deregister an interrupt handler. 346 */ 347 void 348 macintr_disestablish(void *lcp, void *arg) 349 { 350 struct cpu_info *ci = curcpu(); 351 struct intrhand *ih = arg; 352 int irq = ih->ih_irq; 353 int s; 354 struct intrq *iq; 355 356 if (!LEGAL_IRQ(irq)) { 357 printf("%s: bogus irq %d", __func__, irq); 358 return; 359 } 360 361 /* 362 * Remove the handler from the chain. 363 */ 364 365 iq = &macintr_handler[irq]; 366 s = ppc_intr_disable(); 367 368 TAILQ_REMOVE(&iq->iq_list, ih, ih_list); 369 macintr_calc_mask(); 370 371 macintr_setipl(ci->ci_cpl); 372 ppc_intr_enable(s); 373 374 evcount_detach(&ih->ih_count); 375 free(ih, M_DEVBUF, sizeof *ih); 376 377 if (TAILQ_EMPTY(&iq->iq_list)) 378 iq->iq_ist = IST_NONE; 379 } 380 381 /* 382 * Recalculate the interrupt masks from scratch. 383 * We could code special registry and deregistry versions of this function that 384 * would be faster, but the code would be nastier, and we don't expect this to 385 * happen very much anyway. 386 */ 387 void 388 macintr_calc_mask(void) 389 { 390 int irq; 391 struct intrhand *ih; 392 int i; 393 394 for (i = IPL_NONE; i < IPL_NUM; i++) { 395 macintr_pri_share[i] = i; 396 } 397 398 for (irq = 0; irq < ICU_LEN; irq++) { 399 int maxipl = IPL_NONE; 400 int minipl = IPL_HIGH; 401 struct intrq *iq = &macintr_handler[irq]; 402 403 TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 404 if (ih->ih_level > maxipl) 405 maxipl = ih->ih_level; 406 if (ih->ih_level < minipl) 407 minipl = ih->ih_level; 408 } 409 410 iq->iq_ipl = maxipl; 411 412 if (maxipl == IPL_NONE) { 413 minipl = IPL_NONE; /* Interrupt not enabled */ 414 } else { 415 for (i = minipl; i < maxipl; i++) 416 macintr_pri_share[i] = 417 macintr_pri_share[maxipl]; 418 } 419 420 /* Enable interrupts at lower levels */ 421 422 if (irq < 32) { 423 for (i = IPL_NONE; i < minipl; i++) 424 macintr_ienable_l[i] |= (1 << irq); 425 for (; i <= IPL_HIGH; i++) 426 macintr_ienable_l[i] &= ~(1 << irq); 427 } else { 428 for (i = IPL_NONE; i < minipl; i++) 429 macintr_ienable_h[i] |= (1 << (irq-32)); 430 for (; i <= IPL_HIGH; i++) 431 macintr_ienable_h[i] &= ~(1 << (irq-32)); 432 } 433 } 434 435 #if 0 436 for (i = 0; i < IPL_NUM; i++) 437 printf("imask[%d] %x %x\n", i, macintr_ienable_l[i], 438 macintr_ienable_h[i]); 439 #endif 440 } 441 442 /* 443 * external interrupt handler 444 */ 445 void 446 mac_ext_intr(void) 447 { 448 int irq = 0; 449 int pcpl, ret; 450 struct cpu_info *ci = curcpu(); 451 struct intrq *iq; 452 struct intrhand *ih; 453 454 pcpl = ci->ci_cpl; /* Turn off all */ 455 456 irq = macintr_read_irq(); 457 while (irq != 255) { 458 iq = &macintr_handler[irq]; 459 macintr_setipl(iq->iq_ipl); 460 461 TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { 462 ppc_intr_enable(1); 463 ret = ((*ih->ih_fun)(ih->ih_arg)); 464 if (ret) { 465 ih->ih_count.ec_count++; 466 if (intr_shared_edge == 0 && ret == 1) 467 break; 468 } 469 (void)ppc_intr_disable(); 470 } 471 macintr_eoi(irq); 472 macintr_setipl(pcpl); 473 474 uvmexp.intrs++; 475 476 irq = macintr_read_irq(); 477 } 478 479 macintr_splx(pcpl); /* Process pendings. */ 480 } 481 482 void 483 macintr_eoi(int irq) 484 { 485 u_int32_t state0, state1; 486 487 if (irq < 32) { 488 state0 = 1 << irq; 489 out32rb(INT_CLEAR_REG0, state0); 490 } else { 491 if (heathrow_FCR) { /* has heathrow? */ 492 state1 = 1 << (irq - 32); 493 out32rb(INT_CLEAR_REG1, state1); 494 } 495 } 496 } 497 498 int 499 macintr_read_irq(void) 500 { 501 struct cpu_info *ci = curcpu(); 502 u_int32_t state0, state1, irq_mask; 503 int ipl, irq; 504 505 state0 = in32rb(INT_STATE_REG0); 506 507 if (heathrow_FCR) /* has heathrow? */ 508 state1 = in32rb(INT_STATE_REG1); 509 else 510 state1 = 0; 511 512 for (ipl = IPL_HIGH; ipl >= ci->ci_cpl; ipl --) { 513 irq_mask = state0 & macintr_ienable_l[ipl]; 514 if (irq_mask) { 515 irq = ffs(irq_mask) - 1; 516 return irq; 517 } 518 irq_mask = state1 & macintr_ienable_h[ipl]; 519 if (irq_mask) { 520 irq = ffs(irq_mask) + 31; 521 return irq; 522 } 523 } 524 return 255; 525 } 526