Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* sun4c_irq.c |
2 | * arch/sparc/kernel/sun4c_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 | ||
1da177e4 LT |
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/interrupt.h> | |
19 | #include <linux/slab.h> | |
20 | #include <linux/init.h> | |
21 | ||
22 | #include <asm/ptrace.h> | |
23 | #include <asm/processor.h> | |
24 | #include <asm/system.h> | |
25 | #include <asm/psr.h> | |
26 | #include <asm/vaddrs.h> | |
27 | #include <asm/timer.h> | |
28 | #include <asm/openprom.h> | |
29 | #include <asm/oplib.h> | |
30 | #include <asm/traps.h> | |
31 | #include <asm/irq.h> | |
32 | #include <asm/io.h> | |
33 | #include <asm/sun4paddr.h> | |
34 | #include <asm/idprom.h> | |
35 | #include <asm/machines.h> | |
36 | #include <asm/sbus.h> | |
37 | ||
38 | #if 0 | |
39 | static struct resource sun4c_timer_eb = { "sun4c_timer" }; | |
40 | static struct resource sun4c_intr_eb = { "sun4c_intr" }; | |
41 | #endif | |
42 | ||
43 | /* Pointer to the interrupt enable byte | |
44 | * | |
45 | * Dave Redman (djhr@tadpole.co.uk) | |
46 | * What you may not be aware of is that entry.S requires this variable. | |
47 | * | |
48 | * --- linux_trap_nmi_sun4c -- | |
49 | * | |
50 | * so don't go making it static, like I tried. sigh. | |
51 | */ | |
52 | unsigned char *interrupt_enable = NULL; | |
53 | ||
54 | static int sun4c_pil_map[] = { 0, 1, 2, 3, 5, 7, 8, 9 }; | |
55 | ||
56 | unsigned int sun4c_sbint_to_irq(struct sbus_dev *sdev, unsigned int sbint) | |
57 | { | |
58 | if (sbint >= sizeof(sun4c_pil_map)) { | |
59 | printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); | |
60 | BUG(); | |
61 | } | |
62 | return sun4c_pil_map[sbint]; | |
63 | } | |
64 | ||
65 | static void sun4c_disable_irq(unsigned int irq_nr) | |
66 | { | |
67 | unsigned long flags; | |
68 | unsigned char current_mask, new_mask; | |
69 | ||
70 | local_irq_save(flags); | |
71 | irq_nr &= (NR_IRQS - 1); | |
72 | current_mask = *interrupt_enable; | |
73 | switch(irq_nr) { | |
74 | case 1: | |
75 | new_mask = ((current_mask) & (~(SUN4C_INT_E1))); | |
76 | break; | |
77 | case 8: | |
78 | new_mask = ((current_mask) & (~(SUN4C_INT_E8))); | |
79 | break; | |
80 | case 10: | |
81 | new_mask = ((current_mask) & (~(SUN4C_INT_E10))); | |
82 | break; | |
83 | case 14: | |
84 | new_mask = ((current_mask) & (~(SUN4C_INT_E14))); | |
85 | break; | |
86 | default: | |
87 | local_irq_restore(flags); | |
88 | return; | |
89 | } | |
90 | *interrupt_enable = new_mask; | |
91 | local_irq_restore(flags); | |
92 | } | |
93 | ||
94 | static void sun4c_enable_irq(unsigned int irq_nr) | |
95 | { | |
96 | unsigned long flags; | |
97 | unsigned char current_mask, new_mask; | |
98 | ||
99 | local_irq_save(flags); | |
100 | irq_nr &= (NR_IRQS - 1); | |
101 | current_mask = *interrupt_enable; | |
102 | switch(irq_nr) { | |
103 | case 1: | |
104 | new_mask = ((current_mask) | SUN4C_INT_E1); | |
105 | break; | |
106 | case 8: | |
107 | new_mask = ((current_mask) | SUN4C_INT_E8); | |
108 | break; | |
109 | case 10: | |
110 | new_mask = ((current_mask) | SUN4C_INT_E10); | |
111 | break; | |
112 | case 14: | |
113 | new_mask = ((current_mask) | SUN4C_INT_E14); | |
114 | break; | |
115 | default: | |
116 | local_irq_restore(flags); | |
117 | return; | |
118 | } | |
119 | *interrupt_enable = new_mask; | |
120 | local_irq_restore(flags); | |
121 | } | |
122 | ||
123 | #define TIMER_IRQ 10 /* Also at level 14, but we ignore that one. */ | |
124 | #define PROFILE_IRQ 14 /* Level14 ticker.. used by OBP for polling */ | |
125 | ||
126 | volatile struct sun4c_timer_info *sun4c_timers; | |
127 | ||
128 | #ifdef CONFIG_SUN4 | |
129 | /* This is an ugly hack to work around the | |
130 | current timer code, and make it work with | |
131 | the sun4/260 intersil | |
132 | */ | |
133 | volatile struct sun4c_timer_info sun4_timer; | |
134 | #endif | |
135 | ||
136 | static void sun4c_clear_clock_irq(void) | |
137 | { | |
138 | volatile unsigned int clear_intr; | |
139 | #ifdef CONFIG_SUN4 | |
140 | if (idprom->id_machtype == (SM_SUN4 | SM_4_260)) | |
141 | clear_intr = sun4_timer.timer_limit10; | |
142 | else | |
143 | #endif | |
144 | clear_intr = sun4c_timers->timer_limit10; | |
145 | } | |
146 | ||
147 | static void sun4c_clear_profile_irq(int cpu) | |
148 | { | |
149 | /* Errm.. not sure how to do this.. */ | |
150 | } | |
151 | ||
152 | static void sun4c_load_profile_irq(int cpu, unsigned int limit) | |
153 | { | |
154 | /* Errm.. not sure how to do this.. */ | |
155 | } | |
156 | ||
157 | static void __init sun4c_init_timers(irqreturn_t (*counter_fn)(int, void *, struct pt_regs *)) | |
158 | { | |
159 | int irq; | |
160 | ||
161 | /* Map the Timer chip, this is implemented in hardware inside | |
162 | * the cache chip on the sun4c. | |
163 | */ | |
164 | #ifdef CONFIG_SUN4 | |
165 | if (idprom->id_machtype == (SM_SUN4 | SM_4_260)) | |
166 | sun4c_timers = &sun4_timer; | |
167 | else | |
168 | #endif | |
169 | sun4c_timers = ioremap(SUN_TIMER_PHYSADDR, | |
170 | sizeof(struct sun4c_timer_info)); | |
171 | ||
172 | /* Have the level 10 timer tick at 100HZ. We don't touch the | |
173 | * level 14 timer limit since we are letting the prom handle | |
174 | * them until we have a real console driver so L1-A works. | |
175 | */ | |
176 | sun4c_timers->timer_limit10 = (((1000000/HZ) + 1) << 10); | |
177 | master_l10_counter = &sun4c_timers->cur_count10; | |
178 | master_l10_limit = &sun4c_timers->timer_limit10; | |
179 | ||
180 | irq = request_irq(TIMER_IRQ, | |
181 | counter_fn, | |
182 | (SA_INTERRUPT | SA_STATIC_ALLOC), | |
183 | "timer", NULL); | |
184 | if (irq) { | |
185 | prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); | |
186 | prom_halt(); | |
187 | } | |
188 | ||
189 | #if 0 | |
190 | /* This does not work on 4/330 */ | |
191 | sun4c_enable_irq(10); | |
192 | #endif | |
193 | claim_ticker14(NULL, PROFILE_IRQ, 0); | |
194 | } | |
195 | ||
196 | #ifdef CONFIG_SMP | |
197 | static void sun4c_nop(void) {} | |
198 | #endif | |
199 | ||
1da177e4 LT |
200 | void __init sun4c_init_IRQ(void) |
201 | { | |
202 | struct linux_prom_registers int_regs[2]; | |
203 | int ie_node; | |
204 | ||
205 | if (ARCH_SUN4) { | |
206 | interrupt_enable = (char *) | |
207 | ioremap(sun4_ie_physaddr, PAGE_SIZE); | |
208 | } else { | |
209 | struct resource phyres; | |
210 | ||
211 | ie_node = prom_searchsiblings (prom_getchild(prom_root_node), | |
212 | "interrupt-enable"); | |
213 | if(ie_node == 0) | |
214 | panic("Cannot find /interrupt-enable node"); | |
215 | ||
216 | /* Depending on the "address" property is bad news... */ | |
217 | interrupt_enable = NULL; | |
218 | if (prom_getproperty(ie_node, "reg", (char *) int_regs, | |
219 | sizeof(int_regs)) != -1) { | |
220 | memset(&phyres, 0, sizeof(struct resource)); | |
221 | phyres.flags = int_regs[0].which_io; | |
222 | phyres.start = int_regs[0].phys_addr; | |
223 | interrupt_enable = (char *) sbus_ioremap(&phyres, 0, | |
224 | int_regs[0].reg_size, "sun4c_intr"); | |
225 | } | |
226 | } | |
227 | if (!interrupt_enable) | |
228 | panic("Cannot map interrupt_enable"); | |
229 | ||
230 | BTFIXUPSET_CALL(sbint_to_irq, sun4c_sbint_to_irq, BTFIXUPCALL_NORM); | |
231 | BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); | |
232 | BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); | |
233 | BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); | |
234 | BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); | |
235 | BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM); | |
236 | BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP); | |
237 | BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP); | |
1da177e4 LT |
238 | sparc_init_timers = sun4c_init_timers; |
239 | #ifdef CONFIG_SMP | |
240 | BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); | |
241 | BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); | |
242 | BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP); | |
243 | #endif | |
244 | *interrupt_enable = (SUN4C_INT_ENABLE); | |
245 | /* Cannot enable interrupts until OBP ticker is disabled. */ | |
246 | } |