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); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										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)); | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
| #if CONFIG_IS_ENABLED(OF_CONTROL) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue