Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6:
  [SPARC64]: Update defconfig.
  [SPARC64]: Add PCI MSI support on Niagara.
  [SPARC64] IRQ: Use irq_desc->chip_data instead of irq_desc->handler_data
  [SPARC64]: Add obppath sysfs attribute for SBUS and PCI devices.
  [PARTITION]: Add whole_disk attribute.
diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig
index 5a9e68b..069ee3c 100644
--- a/arch/sparc64/defconfig
+++ b/arch/sparc64/defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.20-rc2
-# Thu Dec 28 15:09:49 2006
+# Linux kernel version: 2.6.20
+# Sat Feb 10 23:08:12 2007
 #
 CONFIG_SPARC=y
 CONFIG_SPARC64=y
@@ -151,7 +151,7 @@
 CONFIG_SUN_IO=y
 CONFIG_PCI=y
 CONFIG_PCI_DOMAINS=y
-# CONFIG_PCI_MULTITHREAD_PROBE is not set
+CONFIG_PCI_MSI=y
 # CONFIG_PCI_DEBUG is not set
 CONFIG_SUN_OPENPROMFS=m
 CONFIG_SPARC32_COMPAT=y
@@ -182,7 +182,9 @@
 CONFIG_XFRM=y
 CONFIG_XFRM_USER=m
 # CONFIG_XFRM_SUB_POLICY is not set
+CONFIG_XFRM_MIGRATE=y
 CONFIG_NET_KEY=m
+CONFIG_NET_KEY_MIGRATE=y
 CONFIG_INET=y
 CONFIG_IP_MULTICAST=y
 # CONFIG_IP_ADVANCED_ROUTER is not set
@@ -300,6 +302,7 @@
 # CONFIG_PREVENT_FIRMWARE_BUILD is not set
 CONFIG_FW_LOADER=y
 # CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
 # CONFIG_SYS_HYPERVISOR is not set
 
 #
@@ -393,6 +396,7 @@
 # CONFIG_BLK_DEV_JMICRON is not set
 # CONFIG_BLK_DEV_SC1200 is not set
 # CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_IT8213 is not set
 # CONFIG_BLK_DEV_IT821X is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_PDC202XX_OLD is not set
@@ -402,6 +406,7 @@
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
 # CONFIG_IDE_ARM is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_IVB is not set
@@ -579,6 +584,7 @@
 # CONFIG_EPIC100 is not set
 # CONFIG_SUNDANCE is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_SC92031 is not set
 
 #
 # Ethernet (1000 Mbit)
@@ -601,11 +607,13 @@
 CONFIG_TIGON3=m
 CONFIG_BNX2=m
 # CONFIG_QLA3XXX is not set
+# CONFIG_ATL1 is not set
 
 #
 # Ethernet (10000 Mbit)
 #
 # CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
 # CONFIG_IXGB is not set
 # CONFIG_S2IO is not set
 # CONFIG_MYRI10GE is not set
@@ -627,8 +635,17 @@
 # CONFIG_WAN is not set
 # CONFIG_FDDI is not set
 # CONFIG_HIPPI is not set
-# CONFIG_PPP is not set
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_MPPE=m
+CONFIG_PPPOE=m
 # CONFIG_SLIP is not set
+CONFIG_SLHC=m
 # CONFIG_NET_FC is not set
 # CONFIG_SHAPER is not set
 # CONFIG_NETCONSOLE is not set
@@ -1043,6 +1060,11 @@
 # CONFIG_SND_SUN_DBRI is not set
 
 #
+# SoC audio support
+#
+# CONFIG_SND_SOC is not set
+
+#
 # Open Sound System
 #
 # CONFIG_SOUND_PRIME is not set
@@ -1052,6 +1074,7 @@
 # HID Devices
 #
 CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
 
 #
 # USB support
@@ -1066,9 +1089,7 @@
 # Miscellaneous USB options
 #
 CONFIG_USB_DEVICEFS=y
-# CONFIG_USB_BANDWIDTH is not set
 # CONFIG_USB_DYNAMIC_MINORS is not set
-# CONFIG_USB_MULTITHREAD_PROBE is not set
 # CONFIG_USB_OTG is not set
 
 #
@@ -1078,9 +1099,11 @@
 # CONFIG_USB_EHCI_SPLIT_ISO is not set
 # CONFIG_USB_EHCI_ROOT_HUB_TT is not set
 # CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_EHCI_BIG_ENDIAN_MMIO is not set
 # CONFIG_USB_ISP116X_HCD is not set
 CONFIG_USB_OHCI_HCD=y
-# CONFIG_USB_OHCI_BIG_ENDIAN is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
 CONFIG_USB_OHCI_LITTLE_ENDIAN=y
 CONFIG_USB_UHCI_HCD=m
 # CONFIG_USB_SL811_HCD is not set
@@ -1132,6 +1155,7 @@
 # CONFIG_USB_ATI_REMOTE2 is not set
 # CONFIG_USB_KEYSPAN_REMOTE is not set
 # CONFIG_USB_APPLETOUCH is not set
+# CONFIG_USB_GTCO is not set
 
 #
 # USB Imaging devices
@@ -1473,8 +1497,10 @@
 CONFIG_CRYPTO_GF128MUL=m
 CONFIG_CRYPTO_ECB=m
 CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_PCBC=m
 CONFIG_CRYPTO_LRW=m
 CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_FCRYPT=m
 CONFIG_CRYPTO_BLOWFISH=m
 CONFIG_CRYPTO_TWOFISH=m
 CONFIG_CRYPTO_TWOFISH_COMMON=m
@@ -1489,6 +1515,7 @@
 CONFIG_CRYPTO_DEFLATE=y
 CONFIG_CRYPTO_MICHAEL_MIC=m
 CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_CAMELLIA=m
 CONFIG_CRYPTO_TEST=m
 
 #
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index c3d068c..b5ff3ee 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -22,6 +22,7 @@
 #include <linux/seq_file.h>
 #include <linux/bootmem.h>
 #include <linux/irq.h>
+#include <linux/msi.h>
 
 #include <asm/ptrace.h>
 #include <asm/processor.h>
@@ -87,7 +88,6 @@
 #define irq_work(__cpu)	&(trap_block[(__cpu)].irq_worklist)
 
 static unsigned int virt_to_real_irq_table[NR_IRQS];
-static unsigned char virt_irq_cur = 1;
 
 static unsigned char virt_irq_alloc(unsigned int real_irq)
 {
@@ -95,26 +95,32 @@
 
 	BUILD_BUG_ON(NR_IRQS >= 256);
 
-	ent = virt_irq_cur;
+	for (ent = 1; ent < NR_IRQS; ent++) {
+		if (!virt_to_real_irq_table[ent])
+			break;
+	}
 	if (ent >= NR_IRQS) {
 		printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
 		return 0;
 	}
 
-	virt_irq_cur = ent + 1;
 	virt_to_real_irq_table[ent] = real_irq;
 
 	return ent;
 }
 
-#if 0 /* Currently unused. */
-static unsigned char real_to_virt_irq(unsigned int real_irq)
+static void virt_irq_free(unsigned int virt_irq)
 {
-	struct ino_bucket *bucket = __bucket(real_irq);
+	unsigned int real_irq;
 
-	return bucket->virt_irq;
+	if (virt_irq >= NR_IRQS)
+		return;
+
+	real_irq = virt_to_real_irq_table[virt_irq];
+	virt_to_real_irq_table[virt_irq] = 0;
+
+	__bucket(real_irq)->virt_irq = 0;
 }
-#endif
 
 static unsigned int virt_to_real_irq(unsigned char virt_irq)
 {
@@ -268,8 +274,7 @@
 
 static void sun4u_irq_enable(unsigned int virt_irq)
 {
-	irq_desc_t *desc = irq_desc + virt_irq;
-	struct irq_handler_data *data = desc->handler_data;
+	struct irq_handler_data *data = get_irq_chip_data(virt_irq);
 
 	if (likely(data)) {
 		unsigned long cpuid, imap;
@@ -286,8 +291,7 @@
 
 static void sun4u_irq_disable(unsigned int virt_irq)
 {
-	irq_desc_t *desc = irq_desc + virt_irq;
-	struct irq_handler_data *data = desc->handler_data;
+	struct irq_handler_data *data = get_irq_chip_data(virt_irq);
 
 	if (likely(data)) {
 		unsigned long imap = data->imap;
@@ -300,8 +304,7 @@
 
 static void sun4u_irq_end(unsigned int virt_irq)
 {
-	irq_desc_t *desc = irq_desc + virt_irq;
-	struct irq_handler_data *data = desc->handler_data;
+	struct irq_handler_data *data = get_irq_chip_data(virt_irq);
 
 	if (likely(data))
 		upa_writel(ICLR_IDLE, data->iclr);
@@ -344,6 +347,20 @@
 	}
 }
 
+#ifdef CONFIG_PCI_MSI
+static void sun4v_msi_enable(unsigned int virt_irq)
+{
+	sun4v_irq_enable(virt_irq);
+	unmask_msi_irq(virt_irq);
+}
+
+static void sun4v_msi_disable(unsigned int virt_irq)
+{
+	mask_msi_irq(virt_irq);
+	sun4v_irq_disable(virt_irq);
+}
+#endif
+
 static void sun4v_irq_end(unsigned int virt_irq)
 {
 	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
@@ -362,8 +379,7 @@
 static void run_pre_handler(unsigned int virt_irq)
 {
 	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
-	irq_desc_t *desc = irq_desc + virt_irq;
-	struct irq_handler_data *data = desc->handler_data;
+	struct irq_handler_data *data = get_irq_chip_data(virt_irq);
 
 	if (likely(data->pre_handler)) {
 		data->pre_handler(__irq_ino(__irq(bucket)),
@@ -402,30 +418,47 @@
 	.end		= sun4v_irq_end,
 };
 
+#ifdef CONFIG_PCI_MSI
+static struct irq_chip sun4v_msi = {
+	.typename	= "sun4v+msi",
+	.mask		= mask_msi_irq,
+	.unmask		= unmask_msi_irq,
+	.enable		= sun4v_msi_enable,
+	.disable	= sun4v_msi_disable,
+	.ack		= run_pre_handler,
+	.end		= sun4v_irq_end,
+};
+#endif
+
 void irq_install_pre_handler(int virt_irq,
 			     void (*func)(unsigned int, void *, void *),
 			     void *arg1, void *arg2)
 {
-	irq_desc_t *desc = irq_desc + virt_irq;
-	struct irq_handler_data *data = desc->handler_data;
+	struct irq_handler_data *data = get_irq_chip_data(virt_irq);
+	struct irq_chip *chip;
 
 	data->pre_handler = func;
 	data->pre_handler_arg1 = arg1;
 	data->pre_handler_arg2 = arg2;
 
-	if (desc->chip == &sun4u_irq_ack ||
-	    desc->chip == &sun4v_irq_ack)
+	chip = get_irq_chip(virt_irq);
+	if (chip == &sun4u_irq_ack ||
+	    chip == &sun4v_irq_ack
+#ifdef CONFIG_PCI_MSI
+	    || chip == &sun4v_msi
+#endif
+	    )
 		return;
 
-	desc->chip = (desc->chip == &sun4u_irq ?
-		      &sun4u_irq_ack : &sun4v_irq_ack);
+	chip = (chip == &sun4u_irq ?
+		&sun4u_irq_ack : &sun4v_irq_ack);
+	set_irq_chip(virt_irq, chip);
 }
 
 unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
 {
 	struct ino_bucket *bucket;
 	struct irq_handler_data *data;
-	irq_desc_t *desc;
 	int ino;
 
 	BUG_ON(tlb_type == hypervisor);
@@ -434,11 +467,11 @@
 	bucket = &ivector_table[ino];
 	if (!bucket->virt_irq) {
 		bucket->virt_irq = virt_irq_alloc(__irq(bucket));
-		irq_desc[bucket->virt_irq].chip = &sun4u_irq;
+		set_irq_chip(bucket->virt_irq, &sun4u_irq);
 	}
 
-	desc = irq_desc + bucket->virt_irq;
-	if (unlikely(desc->handler_data))
+	data = get_irq_chip_data(bucket->virt_irq);
+	if (unlikely(data))
 		goto out;
 
 	data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
@@ -446,7 +479,7 @@
 		prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
 		prom_halt();
 	}
-	desc->handler_data = data;
+	set_irq_chip_data(bucket->virt_irq, data);
 
 	data->imap  = imap;
 	data->iclr  = iclr;
@@ -460,7 +493,6 @@
 	struct ino_bucket *bucket;
 	struct irq_handler_data *data;
 	unsigned long sysino;
-	irq_desc_t *desc;
 
 	BUG_ON(tlb_type != hypervisor);
 
@@ -468,11 +500,11 @@
 	bucket = &ivector_table[sysino];
 	if (!bucket->virt_irq) {
 		bucket->virt_irq = virt_irq_alloc(__irq(bucket));
-		irq_desc[bucket->virt_irq].chip = &sun4v_irq;
+		set_irq_chip(bucket->virt_irq, &sun4v_irq);
 	}
 
-	desc = irq_desc + bucket->virt_irq;
-	if (unlikely(desc->handler_data))
+	data = get_irq_chip_data(bucket->virt_irq);
+	if (unlikely(data))
 		goto out;
 
 	data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
@@ -480,7 +512,7 @@
 		prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
 		prom_halt();
 	}
-	desc->handler_data = data;
+	set_irq_chip_data(bucket->virt_irq, data);
 
 	/* Catch accidental accesses to these things.  IMAP/ICLR handling
 	 * is done by hypervisor calls on sun4v platforms, not by direct
@@ -493,6 +525,56 @@
 	return bucket->virt_irq;
 }
 
+#ifdef CONFIG_PCI_MSI
+unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p,
+			     unsigned int msi_start, unsigned int msi_end)
+{
+	struct ino_bucket *bucket;
+	struct irq_handler_data *data;
+	unsigned long sysino;
+	unsigned int devino;
+
+	BUG_ON(tlb_type != hypervisor);
+
+	/* Find a free devino in the given range.  */
+	for (devino = msi_start; devino < msi_end; devino++) {
+		sysino = sun4v_devino_to_sysino(devhandle, devino);
+		bucket = &ivector_table[sysino];
+		if (!bucket->virt_irq)
+			break;
+	}
+	if (devino >= msi_end)
+		return 0;
+
+	sysino = sun4v_devino_to_sysino(devhandle, devino);
+	bucket = &ivector_table[sysino];
+	bucket->virt_irq = virt_irq_alloc(__irq(bucket));
+	*virt_irq_p = bucket->virt_irq;
+	set_irq_chip(bucket->virt_irq, &sun4v_msi);
+
+	data = get_irq_chip_data(bucket->virt_irq);
+	if (unlikely(data))
+		return devino;
+
+	data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
+	if (unlikely(!data)) {
+		prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
+		prom_halt();
+	}
+	set_irq_chip_data(bucket->virt_irq, data);
+
+	data->imap = ~0UL;
+	data->iclr = ~0UL;
+
+	return devino;
+}
+
+void sun4v_destroy_msi(unsigned int virt_irq)
+{
+	virt_irq_free(virt_irq);
+}
+#endif
+
 void ack_bad_irq(unsigned int virt_irq)
 {
 	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index dfc41cd..6b740eb 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -13,6 +13,8 @@
 #include <linux/capability.h>
 #include <linux/errno.h>
 #include <linux/smp_lock.h>
+#include <linux/msi.h>
+#include <linux/irq.h>
 #include <linux/init.h>
 
 #include <asm/uaccess.h>
@@ -646,4 +648,37 @@
 }
 EXPORT_SYMBOL(pci_domain_nr);
 
+#ifdef CONFIG_PCI_MSI
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+	struct pcidev_cookie *pcp = pdev->sysdata;
+	struct pci_pbm_info *pbm = pcp->pbm;
+	struct pci_controller_info *p = pbm->parent;
+	int virt_irq, err;
+
+	if (!pbm->msi_num || !p->setup_msi_irq)
+		return -EINVAL;
+
+	err = p->setup_msi_irq(&virt_irq, pdev, desc);
+	if (err < 0)
+		return err;
+
+	return virt_irq;
+}
+
+void arch_teardown_msi_irq(unsigned int virt_irq)
+{
+	struct msi_desc *entry = get_irq_data(virt_irq);
+	struct pci_dev *pdev = entry->dev;
+	struct pcidev_cookie *pcp = pdev->sysdata;
+	struct pci_pbm_info *pbm = pcp->pbm;
+	struct pci_controller_info *p = pbm->parent;
+
+	if (!pbm->msi_num || !p->setup_msi_irq)
+		return;
+
+	return p->teardown_msi_irq(virt_irq, pdev);
+}
+#endif /* !(CONFIG_PCI_MSI) */
+
 #endif /* !(CONFIG_PCI) */
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
index 827ae30..5a92cb9 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc64/kernel/pci_common.c
@@ -7,6 +7,8 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
 
 #include <asm/pbm.h>
 #include <asm/prom.h>
@@ -129,6 +131,20 @@
 	}
 }
 
+static ssize_t
+show_pciobppath_attr(struct device * dev, struct device_attribute * attr, char * buf)
+{
+	struct pci_dev *pdev;
+	struct pcidev_cookie *sysdata;
+
+	pdev = to_pci_dev(dev);
+	sysdata = pdev->sysdata;
+
+	return snprintf (buf, PAGE_SIZE, "%s\n", sysdata->prom_node->full_name);
+}
+
+static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL);
+
 /* Fill in the PCI device cookie sysdata for the given
  * PCI device.  This cookie is the means by which one
  * can get to OBP and PCI controller specific information
@@ -142,7 +158,7 @@
 	struct pcidev_cookie *pcp;
 	struct device_node *dp;
 	struct property *prop;
-	int nregs, len;
+	int nregs, len, err;
 
 	dp = find_device_prom_node(pbm, pdev, bus_node,
 				   &pregs, &nregs);
@@ -215,6 +231,13 @@
 	fixup_obp_assignments(pdev, pcp);
 
 	pdev->sysdata = pcp;
+
+	/* we don't really care if we can create this file or not,
+	 * but we need to assign the result of the call or the world will fall
+	 * under alien invasion and everybody will be frozen on a spaceship
+	 * ready to be eaten on alpha centauri by some green and jelly humanoid.
+	 */
+	err = sysfs_create_file(&pdev->dev.kobj, &dev_attr_obppath.attr);
 }
 
 void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus,
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c
index 6b04794..ec22cd6 100644
--- a/arch/sparc64/kernel/pci_sun4v.c
+++ b/arch/sparc64/kernel/pci_sun4v.c
@@ -10,6 +10,8 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/percpu.h>
+#include <linux/irq.h>
+#include <linux/msi.h>
 
 #include <asm/pbm.h>
 #include <asm/iommu.h>
@@ -1074,6 +1076,443 @@
 
 }
 
+#ifdef CONFIG_PCI_MSI
+struct pci_sun4v_msiq_entry {
+	u64		version_type;
+#define MSIQ_VERSION_MASK		0xffffffff00000000UL
+#define MSIQ_VERSION_SHIFT		32
+#define MSIQ_TYPE_MASK			0x00000000000000ffUL
+#define MSIQ_TYPE_SHIFT			0
+#define MSIQ_TYPE_NONE			0x00
+#define MSIQ_TYPE_MSG			0x01
+#define MSIQ_TYPE_MSI32			0x02
+#define MSIQ_TYPE_MSI64			0x03
+#define MSIQ_TYPE_INTX			0x08
+#define MSIQ_TYPE_NONE2			0xff
+
+	u64		intx_sysino;
+	u64		reserved1;
+	u64		stick;
+	u64		req_id;  /* bus/device/func */
+#define MSIQ_REQID_BUS_MASK		0xff00UL
+#define MSIQ_REQID_BUS_SHIFT		8
+#define MSIQ_REQID_DEVICE_MASK		0x00f8UL
+#define MSIQ_REQID_DEVICE_SHIFT		3
+#define MSIQ_REQID_FUNC_MASK		0x0007UL
+#define MSIQ_REQID_FUNC_SHIFT		0
+
+	u64		msi_address;
+
+	/* The format of this value is message type dependant.
+	 * For MSI bits 15:0 are the data from the MSI packet.
+	 * For MSI-X bits 31:0 are the data from the MSI packet.
+	 * For MSG, the message code and message routing code where:
+	 * 	bits 39:32 is the bus/device/fn of the msg target-id
+	 *	bits 18:16 is the message routing code
+	 *	bits 7:0 is the message code
+	 * For INTx the low order 2-bits are:
+	 *	00 - INTA
+	 *	01 - INTB
+	 *	10 - INTC
+	 *	11 - INTD
+	 */
+	u64		msi_data;
+
+	u64		reserved2;
+};
+
+/* For now this just runs as a pre-handler for the real interrupt handler.
+ * So we just walk through the queue and ACK all the entries, update the
+ * head pointer, and return.
+ *
+ * In the longer term it would be nice to do something more integrated
+ * wherein we can pass in some of this MSI info to the drivers.  This
+ * would be most useful for PCIe fabric error messages, although we could
+ * invoke those directly from the loop here in order to pass the info around.
+ */
+static void pci_sun4v_msi_prehandler(unsigned int ino, void *data1, void *data2)
+{
+	struct pci_pbm_info *pbm = data1;
+	struct pci_sun4v_msiq_entry *base, *ep;
+	unsigned long msiqid, orig_head, head, type, err;
+
+	msiqid = (unsigned long) data2;
+
+	head = 0xdeadbeef;
+	err = pci_sun4v_msiq_gethead(pbm->devhandle, msiqid, &head);
+	if (unlikely(err))
+		goto hv_error_get;
+
+	if (unlikely(head >= (pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry))))
+		goto bad_offset;
+
+	head /= sizeof(struct pci_sun4v_msiq_entry);
+	orig_head = head;
+	base = (pbm->msi_queues + ((msiqid - pbm->msiq_first) *
+				   (pbm->msiq_ent_count *
+				    sizeof(struct pci_sun4v_msiq_entry))));
+	ep = &base[head];
+	while ((ep->version_type & MSIQ_TYPE_MASK) != 0) {
+		type = (ep->version_type & MSIQ_TYPE_MASK) >> MSIQ_TYPE_SHIFT;
+		if (unlikely(type != MSIQ_TYPE_MSI32 &&
+			     type != MSIQ_TYPE_MSI64))
+			goto bad_type;
+
+		pci_sun4v_msi_setstate(pbm->devhandle,
+				       ep->msi_data /* msi_num */,
+				       HV_MSISTATE_IDLE);
+
+		/* Clear the entry.  */
+		ep->version_type &= ~MSIQ_TYPE_MASK;
+
+		/* Go to next entry in ring.  */
+		head++;
+		if (head >= pbm->msiq_ent_count)
+			head = 0;
+		ep = &base[head];
+	}
+
+	if (likely(head != orig_head)) {
+		/* ACK entries by updating head pointer.  */
+		head *= sizeof(struct pci_sun4v_msiq_entry);
+		err = pci_sun4v_msiq_sethead(pbm->devhandle, msiqid, head);
+		if (unlikely(err))
+			goto hv_error_set;
+	}
+	return;
+
+hv_error_set:
+	printk(KERN_EMERG "MSI: Hypervisor set head gives error %lu\n", err);
+	goto hv_error_cont;
+
+hv_error_get:
+	printk(KERN_EMERG "MSI: Hypervisor get head gives error %lu\n", err);
+
+hv_error_cont:
+	printk(KERN_EMERG "MSI: devhandle[%x] msiqid[%lx] head[%lu]\n",
+	       pbm->devhandle, msiqid, head);
+	return;
+
+bad_offset:
+	printk(KERN_EMERG "MSI: Hypervisor gives bad offset %lx max(%lx)\n",
+	       head, pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry));
+	return;
+
+bad_type:
+	printk(KERN_EMERG "MSI: Entry has bad type %lx\n", type);
+	return;
+}
+
+static int msi_bitmap_alloc(struct pci_pbm_info *pbm)
+{
+	unsigned long size, bits_per_ulong;
+
+	bits_per_ulong = sizeof(unsigned long) * 8;
+	size = (pbm->msi_num + (bits_per_ulong - 1)) & ~(bits_per_ulong - 1);
+	size /= 8;
+	BUG_ON(size % sizeof(unsigned long));
+
+	pbm->msi_bitmap = kzalloc(size, GFP_KERNEL);
+	if (!pbm->msi_bitmap)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void msi_bitmap_free(struct pci_pbm_info *pbm)
+{
+	kfree(pbm->msi_bitmap);
+	pbm->msi_bitmap = NULL;
+}
+
+static int msi_queue_alloc(struct pci_pbm_info *pbm)
+{
+	unsigned long q_size, alloc_size, pages, order;
+	int i;
+
+	q_size = pbm->msiq_ent_count * sizeof(struct pci_sun4v_msiq_entry);
+	alloc_size = (pbm->msiq_num * q_size);
+	order = get_order(alloc_size);
+	pages = __get_free_pages(GFP_KERNEL | __GFP_COMP, order);
+	if (pages == 0UL) {
+		printk(KERN_ERR "MSI: Cannot allocate MSI queues (o=%lu).\n",
+		       order);
+		return -ENOMEM;
+	}
+	memset((char *)pages, 0, PAGE_SIZE << order);
+	pbm->msi_queues = (void *) pages;
+
+	for (i = 0; i < pbm->msiq_num; i++) {
+		unsigned long err, base = __pa(pages + (i * q_size));
+		unsigned long ret1, ret2;
+
+		err = pci_sun4v_msiq_conf(pbm->devhandle,
+					  pbm->msiq_first + i,
+					  base, pbm->msiq_ent_count);
+		if (err) {
+			printk(KERN_ERR "MSI: msiq register fails (err=%lu)\n",
+			       err);
+			goto h_error;
+		}
+
+		err = pci_sun4v_msiq_info(pbm->devhandle,
+					  pbm->msiq_first + i,
+					  &ret1, &ret2);
+		if (err) {
+			printk(KERN_ERR "MSI: Cannot read msiq (err=%lu)\n",
+			       err);
+			goto h_error;
+		}
+		if (ret1 != base || ret2 != pbm->msiq_ent_count) {
+			printk(KERN_ERR "MSI: Bogus qconf "
+			       "expected[%lx:%x] got[%lx:%lx]\n",
+			       base, pbm->msiq_ent_count,
+			       ret1, ret2);
+			goto h_error;
+		}
+	}
+
+	return 0;
+
+h_error:
+	free_pages(pages, order);
+	return -EINVAL;
+}
+
+static void pci_sun4v_msi_init(struct pci_pbm_info *pbm)
+{
+	u32 *val;
+	int len;
+
+	val = of_get_property(pbm->prom_node, "#msi-eqs", &len);
+	if (!val || len != 4)
+		goto no_msi;
+	pbm->msiq_num = *val;
+	if (pbm->msiq_num) {
+		struct msiq_prop {
+			u32 first_msiq;
+			u32 num_msiq;
+			u32 first_devino;
+		} *mqp;
+		struct msi_range_prop {
+			u32 first_msi;
+			u32 num_msi;
+		} *mrng;
+		struct addr_range_prop {
+			u32 msi32_high;
+			u32 msi32_low;
+			u32 msi32_len;
+			u32 msi64_high;
+			u32 msi64_low;
+			u32 msi64_len;
+		} *arng;
+
+		val = of_get_property(pbm->prom_node, "msi-eq-size", &len);
+		if (!val || len != 4)
+			goto no_msi;
+
+		pbm->msiq_ent_count = *val;
+
+		mqp = of_get_property(pbm->prom_node,
+				      "msi-eq-to-devino", &len);
+		if (!mqp || len != sizeof(struct msiq_prop))
+			goto no_msi;
+
+		pbm->msiq_first = mqp->first_msiq;
+		pbm->msiq_first_devino = mqp->first_devino;
+
+		val = of_get_property(pbm->prom_node, "#msi", &len);
+		if (!val || len != 4)
+			goto no_msi;
+		pbm->msi_num = *val;
+
+		mrng = of_get_property(pbm->prom_node, "msi-ranges", &len);
+		if (!mrng || len != sizeof(struct msi_range_prop))
+			goto no_msi;
+		pbm->msi_first = mrng->first_msi;
+
+		val = of_get_property(pbm->prom_node, "msi-data-mask", &len);
+		if (!val || len != 4)
+			goto no_msi;
+		pbm->msi_data_mask = *val;
+
+		val = of_get_property(pbm->prom_node, "msix-data-width", &len);
+		if (!val || len != 4)
+			goto no_msi;
+		pbm->msix_data_width = *val;
+
+		arng = of_get_property(pbm->prom_node, "msi-address-ranges",
+				       &len);
+		if (!arng || len != sizeof(struct addr_range_prop))
+			goto no_msi;
+		pbm->msi32_start = ((u64)arng->msi32_high << 32) |
+			(u64) arng->msi32_low;
+		pbm->msi64_start = ((u64)arng->msi64_high << 32) |
+			(u64) arng->msi64_low;
+		pbm->msi32_len = arng->msi32_len;
+		pbm->msi64_len = arng->msi64_len;
+
+		if (msi_bitmap_alloc(pbm))
+			goto no_msi;
+
+		if (msi_queue_alloc(pbm)) {
+			msi_bitmap_free(pbm);
+			goto no_msi;
+		}
+
+		printk(KERN_INFO "%s: MSI Queue first[%u] num[%u] count[%u] "
+		       "devino[0x%x]\n",
+		       pbm->name,
+		       pbm->msiq_first, pbm->msiq_num,
+		       pbm->msiq_ent_count,
+		       pbm->msiq_first_devino);
+		printk(KERN_INFO "%s: MSI first[%u] num[%u] mask[0x%x] "
+		       "width[%u]\n",
+		       pbm->name,
+		       pbm->msi_first, pbm->msi_num, pbm->msi_data_mask,
+		       pbm->msix_data_width);
+		printk(KERN_INFO "%s: MSI addr32[0x%lx:0x%x] "
+		       "addr64[0x%lx:0x%x]\n",
+		       pbm->name,
+		       pbm->msi32_start, pbm->msi32_len,
+		       pbm->msi64_start, pbm->msi64_len);
+		printk(KERN_INFO "%s: MSI queues at RA [%p]\n",
+		       pbm->name,
+		       pbm->msi_queues);
+	}
+
+	return;
+
+no_msi:
+	pbm->msiq_num = 0;
+	printk(KERN_INFO "%s: No MSI support.\n", pbm->name);
+}
+
+static int alloc_msi(struct pci_pbm_info *pbm)
+{
+	int i;
+
+	for (i = 0; i < pbm->msi_num; i++) {
+		if (!test_and_set_bit(i, pbm->msi_bitmap))
+			return i + pbm->msi_first;
+	}
+
+	return -ENOENT;
+}
+
+static void free_msi(struct pci_pbm_info *pbm, int msi_num)
+{
+	msi_num -= pbm->msi_first;
+	clear_bit(msi_num, pbm->msi_bitmap);
+}
+
+static int pci_sun4v_setup_msi_irq(unsigned int *virt_irq_p,
+				   struct pci_dev *pdev,
+				   struct msi_desc *entry)
+{
+	struct pcidev_cookie *pcp = pdev->sysdata;
+	struct pci_pbm_info *pbm = pcp->pbm;
+	unsigned long devino, msiqid;
+	struct msi_msg msg;
+	int msi_num, err;
+
+	*virt_irq_p = 0;
+
+	msi_num = alloc_msi(pbm);
+	if (msi_num < 0)
+		return msi_num;
+
+	devino = sun4v_build_msi(pbm->devhandle, virt_irq_p,
+				 pbm->msiq_first_devino,
+				 (pbm->msiq_first_devino +
+				  pbm->msiq_num));
+	err = -ENOMEM;
+	if (!devino)
+		goto out_err;
+
+	set_irq_msi(*virt_irq_p, entry);
+
+	msiqid = ((devino - pbm->msiq_first_devino) +
+		  pbm->msiq_first);
+
+	err = -EINVAL;
+	if (pci_sun4v_msiq_setstate(pbm->devhandle, msiqid, HV_MSIQSTATE_IDLE))
+	if (err)
+		goto out_err;
+
+	if (pci_sun4v_msiq_setvalid(pbm->devhandle, msiqid, HV_MSIQ_VALID))
+		goto out_err;
+
+	if (pci_sun4v_msi_setmsiq(pbm->devhandle,
+				  msi_num, msiqid,
+				  (entry->msi_attrib.is_64 ?
+				   HV_MSITYPE_MSI64 : HV_MSITYPE_MSI32)))
+		goto out_err;
+
+	if (pci_sun4v_msi_setstate(pbm->devhandle, msi_num, HV_MSISTATE_IDLE))
+		goto out_err;
+
+	if (pci_sun4v_msi_setvalid(pbm->devhandle, msi_num, HV_MSIVALID_VALID))
+		goto out_err;
+
+	pcp->msi_num = msi_num;
+
+	if (entry->msi_attrib.is_64) {
+		msg.address_hi = pbm->msi64_start >> 32;
+		msg.address_lo = pbm->msi64_start & 0xffffffff;
+	} else {
+		msg.address_hi = 0;
+		msg.address_lo = pbm->msi32_start;
+	}
+	msg.data = msi_num;
+	write_msi_msg(*virt_irq_p, &msg);
+
+	irq_install_pre_handler(*virt_irq_p,
+				pci_sun4v_msi_prehandler,
+				pbm, (void *) msiqid);
+
+	return 0;
+
+out_err:
+	free_msi(pbm, msi_num);
+	sun4v_destroy_msi(*virt_irq_p);
+	*virt_irq_p = 0;
+	return err;
+
+}
+
+static void pci_sun4v_teardown_msi_irq(unsigned int virt_irq,
+				       struct pci_dev *pdev)
+{
+	struct pcidev_cookie *pcp = pdev->sysdata;
+	struct pci_pbm_info *pbm = pcp->pbm;
+	unsigned long msiqid, err;
+	unsigned int msi_num;
+
+	msi_num = pcp->msi_num;
+	err = pci_sun4v_msi_getmsiq(pbm->devhandle, msi_num, &msiqid);
+	if (err) {
+		printk(KERN_ERR "%s: getmsiq gives error %lu\n",
+		       pbm->name, err);
+		return;
+	}
+
+	pci_sun4v_msi_setvalid(pbm->devhandle, msi_num, HV_MSIVALID_INVALID);
+	pci_sun4v_msiq_setvalid(pbm->devhandle, msiqid, HV_MSIQ_INVALID);
+
+	free_msi(pbm, msi_num);
+
+	/* The sun4v_destroy_msi() will liberate the devino and thus the MSIQ
+	 * allocation.
+	 */
+	sun4v_destroy_msi(virt_irq);
+}
+#else /* CONFIG_PCI_MSI */
+static void pci_sun4v_msi_init(struct pci_pbm_info *pbm)
+{
+}
+#endif /* !(CONFIG_PCI_MSI) */
+
 static void pci_sun4v_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 devhandle)
 {
 	struct pci_pbm_info *pbm;
@@ -1119,6 +1558,7 @@
 
 	pci_sun4v_get_bus_range(pbm);
 	pci_sun4v_iommu_init(pbm);
+	pci_sun4v_msi_init(pbm);
 
 	pdev_htab_populate(pbm);
 }
@@ -1187,6 +1627,10 @@
 	p->scan_bus = pci_sun4v_scan_bus;
 	p->base_address_update = pci_sun4v_base_address_update;
 	p->resource_adjust = pci_sun4v_resource_adjust;
+#ifdef CONFIG_PCI_MSI
+	p->setup_msi_irq = pci_sun4v_setup_msi_irq;
+	p->teardown_msi_irq = pci_sun4v_teardown_msi_irq;
+#endif
 	p->pci_ops = &pci_sun4v_ops;
 
 	/* Like PSYCHO and SCHIZO we have a 2GB aligned area
diff --git a/arch/sparc64/kernel/pci_sun4v.h b/arch/sparc64/kernel/pci_sun4v.h
index 884d25f..8e9fc3a 100644
--- a/arch/sparc64/kernel/pci_sun4v.h
+++ b/arch/sparc64/kernel/pci_sun4v.h
@@ -28,4 +28,65 @@
 				unsigned long size,
 				unsigned long data);
 
+extern unsigned long pci_sun4v_msiq_conf(unsigned long devhandle,
+					 unsigned long msiqid,
+					 unsigned long msiq_paddr,
+					 unsigned long num_entries);
+extern unsigned long pci_sun4v_msiq_info(unsigned long devhandle,
+					 unsigned long msiqid,
+					 unsigned long *msiq_paddr,
+					 unsigned long *num_entries);
+extern unsigned long pci_sun4v_msiq_getvalid(unsigned long devhandle,
+					     unsigned long msiqid,
+					     unsigned long *valid);
+extern unsigned long pci_sun4v_msiq_setvalid(unsigned long devhandle,
+					     unsigned long msiqid,
+					     unsigned long valid);
+extern unsigned long pci_sun4v_msiq_getstate(unsigned long devhandle,
+					     unsigned long msiqid,
+					     unsigned long *state);
+extern unsigned long pci_sun4v_msiq_setstate(unsigned long devhandle,
+					     unsigned long msiqid,
+					     unsigned long state);
+extern unsigned long pci_sun4v_msiq_gethead(unsigned long devhandle,
+					     unsigned long msiqid,
+					     unsigned long *head);
+extern unsigned long pci_sun4v_msiq_sethead(unsigned long devhandle,
+					     unsigned long msiqid,
+					     unsigned long head);
+extern unsigned long pci_sun4v_msiq_gettail(unsigned long devhandle,
+					     unsigned long msiqid,
+					     unsigned long *head);
+extern unsigned long pci_sun4v_msi_getvalid(unsigned long devhandle,
+					    unsigned long msinum,
+					    unsigned long *valid);
+extern unsigned long pci_sun4v_msi_setvalid(unsigned long devhandle,
+					    unsigned long msinum,
+					    unsigned long valid);
+extern unsigned long pci_sun4v_msi_getmsiq(unsigned long devhandle,
+					   unsigned long msinum,
+					   unsigned long *msiq);
+extern unsigned long pci_sun4v_msi_setmsiq(unsigned long devhandle,
+					   unsigned long msinum,
+					   unsigned long msiq,
+					   unsigned long msitype);
+extern unsigned long pci_sun4v_msi_getstate(unsigned long devhandle,
+					    unsigned long msinum,
+					    unsigned long *state);
+extern unsigned long pci_sun4v_msi_setstate(unsigned long devhandle,
+					    unsigned long msinum,
+					    unsigned long state);
+extern unsigned long pci_sun4v_msg_getmsiq(unsigned long devhandle,
+					   unsigned long msinum,
+					   unsigned long *msiq);
+extern unsigned long pci_sun4v_msg_setmsiq(unsigned long devhandle,
+					   unsigned long msinum,
+					   unsigned long msiq);
+extern unsigned long pci_sun4v_msg_getvalid(unsigned long devhandle,
+					    unsigned long msinum,
+					    unsigned long *valid);
+extern unsigned long pci_sun4v_msg_setvalid(unsigned long devhandle,
+					    unsigned long msinum,
+					    unsigned long valid);
+
 #endif /* !(_PCI_SUN4V_H) */
diff --git a/arch/sparc64/kernel/pci_sun4v_asm.S b/arch/sparc64/kernel/pci_sun4v_asm.S
index 6604fdb..ecb81f3 100644
--- a/arch/sparc64/kernel/pci_sun4v_asm.S
+++ b/arch/sparc64/kernel/pci_sun4v_asm.S
@@ -93,3 +93,269 @@
 	 mov	-1, %o1
 1:	retl
 	 mov	%o1, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2: msiq phys address
+	 * %o3: num entries
+	 *
+	 * returns %o0: status
+	 *
+	 * status will be zero if the operation completed
+	 * successfully, else -1 if not
+	 */
+	.globl	pci_sun4v_msiq_conf
+pci_sun4v_msiq_conf:
+	mov	HV_FAST_PCI_MSIQ_CONF, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	&msiq_phys_addr
+	 * %o3:	&msiq_num_entries
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_info
+pci_sun4v_msiq_info:
+	mov	%o2, %o4
+	mov	HV_FAST_PCI_MSIQ_INFO, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o4]
+	stx	%o2, [%o3]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	&valid
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_getvalid
+pci_sun4v_msiq_getvalid:
+	mov	HV_FAST_PCI_MSIQ_GETVALID, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	valid
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_setvalid
+pci_sun4v_msiq_setvalid:
+	mov	HV_FAST_PCI_MSIQ_SETVALID, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	&state
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_getstate
+pci_sun4v_msiq_getstate:
+	mov	HV_FAST_PCI_MSIQ_GETSTATE, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	state
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_setstate
+pci_sun4v_msiq_setstate:
+	mov	HV_FAST_PCI_MSIQ_SETSTATE, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	&head
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_gethead
+pci_sun4v_msiq_gethead:
+	mov	HV_FAST_PCI_MSIQ_GETHEAD, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	head
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_sethead
+pci_sun4v_msiq_sethead:
+	mov	HV_FAST_PCI_MSIQ_SETHEAD, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msiqid
+	 * %o2:	&tail
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msiq_gettail
+pci_sun4v_msiq_gettail:
+	mov	HV_FAST_PCI_MSIQ_GETTAIL, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	&valid
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msi_getvalid
+pci_sun4v_msi_getvalid:
+	mov	HV_FAST_PCI_MSI_GETVALID, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	valid
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msi_setvalid
+pci_sun4v_msi_setvalid:
+	mov	HV_FAST_PCI_MSI_SETVALID, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	&msiq
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msi_getmsiq
+pci_sun4v_msi_getmsiq:
+	mov	HV_FAST_PCI_MSI_GETMSIQ, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	msitype
+	 * %o3:	msiq
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msi_setmsiq
+pci_sun4v_msi_setmsiq:
+	mov	HV_FAST_PCI_MSI_SETMSIQ, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	&state
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msi_getstate
+pci_sun4v_msi_getstate:
+	mov	HV_FAST_PCI_MSI_GETSTATE, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	state
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msi_setstate
+pci_sun4v_msi_setstate:
+	mov	HV_FAST_PCI_MSI_SETSTATE, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	&msiq
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msg_getmsiq
+pci_sun4v_msg_getmsiq:
+	mov	HV_FAST_PCI_MSG_GETMSIQ, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	msiq
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msg_setmsiq
+pci_sun4v_msg_setmsiq:
+	mov	HV_FAST_PCI_MSG_SETMSIQ, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	&valid
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msg_getvalid
+pci_sun4v_msg_getvalid:
+	mov	HV_FAST_PCI_MSG_GETVALID, %o5
+	ta	HV_FAST_TRAP
+	stx	%o1, [%o2]
+	retl
+	 mov	%o0, %o0
+
+	/* %o0: devhandle
+	 * %o1: msinum
+	 * %o2:	valid
+	 *
+	 * returns %o0: status
+	 */
+	.globl	pci_sun4v_msg_setvalid
+pci_sun4v_msg_setvalid:
+	mov	HV_FAST_PCI_MSG_SETVALID, %o5
+	ta	HV_FAST_TRAP
+	retl
+	 mov	%o0, %o0
+
diff --git a/block/ioctl.c b/block/ioctl.c
index f6962b6..e3f5eb9 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -61,7 +61,7 @@
 				}
 			}
 			/* all seems OK */
-			add_partition(disk, part, start, length);
+			add_partition(disk, part, start, length, ADDPART_FLAG_NONE);
 			mutex_unlock(&bdev->bd_mutex);
 			return 0;
 		case BLKPG_DEL_PARTITION:
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 3cfb0a3..5ea5bc7 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -4,7 +4,7 @@
 config PCI_MSI
 	bool "Message Signaled Interrupts (MSI and MSI-X)"
 	depends on PCI
-	depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64
+	depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64 || SPARC64
 	help
 	   This allows device drivers to enable MSI (Message Signaled
 	   Interrupts).  Message Signaled Interrupts enable a device to
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
index 98fcbb3..6349dd6 100644
--- a/drivers/sbus/sbus.c
+++ b/drivers/sbus/sbus.c
@@ -7,6 +7,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/device.h>
 
 #include <asm/system.h>
 #include <asm/sbus.h>
@@ -17,13 +18,25 @@
 #include <asm/bpp.h>
 #include <asm/irq.h>
 
+static ssize_t
+show_sbusobppath_attr(struct device * dev, struct device_attribute * attr, char * buf)
+{
+	struct sbus_dev *sbus;
+
+	sbus = to_sbus_device(dev);
+
+	return snprintf (buf, PAGE_SIZE, "%s\n", sbus->ofdev.node->full_name);
+}
+
+static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_sbusobppath_attr, NULL);
+
 struct sbus_bus *sbus_root;
 
 static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev)
 {
 	unsigned long base;
 	void *pval;
-	int len;
+	int len, err;
 
 	sdev->prom_node = dp->node;
 	strcpy(sdev->prom_name, dp->name);
@@ -66,6 +79,9 @@
 	if (of_device_register(&sdev->ofdev) != 0)
 		printk(KERN_DEBUG "sbus: device registration error for %s!\n",
 		       dp->path_component_name);
+
+	/* WE HAVE BEEN INVADED BY ALIENS! */
+	err = sysfs_create_file(&sdev->ofdev.dev.kobj, &dev_attr_obppath.attr);
 }
 
 static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus)
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 3d73d94..ac32a2e 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -365,7 +365,7 @@
 	kobject_put(&p->kobj);
 }
 
-void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len)
+void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags)
 {
 	struct hd_struct *p;
 
@@ -390,6 +390,15 @@
 	if (!disk->part_uevent_suppress)
 		kobject_uevent(&p->kobj, KOBJ_ADD);
 	sysfs_create_link(&p->kobj, &block_subsys.kset.kobj, "subsystem");
+	if (flags & ADDPART_FLAG_WHOLEDISK) {
+		static struct attribute addpartattr = {
+			.name = "whole_disk",
+			.mode = S_IRUSR | S_IRGRP | S_IROTH,
+			.owner = THIS_MODULE,
+		};
+
+		sysfs_create_file(&p->kobj, &addpartattr);
+	}
 	partition_sysfs_add_subdir(p);
 	disk->part[part-1] = p;
 }
@@ -543,9 +552,9 @@
 			printk(" %s: p%d exceeds device capacity\n",
 				disk->disk_name, p);
 		}
-		add_partition(disk, p, from, size);
+		add_partition(disk, p, from, size, state->parts[p].flags);
 #ifdef CONFIG_BLK_DEV_MD
-		if (state->parts[p].flags)
+		if (state->parts[p].flags & ADDPART_FLAG_RAID)
 			md_autodetect_dev(bdev->bd_dev+p);
 #endif
 	}
diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c
index 78443e0..4ccec4c 100644
--- a/fs/partitions/msdos.c
+++ b/fs/partitions/msdos.c
@@ -165,7 +165,7 @@
 
 			put_partition(state, state->next, next, size);
 			if (SYS_IND(p) == LINUX_RAID_PARTITION)
-				state->parts[state->next].flags = 1;
+				state->parts[state->next].flags = ADDPART_FLAG_RAID;
 			loopct = 0;
 			if (++state->next == state->limit)
 				goto done;
diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c
index 6fa4ff8..ed5ac83 100644
--- a/fs/partitions/sgi.c
+++ b/fs/partitions/sgi.c
@@ -72,7 +72,7 @@
 		if (blocks) {
 			put_partition(state, slot, start, blocks);
 			if (be32_to_cpu(p->type) == LINUX_RAID_PARTITION)
-				state->parts[slot].flags = 1;
+				state->parts[slot].flags = ADDPART_FLAG_RAID;
 		}
 		slot++;
 	}
diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c
index 0a5927c..123f8b4 100644
--- a/fs/partitions/sun.c
+++ b/fs/partitions/sun.c
@@ -80,8 +80,11 @@
 		num_sectors = be32_to_cpu(p->num_sectors);
 		if (num_sectors) {
 			put_partition(state, slot, st_sector, num_sectors);
+			state->parts[slot].flags = 0;
 			if (label->infos[i].id == LINUX_RAID_PARTITION)
-				state->parts[slot].flags = 1;
+				state->parts[slot].flags |= ADDPART_FLAG_RAID;
+			if (label->infos[i].id == SUN_WHOLE_DISK)
+				state->parts[slot].flags |= ADDPART_FLAG_WHOLEDISK;
 		}
 		slot++;
 	}
diff --git a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h
index 905e59b..5d233b4 100644
--- a/include/asm-sparc64/irq.h
+++ b/include/asm-sparc64/irq.h
@@ -46,6 +46,10 @@
 #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_msi(u32 devhandle, unsigned int *virt_irq_p,
+				    unsigned int msi_devino_start,
+				    unsigned int msi_devino_end);
+extern void sun4v_destroy_msi(unsigned int virt_irq);
 extern unsigned int sbus_build_irq(void *sbus, unsigned int ino);
 
 static __inline__ void set_softint(unsigned long bits)
diff --git a/include/asm-sparc64/pbm.h b/include/asm-sparc64/pbm.h
index dcfa762..7a246d8 100644
--- a/include/asm-sparc64/pbm.h
+++ b/include/asm-sparc64/pbm.h
@@ -11,6 +11,7 @@
 #include <linux/pci.h>
 #include <linux/ioport.h>
 #include <linux/spinlock.h>
+#include <linux/msi.h>
 
 #include <asm/io.h>
 #include <asm/page.h>
@@ -177,6 +178,24 @@
 	int				is_66mhz_capable;
 	int				all_devs_66mhz;
 
+#ifdef CONFIG_PCI_MSI
+	/* MSI info.  */
+	u32				msiq_num;
+	u32				msiq_ent_count;
+	u32				msiq_first;
+	u32				msiq_first_devino;
+	u32				msi_num;
+	u32				msi_first;
+	u32				msi_data_mask;
+	u32				msix_data_width;
+	u64				msi32_start;
+	u64				msi64_start;
+	u32				msi32_len;
+	u32				msi64_len;
+	void				*msi_queues;
+	unsigned long			*msi_bitmap;
+#endif /* !(CONFIG_PCI_MSI) */
+
 	/* This PBM's streaming buffer. */
 	struct pci_strbuf		stc;
 
@@ -213,6 +232,12 @@
 	void (*base_address_update)(struct pci_dev *, int);
 	void (*resource_adjust)(struct pci_dev *, struct resource *, struct resource *);
 
+#ifdef CONFIG_PCI_MSI
+	int (*setup_msi_irq)(unsigned int *virt_irq_p, struct pci_dev *pdev,
+			     struct msi_desc *entry);
+	void (*teardown_msi_irq)(unsigned int virt_irq, struct pci_dev *pdev);
+#endif
+
 	/* Now things for the actual PCI bus probes. */
 	struct pci_ops			*pci_ops;
 	unsigned int			pci_first_busno;
@@ -231,6 +256,9 @@
 	int num_prom_regs;
 	struct linux_prom_pci_registers prom_assignments[PROMREG_MAX];
 	int num_prom_assignments;
+#ifdef CONFIG_PCI_MSI
+	unsigned int			msi_num;
+#endif
 };
 
 /* Currently these are the same across all PCI controllers
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 7a566fa..2c65da7 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -20,6 +20,8 @@
 	LINUX_EXTENDED_PARTITION = 0x85,
 	WIN98_EXTENDED_PARTITION = 0x0f,
 
+	SUN_WHOLE_DISK = DOS_EXTENDED_PARTITION,
+
 	LINUX_SWAP_PARTITION = 0x82,
 	LINUX_DATA_PARTITION = 0x83,
 	LINUX_LVM_PARTITION = 0x8e,
@@ -402,10 +404,14 @@
 
 #ifdef __KERNEL__
 
+#define ADDPART_FLAG_NONE	0
+#define ADDPART_FLAG_RAID	1
+#define ADDPART_FLAG_WHOLEDISK	2
+
 char *disk_name (struct gendisk *hd, int part, char *buf);
 
 extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev);
-extern void add_partition(struct gendisk *, int, sector_t, sector_t);
+extern void add_partition(struct gendisk *, int, sector_t, sector_t, int);
 extern void delete_partition(struct gendisk *, int);
 
 extern struct gendisk *alloc_disk_node(int minors, int node_id);