diff --git a/arch/arm/mach-mvebu/arm64-common.c b/arch/arm/mach-mvebu/arm64-common.c
index 244ea49d8a..34cc0479a8 100644
--- a/arch/arm/mach-mvebu/arm64-common.c
+++ b/arch/arm/mach-mvebu/arm64-common.c
@@ -49,6 +49,8 @@ __weak int dram_init_banksize(void)
 {
 	if (CONFIG_IS_ENABLED(ARMADA_8K))
 		return a8k_dram_init_banksize();
+	else if (CONFIG_IS_ENABLED(ARMADA_3700))
+		return a3700_dram_init_banksize();
 	else
 		return fdtdec_setup_memory_banksize();
 }
@@ -61,6 +63,9 @@ __weak int dram_init(void)
 			return 0;
 	}
 
+	if (CONFIG_IS_ENABLED(ARMADA_3700))
+		return a3700_dram_init();
+
 	if (fdtdec_setup_mem_size_base() != 0)
 		return -EINVAL;
 
diff --git a/arch/arm/mach-mvebu/armada3700/cpu.c b/arch/arm/mach-mvebu/armada3700/cpu.c
index c83268181b..959a909d8a 100644
--- a/arch/arm/mach-mvebu/armada3700/cpu.c
+++ b/arch/arm/mach-mvebu/armada3700/cpu.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2016 Stefan Roese <sr at denx.de>
+ * Copyright (C) 2020 Marek Behun <marek.behun at nic.cz>
  */
 
 #include <common.h>
@@ -13,6 +14,7 @@
 #include <asm/arch/cpu.h>
 #include <asm/arch/soc.h>
 #include <asm/armv8/mmu.h>
+#include <sort.h>
 
 /* Armada 3700 */
 #define MVEBU_GPIO_NB_REG_BASE		(MVEBU_REGISTER(0x13800))
@@ -26,39 +28,237 @@
 #define MVEBU_NB_WARM_RST_REG		(MVEBU_GPIO_NB_REG_BASE + 0x40)
 #define MVEBU_NB_WARM_RST_MAGIC_NUM	0x1d1e
 
-static struct mm_region mvebu_mem_map[] = {
-	{
-		/* RAM */
-		.phys = 0x0UL,
-		.virt = 0x0UL,
-		.size = 0x80000000UL,
-		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
-			 PTE_BLOCK_INNER_SHARE
-	},
+/* Armada 3700 CPU Address Decoder registers */
+#define MVEBU_CPU_DEC_WIN_REG_BASE	(size_t)(MVEBU_REGISTER(0xcf00))
+#define MVEBU_CPU_DEC_WIN_CTRL(w) \
+	(MVEBU_CPU_DEC_WIN_REG_BASE + ((w) << 4))
+#define MVEBU_CPU_DEC_WIN_CTRL_EN	BIT(0)
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_MASK	0xf
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_OFFS	4
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM	0
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE	2
+#define MVEBU_CPU_DEC_WIN_SIZE(w)	(MVEBU_CPU_DEC_WIN_CTRL(w) + 0x4)
+#define MVEBU_CPU_DEC_WIN_BASE(w)	(MVEBU_CPU_DEC_WIN_CTRL(w) + 0x8)
+#define MVEBU_CPU_DEC_WIN_REMAP(w)	(MVEBU_CPU_DEC_WIN_CTRL(w) + 0xc)
+#define MVEBU_CPU_DEC_WIN_GRANULARITY	16
+#define MVEBU_CPU_DEC_WINS		5
+
+#define MAX_MEM_MAP_REGIONS		(MVEBU_CPU_DEC_WINS + 2)
+
+#define A3700_PTE_BLOCK_NORMAL \
+	(PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE)
+#define A3700_PTE_BLOCK_DEVICE \
+	(PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct mm_region mvebu_mem_map[MAX_MEM_MAP_REGIONS] = {
 	{
-		/* SRAM, MMIO regions */
-		.phys = 0xd0000000UL,
-		.virt = 0xd0000000UL,
+		/*
+		 * SRAM, MMIO regions
+		 * Don't remove this, a3700_build_mem_map needs it.
+		 */
+		.phys = SOC_REGS_PHY_BASE,
+		.virt = SOC_REGS_PHY_BASE,
 		.size = 0x02000000UL,	/* 32MiB internal registers */
-		.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
-			 PTE_BLOCK_NON_SHARE
-	},
-	{
-		/* PCI regions */
-		.phys = 0xe8000000UL,
-		.virt = 0xe8000000UL,
-		.size = 0x02000000UL,	/* 32MiB master PCI space */
-		.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
-			 PTE_BLOCK_NON_SHARE
+		.attrs = A3700_PTE_BLOCK_DEVICE
 	},
-	{
-		/* List terminator */
-		0,
-	}
 };
 
 struct mm_region *mem_map = mvebu_mem_map;
 
+static int get_cpu_dec_win(int win, u32 *tgt, u32 *base, u32 *size)
+{
+	u32 reg;
+
+	reg = readl(MVEBU_CPU_DEC_WIN_CTRL(win));
+	if (!(reg & MVEBU_CPU_DEC_WIN_CTRL_EN))
+		return -1;
+
+	if (tgt) {
+		reg >>= MVEBU_CPU_DEC_WIN_CTRL_TGT_OFFS;
+		reg &= MVEBU_CPU_DEC_WIN_CTRL_TGT_MASK;
+		*tgt = reg;
+	}
+
+	if (base) {
+		reg = readl(MVEBU_CPU_DEC_WIN_BASE(win));
+		*base = reg << MVEBU_CPU_DEC_WIN_GRANULARITY;
+	}
+
+	if (size) {
+		/*
+		 * Window size is encoded as the number of 1s from LSB to MSB,
+		 * followed by 0s. The number of 1s specifies the size in 64 KiB
+		 * granularity.
+		 */
+		reg = readl(MVEBU_CPU_DEC_WIN_SIZE(win));
+		*size = ((reg + 1) << MVEBU_CPU_DEC_WIN_GRANULARITY);
+	}
+
+	return 0;
+}
+
+/*
+ * Builds mem_map according to CPU Address Decoder settings, which were set by
+ * the TIMH image on the Cortex-M3 secure processor, or by ARM Trusted Firmware
+ */
+static void build_mem_map(void)
+{
+	int win, region;
+
+	region = 1;
+	for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+		u32 base, tgt, size;
+		u64 attrs;
+
+		/* skip disabled windows */
+		if (get_cpu_dec_win(win, &tgt, &base, &size))
+			continue;
+
+		if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
+			attrs = A3700_PTE_BLOCK_NORMAL;
+		else if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE)
+			attrs = A3700_PTE_BLOCK_DEVICE;
+		else
+			/* skip windows with other targets */
+			continue;
+
+		mvebu_mem_map[region].phys = base;
+		mvebu_mem_map[region].virt = base;
+		mvebu_mem_map[region].size = size;
+		mvebu_mem_map[region].attrs = attrs;
+		++region;
+	}
+
+	/* add list terminator */
+	mvebu_mem_map[region].size = 0;
+	mvebu_mem_map[region].attrs = 0;
+}
+
+void enable_caches(void)
+{
+	build_mem_map();
+
+	icache_enable();
+	dcache_enable();
+}
+
+int a3700_dram_init(void)
+{
+	int win;
+
+	gd->ram_size = 0;
+	for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+		u32 base, tgt, size;
+
+		/* skip disabled windows */
+		if (get_cpu_dec_win(win, &tgt, &base, &size))
+			continue;
+
+		/* skip non-DRAM windows */
+		if (tgt != MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
+			continue;
+
+		/*
+		 * It is possible that one image was built for boards with
+		 * different RAM sizes, for example 512 MiB and 1 GiB.
+		 * We therefore try to determine the actual RAM size in the
+		 * window with get_ram_size.
+		 */
+		gd->ram_size += get_ram_size((void *)(size_t)base, size);
+	}
+
+	return 0;
+}
+
+struct a3700_dram_window {
+	size_t base, size;
+};
+
+static int dram_win_cmp(const void *a, const void *b)
+{
+	size_t ab, bb;
+
+	ab = ((const struct a3700_dram_window *)a)->base;
+	bb = ((const struct a3700_dram_window *)b)->base;
+
+	if (ab < bb)
+		return -1;
+	else if (ab > bb)
+		return 1;
+	else
+		return 0;
+}
+
+int a3700_dram_init_banksize(void)
+{
+	struct a3700_dram_window dram_wins[MVEBU_CPU_DEC_WINS];
+	int bank, win, ndram_wins;
+	u32 last_end;
+	size_t size;
+
+	ndram_wins = 0;
+	for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+		u32 base, tgt, size;
+
+		/* skip disabled windows */
+		if (get_cpu_dec_win(win, &tgt, &base, &size))
+			continue;
+
+		/* skip non-DRAM windows */
+		if (tgt != MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
+			continue;
+
+		dram_wins[win].base = base;
+		dram_wins[win].size = size;
+		++ndram_wins;
+	}
+
+	qsort(dram_wins, ndram_wins, sizeof(dram_wins[0]), dram_win_cmp);
+
+	bank = 0;
+	last_end = -1;
+
+	for (win = 0; win < ndram_wins; ++win) {
+		/* again determining actual RAM size as in a3700_dram_init */
+		size = get_ram_size((void *)dram_wins[win].base,
+				    dram_wins[win].size);
+
+		/*
+		 * Check if previous window ends as the current starts. If yes,
+		 * merge these windows into one "bank". This is possible by this
+		 * simple check thanks to mem_map regions being qsorted in
+		 * build_mem_map.
+		 */
+		if (last_end == dram_wins[win].base) {
+			gd->bd->bi_dram[bank - 1].size += size;
+			last_end += size;
+		} else {
+			if (bank == CONFIG_NR_DRAM_BANKS) {
+				printf("Need more CONFIG_NR_DRAM_BANKS\n");
+				return -ENOBUFS;
+			}
+
+			gd->bd->bi_dram[bank].start = dram_wins[win].base;
+			gd->bd->bi_dram[bank].size = size;
+			last_end = dram_wins[win].base + size;
+			++bank;
+		}
+	}
+
+	/*
+	 * If there is more place for DRAM BANKS definitions than needed, fill
+	 * the rest with zeros.
+	 */
+	for (; bank < CONFIG_NR_DRAM_BANKS; ++bank) {
+		gd->bd->bi_dram[bank].start = 0;
+		gd->bd->bi_dram[bank].size = 0;
+	}
+
+	return 0;
+}
+
 void reset_cpu(ulong ignored)
 {
 	/*
diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h
index 7af8e5d09f..2a53329420 100644
--- a/arch/arm/mach-mvebu/include/mach/cpu.h
+++ b/arch/arm/mach-mvebu/include/mach/cpu.h
@@ -174,6 +174,10 @@ static inline void mv_avs_init(void) {}
 u64 a8k_dram_scan_ap_sz(void);
 int a8k_dram_init_banksize(void);
 
+/* A3700 dram functions */
+int a3700_dram_init(void);
+int a3700_dram_init_banksize(void);
+
 /*
  * get_ref_clk
  *
