@@ -85,9 +85,9 @@ void *board_fdt_blob_setup(int *err)
/*
* Parse the /memory node while we're here,
* this makes it easy to do other things early.
*/
- qcom_parse_memory();
+ qcom_parse_memory(internal_valid);
return (void *)gd->fdt_blob;
}
@@ -9,14 +9,47 @@
#include <asm-generic/unaligned.h>
#include <dm.h>
#include <log.h>
#include <sort.h>
+#include <soc/qcom/smem.h>
+
+#define SMEM_USABLE_RAM_PARTITION_TABLE 402
+#define RAM_PART_NAME_LENGTH 16
+#define RAM_NUM_PART_ENTRIES 32
+#define CATEGORY_SDRAM 0x0E
+#define TYPE_SYSMEM 0x01
static struct {
phys_addr_t start;
phys_size_t size;
} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
+struct smem_ram_ptable_hdr {
+ u32 magic[2];
+ u32 version;
+ u32 reserved;
+ u32 len;
+} __packed;
+
+struct smem_ram_ptn {
+ char name[RAM_PART_NAME_LENGTH];
+ u64 start;
+ u64 size;
+ u32 attr;
+ u32 category;
+ u32 domain;
+ u32 type;
+ u32 num_partitions;
+ u32 reserved[3];
+ u32 reserved2[2]; /* The struct grew by 8 bytes at some point */
+} __packed;
+
+struct smem_ram_ptable {
+ struct smem_ram_ptable_hdr hdr;
+ u32 reserved; /* Added for 8 bytes alignment of header */
+ struct smem_ram_ptn parts[RAM_NUM_PART_ENTRIES];
+} __packed;
+
int dram_init(void)
{
/*
* gd->ram_base / ram_size have been setup already
@@ -47,8 +80,12 @@ static void qcom_configure_bi_dram(void)
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
+ debug("Bank[%d]: start = %#011llx, size = %#011llx\n",
+ i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size);
+ if (!prevbl_ddr_banks[i].size)
+ break;
}
}
int dram_init_banksize(void)
@@ -57,8 +94,48 @@ int dram_init_banksize(void)
return 0;
}
+/* Parse memory map from SMEM, return the number of entries */
+static int qcom_parse_memory_smem(phys_addr_t *ram_end)
+{
+ size_t size;
+ int i, j = 0, ret;
+ struct smem_ram_ptable *ram_ptable;
+ struct smem_ram_ptn *p;
+
+ ret = qcom_smem_init();
+ if (ret) {
+ debug("Failed to initialize SMEM: %d.\n", ret);
+ return ret;
+ }
+
+ ram_ptable = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_USABLE_RAM_PARTITION_TABLE, &size);
+ if (!ram_ptable) {
+ debug("Failed to find SMEM partition.\n");
+ return -ENODEV;
+ }
+
+ /* Check validy of RAM */
+ for (i = 0; i < RAM_NUM_PART_ENTRIES && j < CONFIG_NR_DRAM_BANKS; i++) {
+ p = &ram_ptable->parts[i];
+ if (p->category != CATEGORY_SDRAM || p->type != TYPE_SYSMEM)
+ continue;
+ if (!p->size && !p->start)
+ break;
+
+ prevbl_ddr_banks[j].start = p->start;
+ prevbl_ddr_banks[j].size = p->size;
+ *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
+ j++;
+ }
+
+ if (j == CONFIG_NR_DRAM_BANKS)
+ log_err("SMEM: More than CONFIG_NR_DRAM_BANKS (%u) entries!", CONFIG_NR_DRAM_BANKS);
+
+ return j;
+}
+
static void qcom_parse_memory_dt(const fdt64_t *memory, int banks, phys_addr_t *ram_end)
{
int i, j;
@@ -76,10 +153,23 @@ static void qcom_parse_memory_dt(const fdt64_t *memory, int banks, phys_addr_t *
*ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
}
}
-/* Parse the memory layout from the FDT. */
-void qcom_parse_memory(void)
+/*
+ * Parse the memory layout from FDT or SMEM:
+ *
+ * If using an internal FDT (where the memory map must have
+ * been written by hand) then we prefer using the layout from
+ * there. This allows overriding SMEM.
+ *
+ * If using an external FDT (coming from ABL), we prefer SMEM
+ * since it is likely to be more accurate / simple, especially
+ * on newer platforms.
+ *
+ * If SMEM parsing fails, we always try to fall back to FDT.
+ *
+ */
+void qcom_parse_memory(bool fdt_is_internal)
{
ofnode node;
const fdt64_t *memory;
int memsize;
@@ -103,10 +193,18 @@ void qcom_parse_memory(void)
log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
qcom_parse_memory_dt(memory, banks, &ram_end);
- debug("%d banks, ram_base = %#011lx, ram_size = %#011llx, ram_end = %#011llx\n",
- banks, gd->ram_base, gd->ram_size, ram_end);
+ /*
+ * If using an internal FDT but the memory node is empty
+ * then fall back to SMEM.
+ */
+ if (!prevbl_ddr_banks[0].size && fdt_is_internal) {
+ banks = qcom_parse_memory_smem(&ram_end);
+ if (banks < 0)
+ panic("Couldn't find a valid memory map!\n");
+ }
+
/* Sort our RAM banks -_- */
qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
gd->ram_base = prevbl_ddr_banks[0].start;
@@ -2,8 +2,10 @@
#ifndef __QCOM_PRIV_H__
#define __QCOM_PRIV_H__
+#include <stdbool.h>
+
#if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
void qcom_configure_capsule_updates(void);
#else
void qcom_configure_capsule_updates(void) {}
@@ -22,7 +24,7 @@ static inline void qcom_of_fixup_nodes(void)
log_debug("Unable to dynamically fixup USB nodes, please enable CONFIG_OF_LIVE\n");
}
#endif /* OF_LIVE */
-void qcom_parse_memory(void);
+void qcom_parse_memory(bool fdt_is_internal);
#endif /* __QCOM_PRIV_H__ */
It is possible to derive the memory map for a Qualcomm platform from the SMEM shared memory region. The memory map is populated by the preloader. Introduce support for parsing this data and using it to populate U-Boot's memory map. Since we aren't yet sure if this will work for every platform, it is not yet used in all cases, if U-Boot is booted with an internal FDT which has the memory map defined, this will be used instead. If the FDT comes from ABL, or we're using an internal FDT with no memory map defined, then U-Boot will try to use SMEM. This should remove the need to define the memory map statically in a U-Boot overlay DT for most boards. Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org> --- arch/arm/mach-snapdragon/board.c | 2 +- arch/arm/mach-snapdragon/dram.c | 106 +++++++++++++++++++++++++++++++++-- arch/arm/mach-snapdragon/qcom-priv.h | 4 +- 3 files changed, 106 insertions(+), 6 deletions(-)