mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-26 09:01:35 +00:00
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
5 changed files with 224 additions and 1 deletions
67
doc/device-tree-bindings/memory/memory.txt
Normal file
67
doc/device-tree-bindings/memory/memory.txt
Normal file
|
@ -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…
Add table
Reference in a new issue