fdt: Add device tree memory bindings
Support a default memory bank, specified in reg, as well as board-specific memory banks in subtree board-id nodes. This allows memory information to be provided in the device tree, rather than hard-coded in, which will make it simpler to handle similar devices with different memory banks, as the board-id values or masks can be used to match devices. Signed-off-by: Michael Pratt <mpratt@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
This commit is contained in:
		
							parent
							
								
									8c5d4fd0ec
								
							
						
					
					
						commit
						90c08fa038
					
				|  | @ -0,0 +1,67 @@ | ||||||
|  | * Memory binding | ||||||
|  | 
 | ||||||
|  | The memory binding for U-Boot is as in the ePAPR with the following additions: | ||||||
|  | 
 | ||||||
|  | Optional subnodes can be used defining the memory layout for different board | ||||||
|  | ID masks. To match a set of board ids, a board-id node may define match-mask | ||||||
|  | and match-value ints to define a mask to apply to the board id, and the value | ||||||
|  | that the result should have for the match to be considered valid. The mask | ||||||
|  | defaults to -1, meaning that the value must fully match the board id. | ||||||
|  | 
 | ||||||
|  | If subnodes are present, then the /memory node must define these properties: | ||||||
|  | 
 | ||||||
|  | - #address-cells: should be 1. | ||||||
|  | - #size-cells: should be 0. | ||||||
|  | 
 | ||||||
|  | Each subnode must define | ||||||
|  | 
 | ||||||
|  |  reg - board ID or mask for this subnode | ||||||
|  |  memory-banks - list of memory banks in the same format as normal | ||||||
|  | 
 | ||||||
|  | Each subnode may optionally define: | ||||||
|  | 
 | ||||||
|  |  match-mask - A mask to apply to the board id.  This must be accompanied by | ||||||
|  |               match-value. | ||||||
|  |  match-value - The required resulting value of the board id mask for the given | ||||||
|  | 	       node to be considered a match. | ||||||
|  |  auto-size - Indicates that the value given for a bank is the maximum size, | ||||||
|  | 	     each bank is probed to determine its actual size, which may be | ||||||
|  | 	     smaller | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | The board id determination is up to the vendor and is not defined by this | ||||||
|  | binding. | ||||||
|  | 
 | ||||||
|  | Example: | ||||||
|  | 
 | ||||||
|  | memory { | ||||||
|  | 	#address-cells = <1>; | ||||||
|  | 	#size-cells = <1>; | ||||||
|  | 	reg = <0x20000000 0x20000000 | ||||||
|  | 		0x40000000 0x20000000 | ||||||
|  | 		0x60000000 0x20000000 | ||||||
|  | 		0x80000000 0x20000000>; | ||||||
|  | 	auto-size; | ||||||
|  | 	board-id@0 { | ||||||
|  | 		match-value = <17>; | ||||||
|  | 		reg = <0x20000000 0x20000000 | ||||||
|  | 			0x40000000 0x20000000>; | ||||||
|  | 	}; | ||||||
|  | 	board-id@1 { | ||||||
|  | 		match-mask = <2>; | ||||||
|  | 		match-value = <2>; | ||||||
|  | 		reg = <0x20000000 0x20000000 | ||||||
|  | 			0x40000000 0x20000000 | ||||||
|  | 			0x60000000 0x20000000 | ||||||
|  | 			0x80000000 0x20000000 | ||||||
|  | 			0xa0000000 0x20000000 | ||||||
|  | 			0xc0000000 0x20000000 | ||||||
|  | 			0xe0000000 0x20000000>; | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | This shows a system with the following properties: | ||||||
|  | * Default of 2GB of memory, auto-sized, so could be smaller | ||||||
|  | * 3.5GB of memory (with no auto-size) if (board id & 2) is 2 | ||||||
|  | * 1GB of memory (with no auto-size) if board id is 17. | ||||||
|  | @ -52,6 +52,7 @@ typedef struct global_data { | ||||||
| 	unsigned long env_has_init;	/* Bitmask of boolean of struct env_location offsets */ | 	unsigned long env_has_init;	/* Bitmask of boolean of struct env_location offsets */ | ||||||
| 	int env_load_location; | 	int env_load_location; | ||||||
| 
 | 
 | ||||||
|  | 	unsigned long ram_base;		/* Base address of RAM used by U-Boot */ | ||||||
| 	unsigned long ram_top;		/* Top address of RAM used by U-Boot */ | 	unsigned long ram_top;		/* Top address of RAM used by U-Boot */ | ||||||
| 	unsigned long relocaddr;	/* Start address of U-Boot in RAM */ | 	unsigned long relocaddr;	/* Start address of U-Boot in RAM */ | ||||||
| 	phys_size_t ram_size;		/* RAM size */ | 	phys_size_t ram_size;		/* RAM size */ | ||||||
|  |  | ||||||
|  | @ -283,6 +283,16 @@ int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width, | ||||||
| 
 | 
 | ||||||
| int fdt_overlay_apply_verbose(void *fdt, void *fdto); | int fdt_overlay_apply_verbose(void *fdt, void *fdto); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * fdt_get_cells_len() - Get the length of a type of cell in top-level nodes | ||||||
|  |  * | ||||||
|  |  * Returns the length of the cell type in bytes (4 or 8). | ||||||
|  |  * | ||||||
|  |  * @blob: Pointer to device tree blob | ||||||
|  |  * @nr_cells_name: Name to lookup, e.g. "#address-cells" | ||||||
|  |  */ | ||||||
|  | int fdt_get_cells_len(const void *blob, char *nr_cells_name); | ||||||
|  | 
 | ||||||
| #endif /* ifdef CONFIG_OF_LIBFDT */ | #endif /* ifdef CONFIG_OF_LIBFDT */ | ||||||
| 
 | 
 | ||||||
| #ifdef USE_HOSTCC | #ifdef USE_HOSTCC | ||||||
|  |  | ||||||
|  | @ -41,6 +41,8 @@ struct fdt_memory { | ||||||
| 	fdt_addr_t end; | 	fdt_addr_t end; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct bd_info; | ||||||
|  | 
 | ||||||
| #ifdef CONFIG_SPL_BUILD | #ifdef CONFIG_SPL_BUILD | ||||||
| #define SPL_BUILD	1 | #define SPL_BUILD	1 | ||||||
| #else | #else | ||||||
|  | @ -993,6 +995,40 @@ int fdtdec_setup(void); | ||||||
|  * Called when CONFIG_OF_BOARD is defined, or if CONFIG_OF_SEPARATE is defined |  * Called when CONFIG_OF_BOARD is defined, or if CONFIG_OF_SEPARATE is defined | ||||||
|  * and the board implements it. |  * and the board implements it. | ||||||
|  */ |  */ | ||||||
| void *board_fdt_blob_setup(void); | 
 | ||||||
|  | /*
 | ||||||
|  |  * Decode the size of memory | ||||||
|  |  * | ||||||
|  |  * RAM size is normally set in a /memory node and consists of a list of | ||||||
|  |  * (base, size) cells in the 'reg' property. This information is used to | ||||||
|  |  * determine the total available memory as well as the address and size | ||||||
|  |  * of each bank. | ||||||
|  |  * | ||||||
|  |  * Optionally the memory configuration can vary depending on a board id, | ||||||
|  |  * typically read from strapping resistors or an EEPROM on the board. | ||||||
|  |  * | ||||||
|  |  * Finally, memory size can be detected (within certain limits) by probing | ||||||
|  |  * the available memory. It is safe to do so within the limits provides by | ||||||
|  |  * the board's device tree information. This makes it possible to produce | ||||||
|  |  * boards with different memory sizes, where the device tree specifies the | ||||||
|  |  * maximum memory configuration, and the smaller memory configuration is | ||||||
|  |  * probed. | ||||||
|  |  * | ||||||
|  |  * This function decodes that information, returning the memory base address, | ||||||
|  |  * size and bank information. See the memory.txt binding for full | ||||||
|  |  * documentation. | ||||||
|  |  * | ||||||
|  |  * @param blob		Device tree blob | ||||||
|  |  * @param area		Name of node to check (NULL means "/memory") | ||||||
|  |  * @param board_id	Board ID to look up | ||||||
|  |  * @param basep		Returns base address of first memory bank (NULL to | ||||||
|  |  *			ignore) | ||||||
|  |  * @param sizep		Returns total memory size (NULL to ignore) | ||||||
|  |  * @param bd		Updated with the memory bank information (NULL to skip) | ||||||
|  |  * @return 0 if OK, -ve on error | ||||||
|  |  */ | ||||||
|  | int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id, | ||||||
|  | 			   phys_addr_t *basep, phys_size_t *sizep, | ||||||
|  | 			   struct bd_info *bd); | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
							
								
								
									
										109
									
								
								lib/fdtdec.c
								
								
								
								
							
							
						
						
									
										109
									
								
								lib/fdtdec.c
								
								
								
								
							|  | @ -11,6 +11,7 @@ | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <fdtdec.h> | #include <fdtdec.h> | ||||||
| #include <fdt_support.h> | #include <fdt_support.h> | ||||||
|  | #include <inttypes.h> | ||||||
| #include <linux/libfdt.h> | #include <linux/libfdt.h> | ||||||
| #include <serial.h> | #include <serial.h> | ||||||
| #include <asm/sections.h> | #include <asm/sections.h> | ||||||
|  | @ -1350,4 +1351,112 @@ int fdtdec_setup(void) | ||||||
| 	return fdtdec_prepare_fdt(); | 	return fdtdec_prepare_fdt(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_NR_DRAM_BANKS | ||||||
|  | int fdtdec_decode_ram_size(const void *blob, const char *area, int board_id, | ||||||
|  | 			   phys_addr_t *basep, phys_size_t *sizep, bd_t *bd) | ||||||
|  | { | ||||||
|  | 	int addr_cells, size_cells; | ||||||
|  | 	const u32 *cell, *end; | ||||||
|  | 	u64 total_size, size, addr; | ||||||
|  | 	int node, child; | ||||||
|  | 	bool auto_size; | ||||||
|  | 	int bank; | ||||||
|  | 	int len; | ||||||
|  | 
 | ||||||
|  | 	debug("%s: board_id=%d\n", __func__, board_id); | ||||||
|  | 	if (!area) | ||||||
|  | 		area = "/memory"; | ||||||
|  | 	node = fdt_path_offset(blob, area); | ||||||
|  | 	if (node < 0) { | ||||||
|  | 		debug("No %s node found\n", area); | ||||||
|  | 		return -ENOENT; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	cell = fdt_getprop(blob, node, "reg", &len); | ||||||
|  | 	if (!cell) { | ||||||
|  | 		debug("No reg property found\n"); | ||||||
|  | 		return -ENOENT; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	addr_cells = fdt_address_cells(blob, node); | ||||||
|  | 	size_cells = fdt_size_cells(blob, node); | ||||||
|  | 
 | ||||||
|  | 	/* Check the board id and mask */ | ||||||
|  | 	for (child = fdt_first_subnode(blob, node); | ||||||
|  | 	     child >= 0; | ||||||
|  | 	     child = fdt_next_subnode(blob, child)) { | ||||||
|  | 		int match_mask, match_value; | ||||||
|  | 
 | ||||||
|  | 		match_mask = fdtdec_get_int(blob, child, "match-mask", -1); | ||||||
|  | 		match_value = fdtdec_get_int(blob, child, "match-value", -1); | ||||||
|  | 
 | ||||||
|  | 		if (match_value >= 0 && | ||||||
|  | 		    ((board_id & match_mask) == match_value)) { | ||||||
|  | 			/* Found matching mask */ | ||||||
|  | 			debug("Found matching mask %d\n", match_mask); | ||||||
|  | 			node = child; | ||||||
|  | 			cell = fdt_getprop(blob, node, "reg", &len); | ||||||
|  | 			if (!cell) { | ||||||
|  | 				debug("No memory-banks property found\n"); | ||||||
|  | 				return -EINVAL; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	/* Note: if no matching subnode was found we use the parent node */ | ||||||
|  | 
 | ||||||
|  | 	if (bd) { | ||||||
|  | 		memset(bd->bi_dram, '\0', sizeof(bd->bi_dram[0]) * | ||||||
|  | 						CONFIG_NR_DRAM_BANKS); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	auto_size = fdtdec_get_bool(blob, node, "auto-size"); | ||||||
|  | 
 | ||||||
|  | 	total_size = 0; | ||||||
|  | 	end = cell + len / 4 - addr_cells - size_cells; | ||||||
|  | 	debug("cell at %p, end %p\n", cell, end); | ||||||
|  | 	for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { | ||||||
|  | 		if (cell > end) | ||||||
|  | 			break; | ||||||
|  | 		addr = 0; | ||||||
|  | 		if (addr_cells == 2) | ||||||
|  | 			addr += (u64)fdt32_to_cpu(*cell++) << 32UL; | ||||||
|  | 		addr += fdt32_to_cpu(*cell++); | ||||||
|  | 		if (bd) | ||||||
|  | 			bd->bi_dram[bank].start = addr; | ||||||
|  | 		if (basep && !bank) | ||||||
|  | 			*basep = (phys_addr_t)addr; | ||||||
|  | 
 | ||||||
|  | 		size = 0; | ||||||
|  | 		if (size_cells == 2) | ||||||
|  | 			size += (u64)fdt32_to_cpu(*cell++) << 32UL; | ||||||
|  | 		size += fdt32_to_cpu(*cell++); | ||||||
|  | 
 | ||||||
|  | 		if (auto_size) { | ||||||
|  | 			u64 new_size; | ||||||
|  | 
 | ||||||
|  | 			debug("Auto-sizing %" PRIx64 ", size %" PRIx64 ": ", | ||||||
|  | 			      addr, size); | ||||||
|  | 			new_size = get_ram_size((long *)(uintptr_t)addr, size); | ||||||
|  | 			if (new_size == size) { | ||||||
|  | 				debug("OK\n"); | ||||||
|  | 			} else { | ||||||
|  | 				debug("sized to %" PRIx64 "\n", new_size); | ||||||
|  | 				size = new_size; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (bd) | ||||||
|  | 			bd->bi_dram[bank].size = size; | ||||||
|  | 		total_size += size; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	debug("Memory size %" PRIu64 "\n", total_size); | ||||||
|  | 	if (sizep) | ||||||
|  | 		*sizep = (phys_size_t)total_size; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_NR_DRAM_BANKS */ | ||||||
|  | 
 | ||||||
| #endif /* !USE_HOSTCC */ | #endif /* !USE_HOSTCC */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue