[SPARC64]: Wire up cookie based sun4v interrupt registry.

This will be used for logical domain channel interrupts.

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index a36f8dd..e60d283 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -1,7 +1,6 @@
-/* $Id: irq.c,v 1.114 2002/01/11 08:45:38 davem Exp $
- * irq.c: UltraSparc IRQ handling/init/registry.
+/* irq.c: UltraSparc IRQ handling/init/registry.
  *
- * Copyright (C) 1997  David S. Miller  (davem@caip.rutgers.edu)
+ * Copyright (C) 1997, 2007  David S. Miller  (davem@davemloft.net)
  * Copyright (C) 1998  Eddie C. Dost    (ecd@skynet.be)
  * Copyright (C) 1998  Jakub Jelinek    (jj@ultra.linux.cz)
  */
@@ -43,6 +42,7 @@
 #include <asm/cpudata.h>
 #include <asm/auxio.h>
 #include <asm/head.h>
+#include <asm/hypervisor.h>
 
 /* UPA nodes send interrupt packet to UltraSparc with first data reg
  * value low 5 (7 on Starfire) bits holding the IRQ identifier being
@@ -380,6 +380,76 @@
 	}
 }
 
+static void sun4v_virq_enable(unsigned int virt_irq)
+{
+	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
+	unsigned int ino = bucket - &ivector_table[0];
+
+	if (likely(bucket)) {
+		unsigned long cpuid, dev_handle, dev_ino;
+		int err;
+
+		cpuid = irq_choose_cpu(virt_irq);
+
+		dev_handle = ino & IMAP_IGN;
+		dev_ino = ino & IMAP_INO;
+
+		err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid);
+		if (err != HV_EOK)
+			printk("sun4v_vintr_set_target(%lx,%lx,%lu): "
+			       "err(%d)\n",
+			       dev_handle, dev_ino, cpuid, err);
+		err = sun4v_vintr_set_state(dev_handle, dev_ino,
+					    HV_INTR_ENABLED);
+		if (err != HV_EOK)
+			printk("sun4v_vintr_set_state(%lx,%lx,"
+			       "HV_INTR_ENABLED): err(%d)\n",
+			       dev_handle, dev_ino, err);
+	}
+}
+
+static void sun4v_virq_disable(unsigned int virt_irq)
+{
+	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
+	unsigned int ino = bucket - &ivector_table[0];
+
+	if (likely(bucket)) {
+		unsigned long dev_handle, dev_ino;
+		int err;
+
+		dev_handle = ino & IMAP_IGN;
+		dev_ino = ino & IMAP_INO;
+
+		err = sun4v_vintr_set_state(dev_handle, dev_ino,
+					    HV_INTR_DISABLED);
+		if (err != HV_EOK)
+			printk("sun4v_vintr_set_state(%lx,%lx,"
+			       "HV_INTR_DISABLED): err(%d)\n",
+			       dev_handle, dev_ino, err);
+	}
+}
+
+static void sun4v_virq_end(unsigned int virt_irq)
+{
+	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
+	unsigned int ino = bucket - &ivector_table[0];
+
+	if (likely(bucket)) {
+		unsigned long dev_handle, dev_ino;
+		int err;
+
+		dev_handle = ino & IMAP_IGN;
+		dev_ino = ino & IMAP_INO;
+
+		err = sun4v_vintr_set_state(dev_handle, dev_ino,
+					    HV_INTR_STATE_IDLE);
+		if (err != HV_EOK)
+			printk("sun4v_vintr_set_state(%lx,%lx,"
+				"HV_INTR_STATE_IDLE): err(%d)\n",
+			       dev_handle, dev_ino, err);
+	}
+}
+
 static void run_pre_handler(unsigned int virt_irq)
 {
 	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
@@ -434,6 +504,21 @@
 };
 #endif
 
+static struct irq_chip sun4v_virq = {
+	.typename	= "vsun4v",
+	.enable		= sun4v_virq_enable,
+	.disable	= sun4v_virq_disable,
+	.end		= sun4v_virq_end,
+};
+
+static struct irq_chip sun4v_virq_ack = {
+	.typename	= "vsun4v+ack",
+	.enable		= sun4v_virq_enable,
+	.disable	= sun4v_virq_disable,
+	.ack		= run_pre_handler,
+	.end		= sun4v_virq_end,
+};
+
 void irq_install_pre_handler(int virt_irq,
 			     void (*func)(unsigned int, void *, void *),
 			     void *arg1, void *arg2)
@@ -447,7 +532,8 @@
 
 	chip = get_irq_chip(virt_irq);
 	if (chip == &sun4u_irq_ack ||
-	    chip == &sun4v_irq_ack
+	    chip == &sun4v_irq_ack ||
+	    chip == &sun4v_virq_ack
 #ifdef CONFIG_PCI_MSI
 	    || chip == &sun4v_msi
 #endif
@@ -455,7 +541,9 @@
 		return;
 
 	chip = (chip == &sun4u_irq ?
-		&sun4u_irq_ack : &sun4v_irq_ack);
+		&sun4u_irq_ack :
+		(chip == &sun4v_irq ?
+		 &sun4v_irq_ack : &sun4v_virq_ack));
 	set_irq_chip(virt_irq, chip);
 }
 
@@ -492,19 +580,18 @@
 	return bucket->virt_irq;
 }
 
-unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
+static unsigned int sun4v_build_common(unsigned long sysino,
+				       struct irq_chip *chip)
 {
 	struct ino_bucket *bucket;
 	struct irq_handler_data *data;
-	unsigned long sysino;
 
 	BUG_ON(tlb_type != hypervisor);
 
-	sysino = sun4v_devino_to_sysino(devhandle, devino);
 	bucket = &ivector_table[sysino];
 	if (!bucket->virt_irq) {
 		bucket->virt_irq = virt_irq_alloc(__irq(bucket));
-		set_irq_chip(bucket->virt_irq, &sun4v_irq);
+		set_irq_chip(bucket->virt_irq, chip);
 	}
 
 	data = get_irq_chip_data(bucket->virt_irq);
@@ -529,6 +616,32 @@
 	return bucket->virt_irq;
 }
 
+unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
+{
+	unsigned long sysino = sun4v_devino_to_sysino(devhandle, devino);
+
+	return sun4v_build_common(sysino, &sun4v_irq);
+}
+
+unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
+{
+	unsigned long sysino, hv_err;
+
+	BUG_ON(devhandle & ~IMAP_IGN);
+	BUG_ON(devino & ~IMAP_INO);
+
+	sysino = devhandle | devino;
+
+	hv_err = sun4v_vintr_set_cookie(devhandle, devino, sysino);
+	if (hv_err) {
+		prom_printf("IRQ: Fatal, cannot set cookie for [%x:%x] "
+			    "err=%lu\n", devhandle, devino, hv_err);
+		prom_halt();
+	}
+
+	return sun4v_build_common(sysino, &sun4v_virq);
+}
+
 #ifdef CONFIG_PCI_MSI
 unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p,
 			     unsigned int msi_start, unsigned int msi_end)
diff --git a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h
index 5d233b4..90781e3 100644
--- a/include/asm-sparc64/irq.h
+++ b/include/asm-sparc64/irq.h
@@ -46,6 +46,7 @@
 #define irq_canonicalize(irq)	(irq)
 extern unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap);
 extern unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino);
+extern unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino);
 extern unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p,
 				    unsigned int msi_devino_start,
 				    unsigned int msi_devino_end);