]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/sparc/kernel/sun4m_irq.c
c09ec39d68ae6f6da061acba571db6a9bd5fa51b
[linux-2.6-omap-h63xx.git] / arch / sparc / kernel / sun4m_irq.c
1 /*  sun4m_irq.c
2  *  arch/sparc/kernel/sun4m_irq.c:
3  *
4  *  djhr: Hacked out of irq.c into a CPU dependent version.
5  *
6  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
7  *  Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
8  *  Copyright (C) 1995 Pete A. Zaitcev (zaitcev@yahoo.com)
9  *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
10  */
11
12 #include <linux/errno.h>
13 #include <linux/linkage.h>
14 #include <linux/kernel_stat.h>
15 #include <linux/signal.h>
16 #include <linux/sched.h>
17 #include <linux/ptrace.h>
18 #include <linux/smp.h>
19 #include <linux/interrupt.h>
20 #include <linux/slab.h>
21 #include <linux/init.h>
22 #include <linux/ioport.h>
23 #include <linux/of.h>
24 #include <linux/of_device.h>
25
26 #include <asm/ptrace.h>
27 #include <asm/processor.h>
28 #include <asm/system.h>
29 #include <asm/psr.h>
30 #include <asm/vaddrs.h>
31 #include <asm/timer.h>
32 #include <asm/openprom.h>
33 #include <asm/oplib.h>
34 #include <asm/traps.h>
35 #include <asm/pgalloc.h>
36 #include <asm/pgtable.h>
37 #include <asm/smp.h>
38 #include <asm/irq.h>
39 #include <asm/io.h>
40 #include <asm/cacheflush.h>
41
42 #include "irq.h"
43
44 struct sun4m_irq_percpu {
45         u32             pending;
46         u32             clear;
47         u32             set;
48 };
49
50 struct sun4m_irq_global {
51         u32             pending;
52         u32             mask;
53         u32             mask_clear;
54         u32             mask_set;
55         u32             interrupt_target;
56 };
57
58 /* Code in entry.S needs to get at these register mappings.  */
59 struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
60 struct sun4m_irq_global __iomem *sun4m_irq_global;
61
62 static unsigned long dummy;
63 unsigned long *irq_rcvreg = &dummy;
64
65 /* Dave Redman (djhr@tadpole.co.uk)
66  * The sun4m interrupt registers.
67  */
68 #define SUN4M_INT_ENABLE        0x80000000
69 #define SUN4M_INT_E14           0x00000080
70 #define SUN4M_INT_E10           0x00080000
71
72 #define SUN4M_HARD_INT(x)       (0x000000001 << (x))
73 #define SUN4M_SOFT_INT(x)       (0x000010000 << (x))
74
75 #define SUN4M_INT_MASKALL       0x80000000        /* mask all interrupts */
76 #define SUN4M_INT_MODULE_ERR    0x40000000        /* module error */
77 #define SUN4M_INT_M2S_WRITE     0x20000000        /* write buffer error */
78 #define SUN4M_INT_ECC           0x10000000        /* ecc memory error */
79 #define SUN4M_INT_FLOPPY        0x00400000        /* floppy disk */
80 #define SUN4M_INT_MODULE        0x00200000        /* module interrupt */
81 #define SUN4M_INT_VIDEO         0x00100000        /* onboard video */
82 #define SUN4M_INT_REALTIME      0x00080000        /* system timer */
83 #define SUN4M_INT_SCSI          0x00040000        /* onboard scsi */
84 #define SUN4M_INT_AUDIO         0x00020000        /* audio/isdn */
85 #define SUN4M_INT_ETHERNET      0x00010000        /* onboard ethernet */
86 #define SUN4M_INT_SERIAL        0x00008000        /* serial ports */
87 #define SUN4M_INT_KBDMS         0x00004000        /* keyboard/mouse */
88 #define SUN4M_INT_SBUSBITS      0x00003F80        /* sbus int bits */
89
90 #define SUN4M_INT_SBUS(x)       (1 << (x+7))
91 #define SUN4M_INT_VME(x)        (1 << (x))
92
93 /* These tables only apply for interrupts greater than 15..
94  * 
95  * any intr value below 0x10 is considered to be a soft-int
96  * this may be useful or it may not.. but that's how I've done it.
97  * and it won't clash with what OBP is telling us about devices.
98  *
99  * take an encoded intr value and lookup if it's valid
100  * then get the mask bits that match from irq_mask
101  *
102  * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee.
103  */
104 static unsigned char irq_xlate[32] = {
105     /*  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  a,  b,  c,  d,  e,  f */
106         0,  0,  0,  0,  1,  0,  2,  0,  3,  0,  4,  5,  6, 14,  0,  7,
107         0,  0,  8,  9,  0, 10,  0, 11,  0, 12,  0, 13,  0, 14,  0,  0
108 };
109
110 static unsigned long irq_mask[] = {
111         0,                                                /* illegal index */
112         SUN4M_INT_SCSI,                                   /*  1 irq 4 */
113         SUN4M_INT_ETHERNET,                               /*  2 irq 6 */
114         SUN4M_INT_VIDEO,                                  /*  3 irq 8 */
115         SUN4M_INT_REALTIME,                               /*  4 irq 10 */
116         SUN4M_INT_FLOPPY,                                 /*  5 irq 11 */
117         (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS),             /*  6 irq 12 */
118         SUN4M_INT_MODULE_ERR,                             /*  7 irq 15 */
119         SUN4M_INT_SBUS(0),                                /*  8 irq 2 */
120         SUN4M_INT_SBUS(1),                                /*  9 irq 3 */
121         SUN4M_INT_SBUS(2),                                /* 10 irq 5 */
122         SUN4M_INT_SBUS(3),                                /* 11 irq 7 */
123         SUN4M_INT_SBUS(4),                                /* 12 irq 9 */
124         SUN4M_INT_SBUS(5),                                /* 13 irq 11 */
125         SUN4M_INT_SBUS(6)                                 /* 14 irq 13 */
126 };
127
128 static unsigned long sun4m_get_irqmask(unsigned int irq)
129 {
130         unsigned long mask;
131     
132         if (irq > 0x20) {
133                 /* OBIO/SBUS interrupts */
134                 irq &= 0x1f;
135                 mask = irq_mask[irq_xlate[irq]];
136                 if (!mask)
137                         printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
138         } else {
139                 /* Soft Interrupts will come here.
140                  * Currently there is no way to trigger them but I'm sure
141                  * something could be cooked up.
142                  */
143                 irq &= 0xf;
144                 mask = SUN4M_SOFT_INT(irq);
145         }
146         return mask;
147 }
148
149 static void sun4m_disable_irq(unsigned int irq_nr)
150 {
151         unsigned long mask, flags;
152         int cpu = smp_processor_id();
153
154         mask = sun4m_get_irqmask(irq_nr);
155         local_irq_save(flags);
156         if (irq_nr > 15)
157                 sbus_writel(mask, &sun4m_irq_global->mask_set);
158         else
159                 sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
160         local_irq_restore(flags);    
161 }
162
163 static void sun4m_enable_irq(unsigned int irq_nr)
164 {
165         unsigned long mask, flags;
166         int cpu = smp_processor_id();
167
168         /* Dreadful floppy hack. When we use 0x2b instead of
169          * 0x0b the system blows (it starts to whistle!).
170          * So we continue to use 0x0b. Fixme ASAP. --P3
171          */
172         if (irq_nr != 0x0b) {
173                 mask = sun4m_get_irqmask(irq_nr);
174                 local_irq_save(flags);
175                 if (irq_nr > 15)
176                         sbus_writel(mask, &sun4m_irq_global->mask_clear);
177                 else
178                         sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
179                 local_irq_restore(flags);    
180         } else {
181                 local_irq_save(flags);
182                 sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear);
183                 local_irq_restore(flags);
184         }
185 }
186
187 static unsigned long cpu_pil_to_imask[16] = {
188 /*0*/   0x00000000,
189 /*1*/   0x00000000,
190 /*2*/   SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0),
191 /*3*/   SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1),
192 /*4*/   SUN4M_INT_SCSI,
193 /*5*/   SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2),
194 /*6*/   SUN4M_INT_ETHERNET,
195 /*7*/   SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3),
196 /*8*/   SUN4M_INT_VIDEO,
197 /*9*/   SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
198 /*10*/  SUN4M_INT_REALTIME,
199 /*11*/  SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
200 /*12*/  SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
201 /*13*/  SUN4M_INT_AUDIO,
202 /*14*/  SUN4M_INT_E14,
203 /*15*/  0x00000000
204 };
205
206 /* We assume the caller has disabled local interrupts when these are called,
207  * or else very bizarre behavior will result.
208  */
209 static void sun4m_disable_pil_irq(unsigned int pil)
210 {
211         sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set);
212 }
213
214 static void sun4m_enable_pil_irq(unsigned int pil)
215 {
216         sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear);
217 }
218
219 #ifdef CONFIG_SMP
220 static void sun4m_send_ipi(int cpu, int level)
221 {
222         unsigned long mask = sun4m_get_irqmask(level);
223         sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
224 }
225
226 static void sun4m_clear_ipi(int cpu, int level)
227 {
228         unsigned long mask = sun4m_get_irqmask(level);
229         sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
230 }
231
232 static void sun4m_set_udt(int cpu)
233 {
234         sbus_writel(cpu, &sun4m_irq_global->interrupt_target);
235 }
236 #endif
237
238 struct sun4m_timer_percpu {
239         u32             l14_limit;
240         u32             l14_count;
241         u32             l14_limit_noclear;
242         u32             user_timer_start_stop;
243 };
244
245 static struct sun4m_timer_percpu __iomem *timers_percpu[SUN4M_NCPUS];
246
247 struct sun4m_timer_global {
248         u32             l10_limit;
249         u32             l10_count;
250         u32             l10_limit_noclear;
251         u32             reserved;
252         u32             timer_config;
253 };
254
255 static struct sun4m_timer_global __iomem *timers_global;
256
257 #define OBIO_INTR       0x20
258 #define TIMER_IRQ       (OBIO_INTR | 10)
259
260 unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10);
261
262 static void sun4m_clear_clock_irq(void)
263 {
264         sbus_readl(&timers_global->l10_limit);
265 }
266
267 /* Exported for sun4m_smp.c */
268 void sun4m_clear_profile_irq(int cpu)
269 {
270         sbus_readl(&timers_percpu[cpu]->l14_limit);
271 }
272
273 static void sun4m_load_profile_irq(int cpu, unsigned int limit)
274 {
275         sbus_writel(limit, &timers_percpu[cpu]->l14_limit);
276 }
277
278 static void __init sun4m_init_timers(irq_handler_t counter_fn)
279 {
280         struct device_node *dp = of_find_node_by_name(NULL, "counter");
281         int i, err, len, num_cpu_timers;
282         const u32 *addr;
283
284         if (!dp) {
285                 printk(KERN_ERR "sun4m_init_timers: No 'counter' node.\n");
286                 return;
287         }
288
289         addr = of_get_property(dp, "address", &len);
290         if (!addr) {
291                 printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n");
292                 return;
293         }
294
295         num_cpu_timers = (len / sizeof(u32)) - 1;
296         for (i = 0; i < num_cpu_timers; i++) {
297                 timers_percpu[i] = (void __iomem *)
298                         (unsigned long) addr[i];
299         }
300         timers_global = (void __iomem *)
301                 (unsigned long) addr[num_cpu_timers];
302
303         sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit);
304
305         master_l10_counter = &timers_global->l10_count;
306
307         err = request_irq(TIMER_IRQ, counter_fn,
308                           (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL);
309         if (err) {
310                 printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n",
311                         err);
312                 return;
313         }
314
315         for (i = 0; i < num_cpu_timers; i++)
316                 sbus_writel(0, &timers_percpu[i]->l14_limit);
317         if (num_cpu_timers == 4)
318                 sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set);
319
320 #ifdef CONFIG_SMP
321         {
322                 unsigned long flags;
323                 extern unsigned long lvl14_save[4];
324                 struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
325
326                 /* For SMP we use the level 14 ticker, however the bootup code
327                  * has copied the firmware's level 14 vector into the boot cpu's
328                  * trap table, we must fix this now or we get squashed.
329                  */
330                 local_irq_save(flags);
331                 trap_table->inst_one = lvl14_save[0];
332                 trap_table->inst_two = lvl14_save[1];
333                 trap_table->inst_three = lvl14_save[2];
334                 trap_table->inst_four = lvl14_save[3];
335                 local_flush_cache_all();
336                 local_irq_restore(flags);
337         }
338 #endif
339 }
340
341 void __init sun4m_init_IRQ(void)
342 {
343         struct device_node *dp = of_find_node_by_name(NULL, "interrupt");
344         int len, i, mid, num_cpu_iregs;
345         const u32 *addr;
346
347         if (!dp) {
348                 printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n");
349                 return;
350         }
351
352         addr = of_get_property(dp, "address", &len);
353         if (!addr) {
354                 printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n");
355                 return;
356         }
357
358         num_cpu_iregs = (len / sizeof(u32)) - 1;
359         for (i = 0; i < num_cpu_iregs; i++) {
360                 sun4m_irq_percpu[i] = (void __iomem *)
361                         (unsigned long) addr[i];
362         }
363         sun4m_irq_global = (void __iomem *)
364                 (unsigned long) addr[num_cpu_iregs];
365
366         local_irq_disable();
367
368         sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set);
369         for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
370                 sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear);
371
372         if (num_cpu_iregs == 4) {
373                 irq_rcvreg = (unsigned long *) &sun4m_irq_global->interrupt_target;
374                 sbus_writel(0, &sun4m_irq_global->interrupt_target);
375         }
376         BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
377         BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
378         BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
379         BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
380         BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
381         BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
382         sparc_init_timers = sun4m_init_timers;
383 #ifdef CONFIG_SMP
384         BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
385         BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
386         BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
387 #endif
388
389         /* Cannot enable interrupts until OBP ticker is disabled. */
390 }