1/* $NetBSD: pic_uic.c,v 1.9 2021/03/05 05:35:50 rin 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.9 2021/03/05 05:35:50 rin 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 68static void uic_enable_irq(struct pic_ops *, int, int); 69static void uic_disable_irq(struct pic_ops *, int); 70static int uic_get_irq(struct pic_ops *, int); 71static void uic_ack_irq(struct pic_ops *, int); 72static void uic_establish_irq(struct pic_ops *, int, int, int); 73 74struct 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 102static uint32_t 103uic403_mfdcr_intr_status(void) 104{ 105 return mfdcr(DCR_EXISR); 106} 107 108static uint32_t 109uic403_mfdcr_intr_enable(void) 110{ 111 return mfdcr(DCR_EXIER); 112} 113 114static void 115uic403_mtdcr_intr_ack(uint32_t v) 116{ 117 mtdcr(DCR_EXISR, v); 118} 119 120static void 121uic403_mtdcr_intr_enable(uint32_t v) 122{ 123 mtdcr(DCR_EXIER, v); 124} 125 126struct 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 134struct 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_name = "uic0" 145}; 146 147#else /* Generic 405/440/460 Universal Interrupt Controller */ 148 149#include <powerpc/ibm4xx/dcr4xx.h> 150 151#include "opt_uic.h" 152 153/* 405EP/405GP/405GPr/Virtex-4 */ 154 155static uint32_t 156uic0_mfdcr_intr_status(void) 157{ 158 return mfdcr(DCR_UIC0_BASE + DCR_UIC_MSR); 159} 160 161static uint32_t 162uic0_mfdcr_intr_enable(void) 163{ 164 return mfdcr(DCR_UIC0_BASE + DCR_UIC_ER); 165} 166 167static void 168uic0_mtdcr_intr_ack(uint32_t v) 169{ 170 mtdcr(DCR_UIC0_BASE + DCR_UIC_SR, v); 171} 172 173static void 174uic0_mtdcr_intr_enable(uint32_t v) 175{ 176 mtdcr(DCR_UIC0_BASE + DCR_UIC_ER, v); 177} 178 179struct uic uic0 = { 180 .uic_intr_enable = 0, 181 .uic_mf_intr_status = uic0_mfdcr_intr_status, 182 .uic_mf_intr_enable = uic0_mfdcr_intr_enable, 183 .uic_mt_intr_enable = uic0_mtdcr_intr_enable, 184 .uic_mt_intr_ack = uic0_mtdcr_intr_ack, 185}; 186 187struct pic_ops pic_uic0 = { 188 .pic_cookie = &uic0, 189 .pic_numintrs = 32, 190 .pic_enable_irq = uic_enable_irq, 191 .pic_reenable_irq = uic_enable_irq, 192 .pic_disable_irq = uic_disable_irq, 193 .pic_establish_irq = uic_establish_irq, 194 .pic_get_irq = uic_get_irq, 195 .pic_ack_irq = uic_ack_irq, 196 .pic_finish_setup = NULL, 197 .pic_name = "uic0" 198}; 199 200#ifdef MULTIUIC 201 202/* 440EP/440GP/440SP/405EX/440SPe/440GX */ 203 204static uint32_t 205uic1_mfdcr_intr_status(void) 206{ 207 return mfdcr(DCR_UIC1_BASE + DCR_UIC_MSR); 208} 209 210static uint32_t 211uic1_mfdcr_intr_enable(void) 212{ 213 return mfdcr(DCR_UIC1_BASE + DCR_UIC_ER); 214} 215 216static void 217uic1_mtdcr_intr_ack(uint32_t v) 218{ 219 mtdcr(DCR_UIC1_BASE + DCR_UIC_SR, v); 220} 221 222static void 223uic1_mtdcr_intr_enable(uint32_t v) 224{ 225 mtdcr(DCR_UIC1_BASE + DCR_UIC_ER, v); 226} 227 228extern struct pic_ops pic_uic1; 229 230static void 231uic1_finish_setup(struct pic_ops *pic) 232{ 233 intr_establish_xname(30, IST_LEVEL, IPL_HIGH, pic_handle_intr, 234 &pic_uic1, "uic1"); 235} 236 237struct uic uic1 = { 238 .uic_intr_enable = 0, 239 .uic_mf_intr_status = uic1_mfdcr_intr_status, 240 .uic_mf_intr_enable = uic1_mfdcr_intr_enable, 241 .uic_mt_intr_enable = uic1_mtdcr_intr_enable, 242 .uic_mt_intr_ack = uic1_mtdcr_intr_ack, 243}; 244 245struct pic_ops pic_uic1 = { 246 .pic_cookie = &uic1, 247 .pic_numintrs = 32, 248 .pic_enable_irq = uic_enable_irq, 249 .pic_reenable_irq = uic_enable_irq, 250 .pic_disable_irq = uic_disable_irq, 251 .pic_establish_irq = uic_establish_irq, 252 .pic_get_irq = uic_get_irq, 253 .pic_ack_irq = uic_ack_irq, 254 .pic_finish_setup = uic1_finish_setup, 255 .pic_name = "uic1" 256}; 257 258/* 440EP/440GP/440SP/405EX/440SPe */ 259 260static uint32_t 261uic2_mfdcr_intr_status(void) 262{ 263 return mfdcr(DCR_UIC2_BASE + DCR_UIC_MSR); 264} 265 266static uint32_t 267uic2_mfdcr_intr_enable(void) 268{ 269 return mfdcr(DCR_UIC2_BASE + DCR_UIC_ER); 270} 271 272static void 273uic2_mtdcr_intr_ack(uint32_t v) 274{ 275 mtdcr(DCR_UIC2_BASE + DCR_UIC_SR, v); 276} 277 278static void 279uic2_mtdcr_intr_enable(uint32_t v) 280{ 281 mtdcr(DCR_UIC2_BASE + DCR_UIC_ER, v); 282} 283 284extern struct pic_ops pic_uic2; 285 286static void 287uic2_finish_setup(struct pic_ops *pic) 288{ 289 intr_establish_xname(28, IST_LEVEL, IPL_HIGH, pic_handle_intr, 290 &pic_uic2, "uic2"); 291} 292 293static struct uic uic2 = { 294 .uic_intr_enable = 0, 295 .uic_mf_intr_status = uic2_mfdcr_intr_status, 296 .uic_mf_intr_enable = uic2_mfdcr_intr_enable, 297 .uic_mt_intr_enable = uic2_mtdcr_intr_enable, 298 .uic_mt_intr_ack = uic2_mtdcr_intr_ack, 299}; 300 301struct pic_ops pic_uic2 = { 302 .pic_cookie = &uic2, 303 .pic_numintrs = 32, 304 .pic_enable_irq = uic_enable_irq, 305 .pic_reenable_irq = uic_enable_irq, 306 .pic_disable_irq = uic_disable_irq, 307 .pic_establish_irq = uic_establish_irq, 308 .pic_get_irq = uic_get_irq, 309 .pic_ack_irq = uic_ack_irq, 310 .pic_finish_setup = uic2_finish_setup, 311 .pic_name = "uic2" 312}; 313 314#endif /* MULTIUIC */ 315#endif /* !PPC_IBM403 */ 316 317/* 318 * Set up interrupt mapping array. 319 */ 320void 321intr_init(void) 322{ 323#ifdef PPC_IBM403 324 struct pic_ops * const pic = &pic_uic403; 325#else 326 struct pic_ops * const pic = &pic_uic0; 327#endif 328 struct uic * const uic = pic->pic_cookie; 329 330 uic->uic_mt_intr_enable(0x00000000); /* mask all */ 331 uic->uic_mt_intr_ack(0xffffffff); /* acknowledge all */ 332 333 pic_add(pic); 334} 335 336static void 337uic_disable_irq(struct pic_ops *pic, int irq) 338{ 339 struct uic * const uic = pic->pic_cookie; 340 const uint32_t irqmask = IRQ_TO_MASK(irq); 341 if ((uic->uic_intr_enable & irqmask) == 0) 342 return; 343 uic->uic_intr_enable ^= irqmask; 344 (*uic->uic_mt_intr_enable)(uic->uic_intr_enable); 345#ifdef IRQ_DEBUG 346 printf("%s: %s: irq=%d, mask=%08x\n", __func__, 347 pic->pic_name, irq, irqmask); 348#endif 349} 350 351static void 352uic_enable_irq(struct pic_ops *pic, int irq, int type) 353{ 354 struct uic * const uic = pic->pic_cookie; 355 const uint32_t irqmask = IRQ_TO_MASK(irq); 356 if ((uic->uic_intr_enable & irqmask) != 0) 357 return; 358 uic->uic_intr_enable ^= irqmask; 359 (*uic->uic_mt_intr_enable)(uic->uic_intr_enable); 360#ifdef IRQ_DEBUG 361 printf("%s: %s: irq=%d, mask=%08x\n", __func__, 362 pic->pic_name, irq, irqmask); 363#endif 364} 365 366static void 367uic_ack_irq(struct pic_ops *pic, int irq) 368{ 369 struct uic * const uic = pic->pic_cookie; 370 const uint32_t irqmask = IRQ_TO_MASK(irq); 371 372#ifdef PPC_IBM403 373 uic->uic_intr_status &= ~irqmask; 374#endif 375 376 (*uic->uic_mt_intr_ack)(irqmask); 377} 378 379static int 380uic_get_irq(struct pic_ops *pic, int req) 381{ 382 struct uic * const uic = pic->pic_cookie; 383 384#ifdef PPC_IBM403 385 if (req == PIC_GET_IRQ) 386 uic->uic_intr_status = (*uic->uic_mf_intr_status)(); 387 const uint32_t irqmask = uic->uic_intr_status; 388#else 389 const uint32_t irqmask = (*uic->uic_mf_intr_status)(); 390#endif 391 392 if (irqmask == 0) 393 return 255; 394 return IRQ_OF_MASK(irqmask); 395} 396 397/* 398 * Register an interrupt handler. 399 */ 400static void 401uic_establish_irq(struct pic_ops *pic, int irq, int type, int ipl) 402{ 403} 404