Pull kmalloc into release branch
[pandora-kernel.git] / arch / sh / boards / overdrive / irq.c
1 /* 
2  * Copyright (C) 2000 David J. Mckay (david.mckay@st.com)
3  *
4  * May be copied or modified under the terms of the GNU General Public
5  * License.  See linux/COPYING for more information.                            
6  *
7  * Looks after interrupts on the overdrive board.
8  *
9  * Bases on the IPR irq system
10  */
11
12 #include <linux/init.h>
13 #include <linux/irq.h>
14
15 #include <asm/system.h>
16 #include <asm/io.h>
17
18 #include <asm/overdrive/overdrive.h>
19
20 struct od_data {
21         int overdrive_irq;
22         int irq_mask;
23 };
24
25 #define NUM_EXTERNAL_IRQS 16
26 #define EXTERNAL_IRQ_NOT_IN_USE (-1)
27 #define EXTERNAL_IRQ_NOT_ASSIGNED (-1)
28
29 /*
30  * This table is used to determine what to program into the FPGA's CT register
31  * for the specified Linux IRQ.
32  *
33  * The irq_mask gives the interrupt number from the PCI board (PCI_Int(6:0))
34  * but is one greater than that because the because the FPGA treats 0
35  * as disabled, a value of 1 asserts PCI_Int0, and so on.
36  *
37  * The overdrive_irq specifies which of the eight interrupt sources generates
38  * that interrupt, and but is multiplied by four to give the bit offset into
39  * the CT register.
40  *
41  * The seven interrupts levels (SH4 IRL's) we have available here is hardwired
42  * by the EPLD. The assignments here of which PCI interrupt generates each
43  * level is arbitary.
44  */
45 static struct od_data od_data_table[NUM_EXTERNAL_IRQS] = {
46         /*    overdrive_irq       , irq_mask */
47         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 0 */
48         {EXTERNAL_IRQ_NOT_ASSIGNED, 7}, /* 1 */
49         {EXTERNAL_IRQ_NOT_ASSIGNED, 6}, /* 2 */
50         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 3 */
51         {EXTERNAL_IRQ_NOT_ASSIGNED, 5}, /* 4 */
52         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 5 */
53         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 6 */
54         {EXTERNAL_IRQ_NOT_ASSIGNED, 4}, /* 7 */
55         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 8 */
56         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 9 */
57         {EXTERNAL_IRQ_NOT_ASSIGNED, 3}, /* 10 */
58         {EXTERNAL_IRQ_NOT_ASSIGNED, 2}, /* 11 */
59         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 12 */
60         {EXTERNAL_IRQ_NOT_ASSIGNED, 1}, /* 13 */
61         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE},   /* 14 */
62         {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}    /* 15 */
63 };
64
65 static void set_od_data(int overdrive_irq, int irq)
66 {
67         if (irq >= NUM_EXTERNAL_IRQS || irq < 0)
68                 return;
69         od_data_table[irq].overdrive_irq = overdrive_irq << 2;
70 }
71
72 static void enable_od_irq(unsigned int irq);
73 void disable_od_irq(unsigned int irq);
74
75 /* shutdown is same as "disable" */
76 #define shutdown_od_irq disable_od_irq
77
78 static void mask_and_ack_od(unsigned int);
79 static void end_od_irq(unsigned int irq);
80
81 static unsigned int startup_od_irq(unsigned int irq)
82 {
83         enable_od_irq(irq);
84         return 0;               /* never anything pending */
85 }
86
87 static struct hw_interrupt_type od_irq_type = {
88         .typename = "Overdrive-IRQ",
89         .startup = startup_od_irq,
90         .shutdown = shutdown_od_irq,
91         .enable = enable_od_irq,
92         .disable = disable_od_irq,
93         .ack = mask_and_ack_od,
94         .end = end_od_irq
95 };
96
97 static void disable_od_irq(unsigned int irq)
98 {
99         unsigned val, flags;
100         int overdrive_irq;
101         unsigned mask;
102
103         /* Not a valid interrupt */
104         if (irq < 0 || irq >= NUM_EXTERNAL_IRQS)
105                 return;
106
107         /* Is is necessary to use a cli here? Would a spinlock not be 
108          * mroe efficient?
109          */
110         local_irq_save(flags);
111         overdrive_irq = od_data_table[irq].overdrive_irq;
112         if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) {
113                 mask = ~(0x7 << overdrive_irq);
114                 val = ctrl_inl(OVERDRIVE_INT_CT);
115                 val &= mask;
116                 ctrl_outl(val, OVERDRIVE_INT_CT);
117         }
118         local_irq_restore(flags);
119 }
120
121 static void enable_od_irq(unsigned int irq)
122 {
123         unsigned val, flags;
124         int overdrive_irq;
125         unsigned mask;
126
127         /* Not a valid interrupt */
128         if (irq < 0 || irq >= NUM_EXTERNAL_IRQS)
129                 return;
130
131         /* Set priority in OD back to original value */
132         local_irq_save(flags);
133         /* This one is not in use currently */
134         overdrive_irq = od_data_table[irq].overdrive_irq;
135         if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) {
136                 val = ctrl_inl(OVERDRIVE_INT_CT);
137                 mask = ~(0x7 << overdrive_irq);
138                 val &= mask;
139                 mask = od_data_table[irq].irq_mask << overdrive_irq;
140                 val |= mask;
141                 ctrl_outl(val, OVERDRIVE_INT_CT);
142         }
143         local_irq_restore(flags);
144 }
145
146
147
148 /* this functions sets the desired irq handler to be an overdrive type */
149 static void __init make_od_irq(unsigned int irq)
150 {
151         disable_irq_nosync(irq);
152         irq_desc[irq].chip = &od_irq_type;
153         disable_od_irq(irq);
154 }
155
156
157 static void mask_and_ack_od(unsigned int irq)
158 {
159         disable_od_irq(irq);
160 }
161
162 static void end_od_irq(unsigned int irq)
163 {
164         enable_od_irq(irq);
165 }
166
167 void __init init_overdrive_irq(void)
168 {
169         int i;
170
171         /* Disable all interrupts */
172         ctrl_outl(0, OVERDRIVE_INT_CT);
173
174         /* Update interrupt pin mode to use encoded interrupts */
175         i = ctrl_inw(INTC_ICR);
176         i &= ~INTC_ICR_IRLM;
177         ctrl_outw(i, INTC_ICR);
178
179         for (i = 0; i < NUM_EXTERNAL_IRQS; i++) {
180                 if (od_data_table[i].irq_mask != EXTERNAL_IRQ_NOT_IN_USE) {
181                         make_od_irq(i);
182                 } else if (i != 15) {   // Cannot use imask on level 15
183                         make_imask_irq(i);
184                 }
185         }
186
187         /* Set up the interrupts */
188         set_od_data(OVERDRIVE_PCI_INTA, OVERDRIVE_PCI_IRQ1);
189         set_od_data(OVERDRIVE_PCI_INTB, OVERDRIVE_PCI_IRQ2);
190         set_od_data(OVERDRIVE_AUDIO_INT, OVERDRIVE_ESS_IRQ);
191 }