1 /* $NetBSD: pic_uic.c,v 1.11 2026/09/28 20:41:00 rkujawa Exp $ */ 2 3 /* 4 * Copyright 2002 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: pic_uic.c,v 1.11 2026/09/28 20:41:00 rkujawa Exp $"); 40 41 #ifdef _KERNEL_OPT 42 #include "opt_ppcarch.h" 43 #include "opt_uic.h" 44 #endif 45 46 #include <sys/param.h> 47 #include <sys/kernel.h> 48 #include <sys/evcnt.h> 49 #include <sys/cpu.h> 50 51 #include <machine/intr.h> 52 #include <machine/psl.h> 53 54 #include <powerpc/spr.h> 55 #include <powerpc/ibm4xx/spr.h> 56 #include <powerpc/ibm4xx/cpu.h> 57 58 #include <powerpc/pic/picvar.h> 59 60 /* 61 * Number of interrupts (hard + soft), irq number legality test, 62 * mapping of irq number to mask and a way to pick irq number 63 * off a mask of active intrs. 64 */ 65 #define IRQ_TO_MASK(irq) (0x80000000UL >> ((irq) & 0x1f)) 66 #define IRQ_OF_MASK(mask) __builtin_clz(mask) 67 68 static void uic_enable_irq(struct pic_ops *, int, int); 69 static void uic_disable_irq(struct pic_ops *, int); 70 static int uic_get_irq(struct pic_ops *, int); 71 static void uic_ack_irq(struct pic_ops *, int); 72 static void uic_establish_irq(struct pic_ops *, int, int, int); 73 74 struct uic { 75 uint32_t uic_intr_enable; /* cached intr enable mask */ 76 #ifdef PPC_IBM403 77 /* 78 * Not clearly documented in reference manual, but DCR_EXISR 79 * register is not updated immediately after some bits are 80 * cleared by mtdcr, no matter whether sync (= eieio) and/or 81 * isync are issued. 82 * 83 * Therefore, we have to manage our own status mask in the 84 * interrupt handler; see uic_{ack,get}_irq() for more details. 85 * This is what we did in obsoleted powerpc/ibm4xx/intr.c. 86 */ 87 uint32_t uic_intr_status; 88 #endif 89 uint32_t (*uic_mf_intr_status)(void); 90 uint32_t (*uic_mf_intr_enable)(void); 91 void (*uic_mt_intr_enable)(uint32_t); 92 void (*uic_mt_intr_ack)(uint32_t); 93 }; 94 95 /* 96 * Platform specific code may override any of the above. 97 */ 98 #ifdef PPC_IBM403 99 100 #include <powerpc/ibm4xx/dcr403cgx.h> 101 102 static uint32_t 103 uic403_mfdcr_intr_status(void) 104 { 105 return mfdcr(DCR_EXISR); 106 } 107 108 static uint32_t 109 uic403_mfdcr_intr_enable(void) 110 { 111 return mfdcr(DCR_EXIER); 112 } 113 114 static void 115 uic403_mtdcr_intr_ack(uint32_t v) 116 { 117 mtdcr(DCR_EXISR, v); 118 } 119 120 static void 121 uic403_mtdcr_intr_enable(uint32_t v) 122 { 123 mtdcr(DCR_EXIER, v); 124 } 125 126 struct uic uic403 = { 127 .uic_intr_enable = 0, 128 .uic_mf_intr_status = uic403_mfdcr_intr_status, 129 .uic_mf_intr_enable = uic403_mfdcr_intr_enable, 130 .uic_mt_intr_enable = uic403_mtdcr_intr_enable, 131 .uic_mt_intr_ack = uic403_mtdcr_intr_ack, 132 }; 133 134 struct pic_ops pic_uic403 = { 135 .pic_cookie = &uic403, 136 .pic_numintrs = 32, 137 .pic_enable_irq = uic_enable_irq, 138 .pic_reenable_irq = uic_enable_irq, 139 .pic_disable_irq = uic_disable_irq, 140 .pic_establish_irq = uic_establish_irq, 141 .pic_get_irq = uic_get_irq, 142 .pic_ack_irq = uic_ack_irq, 143 .pic_finish_setup = NULL, 144 .pic_flags = PIC_FLAG_LEVEL_MASK, 145 .pic_name = "uic0" 146 }; 147 148 #else /* Generic 405/440/460 Universal Interrupt Controller */ 149 150 #include <powerpc/ibm4xx/dcr4xx.h> 151 152 #include "opt_uic.h" 153 154 /* 405EP/405GP/405GPr/Virtex-4 */ 155 156 static uint32_t 157 uic0_mfdcr_intr_status(void) 158 { 159 return mfdcr(DCR_UIC0_BASE + DCR_UIC_MSR); 160 } 161 162 static uint32_t 163 uic0_mfdcr_intr_enable(void) 164 { 165 return mfdcr(DCR_UIC0_BASE + DCR_UIC_ER); 166 } 167 168 static void 169 uic0_mtdcr_intr_ack(uint32_t v) 170 { 171 mtdcr(DCR_UIC0_BASE + DCR_UIC_SR, v); 172 } 173 174 static void 175 uic0_mtdcr_intr_enable(uint32_t v) 176 { 177 mtdcr(DCR_UIC0_BASE + DCR_UIC_ER, v); 178 } 179 180 struct uic uic0 = { 181 .uic_intr_enable = 0, 182 .uic_mf_intr_status = uic0_mfdcr_intr_status, 183 .uic_mf_intr_enable = uic0_mfdcr_intr_enable, 184 .uic_mt_intr_enable = uic0_mtdcr_intr_enable, 185 .uic_mt_intr_ack = uic0_mtdcr_intr_ack, 186 }; 187 188 struct pic_ops pic_uic0 = { 189 .pic_cookie = &uic0, 190 .pic_numintrs = 32, 191 .pic_enable_irq = uic_enable_irq, 192 .pic_reenable_irq = uic_enable_irq, 193 .pic_disable_irq = uic_disable_irq, 194 .pic_establish_irq = uic_establish_irq, 195 .pic_get_irq = uic_get_irq, 196 .pic_ack_irq = uic_ack_irq, 197 .pic_finish_setup = NULL, 198 .pic_flags = PIC_FLAG_LEVEL_MASK, 199 .pic_name = "uic0" 200 }; 201 202 #ifdef MULTIUIC 203 204 /* 205 * Cascade inputs on UIC0. The defaults match the 405EX; SoCs with 206 * different wiring should override via options in the kernel config. 207 */ 208 #ifndef UIC1_CASCADE_IRQ 209 #define UIC1_CASCADE_IRQ 30 210 #endif 211 #ifndef UIC2_CASCADE_IRQ 212 #define UIC2_CASCADE_IRQ 28 213 #endif 214 215 static uint32_t 216 uic1_mfdcr_intr_status(void) 217 { 218 return mfdcr(DCR_UIC1_BASE + DCR_UIC_MSR); 219 } 220 221 static uint32_t 222 uic1_mfdcr_intr_enable(void) 223 { 224 return mfdcr(DCR_UIC1_BASE + DCR_UIC_ER); 225 } 226 227 static void 228 uic1_mtdcr_intr_ack(uint32_t v) 229 { 230 mtdcr(DCR_UIC1_BASE + DCR_UIC_SR, v); 231 } 232 233 static void 234 uic1_mtdcr_intr_enable(uint32_t v) 235 { 236 mtdcr(DCR_UIC1_BASE + DCR_UIC_ER, v); 237 } 238 239 extern struct pic_ops pic_uic1; 240 241 static void 242 uic1_finish_setup(struct pic_ops *pic) 243 { 244 intr_establish_xname(UIC1_CASCADE_IRQ, IST_LEVEL, IPL_HIGH, 245 pic_handle_intr, &pic_uic1, "uic1"); 246 } 247 248 struct uic uic1 = { 249 .uic_intr_enable = 0, 250 .uic_mf_intr_status = uic1_mfdcr_intr_status, 251 .uic_mf_intr_enable = uic1_mfdcr_intr_enable, 252 .uic_mt_intr_enable = uic1_mtdcr_intr_enable, 253 .uic_mt_intr_ack = uic1_mtdcr_intr_ack, 254 }; 255 256 struct pic_ops pic_uic1 = { 257 .pic_cookie = &uic1, 258 .pic_numintrs = 32, 259 .pic_enable_irq = uic_enable_irq, 260 .pic_reenable_irq = uic_enable_irq, 261 .pic_disable_irq = uic_disable_irq, 262 .pic_establish_irq = uic_establish_irq, 263 .pic_get_irq = uic_get_irq, 264 .pic_ack_irq = uic_ack_irq, 265 .pic_finish_setup = uic1_finish_setup, 266 .pic_flags = PIC_FLAG_LEVEL_MASK, 267 .pic_name = "uic1" 268 }; 269 270 static uint32_t 271 uic2_mfdcr_intr_status(void) 272 { 273 return mfdcr(DCR_UIC2_BASE + DCR_UIC_MSR); 274 } 275 276 static uint32_t 277 uic2_mfdcr_intr_enable(void) 278 { 279 return mfdcr(DCR_UIC2_BASE + DCR_UIC_ER); 280 } 281 282 static void 283 uic2_mtdcr_intr_ack(uint32_t v) 284 { 285 mtdcr(DCR_UIC2_BASE + DCR_UIC_SR, v); 286 } 287 288 static void 289 uic2_mtdcr_intr_enable(uint32_t v) 290 { 291 mtdcr(DCR_UIC2_BASE + DCR_UIC_ER, v); 292 } 293 294 extern struct pic_ops pic_uic2; 295 296 static void 297 uic2_finish_setup(struct pic_ops *pic) 298 { 299 intr_establish_xname(UIC2_CASCADE_IRQ, IST_LEVEL, IPL_HIGH, 300 pic_handle_intr, &pic_uic2, "uic2"); 301 } 302 303 static struct uic uic2 = { 304 .uic_intr_enable = 0, 305 .uic_mf_intr_status = uic2_mfdcr_intr_status, 306 .uic_mf_intr_enable = uic2_mfdcr_intr_enable, 307 .uic_mt_intr_enable = uic2_mtdcr_intr_enable, 308 .uic_mt_intr_ack = uic2_mtdcr_intr_ack, 309 }; 310 311 struct pic_ops pic_uic2 = { 312 .pic_cookie = &uic2, 313 .pic_numintrs = 32, 314 .pic_enable_irq = uic_enable_irq, 315 .pic_reenable_irq = uic_enable_irq, 316 .pic_disable_irq = uic_disable_irq, 317 .pic_establish_irq = uic_establish_irq, 318 .pic_get_irq = uic_get_irq, 319 .pic_ack_irq = uic_ack_irq, 320 .pic_finish_setup = uic2_finish_setup, 321 .pic_flags = PIC_FLAG_LEVEL_MASK, 322 .pic_name = "uic2" 323 }; 324 325 #ifdef UIC3_CASCADE_IRQ 326 327 static uint32_t 328 uic3_mfdcr_intr_status(void) 329 { 330 return mfdcr(DCR_UIC3_BASE + DCR_UIC_MSR); 331 } 332 333 static uint32_t 334 uic3_mfdcr_intr_enable(void) 335 { 336 return mfdcr(DCR_UIC3_BASE + DCR_UIC_ER); 337 } 338 339 static void 340 uic3_mtdcr_intr_ack(uint32_t v) 341 { 342 mtdcr(DCR_UIC3_BASE + DCR_UIC_SR, v); 343 } 344 345 static void 346 uic3_mtdcr_intr_enable(uint32_t v) 347 { 348 mtdcr(DCR_UIC3_BASE + DCR_UIC_ER, v); 349 } 350 351 extern struct pic_ops pic_uic3; 352 353 static void 354 uic3_finish_setup(struct pic_ops *pic) 355 { 356 intr_establish_xname(UIC3_CASCADE_IRQ, IST_LEVEL, IPL_HIGH, 357 pic_handle_intr, &pic_uic3, "uic3"); 358 } 359 360 static struct uic uic3 = { 361 .uic_intr_enable = 0, 362 .uic_mf_intr_status = uic3_mfdcr_intr_status, 363 .uic_mf_intr_enable = uic3_mfdcr_intr_enable, 364 .uic_mt_intr_enable = uic3_mtdcr_intr_enable, 365 .uic_mt_intr_ack = uic3_mtdcr_intr_ack, 366 }; 367 368 struct pic_ops pic_uic3 = { 369 .pic_cookie = &uic3, 370 .pic_numintrs = 32, 371 .pic_enable_irq = uic_enable_irq, 372 .pic_reenable_irq = uic_enable_irq, 373 .pic_disable_irq = uic_disable_irq, 374 .pic_establish_irq = uic_establish_irq, 375 .pic_get_irq = uic_get_irq, 376 .pic_ack_irq = uic_ack_irq, 377 .pic_finish_setup = uic3_finish_setup, 378 .pic_flags = PIC_FLAG_LEVEL_MASK, 379 .pic_name = "uic3" 380 }; 381 382 #endif /* UIC3_CASCADE_IRQ */ 383 384 #endif /* MULTIUIC */ 385 #endif /* !PPC_IBM403 */ 386 387 /* 388 * Set up interrupt mapping array. 389 */ 390 void 391 intr_init(void) 392 { 393 #ifdef PPC_IBM403 394 struct pic_ops * const pic = &pic_uic403; 395 #else 396 struct pic_ops * const pic = &pic_uic0; 397 #endif 398 struct uic * const uic = pic->pic_cookie; 399 400 uic->uic_mt_intr_enable(0x00000000); /* mask all */ 401 uic->uic_mt_intr_ack(0xffffffff); /* acknowledge all */ 402 403 pic_add(pic); 404 } 405 406 static void 407 uic_disable_irq(struct pic_ops *pic, int irq) 408 { 409 struct uic * const uic = pic->pic_cookie; 410 const uint32_t irqmask = IRQ_TO_MASK(irq); 411 if ((uic->uic_intr_enable & irqmask) == 0) 412 return; 413 uic->uic_intr_enable ^= irqmask; 414 (*uic->uic_mt_intr_enable)(uic->uic_intr_enable); 415 #ifdef IRQ_DEBUG 416 printf("%s: %s: irq=%d, mask=%08x\n", __func__, 417 pic->pic_name, irq, irqmask); 418 #endif 419 } 420 421 static void 422 uic_enable_irq(struct pic_ops *pic, int irq, int type) 423 { 424 struct uic * const uic = pic->pic_cookie; 425 const uint32_t irqmask = IRQ_TO_MASK(irq); 426 if ((uic->uic_intr_enable & irqmask) != 0) 427 return; 428 uic->uic_intr_enable ^= irqmask; 429 (*uic->uic_mt_intr_enable)(uic->uic_intr_enable); 430 #ifdef IRQ_DEBUG 431 printf("%s: %s: irq=%d, mask=%08x\n", __func__, 432 pic->pic_name, irq, irqmask); 433 #endif 434 } 435 436 static void 437 uic_ack_irq(struct pic_ops *pic, int irq) 438 { 439 struct uic * const uic = pic->pic_cookie; 440 const uint32_t irqmask = IRQ_TO_MASK(irq); 441 442 #ifdef PPC_IBM403 443 uic->uic_intr_status &= ~irqmask; 444 #endif 445 446 (*uic->uic_mt_intr_ack)(irqmask); 447 } 448 449 static int 450 uic_get_irq(struct pic_ops *pic, int req) 451 { 452 struct uic * const uic = pic->pic_cookie; 453 454 #ifdef PPC_IBM403 455 if (req == PIC_GET_IRQ) 456 uic->uic_intr_status = (*uic->uic_mf_intr_status)(); 457 const uint32_t irqmask = uic->uic_intr_status; 458 #else 459 const uint32_t irqmask = (*uic->uic_mf_intr_status)(); 460 #endif 461 462 if (irqmask == 0) 463 return 255; 464 return IRQ_OF_MASK(irqmask); 465 } 466 467 /* 468 * Register an interrupt handler. 469 */ 470 static void 471 uic_establish_irq(struct pic_ops *pic, int irq, int type, int ipl) 472 { 473 } 474