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__ */
