forked from Minki/linux
of/fdt: Convert FDT functions to use libfdt
The kernel FDT functions predate libfdt and are much more limited in functionality. Also, the kernel functions and libfdt functions are not compatible with each other because they have different definitions of node offsets. To avoid this incompatibility and in preparation to add more FDT parsing functions which will need libfdt, let's first convert the existing code to use libfdt. The FDT unflattening, top-level FDT scanning, and property retrieval functions are converted to use libfdt. The scanning code should be re-worked to be more efficient and understandable by using libfdt to find nodes directly by path or compatible strings. Signed-off-by: Rob Herring <robh@kernel.org> Tested-by: Michal Simek <michal.simek@xilinx.com> Tested-by: Grant Likely <grant.likely@linaro.org> Tested-by: Stephen Chivers <schivers@csc.com>
This commit is contained in:
parent
9d0c4dfedd
commit
e6a6928c3e
@ -20,6 +20,7 @@ config OF_SELFTEST
|
||||
config OF_FLATTREE
|
||||
bool
|
||||
select DTC
|
||||
select LIBFDT
|
||||
|
||||
config OF_EARLY_FLATTREE
|
||||
bool
|
||||
|
@ -10,3 +10,5 @@ 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
|
||||
|
201
drivers/of/fdt.c
201
drivers/of/fdt.c
@ -19,58 +19,11 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/libfdt.h>
|
||||
|
||||
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
|
||||
#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,
|
||||
int *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
|
||||
@ -88,7 +41,7 @@ int of_fdt_is_compatible(struct boot_param_header *blob,
|
||||
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) {
|
||||
@ -147,28 +100,27 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
|
||||
*/
|
||||
static void * unflatten_dt_node(struct boot_param_header *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
|
||||
@ -186,7 +138,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'
|
||||
@ -233,32 +185,23 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
|
||||
}
|
||||
}
|
||||
/* process properties */
|
||||
while (1) {
|
||||
u32 sz, noff;
|
||||
char *pname;
|
||||
for (offset = fdt_first_property_offset(blob, *poffset);
|
||||
(offset >= 0);
|
||||
(offset = fdt_next_property_offset(blob, offset))) {
|
||||
const char *pname;
|
||||
u32 sz;
|
||||
|
||||
tag = be32_to_cpup(*p);
|
||||
if (tag == OF_DT_NOP) {
|
||||
*p += 4;
|
||||
continue;
|
||||
}
|
||||
if (tag != OF_DT_PROP)
|
||||
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) {
|
||||
@ -270,26 +213,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) {
|
||||
@ -326,19 +268,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;
|
||||
}
|
||||
|
||||
@ -359,7 +300,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
|
||||
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");
|
||||
@ -380,7 +322,7 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
|
||||
}
|
||||
|
||||
/* First pass, scan for size */
|
||||
start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
|
||||
start = 0;
|
||||
size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
|
||||
size = ALIGN(size, 4);
|
||||
|
||||
@ -395,10 +337,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));
|
||||
@ -574,47 +514,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;
|
||||
const void *blob = initial_boot_params;
|
||||
const char *pathp;
|
||||
int offset, rc = 0, depth = -1;
|
||||
|
||||
do {
|
||||
u32 tag = be32_to_cpup((__be32 *)p);
|
||||
const char *pathp;
|
||||
for (offset = fdt_next_node(blob, -1, &depth);
|
||||
offset >= 0 && depth >= 0 && !rc;
|
||||
offset = fdt_next_node(blob, offset, &depth)) {
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -623,14 +535,7 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -642,7 +547,7 @@ unsigned long __init of_get_flat_dt_root(void)
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,7 +84,6 @@ extern char __dtb_start[];
|
||||
extern char __dtb_end[];
|
||||
|
||||
/* For scanning the flat device-tree at boot time */
|
||||
extern char *find_flat_dt_string(u32 offset);
|
||||
extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname,
|
||||
int depth, void *data),
|
||||
void *data);
|
||||
|
Loading…
Reference in New Issue
Block a user