diff --git a/include/libfdt.h b/include/libfdt.h
index f8bac73..3fd7c9f 100644
--- a/include/libfdt.h
+++ b/include/libfdt.h
@@ -89,6 +89,12 @@ uint32_t fdt_next_tag(const void *fdt, int offset,
 int fdt_num_reservemap(void *fdt, int *used, int *total);
 int fdt_get_reservemap(void *fdt, int n, struct fdt_reserve_entry *re);
 
+int fdt_node_is_compatible(const void *fdt, int nodeoffset,
+			   const char *compat);
+
+int fdt_find_compatible_node(const void *fdt, int startoffset,
+			     const char *type, const char *compat);
+
 /* Write-in-place functions */
 int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
 			const void *val, int len);
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
index 4e2c325..65ede88 100644
--- a/libfdt/fdt_ro.c
+++ b/libfdt/fdt_ro.c
@@ -55,6 +55,236 @@ char *fdt_string(const void *fdt, int stroffset)
 	return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
 }
 
+#define CONFIG_OF_LIBFDT_TABLE 64
+#if CONFIG_OF_LIBFDT_TABLE > 0
+
+#define FDT_DEVICE_MAX CONFIG_OF_LIBFDT_TABLE
+
+struct fdt_device {
+	int offset;
+	int level;
+	char *type;
+};
+
+struct fdt_device_cache {
+	void *fdt;
+	int count;
+	struct fdt_device nodes[FDT_DEVICE_MAX];
+};
+
+static struct fdt_device_cache fdt_devices;
+
+static int fdt_cache_devices(const void *fdt)
+{
+	int level = 0;
+	uint32_t tag;
+	int offset = 0, nextoffset;
+	struct fdt_device *dev = NULL;
+	struct fdt_property *prop;
+	int namestroff;
+
+	CHECK_HEADER(fdt);
+
+	tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
+	if (tag != FDT_BEGIN_NODE)
+		return -FDT_ERR_BADOFFSET;
+
+	do {
+		offset = nextoffset;
+		tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
+
+		switch (tag) {
+		case FDT_END:
+			return -FDT_ERR_TRUNCATED;
+
+		case FDT_BEGIN_NODE:
+			dev = &fdt_devices.nodes[fdt_devices.count];
+			dev->level = level;
+			dev->offset = offset;
+			dev->type = NULL;
+			level++;
+			fdt_devices.count++;
+			break;
+
+		case FDT_END_NODE:
+			level--;
+			break;
+
+		case FDT_PROP:
+
+			prop = fdt_offset_ptr_typed(fdt, offset, prop);
+			if (! prop)
+				continue;
+			namestroff = fdt32_to_cpu(prop->nameoff);
+			if (streq(fdt_string(fdt, namestroff), "device_type")) {
+				int len = fdt32_to_cpu(prop->len);
+				prop = fdt_offset_ptr(fdt, offset,
+						      sizeof(*prop)+len);
+				if (prop)
+					dev->type = (char *)prop->data;
+			}
+			break;
+
+		case FDT_NOP:
+			break;
+
+		default:
+			return -FDT_ERR_BADSTRUCTURE;
+		}
+	} while (level >= 0);
+
+	fdt_devices.fdt = (void *)fdt;
+
+	return 0;
+}
+
+#endif  /* CONFIG_OF_LIBFDT_TABLE > 0 */
+
+/*
+ * Check if the specified node is compatible by comparing the
+ * tokens in its "compatible" property with the specified string:
+ *
+ *   nodeoffset - starting place of the node
+ *   compat     - the string to match to one of the tokens
+ *                in the "compatible" list.
+ */
+int fdt_node_is_compatible(const void *fdt, int nodeoffset,
+			   const char *compat)
+{
+	const char* cp;
+	int cplen, l;
+
+	cp = fdt_getprop(fdt, nodeoffset, "compatible", &cplen);
+	if (cp == NULL)
+		return 0;
+	while (cplen > 0) {
+		if (strncmp(cp, compat, strlen(compat)) == 0)
+			return 1;
+		l = strlen(cp) + 1;
+		cp += l;
+		cplen -= l;
+	}
+
+	return 0;
+}
+
+/*
+ * Find a node based on its device type and one of the tokens in
+ * its its "compatible" property. On success, the offset of that
+ * node is return or an error code:
+ *
+ *   startoffset  - the node to start searching from or 0, the node
+ *                  you pass will not be searched, only the next one
+ *                  will; typically, you pass 0 to start the search
+ *                  and then what the previous call returned.
+ *   type         - the device type string to match against
+ *   compat       - the string to match to one of the tokens
+ *                  in the "compatible" list.
+ */
+#if CONFIG_OF_LIBFDT_TABLE > 0
+int fdt_find_compatible_node(const void *fdt, int startoffset,
+			     const char *type, const char *compat)
+{
+	static int startindex, nodeoffset;
+	struct fdt_device *dev;
+	int i;
+
+	if (fdt != fdt_devices.fdt)
+		fdt_cache_devices(fdt);
+
+	if (startoffset == 0)
+		startindex = 0;
+	else if (startoffset != nodeoffset)
+		return -FDT_ERR_BADOFFSET;
+	startindex++;
+
+	for (i = startindex; i < fdt_devices.count; i++) {
+		dev = &fdt_devices.nodes[i];
+		if (dev->type == NULL ||
+		    strncmp(dev->type, type, strlen(type)))
+			continue;
+		if (fdt_node_is_compatible(fdt_devices.fdt, dev->offset,
+					   compat)) {
+			nodeoffset = dev->offset;
+			startindex = i;
+			return nodeoffset;
+		}
+	}
+	startindex = i;
+	return -FDT_ERR_NOTFOUND;
+}
+
+#else  /* CONFIG_OF_LIBFDT_TABLE <= 0 */
+int fdt_find_compatible_node(const void *fdt, int startoffset,
+			     const char *type, const char *compat)
+{
+	static int level, typefound;
+	static int nodeoffset, nextoffset;
+	int offset, namestroff;
+	struct fdt_property *prop;
+	uint32_t tag;
+
+	CHECK_HEADER(fdt);
+
+	if (startoffset == 0) {
+		level = 0;
+		tag = fdt_next_tag(fdt, 0, &nextoffset, NULL);
+		if (tag != FDT_BEGIN_NODE)
+			return -FDT_ERR_BADOFFSET;
+	} else if (startoffset != nodeoffset)
+		return -FDT_ERR_BADOFFSET;
+
+	do {
+		offset = nextoffset;
+		tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
+
+		switch (tag) {
+		case FDT_END:
+			return -FDT_ERR_TRUNCATED;
+
+		case FDT_BEGIN_NODE:
+			level++;
+			nodeoffset = offset;
+			typefound = 0;
+			break;
+
+		case FDT_END_NODE:
+			level--;
+			break;
+
+		case FDT_PROP:
+			if (typefound)
+				continue;
+
+			prop = fdt_offset_ptr_typed(fdt, offset, prop);
+			if (! prop)
+				continue;
+			namestroff = fdt32_to_cpu(prop->nameoff);
+			if (streq(fdt_string(fdt, namestroff), "device_type")) {
+				int len = fdt32_to_cpu(prop->len);
+				typefound = 0;
+				prop = fdt_offset_ptr(fdt, offset,
+						      sizeof(*prop)+len);
+				if (! prop)
+					continue;
+				if (strncasecmp(prop->data, type, len - 1) == 0 &&
+				    fdt_node_is_compatible(fdt, nodeoffset, compat))
+					return nodeoffset;
+			}
+			break;
+
+		case FDT_NOP:
+			break;
+
+		default:
+			return -FDT_ERR_BADSTRUCTURE;
+		}
+	} while (level >= 0);
+
+	return -FDT_ERR_NOTFOUND;
+}
+#endif  /* CONFIG_OF_LIBFDT_TABLE > 0 */
+
 /*
  * Return the node offset of the node specified by:
  *   parentoffset - starting place (0 to start at the root)
