fdtdec: Implement fdtdec_add_reserved_memory()
This function can be used to add subnodes in the /reserved-memory node. Reviewed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
		
							parent
							
								
									8153d53b93
								
							
						
					
					
						commit
						c9222a08b3
					
				|  | @ -0,0 +1,136 @@ | ||||||
|  | *** Reserved memory regions *** | ||||||
|  | 
 | ||||||
|  | Reserved memory is specified as a node under the /reserved-memory node. | ||||||
|  | The operating system shall exclude reserved memory from normal usage | ||||||
|  | one can create child nodes describing particular reserved (excluded from | ||||||
|  | normal use) memory regions. Such memory regions are usually designed for | ||||||
|  | the special usage by various device drivers. | ||||||
|  | 
 | ||||||
|  | Parameters for each memory region can be encoded into the device tree | ||||||
|  | with the following nodes: | ||||||
|  | 
 | ||||||
|  | /reserved-memory node | ||||||
|  | --------------------- | ||||||
|  | #address-cells, #size-cells (required) - standard definition | ||||||
|  |     - Should use the same values as the root node | ||||||
|  | ranges (required) - standard definition | ||||||
|  |     - Should be empty | ||||||
|  | 
 | ||||||
|  | /reserved-memory/ child nodes | ||||||
|  | ----------------------------- | ||||||
|  | Each child of the reserved-memory node specifies one or more regions of | ||||||
|  | reserved memory. Each child node may either use a 'reg' property to | ||||||
|  | specify a specific range of reserved memory, or a 'size' property with | ||||||
|  | optional constraints to request a dynamically allocated block of memory. | ||||||
|  | 
 | ||||||
|  | Following the generic-names recommended practice, node names should | ||||||
|  | reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit | ||||||
|  | address (@<address>) should be appended to the name if the node is a | ||||||
|  | static allocation. | ||||||
|  | 
 | ||||||
|  | Properties: | ||||||
|  | Requires either a) or b) below. | ||||||
|  | a) static allocation | ||||||
|  |    reg (required) - standard definition | ||||||
|  | b) dynamic allocation | ||||||
|  |    size (required) - length based on parent's #size-cells | ||||||
|  |                    - Size in bytes of memory to reserve. | ||||||
|  |    alignment (optional) - length based on parent's #size-cells | ||||||
|  |                         - Address boundary for alignment of allocation. | ||||||
|  |    alloc-ranges (optional) - prop-encoded-array (address, length pairs). | ||||||
|  |                            - Specifies regions of memory that are | ||||||
|  |                              acceptable to allocate from. | ||||||
|  | 
 | ||||||
|  | If both reg and size are present, then the reg property takes precedence | ||||||
|  | and size is ignored. | ||||||
|  | 
 | ||||||
|  | Additional properties: | ||||||
|  | compatible (optional) - standard definition | ||||||
|  |     - may contain the following strings: | ||||||
|  |         - shared-dma-pool: This indicates a region of memory meant to be | ||||||
|  |           used as a shared pool of DMA buffers for a set of devices. It can | ||||||
|  |           be used by an operating system to instantiate the necessary pool | ||||||
|  |           management subsystem if necessary. | ||||||
|  |         - vendor specific string in the form <vendor>,[<device>-]<usage> | ||||||
|  | no-map (optional) - empty property | ||||||
|  |     - Indicates the operating system must not create a virtual mapping | ||||||
|  |       of the region as part of its standard mapping of system memory, | ||||||
|  |       nor permit speculative access to it under any circumstances other | ||||||
|  |       than under the control of the device driver using the region. | ||||||
|  | reusable (optional) - empty property | ||||||
|  |     - The operating system can use the memory in this region with the | ||||||
|  |       limitation that the device driver(s) owning the region need to be | ||||||
|  |       able to reclaim it back. Typically that means that the operating | ||||||
|  |       system can use that region to store volatile or cached data that | ||||||
|  |       can be otherwise regenerated or migrated elsewhere. | ||||||
|  | 
 | ||||||
|  | Linux implementation note: | ||||||
|  | - If a "linux,cma-default" property is present, then Linux will use the | ||||||
|  |   region for the default pool of the contiguous memory allocator. | ||||||
|  | 
 | ||||||
|  | - If a "linux,dma-default" property is present, then Linux will use the | ||||||
|  |   region for the default pool of the consistent DMA allocator. | ||||||
|  | 
 | ||||||
|  | Device node references to reserved memory | ||||||
|  | ----------------------------------------- | ||||||
|  | Regions in the /reserved-memory node may be referenced by other device | ||||||
|  | nodes by adding a memory-region property to the device node. | ||||||
|  | 
 | ||||||
|  | memory-region (optional) - phandle, specifier pairs to children of /reserved-memory | ||||||
|  | 
 | ||||||
|  | Example | ||||||
|  | ------- | ||||||
|  | This example defines 3 contiguous regions are defined for Linux kernel: | ||||||
|  | one default of all device drivers (named linux,cma@72000000 and 64MiB in size), | ||||||
|  | one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and | ||||||
|  | one for multimedia processing (named multimedia-memory@77000000, 64MiB). | ||||||
|  | 
 | ||||||
|  | / { | ||||||
|  | 	#address-cells = <1>; | ||||||
|  | 	#size-cells = <1>; | ||||||
|  | 
 | ||||||
|  | 	memory { | ||||||
|  | 		reg = <0x40000000 0x40000000>; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	reserved-memory { | ||||||
|  | 		#address-cells = <1>; | ||||||
|  | 		#size-cells = <1>; | ||||||
|  | 		ranges; | ||||||
|  | 
 | ||||||
|  | 		/* global autoconfigured region for contiguous allocations */ | ||||||
|  | 		linux,cma { | ||||||
|  | 			compatible = "shared-dma-pool"; | ||||||
|  | 			reusable; | ||||||
|  | 			size = <0x4000000>; | ||||||
|  | 			alignment = <0x2000>; | ||||||
|  | 			linux,cma-default; | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		display_reserved: framebuffer@78000000 { | ||||||
|  | 			reg = <0x78000000 0x800000>; | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		multimedia_reserved: multimedia@77000000 { | ||||||
|  | 			compatible = "acme,multimedia-memory"; | ||||||
|  | 			reg = <0x77000000 0x4000000>; | ||||||
|  | 		}; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	/* ... */ | ||||||
|  | 
 | ||||||
|  | 	fb0: video@12300000 { | ||||||
|  | 		memory-region = <&display_reserved>; | ||||||
|  | 		/* ... */ | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	scaler: scaler@12500000 { | ||||||
|  | 		memory-region = <&multimedia_reserved>; | ||||||
|  | 		/* ... */ | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	codec: codec@12600000 { | ||||||
|  | 		memory-region = <&multimedia_reserved>; | ||||||
|  | 		/* ... */ | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | @ -1031,6 +1031,54 @@ int fdtdec_setup_memory_banksize(void); | ||||||
|  */ |  */ | ||||||
| int fdtdec_set_phandle(void *blob, int node, uint32_t phandle); | int fdtdec_set_phandle(void *blob, int node, uint32_t phandle); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * fdtdec_add_reserved_memory() - add or find a reserved-memory node | ||||||
|  |  * | ||||||
|  |  * If a reserved-memory node already exists for the given carveout, a phandle | ||||||
|  |  * for that node will be returned. Otherwise a new node will be created and a | ||||||
|  |  * phandle corresponding to it will be returned. | ||||||
|  |  * | ||||||
|  |  * See Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt | ||||||
|  |  * for details on how to use reserved memory regions. | ||||||
|  |  * | ||||||
|  |  * As an example, consider the following code snippet: | ||||||
|  |  * | ||||||
|  |  *     struct fdt_memory fb = { | ||||||
|  |  *         .start = 0x92cb3000, | ||||||
|  |  *         .end = 0x934b2fff, | ||||||
|  |  *     }; | ||||||
|  |  *     uint32_t phandle; | ||||||
|  |  * | ||||||
|  |  *     fdtdec_add_reserved_memory(fdt, "framebuffer", &fb, &phandle); | ||||||
|  |  * | ||||||
|  |  * This results in the following subnode being added to the top-level | ||||||
|  |  * /reserved-memory node: | ||||||
|  |  * | ||||||
|  |  *     reserved-memory { | ||||||
|  |  *         #address-cells = <0x00000002>; | ||||||
|  |  *         #size-cells = <0x00000002>; | ||||||
|  |  *         ranges; | ||||||
|  |  * | ||||||
|  |  *         framebuffer@92cb3000 { | ||||||
|  |  *             reg = <0x00000000 0x92cb3000 0x00000000 0x00800000>; | ||||||
|  |  *             phandle = <0x0000004d>; | ||||||
|  |  *         }; | ||||||
|  |  *     }; | ||||||
|  |  * | ||||||
|  |  * If the top-level /reserved-memory node does not exist, it will be created. | ||||||
|  |  * The phandle returned from the function call can be used to reference this | ||||||
|  |  * reserved memory region from other nodes. | ||||||
|  |  * | ||||||
|  |  * @param blob		FDT blob | ||||||
|  |  * @param basename	base name of the node to create | ||||||
|  |  * @param carveout	information about the carveout region | ||||||
|  |  * @param phandlep	return location for the phandle of the carveout region | ||||||
|  |  * @return 0 on success or a negative error code on failure | ||||||
|  |  */ | ||||||
|  | int fdtdec_add_reserved_memory(void *blob, const char *basename, | ||||||
|  | 			       const struct fdt_memory *carveout, | ||||||
|  | 			       uint32_t *phandlep); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Set up the device tree ready for use |  * Set up the device tree ready for use | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
							
								
								
									
										131
									
								
								lib/fdtdec.c
								
								
								
								
							
							
						
						
									
										131
									
								
								lib/fdtdec.c
								
								
								
								
							|  | @ -1268,6 +1268,137 @@ int fdtdec_set_phandle(void *blob, int node, uint32_t phandle) | ||||||
| 	return fdt_setprop(blob, node, "phandle", &value, sizeof(value)); | 	return fdt_setprop(blob, node, "phandle", &value, sizeof(value)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int fdtdec_init_reserved_memory(void *blob) | ||||||
|  | { | ||||||
|  | 	int na, ns, node, err; | ||||||
|  | 	fdt32_t value; | ||||||
|  | 
 | ||||||
|  | 	/* inherit #address-cells and #size-cells from the root node */ | ||||||
|  | 	na = fdt_address_cells(blob, 0); | ||||||
|  | 	ns = fdt_size_cells(blob, 0); | ||||||
|  | 
 | ||||||
|  | 	node = fdt_add_subnode(blob, 0, "reserved-memory"); | ||||||
|  | 	if (node < 0) | ||||||
|  | 		return node; | ||||||
|  | 
 | ||||||
|  | 	err = fdt_setprop(blob, node, "ranges", NULL, 0); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	value = cpu_to_fdt32(ns); | ||||||
|  | 
 | ||||||
|  | 	err = fdt_setprop(blob, node, "#size-cells", &value, sizeof(value)); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	value = cpu_to_fdt32(na); | ||||||
|  | 
 | ||||||
|  | 	err = fdt_setprop(blob, node, "#address-cells", &value, sizeof(value)); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	return node; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int fdtdec_add_reserved_memory(void *blob, const char *basename, | ||||||
|  | 			       const struct fdt_memory *carveout, | ||||||
|  | 			       uint32_t *phandlep) | ||||||
|  | { | ||||||
|  | 	fdt32_t cells[4] = {}, *ptr = cells; | ||||||
|  | 	uint32_t upper, lower, phandle; | ||||||
|  | 	int parent, node, na, ns, err; | ||||||
|  | 	char name[64]; | ||||||
|  | 
 | ||||||
|  | 	/* create an empty /reserved-memory node if one doesn't exist */ | ||||||
|  | 	parent = fdt_path_offset(blob, "/reserved-memory"); | ||||||
|  | 	if (parent < 0) { | ||||||
|  | 		parent = fdtdec_init_reserved_memory(blob); | ||||||
|  | 		if (parent < 0) | ||||||
|  | 			return parent; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* only 1 or 2 #address-cells and #size-cells are supported */ | ||||||
|  | 	na = fdt_address_cells(blob, parent); | ||||||
|  | 	if (na < 1 || na > 2) | ||||||
|  | 		return -FDT_ERR_BADNCELLS; | ||||||
|  | 
 | ||||||
|  | 	ns = fdt_size_cells(blob, parent); | ||||||
|  | 	if (ns < 1 || ns > 2) | ||||||
|  | 		return -FDT_ERR_BADNCELLS; | ||||||
|  | 
 | ||||||
|  | 	/* find a matching node and return the phandle to that */ | ||||||
|  | 	fdt_for_each_subnode(node, blob, parent) { | ||||||
|  | 		const char *name = fdt_get_name(blob, node, NULL); | ||||||
|  | 		phys_addr_t addr, size; | ||||||
|  | 
 | ||||||
|  | 		addr = fdtdec_get_addr_size(blob, node, "reg", &size); | ||||||
|  | 		if (addr == FDT_ADDR_T_NONE) { | ||||||
|  | 			debug("failed to read address/size for %s\n", name); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (addr == carveout->start && (addr + size) == carveout->end) { | ||||||
|  | 			*phandlep = fdt_get_phandle(blob, node); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Unpack the start address and generate the name of the new node | ||||||
|  | 	 * base on the basename and the unit-address. | ||||||
|  | 	 */ | ||||||
|  | 	lower = fdt_addr_unpack(carveout->start, &upper); | ||||||
|  | 
 | ||||||
|  | 	if (na > 1 && upper > 0) | ||||||
|  | 		snprintf(name, sizeof(name), "%s@%x,%x", basename, upper, | ||||||
|  | 			 lower); | ||||||
|  | 	else { | ||||||
|  | 		if (upper > 0) { | ||||||
|  | 			debug("address %08x:%08x exceeds addressable space\n", | ||||||
|  | 			      upper, lower); | ||||||
|  | 			return -FDT_ERR_BADVALUE; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		snprintf(name, sizeof(name), "%s@%x", basename, lower); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	node = fdt_add_subnode(blob, parent, name); | ||||||
|  | 	if (node < 0) | ||||||
|  | 		return node; | ||||||
|  | 
 | ||||||
|  | 	err = fdt_generate_phandle(blob, &phandle); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	err = fdtdec_set_phandle(blob, node, phandle); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	/* store one or two address cells */ | ||||||
|  | 	if (na > 1) | ||||||
|  | 		*ptr++ = cpu_to_fdt32(upper); | ||||||
|  | 
 | ||||||
|  | 	*ptr++ = cpu_to_fdt32(lower); | ||||||
|  | 
 | ||||||
|  | 	/* store one or two size cells */ | ||||||
|  | 	lower = fdt_size_unpack(carveout->end - carveout->start + 1, &upper); | ||||||
|  | 
 | ||||||
|  | 	if (ns > 1) | ||||||
|  | 		*ptr++ = cpu_to_fdt32(upper); | ||||||
|  | 
 | ||||||
|  | 	*ptr++ = cpu_to_fdt32(lower); | ||||||
|  | 
 | ||||||
|  | 	err = fdt_setprop(blob, node, "reg", cells, (na + ns) * sizeof(*cells)); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	/* return the phandle for the new node for the caller to use */ | ||||||
|  | 	if (phandlep) | ||||||
|  | 		*phandlep = phandle; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int fdtdec_setup(void) | int fdtdec_setup(void) | ||||||
| { | { | ||||||
| #if CONFIG_IS_ENABLED(OF_CONTROL) | #if CONFIG_IS_ENABLED(OF_CONTROL) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue