Broadcom SDK and wireless driver: another attempt to update to ver. 5.10.147.0
[tomato.git] / release / src-rt / linux / linux-2.6 / arch / mips / brcm-boards / bcm947xx / irq.c
blob8e0cd8ef8aada13f0299dda1f56c7a2772e86206
1 /*
2 * Copyright (C) 2009, 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.
10 * Broadcom HND BCM47xx chips interrupt dispatcher.
11 * Derived from ../generic/irq.c
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)
24 * MIPS IRQ Linux IRQ
25 * -------- -----------
26 * 0 - 1 0 - 1
27 * 2 8 and above
28 * 3 - 7 3 - 7
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:
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>
46 #ifdef CONFIG_SMP
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"
55 #endif
57 #include <linux/init.h>
58 #include <linux/irq.h>
59 #include <linux/kernel_stat.h>
61 #include <asm/mipsregs.h>
63 #include <typedefs.h>
64 #include <osl.h>
65 #include <bcmutils.h>
66 #include <hndsoc.h>
67 #include <siutils.h>
68 #include <hndcpu.h>
69 #include <hndsoc.h>
70 #include <mips74k_core.h>
71 #include "bcm947xx.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 */
80 #define NUM_IRQS 32
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 */
96 static INLINE void
97 enable_brcm_irq(unsigned int irq)
99 set_c0_status(SR_IM(irq));
100 irq_enable_hazard();
103 static INLINE void
104 disable_brcm_irq(unsigned int irq)
106 clear_c0_status(SR_IM(irq));
107 irq_disable_hazard();
110 static void
111 ack_brcm_irq(unsigned int irq)
113 /* Done in plat_irq_dispatch()! */
116 static void
117 end_brcm_irq(unsigned int irq)
119 /* Done in plat_irq_dispatch()! */
122 /* Control functions for linux IRQ8 and above */
123 static INLINE void
124 enable_brcm_irq2(unsigned int irq)
126 ASSERT(irq2en >= 0);
127 if (irq2en++)
128 return;
129 enable_brcm_irq(2);
132 static INLINE void
133 disable_brcm_irq2(unsigned int irq)
135 ASSERT(irq2en > 0);
136 if (--irq2en)
137 return;
138 disable_brcm_irq(2);
141 static void
142 ack_brcm_irq2(unsigned int irq)
144 /* Already done in plat_irq_dispatch()! */
147 static void
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().
160 void BCMFASTPATH
161 plat_irq_dispatch(struct pt_regs *regs)
163 u32 pending, ipvec;
164 uint32 flags = 0;
165 int irq;
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) {
179 do_IRQ(7);
180 pending &= ~CAUSEF_IP7;
181 set_c0_status(STATUSF_IP7);
182 irq_enable_hazard();
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) {
191 if (ccsbr)
192 flags = R_REG(NULL, &ccsbr->sbflagst);
194 /* Read intstatus */
195 if (mips_corereg)
196 flags = R_REG(NULL, &((mips74kregs_t *)mips_corereg)->intstatus);
198 flags &= shints;
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))) {
217 irq = mipsirq + 2;
218 do_IRQ(irq);
219 ipvec &= ~(1 << mipsirq);
220 pending &= ~CAUSEF_IP2;
221 set_c0_status(STATUSF_IP2);
222 irq_enable_hazard();
225 /* MIPS core raised the interrupt on a dedicated MIPS IRQ.
226 * Re-enable the IRQ immediately.
228 else {
229 irq = mipsirq + 2;
230 do_IRQ(irq);
231 ipvec &= ~(1 << mipsirq);
232 pending &= ~CR_IP(irq);
233 set_c0_status(SR_IM(irq));
234 irq_enable_hazard();
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.
243 ipvec >>= 1;
245 /* Handle all other interrupts. Re-enable disabled MIPS IRQs
246 * after processing all pending interrupts.
248 for (irq = 3; ipvec != 0; irq++) {
249 if (ipvec & 1)
250 do_IRQ(irq);
251 ipvec >>= 1;
253 set_c0_status(pending);
254 irq_enable_hazard();
256 #if 0
257 /* Process any pending softirqs (tasklets, softirqs ...) */
258 local_irq_save(flags);
259 if (local_softirq_pending() && !in_interrupt())
260 __do_softirq();
261 local_irq_restore(flags);
262 #endif
265 /* MIPS IRQ0 to IRQ7 interrupt controller */
266 static struct irq_chip brcm_irq_type = {
267 .name = "MIPS",
268 .ack = ack_brcm_irq,
269 .mask = disable_brcm_irq,
270 .mask_ack = disable_brcm_irq,
271 .unmask = enable_brcm_irq,
272 .end = end_brcm_irq
275 /* linux IRQ8 and above interrupt controller */
276 static struct irq_chip brcm_irq2_type = {
277 .name = "IRQ2",
278 .ack = ack_brcm_irq2,
279 .mask = disable_brcm_irq2,
280 .mask_ack = disable_brcm_irq2,
281 .unmask = enable_brcm_irq2,
282 .end = end_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.
292 void __init
293 arch_init_irq(void)
295 int i;
296 uint32 coreidx, mips_core_id;
297 void *regs;
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;
303 else {
304 printk(KERN_ERR "MIPS CPU type %x unknown", current_cpu_data.processor_id);
305 return;
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) {
314 if (regs)
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 */
324 if (mipssbr)
325 shints = R_REG(NULL, &mipssbr->sbintvec);
326 } else {
327 uint32 *intmask;
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 */
337 mips_corereg = regs;
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));