[PATCH] powerpc: pci_64 fixes & cleanups

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@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index d7de3ec..5a5b246 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -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))
diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c
index 0e5a8e1..60dec24 100644
--- a/arch/powerpc/kernel/rtas_pci.c
+++ b/arch/powerpc/kernel/rtas_pci.c
@@ -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;
 }
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c
index 4b75131..dafc518 100644
--- a/arch/powerpc/platforms/iseries/pci.c
+++ b/arch/powerpc/platforms/iseries/pci.c
@@ -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;
diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c
index 895aeb3..f40451d 100644
--- a/arch/powerpc/platforms/maple/pci.c
+++ b/arch/powerpc/platforms/maple/pci.c
@@ -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);
diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c
index dfd41b9..443be52 100644
--- a/arch/powerpc/platforms/powermac/pci.c
+++ b/arch/powerpc/platforms/powermac/pci.c
@@ -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";