blob: 4f6017ccae4d5fc67aed9e5245ba05bb6b554c77 [file] [log] [blame]
/*
* Copyright (c) 2018 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <plat/fdt.h>
#define FDT_MAGIC 0xd00dfeed
#define FDT_VERSION 17
struct fdt_header {
u32 magic;
u32 totalsize;
u32 off_dt_struct;
u32 off_dt_strings;
u32 off_mem_rsvmap;
u32 version;
u32 last_comp_version; /* <= 17 */
u32 boot_cpuid_phys;
u32 size_dt_strings;
u32 size_dt_struct;
} __attribute__((packed));
#define FDT_BEGIN_NODE 1
#define FDT_END_NODE 2
#define FDT_PROP 3
#define FDT_NOP 4
#define FDT_END 9
u32 fdt_rev32(u32 v)
{
return ((v & 0x000000FF) << 24) |
((v & 0x0000FF00) << 8) |
((v & 0x00FF0000) >> 8) |
((v & 0xFF000000) >> 24);
}
ulong fdt_strlen(const char *str)
{
ulong ret = 0;
while (*str != '\0') {
ret++;
str++;
}
return ret;
}
int fdt_strcmp(const char *a, const char *b)
{
/* search first diff or end of string */
for (; *a == *b && *a != '\0'; a++, b++);
return *a - *b;
}
int fdt_prop_string_index(const struct fdt_prop *prop,
const char *str)
{
int i;
ulong l = 0;
const char *p, *end;
p = prop->value;
end = p + prop->len;
for (i = 0; p < end; i++, p += l) {
l = fdt_strlen(p) + 1;
if (p + l > end)
return -1;
if (fdt_strcmp(str, p) == 0)
return i; /* Found it; return index */
}
return -1;
}
struct recursive_iter_info {
void (*fn)(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv);
void *fn_priv;
const char *str;
};
#define DATA32(ptr) fdt_rev32(*((u32*)ptr))
static void recursive_iter(char **data, struct recursive_iter_info *info,
const struct fdt_node *parent)
{
struct fdt_node node;
struct fdt_prop prop;
if (DATA32(*data) != FDT_BEGIN_NODE)
return;
node.data = *data;
(*data) += sizeof(u32);
node.parent = parent;
node.name = *data;
*data += fdt_strlen(*data) + 1;
while ((ulong)(*data) % sizeof(u32) != 0)
(*data)++;
node.depth = (parent) ? (parent->depth + 1) : 1;
/* Default cell counts, as per the FDT spec */
node.address_cells = 2;
node.size_cells = 1;
info->fn(&node, NULL, info->fn_priv);
while (DATA32(*data) != FDT_END_NODE) {
switch (DATA32(*data)) {
case FDT_PROP:
prop.node = &node;
*data += sizeof(u32);
prop.len = DATA32(*data);
*data += sizeof(u32);
prop.name = &info->str[DATA32(*data)];
*data += sizeof(u32);
prop.value = *data;
*data += prop.len;
while ((ulong)(*data) % sizeof(u32) != 0)
(*data)++;
info->fn(&node, &prop, info->fn_priv);
break;
case FDT_NOP:
*data += sizeof(u32);
break;
case FDT_BEGIN_NODE:
recursive_iter(data, info, &node);
break;
default:
return;
};
}
*data += sizeof(u32);
}
struct match_iter_info {
int (*match)(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv);
void *match_priv;
void (*fn)(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv);
void *fn_priv;
const char *str;
};
static void match_iter(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv)
{
char *data;
struct match_iter_info *minfo = priv;
struct fdt_prop nprop;
/* Do nothing if node+prop dont match */
if (!minfo->match(node, prop, minfo->match_priv))
return;
/* Call function for node */
if (minfo->fn)
minfo->fn(node, NULL, minfo->fn_priv);
/* Convert node to character stream */
data = node->data;
data += sizeof(u32);
/* Skip node name */
data += fdt_strlen(data) + 1;
while ((ulong)(data) % sizeof(u32) != 0)
data++;
/* Find node property and its value */
while (DATA32(data) == FDT_PROP) {
nprop.node = node;
data += sizeof(u32);
nprop.len = DATA32(data);
data += sizeof(u32);
nprop.name = &minfo->str[DATA32(data)];
data += sizeof(u32);
nprop.value = data;
data += nprop.len;
while ((ulong)(data) % sizeof(u32) != 0)
(data)++;
/* Call function for every property */
if (minfo->fn)
minfo->fn(node, &nprop, minfo->fn_priv);
}
}
int fdt_match_node_prop(void *fdt,
int (*match)(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv),
void *match_priv,
void (*fn)(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv),
void *fn_priv)
{
char *data;
u32 string_offset, data_offset;
struct fdt_header *header;
struct match_iter_info minfo;
struct recursive_iter_info rinfo;
if (!fdt || !match)
return -1;
header = fdt;
if (fdt_rev32(header->magic) != FDT_MAGIC ||
fdt_rev32(header->last_comp_version) > FDT_VERSION)
return -1;
string_offset = fdt_rev32(header->off_dt_strings);
data_offset = fdt_rev32(header->off_dt_struct);
minfo.match = match;
minfo.match_priv = match_priv;
minfo.fn = fn;
minfo.fn_priv = fn_priv;
minfo.str = (const char *)(fdt + string_offset);
rinfo.fn = match_iter;
rinfo.fn_priv = &minfo;
rinfo.str = minfo.str;
data = (char *)(fdt + data_offset);
recursive_iter(&data, &rinfo, NULL);
return 0;
}
struct match_compat_info {
const char *compat;
};
static int match_compat(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv)
{
struct match_compat_info *cinfo = priv;
if (!prop)
return 0;
if (fdt_strcmp(prop->name, "compatible"))
return 0;
if (fdt_prop_string_index(prop, cinfo->compat) < 0)
return 0;
return 1;
}
int fdt_compat_node_prop(void *fdt,
const char *compat,
void (*fn)(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv),
void *fn_priv)
{
struct match_compat_info cinfo = { .compat = compat };
return fdt_match_node_prop(fdt, match_compat, &cinfo,
fn, fn_priv);
}
static int match_walk(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv)
{
if (!prop)
return 1;
return 0;
}
int fdt_walk(void *fdt,
void (*fn)(const struct fdt_node *node,
const struct fdt_prop *prop,
void *priv),
void *fn_priv)
{
return fdt_match_node_prop(fdt, match_walk, NULL,
fn, fn_priv);
}
u32 fdt_size(void *fdt)
{
struct fdt_header *header;
if (!fdt)
return 0;
header = fdt;
if (fdt_rev32(header->magic) != FDT_MAGIC ||
fdt_rev32(header->last_comp_version) > FDT_VERSION)
return 0;
return fdt_rev32(header->totalsize);
}