fdt: add new fdt address parsing functions
fdtdec_get_addr_size() hard-codes the number of cells used to represent an address or size in DT. This is incorrect in many cases depending on the DT binding for a particular node or property (e.g. it is incorrect for the "reg" property). In most cases, DT parsing code must use the properties #address-cells and #size-cells to parse addres properties. This change splits up the implementation of fdtdec_get_addr_size() so that the core logic can be used for both hard-coded and non-hard-coded cases. Various wrapper functions are implemented that support cases where hard-coded cell counts should or should not be used, and where the client does and doesn't know the parent node ID that contains the properties #address-cells and #size-cells. dev_get_addr() is updated to use the new functions. Core functionality in fdtdec_get_addr_size_fixed() is widely tested via fdtdec_get_addr_size(). I tested fdtdec_get_addr_size_auto_noparent() and dev_get_addr() by manually modifying the Tegra I2C driver to invoke them. Much of the core implementation of fdtdec_get_addr_size_fixed(), fdtdec_get_addr_size_auto_parent(), and fdtdec_get_addr_size_auto_noparent() comes from Thierry Reding's previous commit "fdt: Fix fdtdec_get_addr_size() for 64-bit". Based-on-work-by: Thierry Reding <treding@nvidia.com> Cc: Thierry Reding <treding@nvidia.com> Cc: Simon Glass <sjg@chromium.org> Cc: Michal Suchanek <hramrach@gmail.com> Signed-off-by: Stephen Warren <swarren@nvidia.com> Acked-by: Simon Glass <sjg@chromium.org> Dropped #define DEBUG at the top of fdtdec.c: Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		
							parent
							
								
									850f788709
								
							
						
					
					
						commit
						02464e386b
					
				|  | @ -581,7 +581,10 @@ fdt_addr_t dev_get_addr(struct udevice *dev) | ||||||
| #if CONFIG_IS_ENABLED(OF_CONTROL) | #if CONFIG_IS_ENABLED(OF_CONTROL) | ||||||
| 	fdt_addr_t addr; | 	fdt_addr_t addr; | ||||||
| 
 | 
 | ||||||
| 	addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); | 	addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, | ||||||
|  | 						dev->parent->of_offset, | ||||||
|  | 						dev->of_offset, "reg", | ||||||
|  | 						0, NULL); | ||||||
| 	if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) { | 	if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) { | ||||||
| 		if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS) | 		if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS) | ||||||
| 			addr = simple_bus_translate(dev->parent, addr); | 			addr = simple_bus_translate(dev->parent, addr); | ||||||
|  |  | ||||||
							
								
								
									
										111
									
								
								include/fdtdec.h
								
								
								
								
							
							
						
						
									
										111
									
								
								include/fdtdec.h
								
								
								
								
							|  | @ -295,10 +295,90 @@ int fdtdec_next_compatible(const void *blob, int node, | ||||||
| int fdtdec_next_compatible_subnode(const void *blob, int node, | int fdtdec_next_compatible_subnode(const void *blob, int node, | ||||||
| 		enum fdt_compat_id id, int *depthp); | 		enum fdt_compat_id id, int *depthp); | ||||||
| 
 | 
 | ||||||
| /**
 | /*
 | ||||||
|  * Look up an address property in a node and return it as an address. |  * Look up an address property in a node and return the parsed address, and | ||||||
|  * The property must hold either one address with no trailing data or |  * optionally the parsed size. | ||||||
|  * one address with a length. This is only tested on 32-bit machines. |  * | ||||||
|  |  * This variant assumes a known and fixed number of cells are used to | ||||||
|  |  * represent the address and size. | ||||||
|  |  * | ||||||
|  |  * You probably don't want to use this function directly except to parse | ||||||
|  |  * non-standard properties, and never to parse the "reg" property. Instead, | ||||||
|  |  * use one of the "auto" variants below, which automatically honor the | ||||||
|  |  * #address-cells and #size-cells properties in the parent node. | ||||||
|  |  * | ||||||
|  |  * @param blob	FDT blob | ||||||
|  |  * @param node	node to examine | ||||||
|  |  * @param prop_name	name of property to find | ||||||
|  |  * @param index	which address to retrieve from a list of addresses. Often 0. | ||||||
|  |  * @param na	the number of cells used to represent an address | ||||||
|  |  * @param ns	the number of cells used to represent a size | ||||||
|  |  * @param sizep	a pointer to store the size into. Use NULL if not required | ||||||
|  |  * @return address, if found, or FDT_ADDR_T_NONE if not | ||||||
|  |  */ | ||||||
|  | fdt_addr_t fdtdec_get_addr_size_fixed(const void *blob, int node, | ||||||
|  | 		const char *prop_name, int index, int na, int ns, | ||||||
|  | 		fdt_size_t *sizep); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Look up an address property in a node and return the parsed address, and | ||||||
|  |  * optionally the parsed size. | ||||||
|  |  * | ||||||
|  |  * This variant automatically determines the number of cells used to represent | ||||||
|  |  * the address and size by parsing the provided parent node's #address-cells | ||||||
|  |  * and #size-cells properties. | ||||||
|  |  * | ||||||
|  |  * @param blob	FDT blob | ||||||
|  |  * @param parent	parent node of @node | ||||||
|  |  * @param node	node to examine | ||||||
|  |  * @param prop_name	name of property to find | ||||||
|  |  * @param index	which address to retrieve from a list of addresses. Often 0. | ||||||
|  |  * @param sizep	a pointer to store the size into. Use NULL if not required | ||||||
|  |  * @return address, if found, or FDT_ADDR_T_NONE if not | ||||||
|  |  */ | ||||||
|  | fdt_addr_t fdtdec_get_addr_size_auto_parent(const void *blob, int parent, | ||||||
|  | 		int node, const char *prop_name, int index, fdt_size_t *sizep); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Look up an address property in a node and return the parsed address, and | ||||||
|  |  * optionally the parsed size. | ||||||
|  |  * | ||||||
|  |  * This variant automatically determines the number of cells used to represent | ||||||
|  |  * the address and size by parsing the parent node's #address-cells | ||||||
|  |  * and #size-cells properties. The parent node is automatically found. | ||||||
|  |  * | ||||||
|  |  * The automatic parent lookup implemented by this function is slow. | ||||||
|  |  * Consequently, fdtdec_get_addr_size_auto_parent() should be used where | ||||||
|  |  * possible. | ||||||
|  |  * | ||||||
|  |  * @param blob	FDT blob | ||||||
|  |  * @param parent	parent node of @node | ||||||
|  |  * @param node	node to examine | ||||||
|  |  * @param prop_name	name of property to find | ||||||
|  |  * @param index	which address to retrieve from a list of addresses. Often 0. | ||||||
|  |  * @param sizep	a pointer to store the size into. Use NULL if not required | ||||||
|  |  * @return address, if found, or FDT_ADDR_T_NONE if not | ||||||
|  |  */ | ||||||
|  | fdt_addr_t fdtdec_get_addr_size_auto_noparent(const void *blob, int node, | ||||||
|  | 		const char *prop_name, int index, fdt_size_t *sizep); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Look up an address property in a node and return the parsed address. | ||||||
|  |  * | ||||||
|  |  * This variant hard-codes the number of cells used to represent the address | ||||||
|  |  * and size based on sizeof(fdt_addr_t) and sizeof(fdt_size_t). It also | ||||||
|  |  * always returns the first address value in the property (index 0). | ||||||
|  |  * | ||||||
|  |  * Use of this function is not recommended due to the hard-coding of cell | ||||||
|  |  * counts. There is no programmatic validation that these hard-coded values | ||||||
|  |  * actually match the device tree content in any way at all. This assumption | ||||||
|  |  * can be satisfied by manually ensuring CONFIG_PHYS_64BIT is appropriately | ||||||
|  |  * set in the U-Boot build and exercising strict control over DT content to | ||||||
|  |  * ensure use of matching #address-cells/#size-cells properties. However, this | ||||||
|  |  * approach is error-prone; those familiar with DT will not expect the | ||||||
|  |  * assumption to exist, and could easily invalidate it. If the assumption is | ||||||
|  |  * invalidated, this function will not report the issue, and debugging will | ||||||
|  |  * be required. Instead, use fdtdec_get_addr_size_auto_parent(). | ||||||
|  * |  * | ||||||
|  * @param blob	FDT blob |  * @param blob	FDT blob | ||||||
|  * @param node	node to examine |  * @param node	node to examine | ||||||
|  | @ -308,14 +388,29 @@ int fdtdec_next_compatible_subnode(const void *blob, int node, | ||||||
| fdt_addr_t fdtdec_get_addr(const void *blob, int node, | fdt_addr_t fdtdec_get_addr(const void *blob, int node, | ||||||
| 		const char *prop_name); | 		const char *prop_name); | ||||||
| 
 | 
 | ||||||
| /**
 | /*
 | ||||||
|  * Look up an address property in a node and return it as an address. |  * Look up an address property in a node and return the parsed address, and | ||||||
|  * The property must hold one address with a length. This is only tested |  * optionally the parsed size. | ||||||
|  * on 32-bit machines. |  * | ||||||
|  |  * This variant hard-codes the number of cells used to represent the address | ||||||
|  |  * and size based on sizeof(fdt_addr_t) and sizeof(fdt_size_t). It also | ||||||
|  |  * always returns the first address value in the property (index 0). | ||||||
|  |  * | ||||||
|  |  * Use of this function is not recommended due to the hard-coding of cell | ||||||
|  |  * counts. There is no programmatic validation that these hard-coded values | ||||||
|  |  * actually match the device tree content in any way at all. This assumption | ||||||
|  |  * can be satisfied by manually ensuring CONFIG_PHYS_64BIT is appropriately | ||||||
|  |  * set in the U-Boot build and exercising strict control over DT content to | ||||||
|  |  * ensure use of matching #address-cells/#size-cells properties. However, this | ||||||
|  |  * approach is error-prone; those familiar with DT will not expect the | ||||||
|  |  * assumption to exist, and could easily invalidate it. If the assumption is | ||||||
|  |  * invalidated, this function will not report the issue, and debugging will | ||||||
|  |  * be required. Instead, use fdtdec_get_addr_size_auto_parent(). | ||||||
|  * |  * | ||||||
|  * @param blob	FDT blob |  * @param blob	FDT blob | ||||||
|  * @param node	node to examine |  * @param node	node to examine | ||||||
|  * @param prop_name	name of property to find |  * @param prop_name	name of property to find | ||||||
|  |  * @param sizep	a pointer to store the size into. Use NULL if not required | ||||||
|  * @return address, if found, or FDT_ADDR_T_NONE if not |  * @return address, if found, or FDT_ADDR_T_NONE if not | ||||||
|  */ |  */ | ||||||
| fdt_addr_t fdtdec_get_addr_size(const void *blob, int node, | fdt_addr_t fdtdec_get_addr_size(const void *blob, int node, | ||||||
|  |  | ||||||
							
								
								
									
										118
									
								
								lib/fdtdec.c
								
								
								
								
							
							
						
						
									
										118
									
								
								lib/fdtdec.c
								
								
								
								
							|  | @ -86,32 +86,104 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id) | ||||||
| 	return compat_names[id]; | 	return compat_names[id]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fdt_addr_t fdtdec_get_addr_size_fixed(const void *blob, int node, | ||||||
|  | 		const char *prop_name, int index, int na, int ns, | ||||||
|  | 		fdt_size_t *sizep) | ||||||
|  | { | ||||||
|  | 	const fdt32_t *prop, *prop_end; | ||||||
|  | 	const fdt32_t *prop_addr, *prop_size, *prop_after_size; | ||||||
|  | 	int len; | ||||||
|  | 	fdt_addr_t addr; | ||||||
|  | 
 | ||||||
|  | 	debug("%s: %s: ", __func__, prop_name); | ||||||
|  | 
 | ||||||
|  | 	if (na > (sizeof(fdt_addr_t) / sizeof(fdt32_t))) { | ||||||
|  | 		debug("(na too large for fdt_addr_t type)\n"); | ||||||
|  | 		return FDT_ADDR_T_NONE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ns > (sizeof(fdt_size_t) / sizeof(fdt32_t))) { | ||||||
|  | 		debug("(ns too large for fdt_size_t type)\n"); | ||||||
|  | 		return FDT_ADDR_T_NONE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	prop = fdt_getprop(blob, node, prop_name, &len); | ||||||
|  | 	if (!prop) { | ||||||
|  | 		debug("(not found)\n"); | ||||||
|  | 		return FDT_ADDR_T_NONE; | ||||||
|  | 	} | ||||||
|  | 	prop_end = prop + (len / sizeof(*prop)); | ||||||
|  | 
 | ||||||
|  | 	prop_addr = prop + (index * (na + ns)); | ||||||
|  | 	prop_size = prop_addr + na; | ||||||
|  | 	prop_after_size = prop_size + ns; | ||||||
|  | 	if (prop_after_size > prop_end) { | ||||||
|  | 		debug("(not enough data: expected >= %d cells, got %d cells)\n", | ||||||
|  | 		      (u32)(prop_after_size - prop), ((u32)(prop_end - prop))); | ||||||
|  | 		return FDT_ADDR_T_NONE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	addr = fdtdec_get_number(prop_addr, na); | ||||||
|  | 
 | ||||||
|  | 	if (sizep) { | ||||||
|  | 		*sizep = fdtdec_get_number(prop_size, ns); | ||||||
|  | 		debug("addr=%08llx, size=%llx\n", (u64)addr, (u64)*sizep); | ||||||
|  | 	} else { | ||||||
|  | 		debug("addr=%08llx\n", (u64)addr); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return addr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fdt_addr_t fdtdec_get_addr_size_auto_parent(const void *blob, int parent, | ||||||
|  | 		int node, const char *prop_name, int index, fdt_size_t *sizep) | ||||||
|  | { | ||||||
|  | 	int na, ns; | ||||||
|  | 
 | ||||||
|  | 	debug("%s: ", __func__); | ||||||
|  | 
 | ||||||
|  | 	na = fdt_address_cells(blob, parent); | ||||||
|  | 	if (na < 1) { | ||||||
|  | 		debug("(bad #address-cells)\n"); | ||||||
|  | 		return FDT_ADDR_T_NONE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ns = fdt_size_cells(blob, parent); | ||||||
|  | 	if (ns < 1) { | ||||||
|  | 		debug("(bad #size-cells)\n"); | ||||||
|  | 		return FDT_ADDR_T_NONE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	debug("na=%d, ns=%d, ", na, ns); | ||||||
|  | 
 | ||||||
|  | 	return fdtdec_get_addr_size_fixed(blob, node, prop_name, index, na, | ||||||
|  | 					  ns, sizep); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fdt_addr_t fdtdec_get_addr_size_auto_noparent(const void *blob, int node, | ||||||
|  | 		const char *prop_name, int index, fdt_size_t *sizep) | ||||||
|  | { | ||||||
|  | 	int parent; | ||||||
|  | 
 | ||||||
|  | 	debug("%s: ", __func__); | ||||||
|  | 
 | ||||||
|  | 	parent = fdt_parent_offset(blob, node); | ||||||
|  | 	if (parent < 0) { | ||||||
|  | 		debug("(no parent found)\n"); | ||||||
|  | 		return FDT_ADDR_T_NONE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return fdtdec_get_addr_size_auto_parent(blob, parent, node, prop_name, | ||||||
|  | 						index, sizep); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fdt_addr_t fdtdec_get_addr_size(const void *blob, int node, | fdt_addr_t fdtdec_get_addr_size(const void *blob, int node, | ||||||
| 		const char *prop_name, fdt_size_t *sizep) | 		const char *prop_name, fdt_size_t *sizep) | ||||||
| { | { | ||||||
| 	const fdt_addr_t *cell; | 	return fdtdec_get_addr_size_fixed(blob, node, prop_name, 0, | ||||||
| 	int len; | 					  sizeof(fdt_addr_t) / sizeof(fdt32_t), | ||||||
| 
 | 					  sizeof(fdt_size_t) / sizeof(fdt32_t), | ||||||
| 	debug("%s: %s: ", __func__, prop_name); | 					  sizep); | ||||||
| 	cell = fdt_getprop(blob, node, prop_name, &len); |  | ||||||
| 	if (cell && ((!sizep && len == sizeof(fdt_addr_t)) || |  | ||||||
| 		     len == sizeof(fdt_addr_t) * 2)) { |  | ||||||
| 		fdt_addr_t addr = fdt_addr_to_cpu(*cell); |  | ||||||
| 		if (sizep) { |  | ||||||
| 			const fdt_size_t *size; |  | ||||||
| 
 |  | ||||||
| 			size = (fdt_size_t *)((char *)cell + |  | ||||||
| 					sizeof(fdt_addr_t)); |  | ||||||
| 			*sizep = fdt_size_to_cpu(*size); |  | ||||||
| 			debug("addr=%08lx, size=%llx\n", |  | ||||||
| 			      (ulong)addr, (u64)*sizep); |  | ||||||
| 		} else { |  | ||||||
| 			debug("%08lx\n", (ulong)addr); |  | ||||||
| 		} |  | ||||||
| 		return addr; |  | ||||||
| 	} |  | ||||||
| 	debug("(not found)\n"); |  | ||||||
| 	return FDT_ADDR_T_NONE; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fdt_addr_t fdtdec_get_addr(const void *blob, int node, | fdt_addr_t fdtdec_get_addr(const void *blob, int node, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue