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); |
