aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/kernel/irq.c')
-rw-r--r--arch/sparc64/kernel/irq.c104
1 files changed, 95 insertions, 9 deletions
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index 2873835e2626..b5ff3ee5ace1 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
89static unsigned int virt_to_real_irq_table[NR_IRQS]; 90static unsigned int virt_to_real_irq_table[NR_IRQS];
90static unsigned char virt_irq_cur = 1;
91 91
92static unsigned char virt_irq_alloc(unsigned int real_irq) 92static unsigned char virt_irq_alloc(unsigned int real_irq)
93{ 93{
@@ -95,26 +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. */ 112static void virt_irq_free(unsigned int virt_irq)
111static unsigned char real_to_virt_irq(unsigned int real_irq)
112{ 113{
113 struct ino_bucket *bucket = __bucket(real_irq); 114 unsigned int real_irq;
114 115
115 return bucket->virt_irq; 116 if (virt_irq >= NR_IRQS)
117 return;
118
119 real_irq = virt_to_real_irq_table[virt_irq];
120 virt_to_real_irq_table[virt_irq] = 0;
121
122 __bucket(real_irq)->virt_irq = 0;
116} 123}
117#endif
118 124
119static unsigned int virt_to_real_irq(unsigned char virt_irq) 125static unsigned int virt_to_real_irq(unsigned char virt_irq)
120{ 126{
@@ -341,6 +347,20 @@ static void sun4v_irq_disable(unsigned int virt_irq)
341 } 347 }
342} 348}
343 349
350#ifdef CONFIG_PCI_MSI
351static void sun4v_msi_enable(unsigned int virt_irq)
352{
353 sun4v_irq_enable(virt_irq);
354 unmask_msi_irq(virt_irq);
355}
356
357static void sun4v_msi_disable(unsigned int virt_irq)
358{
359 mask_msi_irq(virt_irq);
360 sun4v_irq_disable(virt_irq);
361}
362#endif
363
344static void sun4v_irq_end(unsigned int virt_irq) 364static void sun4v_irq_end(unsigned int virt_irq)
345{ 365{
346 struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); 366 struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
@@ -398,6 +418,18 @@ static struct irq_chip sun4v_irq_ack = {
398 .end = sun4v_irq_end, 418 .end = sun4v_irq_end,
399}; 419};
400 420
421#ifdef CONFIG_PCI_MSI
422static struct irq_chip sun4v_msi = {
423 .typename = "sun4v+msi",
424 .mask = mask_msi_irq,
425 .unmask = unmask_msi_irq,
426 .enable = sun4v_msi_enable,
427 .disable = sun4v_msi_disable,
428 .ack = run_pre_handler,
429 .end = sun4v_irq_end,
430};
431#endif
432
401void irq_install_pre_handler(int virt_irq, 433void irq_install_pre_handler(int virt_irq,
402 void (*func)(unsigned int, void *, void *), 434 void (*func)(unsigned int, void *, void *),
403 void *arg1, void *arg2) 435 void *arg1, void *arg2)
@@ -411,7 +443,11 @@ void irq_install_pre_handler(int virt_irq,
411 443
412 chip = get_irq_chip(virt_irq); 444 chip = get_irq_chip(virt_irq);
413 if (chip == &sun4u_irq_ack || 445 if (chip == &sun4u_irq_ack ||
414 chip == &sun4v_irq_ack) 446 chip == &sun4v_irq_ack
447#ifdef CONFIG_PCI_MSI
448 || chip == &sun4v_msi
449#endif
450 )
415 return; 451 return;
416 452
417 chip = (chip == &sun4u_irq ? 453 chip = (chip == &sun4u_irq ?
@@ -489,6 +525,56 @@ out:
489 return bucket->virt_irq; 525 return bucket->virt_irq;
490} 526}
491 527
528#ifdef CONFIG_PCI_MSI
529unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p,
530 unsigned int msi_start, unsigned int msi_end)
531{
532 struct ino_bucket *bucket;
533 struct irq_handler_data *data;
534 unsigned long sysino;
535 unsigned int devino;
536
537 BUG_ON(tlb_type != hypervisor);
538
539 /* Find a free devino in the given range. */
540 for (devino = msi_start; devino < msi_end; devino++) {
541 sysino = sun4v_devino_to_sysino(devhandle, devino);
542 bucket = &ivector_table[sysino];
543 if (!bucket->virt_irq)
544 break;
545 }
546 if (devino >= msi_end)
547 return 0;
548
549 sysino = sun4v_devino_to_sysino(devhandle, devino);
550 bucket = &ivector_table[sysino];
551 bucket->virt_irq = virt_irq_alloc(__irq(bucket));
552 *virt_irq_p = bucket->virt_irq;
553 set_irq_chip(bucket->virt_irq, &sun4v_msi);
554
555 data = get_irq_chip_data(bucket->virt_irq);
556 if (unlikely(data))
557 return devino;
558
559 data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
560 if (unlikely(!data)) {
561 prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
562 prom_halt();
563 }
564 set_irq_chip_data(bucket->virt_irq, data);
565
566 data->imap = ~0UL;
567 data->iclr = ~0UL;
568
569 return devino;
570}
571
572void sun4v_destroy_msi(unsigned int virt_irq)
573{
574 virt_irq_free(virt_irq);
575}
576#endif
577
492void ack_bad_irq(unsigned int virt_irq) 578void ack_bad_irq(unsigned int virt_irq)
493{ 579{
494 struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); 580 struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);