3 Copyright 2002 Broadcom Corp. All Rights Reserved.
5 This program is free software; you can distribute it and/or modify it
6 under the terms of the GNU General Public License (Version 2) as
7 published by the Free Software Foundation.
9 This program is distributed in the hope it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
20 * Interrupt control functions for Broadcom 963xx MIPS boards
23 #include <asm/atomic.h>
25 #include <linux/delay.h>
26 #include <linux/init.h>
27 #include <linux/ioport.h>
28 #include <linux/irq.h>
29 #include <linux/interrupt.h>
30 #include <linux/kernel.h>
31 #include <linux/slab.h>
32 #include <linux/module.h>
35 #include <asm/mipsregs.h>
36 #include <asm/addrspace.h>
37 #include <asm/signal.h>
38 #include <bcm_map_part.h>
41 extern asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs);
43 static void irq_dispatch_int(struct pt_regs *regs)
45 unsigned int pendingIrqs;
46 static unsigned int irqBit;
47 static unsigned int isrNumber = 31;
49 pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
57 if (isrNumber == 32) {
61 if (pendingIrqs & irqBit) {
62 unsigned int irq = isrNumber + INTERNAL_ISR_TABLE_OFFSET;
63 #if defined(CONFIG_BCM96358)
64 if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
65 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear
67 else if (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) {
68 PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_CLEAR_SHFT)); // Clear
71 PERF->IrqMask &= ~irqBit; // mask
78 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348)
79 static void irq_dispatch_ext(uint32 irq, struct pt_regs *regs)
81 if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
82 printk("**** Ext IRQ mask. Should not dispatch ****\n");
84 /* disable and clear interrupt in the controller */
85 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
86 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
91 static void irq_dispatch_sw(uint32 irq, struct pt_regs *regs)
93 clear_c0_cause(0x1 << (CAUSEB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
98 void brcm_irq_dispatch(struct pt_regs *regs)
101 while((cause = (read_c0_cause() & read_c0_status() & CAUSEF_IP))) {
102 if (cause & CAUSEF_IP7)
103 do_IRQ(MIPS_TIMER_INT, regs);
104 else if (cause & CAUSEF_IP2)
105 irq_dispatch_int(regs);
106 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348)
107 else if (cause & CAUSEF_IP3)
108 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0, regs);
109 else if (cause & CAUSEF_IP4)
110 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1, regs);
111 else if (cause & CAUSEF_IP5)
112 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2, regs);
113 else if (cause & CAUSEF_IP6)
114 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3, regs);
116 else if (cause & CAUSEF_IP0)
117 irq_dispatch_sw(INTERRUPT_ID_SOFTWARE_0, regs);
118 else if (cause & CAUSEF_IP1)
119 irq_dispatch_sw(INTERRUPT_ID_SOFTWARE_1, regs);
125 void enable_brcm_irq(unsigned int irq)
129 local_irq_save(flags);
130 if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
131 PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
133 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348)
134 else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
135 /* enable and clear interrupt in the controller */
136 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
137 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
140 else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) {
141 set_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
143 local_irq_restore(flags);
146 void disable_brcm_irq(unsigned int irq)
150 local_irq_save(flags);
151 if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
152 PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
154 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348)
155 else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
156 /* disable interrupt in the controller */
157 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
160 else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) {
161 clear_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
163 local_irq_restore(flags);
166 void ack_brcm_irq(unsigned int irq)
168 /* Already done in brcm_irq_dispatch */
171 unsigned int startup_brcm_irq(unsigned int irq)
173 enable_brcm_irq(irq);
175 return 0; /* never anything pending */
178 unsigned int startup_brcm_none(unsigned int irq)
183 void end_brcm_irq(unsigned int irq)
185 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
186 enable_brcm_irq(irq);
189 void end_brcm_none(unsigned int irq)
193 #if defined (CONFIG_BCM96358)
194 #define ALLINTS_NOTIMER IE_IRQ0
196 #define ALLINTS_NOTIMER (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)
199 static void __init brcm_irq_setup(void)
201 extern asmlinkage void brcmIRQ(void);
203 clear_c0_status(ST0_BEV);
204 set_except_vector(0, brcmIRQ);
205 change_c0_status(ST0_IM, ALLINTS_NOTIMER);
207 #ifdef CONFIG_REMOTE_DEBUG
212 static struct hw_interrupt_type brcm_irq_type = {
214 .startup = startup_brcm_irq,
215 .shutdown = disable_brcm_irq,
216 .enable = enable_brcm_irq,
217 .disable = disable_brcm_irq,
223 static struct hw_interrupt_type brcm_irq_no_end_type = {
225 .startup = startup_brcm_none,
226 .shutdown = disable_brcm_irq,
227 .enable = enable_brcm_irq,
228 .disable = disable_brcm_irq,
230 .end = end_brcm_none,
234 void __init arch_init_irq(void)
238 for (i = 0; i < NR_IRQS; i++) {
239 irq_desc[i].status = IRQ_DISABLED;
240 irq_desc[i].action = 0;
241 irq_desc[i].depth = 1;
242 irq_desc[i].handler = &brcm_irq_type;
248 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348)
249 int request_external_irq(unsigned int irq,
251 unsigned long irqflags,
252 const char * devname,
257 local_irq_save(flags);
259 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear
260 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Mask
261 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive
262 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered
263 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low level
265 local_irq_restore(flags);
267 return( request_irq(irq, handler, irqflags, devname, dev_id) );
271 unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
277 devname = kmalloc(16, GFP_KERNEL);
279 sprintf( devname, "brcm_%d", irq );
281 /* Set the IRQ description to not automatically enable the interrupt at
282 * the end of an ISR. The driver that handles the interrupt must
283 * explicitly call BcmHalInterruptEnable or enable_brcm_irq. This behavior
284 * is consistent with interrupt handling on VxWorks.
286 irq_desc[irq].handler = &brcm_irq_no_end_type;
288 #if defined(CONFIG_BCM96348) || defined(CONFIG_BCM96358)
289 if( irq == INTERRUPT_ID_MPI ) {
290 nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT | SA_SHIRQ,
291 devname, (void *) param );
296 if( irq >= INTERNAL_ISR_TABLE_OFFSET )
298 #if defined(CONFIG_BCM96358)
301 local_irq_save(flags);
302 if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
303 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear
304 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Unmask
305 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive
306 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered
307 PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low level
309 else if (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) {
310 PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_CLEAR_SHFT)); // Clear
311 PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_MASK_SHFT)); // Unmask
312 PERF->ExtIrqCfg1 &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_INSENS_SHFT)); // Edge insesnsitive
313 PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_LEVEL_SHFT)); // Level triggered
314 PERF->ExtIrqCfg1 &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_SENSE_SHFT)); // Low level
316 local_irq_restore(flags);
318 nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
319 devname, (void *) param );
322 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348)
323 else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3)
325 nRet = request_external_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
326 devname, (void *) param );
329 else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1))
331 nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
332 devname, (void *) param );
339 //***************************************************************************
340 // void BcmHalGenerateSoftInterrupt
342 // Triggers a software interrupt.
344 //***************************************************************************
345 void BcmHalGenerateSoftInterrupt( unsigned int irq )
349 local_irq_save(flags);
351 set_c0_cause(0x1 << (CAUSEB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
353 local_irq_restore(flags);
356 EXPORT_SYMBOL(enable_brcm_irq);
357 EXPORT_SYMBOL(disable_brcm_irq);
358 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348)
359 EXPORT_SYMBOL(request_external_irq);
361 EXPORT_SYMBOL(BcmHalMapInterrupt);
362 EXPORT_SYMBOL(BcmHalGenerateSoftInterrupt);