/*
* Procedures for creating, accessing and interpreting the device tree.
*
* Paul Mackerras August 1996.
* Copyright (C) 1996-2005 Paul Mackerras.
*
* Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
* {engebret|bergner}@us.ibm.com
*
* 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 of the License, or (at your option) any later version.
*/
#include <stdarg.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/stringify.h>
#include <linux/delay.h>
#include <linux/initrd.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/kexec.h>
#include <linux/debugfs.h>
#include <linux/irq.h>
#include <linux/lmb.h>
#include <asm/prom.h>
#include <asm/page.h>
#include <asm/processor.h>
#include <asm/irq.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/mmu.h>
#include <asm/pgtable.h>
#include <asm/sections.h>
#include <asm/pci-bridge.h>
static int __initdata dt_root_addr_cells;
static int __initdata dt_root_size_cells;
typedef u32 cell_t;
static struct boot_param_header *initial_boot_params;
/* export that to outside world */
struct device_node *of_chosen;
static inline char *find_flat_dt_string(u32 offset)
{
return ((char *)initial_boot_params) +
initial_boot_params->off_dt_strings + offset;
}
/**
* This function is used to scan the flattened device-tree, it is
* used to extract the memory informations 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) +
initial_boot_params->off_dt_struct;
int rc = 0;
int depth = -1;
do {
u32 tag = *((u32 *)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 = *((u32 *)p);
p += 8;
if (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) {
printk(KERN_WARNING "Invalid tag %x scanning flattened"
" 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;
}
unsigned long __init of_get_flat_dt_root(void)
{
unsigned long p = ((unsigned long)initial_boot_params) +
initial_boot_params->off_dt_struct;
while (*((u32 *)p) == OF_DT_NOP)
p += 4;
BUG_ON(*((u32 *)p) != OF_DT_BEGIN_NODE);
p += 4;
return _ALIGN(p + strlen((char *)p) + 1, 4);
}
/**
* 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)
{
unsigned long p = node;
do {
u32 tag = *((u32 *)p);
u32 sz, noff;
const char *nstr;
p += 4;
if (tag == OF_DT_NOP)
continue;
if (tag != OF_DT_PROP)
return NULL;
sz = *((u32 *)p);
noff = *((u32 *)(p + 4));
p += 8;
if (initial_boot_params->version < 0x10)
p = _ALIGN(p, sz >= 8 ? 8 : 4);
nstr = find_flat_dt_string(noff);
if (nstr == NULL) {
printk(KERN_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);
}
int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
{
const char *cp;
unsigned long cplen, l;
cp = of_get_flat_dt_prop(node, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
if (strncasecmp(cp, compat, strlen(compat)) == 0)
return 1;
l = strlen(cp) + 1;
cp += l;
cplen -= l;
}
return 0;
}
static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
unsigned long align)
{
void *res;
*mem = _ALIGN(*mem, align);
res = (void *)*mem;
*mem += size;
return res;
}
static unsigned long __init unflatten_dt_node(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;
char *pathp;
u32 tag;
unsigned int l, allocl;
int has_name = 0;
int new_format = 0;
tag = *((u32 *)(*p));
if (tag != OF_DT_BEGIN_NODE) {
printk("Weird tag at start of node: %x\n", tag
|