aboutsummaryrefslogtreecommitdiff
path: root/platform/common/tinyfdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'platform/common/tinyfdt.c')
-rw-r--r--platform/common/tinyfdt.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/platform/common/tinyfdt.c b/platform/common/tinyfdt.c
new file mode 100644
index 0000000..4f6017c
--- /dev/null
+++ b/platform/common/tinyfdt.c
@@ -0,0 +1,314 @@
+/*
+ * 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);
+}