1/* 2 * Copyright 2006, Broadcom Corporation 3 * All Rights Reserved. 4 * 5 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY 6 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM 7 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS 8 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. 9 * 10 * Broadcom HND BCM47xx chips interrupt dispatcher. 11 * Derived from ../generic/irq.c 12 * 13 * MIPS IRQ Source 14 * -------- ------------------ 15 * 0 Software 16 * 1 Software 17 * 2 Hardware (shared) 18 * 3 Hardware 19 * 4 Hardware 20 * 5 Hardware 21 * 6 Hardware 22 * 7 Hardware (r4k timer) 23 * 24 * MIPS IRQ Linux IRQ 25 * -------- ----------- 26 * 0 - 1 0 - 1 27 * 2 8 and above 28 * 3 - 7 3 - 7 29 * 30 * MIPS has 8 IRQs as indicated and assigned above. SB cores 31 * that use dedicated MIPS IRQ3 to IRQ6 are 1-to-1 mapped to 32 * linux IRQ3 to IRQ6. SB cores sharing MIPS IRQ2 are mapped 33 * to linux IRQ8 and above as virtual IRQs using the following 34 * mapping: 35 * 36 * <linux-IRQ#> = <SB-core-flag> + <base-IRQ> + 2 37 * 38 * where <base-IRQ> is specified in setup.c when calling 39 * sb_mips_init(), 2 is to offset the two software IRQs. 40 * 41 * $Id: irq.c,v 1.1.1.1 2008/10/15 03:26:06 james26_jang Exp $ 42 */ 43 44#include <linux/config.h> 45 46#ifdef CONFIG_SMP 47/* 48 * This code is designed to work on Uniprocessor only. 49 * 50 * To support SMP we must know: 51 * - interrupt architecture 52 * - interrupt distribution machenism 53 */ 54#error "This implementation does not support SMP" 55#endif 56 57#include <linux/init.h> 58#include <linux/irq.h> 59#include <linux/kernel_stat.h> 60 61#include <asm/mipsregs.h> 62 63#include <typedefs.h> 64#include <osl.h> 65#include <bcmutils.h> 66#include <sbconfig.h> 67#include <sbutils.h> 68#include <hndcpu.h> 69#include "bcm947xx.h" 70 71/* cp0 SR register IM field */ 72#define SR_IM(irq) (1 << ((irq) + STATUSB_IP0)) 73 74/* cp0 CR register IP field */ 75#define CR_IP(irq) (1 << ((irq) + CAUSEB_IP0)) 76 77/* other local constants */ 78#define NUM_IRQS 32 79 80/* local variables and functions */ 81static sbconfig_t *ccsbr = NULL; /* Chipc core SB config regs */ 82static sbconfig_t *mipssbr = NULL; /* MIPS core SB config regs */ 83static int mipsirq = -1; /* MIPS core virtual IRQ number */ 84static uint32 sbintvec = 0; /* MIPS core sbintvec reg val */ 85static int irq2en = 0; /* MIPS IRQ2 enable count */ 86 87/* global variables and functions */ 88extern sb_t *bcm947xx_sbh; /* defined in setup.c */ 89 90extern asmlinkage void brcmIRQ(void); 91 92/* Control functions for MIPS IRQ0 to IRQ7 */ 93static INLINE void 94enable_brcm_irq(unsigned int irq) 95{ 96 set_c0_status(SR_IM(irq)); 97} 98 99static INLINE void 100disable_brcm_irq(unsigned int irq) 101{ 102 clear_c0_status(SR_IM(irq)); 103} 104 105static unsigned int 106startup_brcm_irq(unsigned int irq) 107{ 108 enable_brcm_irq(irq); 109 return 0; 110} 111 112static void 113shutdown_brcm_irq(unsigned int irq) 114{ 115 disable_brcm_irq(irq); 116} 117 118static void 119ack_brcm_irq(unsigned int irq) 120{ 121 /* Done in brcm_irq_dispatch()! */ 122} 123 124static void 125end_brcm_irq(unsigned int irq) 126{ 127 /* Done in brcm_irq_dispatch()! */ 128} 129 130/* Control functions for linux IRQ8 and above */ 131static INLINE void 132enable_brcm_irq2(unsigned int irq) 133{ 134 ASSERT(irq2en >= 0); 135 if (irq2en++) 136 return; 137 enable_brcm_irq(2); 138} 139 140static INLINE void 141disable_brcm_irq2(unsigned int irq) 142{ 143 ASSERT(irq2en > 0); 144 if (--irq2en) 145 return; 146 disable_brcm_irq(2); 147} 148 149static unsigned int 150startup_brcm_irq2(unsigned int irq) 151{ 152 enable_brcm_irq2(irq); 153 return 0; 154} 155 156static void 157shutdown_brcm_irq2(unsigned int irq) 158{ 159 disable_brcm_irq2(irq); 160} 161 162static void 163ack_brcm_irq2(unsigned int irq) 164{ 165 /* Already done in brcm_irq_dispatch()! */ 166} 167 168static void 169end_brcm_irq2(unsigned int irq) 170{ 171 /* Already done in brcm_irq_dispatch()! */ 172} 173 174/* 175 * Route interrupts to ISR(s). 176 * 177 * This function is entered with the IE disabled. It can be 178 * re-entered as soon as the IE is re-enabled in function 179 * handle_IRQ_envet(). 180 */ 181void 182brcm_irq_dispatch(struct pt_regs *regs) 183{ 184 u32 pending, ipvec; 185 uint32 sbflagst = 0; 186 struct irqaction *action; 187 int cpu = smp_processor_id(); 188 int irq; 189 190 /* Disable MIPS IRQs with pending interrupts */ 191 pending = regs->cp0_cause & CAUSEF_IP; 192 pending &= regs->cp0_status; 193 clear_c0_status(pending); 194 195 /* 196 * Build bitvec for pending interrupts. Start with 197 * MIPS IRQ2 and add linux IRQs to higher bits to 198 * make the interrupt processing uniform. 199 */ 200 ipvec = pending >> CAUSEB_IP2; 201 if (pending & CAUSEF_IP2) { 202 if (ccsbr && mipssbr) { 203 /* Make sure no one has changed IRQ assignments */ 204 ASSERT(R_REG(NULL, &mipssbr->sbintvec) == sbintvec); 205 sbflagst = R_REG(NULL, &ccsbr->sbflagst); 206 sbflagst &= sbintvec; 207 ipvec += sbflagst << SBMIPS_VIRTIRQ_BASE; 208 } 209 } 210 211 /* 212 * Handle MIPS timer interrupt. Re-enable MIPS IRQ7 213 * immediately after servicing the interrupt so that 214 * we can take this kind of interrupt again later 215 * while servicing other interrupts. 216 */ 217 if (pending & CAUSEF_IP7) { 218 kstat.irqs[cpu][7]++; 219 action = irq_desc[7].action; 220 if (action) 221 handle_IRQ_event(7, regs, action); 222 ipvec &= ~(1 << 5); 223 pending &= ~CAUSEF_IP7; 224 set_c0_status(STATUSF_IP7); 225 } 226 227#ifdef CONFIG_HND_BMIPS3300_PROF 228 /* 229 * Handle MIPS core interrupt. Re-enable the MIPS IRQ that 230 * MIPS core is assigned to immediately after servicing the 231 * interrupt so that we can take this kind of interrupt again 232 * later while servicing other interrupts. 233 * 234 * mipsirq < 0 indicates MIPS core IRQ # is unknown. 235 */ 236 if (mipsirq >= 0 && (ipvec & (1 << mipsirq))) { 237 /* 238 * MIPS core raised the interrupt on the shared MIPS IRQ2. 239 * Make sure MIPS core is the only interrupt source before 240 * re-enabling the IRQ. 241 */ 242 if (mipsirq >= SBMIPS_VIRTIRQ_BASE) { 243 if (sbflagst == (1 << (mipsirq-SBMIPS_VIRTIRQ_BASE))) { 244 irq = mipsirq + 2; 245 kstat.irqs[cpu][irq]++; 246 action = irq_desc[irq].action; 247 if (action) 248 handle_IRQ_event(irq, regs, action); 249 ipvec &= ~(1 << mipsirq); 250 pending &= ~CAUSEF_IP2; 251 set_c0_status(STATUSF_IP2); 252 } 253 } 254 /* 255 * MIPS core raised the interrupt on a dedicated MIPS IRQ. 256 * Re-enable the IRQ immediately. 257 */ 258 else { 259 irq = mipsirq + 2; 260 kstat.irqs[cpu][irq]++; 261 action = irq_desc[irq].action; 262 if (action) 263 handle_IRQ_event(irq, regs, action); 264 ipvec &= ~(1 << mipsirq); 265 pending &= ~CR_IP(irq); 266 set_c0_status(SR_IM(irq)); 267 } 268 } 269#endif /* CONFIG_HND_BMIPS3300_PROF */ 270 271 /* 272 * Handle all other interrupts. Re-enable disabled MIPS IRQs 273 * after processing all pending interrupts. 274 */ 275 for (irq = 2; irq < NUM_IRQS && ipvec != 0; irq ++) { 276 if (ipvec & 1) { 277 kstat.irqs[cpu][irq]++; 278 if ((action = irq_desc[irq].action)) 279 handle_IRQ_event(irq, regs, action); 280 } 281 ipvec >>= 1; 282 } 283 set_c0_status(pending); 284 285 /* Run pending bottom halves */ 286 if (softirq_pending(cpu)) 287 do_softirq(); 288} 289 290/* MIPS IRQ0 to IRQ7 interrupt controller */ 291static struct hw_interrupt_type brcm_irq_type = { 292 typename: "MIPS", 293 startup: startup_brcm_irq, 294 shutdown: shutdown_brcm_irq, 295 enable: enable_brcm_irq, 296 disable: disable_brcm_irq, 297 ack: ack_brcm_irq, 298 end: end_brcm_irq, 299 NULL 300}; 301 302/* linux IRQ8 and above interrupt controller */ 303static struct hw_interrupt_type brcm_irq2_type = { 304 typename: "IRQ2", 305 startup: startup_brcm_irq2, 306 shutdown: shutdown_brcm_irq2, 307 enable: enable_brcm_irq2, 308 disable: disable_brcm_irq2, 309 ack: ack_brcm_irq2, 310 end: end_brcm_irq2, 311 NULL 312}; 313 314/* 315 * We utilize chipcommon configuration register SBFlagSt to implement a 316 * smart shared IRQ handling machenism through which only ISRs registered 317 * for the SB cores that raised the interrupt are invoked. This machenism 318 * relies on the SBFlagSt register's reliable recording of the SB cores 319 * that raised the interrupt. 320 */ 321void __init 322init_IRQ(void) 323{ 324 int i; 325 uint32 coreidx; 326 void *regs; 327 328 /* Cache chipc and mips33 config registers */ 329 ASSERT(bcm947xx_sbh); 330 coreidx = sb_coreidx(bcm947xx_sbh); 331 if ((regs = sb_setcore(bcm947xx_sbh, SB_CC, 0))) 332 ccsbr = (sbconfig_t *)((ulong)regs + SBCONFIGOFF); 333 if ((regs = sb_setcore(bcm947xx_sbh, SB_MIPS33, 0))) { 334 mipssbr = (sbconfig_t *)((ulong)regs + SBCONFIGOFF); 335 mipsirq = sb_irq(bcm947xx_sbh); 336 } 337 sb_setcoreidx(bcm947xx_sbh, coreidx); 338 339 /* Cache mips33 sbintvec register */ 340 if (mipssbr) 341 sbintvec = R_REG(NULL, &mipssbr->sbintvec); 342 343 /* Install interrupt controllers */ 344 for (i = 0; i < NR_IRQS; i++) { 345 irq_desc[i].status = IRQ_DISABLED; 346 irq_desc[i].action = 0; 347 irq_desc[i].depth = 1; 348 irq_desc[i].handler = i < SBMIPS_NUMIRQS ? 349 &brcm_irq_type : 350 &brcm_irq2_type; 351 } 352 set_except_vector(0, brcmIRQ); 353} 354