diff options
Diffstat (limited to 'arch/sparc/kernel/sun4m_irq.c')
-rw-r--r-- | arch/sparc/kernel/sun4m_irq.c | 179 |
1 files changed, 98 insertions, 81 deletions
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 69df6257a32e..422c16dad1f6 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c | |||
@@ -100,6 +100,11 @@ | |||
100 | struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; | 100 | struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; |
101 | struct sun4m_irq_global __iomem *sun4m_irq_global; | 101 | struct sun4m_irq_global __iomem *sun4m_irq_global; |
102 | 102 | ||
103 | struct sun4m_handler_data { | ||
104 | bool percpu; | ||
105 | long mask; | ||
106 | }; | ||
107 | |||
103 | /* Dave Redman (djhr@tadpole.co.uk) | 108 | /* Dave Redman (djhr@tadpole.co.uk) |
104 | * The sun4m interrupt registers. | 109 | * The sun4m interrupt registers. |
105 | */ | 110 | */ |
@@ -142,9 +147,9 @@ struct sun4m_irq_global __iomem *sun4m_irq_global; | |||
142 | #define OBP_INT_LEVEL_VME 0x40 | 147 | #define OBP_INT_LEVEL_VME 0x40 |
143 | 148 | ||
144 | #define SUN4M_TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10) | 149 | #define SUN4M_TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10) |
145 | #define SUM4M_PROFILE_IRQ (OBP_INT_LEVEL_ONBOARD | 14) | 150 | #define SUN4M_PROFILE_IRQ (OBP_INT_LEVEL_ONBOARD | 14) |
146 | 151 | ||
147 | static unsigned long irq_mask[0x50] = { | 152 | static unsigned long sun4m_imask[0x50] = { |
148 | /* 0x00 - SMP */ | 153 | /* 0x00 - SMP */ |
149 | 0, SUN4M_SOFT_INT(1), | 154 | 0, SUN4M_SOFT_INT(1), |
150 | SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), | 155 | SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), |
@@ -169,7 +174,7 @@ static unsigned long irq_mask[0x50] = { | |||
169 | SUN4M_INT_VIDEO, SUN4M_INT_MODULE, | 174 | SUN4M_INT_VIDEO, SUN4M_INT_MODULE, |
170 | SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY, | 175 | SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY, |
171 | (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), | 176 | (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), |
172 | SUN4M_INT_AUDIO, 0, SUN4M_INT_MODULE_ERR, | 177 | SUN4M_INT_AUDIO, SUN4M_INT_E14, SUN4M_INT_MODULE_ERR, |
173 | /* 0x30 - sbus */ | 178 | /* 0x30 - sbus */ |
174 | 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1), | 179 | 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1), |
175 | 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3), | 180 | 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3), |
@@ -182,105 +187,110 @@ static unsigned long irq_mask[0x50] = { | |||
182 | 0, SUN4M_INT_VME(6), 0, 0 | 187 | 0, SUN4M_INT_VME(6), 0, 0 |
183 | }; | 188 | }; |
184 | 189 | ||
185 | static unsigned long sun4m_get_irqmask(unsigned int irq) | 190 | static void sun4m_mask_irq(struct irq_data *data) |
186 | { | 191 | { |
187 | unsigned long mask; | 192 | struct sun4m_handler_data *handler_data = data->handler_data; |
188 | 193 | int cpu = smp_processor_id(); | |
189 | if (irq < 0x50) | ||
190 | mask = irq_mask[irq]; | ||
191 | else | ||
192 | mask = 0; | ||
193 | 194 | ||
194 | if (!mask) | 195 | if (handler_data->mask) { |
195 | printk(KERN_ERR "sun4m_get_irqmask: IRQ%d has no valid mask!\n", | 196 | unsigned long flags; |
196 | irq); | ||
197 | 197 | ||
198 | return mask; | 198 | local_irq_save(flags); |
199 | if (handler_data->percpu) { | ||
200 | sbus_writel(handler_data->mask, &sun4m_irq_percpu[cpu]->set); | ||
201 | } else { | ||
202 | sbus_writel(handler_data->mask, &sun4m_irq_global->mask_set); | ||
203 | } | ||
204 | local_irq_restore(flags); | ||
205 | } | ||
199 | } | 206 | } |
200 | 207 | ||
201 | static void sun4m_disable_irq(unsigned int irq_nr) | 208 | static void sun4m_unmask_irq(struct irq_data *data) |
202 | { | 209 | { |
203 | unsigned long mask, flags; | 210 | struct sun4m_handler_data *handler_data = data->handler_data; |
204 | int cpu = smp_processor_id(); | 211 | int cpu = smp_processor_id(); |
205 | 212 | ||
206 | mask = sun4m_get_irqmask(irq_nr); | 213 | if (handler_data->mask) { |
207 | local_irq_save(flags); | 214 | unsigned long flags; |
208 | if (irq_nr > 15) | ||
209 | sbus_writel(mask, &sun4m_irq_global->mask_set); | ||
210 | else | ||
211 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); | ||
212 | local_irq_restore(flags); | ||
213 | } | ||
214 | |||
215 | static void sun4m_enable_irq(unsigned int irq_nr) | ||
216 | { | ||
217 | unsigned long mask, flags; | ||
218 | int cpu = smp_processor_id(); | ||
219 | 215 | ||
220 | /* Dreadful floppy hack. When we use 0x2b instead of | ||
221 | * 0x0b the system blows (it starts to whistle!). | ||
222 | * So we continue to use 0x0b. Fixme ASAP. --P3 | ||
223 | */ | ||
224 | if (irq_nr != 0x0b) { | ||
225 | mask = sun4m_get_irqmask(irq_nr); | ||
226 | local_irq_save(flags); | ||
227 | if (irq_nr > 15) | ||
228 | sbus_writel(mask, &sun4m_irq_global->mask_clear); | ||
229 | else | ||
230 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); | ||
231 | local_irq_restore(flags); | ||
232 | } else { | ||
233 | local_irq_save(flags); | 216 | local_irq_save(flags); |
234 | sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear); | 217 | if (handler_data->percpu) { |
218 | sbus_writel(handler_data->mask, &sun4m_irq_percpu[cpu]->clear); | ||
219 | } else { | ||
220 | sbus_writel(handler_data->mask, &sun4m_irq_global->mask_clear); | ||
221 | } | ||
235 | local_irq_restore(flags); | 222 | local_irq_restore(flags); |
236 | } | 223 | } |
237 | } | 224 | } |
238 | 225 | ||
239 | static unsigned long cpu_pil_to_imask[16] = { | 226 | static unsigned int sun4m_startup_irq(struct irq_data *data) |
240 | /*0*/ 0x00000000, | 227 | { |
241 | /*1*/ 0x00000000, | 228 | irq_link(data->irq); |
242 | /*2*/ SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0), | 229 | sun4m_unmask_irq(data); |
243 | /*3*/ SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1), | 230 | return 0; |
244 | /*4*/ SUN4M_INT_SCSI, | 231 | } |
245 | /*5*/ SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2), | ||
246 | /*6*/ SUN4M_INT_ETHERNET, | ||
247 | /*7*/ SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3), | ||
248 | /*8*/ SUN4M_INT_VIDEO, | ||
249 | /*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR, | ||
250 | /*10*/ SUN4M_INT_REALTIME, | ||
251 | /*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY, | ||
252 | /*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS, | ||
253 | /*13*/ SUN4M_INT_SBUS(6) | SUN4M_INT_VME(6) | SUN4M_INT_AUDIO, | ||
254 | /*14*/ SUN4M_INT_E14, | ||
255 | /*15*/ SUN4M_INT_ERROR, | ||
256 | }; | ||
257 | 232 | ||
258 | /* We assume the caller has disabled local interrupts when these are called, | 233 | static void sun4m_shutdown_irq(struct irq_data *data) |
259 | * or else very bizarre behavior will result. | ||
260 | */ | ||
261 | static void sun4m_disable_pil_irq(unsigned int pil) | ||
262 | { | 234 | { |
263 | sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set); | 235 | sun4m_mask_irq(data); |
236 | irq_unlink(data->irq); | ||
264 | } | 237 | } |
265 | 238 | ||
266 | static void sun4m_enable_pil_irq(unsigned int pil) | 239 | static struct irq_chip sun4m_irq = { |
240 | .name = "sun4m", | ||
241 | .irq_startup = sun4m_startup_irq, | ||
242 | .irq_shutdown = sun4m_shutdown_irq, | ||
243 | .irq_mask = sun4m_mask_irq, | ||
244 | .irq_unmask = sun4m_unmask_irq, | ||
245 | }; | ||
246 | |||
247 | |||
248 | static unsigned int sun4m_build_device_irq(struct platform_device *op, | ||
249 | unsigned int real_irq) | ||
267 | { | 250 | { |
268 | sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear); | 251 | struct sun4m_handler_data *handler_data; |
252 | unsigned int irq; | ||
253 | unsigned int pil; | ||
254 | |||
255 | if (real_irq >= OBP_INT_LEVEL_VME) { | ||
256 | prom_printf("Bogus sun4m IRQ %u\n", real_irq); | ||
257 | prom_halt(); | ||
258 | } | ||
259 | pil = (real_irq & 0xf); | ||
260 | irq = irq_alloc(real_irq, pil); | ||
261 | |||
262 | if (irq == 0) | ||
263 | goto out; | ||
264 | |||
265 | handler_data = irq_get_handler_data(irq); | ||
266 | if (unlikely(handler_data)) | ||
267 | goto out; | ||
268 | |||
269 | handler_data = kzalloc(sizeof(struct sun4m_handler_data), GFP_ATOMIC); | ||
270 | if (unlikely(!handler_data)) { | ||
271 | prom_printf("IRQ: kzalloc(sun4m_handler_data) failed.\n"); | ||
272 | prom_halt(); | ||
273 | } | ||
274 | |||
275 | handler_data->mask = sun4m_imask[real_irq]; | ||
276 | handler_data->percpu = real_irq < OBP_INT_LEVEL_ONBOARD; | ||
277 | irq_set_chip_and_handler_name(irq, &sun4m_irq, | ||
278 | handle_level_irq, "level"); | ||
279 | irq_set_handler_data(irq, handler_data); | ||
280 | |||
281 | out: | ||
282 | return irq; | ||
269 | } | 283 | } |
270 | 284 | ||
271 | #ifdef CONFIG_SMP | 285 | #ifdef CONFIG_SMP |
272 | static void sun4m_send_ipi(int cpu, int level) | 286 | static void sun4m_send_ipi(int cpu, int level) |
273 | { | 287 | { |
274 | unsigned long mask = sun4m_get_irqmask(level); | 288 | sbus_writel(SUN4M_SOFT_INT(level), &sun4m_irq_percpu[cpu]->set); |
275 | |||
276 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); | ||
277 | } | 289 | } |
278 | 290 | ||
279 | static void sun4m_clear_ipi(int cpu, int level) | 291 | static void sun4m_clear_ipi(int cpu, int level) |
280 | { | 292 | { |
281 | unsigned long mask = sun4m_get_irqmask(level); | 293 | sbus_writel(SUN4M_SOFT_INT(level), &sun4m_irq_percpu[cpu]->clear); |
282 | |||
283 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); | ||
284 | } | 294 | } |
285 | 295 | ||
286 | static void sun4m_set_udt(int cpu) | 296 | static void sun4m_set_udt(int cpu) |
@@ -343,7 +353,15 @@ void sun4m_nmi(struct pt_regs *regs) | |||
343 | prom_halt(); | 353 | prom_halt(); |
344 | } | 354 | } |
345 | 355 | ||
346 | /* Exported for sun4m_smp.c */ | 356 | void sun4m_unmask_profile_irq(void) |
357 | { | ||
358 | unsigned long flags; | ||
359 | |||
360 | local_irq_save(flags); | ||
361 | sbus_writel(sun4m_imask[SUN4M_PROFILE_IRQ], &sun4m_irq_global->mask_clear); | ||
362 | local_irq_restore(flags); | ||
363 | } | ||
364 | |||
347 | void sun4m_clear_profile_irq(int cpu) | 365 | void sun4m_clear_profile_irq(int cpu) |
348 | { | 366 | { |
349 | sbus_readl(&timers_percpu[cpu]->l14_limit); | 367 | sbus_readl(&timers_percpu[cpu]->l14_limit); |
@@ -358,6 +376,7 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) | |||
358 | { | 376 | { |
359 | struct device_node *dp = of_find_node_by_name(NULL, "counter"); | 377 | struct device_node *dp = of_find_node_by_name(NULL, "counter"); |
360 | int i, err, len, num_cpu_timers; | 378 | int i, err, len, num_cpu_timers; |
379 | unsigned int irq; | ||
361 | const u32 *addr; | 380 | const u32 *addr; |
362 | 381 | ||
363 | if (!dp) { | 382 | if (!dp) { |
@@ -384,8 +403,9 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) | |||
384 | 403 | ||
385 | master_l10_counter = &timers_global->l10_count; | 404 | master_l10_counter = &timers_global->l10_count; |
386 | 405 | ||
387 | err = request_irq(SUN4M_TIMER_IRQ, counter_fn, | 406 | irq = sun4m_build_device_irq(NULL, SUN4M_TIMER_IRQ); |
388 | (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); | 407 | |
408 | err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); | ||
389 | if (err) { | 409 | if (err) { |
390 | printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", | 410 | printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", |
391 | err); | 411 | err); |
@@ -452,14 +472,11 @@ void __init sun4m_init_IRQ(void) | |||
452 | if (num_cpu_iregs == 4) | 472 | if (num_cpu_iregs == 4) |
453 | sbus_writel(0, &sun4m_irq_global->interrupt_target); | 473 | sbus_writel(0, &sun4m_irq_global->interrupt_target); |
454 | 474 | ||
455 | BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); | ||
456 | BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); | ||
457 | BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM); | ||
458 | BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM); | ||
459 | BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); | 475 | BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); |
460 | BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); | 476 | BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); |
461 | 477 | ||
462 | sparc_irq_config.init_timers = sun4m_init_timers; | 478 | sparc_irq_config.init_timers = sun4m_init_timers; |
479 | sparc_irq_config.build_device_irq = sun4m_build_device_irq; | ||
463 | 480 | ||
464 | #ifdef CONFIG_SMP | 481 | #ifdef CONFIG_SMP |
465 | BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM); | 482 | BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM); |