Commit 0e1311b1 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix for hotplug of multifunction cards.

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()
parent 9fe46cd9
......@@ -2853,41 +2853,13 @@ int of_remove_node(struct device_node *np)
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);
if ((child = of_get_next_child(np, NULL))) {
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;
}
}
write_lock(&devtree_lock);
OF_MARK_STALE(np);
......@@ -2963,6 +2935,53 @@ static void remove_node_proc_entries(struct device_node *np)
}
#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 device_node *node)
{
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 device_node *node)
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 device_node *node)
}
/* 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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment