mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 02:15:45 +01:00 
			
		
		
		
	dm: core: Add operations on device tree references
Since U-Boot supports both a live tree and a flat tree, we need an easy way to access the tree without worrying about which is currently active. To support this, U-Boot has the concept of an ofnode, which can refer either to a live tree node or a flat tree node. For the live tree, the reference contains a pointer to the node (struct device_node *) or NULL if the node is invalid. For the flat tree, the reference contains the node offset or -1 if the node is invalid. Add a basic set of operations using ofnodes. These are implemented by using either libfdt functions (in the case of a flat DT reference) or the live-tree of_...() functions. Note that it is not possible to have both live and flat references active at the same time. As soon as the live tree is available, everything in U-Boot should switch to using that. This avoids confusion and allows us to assume that the type of a reference is simply based on whether we have a live tree yet, or not. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		| @@ -12,3 +12,4 @@ obj-$(CONFIG_DM)	+= dump.o | ||||
| obj-$(CONFIG_$(SPL_)REGMAP)	+= regmap.o | ||||
| obj-$(CONFIG_$(SPL_)SYSCON)	+= syscon-uclass.o | ||||
| obj-$(CONFIG_OF_LIVE) += of_access.o | ||||
| obj-$(CONFIG_OF_CONTROL) += ofnode.o | ||||
|   | ||||
							
								
								
									
										552
									
								
								drivers/core/ofnode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										552
									
								
								drivers/core/ofnode.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,552 @@ | ||||
| /* | ||||
|  * Copyright (c) 2017 Google, Inc | ||||
|  * Written by Simon Glass <sjg@chromium.org> | ||||
|  * | ||||
|  * SPDX-License-Identifier:	GPL-2.0+ | ||||
|  */ | ||||
|  | ||||
| #include <common.h> | ||||
| #include <dm.h> | ||||
| #include <fdtdec.h> | ||||
| #include <fdt_support.h> | ||||
| #include <libfdt.h> | ||||
| #include <dm/of_access.h> | ||||
| #include <dm/ofnode.h> | ||||
| #include <linux/err.h> | ||||
|  | ||||
| int ofnode_read_u32(ofnode node, const char *propname, u32 *outp) | ||||
| { | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	debug("%s: %s: ", __func__, propname); | ||||
|  | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		return of_read_u32(ofnode_to_np(node), propname, outp); | ||||
| 	} else { | ||||
| 		const int *cell; | ||||
| 		int len; | ||||
|  | ||||
| 		cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), | ||||
| 				   propname, &len); | ||||
| 		if (!cell || len < sizeof(int)) { | ||||
| 			debug("(not found)\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 		*outp = fdt32_to_cpu(cell[0]); | ||||
| 	} | ||||
| 	debug("%#x (%d)\n", *outp, *outp); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int ofnode_read_u32_default(ofnode node, const char *propname, u32 def) | ||||
| { | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	ofnode_read_u32(node, propname, &def); | ||||
|  | ||||
| 	return def; | ||||
| } | ||||
|  | ||||
| int ofnode_read_s32_default(ofnode node, const char *propname, s32 def) | ||||
| { | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	ofnode_read_u32(node, propname, (u32 *)&def); | ||||
|  | ||||
| 	return def; | ||||
| } | ||||
|  | ||||
| bool ofnode_read_bool(ofnode node, const char *propname) | ||||
| { | ||||
| 	bool val; | ||||
|  | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	debug("%s: %s: ", __func__, propname); | ||||
|  | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		val = !!of_find_property(ofnode_to_np(node), propname, NULL); | ||||
| 	} else { | ||||
| 		val = !!fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), | ||||
| 				    propname, NULL); | ||||
| 	} | ||||
| 	debug("%s\n", val ? "true" : "false"); | ||||
|  | ||||
| 	return val; | ||||
| } | ||||
|  | ||||
| const char *ofnode_read_string(ofnode node, const char *propname) | ||||
| { | ||||
| 	const char *str = NULL; | ||||
| 	int len = -1; | ||||
|  | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	debug("%s: %s: ", __func__, propname); | ||||
|  | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		struct property *prop = of_find_property( | ||||
| 				ofnode_to_np(node), propname, NULL); | ||||
|  | ||||
| 		if (prop) { | ||||
| 			str = prop->value; | ||||
| 			len = prop->length; | ||||
| 		} | ||||
| 	} else { | ||||
| 		str = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), | ||||
| 				  propname, &len); | ||||
| 	} | ||||
| 	if (!str) { | ||||
| 		debug("<not found>\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (strnlen(str, len) >= len) { | ||||
| 		debug("<invalid>\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	debug("%s\n", str); | ||||
|  | ||||
| 	return str; | ||||
| } | ||||
|  | ||||
| ofnode ofnode_find_subnode(ofnode node, const char *subnode_name) | ||||
| { | ||||
| 	ofnode subnode; | ||||
|  | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	debug("%s: %s: ", __func__, subnode_name); | ||||
|  | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		const struct device_node *np = ofnode_to_np(node); | ||||
|  | ||||
| 		for (np = np->child; np; np = np->sibling) { | ||||
| 			if (!strcmp(subnode_name, np->name)) | ||||
| 				break; | ||||
| 		} | ||||
| 		subnode = np_to_ofnode(np); | ||||
| 	} else { | ||||
| 		int ooffset = fdt_subnode_offset(gd->fdt_blob, | ||||
| 				ofnode_to_offset(node), subnode_name); | ||||
| 		subnode = offset_to_ofnode(ooffset); | ||||
| 	} | ||||
| 	debug("%s\n", ofnode_valid(subnode) ? | ||||
| 	      ofnode_get_name(subnode) : "<none>"); | ||||
|  | ||||
| 	return subnode; | ||||
| } | ||||
|  | ||||
| int ofnode_read_u32_array(ofnode node, const char *propname, | ||||
| 			  u32 *out_values, size_t sz) | ||||
| { | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	debug("%s: %s: ", __func__, propname); | ||||
|  | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		return of_read_u32_array(ofnode_to_np(node), propname, | ||||
| 					 out_values, sz); | ||||
| 	} else { | ||||
| 		return fdtdec_get_int_array(gd->fdt_blob, | ||||
| 					    ofnode_to_offset(node), propname, | ||||
| 					    out_values, sz); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ofnode ofnode_first_subnode(ofnode node) | ||||
| { | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	if (ofnode_is_np(node)) | ||||
| 		return np_to_ofnode(node.np->child); | ||||
|  | ||||
| 	return offset_to_ofnode( | ||||
| 		fdt_first_subnode(gd->fdt_blob, ofnode_to_offset(node))); | ||||
| } | ||||
|  | ||||
| ofnode ofnode_next_subnode(ofnode node) | ||||
| { | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	if (ofnode_is_np(node)) | ||||
| 		return np_to_ofnode(node.np->sibling); | ||||
|  | ||||
| 	return offset_to_ofnode( | ||||
| 		fdt_next_subnode(gd->fdt_blob, ofnode_to_offset(node))); | ||||
| } | ||||
|  | ||||
| const char *ofnode_get_name(ofnode node) | ||||
| { | ||||
| 	assert(ofnode_valid(node)); | ||||
| 	if (ofnode_is_np(node)) | ||||
| 		return strrchr(node.np->full_name, '/') + 1; | ||||
|  | ||||
| 	return fdt_get_name(gd->fdt_blob, ofnode_to_offset(node), NULL); | ||||
| } | ||||
|  | ||||
| int ofnode_read_size(ofnode node, const char *propname) | ||||
| { | ||||
| 	int len; | ||||
|  | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		struct property *prop = of_find_property( | ||||
| 				ofnode_to_np(node), propname, NULL); | ||||
|  | ||||
| 		if (prop) | ||||
| 			return prop->length; | ||||
| 	} else { | ||||
| 		if (fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, | ||||
| 				&len)) | ||||
| 			return len; | ||||
| 	} | ||||
|  | ||||
| 	return -EINVAL; | ||||
| } | ||||
|  | ||||
| int ofnode_stringlist_search(ofnode node, const char *property, | ||||
| 			     const char *string) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		return of_property_match_string(ofnode_to_np(node), | ||||
| 						property, string); | ||||
| 	} else { | ||||
| 		int ret; | ||||
|  | ||||
| 		ret = fdt_stringlist_search(gd->fdt_blob, | ||||
| 					    ofnode_to_offset(node), property, | ||||
| 					    string); | ||||
| 		if (ret == -FDT_ERR_NOTFOUND) | ||||
| 			return -ENODATA; | ||||
| 		else if (ret < 0) | ||||
| 			return -EINVAL; | ||||
|  | ||||
| 		return ret; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int ofnode_read_string_index(ofnode node, const char *property, int index, | ||||
| 			     const char **outp) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		return of_property_read_string_index(ofnode_to_np(node), | ||||
| 						     property, index, outp); | ||||
| 	} else { | ||||
| 		int len; | ||||
|  | ||||
| 		*outp = fdt_stringlist_get(gd->fdt_blob, ofnode_to_offset(node), | ||||
| 					   property, index, &len); | ||||
| 		if (len < 0) | ||||
| 			return -EINVAL; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in, | ||||
| 					    struct ofnode_phandle_args *out) | ||||
| { | ||||
| 	assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); | ||||
| 	out->node = offset_to_ofnode(in->node); | ||||
| 	out->args_count = in->args_count; | ||||
| 	memcpy(out->args, in->args, sizeof(out->args)); | ||||
| } | ||||
|  | ||||
| static void ofnode_from_of_phandle_args(struct of_phandle_args *in, | ||||
| 					struct ofnode_phandle_args *out) | ||||
| { | ||||
| 	assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS); | ||||
| 	out->node = np_to_ofnode(in->np); | ||||
| 	out->args_count = in->args_count; | ||||
| 	memcpy(out->args, in->args, sizeof(out->args)); | ||||
| } | ||||
|  | ||||
| int ofnode_parse_phandle_with_args(ofnode node, const char *list_name, | ||||
| 				   const char *cells_name, int cell_count, | ||||
| 				   int index, | ||||
| 				   struct ofnode_phandle_args *out_args) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		struct of_phandle_args args; | ||||
| 		int ret; | ||||
|  | ||||
| 		ret = of_parse_phandle_with_args(ofnode_to_np(node), | ||||
| 				list_name, cells_name, index, &args); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		ofnode_from_of_phandle_args(&args, out_args); | ||||
| 	} else { | ||||
| 		struct fdtdec_phandle_args args; | ||||
| 		int ret; | ||||
|  | ||||
| 		ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, | ||||
| 				ofnode_to_offset(node), list_name, cells_name, | ||||
| 				cell_count, index, &args); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		ofnode_from_fdtdec_phandle_args(&args, out_args); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| ofnode ofnode_path(const char *path) | ||||
| { | ||||
| 	if (of_live_active()) | ||||
| 		return np_to_ofnode(of_find_node_by_path(path)); | ||||
| 	else | ||||
| 		return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path)); | ||||
| } | ||||
|  | ||||
| const char *ofnode_get_chosen_prop(const char *name) | ||||
| { | ||||
| 	ofnode chosen_node; | ||||
|  | ||||
| 	chosen_node = ofnode_path("/chosen"); | ||||
|  | ||||
| 	return ofnode_read_string(chosen_node, name); | ||||
| } | ||||
|  | ||||
| ofnode ofnode_get_chosen_node(const char *name) | ||||
| { | ||||
| 	const char *prop; | ||||
|  | ||||
| 	prop = ofnode_get_chosen_prop(name); | ||||
| 	if (!prop) | ||||
| 		return ofnode_null(); | ||||
|  | ||||
| 	return ofnode_path(prop); | ||||
| } | ||||
|  | ||||
| static int decode_timing_property(ofnode node, const char *name, | ||||
| 				  struct timing_entry *result) | ||||
| { | ||||
| 	int length, ret = 0; | ||||
|  | ||||
| 	length = ofnode_read_size(node, name); | ||||
| 	if (length < 0) { | ||||
| 		debug("%s: could not find property %s\n", | ||||
| 		      ofnode_get_name(node), name); | ||||
| 		return length; | ||||
| 	} | ||||
|  | ||||
| 	if (length == sizeof(u32)) { | ||||
| 		result->typ = ofnode_read_u32_default(node, name, 0); | ||||
| 		result->min = result->typ; | ||||
| 		result->max = result->typ; | ||||
| 	} else { | ||||
| 		ret = ofnode_read_u32_array(node, name, &result->min, 3); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int ofnode_decode_display_timing(ofnode parent, int index, | ||||
| 				 struct display_timing *dt) | ||||
| { | ||||
| 	int i; | ||||
| 	ofnode timings, node; | ||||
| 	u32 val = 0; | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	timings = ofnode_find_subnode(parent, "display-timings"); | ||||
| 	if (!ofnode_valid(timings)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	for (i = 0, node = ofnode_first_subnode(timings); | ||||
| 	     ofnode_valid(node) && i != index; | ||||
| 	     node = ofnode_first_subnode(node)) | ||||
| 		i++; | ||||
|  | ||||
| 	if (!ofnode_valid(node)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	memset(dt, 0, sizeof(*dt)); | ||||
|  | ||||
| 	ret |= decode_timing_property(node, "hback-porch", &dt->hback_porch); | ||||
| 	ret |= decode_timing_property(node, "hfront-porch", &dt->hfront_porch); | ||||
| 	ret |= decode_timing_property(node, "hactive", &dt->hactive); | ||||
| 	ret |= decode_timing_property(node, "hsync-len", &dt->hsync_len); | ||||
| 	ret |= decode_timing_property(node, "vback-porch", &dt->vback_porch); | ||||
| 	ret |= decode_timing_property(node, "vfront-porch", &dt->vfront_porch); | ||||
| 	ret |= decode_timing_property(node, "vactive", &dt->vactive); | ||||
| 	ret |= decode_timing_property(node, "vsync-len", &dt->vsync_len); | ||||
| 	ret |= decode_timing_property(node, "clock-frequency", &dt->pixelclock); | ||||
|  | ||||
| 	dt->flags = 0; | ||||
| 	val = ofnode_read_u32_default(node, "vsync-active", -1); | ||||
| 	if (val != -1) { | ||||
| 		dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : | ||||
| 				DISPLAY_FLAGS_VSYNC_LOW; | ||||
| 	} | ||||
| 	val = ofnode_read_u32_default(node, "hsync-active", -1); | ||||
| 	if (val != -1) { | ||||
| 		dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : | ||||
| 				DISPLAY_FLAGS_HSYNC_LOW; | ||||
| 	} | ||||
| 	val = ofnode_read_u32_default(node, "de-active", -1); | ||||
| 	if (val != -1) { | ||||
| 		dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : | ||||
| 				DISPLAY_FLAGS_DE_LOW; | ||||
| 	} | ||||
| 	val = ofnode_read_u32_default(node, "pixelclk-active", -1); | ||||
| 	if (val != -1) { | ||||
| 		dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : | ||||
| 				DISPLAY_FLAGS_PIXDATA_NEGEDGE; | ||||
| 	} | ||||
|  | ||||
| 	if (ofnode_read_bool(node, "interlaced")) | ||||
| 		dt->flags |= DISPLAY_FLAGS_INTERLACED; | ||||
| 	if (ofnode_read_bool(node, "doublescan")) | ||||
| 		dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; | ||||
| 	if (ofnode_read_bool(node, "doubleclk")) | ||||
| 		dt->flags |= DISPLAY_FLAGS_DOUBLECLK; | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| const u32 *ofnode_read_prop(ofnode node, const char *propname, int *lenp) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		struct property *prop; | ||||
|  | ||||
| 		prop = of_find_property(ofnode_to_np(node), propname, lenp); | ||||
| 		if (!prop) | ||||
| 			return NULL; | ||||
| 		return prop->value; | ||||
| 	} else { | ||||
| 		return fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), | ||||
| 				   propname, lenp); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool ofnode_is_available(ofnode node) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) | ||||
| 		return of_device_is_available(ofnode_to_np(node)); | ||||
| 	else | ||||
| 		return fdtdec_get_is_enabled(gd->fdt_blob, | ||||
| 					     ofnode_to_offset(node)); | ||||
| } | ||||
|  | ||||
| fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property, | ||||
| 				fdt_size_t *sizep) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		int na, ns; | ||||
| 		int psize; | ||||
| 		const struct device_node *np = ofnode_to_np(node); | ||||
| 		const __be32 *prop = of_get_property(np, "reg", &psize); | ||||
|  | ||||
| 		na = of_n_addr_cells(np); | ||||
| 		ns = of_n_addr_cells(np); | ||||
| 		*sizep = of_read_number(prop + na, ns); | ||||
| 		return of_read_number(prop, na); | ||||
| 	} else { | ||||
| 		return fdtdec_get_addr_size(gd->fdt_blob, | ||||
| 					    ofnode_to_offset(node), property, | ||||
| 					    sizep); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname, | ||||
| 					size_t sz) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) { | ||||
| 		const struct device_node *np = ofnode_to_np(node); | ||||
| 		int psize; | ||||
| 		const __be32 *prop = of_get_property(np, propname, &psize); | ||||
|  | ||||
| 		if (!prop || sz != psize) | ||||
| 			return NULL; | ||||
| 		return (uint8_t *)prop; | ||||
|  | ||||
| 	} else { | ||||
| 		return fdtdec_locate_byte_array(gd->fdt_blob, | ||||
| 				ofnode_to_offset(node), propname, sz); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, | ||||
| 			 const char *propname, struct fdt_pci_addr *addr) | ||||
| { | ||||
| 	const u32 *cell; | ||||
| 	int len; | ||||
| 	int ret = -ENOENT; | ||||
|  | ||||
| 	debug("%s: %s: ", __func__, propname); | ||||
|  | ||||
| 	/* | ||||
| 	 * If we follow the pci bus bindings strictly, we should check | ||||
| 	 * the value of the node's parent node's #address-cells and | ||||
| 	 * #size-cells. They need to be 3 and 2 accordingly. However, | ||||
| 	 * for simplicity we skip the check here. | ||||
| 	 */ | ||||
| 	cell = ofnode_read_prop(node, propname, &len); | ||||
| 	if (!cell) | ||||
| 		goto fail; | ||||
|  | ||||
| 	if ((len % FDT_PCI_REG_SIZE) == 0) { | ||||
| 		int num = len / FDT_PCI_REG_SIZE; | ||||
| 		int i; | ||||
|  | ||||
| 		for (i = 0; i < num; i++) { | ||||
| 			debug("pci address #%d: %08lx %08lx %08lx\n", i, | ||||
| 			      (ulong)fdt32_to_cpu(cell[0]), | ||||
| 			      (ulong)fdt32_to_cpu(cell[1]), | ||||
| 			      (ulong)fdt32_to_cpu(cell[2])); | ||||
| 			if ((fdt32_to_cpu(*cell) & type) == type) { | ||||
| 				addr->phys_hi = fdt32_to_cpu(cell[0]); | ||||
| 				addr->phys_mid = fdt32_to_cpu(cell[1]); | ||||
| 				addr->phys_lo = fdt32_to_cpu(cell[1]); | ||||
| 				break; | ||||
| 			} else { | ||||
| 				cell += (FDT_PCI_ADDR_CELLS + | ||||
| 					 FDT_PCI_SIZE_CELLS); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (i == num) { | ||||
| 			ret = -ENXIO; | ||||
| 			goto fail; | ||||
| 		} | ||||
|  | ||||
| 		return 0; | ||||
| 	} else { | ||||
| 		ret = -EINVAL; | ||||
| 	} | ||||
|  | ||||
| fail: | ||||
| 	debug("(not found)\n"); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int ofnode_read_addr_cells(ofnode node) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) | ||||
| 		return of_n_addr_cells(ofnode_to_np(node)); | ||||
| 	else | ||||
| 		return fdt_address_cells(gd->fdt_blob, ofnode_to_offset(node)); | ||||
| } | ||||
|  | ||||
| int ofnode_read_size_cells(ofnode node) | ||||
| { | ||||
| 	if (ofnode_is_np(node)) | ||||
| 		return of_n_size_cells(ofnode_to_np(node)); | ||||
| 	else | ||||
| 		return fdt_size_cells(gd->fdt_blob, ofnode_to_offset(node)); | ||||
| } | ||||
|  | ||||
| bool ofnode_pre_reloc(ofnode node) | ||||
| { | ||||
| 	if (ofnode_read_prop(node, "u-boot,dm-pre-reloc", NULL)) | ||||
| 		return true; | ||||
|  | ||||
| #ifdef CONFIG_TPL_BUILD | ||||
| 	if (ofnode_read_prop(node, "u-boot,dm-tpl", NULL)) | ||||
| 		return true; | ||||
| #elif defined(CONFIG_SPL_BUILD) | ||||
| 	if (ofnode_read_prop(node, "u-boot,dm-spl", NULL)) | ||||
| 		return true; | ||||
| #else | ||||
| 	/* | ||||
| 	 * In regular builds individual spl and tpl handling both | ||||
| 	 * count as handled pre-relocation for later second init. | ||||
| 	 */ | ||||
| 	if (ofnode_read_prop(node, "u-boot,dm-spl", NULL) || | ||||
| 	    ofnode_read_prop(node, "u-boot,dm-tpl", NULL)) | ||||
| 		return true; | ||||
| #endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
| @@ -8,6 +8,13 @@ | ||||
| #ifndef _DM_OFNODE_H | ||||
| #define _DM_OFNODE_H | ||||
|  | ||||
| /* TODO(sjg@chromium.org): Drop fdtdec.h include */ | ||||
| #include <fdtdec.h> | ||||
| #include <dm/of.h> | ||||
|  | ||||
| /* Enable checks to protect against invalid calls */ | ||||
| #undef OF_CHECKS | ||||
|  | ||||
| /** | ||||
|  * ofnode - reference to a device tree node | ||||
|  * | ||||
| @@ -20,7 +27,7 @@ | ||||
|  * ofnode and either an offset or a struct device_node *. | ||||
|  * | ||||
|  * The reference can also hold a null offset, in which case the pointer value | ||||
|  * here is (void *)-1. This corresponds to a struct device_node * value of | ||||
|  * here is NULL. This corresponds to a struct device_node * value of | ||||
|  * NULL, or an offset of -1. | ||||
|  * | ||||
|  * There is no ambiguity as to whether ofnode holds an offset or a node | ||||
| @@ -44,6 +51,29 @@ typedef union ofnode_union { | ||||
| 	long of_offset; | ||||
| } ofnode; | ||||
|  | ||||
| struct ofnode_phandle_args { | ||||
| 	ofnode node; | ||||
| 	int args_count; | ||||
| 	uint32_t args[OF_MAX_PHANDLE_ARGS]; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * _ofnode_to_np() - convert an ofnode to a live DT node pointer | ||||
|  * | ||||
|  * This cannot be called if the reference contains an offset. | ||||
|  * | ||||
|  * @node: Reference containing struct device_node * (possibly invalid) | ||||
|  * @return pointer to device node (can be NULL) | ||||
|  */ | ||||
| static inline const struct device_node *ofnode_to_np(ofnode node) | ||||
| { | ||||
| #ifdef OF_CHECKS | ||||
| 	if (!of_live_active()) | ||||
| 		return NULL; | ||||
| #endif | ||||
| 	return node.np; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ofnode_to_offset() - convert an ofnode to a flat DT offset | ||||
|  * | ||||
| @@ -54,6 +84,10 @@ typedef union ofnode_union { | ||||
|  */ | ||||
| static inline int ofnode_to_offset(ofnode node) | ||||
| { | ||||
| #ifdef OF_CHECKS | ||||
| 	if (of_live_active()) | ||||
| 		return -1; | ||||
| #endif | ||||
| 	return node.of_offset; | ||||
| } | ||||
|  | ||||
| @@ -64,7 +98,10 @@ static inline int ofnode_to_offset(ofnode node) | ||||
|  */ | ||||
| static inline bool ofnode_valid(ofnode node) | ||||
| { | ||||
| 	return node.of_offset != -1; | ||||
| 	if (of_live_active()) | ||||
| 		return node.np != NULL; | ||||
| 	else | ||||
| 		return node.of_offset != -1; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -77,11 +114,54 @@ static inline ofnode offset_to_ofnode(int of_offset) | ||||
| { | ||||
| 	ofnode node; | ||||
|  | ||||
| 	node.of_offset = of_offset; | ||||
| 	if (of_live_active()) | ||||
| 		node.np = NULL; | ||||
| 	else | ||||
| 		node.of_offset = of_offset; | ||||
|  | ||||
| 	return node; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * np_to_ofnode() - convert a node pointer to an ofnode | ||||
|  * | ||||
|  * @np: Live node pointer (can be NULL) | ||||
|  * @return reference to the associated node pointer | ||||
|  */ | ||||
| static inline ofnode np_to_ofnode(const struct device_node *np) | ||||
| { | ||||
| 	ofnode node; | ||||
|  | ||||
| 	node.np = np; | ||||
|  | ||||
| 	return node; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ofnode_is_np() - check if a reference is a node pointer | ||||
|  * | ||||
|  * This function associated that if there is a valid live tree then all | ||||
|  * references will use it. This is because using the flat DT when the live tree | ||||
|  * is valid is not permitted. | ||||
|  * | ||||
|  * @node: reference to check (possibly invalid) | ||||
|  * @return true if the reference is a live node pointer, false if it is a DT | ||||
|  * offset | ||||
|  */ | ||||
| static inline bool ofnode_is_np(ofnode node) | ||||
| { | ||||
| #ifdef OF_CHECKS | ||||
| 	/* | ||||
| 	 * Check our assumption that flat tree offsets are not used when a | ||||
| 	 * live tree is in use. | ||||
| 	 */ | ||||
| 	assert(!ofnode_valid(node) || | ||||
| 	       (of_live_active() ? _ofnode_to_np(node) | ||||
| 				  : _ofnode_to_np(node))); | ||||
| #endif | ||||
| 	return of_live_active() && ofnode_valid(node); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ofnode_equal() - check if two references are equal | ||||
|  * | ||||
| @@ -93,4 +173,385 @@ static inline bool ofnode_equal(ofnode ref1, ofnode ref2) | ||||
| 	return ref1.of_offset == ref2.of_offset; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ofnode_null() - Obtain a null ofnode | ||||
|  * | ||||
|  * This returns an ofnode which points to no node. It works both with the flat | ||||
|  * tree and livetree. | ||||
|  */ | ||||
| static inline ofnode ofnode_null(void) | ||||
| { | ||||
| 	ofnode node; | ||||
|  | ||||
| 	if (of_live_active()) | ||||
| 		node.np = NULL; | ||||
| 	else | ||||
| 		node.of_offset = -1; | ||||
|  | ||||
| 	return node; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_u32() - Read a 32-bit integer from a property | ||||
|  * | ||||
|  * @ref:	valid node reference to read property from | ||||
|  * @propname:	name of the property to read from | ||||
|  * @outp:	place to put value (if found) | ||||
|  * @return 0 if OK, -ve on error | ||||
|  */ | ||||
| int ofnode_read_u32(ofnode node, const char *propname, u32 *outp); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_s32() - Read a 32-bit integer from a property | ||||
|  * | ||||
|  * @ref:	valid node reference to read property from | ||||
|  * @propname:	name of the property to read from | ||||
|  * @outp:	place to put value (if found) | ||||
|  * @return 0 if OK, -ve on error | ||||
|  */ | ||||
| static inline int ofnode_read_s32(ofnode node, const char *propname, | ||||
| 				  s32 *out_value) | ||||
| { | ||||
| 	return ofnode_read_u32(node, propname, (u32 *)out_value); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_u32_default() - Read a 32-bit integer from a property | ||||
|  * | ||||
|  * @ref:	valid node reference to read property from | ||||
|  * @propname:	name of the property to read from | ||||
|  * @def:	default value to return if the property has no value | ||||
|  * @return property value, or @def if not found | ||||
|  */ | ||||
| int ofnode_read_u32_default(ofnode ref, const char *propname, u32 def); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_s32_default() - Read a 32-bit integer from a property | ||||
|  * | ||||
|  * @ref:	valid node reference to read property from | ||||
|  * @propname:	name of the property to read from | ||||
|  * @def:	default value to return if the property has no value | ||||
|  * @return property value, or @def if not found | ||||
|  */ | ||||
| int ofnode_read_s32_default(ofnode node, const char *propname, s32 def); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_string() - Read a string from a property | ||||
|  * | ||||
|  * @ref:	valid node reference to read property from | ||||
|  * @propname:	name of the property to read | ||||
|  * @return string from property value, or NULL if there is no such property | ||||
|  */ | ||||
| const char *ofnode_read_string(ofnode node, const char *propname); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_u32_array - Find and read an array of 32 bit integers | ||||
|  * | ||||
|  * @node:	valid node reference to read property from | ||||
|  * @propname:	name of the property to read | ||||
|  * @out_values:	pointer to return value, modified only if return value is 0 | ||||
|  * @sz:		number of array elements to read | ||||
|  * | ||||
|  * Search for a property in a device node and read 32-bit value(s) from | ||||
|  * it. Returns 0 on success, -EINVAL if the property does not exist, | ||||
|  * -ENODATA if property does not have a value, and -EOVERFLOW if the | ||||
|  * property data isn't large enough. | ||||
|  * | ||||
|  * The out_values is modified only if a valid u32 value can be decoded. | ||||
|  */ | ||||
| int ofnode_read_u32_array(ofnode node, const char *propname, | ||||
| 			  u32 *out_values, size_t sz); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_bool() - read a boolean value from a property | ||||
|  * | ||||
|  * @node:	valid node reference to read property from | ||||
|  * @propname:	name of property to read | ||||
|  * @return true if property is present (meaning true), false if not present | ||||
|  */ | ||||
| bool ofnode_read_bool(ofnode node, const char *propname); | ||||
|  | ||||
| /** | ||||
|  * ofnode_find_subnode() - find a named subnode of a parent node | ||||
|  * | ||||
|  * @node:	valid reference to parent node | ||||
|  * @subnode_name: name of subnode to find | ||||
|  * @return reference to subnode (which can be invalid if there is no such | ||||
|  * subnode) | ||||
|  */ | ||||
| ofnode ofnode_find_subnode(ofnode node, const char *subnode_name); | ||||
|  | ||||
| /** | ||||
|  * ofnode_first_subnode() - find the first subnode of a parent node | ||||
|  * | ||||
|  * @node:	valid reference to a valid parent node | ||||
|  * @return reference to the first subnode (which can be invalid if the parent | ||||
|  * node has no subnodes) | ||||
|  */ | ||||
| ofnode ofnode_first_subnode(ofnode node); | ||||
|  | ||||
| /** | ||||
|  * ofnode_next_subnode() - find the next sibling of a subnode | ||||
|  * | ||||
|  * @node:	valid reference to previous node (sibling) | ||||
|  * @return reference to the next subnode (which can be invalid if the node | ||||
|  * has no more siblings) | ||||
|  */ | ||||
| ofnode ofnode_next_subnode(ofnode node); | ||||
|  | ||||
| /** | ||||
|  * ofnode_get_name() - get the name of a node | ||||
|  * | ||||
|  * @node: valid node to look up | ||||
|  * @return name or node | ||||
|  */ | ||||
| const char *ofnode_get_name(ofnode node); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_size() - read the size of a property | ||||
|  * | ||||
|  * @node: node to check | ||||
|  * @propname: property to check | ||||
|  * @return size of property if present, or -EINVAL if not | ||||
|  */ | ||||
| int ofnode_read_size(ofnode node, const char *propname); | ||||
|  | ||||
| /** | ||||
|  * ofnode_stringlist_search() - find a string in a string list and return index | ||||
|  * | ||||
|  * Note that it is possible for this function to succeed on property values | ||||
|  * that are not NUL-terminated. That's because the function will stop after | ||||
|  * finding the first occurrence of @string. This can for example happen with | ||||
|  * small-valued cell properties, such as #address-cells, when searching for | ||||
|  * the empty string. | ||||
|  * | ||||
|  * @node: node to check | ||||
|  * @propname: name of the property containing the string list | ||||
|  * @string: string to look up in the string list | ||||
|  * | ||||
|  * @return: | ||||
|  *   the index of the string in the list of strings | ||||
|  *   -ENODATA if the property is not found | ||||
|  *   -EINVAL on some other error | ||||
|  */ | ||||
| int ofnode_stringlist_search(ofnode node, const char *propname, | ||||
| 			     const char *string); | ||||
|  | ||||
| /** | ||||
|  * fdt_stringlist_get() - obtain the string at a given index in a string list | ||||
|  * | ||||
|  * Note that this will successfully extract strings from properties with | ||||
|  * non-NUL-terminated values. For example on small-valued cell properties | ||||
|  * this function will return the empty string. | ||||
|  * | ||||
|  * If non-NULL, the length of the string (on success) or a negative error-code | ||||
|  * (on failure) will be stored in the integer pointer to by lenp. | ||||
|  * | ||||
|  * @node: node to check | ||||
|  * @propname: name of the property containing the string list | ||||
|  * @index: index of the string to return | ||||
|  * @lenp: return location for the string length or an error code on failure | ||||
|  * | ||||
|  * @return: | ||||
|  *   length of string, if found or -ve error value if not found | ||||
|  */ | ||||
| int ofnode_read_string_index(ofnode node, const char *propname, int index, | ||||
| 			     const char **outp); | ||||
|  | ||||
| /** | ||||
|  * ofnode_parse_phandle_with_args() - Find a node pointed by phandle in a list | ||||
|  * | ||||
|  * This function is useful to parse lists of phandles and their arguments. | ||||
|  * Returns 0 on success and fills out_args, on error returns appropriate | ||||
|  * errno value. | ||||
|  * | ||||
|  * Caller is responsible to call of_node_put() on the returned out_args->np | ||||
|  * pointer. | ||||
|  * | ||||
|  * Example: | ||||
|  * | ||||
|  * phandle1: node1 { | ||||
|  *	#list-cells = <2>; | ||||
|  * } | ||||
|  * | ||||
|  * phandle2: node2 { | ||||
|  *	#list-cells = <1>; | ||||
|  * } | ||||
|  * | ||||
|  * node3 { | ||||
|  *	list = <&phandle1 1 2 &phandle2 3>; | ||||
|  * } | ||||
|  * | ||||
|  * To get a device_node of the `node2' node you may call this: | ||||
|  * ofnode_parse_phandle_with_args(node3, "list", "#list-cells", 0, 1, &args); | ||||
|  * | ||||
|  * @node:	device tree node containing a list | ||||
|  * @list_name:	property name that contains a list | ||||
|  * @cells_name:	property name that specifies phandles' arguments count | ||||
|  * @cells_count: Cell count to use if @cells_name is NULL | ||||
|  * @index:	index of a phandle to parse out | ||||
|  * @out_args:	optional pointer to output arguments structure (will be filled) | ||||
|  * @return 0 on success (with @out_args filled out if not NULL), -ENOENT if | ||||
|  *	@list_name does not exist, -EINVAL if a phandle was not found, | ||||
|  *	@cells_name could not be found, the arguments were truncated or there | ||||
|  *	were too many arguments. | ||||
|  */ | ||||
| int ofnode_parse_phandle_with_args(ofnode node, const char *list_name, | ||||
| 				   const char *cells_name, int cell_count, | ||||
| 				   int index, | ||||
| 				   struct ofnode_phandle_args *out_args); | ||||
|  | ||||
| /** | ||||
|  * ofnode_path() - find a node by full path | ||||
|  * | ||||
|  * @path: Full path to node, e.g. "/bus/spi@1" | ||||
|  * @return reference to the node found. Use ofnode_valid() to check if it exists | ||||
|  */ | ||||
| ofnode ofnode_path(const char *path); | ||||
|  | ||||
| /** | ||||
|  * ofnode_get_chosen_prop() - get the value of a chosen property | ||||
|  * | ||||
|  * This looks for a property within the /chosen node and returns its value | ||||
|  * | ||||
|  * @propname: Property name to look for | ||||
|  */ | ||||
| const char *ofnode_get_chosen_prop(const char *propname); | ||||
|  | ||||
| /** | ||||
|  * ofnode_get_chosen_node() - get the chosen node | ||||
|  * | ||||
|  * @return the chosen node if present, else ofnode_null() | ||||
|  */ | ||||
| ofnode ofnode_get_chosen_node(const char *name); | ||||
|  | ||||
| struct display_timing; | ||||
| /** | ||||
|  * ofnode_decode_display_timing() - decode display timings | ||||
|  * | ||||
|  * Decode display timings from the supplied 'display-timings' node. | ||||
|  * See doc/device-tree-bindings/video/display-timing.txt for binding | ||||
|  * information. | ||||
|  * | ||||
|  * @node	'display-timing' node containing the timing subnodes | ||||
|  * @index	Index number to read (0=first timing subnode) | ||||
|  * @config	Place to put timings | ||||
|  * @return 0 if OK, -FDT_ERR_NOTFOUND if not found | ||||
|  */ | ||||
| int ofnode_decode_display_timing(ofnode node, int index, | ||||
| 				 struct display_timing *config); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_prop()- - read a node property | ||||
|  * | ||||
|  * @node: node to read | ||||
|  * @propname: property to read | ||||
|  * @lenp: place to put length on success | ||||
|  * @return pointer to property, or NULL if not found | ||||
|  */ | ||||
| const u32 *ofnode_read_prop(ofnode node, const char *propname, int *lenp); | ||||
|  | ||||
| /** | ||||
|  * ofnode_is_available() - check if a node is marked available | ||||
|  * | ||||
|  * @node: node to check | ||||
|  * @return true if node's 'status' property is "okay" (or is missing) | ||||
|  */ | ||||
| bool ofnode_is_available(ofnode node); | ||||
|  | ||||
| /** | ||||
|  * ofnode_get_addr_size() - get address and size from a property | ||||
|  * | ||||
|  * This does no address translation. It simply reads an property that contains | ||||
|  * an address and a size value, one after the other. | ||||
|  * | ||||
|  * @node: node to read from | ||||
|  * @propname: property to read | ||||
|  * @sizep: place to put size value (on success) | ||||
|  * @return address value, or FDT_ADDR_T_NONE on error | ||||
|  */ | ||||
| phys_addr_t ofnode_get_addr_size(ofnode node, const char *propname, | ||||
| 				 phys_size_t *sizep); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_u8_array_ptr() - find an 8-bit array | ||||
|  * | ||||
|  * Look up a property in a node and return a pointer to its contents as a | ||||
|  * byte array of given length. The property must have at least enough data | ||||
|  * for the array (count bytes). It may have more, but this will be ignored. | ||||
|  * The data is not copied. | ||||
|  * | ||||
|  * @node	node to examine | ||||
|  * @propname	name of property to find | ||||
|  * @sz		number of array elements | ||||
|  * @return pointer to byte array if found, or NULL if the property is not | ||||
|  *		found or there is not enough data | ||||
|  */ | ||||
| const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname, | ||||
| 					size_t sz); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_pci_addr() - look up a PCI address | ||||
|  * | ||||
|  * Look at an address property in a node and return the PCI address which | ||||
|  * corresponds to the given type in the form of fdt_pci_addr. | ||||
|  * The property must hold one fdt_pci_addr with a lengh. | ||||
|  * | ||||
|  * @node	node to examine | ||||
|  * @type	pci address type (FDT_PCI_SPACE_xxx) | ||||
|  * @propname	name of property to find | ||||
|  * @addr	returns pci address in the form of fdt_pci_addr | ||||
|  * @return 0 if ok, -ENOENT if the property did not exist, -EINVAL if the | ||||
|  *		format of the property was invalid, -ENXIO if the requested | ||||
|  *		address type was not found | ||||
|  */ | ||||
| int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type, | ||||
| 			 const char *propname, struct fdt_pci_addr *addr); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_addr_cells() - Get the number of address cells for a node | ||||
|  * | ||||
|  * This walks back up the tree to find the closest #address-cells property | ||||
|  * which controls the given node. | ||||
|  * | ||||
|  * @node: Node to check | ||||
|  * @return number of address cells this node uses | ||||
|  */ | ||||
| int ofnode_read_addr_cells(ofnode node); | ||||
|  | ||||
| /** | ||||
|  * ofnode_read_size_cells() - Get the number of size cells for a node | ||||
|  * | ||||
|  * This walks back up the tree to find the closest #size-cells property | ||||
|  * which controls the given node. | ||||
|  * | ||||
|  * @node: Node to check | ||||
|  * @return number of size cells this node uses | ||||
|  */ | ||||
| int ofnode_read_size_cells(ofnode node); | ||||
|  | ||||
| /** | ||||
|  * ofnode_pre_reloc() - check if a node should be bound before relocation | ||||
|  * | ||||
|  * Device tree nodes can be marked as needing-to-be-bound in the loader stages | ||||
|  * via special device tree properties. | ||||
|  * | ||||
|  * Before relocation this function can be used to check if nodes are required | ||||
|  * in either SPL or TPL stages. | ||||
|  * | ||||
|  * After relocation and jumping into the real U-Boot binary it is possible to | ||||
|  * determine if a node was bound in one of SPL/TPL stages. | ||||
|  * | ||||
|  * There are 3 settings currently in use | ||||
|  * - | ||||
|  * - u-boot,dm-pre-reloc: legacy and indicates any of TPL or SPL | ||||
|  *   Existing platforms only use it to indicate nodes needed in | ||||
|  *   SPL. Should probably be replaced by u-boot,dm-spl for | ||||
|  *   new platforms. | ||||
|  * | ||||
|  * @node: node to check | ||||
|  * @eturns true if node is needed in SPL/TL, false otherwise | ||||
|  */ | ||||
| bool ofnode_pre_reloc(ofnode node); | ||||
|  | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user