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()! */
153 extern asmlinkage
void __do_softirq(void);
156 * Route interrupts to ISR(s).
158 * This function is entered with the IE disabled. It can be
159 * re-entered as soon as the IE is re-enabled in function
160 * handle_IRQ_envet().
163 plat_irq_dispatch(struct pt_regs
*regs
)
169 /* Disable MIPS IRQs with pending interrupts */
170 pending
= read_c0_cause() & CAUSEF_IP
;
171 pending
&= read_c0_status();
172 clear_c0_status(pending
);
173 irq_disable_hazard();
175 /* Handle MIPS timer interrupt. Re-enable MIPS IRQ7
176 * immediately after servicing the interrupt so that
177 * we can take this kind of interrupt again later
178 * while servicing other interrupts.
180 if (pending
& CAUSEF_IP7
) {
182 pending
&= ~CAUSEF_IP7
;
183 set_c0_status(STATUSF_IP7
);
187 /* Build bitvec for pending interrupts. Start with
188 * MIPS IRQ2 and add linux IRQs to higher bits to
189 * make the interrupt processing uniform.
191 ipvec
= pending
>> CAUSEB_IP2
;
192 if (pending
& CAUSEF_IP2
) {
194 flags
= R_REG(NULL
, &ccsbr
->sbflagst
);
198 flags
= R_REG(NULL
, &((mips74kregs_t
*)mips_corereg
)->intstatus
);
201 ipvec
|= flags
<< SBMIPS_VIRTIRQ_BASE
;
204 #ifdef CONFIG_HND_BMIPS3300_PROF
205 /* Handle MIPS core interrupt. Re-enable the MIPS IRQ that
206 * MIPS core is assigned to immediately after servicing the
207 * interrupt so that we can take this kind of interrupt again
208 * later while servicing other interrupts.
210 * mipsirq < 0 indicates MIPS core IRQ # is unknown.
212 if (mipsirq
>= 0 && (ipvec
& (1 << mipsirq
))) {
213 /* MIPS core raised the interrupt on the shared MIPS IRQ2.
214 * Make sure MIPS core is the only interrupt source before
215 * re-enabling the IRQ.
217 if (mipsirq
>= SBMIPS_VIRTIRQ_BASE
) {
218 if (flags
== (1 << (mipsirq
-SBMIPS_VIRTIRQ_BASE
))) {
221 ipvec
&= ~(1 << mipsirq
);
222 pending
&= ~CAUSEF_IP2
;
223 set_c0_status(STATUSF_IP2
);
227 /* MIPS core raised the interrupt on a dedicated MIPS IRQ.
228 * Re-enable the IRQ immediately.
233 ipvec
&= ~(1 << mipsirq
);
234 pending
&= ~CR_IP(irq
);
235 set_c0_status(SR_IM(irq
));
239 #endif /* CONFIG_HND_BMIPS3300_PROF */
241 /* Shared interrupt bits are shifted to respective bit positions in
242 * ipvec above. IP2 (bit 0) is of no significance, hence shifting the
243 * bit map by 1 to the right.
247 /* Handle all other interrupts. Re-enable disabled MIPS IRQs
248 * after processing all pending interrupts.
250 for (irq
= 3; ipvec
!= 0; irq
++) {
255 set_c0_status(pending
);
259 /* Process any pending softirqs (tasklets, softirqs ...) */
260 local_irq_save(flags
);
261 if (local_softirq_pending() && !in_interrupt())
263 local_irq_restore(flags
);
267 /* MIPS IRQ0 to IRQ7 interrupt controller */
268 static struct irq_chip brcm_irq_type
= {
271 .mask
= disable_brcm_irq
,
272 .mask_ack
= disable_brcm_irq
,
273 .unmask
= enable_brcm_irq
,
277 /* linux IRQ8 and above interrupt controller */
278 static struct irq_chip brcm_irq2_type
= {
280 .ack
= ack_brcm_irq2
,
281 .mask
= disable_brcm_irq2
,
282 .mask_ack
= disable_brcm_irq2
,
283 .unmask
= enable_brcm_irq2
,
288 * We utilize chipcommon configuration register SBFlagSt to implement a
289 * smart shared IRQ handling machenism through which only ISRs registered
290 * for the SB cores that raised the interrupt are invoked. This machenism
291 * relies on the SBFlagSt register's reliable recording of the SB cores
292 * that raised the interrupt.
298 uint32 coreidx
, mips_core_id
;
301 if (BCM330X(current_cpu_data
.processor_id
))
302 mips_core_id
= MIPS33_CORE_ID
;
303 else if (MIPS74K(current_cpu_data
.processor_id
))
304 mips_core_id
= MIPS74K_CORE_ID
;
306 printk(KERN_ERR
"MIPS CPU type %x unknown", current_cpu_data
.processor_id
);
310 /* Cache chipc and mips33 config registers */
311 ASSERT(bcm947xx_sih
);
312 coreidx
= si_coreidx(bcm947xx_sih
);
313 regs
= si_setcore(bcm947xx_sih
, mips_core_id
, 0);
314 mipsirq
= si_irq(bcm947xx_sih
);
315 if (bcm947xx_sih
->socitype
== SOCI_SB
) {
317 mipssbr
= (sbconfig_t
*)((ulong
)regs
+ SBCONFIGOFF
);
319 if ((regs
= si_setcore(bcm947xx_sih
, CC_CORE_ID
, 0)))
320 ccsbr
= (sbconfig_t
*)((ulong
)regs
+ SBCONFIGOFF
);
322 si_setcoreidx(bcm947xx_sih
, coreidx
);
324 if (BCM330X(current_cpu_data
.processor_id
)) {
325 /* Cache mips33 sbintvec register */
327 shints
= R_REG(NULL
, &mipssbr
->sbintvec
);
331 /* Use intmask5 register to route the timer interrupt */
332 intmask
= (uint32
*) &((mips74kregs_t
*)regs
)->intmask
[5];
333 W_REG(NULL
, intmask
, 1 << 31);
335 intmask
= (uint32
*) &((mips74kregs_t
*)regs
)->intmask
[0];
336 shints
= R_REG(NULL
, intmask
);
338 /* Save the pointer to mips core registers */
342 /* Install interrupt controllers */
343 for (i
= 0; i
< NR_IRQS
; i
++) {
344 set_irq_chip(i
, (i
< SBMIPS_NUMIRQS
? &brcm_irq_type
: &brcm_irq2_type
));