[PATCH 2/8] make OF node fixup code usable at runtime
Nathan Lynch
ntl at pobox.com
Thu Mar 10 11:51:42 EST 2005
At boot we recurse through the device tree "fixing up" various fields
and properties in the device nodes. Long ago, to support DLPAR and
hotplug, we largely duplicated some of this fixup code, the main
difference being that the new code used kmalloc for allocating various
data structures which are attached to the new device nodes.
This patch kills most of the duplicated code and makes finish_node,
finish_node_interrupts, and interpret_pci_props suitable for use at
runtime. These functions, if passed a null mem_start argument, will
use kmalloc for allocating extra data structures for the device node
being processed. Not terribly elegant, but it seems worth it to get
rid of the duplicated code (and bugs).
Signed-off-by: Nathan Lynch <ntl at pobox.com>
prom.c | 169 ++++++++++++++++++++---------------------------------------------
1 files changed, 54 insertions(+), 115 deletions(-)
Index: linux-2.6.11-bk5/arch/ppc64/kernel/prom.c
===================================================================
--- linux-2.6.11-bk5.orig/arch/ppc64/kernel/prom.c 2005-03-09 20:02:15.000000000 +0000
+++ linux-2.6.11-bk5/arch/ppc64/kernel/prom.c 2005-03-09 20:08:28.000000000 +0000
@@ -255,9 +255,9 @@ static int __devinit map_interrupt(unsig
return nintrc;
}
-static int __init finish_node_interrupts(struct device_node *np,
- unsigned long *mem_start,
- int measure_only)
+static int __devinit finish_node_interrupts(struct device_node *np,
+ unsigned long *mem_start,
+ int measure_only)
{
unsigned int *ints;
int intlen, intrcells, intrcount;
@@ -270,8 +270,15 @@ static int __init finish_node_interrupts
return 0;
intrcells = prom_n_intr_cells(np);
intlen /= intrcells * sizeof(unsigned int);
- np->intrs = (struct interrupt_info *) (*mem_start);
- (*mem_start) += intlen * sizeof(struct interrupt_info);
+
+ if (mem_start) {
+ np->intrs = (struct interrupt_info *) (*mem_start);
+ (*mem_start) += intlen * sizeof(struct interrupt_info);
+ } else {
+ np->intrs = kmalloc(intlen * sizeof(*(np->intrs)), GFP_KERNEL);
+ if (!np->intrs)
+ return -ENOMEM;
+ }
if (measure_only)
return 0;
@@ -318,33 +325,44 @@ static int __init finish_node_interrupts
return 0;
}
-static int __init interpret_pci_props(struct device_node *np,
- unsigned long *mem_start,
- int naddrc, int nsizec,
- int measure_only)
+static int __devinit interpret_pci_props(struct device_node *np,
+ unsigned long *mem_start,
+ int naddrc, int nsizec,
+ int measure_only)
{
struct address_range *adr;
struct pci_reg_property *pci_addrs;
- int i, l;
+ int i, l, n_addrs;
pci_addrs = (struct pci_reg_property *)
get_property(np, "assigned-addresses", &l);
- if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
- i = 0;
- adr = (struct address_range *) (*mem_start);
- while ((l -= sizeof(struct pci_reg_property)) >= 0) {
- if (!measure_only) {
- adr[i].space = pci_addrs[i].addr.a_hi;
- adr[i].address = pci_addrs[i].addr.a_lo |
- ((u64)pci_addrs[i].addr.a_mid << 32);
- adr[i].size = pci_addrs[i].size_lo;
- }
- ++i;
- }
- np->addrs = adr;
- np->n_addrs = i;
- (*mem_start) += i * sizeof(struct address_range);
+ if (!pci_addrs)
+ return 0;
+
+ n_addrs = l / sizeof(*pci_addrs);
+
+ if (!mem_start) {
+ adr = kmalloc(n_addrs * sizeof(*adr), GFP_KERNEL);
+ if (!adr)
+ return -ENOMEM;
+ } else {
+ adr = (struct address_range *)(*mem_start);
+ (*mem_start) += n_addrs * sizeof(struct address_range);
+ }
+
+ if (measure_only)
+ return 0;
+
+ np->addrs = adr;
+ np->n_addrs = n_addrs;
+
+ for (i = 0; i < n_addrs; i++) {
+ adr[i].space = pci_addrs[i].addr.a_hi;
+ adr[i].address = pci_addrs[i].addr.a_lo |
+ ((u64)pci_addrs[i].addr.a_mid << 32);
+ adr[i].size = pci_addrs[i].size_lo;
}
+
return 0;
}
@@ -490,11 +508,12 @@ static int __init interpret_root_props(s
return 0;
}
-static int __init finish_node(struct device_node *np,
- unsigned long *mem_start,
- interpret_func *ifunc,
- int naddrc, int nsizec,
- int measure_only)
+/* If mem_start == NULL ifuncs should use kmalloc for allocations. */
+static int __devinit finish_node(struct device_node *np,
+ unsigned long *mem_start,
+ interpret_func *ifunc,
+ int naddrc, int nsizec,
+ int measure_only)
{
struct device_node *child;
int *ip, rc = 0;
@@ -1627,54 +1646,6 @@ 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 = irq_offset_up(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
*
@@ -1685,7 +1656,9 @@ static int of_finish_dynamic_node_interr
* This should probably be split up into smaller chunks.
*/
-static int of_finish_dynamic_node(struct device_node *node)
+static int of_finish_dynamic_node(struct device_node *node,
+ unsigned long *unused1, int unused2,
+ int unused3, int unused4)
{
struct device_node *parent = of_get_parent(node);
u32 *regs;
@@ -1710,41 +1683,6 @@ static int of_finish_dynamic_node(struct
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;
- struct pci_reg_property *pci_addrs;
- int i, l;
-
- pci_addrs = (struct pci_reg_property *)
- get_property(node, "assigned-addresses", &l);
- if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
- i = 0;
- adr = kmalloc(sizeof(struct address_range) *
- (l / sizeof(struct pci_reg_property)),
- GFP_KERNEL);
- if (!adr) {
- err = -ENOMEM;
- goto out;
- }
- while ((l -= sizeof(struct pci_reg_property)) >= 0) {
- adr[i].space = pci_addrs[i].addr.a_hi;
- adr[i].address = pci_addrs[i].addr.a_lo |
- ((u64)pci_addrs[i].addr.a_mid << 32);
- adr[i].size = pci_addrs[i].size_lo;
- ++i;
- }
- node->addrs = adr;
- node->n_addrs = i;
- }
- }
-
- /* now do the work of finish_node_interrupts */
- if (get_property(node, "interrupts", NULL)) {
- err = of_finish_dynamic_node_interrupts(node);
- if (err) goto out;
- }
-
/* now do the rough equivalent of update_dn_pci_info, this
* probably is not correct for phb's, but should work for
* IOAs and slots.
@@ -1796,7 +1734,8 @@ int of_add_node(const char *path, struct
return -EINVAL; /* could also be ENOMEM, though */
}
- if (0 != (err = of_finish_dynamic_node(np))) {
+ err = finish_node(np, NULL, of_finish_dynamic_node, 0, 0, 0);
+ if (err < 0) {
kfree(np);
return err;
}
More information about the Linuxppc64-dev
mailing list