diff mbox series

[13/15] mach-snapdragon: support parsing memory map from SMEM

Message ID 20241124-b4-modernise-smem-v1-13-b7852c11b67c@linaro.org
State New
Headers show
Series qcom: smem: modernize SMEM in U-Boot | expand

Commit Message

Caleb Connolly Nov. 24, 2024, 7:17 p.m. UTC
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(-)
diff mbox series

Patch

diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index 8d171957b852..269d39e4f6e1 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -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;
 }
 
diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c
index c4c60039cb4c..ef226e000858 100644
--- a/arch/arm/mach-snapdragon/dram.c
+++ b/arch/arm/mach-snapdragon/dram.c
@@ -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;
diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
index b7f3bf798d3c..690557463642 100644
--- a/arch/arm/mach-snapdragon/qcom-priv.h
+++ b/arch/arm/mach-snapdragon/qcom-priv.h
@@ -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__ */