diff options
Diffstat (limited to 'arch/sparc64/kernel/irq.c')
-rw-r--r-- | arch/sparc64/kernel/irq.c | 146 |
1 files changed, 115 insertions, 31 deletions
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index c3d068c7a412..c443db184371 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/seq_file.h> | 22 | #include <linux/seq_file.h> |
23 | #include <linux/bootmem.h> | 23 | #include <linux/bootmem.h> |
24 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
25 | #include <linux/msi.h> | ||
25 | 26 | ||
26 | #include <asm/ptrace.h> | 27 | #include <asm/ptrace.h> |
27 | #include <asm/processor.h> | 28 | #include <asm/processor.h> |
@@ -87,7 +88,6 @@ struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (SMP_CACHE_BY | |||
87 | #define irq_work(__cpu) &(trap_block[(__cpu)].irq_worklist) | 88 | #define irq_work(__cpu) &(trap_block[(__cpu)].irq_worklist) |
88 | 89 | ||
89 | static unsigned int virt_to_real_irq_table[NR_IRQS]; | 90 | static unsigned int virt_to_real_irq_table[NR_IRQS]; |
90 | static unsigned char virt_irq_cur = 1; | ||
91 | 91 | ||
92 | static unsigned char virt_irq_alloc(unsigned int real_irq) | 92 | static unsigned char virt_irq_alloc(unsigned int real_irq) |
93 | { | 93 | { |
@@ -95,24 +95,32 @@ static unsigned char virt_irq_alloc(unsigned int real_irq) | |||
95 | 95 | ||
96 | BUILD_BUG_ON(NR_IRQS >= 256); | 96 | BUILD_BUG_ON(NR_IRQS >= 256); |
97 | 97 | ||
98 | ent = virt_irq_cur; | 98 | for (ent = 1; ent < NR_IRQS; ent++) { |
99 | if (!virt_to_real_irq_table[ent]) | ||
100 | break; | ||
101 | } | ||
99 | if (ent >= NR_IRQS) { | 102 | if (ent >= NR_IRQS) { |
100 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); | 103 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); |
101 | return 0; | 104 | return 0; |
102 | } | 105 | } |
103 | 106 | ||
104 | virt_irq_cur = ent + 1; | ||
105 | virt_to_real_irq_table[ent] = real_irq; | 107 | virt_to_real_irq_table[ent] = real_irq; |
106 | 108 | ||
107 | return ent; | 109 | return ent; |
108 | } | 110 | } |
109 | 111 | ||
110 | #if 0 /* Currently unused. */ | 112 | #ifdef CONFIG_PCI_MSI |
111 | static unsigned char real_to_virt_irq(unsigned int real_irq) | 113 | static void virt_irq_free(unsigned int virt_irq) |
112 | { | 114 | { |
113 | struct ino_bucket *bucket = __bucket(real_irq); | 115 | unsigned int real_irq; |
114 | 116 | ||
115 | return bucket->virt_irq; | 117 | if (virt_irq >= NR_IRQS) |
118 | return; | ||
119 | |||
120 | real_irq = virt_to_real_irq_table[virt_irq]; | ||
121 | virt_to_real_irq_table[virt_irq] = 0; | ||
122 | |||
123 | __bucket(real_irq)->virt_irq = 0; | ||
116 | } | 124 | } |
117 | #endif | 125 | #endif |
118 | 126 | ||
@@ -268,8 +276,7 @@ static int irq_choose_cpu(unsigned int virt_irq) | |||
268 | 276 | ||
269 | static void sun4u_irq_enable(unsigned int virt_irq) | 277 | static void sun4u_irq_enable(unsigned int virt_irq) |
270 | { | 278 | { |
271 | irq_desc_t *desc = irq_desc + virt_irq; | 279 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
272 | struct irq_handler_data *data = desc->handler_data; | ||
273 | 280 | ||
274 | if (likely(data)) { | 281 | if (likely(data)) { |
275 | unsigned long cpuid, imap; | 282 | unsigned long cpuid, imap; |
@@ -286,8 +293,7 @@ static void sun4u_irq_enable(unsigned int virt_irq) | |||
286 | 293 | ||
287 | static void sun4u_irq_disable(unsigned int virt_irq) | 294 | static void sun4u_irq_disable(unsigned int virt_irq) |
288 | { | 295 | { |
289 | irq_desc_t *desc = irq_desc + virt_irq; | 296 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
290 | struct irq_handler_data *data = desc->handler_data; | ||
291 | 297 | ||
292 | if (likely(data)) { | 298 | if (likely(data)) { |
293 | unsigned long imap = data->imap; | 299 | unsigned long imap = data->imap; |
@@ -300,8 +306,7 @@ static void sun4u_irq_disable(unsigned int virt_irq) | |||
300 | 306 | ||
301 | static void sun4u_irq_end(unsigned int virt_irq) | 307 | static void sun4u_irq_end(unsigned int virt_irq) |
302 | { | 308 | { |
303 | irq_desc_t *desc = irq_desc + virt_irq; | 309 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
304 | struct irq_handler_data *data = desc->handler_data; | ||
305 | 310 | ||
306 | if (likely(data)) | 311 | if (likely(data)) |
307 | upa_writel(ICLR_IDLE, data->iclr); | 312 | upa_writel(ICLR_IDLE, data->iclr); |
@@ -344,6 +349,20 @@ static void sun4v_irq_disable(unsigned int virt_irq) | |||
344 | } | 349 | } |
345 | } | 350 | } |
346 | 351 | ||
352 | #ifdef CONFIG_PCI_MSI | ||
353 | static void sun4v_msi_enable(unsigned int virt_irq) | ||
354 | { | ||
355 | sun4v_irq_enable(virt_irq); | ||
356 | unmask_msi_irq(virt_irq); | ||
357 | } | ||
358 | |||
359 | static void sun4v_msi_disable(unsigned int virt_irq) | ||
360 | { | ||
361 | mask_msi_irq(virt_irq); | ||
362 | sun4v_irq_disable(virt_irq); | ||
363 | } | ||
364 | #endif | ||
365 | |||
347 | static void sun4v_irq_end(unsigned int virt_irq) | 366 | static void sun4v_irq_end(unsigned int virt_irq) |
348 | { | 367 | { |
349 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 368 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
@@ -362,8 +381,7 @@ static void sun4v_irq_end(unsigned int virt_irq) | |||
362 | static void run_pre_handler(unsigned int virt_irq) | 381 | static void run_pre_handler(unsigned int virt_irq) |
363 | { | 382 | { |
364 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 383 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
365 | irq_desc_t *desc = irq_desc + virt_irq; | 384 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
366 | struct irq_handler_data *data = desc->handler_data; | ||
367 | 385 | ||
368 | if (likely(data->pre_handler)) { | 386 | if (likely(data->pre_handler)) { |
369 | data->pre_handler(__irq_ino(__irq(bucket)), | 387 | data->pre_handler(__irq_ino(__irq(bucket)), |
@@ -402,30 +420,47 @@ static struct irq_chip sun4v_irq_ack = { | |||
402 | .end = sun4v_irq_end, | 420 | .end = sun4v_irq_end, |
403 | }; | 421 | }; |
404 | 422 | ||
423 | #ifdef CONFIG_PCI_MSI | ||
424 | static struct irq_chip sun4v_msi = { | ||
425 | .typename = "sun4v+msi", | ||
426 | .mask = mask_msi_irq, | ||
427 | .unmask = unmask_msi_irq, | ||
428 | .enable = sun4v_msi_enable, | ||
429 | .disable = sun4v_msi_disable, | ||
430 | .ack = run_pre_handler, | ||
431 | .end = sun4v_irq_end, | ||
432 | }; | ||
433 | #endif | ||
434 | |||
405 | void irq_install_pre_handler(int virt_irq, | 435 | void irq_install_pre_handler(int virt_irq, |
406 | void (*func)(unsigned int, void *, void *), | 436 | void (*func)(unsigned int, void *, void *), |
407 | void *arg1, void *arg2) | 437 | void *arg1, void *arg2) |
408 | { | 438 | { |
409 | irq_desc_t *desc = irq_desc + virt_irq; | 439 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
410 | struct irq_handler_data *data = desc->handler_data; | 440 | struct irq_chip *chip; |
411 | 441 | ||
412 | data->pre_handler = func; | 442 | data->pre_handler = func; |
413 | data->pre_handler_arg1 = arg1; | 443 | data->pre_handler_arg1 = arg1; |
414 | data->pre_handler_arg2 = arg2; | 444 | data->pre_handler_arg2 = arg2; |
415 | 445 | ||
416 | if (desc->chip == &sun4u_irq_ack || | 446 | chip = get_irq_chip(virt_irq); |
417 | desc->chip == &sun4v_irq_ack) | 447 | if (chip == &sun4u_irq_ack || |
448 | chip == &sun4v_irq_ack | ||
449 | #ifdef CONFIG_PCI_MSI | ||
450 | || chip == &sun4v_msi | ||
451 | #endif | ||
452 | ) | ||
418 | return; | 453 | return; |
419 | 454 | ||
420 | desc->chip = (desc->chip == &sun4u_irq ? | 455 | chip = (chip == &sun4u_irq ? |
421 | &sun4u_irq_ack : &sun4v_irq_ack); | 456 | &sun4u_irq_ack : &sun4v_irq_ack); |
457 | set_irq_chip(virt_irq, chip); | ||
422 | } | 458 | } |
423 | 459 | ||
424 | unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) | 460 | unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) |
425 | { | 461 | { |
426 | struct ino_bucket *bucket; | 462 | struct ino_bucket *bucket; |
427 | struct irq_handler_data *data; | 463 | struct irq_handler_data *data; |
428 | irq_desc_t *desc; | ||
429 | int ino; | 464 | int ino; |
430 | 465 | ||
431 | BUG_ON(tlb_type == hypervisor); | 466 | BUG_ON(tlb_type == hypervisor); |
@@ -434,11 +469,11 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) | |||
434 | bucket = &ivector_table[ino]; | 469 | bucket = &ivector_table[ino]; |
435 | if (!bucket->virt_irq) { | 470 | if (!bucket->virt_irq) { |
436 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | 471 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); |
437 | irq_desc[bucket->virt_irq].chip = &sun4u_irq; | 472 | set_irq_chip(bucket->virt_irq, &sun4u_irq); |
438 | } | 473 | } |
439 | 474 | ||
440 | desc = irq_desc + bucket->virt_irq; | 475 | data = get_irq_chip_data(bucket->virt_irq); |
441 | if (unlikely(desc->handler_data)) | 476 | if (unlikely(data)) |
442 | goto out; | 477 | goto out; |
443 | 478 | ||
444 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | 479 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); |
@@ -446,7 +481,7 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) | |||
446 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); | 481 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); |
447 | prom_halt(); | 482 | prom_halt(); |
448 | } | 483 | } |
449 | desc->handler_data = data; | 484 | set_irq_chip_data(bucket->virt_irq, data); |
450 | 485 | ||
451 | data->imap = imap; | 486 | data->imap = imap; |
452 | data->iclr = iclr; | 487 | data->iclr = iclr; |
@@ -460,7 +495,6 @@ unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino) | |||
460 | struct ino_bucket *bucket; | 495 | struct ino_bucket *bucket; |
461 | struct irq_handler_data *data; | 496 | struct irq_handler_data *data; |
462 | unsigned long sysino; | 497 | unsigned long sysino; |
463 | irq_desc_t *desc; | ||
464 | 498 | ||
465 | BUG_ON(tlb_type != hypervisor); | 499 | BUG_ON(tlb_type != hypervisor); |
466 | 500 | ||
@@ -468,11 +502,11 @@ unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino) | |||
468 | bucket = &ivector_table[sysino]; | 502 | bucket = &ivector_table[sysino]; |
469 | if (!bucket->virt_irq) { | 503 | if (!bucket->virt_irq) { |
470 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | 504 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); |
471 | irq_desc[bucket->virt_irq].chip = &sun4v_irq; | 505 | set_irq_chip(bucket->virt_irq, &sun4v_irq); |
472 | } | 506 | } |
473 | 507 | ||
474 | desc = irq_desc + bucket->virt_irq; | 508 | data = get_irq_chip_data(bucket->virt_irq); |
475 | if (unlikely(desc->handler_data)) | 509 | if (unlikely(data)) |
476 | goto out; | 510 | goto out; |
477 | 511 | ||
478 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | 512 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); |
@@ -480,7 +514,7 @@ unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino) | |||
480 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); | 514 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); |
481 | prom_halt(); | 515 | prom_halt(); |
482 | } | 516 | } |
483 | desc->handler_data = data; | 517 | set_irq_chip_data(bucket->virt_irq, data); |
484 | 518 | ||
485 | /* Catch accidental accesses to these things. IMAP/ICLR handling | 519 | /* Catch accidental accesses to these things. IMAP/ICLR handling |
486 | * is done by hypervisor calls on sun4v platforms, not by direct | 520 | * is done by hypervisor calls on sun4v platforms, not by direct |
@@ -493,6 +527,56 @@ out: | |||
493 | return bucket->virt_irq; | 527 | return bucket->virt_irq; |
494 | } | 528 | } |
495 | 529 | ||
530 | #ifdef CONFIG_PCI_MSI | ||
531 | unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p, | ||
532 | unsigned int msi_start, unsigned int msi_end) | ||
533 | { | ||
534 | struct ino_bucket *bucket; | ||
535 | struct irq_handler_data *data; | ||
536 | unsigned long sysino; | ||
537 | unsigned int devino; | ||
538 | |||
539 | BUG_ON(tlb_type != hypervisor); | ||
540 | |||
541 | /* Find a free devino in the given range. */ | ||
542 | for (devino = msi_start; devino < msi_end; devino++) { | ||
543 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
544 | bucket = &ivector_table[sysino]; | ||
545 | if (!bucket->virt_irq) | ||
546 | break; | ||
547 | } | ||
548 | if (devino >= msi_end) | ||
549 | return 0; | ||
550 | |||
551 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
552 | bucket = &ivector_table[sysino]; | ||
553 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | ||
554 | *virt_irq_p = bucket->virt_irq; | ||
555 | set_irq_chip(bucket->virt_irq, &sun4v_msi); | ||
556 | |||
557 | data = get_irq_chip_data(bucket->virt_irq); | ||
558 | if (unlikely(data)) | ||
559 | return devino; | ||
560 | |||
561 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | ||
562 | if (unlikely(!data)) { | ||
563 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); | ||
564 | prom_halt(); | ||
565 | } | ||
566 | set_irq_chip_data(bucket->virt_irq, data); | ||
567 | |||
568 | data->imap = ~0UL; | ||
569 | data->iclr = ~0UL; | ||
570 | |||
571 | return devino; | ||
572 | } | ||
573 | |||
574 | void sun4v_destroy_msi(unsigned int virt_irq) | ||
575 | { | ||
576 | virt_irq_free(virt_irq); | ||
577 | } | ||
578 | #endif | ||
579 | |||
496 | void ack_bad_irq(unsigned int virt_irq) | 580 | void ack_bad_irq(unsigned int virt_irq) |
497 | { | 581 | { |
498 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 582 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |