[PATCH] powerpc: pci_64 fixes & cleanups

Benjamin Herrenschmidt benh at kernel.crashing.org
Tue Nov 15 16:05:33 EST 2005


I discovered that in some cases (PowerMac for example) we wouldn't
properly map the PCI IO space on recent kernels. In addition, the code
for initializing PCI host bridges was scattered all over the place with
some duplication between platforms.

This patch fixes the problem and does a small cleanup by creating a
pcibios_alloc_controller() in pci_64.c that is similar to the one in
pci_32.c (just takes an additional device node argument) that takes care
of all the grunt allocation and initialisation work. It should work for
both boot time and dynamically allocated PHBs.

Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
---

John, can you give it a spin with pci hotplug and see if I didn't break
anything there ? Thanks !

Index: linux-work/arch/powerpc/kernel/rtas_pci.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/rtas_pci.c	2005-11-11 10:14:48.000000000 +1100
+++ linux-work/arch/powerpc/kernel/rtas_pci.c	2005-11-15 14:58:09.000000000 +1100
@@ -304,75 +304,18 @@
 			       struct pci_controller *phb,
 			       unsigned int addr_size_words)
 {
-	pci_setup_pci_controller(phb);
-
 	if (is_python(dev))
 		python_countermeasures(dev, addr_size_words);
 
 	if (phb_set_bus_ranges(dev, phb))
 		return 1;
 
-	phb->arch_data = dev;
 	phb->ops = &rtas_pci_ops;
 	phb->buid = get_phb_buid(dev);
 
 	return 0;
 }
 
-static void __devinit add_linux_pci_domain(struct device_node *dev,
-					   struct pci_controller *phb,
-					   struct property *of_prop)
-{
-	memset(of_prop, 0, sizeof(struct property));
-	of_prop->name = "linux,pci-domain";
-	of_prop->length = sizeof(phb->global_number);
-	of_prop->value = (unsigned char *)&of_prop[1];
-	memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number));
-	prom_add_property(dev, of_prop);
-}
-
-static struct pci_controller * __init alloc_phb(struct device_node *dev,
-						unsigned int addr_size_words)
-{
-	struct pci_controller *phb;
-	struct property *of_prop;
-
-	phb = alloc_bootmem(sizeof(struct pci_controller));
-	if (phb == NULL)
-		return NULL;
-
-	of_prop = alloc_bootmem(sizeof(struct property) +
-				sizeof(phb->global_number));
-	if (!of_prop)
-		return NULL;
-
-	if (setup_phb(dev, phb, addr_size_words))
-		return NULL;
-
-	add_linux_pci_domain(dev, phb, of_prop);
-
-	return phb;
-}
-
-static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words)
-{
-	struct pci_controller *phb;
-
-	phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller),
-					       GFP_KERNEL);
-	if (phb == NULL)
-		return NULL;
-
-	if (setup_phb(dev, phb, addr_size_words))
-		return NULL;
-
-	phb->is_dynamic = 1;
-
-	/* TODO: linux,pci-domain? */
-
- 	return phb;
-}
-
 unsigned long __init find_and_init_phbs(void)
 {
 	struct device_node *node;
@@ -397,10 +340,10 @@
 		if (node->type == NULL || strcmp(node->type, "pci") != 0)
 			continue;
 
-		phb = alloc_phb(node, root_size_cells);
+		phb = pcibios_alloc_controller(node);
 		if (!phb)
 			continue;
-
+		setup_phb(node, phb, root_size_cells);
 		pci_process_bridge_OF_ranges(phb, node, 0);
 		pci_setup_phb_io(phb, index == 0);
 #ifdef CONFIG_PPC_PSERIES
@@ -446,10 +389,10 @@
 	root_size_cells = prom_n_size_cells(root);
 
 	primary = list_empty(&hose_list);
-	phb = alloc_phb_dynamic(dn, root_size_cells);
+	phb = pcibios_alloc_controller(dn);
 	if (!phb)
 		return NULL;
-
+	setup_phb(dn, phb, root_size_cells);
 	pci_process_bridge_OF_ranges(phb, dn, primary);
 
 	pci_setup_phb_io_dynamic(phb, primary);
@@ -505,8 +448,7 @@
 	}
 
 	list_del(&phb->list_node);
-	if (phb->is_dynamic)
-		kfree(phb);
+	pcibios_free_controller(phb);
 
 	return 0;
 }
Index: linux-work/arch/powerpc/platforms/iseries/pci.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/iseries/pci.c	2005-11-09 11:49:03.000000000 +1100
+++ linux-work/arch/powerpc/platforms/iseries/pci.c	2005-11-15 14:41:29.000000000 +1100
@@ -244,10 +244,9 @@
 		if (ret == 0) {
 			printk("bus %d appears to exist\n", bus);
 
-			phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), GFP_KERNEL);
+			phb = pcibios_alloc_controller(NULL);
 			if (phb == NULL)
 				return -ENOMEM;
-			pci_setup_pci_controller(phb);
 
 			phb->pci_mem_offset = phb->local_number = bus;
 			phb->first_busno = bus;
Index: linux-work/arch/powerpc/platforms/maple/pci.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/maple/pci.c	2005-11-11 10:14:48.000000000 +1100
+++ linux-work/arch/powerpc/platforms/maple/pci.c	2005-11-15 14:41:29.000000000 +1100
@@ -326,26 +326,12 @@
 		dev->full_name);
 	}
 
-	hose = alloc_bootmem(sizeof(struct pci_controller));
+	hose = pcibios_alloc_controller(dev);
 	if (hose == NULL)
 		return -ENOMEM;
-	pci_setup_pci_controller(hose);
-
-	hose->arch_data = dev;
 	hose->first_busno = bus_range ? bus_range[0] : 0;
 	hose->last_busno = bus_range ? bus_range[1] : 0xff;
 
-	of_prop = alloc_bootmem(sizeof(struct property) +
-				sizeof(hose->global_number));
-	if (of_prop) {
-		memset(of_prop, 0, sizeof(struct property));
-		of_prop->name = "linux,pci-domain";
-		of_prop->length = sizeof(hose->global_number);
-		of_prop->value = (unsigned char *)&of_prop[1];
-		memcpy(of_prop->value, &hose->global_number, sizeof(hose->global_number));
-		prom_add_property(dev, of_prop);
-	}
-
 	disp_name = NULL;
 	if (device_is_compatible(dev, "u3-agp")) {
 		setup_u3_agp(hose);
Index: linux-work/arch/powerpc/platforms/powermac/pci.c
===================================================================
--- linux-work.orig/arch/powerpc/platforms/powermac/pci.c	2005-11-11 10:14:48.000000000 +1100
+++ linux-work/arch/powerpc/platforms/powermac/pci.c	2005-11-15 14:41:29.000000000 +1100
@@ -640,15 +640,16 @@
 	 * the reg address cell, we shall fix that by killing struct
 	 * reg_property and using some accessor functions instead
 	 */
-	hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000, 0x02000000);
+	hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000,
+							   0x02000000);
 
 	/*
-	 * /ht node doesn't expose a "ranges" property, so we "remove" regions that
-	 * have been allocated to AGP. So far, this version of the code doesn't assign
-	 * any of the 0xfxxxxxxx "fine" memory regions to /ht.
-	 * We need to fix that sooner or later by either parsing all child "ranges"
-	 * properties or figuring out the U3 address space decoding logic and
-	 * then read its configuration register (if any).
+	 * /ht node doesn't expose a "ranges" property, so we "remove"
+	 * regions that have been allocated to AGP. So far, this version of
+	 * the code doesn't assign any of the 0xfxxxxxxx "fine" memory regions
+	 * to /ht. We need to fix that sooner or later by either parsing all
+	 * child "ranges" properties or figuring out the U3 address space
+	 * decoding logic and then read its configuration register (if any).
 	 */
 	hose->io_base_phys = 0xf4000000;
 	hose->pci_io_size = 0x00400000;
@@ -671,10 +672,10 @@
 		return;
 	}
 
-	/* We "remove" the AGP resources from the resources allocated to HT, that
-	 * is we create "holes". However, that code does assumptions that so far
-	 * happen to be true (cross fingers...), typically that resources in the
-	 * AGP node are properly ordered
+	/* We "remove" the AGP resources from the resources allocated to HT,
+	 * that is we create "holes". However, that code does assumptions
+	 * that so far happen to be true (cross fingers...), typically that
+	 * resources in the AGP node are properly ordered
 	 */
 	cur = 0;
 	for (i=0; i<3; i++) {
@@ -684,23 +685,30 @@
 		/* We don't care about "fine" resources */
 		if (res->start >= 0xf0000000)
 			continue;
-		/* Check if it's just a matter of "shrinking" us in one direction */
+		/* Check if it's just a matter of "shrinking" us in one
+		 * direction
+		 */
 		if (hose->mem_resources[cur].start == res->start) {
 			DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n",
-			    cur, hose->mem_resources[cur].start, res->end + 1);
+			    cur, hose->mem_resources[cur].start,
+			    res->end + 1);
 			hose->mem_resources[cur].start = res->end + 1;
 			continue;
 		}
 		if (hose->mem_resources[cur].end == res->end) {
 			DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n",
-			    cur, hose->mem_resources[cur].end, res->start - 1);
+			    cur, hose->mem_resources[cur].end,
+			    res->start - 1);
 			hose->mem_resources[cur].end = res->start - 1;
 			continue;
 		}
 		/* No, it's not the case, we need a hole */
 		if (cur == 2) {
-			/* not enough resources for a hole, we drop part of the range */
-			printk(KERN_WARNING "Running out of resources for /ht host !\n");
+			/* not enough resources for a hole, we drop part
+			 * of the range
+			 */
+			printk(KERN_WARNING "Running out of resources"
+			       " for /ht host !\n");
 			hose->mem_resources[cur].end = res->start - 1;
 			continue;
 		}
@@ -714,17 +722,6 @@
 		hose->mem_resources[cur-1].end = res->start - 1;
 	}
 }
-
-/* XXX this needs to be converged between ppc32 and ppc64... */
-static struct pci_controller * __init pcibios_alloc_controller(void)
-{
-	struct pci_controller *hose;
-
-	hose = alloc_bootmem(sizeof(struct pci_controller));
-	if (hose)
-		pci_setup_pci_controller(hose);
-	return hose;
-}
 #endif
 
 /*
@@ -756,11 +753,16 @@
 #endif
 	bus_range = (int *) get_property(dev, "bus-range", &len);
 	if (bus_range == NULL || len < 2 * sizeof(int)) {
-		printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
-			       dev->full_name);
+		printk(KERN_WARNING "Can't get bus-range for %s, assume"
+		       " bus 0\n", dev->full_name);
 	}
 
+	/* XXX Different prototypes, to be merged */
+#ifdef CONFIG_PPC64
+	hose = pcibios_alloc_controller(dev);
+#else
 	hose = pcibios_alloc_controller();
+#endif
 	if (!hose)
 		return -ENOMEM;
 	hose->arch_data = dev;
@@ -768,7 +770,7 @@
 	hose->last_busno = bus_range ? bus_range[1] : 0xff;
 
 	disp_name = NULL;
-#ifdef CONFIG_POWER4
+#ifdef CONFIG_PPC64
 	if (device_is_compatible(dev, "u3-agp")) {
 		setup_u3_agp(hose);
 		disp_name = "U3-AGP";
Index: linux-work/include/asm-ppc64/pci-bridge.h
===================================================================
--- linux-work.orig/include/asm-ppc64/pci-bridge.h	2005-11-11 10:14:49.000000000 +1100
+++ linux-work/include/asm-ppc64/pci-bridge.h	2005-11-15 14:59:23.000000000 +1100
@@ -61,12 +61,14 @@
 	int	busno;			/* for pci devices */
 	int	bussubno;		/* for pci devices */
 	int	devfn;			/* for pci devices */
+
+#ifdef CONFIG_PPC_PSERIES
 	int	eeh_mode;		/* See eeh.h for possible EEH_MODEs */
 	int	eeh_config_addr;
 	int 	eeh_check_count;	/* # times driver ignored error */
 	int 	eeh_freeze_count;	/* # times this device froze up. */
 	int	eeh_is_bridge;		/* device is pci-to-pci bridge */
-
+#endif
 	int	pci_ext_config_space;	/* for pci devices */
 	struct  pci_controller *phb;	/* for pci devices */
 	struct	iommu_table *iommu_table;	/* for phb's or bridges */
@@ -74,9 +76,9 @@
 	struct	device_node *node;	/* back-pointer to the device_node */
 #ifdef CONFIG_PPC_ISERIES
 	struct	list_head Device_List;
-	int		Irq;		/* Assigned IRQ */
-	int		Flags;		/* Possible flags(disable/bist)*/
-	u8		LogicalSlot;	/* Hv Slot Index for Tces */
+	int	Irq;			/* Assigned IRQ */
+	int	Flags;			/* Possible flags(disable/bist)*/
+	u8	LogicalSlot;		/* Hv Slot Index for Tces */
 #endif
 	u32	config_space[16];	/* saved PCI config space */
 };
@@ -136,6 +138,10 @@
 	return PCI_DN(busdn)->phb;
 }
 
+extern struct pci_controller *
+pcibios_alloc_controller(struct device_node *dev);
+extern void pcibios_free_controller(struct pci_controller *phb);
+
 /* Return values for ppc_md.pci_probe_mode function */
 #define PCI_PROBE_NONE		-1	/* Don't look at this bus at all */
 #define PCI_PROBE_NORMAL	0	/* Do normal PCI probing */
Index: linux-work/arch/powerpc/kernel/pci_64.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/pci_64.c	2005-11-15 13:31:57.000000000 +1100
+++ linux-work/arch/powerpc/kernel/pci_64.c	2005-11-15 15:53:07.000000000 +1100
@@ -187,7 +187,7 @@
 /*
  * pci_controller(phb) initialized common variables.
  */
-void __devinit pci_setup_pci_controller(struct pci_controller *hose)
+static void __devinit pci_setup_pci_controller(struct pci_controller *hose)
 {
 	memset(hose, 0, sizeof(struct pci_controller));
 
@@ -197,6 +197,65 @@
 	spin_unlock(&hose_spinlock);
 }
 
+static void add_linux_pci_domain(struct device_node *dev,
+				 struct pci_controller *phb)
+{
+	struct property *of_prop;
+	unsigned int size;
+
+	of_prop = (struct property *)
+		get_property(dev, "linux,pci-domain", &size);
+	if (of_prop != NULL)
+		return;
+	WARN_ON(of_prop && size < sizeof(int));
+	if (of_prop && size < sizeof(int))
+		of_prop = NULL;
+	size = sizeof(struct property) + sizeof(int);
+	if (of_prop == NULL) {
+		if (mem_init_done)
+			of_prop = kmalloc(size, GFP_KERNEL);
+		else
+			of_prop = alloc_bootmem(size);
+	}
+	memset(of_prop, 0, sizeof(struct property));
+	of_prop->name = "linux,pci-domain";
+	of_prop->length = sizeof(int);
+	of_prop->value = (unsigned char *)&of_prop[1];
+	*((int *)of_prop->value) = phb->global_number;
+	prom_add_property(dev, of_prop);
+}
+
+struct pci_controller * pcibios_alloc_controller(struct device_node *dev)
+{
+	struct pci_controller *phb;
+
+	if (mem_init_done)
+		phb = kmalloc(sizeof(struct pci_controller), GFP_KERNEL);
+	else
+		phb = alloc_bootmem(sizeof (struct pci_controller));
+	if (phb == NULL)
+		return NULL;
+	pci_setup_pci_controller(phb);
+	phb->arch_data = dev;
+	phb->is_dynamic = mem_init_done;
+	if (dev)
+		add_linux_pci_domain(dev, phb);
+	return phb;
+}
+
+void pcibios_free_controller(struct pci_controller *phb)
+{
+	if (phb->arch_data) {
+		struct device_node *np = phb->arch_data;
+		int *domain = (int *)get_property(np,
+						  "linux,pci-domain", NULL);
+		if (domain)
+			*domain = -1;
+	}
+	if (phb->is_dynamic)
+		kfree(phb);
+}
+
 static void __init pcibios_claim_one_bus(struct pci_bus *b)
 {
 	struct pci_dev *dev;
@@ -907,9 +966,10 @@
 	 *			(size depending on dev->n_addr_cells)
 	 *   cells 4+5 or 5+6:	the size of the range
 	 */
-	rlen = 0;
-	hose->io_base_phys = 0;
 	ranges = (unsigned int *) get_property(dev, "ranges", &rlen);
+	if (ranges == NULL)
+		return;
+	hose->io_base_phys = 0;
 	while ((rlen -= np * sizeof(unsigned int)) >= 0) {
 		res = NULL;
 		pci_space = ranges[0];
@@ -1107,6 +1167,8 @@
 	
 	if (get_bus_io_range(bus, &start_phys, &start_virt, &size))
 		return 1;
+	if (start_phys == 0)
+		return 1;
 	printk("mapping IO %lx -> %lx, size: %lx\n", start_phys, start_virt, size);
 	if (__ioremap_explicit(start_phys, start_virt, size,
 			       _PAGE_NO_CACHE | _PAGE_GUARDED))
Index: linux-work/include/asm-powerpc/ppc-pci.h
===================================================================
--- linux-work.orig/include/asm-powerpc/ppc-pci.h	2005-11-11 10:14:49.000000000 +1100
+++ linux-work/include/asm-powerpc/ppc-pci.h	2005-11-15 14:57:24.000000000 +1100
@@ -14,7 +14,6 @@
 
 extern unsigned long isa_io_base;
 
-extern void pci_setup_pci_controller(struct pci_controller *hose);
 extern void pci_setup_phb_io(struct pci_controller *hose, int primary);
 extern void pci_setup_phb_io_dynamic(struct pci_controller *hose, int primary);
 





More information about the Linuxppc64-dev mailing list