Commit | Line | Data |
---|---|---|
e787098c SR |
1 | /* |
2 | * sun4c irq support | |
1da177e4 LT |
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/init.h> |
13 | ||
1da177e4 | 14 | #include <asm/oplib.h> |
e787098c | 15 | #include <asm/timer.h> |
1da177e4 LT |
16 | #include <asm/irq.h> |
17 | #include <asm/io.h> | |
e787098c SR |
18 | |
19 | #include "irq.h" | |
20 | ||
21 | /* Sun4c interrupts are typically laid out as follows: | |
22 | * | |
23 | * 1 - Software interrupt, SBUS level 1 | |
24 | * 2 - SBUS level 2 | |
25 | * 3 - ESP SCSI, SBUS level 3 | |
26 | * 4 - Software interrupt | |
27 | * 5 - Lance ethernet, SBUS level 4 | |
28 | * 6 - Software interrupt | |
29 | * 7 - Graphics card, SBUS level 5 | |
30 | * 8 - SBUS level 6 | |
31 | * 9 - SBUS level 7 | |
32 | * 10 - Counter timer | |
33 | * 11 - Floppy | |
34 | * 12 - Zilog uart | |
35 | * 13 - CS4231 audio | |
36 | * 14 - Profiling timer | |
37 | * 15 - NMI | |
38 | * | |
39 | * The interrupt enable bits in the interrupt mask register are | |
40 | * really only used to enable/disable the timer interrupts, and | |
41 | * for signalling software interrupts. There is also a master | |
42 | * interrupt enable bit in this register. | |
43 | * | |
44 | * Interrupts are enabled by setting the SUN4C_INT_* bits, they | |
45 | * are disabled by clearing those bits. | |
46 | */ | |
1da177e4 | 47 | |
32231a66 AV |
48 | /* |
49 | * Bit field defines for the interrupt registers on various | |
50 | * Sparc machines. | |
51 | */ | |
52 | ||
53 | /* The sun4c interrupt register. */ | |
54 | #define SUN4C_INT_ENABLE 0x01 /* Allow interrupts. */ | |
55 | #define SUN4C_INT_E14 0x80 /* Enable level 14 IRQ. */ | |
56 | #define SUN4C_INT_E10 0x20 /* Enable level 10 IRQ. */ | |
57 | #define SUN4C_INT_E8 0x10 /* Enable level 8 IRQ. */ | |
58 | #define SUN4C_INT_E6 0x08 /* Enable level 6 IRQ. */ | |
59 | #define SUN4C_INT_E4 0x04 /* Enable level 4 IRQ. */ | |
60 | #define SUN4C_INT_E1 0x02 /* Enable level 1 IRQ. */ | |
61 | ||
e787098c SR |
62 | /* |
63 | * Pointer to the interrupt enable byte | |
64 | * Used by entry.S | |
1da177e4 | 65 | */ |
e787098c | 66 | unsigned char __iomem *interrupt_enable; |
1da177e4 | 67 | |
6baa9b20 | 68 | static void sun4c_mask_irq(struct irq_data *data) |
1da177e4 | 69 | { |
6baa9b20 SR |
70 | unsigned long mask = (unsigned long)data->chip_data; |
71 | ||
72 | if (mask) { | |
73 | unsigned long flags; | |
74 | ||
75 | local_irq_save(flags); | |
76 | mask = sbus_readb(interrupt_enable) & ~mask; | |
77 | sbus_writeb(mask, interrupt_enable); | |
1da177e4 | 78 | local_irq_restore(flags); |
1da177e4 | 79 | } |
1da177e4 LT |
80 | } |
81 | ||
6baa9b20 | 82 | static void sun4c_unmask_irq(struct irq_data *data) |
1da177e4 | 83 | { |
6baa9b20 SR |
84 | unsigned long mask = (unsigned long)data->chip_data; |
85 | ||
86 | if (mask) { | |
87 | unsigned long flags; | |
88 | ||
89 | local_irq_save(flags); | |
90 | mask = sbus_readb(interrupt_enable) | mask; | |
91 | sbus_writeb(mask, interrupt_enable); | |
1da177e4 | 92 | local_irq_restore(flags); |
1da177e4 | 93 | } |
6baa9b20 SR |
94 | } |
95 | ||
96 | static unsigned int sun4c_startup_irq(struct irq_data *data) | |
97 | { | |
98 | irq_link(data->irq); | |
99 | sun4c_unmask_irq(data); | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | static void sun4c_shutdown_irq(struct irq_data *data) | |
105 | { | |
106 | sun4c_mask_irq(data); | |
107 | irq_unlink(data->irq); | |
108 | } | |
109 | ||
110 | static struct irq_chip sun4c_irq = { | |
111 | .name = "sun4c", | |
112 | .irq_startup = sun4c_startup_irq, | |
113 | .irq_shutdown = sun4c_shutdown_irq, | |
114 | .irq_mask = sun4c_mask_irq, | |
115 | .irq_unmask = sun4c_unmask_irq, | |
116 | }; | |
117 | ||
118 | static unsigned int sun4c_build_device_irq(struct platform_device *op, | |
119 | unsigned int real_irq) | |
120 | { | |
121 | unsigned int irq; | |
122 | ||
123 | if (real_irq >= 16) { | |
124 | prom_printf("Bogus sun4c IRQ %u\n", real_irq); | |
125 | prom_halt(); | |
126 | } | |
127 | ||
128 | irq = irq_alloc(real_irq, real_irq); | |
129 | if (irq) { | |
130 | unsigned long mask = 0UL; | |
131 | ||
132 | switch (real_irq) { | |
133 | case 1: | |
134 | mask = SUN4C_INT_E1; | |
135 | break; | |
136 | case 8: | |
137 | mask = SUN4C_INT_E8; | |
138 | break; | |
139 | case 10: | |
140 | mask = SUN4C_INT_E10; | |
141 | break; | |
142 | case 14: | |
143 | mask = SUN4C_INT_E14; | |
144 | break; | |
145 | default: | |
146 | /* All the rest are either always enabled, | |
147 | * or are for signalling software interrupts. | |
148 | */ | |
149 | break; | |
150 | } | |
151 | irq_set_chip_and_handler_name(irq, &sun4c_irq, | |
152 | handle_level_irq, "level"); | |
153 | irq_set_chip_data(irq, (void *)mask); | |
154 | } | |
155 | return irq; | |
1da177e4 LT |
156 | } |
157 | ||
8bd8deea DM |
158 | struct sun4c_timer_info { |
159 | u32 l10_count; | |
160 | u32 l10_limit; | |
161 | u32 l14_count; | |
162 | u32 l14_limit; | |
163 | }; | |
1da177e4 | 164 | |
8bd8deea | 165 | static struct sun4c_timer_info __iomem *sun4c_timers; |
1da177e4 | 166 | |
1da177e4 LT |
167 | static void sun4c_clear_clock_irq(void) |
168 | { | |
8bd8deea | 169 | sbus_readl(&sun4c_timers->l10_limit); |
1da177e4 LT |
170 | } |
171 | ||
1da177e4 LT |
172 | static void sun4c_load_profile_irq(int cpu, unsigned int limit) |
173 | { | |
174 | /* Errm.. not sure how to do this.. */ | |
175 | } | |
176 | ||
62f08283 | 177 | static void __init sun4c_init_timers(void) |
1da177e4 | 178 | { |
6baa9b20 | 179 | const struct linux_prom_irqs *prom_irqs; |
8bd8deea | 180 | struct device_node *dp; |
6baa9b20 | 181 | unsigned int irq; |
8bd8deea DM |
182 | const u32 *addr; |
183 | int err; | |
1da177e4 | 184 | |
8bd8deea DM |
185 | dp = of_find_node_by_name(NULL, "counter-timer"); |
186 | if (!dp) { | |
187 | prom_printf("sun4c_init_timers: Unable to find counter-timer\n"); | |
188 | prom_halt(); | |
189 | } | |
190 | ||
191 | addr = of_get_property(dp, "address", NULL); | |
192 | if (!addr) { | |
193 | prom_printf("sun4c_init_timers: No address property\n"); | |
194 | prom_halt(); | |
195 | } | |
196 | ||
197 | sun4c_timers = (void __iomem *) (unsigned long) addr[0]; | |
198 | ||
6baa9b20 | 199 | prom_irqs = of_get_property(dp, "intr", NULL); |
c2e27c35 | 200 | of_node_put(dp); |
6baa9b20 | 201 | if (!prom_irqs) { |
8bd8deea DM |
202 | prom_printf("sun4c_init_timers: No intr property\n"); |
203 | prom_halt(); | |
204 | } | |
1da177e4 LT |
205 | |
206 | /* Have the level 10 timer tick at 100HZ. We don't touch the | |
207 | * level 14 timer limit since we are letting the prom handle | |
208 | * them until we have a real console driver so L1-A works. | |
209 | */ | |
62f08283 TK |
210 | sparc_config.cs_period = SBUS_CLOCK_RATE / HZ; |
211 | sparc_config.features |= | |
212 | FEAT_L10_CLOCKSOURCE | FEAT_L10_CLOCKEVENT; | |
213 | sbus_writel(timer_value(sparc_config.cs_period), | |
214 | &sun4c_timers->l10_limit); | |
8bd8deea DM |
215 | |
216 | master_l10_counter = &sun4c_timers->l10_count; | |
1da177e4 | 217 | |
6baa9b20 | 218 | irq = sun4c_build_device_irq(NULL, prom_irqs[0].pri); |
62f08283 | 219 | err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); |
8bd8deea DM |
220 | if (err) { |
221 | prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); | |
1da177e4 LT |
222 | prom_halt(); |
223 | } | |
e787098c | 224 | |
6baa9b20 SR |
225 | /* disable timer interrupt */ |
226 | sun4c_mask_irq(irq_get_irq_data(irq)); | |
1da177e4 LT |
227 | } |
228 | ||
229 | #ifdef CONFIG_SMP | |
e787098c SR |
230 | static void sun4c_nop(void) |
231 | { | |
232 | } | |
1da177e4 LT |
233 | #endif |
234 | ||
1da177e4 LT |
235 | void __init sun4c_init_IRQ(void) |
236 | { | |
45bb5a7c DM |
237 | struct device_node *dp; |
238 | const u32 *addr; | |
239 | ||
240 | dp = of_find_node_by_name(NULL, "interrupt-enable"); | |
241 | if (!dp) { | |
242 | prom_printf("sun4c_init_IRQ: Unable to find interrupt-enable\n"); | |
243 | prom_halt(); | |
244 | } | |
245 | ||
246 | addr = of_get_property(dp, "address", NULL); | |
c2e27c35 | 247 | of_node_put(dp); |
45bb5a7c DM |
248 | if (!addr) { |
249 | prom_printf("sun4c_init_IRQ: No address property\n"); | |
250 | prom_halt(); | |
1da177e4 | 251 | } |
45bb5a7c DM |
252 | |
253 | interrupt_enable = (void __iomem *) (unsigned long) addr[0]; | |
1da177e4 | 254 | |
1da177e4 | 255 | BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM); |
1da177e4 | 256 | BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP); |
bbdc2661 | 257 | |
472bc4f2 SR |
258 | sparc_config.init_timers = sun4c_init_timers; |
259 | sparc_config.build_device_irq = sun4c_build_device_irq; | |
62f08283 | 260 | sparc_config.clock_rate = SBUS_CLOCK_RATE; |
bbdc2661 | 261 | |
1da177e4 LT |
262 | #ifdef CONFIG_SMP |
263 | BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); | |
264 | BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); | |
265 | BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP); | |
266 | #endif | |
45bb5a7c | 267 | sbus_writeb(SUN4C_INT_ENABLE, interrupt_enable); |
1da177e4 LT |
268 | /* Cannot enable interrupts until OBP ticker is disabled. */ |
269 | } |