fdt: Add device tree memory bindings
authorMichael Pratt <mpratt@chromium.org>
Mon, 11 Jun 2018 19:07:09 +0000 (13:07 -0600)
committerSimon Glass <sjg@chromium.org>
Mon, 9 Jul 2018 15:11:00 +0000 (09:11 -0600)
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>
doc/device-tree-bindings/memory/memory.txt [new file with mode: 0644]
include/asm-generic/global_data.h
include/fdt_support.h
include/fdtdec.h
lib/fdtdec.c

diff --git a/doc/device-tree-bindings/memory/memory.txt b/doc/device-tree-bindings/memory/memory.txt
new file mode 100644 (file)
index 0000000..321894e
--- /dev/null
@@ -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.
index 2d451f8a1b4a59ad2624e560ceeb00d8d7f2d4c9..0fd4900392be58b9da2be98461ac403a81aeb4e8 100644 (file)
@@ -52,6 +52,7 @@ typedef struct global_data {
        unsigned long env_has_init;     /* Bitmask of boolean of struct env_location offsets */
        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 relocaddr;        /* Start address of U-Boot in RAM */
        phys_size_t ram_size;           /* RAM size */
index e6c43ea98380ef13c2296dd42e84aa111bbb9c0f..a9a0078af6635cc1969d389f0262d67a44474290 100644 (file)
@@ -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);
 
+/**
+ * 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 */
 
 #ifdef USE_HOSTCC
index c15b2a04a7aea5a17148a8608a15fce3bd3aacb9..332105504b4a980f6c32de48c65633dc7ead3bd0 100644 (file)
@@ -41,6 +41,8 @@ struct fdt_memory {
        fdt_addr_t end;
 };
 
+struct bd_info;
+
 #ifdef CONFIG_SPL_BUILD
 #define SPL_BUILD      1
 #else
@@ -993,6 +995,40 @@ int fdtdec_setup(void);
  * Called when CONFIG_OF_BOARD is defined, or if CONFIG_OF_SEPARATE is defined
  * 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
index f4e8dbf699a88cebd7793e87066c319b23b037c3..fc92082b0bdf5777cac56004d77ca2646b47b4cb 100644 (file)
@@ -11,6 +11,7 @@
 #include <errno.h>
 #include <fdtdec.h>
 #include <fdt_support.h>
+#include <inttypes.h>
 #include <linux/libfdt.h>
 #include <serial.h>
 #include <asm/sections.h>
@@ -1350,4 +1351,112 @@ int fdtdec_setup(void)
        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 */