From: Linda Xie <lxiep@us.ibm.com>

The changes in this patch are for multifunction cards insertions/removals
and  bug fixes:

1. fix up new nodes' linux_phandle field.
2. new nodes' phb, devfn(and so on) need to be fixed even the nodes don't 
	have "interrupts".
3. change of_remove_node to non-recurisve func. The recursions will be done 
	by the caller. 
4. add a new function -- of_finish_dynamic_node_interrupts()


---

 25-akpm/arch/ppc64/kernel/prom.c |  134 +++++++++++++++++----------------------
 1 files changed, 59 insertions(+), 75 deletions(-)

diff -puN arch/ppc64/kernel/prom.c~ppc64-multifunction-fix arch/ppc64/kernel/prom.c
--- 25/arch/ppc64/kernel/prom.c~ppc64-multifunction-fix	2004-03-14 15:32:39.652274680 -0800
+++ 25-akpm/arch/ppc64/kernel/prom.c	2004-03-14 15:32:39.656274072 -0800
@@ -2853,40 +2853,12 @@ int of_remove_node(struct device_node *n
 	struct device_node *parent, *child;
 
 	parent = of_get_parent(np);
-
 	if (!parent)
 		return -EINVAL;
 
-	/* Make sure we are not recursively removing
-	 * more than one level of nodes.  We need to
-	 * allow this so we can remove a slot containing
-	 * an IOA.
-	 */
-	for (child = of_get_next_child(np, NULL);
-	     child != NULL;
-	     child = of_get_next_child(np, child)) {
-		struct device_node *grandchild;
-
-		if ((grandchild = of_get_next_child(child, NULL))) {
-			/* Too deep */
-			of_node_put(grandchild);
-			of_node_put(child);
-			return -EBUSY;
-		}
-	}
-
-	/* Now that we're reasonably sure that we won't
-	 * overflow our stack, remove any children of np.
-	 */
-	for (child = of_get_next_child(np, NULL);
-	     child != NULL;
-	     child = of_get_next_child(np, child)) {
-		int rc;
-
-		if ((rc = of_remove_node(child))) {
-			of_node_put(child);
-			return rc;
-		}
+	if ((child = of_get_next_child(np, NULL))) {
+		of_node_put(child);
+		return -EBUSY;
 	}
 
 	write_lock(&devtree_lock);
@@ -2964,6 +2936,53 @@ static void remove_node_proc_entries(str
 #endif /* CONFIG_PROC_DEVICETREE */
 
 /*
+ * Fix up n_intrs and intrs fields in a new device node
+ *
+ */
+static int of_finish_dynamic_node_interrupts(struct device_node *node)
+{
+	int intrcells, intlen, i;
+	unsigned *irq, *ints, virq;
+	struct device_node *ic;
+
+	ints = (unsigned int *)get_property(node, "interrupts", &intlen);
+	intrcells = prom_n_intr_cells(node);
+	intlen /= intrcells * sizeof(unsigned int);
+	node->n_intrs = intlen;
+	node->intrs = kmalloc(sizeof(struct interrupt_info) * intlen,
+			      GFP_KERNEL);
+	if (!node->intrs)
+		return -ENOMEM;
+
+	for (i = 0; i < intlen; ++i) {
+		int n, j;
+		node->intrs[i].line = 0;
+		node->intrs[i].sense = 1;
+		n = map_interrupt(&irq, &ic, node, ints, intrcells);
+		if (n <= 0)
+			continue;
+		virq = virt_irq_create_mapping(irq[0]);
+		if (virq == NO_IRQ) {
+			printk(KERN_CRIT "Could not allocate interrupt "
+			       "number for %s\n", node->full_name);
+			return -ENOMEM;
+		}
+		node->intrs[i].line = openpic_to_irq(virq);
+		if (n > 1)
+			node->intrs[i].sense = irq[1];
+		if (n > 2) {
+			printk(KERN_DEBUG "hmmm, got %d intr cells for %s:", n,
+			       node->full_name);
+			for (j = 0; j < n; ++j)
+				printk(" %d", irq[j]);
+			printk("\n");
+		}
+		ints += intrcells;
+	}
+	return 0;
+}
+
+/*
  * Fix up the uninitialized fields in a new device node:
  * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields
  *
@@ -2978,11 +2997,8 @@ static int of_finish_dynamic_node(struct
 {
 	struct device_node *parent = of_get_parent(node);
 	u32 *regs;
-	unsigned int *ints;
-	int intlen, intrcells;
-	int i, j, n, err = 0;
-	unsigned int *irq, virq;
-	struct device_node *ic;
+	int err = 0;
+	phandle *ibm_phandle;
  
 	node->name = get_property(node, "name", 0);
 	node->type = get_property(node, "device_type", 0);
@@ -2998,6 +3014,10 @@ static int of_finish_dynamic_node(struct
 	if (systemcfg->platform == PLATFORM_POWERMAC)
 		return -ENODEV;
 
+	/* fix up new node's linux_phandle field */
+	if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL)))
+		node->linux_phandle = *ibm_phandle;
+
 	/* do the work of interpret_pci_props */
 	if (parent->type && !strcmp(parent->type, "pci")) {
 		struct address_range *adr;
@@ -3027,45 +3047,9 @@ static int of_finish_dynamic_node(struct
 	}
 
 	/* now do the work of finish_node_interrupts */
-
-	ints = (unsigned int *) get_property(node, "interrupts", &intlen);
-	if (!ints) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	intrcells = prom_n_intr_cells(node);
-	intlen /= intrcells * sizeof(unsigned int);
-	node->n_intrs = intlen;
-	node->intrs = kmalloc(sizeof(struct interrupt_info) * intlen,
-			      GFP_KERNEL);
-	if (!node->intrs) {
-		err = -ENOMEM;
-		goto out;
-	}
-
-	for (i = 0; i < intlen; ++i) {
-		node->intrs[i].line = 0;
-		node->intrs[i].sense = 1;
-		n = map_interrupt(&irq, &ic, node, ints, intrcells);
-		if (n <= 0)
-			continue;
-		virq = virt_irq_create_mapping(irq[0]);
-		if (virq == NO_IRQ) {
-			printk(KERN_CRIT "Could not allocate interrupt "
-			       "number for %s\n", node->full_name);
-		} else
-			node->intrs[i].line = openpic_to_irq(virq);
-		if (n > 1)
-			node->intrs[i].sense = irq[1];
-		if (n > 2) {
-			printk(KERN_DEBUG "hmmm, got %d intr cells for %s:", n,
-			       node->full_name);
-			for (j = 0; j < n; ++j)
-				printk(" %d", irq[j]);
-			printk("\n");
-		}
-		ints += intrcells;
+	if (get_property(node, "interrupts", 0)) {
+		err = of_finish_dynamic_node_interrupts(node);
+		if (err) goto out;
 	}
 
        /* now do the rough equivalent of update_dn_pci_info, this

_