Merge branch 'master' of hera.kernel.org:/pub/scm/linux/kernel/git/kyle/parisc-2.6
[linux-2.6] / arch / mips / pmc-sierra / msp71xx / msp_irq_cic.c
1 /*
2  * This file define the irq handler for MSP SLM subsystem interrupts.
3  *
4  * Copyright 2005-2007 PMC-Sierra, Inc, derived from irq_cpu.c
5  * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com
6  *
7  * This program is free software; you can redistribute  it and/or modify it
8  * under  the terms of  the GNU General  Public License as published by the
9  * Free Software Foundation;  either version 2 of the  License, or (at your
10  * option) any later version.
11  */
12
13 #include <linux/init.h>
14 #include <linux/interrupt.h>
15 #include <linux/kernel.h>
16 #include <linux/bitops.h>
17
18 #include <asm/system.h>
19
20 #include <msp_cic_int.h>
21 #include <msp_regs.h>
22
23 /*
24  * NOTE: We are only enabling support for VPE0 right now.
25  */
26
27 static inline void unmask_msp_cic_irq(unsigned int irq)
28 {
29
30         /* check for PER interrupt range */
31         if (irq < MSP_PER_INTBASE)
32                 *CIC_VPE0_MSK_REG |= (1 << (irq - MSP_CIC_INTBASE));
33         else
34                 *PER_INT_MSK_REG |= (1 << (irq - MSP_PER_INTBASE));
35 }
36
37 static inline void mask_msp_cic_irq(unsigned int irq)
38 {
39         /* check for PER interrupt range */
40         if (irq < MSP_PER_INTBASE)
41                 *CIC_VPE0_MSK_REG &= ~(1 << (irq - MSP_CIC_INTBASE));
42         else
43                 *PER_INT_MSK_REG &= ~(1 << (irq - MSP_PER_INTBASE));
44 }
45
46 /*
47  * While we ack the interrupt interrupts are disabled and thus we don't need
48  * to deal with concurrency issues.  Same for msp_cic_irq_end.
49  */
50 static inline void ack_msp_cic_irq(unsigned int irq)
51 {
52         mask_msp_cic_irq(irq);
53
54         /*
55          * only really necessary for 18, 16-14 and sometimes 3:0 (since
56          * these can be edge sensitive) but it doesn't hurt for the others.
57          */
58
59         /* check for PER interrupt range */
60         if (irq < MSP_PER_INTBASE)
61                 *CIC_STS_REG = (1 << (irq - MSP_CIC_INTBASE));
62         else
63                 *PER_INT_STS_REG = (1 << (irq - MSP_PER_INTBASE));
64 }
65
66 static struct irq_chip msp_cic_irq_controller = {
67         .name = "MSP_CIC",
68         .ack = ack_msp_cic_irq,
69         .mask = ack_msp_cic_irq,
70         .mask_ack = ack_msp_cic_irq,
71         .unmask = unmask_msp_cic_irq,
72 };
73
74
75 void __init msp_cic_irq_init(void)
76 {
77         int i;
78
79         /* Mask/clear interrupts. */
80         *CIC_VPE0_MSK_REG = 0x00000000;
81         *PER_INT_MSK_REG  = 0x00000000;
82         *CIC_STS_REG      = 0xFFFFFFFF;
83         *PER_INT_STS_REG  = 0xFFFFFFFF;
84
85 #if defined(CONFIG_PMC_MSP7120_GW) || \
86     defined(CONFIG_PMC_MSP7120_EVAL)
87         /*
88          * The MSP7120 RG and EVBD boards use IRQ[6:4] for PCI.
89          * These inputs map to EXT_INT_POL[6:4] inside the CIC.
90          * They are to be active low, level sensitive.
91          */
92         *CIC_EXT_CFG_REG &= 0xFFFF8F8F;
93 #endif
94
95         /* initialize all the IRQ descriptors */
96         for (i = MSP_CIC_INTBASE; i < MSP_PER_INTBASE + 32; i++)
97                 set_irq_chip_and_handler(i, &msp_cic_irq_controller,
98                                          handle_level_irq);
99 }
100
101 void msp_cic_irq_dispatch(void)
102 {
103         u32 pending;
104         int intbase;
105
106         intbase = MSP_CIC_INTBASE;
107         pending = *CIC_STS_REG & *CIC_VPE0_MSK_REG;
108
109         /* check for PER interrupt */
110         if (pending == (1 << (MSP_INT_PER - MSP_CIC_INTBASE))) {
111                 intbase = MSP_PER_INTBASE;
112                 pending = *PER_INT_STS_REG & *PER_INT_MSK_REG;
113         }
114
115         /* check for spurious interrupt */
116         if (pending == 0x00000000) {
117                 printk(KERN_ERR
118                         "Spurious %s interrupt? status %08x, mask %08x\n",
119                         (intbase == MSP_CIC_INTBASE) ? "CIC" : "PER",
120                         (intbase == MSP_CIC_INTBASE) ?
121                                 *CIC_STS_REG : *PER_INT_STS_REG,
122                         (intbase == MSP_CIC_INTBASE) ?
123                                 *CIC_VPE0_MSK_REG : *PER_INT_MSK_REG);
124                 return;
125         }
126
127         /* check for the timer and dispatch it first */
128         if ((intbase == MSP_CIC_INTBASE) &&
129             (pending & (1 << (MSP_INT_VPE0_TIMER - MSP_CIC_INTBASE))))
130                 do_IRQ(MSP_INT_VPE0_TIMER);
131         else
132                 do_IRQ(ffs(pending) + intbase - 1);
133 }
134