summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-04 10:02:38 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-04 10:02:38 -0700
commitd27050641e9bc056446deb0814e7ba1aa7911f5a (patch)
tree160f46d9a6df3d7234c71a9fbaa31ebcf89c04d0 /drivers/of
parentb77279bc2e81545b20824da701b349272a78e4e7 (diff)
parent43cb43678705e39b175b325f17938295996aefc7 (diff)
Merge tag 'devicetree-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux into next
Pull DeviceTree updates from Rob Herring: - Another round of clean-up of FDT related code in architecture code. This removes knowledge of internal FDT details from most architectures except powerpc. - Conversion of kernel's custom FDT parsing code to use libfdt. - DT based initialization for generic serial earlycon. The introduction of generic serial earlycon support went in through the tty tree. - Improve the platform device naming for DT probed devices to ensure unique naming and use parent names instead of a global index. - Fix a race condition in of_update_property. - Unify the various linker section OF match tables and fix several function prototype errors. - Update platform_get_irq_byname to work in deferred probe cases. - 2 binding doc updates * tag 'devicetree-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: (58 commits) of: handle NULL node in next_child iterators of/irq: provide more wrappers for !CONFIG_OF devicetree: bindings: Document micrel vendor prefix dt: bindings: dwc2: fix required value for the phy-names property of_pci_irq: kill useless variable in of_irq_parse_pci() of/irq: do irq resolution in platform_get_irq_byname() of: Add a testcase for of_find_node_by_path() of: Make of_find_node_by_path() handle /aliases of: Create unlocked version of for_each_child_of_node() lib: add glibc style strchrnul() variant of: Handle memory@0 node on PPC32 only pci/of: Remove dead code of: fix race between search and remove in of_update_property() of: Use NULL for pointers of: Stop naming platform_device using dcr address of: Ensure unique names without sacrificing determinism tty/serial: pl011: add DT based earlycon support of/fdt: add FDT serial scanning for earlycon of/fdt: add FDT address translation support serial: earlycon: add DT support ...
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/Kconfig1
-rw-r--r--drivers/of/Makefile4
-rw-r--r--drivers/of/address.c22
-rw-r--r--drivers/of/base.c119
-rw-r--r--drivers/of/fdt.c458
-rw-r--r--drivers/of/fdt_address.c241
-rw-r--r--drivers/of/irq.c22
-rw-r--r--drivers/of/of_pci_irq.c8
-rw-r--r--drivers/of/of_reserved_mem.c6
-rw-r--r--drivers/of/platform.c76
-rw-r--r--drivers/of/selftest.c80
-rw-r--r--drivers/of/testcase-data/testcases.dtsi1
-rw-r--r--drivers/of/testcase-data/tests-phandle.dtsi6
-rw-r--r--drivers/of/testcase-data/tests-platform.dtsi35
14 files changed, 698 insertions, 381 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 889005fa4d04..2dcb0541012d 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -20,6 +20,7 @@ config OF_SELFTEST
config OF_FLATTREE
bool
select DTC
+ select LIBFDT
config OF_EARLY_FLATTREE
bool
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index ed9660adad77..099b1fb00af4 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,5 +1,6 @@
obj-y = base.o device.o platform.o
obj-$(CONFIG_OF_FLATTREE) += fdt.o
+obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o
obj-$(CONFIG_OF_IRQ) += irq.o
@@ -10,3 +11,6 @@ obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+
+CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
+CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
diff --git a/drivers/of/address.c b/drivers/of/address.c
index cb4242a69cd5..95351b2a112c 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -498,8 +498,7 @@ static u64 __of_translate_address(struct device_node *dev,
/* Count address cells & copy address locally */
bus->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
- printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
- of_node_full_name(dev));
+ pr_debug("OF: Bad cell count for %s\n", of_node_full_name(dev));
goto bail;
}
memcpy(addr, in_addr, na * 4);
@@ -564,25 +563,6 @@ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
}
EXPORT_SYMBOL(of_translate_dma_address);
-bool of_can_translate_address(struct device_node *dev)
-{
- struct device_node *parent;
- struct of_bus *bus;
- int na, ns;
-
- parent = of_get_parent(dev);
- if (parent == NULL)
- return false;
-
- bus = of_match_bus(parent);
- bus->count_cells(dev, &na, &ns);
-
- of_node_put(parent);
-
- return OF_CHECK_COUNTS(na, ns);
-}
-EXPORT_SYMBOL(of_can_translate_address);
-
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
unsigned int *flags)
{
diff --git a/drivers/of/base.c b/drivers/of/base.c
index aab9728271fd..8368d96ae7b4 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -695,6 +695,25 @@ struct device_node *of_get_next_parent(struct device_node *node)
}
EXPORT_SYMBOL(of_get_next_parent);
+static struct device_node *__of_get_next_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ struct device_node *next;
+
+ if (!node)
+ return NULL;
+
+ next = prev ? prev->sibling : node->child;
+ for (; next; next = next->sibling)
+ if (of_node_get(next))
+ break;
+ of_node_put(prev);
+ return next;
+}
+#define __for_each_child_of_node(parent, child) \
+ for (child = __of_get_next_child(parent, NULL); child != NULL; \
+ child = __of_get_next_child(parent, child))
+
/**
* of_get_next_child - Iterate a node childs
* @node: parent node
@@ -710,11 +729,7 @@ struct device_node *of_get_next_child(const struct device_node *node,
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
- next = prev ? prev->sibling : node->child;
- for (; next; next = next->sibling)
- if (of_node_get(next))
- break;
- of_node_put(prev);
+ next = __of_get_next_child(node, prev);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return next;
}
@@ -734,6 +749,9 @@ struct device_node *of_get_next_available_child(const struct device_node *node,
struct device_node *next;
unsigned long flags;
+ if (!node)
+ return NULL;
+
raw_spin_lock_irqsave(&devtree_lock, flags);
next = prev ? prev->sibling : node->child;
for (; next; next = next->sibling) {
@@ -771,23 +789,78 @@ struct device_node *of_get_child_by_name(const struct device_node *node,
}
EXPORT_SYMBOL(of_get_child_by_name);
+static struct device_node *__of_find_node_by_path(struct device_node *parent,
+ const char *path)
+{
+ struct device_node *child;
+ int len = strchrnul(path, '/') - path;
+
+ if (!len)
+ return NULL;
+
+ __for_each_child_of_node(parent, child) {
+ const char *name = strrchr(child->full_name, '/');
+ if (WARN(!name, "malformed device_node %s\n", child->full_name))
+ continue;
+ name++;
+ if (strncmp(path, name, len) == 0 && (strlen(name) == len))
+ return child;
+ }
+ return NULL;
+}
+
/**
* of_find_node_by_path - Find a node matching a full OF path
- * @path: The full path to match
+ * @path: Either the full path to match, or if the path does not
+ * start with '/', the name of a property of the /aliases
+ * node (an alias). In the case of an alias, the node
+ * matching the alias' value will be returned.
+ *
+ * Valid paths:
+ * /foo/bar Full path
+ * foo Valid alias
+ * foo/bar Valid alias + relative path
*
* Returns a node pointer with refcount incremented, use
* of_node_put() on it when done.
*/
struct device_node *of_find_node_by_path(const char *path)
{
- struct device_node *np = of_allnodes;
+ struct device_node *np = NULL;
+ struct property *pp;
unsigned long flags;
+ if (strcmp(path, "/") == 0)
+ return of_node_get(of_allnodes);
+
+ /* The path could begin with an alias */
+ if (*path != '/') {
+ char *p = strchrnul(path, '/');
+ int len = p - path;
+
+ /* of_aliases must not be NULL */
+ if (!of_aliases)
+ return NULL;
+
+ for_each_property_of_node(of_aliases, pp) {
+ if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {
+ np = of_find_node_by_path(pp->value);
+ break;
+ }
+ }
+ if (!np)
+ return NULL;
+ path = p;
+ }
+
+ /* Step down the tree matching path components */
raw_spin_lock_irqsave(&devtree_lock, flags);
- for (; np; np = np->allnext) {
- if (np->full_name && (of_node_cmp(np->full_name, path) == 0)
- && of_node_get(np))
- break;
+ if (!np)
+ np = of_node_get(of_allnodes);
+ while (np && *path == '/') {
+ path++; /* Increment past '/' delimiter */
+ np = __of_find_node_by_path(np, path);
+ path = strchrnul(path, '/');
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
@@ -1800,7 +1873,7 @@ int of_update_property(struct device_node *np, struct property *newprop)
{
struct property **next, *oldprop;
unsigned long flags;
- int rc, found = 0;
+ int rc;
rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
if (rc)
@@ -1809,34 +1882,34 @@ int of_update_property(struct device_node *np, struct property *newprop)
if (!newprop->name)
return -EINVAL;
- oldprop = of_find_property(np, newprop->name, NULL);
- if (!oldprop)
- return of_add_property(np, newprop);
-
raw_spin_lock_irqsave(&devtree_lock, flags);
next = &np->properties;
- while (*next) {
+ oldprop = __of_find_property(np, newprop->name, NULL);
+ if (!oldprop) {
+ /* add the new node */
+ rc = __of_add_property(np, newprop);
+ } else while (*next) {
+ /* replace the node */
if (*next == oldprop) {
- /* found the node */
newprop->next = oldprop->next;
*next = newprop;
oldprop->next = np->deadprops;
np->deadprops = oldprop;
- found = 1;
break;
}
next = &(*next)->next;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
- if (!found)
- return -ENODEV;
+ if (rc)
+ return rc;
/* At early boot, bail out and defer setup to of_init() */
if (!of_kset)
- return found ? 0 : -ENODEV;
+ return 0;
/* Update the sysfs attribute */
- sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
+ if (oldprop)
+ sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
__of_add_property_sysfs(np, newprop);
return 0;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 7a2ef7bb8022..c4cddf0cd96d 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -12,7 +12,6 @@
#include <linux/kernel.h>
#include <linux/initrd.h>
#include <linux/memblock.h>
-#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
@@ -20,62 +19,13 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/slab.h>
+#include <linux/libfdt.h>
+#include <linux/debugfs.h>
+#include <linux/serial_core.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
-#ifdef CONFIG_PPC
-#include <asm/machdep.h>
-#endif /* CONFIG_PPC */
-
#include <asm/page.h>
-char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
-{
- return ((char *)blob) +
- be32_to_cpu(blob->off_dt_strings) + offset;
-}
-
-/**
- * of_fdt_get_property - Given a node in the given flat blob, return
- * the property ptr
- */
-void *of_fdt_get_property(struct boot_param_header *blob,
- unsigned long node, const char *name,
- unsigned long *size)
-{
- unsigned long p = node;
-
- do {
- u32 tag = be32_to_cpup((__be32 *)p);
- u32 sz, noff;
- const char *nstr;
-
- p += 4;
- if (tag == OF_DT_NOP)
- continue;
- if (tag != OF_DT_PROP)
- return NULL;
-
- sz = be32_to_cpup((__be32 *)p);
- noff = be32_to_cpup((__be32 *)(p + 4));
- p += 8;
- if (be32_to_cpu(blob->version) < 0x10)
- p = ALIGN(p, sz >= 8 ? 8 : 4);
-
- nstr = of_fdt_get_string(blob, noff);
- if (nstr == NULL) {
- pr_warning("Can't find property index name !\n");
- return NULL;
- }
- if (strcmp(name, nstr) == 0) {
- if (size)
- *size = sz;
- return (void *)p;
- }
- p += sz;
- p = ALIGN(p, 4);
- } while (1);
-}
-
/**
* of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list
@@ -86,13 +36,14 @@ void *of_fdt_get_property(struct boot_param_header *blob,
* On match, returns a non-zero value with smaller values returned for more
* specific compatible values.
*/
-int of_fdt_is_compatible(struct boot_param_header *blob,
+int of_fdt_is_compatible(const void *blob,
unsigned long node, const char *compat)
{
const char *cp;
- unsigned long cplen, l, score = 0;
+ int cplen;
+ unsigned long l, score = 0;
- cp = of_fdt_get_property(blob, node, "compatible", &cplen);
+ cp = fdt_getprop(blob, node, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
@@ -110,7 +61,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob,
/**
* 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,
+int of_fdt_match(const void *blob, unsigned long node,
const char *const *compat)
{
unsigned int tmp, score = 0;
@@ -149,30 +100,29 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
* @allnextpp: pointer to ->allnext from last allocated device_node
* @fpsize: Size of the node path up at the current depth.
*/
-static void * unflatten_dt_node(struct boot_param_header *blob,
+static void * unflatten_dt_node(void *blob,
void *mem,
- void **p,
+ int *poffset,
struct device_node *dad,
struct device_node ***allnextpp,
unsigned long fpsize)
{
+ const __be32 *p;
struct device_node *np;
struct property *pp, **prev_pp = NULL;
- char *pathp;
- u32 tag;
+ const char *pathp;
unsigned int l, allocl;
+ static int depth = 0;
+ int old_depth;
+ int offset;
int has_name = 0;
int new_format = 0;
- tag = be32_to_cpup(*p);
- if (tag != OF_DT_BEGIN_NODE) {
- pr_err("Weird tag at start of node: %x\n", tag);
+ pathp = fdt_get_name(blob, *poffset, &l);
+ if (!pathp)
return mem;
- }
- *p += 4;
- pathp = *p;
- l = allocl = strlen(pathp) + 1;
- *p = PTR_ALIGN(*p + l, 4);
+
+ allocl = l++;
/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
@@ -190,7 +140,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
fpsize = 1;
allocl = 2;
l = 1;
- *pathp = '\0';
+ pathp = "";
} else {
/* account for '/' and path size minus terminal 0
* already in 'l'
@@ -237,32 +187,23 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
}
}
/* process properties */
- while (1) {
- u32 sz, noff;
- char *pname;
-
- tag = be32_to_cpup(*p);
- if (tag == OF_DT_NOP) {
- *p += 4;
- continue;
- }
- if (tag != OF_DT_PROP)
+ for (offset = fdt_first_property_offset(blob, *poffset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(blob, offset))) {
+ const char *pname;
+ u32 sz;
+
+ if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
+ offset = -FDT_ERR_INTERNAL;
break;
- *p += 4;
- sz = be32_to_cpup(*p);
- noff = be32_to_cpup(*p + 4);
- *p += 8;
- if (be32_to_cpu(blob->version) < 0x10)
- *p = PTR_ALIGN(*p, sz >= 8 ? 8 : 4);
-
- pname = of_fdt_get_string(blob, noff);
+ }
+
if (pname == NULL) {
pr_info("Can't find property name in list !\n");
break;
}
if (strcmp(pname, "name") == 0)
has_name = 1;
- l = strlen(pname) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property));
if (allnextpp) {
@@ -274,26 +215,25 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
if ((strcmp(pname, "phandle") == 0) ||
(strcmp(pname, "linux,phandle") == 0)) {
if (np->phandle == 0)
- np->phandle = be32_to_cpup((__be32*)*p);
+ np->phandle = be32_to_cpup(p);
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff */
if (strcmp(pname, "ibm,phandle") == 0)
- np->phandle = be32_to_cpup((__be32 *)*p);
- pp->name = pname;
+ np->phandle = be32_to_cpup(p);
+ pp->name = (char *)pname;
pp->length = sz;
- pp->value = *p;
+ pp->value = (__be32 *)p;
*prev_pp = pp;
prev_pp = &pp->next;
}
- *p = PTR_ALIGN((*p) + sz, 4);
}
/* with version 0x10 we may not have the name property, recreate
* it here from the unit name if absent
*/
if (!has_name) {
- char *p1 = pathp, *ps = pathp, *pa = NULL;
+ const char *p1 = pathp, *ps = pathp, *pa = NULL;
int sz;
while (*p1) {
@@ -330,19 +270,18 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
if (!np->type)
np->type = "<NULL>";
}
- while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {
- if (tag == OF_DT_NOP)
- *p += 4;
- else
- mem = unflatten_dt_node(blob, mem, p, np, allnextpp,
- fpsize);
- tag = be32_to_cpup(*p);
- }
- if (tag != OF_DT_END_NODE) {
- pr_err("Weird tag at end of node: %x\n", tag);
- return mem;
- }
- *p += 4;
+
+ old_depth = depth;
+ *poffset = fdt_next_node(blob, *poffset, &depth);
+ if (depth < 0)
+ depth = 0;
+ while (*poffset > 0 && depth > old_depth)
+ mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
+ fpsize);
+
+ if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
+ pr_err("unflatten: error %d processing FDT\n", *poffset);
+
return mem;
}
@@ -358,12 +297,13 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
* @dt_alloc: An allocator that provides a virtual address to memory
* for the resulting tree
*/
-static void __unflatten_device_tree(struct boot_param_header *blob,
+static void __unflatten_device_tree(void *blob,
struct device_node **mynodes,
void * (*dt_alloc)(u64 size, u64 align))
{
unsigned long size;
- void *start, *mem;
+ int start;
+ void *mem;
struct device_node **allnextp = mynodes;
pr_debug(" -> unflatten_device_tree()\n");
@@ -374,18 +314,18 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
}
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));
+ pr_debug("magic: %08x\n", fdt_magic(blob));
+ pr_debug("size: %08x\n", fdt_totalsize(blob));
+ pr_debug("version: %08x\n", fdt_version(blob));
- if (be32_to_cpu(blob->magic) != OF_DT_HEADER) {
+ if (fdt_check_header(blob)) {
pr_err("Invalid device tree blob header\n");
return;
}
/* First pass, scan for size */
- start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
- size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
+ start = 0;
+ size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0);
size = ALIGN(size, 4);
pr_debug(" size is %lx, allocating...\n", size);
@@ -399,10 +339,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
pr_debug(" unflattening %p...\n", mem);
/* Second pass, do actual unflattening */
- start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
+ start = 0;
unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
- if (be32_to_cpup(start) != OF_DT_END)
- pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start));
if (be32_to_cpup(mem + size) != 0xdeadbeef)
pr_warning("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size));
@@ -427,9 +365,7 @@ static void *kernel_tree_alloc(u64 size, u64 align)
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);
+ __unflatten_device_tree(blob, mynodes, &kernel_tree_alloc);
}
EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree);
@@ -437,7 +373,7 @@ EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree);
int __initdata dt_root_addr_cells;
int __initdata dt_root_size_cells;
-struct boot_param_header *initial_boot_params;
+void *initial_boot_params;
#ifdef CONFIG_OF_EARLY_FLATTREE
@@ -449,8 +385,8 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
{
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
phys_addr_t base, size;
- unsigned long len;
- __be32 *prop;
+ int len;
+ const __be32 *prop;
int nomap, first = 1;
prop = of_get_flat_dt_prop(node, "reg", &len);
@@ -493,7 +429,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
*/
static int __init __reserved_mem_check_root(unsigned long node)
{
- __be32 *prop;
+ const __be32 *prop;
prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
@@ -557,9 +493,25 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
*/
void __init early_init_fdt_scan_reserved_mem(void)
{
+ int n;
+ u64 base, size;
+
if (!initial_boot_params)
return;
+ /* Reserve the dtb region */
+ early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
+ fdt_totalsize(initial_boot_params),
+ 0);
+
+ /* Process header /memreserve/ fields */
+ for (n = 0; ; n++) {
+ fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
+ if (!size)
+ break;
+ early_init_dt_reserve_memory_arch(base, size, 0);
+ }
+
of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
fdt_init_reserved_mem();
}
@@ -578,47 +530,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
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);
- const 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);
+ const void *blob = initial_boot_params;
+ const char *pathp;
+ int offset, rc = 0, depth = -1;
+
+ for (offset = fdt_next_node(blob, -1, &depth);
+ offset >= 0 && depth >= 0 && !rc;
+ offset = fdt_next_node(blob, offset, &depth)) {
+
+ pathp = fdt_get_name(blob, offset, NULL);
if (*pathp == '/')
pathp = kbasename(pathp);
- rc = it(p, pathp, depth, data);
- if (rc != 0)
- break;
- } while (1);
-
+ rc = it(offset, pathp, depth, data);
+ }
return rc;
}
@@ -627,14 +551,15 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
*/
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);
+ return 0;
+}
+
+/**
+ * of_get_flat_dt_size - Return the total size of the FDT
+ */
+int __init of_get_flat_dt_size(void)
+{
+ return fdt_totalsize(initial_boot_params);
}
/**
@@ -643,10 +568,10 @@ unsigned long __init of_get_flat_dt_root(void)
* 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)
+const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
+ int *size)
{
- return of_fdt_get_property(initial_boot_params, node, name, size);
+ return fdt_getprop(initial_boot_params, node, name, size);
}
/**
@@ -676,73 +601,6 @@ struct fdt_scan_status {
void *data;
};
-/**
- * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function
- */
-static int __init fdt_scan_node_by_path(unsigned long node, const char *uname,
- int depth, void *data)
-{
- struct fdt_scan_status *st = data;
-
- /*
- * if scan at the requested fdt node has been completed,
- * return -ENXIO to abort further scanning
- */
- if (depth <= st->depth)
- return -ENXIO;
-
- /* requested fdt node has been found, so call iterator function */
- if (st->found)
- return st->iterator(node, uname, depth, st->data);
-
- /* check if scanning automata is entering next level of fdt nodes */
- if (depth == st->depth + 1 &&
- strncmp(st->name, uname, st->namelen) == 0 &&
- uname[st->namelen] == 0) {
- st->depth += 1;
- if (st->name[st->namelen] == 0) {
- st->found = 1;
- } else {
- const char *next = st->name + st->namelen + 1;
- st->name = next;
- st->namelen = strcspn(next, "/");
- }
- return 0;
- }
-
- /* scan next fdt node */
- return 0;
-}
-
-/**
- * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each
- * child of the given path.
- * @path: path to start searching for children
- * @it: callback function
- * @data: context data pointer
- *
- * This function is used to scan the flattened device-tree starting from the
- * node given by path. It is used to extract information (like reserved
- * memory), which is required on ealy boot before we can unflatten the tree.
- */
-int __init of_scan_flat_dt_by_path(const char *path,
- int (*it)(unsigned long node, const char *name, int depth, void *data),
- void *data)
-{
- struct fdt_scan_status st = {path, 0, -1, 0, it, data};
- int ret = 0;
-
- if (initial_boot_params)
- ret = of_scan_flat_dt(fdt_scan_node_by_path, &st);
-
- if (!st.found)
- return -ENOENT;
- else if (ret == -ENXIO) /* scan has been completed */
- return 0;
- else
- return ret;
-}
-
const char * __init of_flat_dt_get_machine_name(void)
{
const char *name;
@@ -782,7 +640,7 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
}
if (!best_data) {
const char *prop;
- long size;
+ int size;
pr_err("\n unrecognized device tree list:\n[ ");
@@ -811,8 +669,8 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
static void __init early_init_dt_check_for_initrd(unsigned long node)
{
u64 start, end;
- unsigned long len;
- __be32 *prop;
+ int len;
+ const __be32 *prop;
pr_debug("Looking for initrd properties... ");
@@ -839,13 +697,68 @@ static inline void early_init_dt_check_for_initrd(unsigned long node)
}
#endif /* CONFIG_BLK_DEV_INITRD */
+#ifdef CONFIG_SERIAL_EARLYCON
+extern struct of_device_id __earlycon_of_table[];
+
+int __init early_init_dt_scan_chosen_serial(void)
+{
+ int offset;
+ const char *p;
+ int l;
+ const struct of_device_id *match = __earlycon_of_table;
+ const void *fdt = initial_boot_params;
+
+ offset = fdt_path_offset(fdt, "/chosen");
+ if (offset < 0)
+ offset = fdt_path_offset(fdt, "/chosen@0");
+ if (offset < 0)
+ return -ENOENT;
+
+ p = fdt_getprop(fdt, offset, "stdout-path", &l);
+ if (!p)
+ p = fdt_getprop(fdt, offset, "linux,stdout-path", &l);
+ if (!p || !l)
+ return -ENOENT;
+
+ /* Get the node specified by stdout-path */
+ offset = fdt_path_offset(fdt, p);
+ if (offset < 0)
+ return -ENODEV;
+
+ while (match->compatible) {
+ unsigned long addr;
+ if (fdt_node_check_compatible(fdt, offset, match->compatible)) {
+ match++;
+ continue;
+ }
+
+ addr = fdt_translate_address(fdt, offset);
+ if (!addr)
+ return -ENXIO;
+
+ of_setup_earlycon(addr, match->data);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int __init setup_of_earlycon(char *buf)
+{
+ if (buf)
+ return 0;
+
+ return early_init_dt_scan_chosen_serial();
+}
+early_param("earlycon", setup_of_earlycon);
+#endif
+
/**
* early_init_dt_scan_root - fetch the top level address and size cells
*/
int __init early_init_dt_scan_root(unsigned long node, const char *uname,
int depth, void *data)
{
- __be32 *prop;
+ const __be32 *prop;
if (depth != 0)
return 0;
@@ -867,9 +780,9 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname,
return 1;
}
-u64 __init dt_mem_next_cell(int s, __be32 **cellp)
+u64 __init dt_mem_next_cell(int s, const __be32 **cellp)
{
- __be32 *p = *cellp;
+ const __be32 *p = *cellp;
*cellp = p + s;
return of_read_number(p, s);
@@ -881,9 +794,9 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp)
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int depth, void *data)
{
- char *type = of_get_flat_dt_prop(node, "device_type", NULL);
- __be32 *reg, *endp;
- unsigned long l;
+ const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+ const __be32 *reg, *endp;
+ int l;
/* We are scanning "memory" nodes only */
if (type == NULL) {
@@ -891,7 +804,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
* The longtrail doesn't have a device_type on the
* /memory node, so look for the node called /memory@0.
*/
- if (depth != 1 || strcmp(uname, "memory@0") != 0)
+ if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
return 0;
} else if (strcmp(type, "memory") != 0)
return 0;
@@ -904,7 +817,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
endp = reg + (l / sizeof(__be32));
- pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
+ pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n",
uname, l, reg[0], reg[1], reg[2], reg[3]);
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
@@ -927,8 +840,8 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
int depth, void *data)
{
- unsigned long l;
- char *p;
+ int l;
+ const char *p;
pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
@@ -1003,8 +916,8 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align)
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
- pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n",
- base, size, nomap ? " (nomap)" : "");
+ pr_err("Reserved memory not supported, ignoring range 0x%pa - 0x%pa%s\n",
+ &base, &size, nomap ? " (nomap)" : "");
return -ENOSYS;
}
#endif
@@ -1018,7 +931,7 @@ bool __init early_init_dt_scan(void *params)
initial_boot_params = params;
/* check device tree validity */
- if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {
+ if (fdt_check_header(params)) {
initial_boot_params = NULL;
return false;
}
@@ -1073,9 +986,9 @@ void __init unflatten_and_copy_device_tree(void)
return;
}
- size = __be32_to_cpu(initial_boot_params->totalsize);
+ size = fdt_totalsize(initial_boot_params);
dt = early_init_dt_alloc_memory_arch(size,
- __alignof__(struct boot_param_header));
+ roundup_pow_of_two(FDT_V17_SIZE));
if (dt) {
memcpy(dt, initial_boot_params, size);
@@ -1084,4 +997,27 @@ void __init unflatten_and_copy_device_tree(void)
unflatten_device_tree();
}
+#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
+static struct debugfs_blob_wrapper flat_dt_blob;
+
+static int __init of_flat_dt_debugfs_export_fdt(void)
+{
+ struct dentry *d = debugfs_create_dir("device-tree", NULL);
+
+ if (!d)
+ return -ENOENT;
+
+ flat_dt_blob.data = initial_boot_params;
+ flat_dt_blob.size = fdt_totalsize(initial_boot_params);
+
+ d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
+ d, &flat_dt_blob);
+ if (!d)
+ return -ENOENT;
+
+ return 0;
+}
+module_init(of_flat_dt_debugfs_export_fdt);
+#endif
+
#endif /* CONFIG_OF_EARLY_FLATTREE */
diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c
new file mode 100644
index 000000000000..8d3dc6fbdb7a
--- /dev/null
+++ b/drivers/of/fdt_address.c
@@ -0,0 +1,241 @@
+/*
+ * FDT Address translation based on u-boot fdt_support.c which in turn was
+ * based on the kernel unflattened DT address translation code.
+ *
+ * (C) Copyright 2007
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/libfdt.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/sizes.h>
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS 4
+#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
+ (ns) > 0)
+
+/* Debug utility */
+#ifdef DEBUG
+static void __init of_dump_addr(const char *s, const __be32 *addr, int na)
+{
+ pr_debug("%s", s);
+ while(na--)
+ pr_cont(" %08x", *(addr++));
+ pr_debug("\n");
+}
+#else
+static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { }
+#endif
+
+/* Callbacks for bus specific translators */
+struct of_bus {
+ void (*count_cells)(const void *blob, int parentoffset,
+ int *addrc, int *sizec);
+ u64 (*map)(__be32 *addr, const __be32 *range,
+ int na, int ns, int pna);
+ int (*translate)(__be32 *addr, u64 offset, int na);
+};
+
+/* Default translator (generic bus) */
+static void __init fdt_bus_default_count_cells(const void *blob, int parentoffset,
+ int *addrc, int *sizec)
+{
+ const __be32 *prop;
+
+ if (addrc) {
+ prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL);
+ if (prop)
+ *addrc = be32_to_cpup(prop);
+ else
+ *addrc = dt_root_addr_cells;
+ }
+
+ if (sizec) {
+ prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
+ if (prop)
+ *sizec = be32_to_cpup(prop);
+ else
+ *sizec = dt_root_size_cells;
+ }
+}
+
+static u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range,
+ int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ cp = of_read_number(range, na);
+ s = of_read_number(range + na + pna, ns);
+ da = of_read_number(addr, na);
+
+ pr_debug("FDT: default map, cp=%llx, s=%llx, da=%llx\n",
+ cp, s, da);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na)
+{
+ u64 a = of_read_number(addr, na);
+ memset(addr, 0, na * 4);
+ a += offset;
+ if (na > 1)
+ addr[na - 2] = cpu_to_fdt32(a >> 32);
+ addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
+
+ return 0;
+}
+
+/* Array of bus specific translators */
+static const struct of_bus of_busses[] __initconst = {
+ /* Default */
+ {
+ .count_cells = fdt_bus_default_count_cells,
+ .map = fdt_bus_default_map,
+ .translate = fdt_bus_default_translate,
+ },
+};
+
+static int __init fdt_translate_one(const void *blob, int parent,
+ const struct of_bus *bus,
+ const struct of_bus *pbus, __be32 *addr,
+ int na, int ns, int pna, const char *rprop)
+{
+ const __be32 *ranges;
+ int rlen;
+ int rone;
+ u64 offset = OF_BAD_ADDR;
+
+ ranges = fdt_getprop(blob, parent, rprop, &rlen);
+ if (!ranges)
+ return 1;
+ if (rlen == 0) {
+ offset = of_read_number(addr, na);
+ memset(addr, 0, pna * 4);
+ pr_debug("FDT: empty ranges, 1:1 translation\n");
+ goto finish;
+ }
+
+ pr_debug("FDT: walking ranges...\n");
+
+ /* Now walk through the ranges */
+ rlen /= 4;
+ rone = na + pna + ns;
+ for (; rlen >= rone; rlen -= rone, ranges += rone) {
+ offset = bus->map(addr, ranges, na, ns, pna);
+ if (offset != OF_BAD_ADDR)
+ break;
+ }
+ if (offset == OF_BAD_ADDR) {
+ pr_debug("FDT: not found !\n");
+ return 1;
+ }
+ memcpy(addr, ranges + na, 4 * pna);
+
+ finish:
+ of_dump_addr("FDT: parent translation for:", addr, pna);
+ pr_debug("FDT: with offset: %llx\n", offset);
+
+ /* Translate it into parent bus space */
+ return pbus->translate(addr, offset, pna);
+}
+
+/*
+ * Translate an address from the device-tree into a CPU physical address,
+ * this walks up the tree and applies the various bus mappings on the
+ * way.
+ *
+ * Note: We consider that crossing any level with #size-cells == 0 to mean
+ * that translation is impossible (that is we are not dealing with a value
+ * 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 __init fdt_translate_address(const void *blob, int node_offset)
+{
+ int parent, len;
+ const struct of_bus *bus, *pbus;
+ const __be32 *reg;
+ __be32 addr[OF_MAX_ADDR_CELLS];
+ int na, ns, pna, pns;
+ u64 result = OF_BAD_ADDR;
+
+ pr_debug("FDT: ** translation for device %s **\n",
+ fdt_get_name(blob, node_offset, NULL));
+
+ reg = fdt_getprop(blob, node_offset, "reg", &len);
+ if (!reg) {
+ pr_err("FDT: warning: device tree node '%s' has no address.\n",
+ fdt_get_name(blob, node_offset, NULL));
+ goto bail;
+ }
+
+ /* Get parent & match bus type */
+ parent = fdt_parent_offset(blob, node_offset);
+ if (parent < 0)
+ goto bail;
+ bus = &of_busses[0];
+
+ /* Cound address cells & copy address locally */
+ bus->count_cells(blob, parent, &na, &ns);
+ if (!OF_CHECK_COUNTS(na, ns)) {
+ pr_err("FDT: Bad cell count for %s\n",
+ fdt_get_name(blob, node_offset, NULL));
+ goto bail;
+ }
+ memcpy(addr, reg, na * 4);
+
+ pr_debug("FDT: bus (na=%d, ns=%d) on %s\n",
+ na, ns, fdt_get_name(blob, parent, NULL));
+ of_dump_addr("OF: translating address:", addr, na);
+
+ /* Translate */
+ for (;;) {
+ /* Switch to parent bus */
+ node_offset = parent;
+ parent = fdt_parent_offset(blob, node_offset);
+
+ /* If root, we have finished */
+ if (parent < 0) {
+ pr_debug("FDT: reached root node\n");
+ result = of_read_number(addr, na);
+ break;
+ }
+
+ /* Get new parent bus and counts */
+ pbus = &of_busses[0];
+ pbus->count_cells(blob, parent, &pna, &pns);
+ if (!OF_CHECK_COUNTS(pna, pns)) {
+ pr_err("FDT: Bad cell count for %s\n",
+ fdt_get_name(blob, node_offset, NULL));
+ break;
+ }
+
+ pr_debug("FDT: parent bus (na=%d, ns=%d) on %s\n",
+ pna, pns, fdt_get_name(blob, parent, NULL));
+
+ /* Apply bus translation */
+ if (fdt_translate_one(blob, node_offset, bus, pbus,
+ addr, na, ns, pna, "ranges"))
+ break;
+
+ /* Complete the move up one level */
+ na = pna;
+ ns = pns;
+ bus = pbus;
+
+ of_dump_addr("FDT: one level translation:", addr, na);
+ }
+ bail:
+ return result;
+}
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 5aeb89411350..3e06a699352d 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -406,6 +406,28 @@ int of_irq_get(struct device_node *dev, int index)
}
/**
+ * of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
+ * @dev: pointer to device tree node
+ * @name: irq name
+ *
+ * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain
+ * is not yet created, or error code in case of any other failure.
+ */
+int of_irq_get_byname(struct device_node *dev, const char *name)
+{
+ int index;
+
+ if (unlikely(!name))
+ return -EINVAL;
+
+ index = of_property_match_string(dev, "interrupt-names", name);
+ if (index < 0)
+ return index;
+
+ return of_irq_get(dev, index);
+}
+
+/**
* of_irq_count - Count the number of IRQs a node uses
* @dev: pointer to device tree node
*/
diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index 8736bc7676c5..1710d9dc7fc2 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -18,8 +18,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
{
struct device_node *dn, *ppnode;
struct pci_dev *ppdev;
- u32 lspec;
- __be32 lspec_be;
__be32 laddr[3];
u8 pin;
int rc;
@@ -46,7 +44,6 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
return -ENODEV;
/* Now we walk up the PCI tree */
- lspec = pin;
for (;;) {
/* Get the pci_dev of our parent */
ppdev = pdev->bus->self;
@@ -80,14 +77,13 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
/* We can only get here if we hit a P2P bridge with no node,
* let's do standard swizzling and try again
*/
- lspec = pci_swizzle_interrupt_pin(pdev, lspec);
+ pin = pci_swizzle_interrupt_pin(pdev, pin);
pdev = ppdev;
}
out_irq->np = ppnode;
out_irq->args_count = 1;
- out_irq->args[0] = lspec;
- lspec_be = cpu_to_be32(lspec);
+ out_irq->args[0] = pin;
laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
laddr[1] = laddr[2] = cpu_to_be32(0);
return of_irq_parse_raw(laddr, out_irq);
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index daaaf935911d..632aae861375 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -95,8 +95,8 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
phys_addr_t start = 0, end = 0;
phys_addr_t base = 0, align = 0, size;
- unsigned long len;
- __be32 *prop;
+ int len;
+ const __be32 *prop;
int nomap;
int ret;
@@ -188,7 +188,7 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
continue;
- if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) {
+ if (initfn(rmem) == 0) {
pr_info("Reserved memory: initialized node %s, compatible id %s\n",
rmem->name, compat);
return 0;
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index e8376d646d98..92c060e58b02 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -51,10 +51,6 @@ struct platform_device *of_find_device_by_node(struct device_node *np)
}
EXPORT_SYMBOL(of_find_device_by_node);
-#if defined(CONFIG_PPC_DCR)
-#include <asm/dcr.h>
-#endif
-
#ifdef CONFIG_OF_ADDRESS
/*
* The following routines scan a subtree and registers a device for
@@ -68,66 +64,35 @@ EXPORT_SYMBOL(of_find_device_by_node);
* of_device_make_bus_id - Use the device node data to assign a unique name
* @dev: pointer to device structure that is linked to a device tree node
*
- * This routine will first try using either the dcr-reg or the reg property
- * value to derive a unique name. As a last resort it will use the node
- * name followed by a unique number.
+ * This routine will first try using the translated bus address to
+ * derive a unique name. If it cannot, then it will prepend names from
+ * parent nodes until a unique name can be derived.
*/
void of_device_make_bus_id(struct device *dev)
{
- static atomic_t bus_no_reg_magic;
struct device_node *node = dev->of_node;
const __be32 *reg;
u64 addr;
- const __be32 *addrp;
- int magic;
-#ifdef CONFIG_PPC_DCR
- /*
- * If it's a DCR based device, use 'd' for native DCRs
- * and 'D' for MMIO DCRs.
- */
- reg = of_get_property(node, "dcr-reg", NULL);
- if (reg) {
-#ifdef CONFIG_PPC_DCR_NATIVE
- dev_set_name(dev, "d%x.%s", *reg, node->name);
-#else /* CONFIG_PPC_DCR_NATIVE */
- u64 addr = of_translate_dcr_address(node, *reg, NULL);
- if (addr != OF_BAD_ADDR) {
- dev_set_name(dev, "D%llx.%s",
- (unsigned long long)addr, node->name);
+ /* Construct the name, using parent nodes if necessary to ensure uniqueness */
+ while (node->parent) {
+ /*
+ * If the address can be translated, then that is as much
+ * uniqueness as we need. Make it the first component and return
+ */
+ reg = of_get_property(node, "reg", NULL);
+ if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
+ dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s",
+ (unsigned long long)addr, node->name,
+ dev_name(dev));
return;
}
-#endif /* !CONFIG_PPC_DCR_NATIVE */
- }
-#endif /* CONFIG_PPC_DCR */
- /*
- * For MMIO, get the physical address
- */
- reg = of_get_property(node, "reg", NULL);
- if (reg) {
- if (of_can_translate_address(node)) {
- addr = of_translate_address(node, reg);
- } else {
- addrp = of_get_address(node, 0, NULL, NULL);
- if (addrp)
- addr = of_read_number(addrp, 1);
- else
- addr = OF_BAD_ADDR;
- }
- if (addr != OF_BAD_ADDR) {
- dev_set_name(dev, "%llx.%s",
- (unsigned long long)addr, node->name);
- return;
- }
+ /* format arguments only used if dev_name() resolves to NULL */
+ dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
+ strrchr(node->full_name, '/') + 1, dev_name(dev));
+ node = node->parent;
}
-
- /*
- * No BusID, use the node name and add a globally incremented
- * counter (and pray...)
- */
- magic = atomic_add_return(1, &bus_no_reg_magic);
- dev_set_name(dev, "%s.%d", node->name, magic - 1);
}
/**
@@ -149,9 +114,8 @@ struct platform_device *of_device_alloc(struct device_node *np,
return NULL;
/* count the io and irq resources */
- if (of_can_translate_address(np))
- while (of_address_to_resource(np, num_reg, &temp_res) == 0)
- num_reg++;
+ while (of_address_to_resource(np, num_reg, &temp_res) == 0)
+ num_reg++;
num_irq = of_irq_count(np);
/* Populate the resource table */
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
index fe70b86bcffb..077314eebb95 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/selftest.c
@@ -31,6 +31,51 @@ static struct selftest_results {
} \
}
+static void __init of_selftest_find_node_by_name(void)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_path("/testcase-data");
+ selftest(np && !strcmp("/testcase-data", np->full_name),
+ "find /testcase-data failed\n");
+ of_node_put(np);
+
+ /* Test if trailing '/' works */
+ np = of_find_node_by_path("/testcase-data/");
+ selftest(!np, "trailing '/' on /testcase-data/ should fail\n");
+
+ np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
+ selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
+ "find /testcase-data/phandle-tests/consumer-a failed\n");
+ of_node_put(np);
+
+ np = of_find_node_by_path("testcase-alias");
+ selftest(np && !strcmp("/testcase-data", np->full_name),
+ "find testcase-alias failed\n");
+ of_node_put(np);
+
+ /* Test if trailing '/' works on aliases */
+ np = of_find_node_by_path("testcase-alias/");
+ selftest(!np, "trailing '/' on testcase-alias/ should fail\n");
+
+ np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a");
+ selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name),
+ "find testcase-alias/phandle-tests/consumer-a failed\n");
+ of_node_put(np);
+
+ np = of_find_node_by_path("/testcase-data/missing-path");
+ selftest(!np, "non-existent path returned node %s\n", np->full_name);
+ of_node_put(np);
+
+ np = of_find_node_by_path("missing-alias");
+ selftest(!np, "non-existent alias returned node %s\n", np->full_name);
+ of_node_put(np);
+
+ np = of_find_node_by_path("testcase-alias/missing-path");
+ selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name);
+ of_node_put(np);
+}
+
static void __init of_selftest_dynamic(void)
{
struct device_node *np;
@@ -431,8 +476,12 @@ static void __init of_selftest_match_node(void)
static void __init of_selftest_platform_populate(void)
{
int irq;
- struct device_node *np;
+ struct device_node *np, *child;
struct platform_device *pdev;
+ struct of_device_id match[] = {
+ { .compatible = "test-device", },
+ {}
+ };
np = of_find_node_by_path("/testcase-data");
of_platform_populate(np, of_default_bus_match_table, NULL, NULL);
@@ -440,22 +489,32 @@ static void __init of_selftest_platform_populate(void)
/* Test that a missing irq domain returns -EPROBE_DEFER */
np = of_find_node_by_path("/testcase-data/testcase-device1");
pdev = of_find_device_by_node(np);
- if (!pdev)
- selftest(0, "device 1 creation failed\n");
+ selftest(pdev, "device 1 creation failed\n");
+
irq = platform_get_irq(pdev, 0);
- if (irq != -EPROBE_DEFER)
- selftest(0, "device deferred probe failed - %d\n", irq);
+ selftest(irq == -EPROBE_DEFER, "device deferred probe failed - %d\n", irq);
/* Test that a parsing failure does not return -EPROBE_DEFER */
np = of_find_node_by_path("/testcase-data/testcase-device2");
pdev = of_find_device_by_node(np);
- if (!pdev)
- selftest(0, "device 2 creation failed\n");
+ selftest(pdev, "device 2 creation failed\n");
irq = platform_get_irq(pdev, 0);
- if (irq >= 0 || irq == -EPROBE_DEFER)
- selftest(0, "device parsing error failed - %d\n", irq);
+ selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq);
- selftest(1, "passed");
+ np = of_find_node_by_path("/testcase-data/platform-tests");
+ if (!np) {
+ pr_err("No testcase data in device tree\n");
+ return;
+ }
+
+ for_each_child_of_node(np, child) {
+ struct device_node *grandchild;
+ of_platform_populate(child, match, NULL, NULL);
+ for_each_child_of_node(child, grandchild)
+ selftest(of_find_device_by_node(grandchild),
+ "Could not create device for node '%s'\n",
+ grandchild->name);
+ }
}
static int __init of_selftest(void)
@@ -470,6 +529,7 @@ static int __init of_selftest(void)
of_node_put(np);
pr_info("start of selftest - you will see error messages\n");
+ of_selftest_find_node_by_name();
of_selftest_dynamic();
of_selftest_parse_phandle_with_args();
of_selftest_property_match_string();
diff --git a/drivers/of/testcase-data/testcases.dtsi b/drivers/of/testcase-data/testcases.dtsi
index 3a5b75a8e4d7..6d8d980ac858 100644
--- a/drivers/of/testcase-data/testcases.dtsi
+++ b/drivers/of/testcase-data/testcases.dtsi
@@ -1,3 +1,4 @@
#include "tests-phandle.dtsi"
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
+#include "tests-platform.dtsi"
diff --git a/drivers/of/testcase-data/tests-phandle.dtsi b/drivers/of/testcase-data/tests-phandle.dtsi
index 788a4c24b8f5..ce0fe083d406 100644
--- a/drivers/of/testcase-data/tests-phandle.dtsi
+++ b/drivers/of/testcase-data/tests-phandle.dtsi
@@ -1,6 +1,10 @@
/ {
- testcase-data {
+ aliases {
+ testcase-alias = &testcase;
+ };
+
+ testcase: testcase-data {
security-password = "password";
duplicate-name = "duplicate";
duplicate-name { };
diff --git a/drivers/of/testcase-data/tests-platform.dtsi b/drivers/of/testcase-data/tests-platform.dtsi
new file mode 100644
index 000000000000..eb20eeb2b062
--- /dev/null
+++ b/drivers/of/testcase-data/tests-platform.dtsi
@@ -0,0 +1,35 @@
+
+/ {
+ testcase-data {
+ platform-tests {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ test-device@0 {
+ compatible = "test-device";
+ reg = <0x0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dev@100 {
+ compatible = "test-sub-device";
+ reg = <0x100>;
+ };
+ };
+
+ test-device@1 {
+ compatible = "test-device";
+ reg = <0x1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dev@100 {
+ compatible = "test-sub-device";
+ reg = <0x100>;
+ };
+ };
+ };
+ };
+};