ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ppc64 / kernel / open_pic_u3.c
1 /*
2  *  arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling
3  *
4  *  Copyright (C) 1997 Geert Uytterhoeven
5  *
6  *  This file is subject to the terms and conditions of the GNU General Public
7  *  License.  See the file COPYING in the main directory of this archive
8  *  for more details.
9  */
10
11 #include <linux/config.h>
12 #include <linux/types.h>
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/irq.h>
16 #include <linux/smp.h>
17 #include <linux/interrupt.h>
18 #include <asm/ptrace.h>
19 #include <asm/signal.h>
20 #include <asm/io.h>
21 #include <asm/pgtable.h>
22 #include <asm/irq.h>
23 #include <asm/prom.h>
24
25 #include <asm/machdep.h>
26
27 #include "open_pic.h"
28 #include "open_pic_defs.h"
29
30 void* OpenPIC2_Addr;
31 static volatile struct OpenPIC *OpenPIC2 = NULL;
32
33 extern u_int OpenPIC_NumInitSenses;
34 extern u_char *OpenPIC_InitSenses;
35
36 static u_int NumSources;
37 static int NumISUs;
38 static int open_pic2_irq_offset;
39
40 static OpenPIC_SourcePtr ISU2[OPENPIC_MAX_ISU];
41
42 unsigned int openpic2_vec_spurious;
43
44 /*
45  *  Accesses to the current processor's openpic registers
46  *  U3 secondary openpic has only one output
47  */
48 #define THIS_CPU                Processor[0]
49 #define DECL_THIS_CPU
50 #define CHECK_THIS_CPU
51
52 #define GET_ISU(source) ISU2[(source) >> 4][(source) & 0xf]
53
54 static inline u_int openpic2_read(volatile u_int *addr)
55 {
56         u_int val;
57
58         val = in_be32(addr);
59         return val;
60 }
61
62 static inline void openpic2_write(volatile u_int *addr, u_int val)
63 {
64         out_be32(addr, val);
65 }
66
67 static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask)
68 {
69         u_int val = openpic2_read(addr);
70         return val & mask;
71 }
72
73 static inline void openpic2_writefield(volatile u_int *addr, u_int mask,
74                                u_int field)
75 {
76         u_int val = openpic2_read(addr);
77         openpic2_write(addr, (val & ~mask) | (field & mask));
78 }
79
80 static inline void openpic2_clearfield(volatile u_int *addr, u_int mask)
81 {
82         openpic2_writefield(addr, mask, 0);
83 }
84
85 static inline void openpic2_setfield(volatile u_int *addr, u_int mask)
86 {
87         openpic2_writefield(addr, mask, mask);
88 }
89
90 static void openpic2_safe_writefield(volatile u_int *addr, u_int mask,
91                                     u_int field)
92 {
93         unsigned int loops = 100000;
94
95         openpic2_setfield(addr, OPENPIC_MASK);
96         while (openpic2_read(addr) & OPENPIC_ACTIVITY) {
97                 if (!loops--) {
98                         printk(KERN_ERR "openpic2_safe_writefield timeout\n");
99                         break;
100                 }
101         }
102         openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
103 }
104
105
106 static inline void openpic2_reset(void)
107 {
108         openpic2_setfield(&OpenPIC2->Global.Global_Configuration0,
109                          OPENPIC_CONFIG_RESET);
110 }
111
112 static void openpic2_disable_8259_pass_through(void)
113 {
114         openpic2_setfield(&OpenPIC2->Global.Global_Configuration0,
115                          OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
116 }
117
118 /*
119  *  Find out the current interrupt
120  */
121 static u_int openpic2_irq(void)
122 {
123         u_int vec;
124         DECL_THIS_CPU;
125         CHECK_THIS_CPU;
126         vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge,
127                                  OPENPIC_VECTOR_MASK);
128         return vec;
129 }
130
131 static void openpic2_eoi(void)
132 {
133         DECL_THIS_CPU;
134         CHECK_THIS_CPU;
135         openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0);
136         /* Handle PCI write posting */
137         (void)openpic2_read(&OpenPIC2->THIS_CPU.EOI);
138 }
139
140
141 static inline u_int openpic2_get_priority(void)
142 {
143         DECL_THIS_CPU;
144         CHECK_THIS_CPU;
145         return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority,
146                                   OPENPIC_CURRENT_TASK_PRIORITY_MASK);
147 }
148
149 static void openpic2_set_priority(u_int pri)
150 {
151         DECL_THIS_CPU;
152         CHECK_THIS_CPU;
153         openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority,
154                             OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
155 }
156
157 /*
158  *  Get/set the spurious vector
159  */
160 static inline u_int openpic2_get_spurious(void)
161 {
162         return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector,
163                                   OPENPIC_VECTOR_MASK);
164 }
165
166 static void openpic2_set_spurious(u_int vec)
167 {
168         openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
169                             vec);
170 }
171
172 /*
173  *  Enable/disable an external interrupt source
174  *
175  *  Externally called, irq is an offseted system-wide interrupt number
176  */
177 static void openpic2_enable_irq(u_int irq)
178 {
179         unsigned int loops = 100000;
180
181         openpic2_clearfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority, OPENPIC_MASK);
182         /* make sure mask gets to controller before we return to user */
183         do {
184                 if (!loops--) {
185                         printk(KERN_ERR "openpic_enable_irq timeout\n");
186                         break;
187                 }
188
189                 mb(); /* sync is probably useless here */
190         } while(openpic2_readfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority,
191                         OPENPIC_MASK));
192 }
193
194 static void openpic2_disable_irq(u_int irq)
195 {
196         u32 vp;
197         unsigned int loops = 100000;
198         
199         openpic2_setfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority,
200                           OPENPIC_MASK);
201         /* make sure mask gets to controller before we return to user */
202         do {
203                 if (!loops--) {
204                         printk(KERN_ERR "openpic_disable_irq timeout\n");
205                         break;
206                 }
207
208                 mb();  /* sync is probably useless here */
209                 vp = openpic2_readfield(&GET_ISU(irq - open_pic2_irq_offset).Vector_Priority,
210                         OPENPIC_MASK | OPENPIC_ACTIVITY);
211         } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK));
212 }
213
214 /*
215  *  Initialize an interrupt source (and disable it!)
216  *
217  *  irq: OpenPIC interrupt number
218  *  pri: interrupt source priority
219  *  vec: the vector it will produce
220  *  pol: polarity (1 for positive, 0 for negative)
221  *  sense: 1 for level, 0 for edge
222  */
223 static void openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
224 {
225         openpic2_safe_writefield(&GET_ISU(irq).Vector_Priority,
226                                  OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
227                                  OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK,
228                                  (pri << OPENPIC_PRIORITY_SHIFT) | vec |
229                                  (pol ? OPENPIC_POLARITY_POSITIVE :
230                                   OPENPIC_POLARITY_NEGATIVE) |
231                                  (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE));
232 }
233
234 /*
235  *  Map an interrupt source to one or more CPUs
236  */
237 static void openpic2_mapirq(u_int irq, u_int physmask)
238 {
239         openpic2_write(&GET_ISU(irq).Destination, physmask);
240 }
241
242 /*
243  *  Set the sense for an interrupt source (and disable it!)
244  *
245  *  sense: 1 for level, 0 for edge
246  */
247 static inline void openpic2_set_sense(u_int irq, int sense)
248 {
249         openpic2_safe_writefield(&GET_ISU(irq).Vector_Priority,
250                                  OPENPIC_SENSE_LEVEL,
251                                  (sense ? OPENPIC_SENSE_LEVEL : 0));
252 }
253
254 static void openpic2_end_irq(unsigned int irq_nr)
255 {
256         openpic2_eoi();
257 }
258
259 int openpic2_get_irq(struct pt_regs *regs)
260 {
261         int irq = openpic2_irq();
262
263         if (irq == openpic2_vec_spurious)
264                 return -1;
265         return irq + open_pic2_irq_offset;
266 }
267
268 struct hw_interrupt_type open_pic2 = {
269         " OpenPIC2 ",
270         NULL,
271         NULL,
272         openpic2_enable_irq,
273         openpic2_disable_irq,
274         NULL,
275         openpic2_end_irq,
276 };
277
278 void __init openpic2_init(int offset)
279 {
280         u_int t, i;
281         const char *version;
282
283         if (!OpenPIC2_Addr) {
284                 printk(KERN_INFO "No OpenPIC2 found !\n");
285                 return;
286         }
287         OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr;
288
289         ppc64_boot_msg(0x20, "OpenPic U3 Init");
290
291         t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0);
292         switch (t & OPENPIC_FEATURE_VERSION_MASK) {
293         case 1:
294                 version = "1.0";
295                 break;
296         case 2:
297                 version = "1.2";
298                 break;
299         case 3:
300                 version = "1.3";
301                 break;
302         default:
303                 version = "?";
304                 break;
305         }
306         printk(KERN_INFO "OpenPIC (U3) Version %s\n", version);
307
308         open_pic2_irq_offset = offset;
309
310         for (i=0; i<128; i+=0x10) {
311                 ISU2[i>>4] = &((struct OpenPIC *)OpenPIC2_Addr)->Source[i];
312                 NumISUs++;
313         }
314         NumSources = NumISUs * 0x10;
315         openpic2_vec_spurious = NumSources;
316
317         openpic2_set_priority(0xf);
318
319         /* Init all external sources */
320         for (i = 0; i < NumSources; i++) {
321                 int pri, sense;
322
323                 /* the bootloader may have left it enabled (bad !) */
324                 openpic2_disable_irq(i+offset);
325
326                 pri = 8;
327                 sense = (i < OpenPIC_NumInitSenses) ? OpenPIC_InitSenses[i]: 1;
328                 if (sense)
329                         irq_desc[i+offset].status = IRQ_LEVEL;
330
331                 /* Enabled, Priority 8 or 9 */
332                 openpic2_initirq(i, pri, i, !sense, sense);
333                 /* Processor 0 */
334                 openpic2_mapirq(i, 0x1);
335         }
336
337         /* Init descriptors */
338         for (i = offset; i < NumSources + offset; i++)
339                 irq_desc[i].handler = &open_pic2;
340
341         /* Initialize the spurious interrupt */
342         openpic2_set_spurious(openpic2_vec_spurious);
343
344         openpic2_set_priority(0);
345         openpic2_disable_8259_pass_through();
346
347         ppc64_boot_msg(0x25, "OpenPic2 Done");
348 }