From: Linda Xie 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 _