summaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/Kconfig8
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/address.c54
-rw-r--r--drivers/of/fdt.c424
-rw-r--r--drivers/of/of_mdio.c26
-rw-r--r--drivers/of/of_net.c48
-rw-r--r--drivers/of/pdt.c112
-rw-r--r--drivers/of/platform.c22
8 files changed, 424 insertions, 271 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index aa675ebd8eb3..3c6e100a3ad0 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -19,6 +19,10 @@ config OF_FLATTREE
bool
select DTC
+config OF_EARLY_FLATTREE
+ bool
+ select OF_FLATTREE
+
config OF_PROMTREE
bool
@@ -49,6 +53,10 @@ config OF_I2C
help
OpenFirmware I2C accessors
+config OF_NET
+ depends on NETDEVICES
+ def_bool y
+
config OF_SPI
def_tristate SPI
depends on SPI && !SPARC
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 7888155bea08..3ab21a0a4907 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_OF_IRQ) += irq.o
obj-$(CONFIG_OF_DEVICE) += device.o platform.o
obj-$(CONFIG_OF_GPIO) += gpio.o
obj-$(CONFIG_OF_I2C) += of_i2c.o
+obj-$(CONFIG_OF_NET) += of_net.o
obj-$(CONFIG_OF_SPI) += of_spi.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 3a1c7e70b192..b4559c58c095 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -12,13 +12,13 @@
(ns) > 0)
static struct of_bus *of_match_bus(struct device_node *np);
-static int __of_address_to_resource(struct device_node *dev, const u32 *addrp,
- u64 size, unsigned int flags,
+static int __of_address_to_resource(struct device_node *dev,
+ const __be32 *addrp, u64 size, unsigned int flags,
struct resource *r);
/* Debug utility */
#ifdef DEBUG
-static void of_dump_addr(const char *s, const u32 *addr, int na)
+static void of_dump_addr(const char *s, const __be32 *addr, int na)
{
printk(KERN_DEBUG "%s", s);
while (na--)
@@ -26,7 +26,7 @@ static void of_dump_addr(const char *s, const u32 *addr, int na)
printk("\n");
}
#else
-static void of_dump_addr(const char *s, const u32 *addr, int na) { }
+static void of_dump_addr(const char *s, const __be32 *addr, int na) { }
#endif
/* Callbacks for bus specific translators */
@@ -36,10 +36,10 @@ struct of_bus {
int (*match)(struct device_node *parent);
void (*count_cells)(struct device_node *child,
int *addrc, int *sizec);
- u64 (*map)(u32 *addr, const u32 *range,
+ u64 (*map)(u32 *addr, const __be32 *range,
int na, int ns, int pna);
int (*translate)(u32 *addr, u64 offset, int na);
- unsigned int (*get_flags)(const u32 *addr);
+ unsigned int (*get_flags)(const __be32 *addr);
};
/*
@@ -55,7 +55,7 @@ static void of_bus_default_count_cells(struct device_node *dev,
*sizec = of_n_size_cells(dev);
}
-static u64 of_bus_default_map(u32 *addr, const u32 *range,
+static u64 of_bus_default_map(u32 *addr, const __be32 *range,
int na, int ns, int pna)
{
u64 cp, s, da;
@@ -85,7 +85,7 @@ static int of_bus_default_translate(u32 *addr, u64 offset, int na)
return 0;
}
-static unsigned int of_bus_default_get_flags(const u32 *addr)
+static unsigned int of_bus_default_get_flags(const __be32 *addr)
{
return IORESOURCE_MEM;
}
@@ -110,10 +110,10 @@ static void of_bus_pci_count_cells(struct device_node *np,
*sizec = 2;
}
-static unsigned int of_bus_pci_get_flags(const u32 *addr)
+static unsigned int of_bus_pci_get_flags(const __be32 *addr)
{
unsigned int flags = 0;
- u32 w = addr[0];
+ u32 w = be32_to_cpup(addr);
switch((w >> 24) & 0x03) {
case 0x01:
@@ -129,7 +129,8 @@ static unsigned int of_bus_pci_get_flags(const u32 *addr)
return flags;
}
-static u64 of_bus_pci_map(u32 *addr, const u32 *range, int na, int ns, int pna)
+static u64 of_bus_pci_map(u32 *addr, const __be32 *range, int na, int ns,
+ int pna)
{
u64 cp, s, da;
unsigned int af, rf;
@@ -160,7 +161,7 @@ static int of_bus_pci_translate(u32 *addr, u64 offset, int na)
return of_bus_default_translate(addr + 1, offset, na - 1);
}
-const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
+const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
unsigned int *flags)
{
const __be32 *prop;
@@ -207,7 +208,7 @@ EXPORT_SYMBOL(of_get_pci_address);
int of_pci_address_to_resource(struct device_node *dev, int bar,
struct resource *r)
{
- const u32 *addrp;
+ const __be32 *addrp;
u64 size;
unsigned int flags;
@@ -237,12 +238,13 @@ static void of_bus_isa_count_cells(struct device_node *child,
*sizec = 1;
}
-static u64 of_bus_isa_map(u32 *addr, const u32 *range, int na, int ns, int pna)
+static u64 of_bus_isa_map(u32 *addr, const __be32 *range, int na, int ns,
+ int pna)
{
u64 cp, s, da;
/* Check address type match */
- if ((addr[0] ^ range[0]) & 0x00000001)
+ if ((addr[0] ^ range[0]) & cpu_to_be32(1))
return OF_BAD_ADDR;
/* Read address values, skipping high cell */
@@ -264,10 +266,10 @@ static int of_bus_isa_translate(u32 *addr, u64 offset, int na)
return of_bus_default_translate(addr + 1, offset, na - 1);
}
-static unsigned int of_bus_isa_get_flags(const u32 *addr)
+static unsigned int of_bus_isa_get_flags(const __be32 *addr)
{
unsigned int flags = 0;
- u32 w = addr[0];
+ u32 w = be32_to_cpup(addr);
if (w & 1)
flags |= IORESOURCE_IO;
@@ -330,7 +332,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
struct of_bus *pbus, u32 *addr,
int na, int ns, int pna, const char *rprop)
{
- const u32 *ranges;
+ const __be32 *ranges;
unsigned int rlen;
int rone;
u64 offset = OF_BAD_ADDR;
@@ -398,7 +400,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
* that can be mapped to a cpu physical address). This is not really specified
* that way, but this is traditionally the way IBM at least do things
*/
-u64 __of_translate_address(struct device_node *dev, const u32 *in_addr,
+u64 __of_translate_address(struct device_node *dev, const __be32 *in_addr,
const char *rprop)
{
struct device_node *parent = NULL;
@@ -475,22 +477,22 @@ u64 __of_translate_address(struct device_node *dev, const u32 *in_addr,
return result;
}
-u64 of_translate_address(struct device_node *dev, const u32 *in_addr)
+u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
{
return __of_translate_address(dev, in_addr, "ranges");
}
EXPORT_SYMBOL(of_translate_address);
-u64 of_translate_dma_address(struct device_node *dev, const u32 *in_addr)
+u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
{
return __of_translate_address(dev, in_addr, "dma-ranges");
}
EXPORT_SYMBOL(of_translate_dma_address);
-const u32 *of_get_address(struct device_node *dev, int index, u64 *size,
+const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
unsigned int *flags)
{
- const u32 *prop;
+ const __be32 *prop;
unsigned int psize;
struct device_node *parent;
struct of_bus *bus;
@@ -525,8 +527,8 @@ const u32 *of_get_address(struct device_node *dev, int index, u64 *size,
}
EXPORT_SYMBOL(of_get_address);
-static int __of_address_to_resource(struct device_node *dev, const u32 *addrp,
- u64 size, unsigned int flags,
+static int __of_address_to_resource(struct device_node *dev,
+ const __be32 *addrp, u64 size, unsigned int flags,
struct resource *r)
{
u64 taddr;
@@ -564,7 +566,7 @@ static int __of_address_to_resource(struct device_node *dev, const u32 *addrp,
int of_address_to_resource(struct device_node *dev, int index,
struct resource *r)
{
- const u32 *addrp;
+ const __be32 *addrp;
u64 size;
unsigned int flags;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index c1360e02f921..af824e7e0367 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -11,10 +11,12 @@
#include <linux/kernel.h>
#include <linux/initrd.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/string.h>
#include <linux/errno.h>
+#include <linux/slab.h>
#ifdef CONFIG_PPC
#include <asm/machdep.h>
@@ -22,104 +24,19 @@
#include <asm/page.h>
-int __initdata dt_root_addr_cells;
-int __initdata dt_root_size_cells;
-
-struct boot_param_header *initial_boot_params;
-
-char *find_flat_dt_string(u32 offset)
+char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
{
- return ((char *)initial_boot_params) +
- be32_to_cpu(initial_boot_params->off_dt_strings) + offset;
+ return ((char *)blob) +
+ be32_to_cpu(blob->off_dt_strings) + offset;
}
/**
- * of_scan_flat_dt - scan flattened tree blob and call callback on each.
- * @it: callback function
- * @data: context data pointer
- *
- * This function is used to scan the flattened device-tree, it is
- * used to extract the memory information at boot before we can
- * unflatten the tree
+ * of_fdt_get_property - Given a node in the given flat blob, return
+ * the property ptr
*/
-int __init of_scan_flat_dt(int (*it)(unsigned long node,
- const char *uname, int depth,
- void *data),
- void *data)
-{
- unsigned long p = ((unsigned long)initial_boot_params) +
- be32_to_cpu(initial_boot_params->off_dt_struct);
- int rc = 0;
- int depth = -1;
-
- do {
- u32 tag = be32_to_cpup((__be32 *)p);
- char *pathp;
-
- p += 4;
- if (tag == OF_DT_END_NODE) {
- depth--;
- continue;
- }
- if (tag == OF_DT_NOP)
- continue;
- if (tag == OF_DT_END)
- break;
- if (tag == OF_DT_PROP) {
- u32 sz = be32_to_cpup((__be32 *)p);
- p += 8;
- if (be32_to_cpu(initial_boot_params->version) < 0x10)
- p = ALIGN(p, sz >= 8 ? 8 : 4);
- p += sz;
- p = ALIGN(p, 4);
- continue;
- }
- if (tag != OF_DT_BEGIN_NODE) {
- pr_err("Invalid tag %x in flat device tree!\n", tag);
- return -EINVAL;
- }
- depth++;
- pathp = (char *)p;
- p = ALIGN(p + strlen(pathp) + 1, 4);
- if ((*pathp) == '/') {
- char *lp, *np;
- for (lp = NULL, np = pathp; *np; np++)
- if ((*np) == '/')
- lp = np+1;
- if (lp != NULL)
- pathp = lp;
- }
- rc = it(p, pathp, depth, data);
- if (rc != 0)
- break;
- } while (1);
-
- return rc;
-}
-
-/**
- * of_get_flat_dt_root - find the root node in the flat blob
- */
-unsigned long __init of_get_flat_dt_root(void)
-{
- unsigned long p = ((unsigned long)initial_boot_params) +
- be32_to_cpu(initial_boot_params->off_dt_struct);
-
- while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
- p += 4;
- BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
- p += 4;
- return ALIGN(p + strlen((char *)p) + 1, 4);
-}
-
-/**
- * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
- *
- * This function can be used within scan_flattened_dt callback to get
- * access to properties
- */
-void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
- unsigned long *size)
+void *of_fdt_get_property(struct boot_param_header *blob,
+ unsigned long node, const char *name,
+ unsigned long *size)
{
unsigned long p = node;
@@ -137,10 +54,10 @@ void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
sz = be32_to_cpup((__be32 *)p);
noff = be32_to_cpup((__be32 *)(p + 4));
p += 8;
- if (be32_to_cpu(initial_boot_params->version) < 0x10)
+ if (be32_to_cpu(blob->version) < 0x10)
p = ALIGN(p, sz >= 8 ? 8 : 4);
- nstr = find_flat_dt_string(noff);
+ nstr = of_fdt_get_string(blob, noff);
if (nstr == NULL) {
pr_warning("Can't find property index name !\n");
return NULL;
@@ -156,21 +73,28 @@ void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
}
/**
- * of_flat_dt_is_compatible - Return true if given node has compat in compatible list
+ * of_fdt_is_compatible - Return true if given node from the given blob has
+ * compat in its compatible list
+ * @blob: A device tree blob
* @node: node to test
* @compat: compatible string to compare with compatible list.
+ *
+ * On match, returns a non-zero value with smaller values returned for more
+ * specific compatible values.
*/
-int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
+int of_fdt_is_compatible(struct boot_param_header *blob,
+ unsigned long node, const char *compat)
{
const char *cp;
- unsigned long cplen, l;
+ unsigned long cplen, l, score = 0;
- cp = of_get_flat_dt_prop(node, "compatible", &cplen);
+ cp = of_fdt_get_property(blob, node, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
+ score++;
if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
- return 1;
+ return score;
l = strlen(cp) + 1;
cp += l;
cplen -= l;
@@ -179,7 +103,28 @@ int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
return 0;
}
-static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
+/**
+ * of_fdt_match - Return true if node matches a list of compatible values
+ */
+int of_fdt_match(struct boot_param_header *blob, unsigned long node,
+ const char **compat)
+{
+ unsigned int tmp, score = 0;
+
+ if (!compat)
+ return 0;
+
+ while (*compat) {
+ tmp = of_fdt_is_compatible(blob, node, *compat);
+ if (tmp && (score == 0 || (tmp < score)))
+ score = tmp;
+ compat++;
+ }
+
+ return score;
+}
+
+static void *unflatten_dt_alloc(unsigned long *mem, unsigned long size,
unsigned long align)
{
void *res;
@@ -193,16 +138,18 @@ static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
/**
* unflatten_dt_node - Alloc and populate a device_node from the flat tree
+ * @blob: The parent device tree blob
* @p: pointer to node in flat tree
* @dad: Parent struct device_node
* @allnextpp: pointer to ->allnext from last allocated device_node
* @fpsize: Size of the node path up at the current depth.
*/
-unsigned long __init unflatten_dt_node(unsigned long mem,
- unsigned long *p,
- struct device_node *dad,
- struct device_node ***allnextpp,
- unsigned long fpsize)
+unsigned long unflatten_dt_node(struct boot_param_header *blob,
+ unsigned long mem,
+ unsigned long *p,
+ struct device_node *dad,
+ struct device_node ***allnextpp,
+ unsigned long fpsize)
{
struct device_node *np;
struct property *pp, **prev_pp = NULL;
@@ -298,10 +245,10 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
sz = be32_to_cpup((__be32 *)(*p));
noff = be32_to_cpup((__be32 *)((*p) + 4));
*p += 8;
- if (be32_to_cpu(initial_boot_params->version) < 0x10)
+ if (be32_to_cpu(blob->version) < 0x10)
*p = ALIGN(*p, sz >= 8 ? 8 : 4);
- pname = find_flat_dt_string(noff);
+ pname = of_fdt_get_string(blob, noff);
if (pname == NULL) {
pr_info("Can't find property name in list !\n");
break;
@@ -380,7 +327,8 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
if (tag == OF_DT_NOP)
*p += 4;
else
- mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
+ mem = unflatten_dt_node(blob, mem, p, np, allnextpp,
+ fpsize);
tag = be32_to_cpup((__be32 *)(*p));
}
if (tag != OF_DT_END_NODE) {
@@ -391,6 +339,211 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
return mem;
}
+/**
+ * __unflatten_device_tree - create tree of device_nodes from flat blob
+ *
+ * unflattens a device-tree, creating the
+ * tree of struct device_node. It also fills the "name" and "type"
+ * pointers of the nodes so the normal device-tree walking functions
+ * can be used.
+ * @blob: The blob to expand
+ * @mynodes: The device_node tree created by the call
+ * @dt_alloc: An allocator that provides a virtual address to memory
+ * for the resulting tree
+ */
+void __unflatten_device_tree(struct boot_param_header *blob,
+ struct device_node **mynodes,
+ void * (*dt_alloc)(u64 size, u64 align))
+{
+ unsigned long start, mem, size;
+ struct device_node **allnextp = mynodes;
+
+ pr_debug(" -> unflatten_device_tree()\n");
+
+ if (!blob) {
+ pr_debug("No device tree pointer\n");
+ return;
+ }
+
+ pr_debug("Unflattening device tree:\n");
+ pr_debug("magic: %08x\n", be32_to_cpu(blob->magic));
+ pr_debug("size: %08x\n", be32_to_cpu(blob->totalsize));
+ pr_debug("version: %08x\n", be32_to_cpu(blob->version));
+
+ if (be32_to_cpu(blob->magic) != OF_DT_HEADER) {
+ pr_err("Invalid device tree blob header\n");
+ return;
+ }
+
+ /* First pass, scan for size */
+ start = ((unsigned long)blob) +
+ be32_to_cpu(blob->off_dt_struct);
+ size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
+ size = (size | 3) + 1;
+
+ pr_debug(" size is %lx, allocating...\n", size);
+
+ /* Allocate memory for the expanded device tree */
+ mem = (unsigned long)
+ dt_alloc(size + 4, __alignof__(struct device_node));
+
+ ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
+
+ pr_debug(" unflattening %lx...\n", mem);
+
+ /* Second pass, do actual unflattening */
+ start = ((unsigned long)blob) +
+ be32_to_cpu(blob->off_dt_struct);
+ unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
+ if (be32_to_cpup((__be32 *)start) != OF_DT_END)
+ pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
+ if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef)
+ pr_warning("End of tree marker overwritten: %08x\n",
+ be32_to_cpu(((__be32 *)mem)[size / 4]));
+ *allnextp = NULL;
+
+ pr_debug(" <- unflatten_device_tree()\n");
+}
+
+static void *kernel_tree_alloc(u64 size, u64 align)
+{
+ return kzalloc(size, GFP_KERNEL);
+}
+
+/**
+ * of_fdt_unflatten_tree - create tree of device_nodes from flat blob
+ *
+ * unflattens the device-tree passed by the firmware, creating the
+ * tree of struct device_node. It also fills the "name" and "type"
+ * pointers of the nodes so the normal device-tree walking functions
+ * can be used.
+ */
+void of_fdt_unflatten_tree(unsigned long *blob,
+ struct device_node **mynodes)
+{
+ struct boot_param_header *device_tree =
+ (struct boot_param_header *)blob;
+ __unflatten_device_tree(device_tree, mynodes, &kernel_tree_alloc);
+}
+EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree);
+
+/* Everything below here references initial_boot_params directly. */
+int __initdata dt_root_addr_cells;
+int __initdata dt_root_size_cells;
+
+struct boot_param_header *initial_boot_params;
+
+#ifdef CONFIG_OF_EARLY_FLATTREE
+
+/**
+ * of_scan_flat_dt - scan flattened tree blob and call callback on each.
+ * @it: callback function
+ * @data: context data pointer
+ *
+ * This function is used to scan the flattened device-tree, it is
+ * used to extract the memory information at boot before we can
+ * unflatten the tree
+ */
+int __init of_scan_flat_dt(int (*it)(unsigned long node,
+ const char *uname, int depth,
+ void *data),
+ void *data)
+{
+ unsigned long p = ((unsigned long)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_dt_struct);
+ int rc = 0;
+ int depth = -1;
+
+ do {
+ u32 tag = be32_to_cpup((__be32 *)p);
+ char *pathp;
+
+ p += 4;
+ if (tag == OF_DT_END_NODE) {
+ depth--;
+ continue;
+ }
+ if (tag == OF_DT_NOP)
+ continue;
+ if (tag == OF_DT_END)
+ break;
+ if (tag == OF_DT_PROP) {
+ u32 sz = be32_to_cpup((__be32 *)p);
+ p += 8;
+ if (be32_to_cpu(initial_boot_params->version) < 0x10)
+ p = ALIGN(p, sz >= 8 ? 8 : 4);
+ p += sz;
+ p = ALIGN(p, 4);
+ continue;
+ }
+ if (tag != OF_DT_BEGIN_NODE) {
+ pr_err("Invalid tag %x in flat device tree!\n", tag);
+ return -EINVAL;
+ }
+ depth++;
+ pathp = (char *)p;
+ p = ALIGN(p + strlen(pathp) + 1, 4);
+ if ((*pathp) == '/') {
+ char *lp, *np;
+ for (lp = NULL, np = pathp; *np; np++)
+ if ((*np) == '/')
+ lp = np+1;
+ if (lp != NULL)
+ pathp = lp;
+ }
+ rc = it(p, pathp, depth, data);
+ if (rc != 0)
+ break;
+ } while (1);
+
+ return rc;
+}
+
+/**
+ * of_get_flat_dt_root - find the root node in the flat blob
+ */
+unsigned long __init of_get_flat_dt_root(void)
+{
+ unsigned long p = ((unsigned long)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_dt_struct);
+
+ while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
+ p += 4;
+ BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
+ p += 4;
+ return ALIGN(p + strlen((char *)p) + 1, 4);
+}
+
+/**
+ * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
+ *
+ * This function can be used within scan_flattened_dt callback to get
+ * access to properties
+ */
+void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
+ unsigned long *size)
+{
+ return of_fdt_get_property(initial_boot_params, node, name, size);
+}
+
+/**
+ * of_flat_dt_is_compatible - Return true if given node has compat in compatible list
+ * @node: node to test
+ * @compat: compatible string to compare with compatible list.
+ */
+int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
+{
+ return of_fdt_is_compatible(initial_boot_params, node, compat);
+}
+
+/**
+ * of_flat_dt_match - Return true if node matches a list of compatible values
+ */
+int __init of_flat_dt_match(unsigned long node, const char **compat)
+{
+ return of_fdt_match(initial_boot_params, node, compat);
+}
+
#ifdef CONFIG_BLK_DEV_INITRD
/**
* early_init_dt_check_for_initrd - Decode initrd location from flat tree
@@ -549,58 +702,13 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
*/
void __init unflatten_device_tree(void)
{
- unsigned long start, mem, size;
- struct device_node **allnextp = &allnodes;
-
- pr_debug(" -> unflatten_device_tree()\n");
-
- if (!initial_boot_params) {
- pr_debug("No device tree pointer\n");
- return;
- }
-
- pr_debug("Unflattening device tree:\n");
- pr_debug("magic: %08x\n", be32_to_cpu(initial_boot_params->magic));
- pr_debug("size: %08x\n", be32_to_cpu(initial_boot_params->totalsize));
- pr_debug("version: %08x\n", be32_to_cpu(initial_boot_params->version));
-
- if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {
- pr_err("Invalid device tree blob header\n");
- return;
- }
-
- /* First pass, scan for size */
- start = ((unsigned long)initial_boot_params) +
- be32_to_cpu(initial_boot_params->off_dt_struct);
- size = unflatten_dt_node(0, &start, NULL, NULL, 0);
- size = (size | 3) + 1;
-
- pr_debug(" size is %lx, allocating...\n", size);
-
- /* Allocate memory for the expanded device tree */
- mem = early_init_dt_alloc_memory_arch(size + 4,
- __alignof__(struct device_node));
- mem = (unsigned long) __va(mem);
-
- ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
-
- pr_debug(" unflattening %lx...\n", mem);
-
- /* Second pass, do actual unflattening */
- start = ((unsigned long)initial_boot_params) +
- be32_to_cpu(initial_boot_params->off_dt_struct);
- unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
- if (be32_to_cpup((__be32 *)start) != OF_DT_END)
- pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
- if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef)
- pr_warning("End of tree marker overwritten: %08x\n",
- be32_to_cpu(((__be32 *)mem)[size / 4]));
- *allnextp = NULL;
+ __unflatten_device_tree(initial_boot_params, &allnodes,
+ early_init_dt_alloc_memory_arch);
/* Get pointer to OF "/chosen" node for use everywhere */
of_chosen = of_find_node_by_path("/chosen");
if (of_chosen == NULL)
of_chosen = of_find_node_by_path("/chosen@0");
-
- pr_debug(" <- unflatten_device_tree()\n");
}
+
+#endif /* CONFIG_OF_EARLY_FLATTREE */
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 1fce00eb421b..dcd7857784f2 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -52,27 +52,35 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
/* Loop over the child nodes and register a phy_device for each one */
for_each_child_of_node(np, child) {
- const __be32 *addr;
+ const __be32 *paddr;
+ u32 addr;
int len;
/* A PHY must have a reg property in the range [0-31] */
- addr = of_get_property(child, "reg", &len);
- if (!addr || len < sizeof(*addr) || *addr >= 32 || *addr < 0) {
+ paddr = of_get_property(child, "reg", &len);
+ if (!paddr || len < sizeof(*paddr)) {
dev_err(&mdio->dev, "%s has invalid PHY address\n",
child->full_name);
continue;
}
+ addr = be32_to_cpup(paddr);
+ if (addr >= 32) {
+ dev_err(&mdio->dev, "%s PHY address %i is too large\n",
+ child->full_name, addr);
+ continue;
+ }
+
if (mdio->irq) {
- mdio->irq[*addr] = irq_of_parse_and_map(child, 0);
- if (!mdio->irq[*addr])
- mdio->irq[*addr] = PHY_POLL;
+ mdio->irq[addr] = irq_of_parse_and_map(child, 0);
+ if (!mdio->irq[addr])
+ mdio->irq[addr] = PHY_POLL;
}
- phy = get_phy_device(mdio, be32_to_cpup(addr));
+ phy = get_phy_device(mdio, addr);
if (!phy || IS_ERR(phy)) {
dev_err(&mdio->dev, "error probing PHY at address %i\n",
- *addr);
+ addr);
continue;
}
phy_scan_fixups(phy);
@@ -91,7 +99,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
}
dev_dbg(&mdio->dev, "registered phy %s at address %i\n",
- child->name, *addr);
+ child->name, addr);
}
return 0;
diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c
new file mode 100644
index 000000000000..86f334a2769c
--- /dev/null
+++ b/drivers/of/of_net.c
@@ -0,0 +1,48 @@
+/*
+ * OF helpers for network devices.
+ *
+ * This file is released under the GPLv2
+ *
+ * Initially copied out of arch/powerpc/kernel/prom_parse.c
+ */
+#include <linux/etherdevice.h>
+#include <linux/kernel.h>
+#include <linux/of_net.h>
+
+/**
+ * Search the device tree for the best MAC address to use. 'mac-address' is
+ * checked first, because that is supposed to contain to "most recent" MAC
+ * address. If that isn't set, then 'local-mac-address' is checked next,
+ * because that is the default address. If that isn't set, then the obsolete
+ * 'address' is checked, just in case we're using an old device tree.
+ *
+ * Note that the 'address' property is supposed to contain a virtual address of
+ * the register set, but some DTS files have redefined that property to be the
+ * MAC address.
+ *
+ * All-zero MAC addresses are rejected, because those could be properties that
+ * exist in the device tree, but were not set by U-Boot. For example, the
+ * DTS could define 'mac-address' and 'local-mac-address', with zero MAC
+ * addresses. Some older U-Boots only initialized 'local-mac-address'. In
+ * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists
+ * but is all zeros.
+*/
+const void *of_get_mac_address(struct device_node *np)
+{
+ struct property *pp;
+
+ pp = of_find_property(np, "mac-address", NULL);
+ if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value))
+ return pp->value;
+
+ pp = of_find_property(np, "local-mac-address", NULL);
+ if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value))
+ return pp->value;
+
+ pp = of_find_property(np, "address", NULL);
+ if (pp && (pp->length == 6) && is_valid_ether_addr(pp->value))
+ return pp->value;
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_get_mac_address);
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c
index 28295d0a50f6..4d87b5dc9284 100644
--- a/drivers/of/pdt.c
+++ b/drivers/of/pdt.c
@@ -36,19 +36,55 @@ unsigned int of_pdt_unique_id __initdata;
(p)->unique_id = of_pdt_unique_id++; \
} while (0)
-static inline const char *of_pdt_node_name(struct device_node *dp)
+static char * __init of_pdt_build_full_name(struct device_node *dp)
{
- return dp->path_component_name;
+ int len, ourlen, plen;
+ char *n;
+
+ dp->path_component_name = build_path_component(dp);
+
+ plen = strlen(dp->parent->full_name);
+ ourlen = strlen(dp->path_component_name);
+ len = ourlen + plen + 2;
+
+ n = prom_early_alloc(len);
+ strcpy(n, dp->parent->full_name);
+ if (!of_node_is_root(dp->parent)) {
+ strcpy(n + plen, "/");
+ plen++;
+ }
+ strcpy(n + plen, dp->path_component_name);
+
+ return n;
}
-#else
+#else /* CONFIG_SPARC */
static inline void of_pdt_incr_unique_id(void *p) { }
static inline void irq_trans_init(struct device_node *dp) { }
-static inline const char *of_pdt_node_name(struct device_node *dp)
+static char * __init of_pdt_build_full_name(struct device_node *dp)
{
- return dp->name;
+ static int failsafe_id = 0; /* for generating unique names on failure */
+ char *buf;
+ int len;
+
+ if (of_pdt_prom_ops->pkg2path(dp->phandle, NULL, 0, &len))
+ goto failsafe;
+
+ buf = prom_early_alloc(len + 1);
+ if (of_pdt_prom_ops->pkg2path(dp->phandle, buf, len, &len))
+ goto failsafe;
+ return buf;
+
+ failsafe:
+ buf = prom_early_alloc(strlen(dp->parent->full_name) +
+ strlen(dp->name) + 16);
+ sprintf(buf, "%s/%s@unknown%i",
+ of_node_is_root(dp->parent) ? "" : dp->parent->full_name,
+ dp->name, failsafe_id++);
+ pr_err("%s: pkg2path failed; assigning %s\n", __func__, buf);
+ return buf;
}
#endif /* !CONFIG_SPARC */
@@ -132,47 +168,6 @@ static char * __init of_pdt_get_one_property(phandle node, const char *name)
return buf;
}
-static char * __init of_pdt_try_pkg2path(phandle node)
-{
- char *res, *buf = NULL;
- int len;
-
- if (!of_pdt_prom_ops->pkg2path)
- return NULL;
-
- if (of_pdt_prom_ops->pkg2path(node, buf, 0, &len))
- return NULL;
- buf = prom_early_alloc(len + 1);
- if (of_pdt_prom_ops->pkg2path(node, buf, len, &len)) {
- pr_err("%s: package-to-path failed\n", __func__);
- return NULL;
- }
-
- res = strrchr(buf, '/');
- if (!res) {
- pr_err("%s: couldn't find / in %s\n", __func__, buf);
- return NULL;
- }
- return res+1;
-}
-
-/*
- * When fetching the node's name, first try using package-to-path; if
- * that fails (either because the arch hasn't supplied a PROM callback,
- * or some other random failure), fall back to just looking at the node's
- * 'name' property.
- */
-static char * __init of_pdt_build_name(phandle node)
-{
- char *buf;
-
- buf = of_pdt_try_pkg2path(node);
- if (!buf)
- buf = of_pdt_get_one_property(node, "name");
-
- return buf;
-}
-
static struct device_node * __init of_pdt_create_node(phandle node,
struct device_node *parent)
{
@@ -187,7 +182,7 @@ static struct device_node * __init of_pdt_create_node(phandle node,
kref_init(&dp->kref);
- dp->name = of_pdt_build_name(node);
+ dp->name = of_pdt_get_one_property(node, "name");
dp->type = of_pdt_get_one_property(node, "device_type");
dp->phandle = node;
@@ -198,26 +193,6 @@ static struct device_node * __init of_pdt_create_node(phandle node,
return dp;
}
-static char * __init of_pdt_build_full_name(struct device_node *dp)
-{
- int len, ourlen, plen;
- char *n;
-
- plen = strlen(dp->parent->full_name);
- ourlen = strlen(of_pdt_node_name(dp));
- len = ourlen + plen + 2;
-
- n = prom_early_alloc(len);
- strcpy(n, dp->parent->full_name);
- if (!of_node_is_root(dp->parent)) {
- strcpy(n + plen, "/");
- plen++;
- }
- strcpy(n + plen, of_pdt_node_name(dp));
-
- return n;
-}
-
static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
phandle node,
struct device_node ***nextp)
@@ -240,9 +215,6 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
*(*nextp) = dp;
*nextp = &dp->allnext;
-#if defined(CONFIG_SPARC)
- dp->path_component_name = build_path_component(dp);
-#endif
dp->full_name = of_pdt_build_full_name(dp);
dp->child = of_pdt_build_tree(dp,
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 5b4a07f1220e..c01cd1ac7617 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -633,6 +633,9 @@ EXPORT_SYMBOL(of_device_alloc);
* @np: pointer to node to create device for
* @bus_id: name to assign device
* @parent: Linux device model parent device.
+ *
+ * Returns pointer to created platform device, or NULL if a device was not
+ * registered. Unavailable devices will not get registered.
*/
struct platform_device *of_platform_device_create(struct device_node *np,
const char *bus_id,
@@ -640,6 +643,9 @@ struct platform_device *of_platform_device_create(struct device_node *np,
{
struct platform_device *dev;
+ if (!of_device_is_available(np))
+ return NULL;
+
dev = of_device_alloc(np, bus_id, parent);
if (!dev)
return NULL;
@@ -683,8 +689,9 @@ static int of_platform_bus_create(const struct device_node *bus,
pr_debug(" create child: %s\n", child->full_name);
dev = of_platform_device_create(child, NULL, parent);
if (dev == NULL)
- rc = -ENOMEM;
- else if (!of_match_node(matches, child))
+ continue;
+
+ if (!of_match_node(matches, child))
continue;
if (rc == 0) {
pr_debug(" and sub busses\n");
@@ -733,10 +740,9 @@ int of_platform_bus_probe(struct device_node *root,
if (of_match_node(matches, root)) {
pr_debug(" root match, create all sub devices\n");
dev = of_platform_device_create(root, NULL, parent);
- if (dev == NULL) {
- rc = -ENOMEM;
+ if (dev == NULL)
goto bail;
- }
+
pr_debug(" create all sub busses\n");
rc = of_platform_bus_create(root, matches, &dev->dev);
goto bail;
@@ -748,9 +754,9 @@ int of_platform_bus_probe(struct device_node *root,
pr_debug(" match: %s\n", child->full_name);
dev = of_platform_device_create(child, NULL, parent);
if (dev == NULL)
- rc = -ENOMEM;
- else
- rc = of_platform_bus_create(child, matches, &dev->dev);
+ continue;
+
+ rc = of_platform_bus_create(child, matches, &dev->dev);
if (rc) {
of_node_put(child);
break;
OpenPOWER on IntegriCloud