Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 2908

kernel-2.6.18-194.11.1.el5.src.rpm

From: Brad Peters <bpeters@redhat.com>
Date: Wed, 5 Mar 2008 17:26:36 -0500
Subject: [ppc64] broken MSI on cell blades when IOMMU is on
Message-id: 20080305222636.GF8084@bpeters-ibm
O-Subject: Re: [RHEL5 U2 PATCH 1/1] - MSI is broken on cell blades when IOMMU is on
Bugzilla: 430949

RHBZ#:
------
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=430949

Author: Michael Ellerman <michael@ellerman.id.au>
Ported to 2.6.18-80 by: Brad Peters <bpeters@redhat.com>

Description:
------------
Overview:
Search for and publish cell OpenFirmware platform devices earlier.
Currently cell publishes OF devices at device_initcall() time, which
means the earliest a driver can bind to a device is also device_initcall()
time. We have a driver we want to register before other devices, so
publish the devices at subsys_initcall() time.

This should not cause any behaviour change for existing drivers, as they
are still bound at device_initcall() time.

- Although of_platform_device's can have a shutdown routine, at the moment
the bus code doesn't actually call it. So add the required glue to
hook the shutdown routine.

- Now that we create of_platform devices earlier on cell, we can make the
axon_msi driver an of_platform driver. This makes the code cleaner in
several ways, and most importantly means we have a struct device.

- There's a brown-paper-bag bug in axon_msi, we pass the address of our
FIFO directly to the hardware, without DMA mapping it. This leads to
DMA exceptions if you enable MSI & the IOMMU.

The fix is to correctly DMA map the fifo, dma_alloc_coherent() does
what we want - and we need to track the virt & phys addresses.

RHEL Version Found:
------------------
5.0

kABI Status:
------------
No symbols harmed.

Kernel:
-----
http://people.redhat.com/bpeters/kernels/kernel-2.6.18-80.el5.80.el5.430949.ppc64.rpm

Upstream Status:
----------------
now accepted upstream in kernel:
2.6.25-rc1 ... see
http://kernel.org/pub/linux/kernel/v2.6/testing/ChangeLog-2.6.25-rc1

Test Status:
------------
Verified boot success.

Best regards,

Brad Peters

[1]:
http://post-office.corp.redhat.com/archives/rhkernel-list/2008-February/msg00299.html

Acked-by: David Howells <dhowells@redhat.com>

diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c
index 9671c58..13b1adc 100644
--- a/arch/powerpc/kernel/of_platform.c
+++ b/arch/powerpc/kernel/of_platform.c
@@ -131,6 +131,15 @@ static int of_platform_device_resume(struct device * dev)
 	return error;
 }
 
+static void of_platform_device_shutdown(struct device *dev)
+{
+	struct of_device *of_dev = to_of_device(dev);
+	struct of_platform_driver *drv = to_of_platform_driver(dev->driver);
+
+	if (dev->driver && drv->shutdown)
+		drv->shutdown(of_dev);
+}
+
 struct bus_type of_platform_bus_type = {
        .name	= "of_platform",
        .match	= of_platform_bus_match,
@@ -138,6 +147,7 @@ struct bus_type of_platform_bus_type = {
        .remove	= of_platform_device_remove,
        .suspend	= of_platform_device_suspend,
        .resume	= of_platform_device_resume,
+       .shutdown = of_platform_device_shutdown,
 };
 EXPORT_SYMBOL(of_platform_bus_type);
 
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index 180c8ef..0fd3e53 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -12,8 +12,8 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
-#include <linux/reboot.h>
 
+#include <asm/of_device.h>
 #include <asm/dcr.h>
 #include <asm/machdep.h>
 #include <asm/msi.h>
@@ -66,15 +66,13 @@
 struct axon_msic {
 	struct device_node *dn;
 	struct irq_host *irq_host;
-	__le32 *fifo;
+	__le32 *fifo_virt;
+	dma_addr_t fifo_phys;
 	dcr_host_t dcr_host;
-	struct list_head list;
 	u32 read_offset;
 	u32 dcr_base;
 };
 
-static LIST_HEAD(axon_msic_list);
-
 static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val)
 {
 	pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n);
@@ -102,7 +100,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc,
 
 	while (msic->read_offset != write_offset) {
 		idx  = msic->read_offset / sizeof(__le32);
-		msi  = le32_to_cpu(msic->fifo[idx]);
+		msi  = le32_to_cpu(msic->fifo_virt[idx]);
 		msi &= 0xFFFF;
 
 		pr_debug("axon_msi: woff %x roff %x msi %x\n",
@@ -309,29 +307,24 @@ static struct irq_host_ops msic_host_ops = {
 	.map	= msic_host_map,
 };
 
-static int axon_msi_notify_reboot(struct notifier_block *nb,
-				  unsigned long code, void *data)
+static int axon_msi_shutdown(struct of_device *device)
 {
-	struct axon_msic *msic;
+	struct axon_msic *msic = device->dev.platform_data;
 	u32 tmp;
 
-	list_for_each_entry(msic, &axon_msic_list, list) {
-		pr_debug("axon_msi: disabling %s\n", msic->dn->full_name);
-		tmp  = msic_dcr_read(msic, MSIC_CTRL_REG);
-		tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
-		msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
-	}
+	pr_debug("axon_msi: disabling %s\n", msic->dn->full_name);
+	tmp  = msic_dcr_read(msic, MSIC_CTRL_REG);
+	tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
+	msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
 
 	return 0;
 }
 
-static struct notifier_block axon_msi_reboot_notifier = {
-	.notifier_call = axon_msi_notify_reboot
-};
-
-static int axon_msi_setup_one(struct device_node *dn)
+static int axon_msi_probe(struct of_device *device,
+			const struct of_device_id *device_id)
 {
 	struct page *page;
+	struct device_node *dn = device->node;
 	struct axon_msic *msic;
 	unsigned int virq;
 	int dcr_len;
@@ -362,16 +355,14 @@ static int axon_msi_setup_one(struct device_node *dn)
 		goto out_free_msic;
 	}
 
-	page = alloc_pages_node(of_node_to_nid(dn), GFP_KERNEL,
-				get_order(MSIC_FIFO_SIZE_BYTES));
-	if (!page) {
+	msic->fifo_virt = dma_alloc_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES,
+						&msic->fifo_phys, GFP_KERNEL);
+	if (!msic->fifo_virt) {
 		printk(KERN_ERR "axon_msi: couldn't allocate fifo for %s\n",
 		       dn->full_name);
 		goto out_free_msic;
 	}
 
-	msic->fifo = page_address(page);
-
 	msic->irq_host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, NR_IRQS,
 					&msic_host_ops, 0);
 	if (!msic->irq_host) {
@@ -396,14 +387,18 @@ static int axon_msi_setup_one(struct device_node *dn)
 	pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq);
 
 	/* Enable the MSIC hardware */
-	msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, (u64)msic->fifo >> 32);
+	msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32);
 	msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG,
-				  (u64)msic->fifo & 0xFFFFFFFF);
+					msic->fifo_phys & 0xFFFFFFFF);
 	msic_dcr_write(msic, MSIC_CTRL_REG,
 			MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE |
 			MSIC_CTRL_FIFO_SIZE);
 
-	list_add(&msic->list, &axon_msic_list);
+	device->dev.platform_data = msic;
+
+	ppc_msi_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
+	ppc_msi_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
+	ppc_msi_md.msi_check_device = axon_msi_check_device;
 
 	printk(KERN_DEBUG "axon_msi: setup MSIC on %s\n", dn->full_name);
 
@@ -412,7 +407,8 @@ static int axon_msi_setup_one(struct device_node *dn)
 out_free_host:
 	kfree(msic->irq_host);
 out_free_fifo:
-	__free_pages(virt_to_page(msic->fifo), get_order(MSIC_FIFO_SIZE_BYTES));
+	dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt,
+			msic->fifo_phys);
 out_free_msic:
 	kfree(msic);
 out:
@@ -420,28 +416,26 @@ out:
 	return -1;
 }
 
-static int axon_msi_init(void)
-{
-	struct device_node *dn;
-	int found = 0;
-
-	pr_debug("axon_msi: initialising ...\n");
 
-	for_each_compatible_node(dn, NULL, "ibm,axon-msic") {
-		if (axon_msi_setup_one(dn) == 0)
-			found++;
-	}
-
-	if (found) {
-		ppc_msi_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
-		ppc_msi_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
-		ppc_msi_md.msi_check_device = axon_msi_check_device;
-
-		register_reboot_notifier(&axon_msi_reboot_notifier);
+static struct of_device_id axon_msi_device_id[] = {
+	{
+		.compatible     = "ibm,axon-msic"
+	},
+	{}
+};
 
-		pr_debug("axon_msi: registered callbacks!\n");
-	}
+static struct of_platform_driver axon_msi_driver = {
+	.match_table    = axon_msi_device_id,
+	.probe          = axon_msi_probe,
+	.shutdown       = axon_msi_shutdown,
+	.driver         = {
+		.name = "axon-msi"
+	},
+};
 
-	return 0;
+static int __init axon_msi_init(void)
+{
+	return of_register_platform_driver(&axon_msi_driver);
 }
-arch_initcall(axon_msi_init);
+
+subsys_initcall(axon_msi_init);
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c
index d9182b0..2693d17 100644
--- a/arch/powerpc/platforms/cell/setup.c
+++ b/arch/powerpc/platforms/cell/setup.c
@@ -95,7 +95,7 @@ static int __init cell_publish_devices(void)
 		of_platform_bus_probe(NULL, NULL, NULL);
 	return 0;
 }
-device_initcall(cell_publish_devices);
+subsys_initcall(cell_publish_devices);
 
 static void cell_mpic_cascade(unsigned int irq, struct irq_desc *desc,
 			      struct pt_regs *regs)