2 * Copyright (C) 2009, Broadcom Corporation
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.
10 * Broadcom HND BCM47xx chips interrupt dispatcher.
11 * Derived from ../generic/irq.c
14 * -------- ------------------
22 * 7 Hardware (r4k timer)
25 * -------- -----------
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
36 * <linux-IRQ#> = <SB-core-flag> + <base-IRQ> + 2
38 * where <base-IRQ> is specified in setup.c when calling
39 * sb_mips_init(), 2 is to offset the two software IRQs.
41 * $Id: irq.c,v 1.11 2010/01/07 06:40:35 Exp $
44 #include <linux/config.h>
48 * This code is designed to work on Uniprocessor only.
50 * To support SMP we must know:
51 * - interrupt architecture
52 * - interrupt distribution machenism
54 #error "This implementation does not support SMP"
57 #include <linux/init.h>
58 #include <linux/irq.h>
59 #include <linux/kernel_stat.h>
61 #include <asm/mipsregs.h>
70 #include <mips74k_core.h>
73 /* cp0 SR register IM field */
74 #define SR_IM(irq) (1 << ((irq) + STATUSB_IP0))
76 /* cp0 CR register IP field */
77 #define CR_IP(irq) (1 << ((irq) + CAUSEB_IP0))
79 /* other local constants */
82 /* local variables and functions */
83 static sbconfig_t
*ccsbr
= NULL
; /* Chipc core SB config regs */
84 static sbconfig_t
*mipssbr
= NULL
; /* MIPS core SB config regs */
85 static int mipsirq
= -1; /* MIPS core virtual IRQ number */
86 static uint32 shints
= 0; /* Set of shared interrupts */
87 static int irq2en
= 0; /* MIPS IRQ2 enable count */
88 static uint
*mips_corereg
= NULL
;
90 /* global variables and functions */
91 extern si_t
*bcm947xx_sih
; /* defined in setup.c */
93 extern asmlinkage
void brcmIRQ(void);
95 /* Control functions for MIPS IRQ0 to IRQ7 */
97 enable_brcm_irq(unsigned int irq
)
99 set_c0_status(SR_IM(irq
));
104 disable_brcm_irq(unsigned int irq
)
106 clear_c0_status(SR_IM(irq
));
107 irq_disable_hazard();
111 ack_brcm_irq(unsigned int irq
)
113 /* Done in plat_irq_dispatch()! */
117 end_brcm_irq(unsigned int irq
)
119 /* Done in plat_irq_dispatch()! */
122 /* Control functions for linux IRQ8 and above */
124 enable_brcm_irq2(unsigned int irq
)
133 disable_brcm_irq2(unsigned int irq
)
142 ack_brcm_irq2(unsigned int irq
)
144 /* Already done in plat_irq_dispatch()! */
148 end_brcm_irq2(unsigned int irq
)
150 /* Already done in plat_irq_dispatch()! */
154 * Route interrupts to ISR(s).
156 * This function is entered with the IE disabled. It can be
157 * re-entered as soon as the IE is re-enabled in function
158 * handle_IRQ_envet().
161 plat_irq_dispatch(struct pt_regs
*regs
)
167 /* Disable MIPS IRQs with pending interrupts */
168 pending
= read_c0_cause() & CAUSEF_IP
;
169 pending
&= read_c0_status();
170 clear_c0_status(pending
);
171 irq_disable_hazard();
173 /* Handle MIPS timer interrupt. Re-enable MIPS IRQ7
174 * immediately after servicing the interrupt so that
175 * we can take this kind of interrupt again later
176 * while servicing other interrupts.
178 if (pending
& CAUSEF_IP7
) {
180 pending
&= ~CAUSEF_IP7
;
181 set_c0_status(STATUSF_IP7
);
185 /* Build bitvec for pending interrupts. Start with
186 * MIPS IRQ2 and add linux IRQs to higher bits to
187 * make the interrupt processing uniform.
189 ipvec
= pending
>> CAUSEB_IP2
;
190 if (pending
& CAUSEF_IP2
) {
192 flags
= R_REG(NULL
, &ccsbr
->sbflagst
);
196 flags
= R_REG(NULL
, &((mips74kregs_t
*)mips_corereg
)->intstatus
);
199 ipvec
|= flags
<< SBMIPS_VIRTIRQ_BASE
;
202 #ifdef CONFIG_HND_BMIPS3300_PROF
203 /* Handle MIPS core interrupt. Re-enable the MIPS IRQ that
204 * MIPS core is assigned to immediately after servicing the
205 * interrupt so that we can take this kind of interrupt again
206 * later while servicing other interrupts.
208 * mipsirq < 0 indicates MIPS core IRQ # is unknown.
210 if (mipsirq
>= 0 && (ipvec
& (1 << mipsirq
))) {
211 /* MIPS core raised the interrupt on the shared MIPS IRQ2.
212 * Make sure MIPS core is the only interrupt source before
213 * re-enabling the IRQ.
215 if (mipsirq
>= SBMIPS_VIRTIRQ_BASE
) {
216 if (flags
== (1 << (mipsirq
-SBMIPS_VIRTIRQ_BASE
))) {
219 ipvec
&= ~(1 << mipsirq
);
220 pending
&= ~CAUSEF_IP2
;
221 set_c0_status(STATUSF_IP2
);
225 /* MIPS core raised the interrupt on a dedicated MIPS IRQ.
226 * Re-enable the IRQ immediately.
231 ipvec
&= ~(1 << mipsirq
);
232 pending
&= ~CR_IP(irq
);
233 set_c0_status(SR_IM(irq
));
237 #endif /* CONFIG_HND_BMIPS3300_PROF */
239 /* Shared interrupt bits are shifted to respective bit positions in
240 * ipvec above. IP2 (bit 0) is of no significance, hence shifting the
241 * bit map by 1 to the right.
245 /* Handle all other interrupts. Re-enable disabled MIPS IRQs
246 * after processing all pending interrupts.
248 for (irq
= 3; ipvec
!= 0; irq
++) {
253 set_c0_status(pending
);
257 /* Process any pending softirqs (tasklets, softirqs ...) */
258 local_irq_save(flags
);
259 if (local_softirq_pending() && !in_interrupt())
261 local_irq_restore(flags
);
265 /* MIPS IRQ0 to IRQ7 interrupt controller */
266 static struct irq_chip brcm_irq_type
= {
269 .mask
= disable_brcm_irq
,
270 .mask_ack
= disable_brcm_irq
,
271 .unmask
= enable_brcm_irq
,
275 /* linux IRQ8 and above interrupt controller */
276 static struct irq_chip brcm_irq2_type
= {
278 .ack
= ack_brcm_irq2
,
279 .mask
= disable_brcm_irq2
,
280 .mask_ack
= disable_brcm_irq2
,
281 .unmask
= enable_brcm_irq2
,
286 * We utilize chipcommon configuration register SBFlagSt to implement a
287 * smart shared IRQ handling machenism through which only ISRs registered
288 * for the SB cores that raised the interrupt are invoked. This machenism
289 * relies on the SBFlagSt register's reliable recording of the SB cores
290 * that raised the interrupt.
296 uint32 coreidx
, mips_core_id
;
299 if (BCM330X(current_cpu_data
.processor_id
))
300 mips_core_id
= MIPS33_CORE_ID
;
301 else if (MIPS74K(current_cpu_data
.processor_id
))
302 mips_core_id
= MIPS74K_CORE_ID
;
304 printk(KERN_ERR
"MIPS CPU type %x unknown", current_cpu_data
.processor_id
);
308 /* Cache chipc and mips33 config registers */
309 ASSERT(bcm947xx_sih
);
310 coreidx
= si_coreidx(bcm947xx_sih
);
311 regs
= si_setcore(bcm947xx_sih
, mips_core_id
, 0);
312 mipsirq
= si_irq(bcm947xx_sih
);
313 if (bcm947xx_sih
->socitype
== SOCI_SB
) {
315 mipssbr
= (sbconfig_t
*)((ulong
)regs
+ SBCONFIGOFF
);
317 if ((regs
= si_setcore(bcm947xx_sih
, CC_CORE_ID
, 0)))
318 ccsbr
= (sbconfig_t
*)((ulong
)regs
+ SBCONFIGOFF
);
320 si_setcoreidx(bcm947xx_sih
, coreidx
);
322 if (BCM330X(current_cpu_data
.processor_id
)) {
323 /* Cache mips33 sbintvec register */
325 shints
= R_REG(NULL
, &mipssbr
->sbintvec
);
329 /* Use intmask5 register to route the timer interrupt */
330 intmask
= (uint32
*) &((mips74kregs_t
*)regs
)->intmask
[5];
331 W_REG(NULL
, intmask
, 1 << 31);
333 intmask
= (uint32
*) &((mips74kregs_t
*)regs
)->intmask
[0];
334 shints
= R_REG(NULL
, intmask
);
336 /* Save the pointer to mips core registers */
340 /* Install interrupt controllers */
341 for (i
= 0; i
< NR_IRQS
; i
++) {
342 set_irq_chip(i
, (i
< SBMIPS_NUMIRQS
? &brcm_irq_type
: &brcm_irq2_type
));