Merge branch 'master' of git://git.denx.de/u-boot-marvell

- Misc enhancements to Clearfog, including board variant detection
  (Joel)
- Misc enhancements to Turris Mox, including generalization of the
  ARMADA37xx DDR size detection (Marek)
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 2a89da2..6d1e866 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -191,25 +191,25 @@
 	armada-3720-turris-mox.dtb		\
 	armada-3720-uDPU.dtb			\
 	armada-375-db.dtb			\
+	armada-385-atl-x530.dtb			\
+	armada-385-atl-x530DP.dtb		\
+	armada-385-db-88f6820-amc.dtb		\
+	armada-385-turris-omnia.dtb		\
 	armada-388-clearfog.dtb			\
 	armada-388-gp.dtb			\
 	armada-388-helios4.dtb			\
-	armada-385-db-88f6820-amc.dtb		\
-	armada-385-turris-omnia.dtb		\
-	armada-7040-db.dtb			\
+	armada-38x-controlcenterdc.dtb		\
 	armada-7040-db-nand.dtb			\
+	armada-7040-db.dtb			\
+	armada-8040-clearfog-gt-8k.dtb		\
 	armada-8040-db.dtb			\
 	armada-8040-mcbin.dtb			\
-	armada-8040-clearfog-gt-8k.dtb		\
+	armada-xp-crs305-1g-4s.dtb		\
+	armada-xp-db-xc3-24g4xg.dtb		\
 	armada-xp-gp.dtb			\
 	armada-xp-maxbcm.dtb			\
 	armada-xp-synology-ds414.dtb		\
-	armada-xp-theadorable.dtb		\
-	armada-38x-controlcenterdc.dtb		\
-	armada-385-atl-x530.dtb			\
-	armada-385-atl-x530DP.dtb		\
-	armada-xp-db-xc3-24g4xg.dtb		\
-	armada-xp-crs305-1g-4s.dtb
+	armada-xp-theadorable.dtb
 
 dtb-$(CONFIG_ARCH_UNIPHIER_LD11) += \
 	uniphier-ld11-global.dtb \
diff --git a/arch/arm/dts/armada-3720-turris-mox.dts b/arch/arm/dts/armada-3720-turris-mox.dts
index c36a5b8..a1e0ad5 100644
--- a/arch/arm/dts/armada-3720-turris-mox.dts
+++ b/arch/arm/dts/armada-3720-turris-mox.dts
@@ -42,9 +42,24 @@
 		startup-delay-us = <2000000>;
 		shutdown-delay-us = <1000000>;
 		gpio = <&gpiosb 0 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
 		regulator-boot-on;
 	};
 
+	vsdc_reg: vsdc-reg {
+		compatible = "regulator-gpio";
+		regulator-name = "vsdc";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+
+		gpios = <&gpiosb 23 GPIO_ACTIVE_HIGH>;
+		gpios-states = <0>;
+		states = <1800000 0x1
+			  3300000 0x0>;
+		enable-active-high;
+	};
+
 	mdio {
 		#address-cells = <1>;
 		#size-cells = <0>;
@@ -93,7 +108,11 @@
 };
 
 &sdhci1 {
+	wp-inverted;
 	bus-width = <4>;
+	cd-gpios = <&gpionb 10 GPIO_ACTIVE_HIGH>;
+	vqmmc-supply = <&vsdc_reg>;
+	marvell,pad-type = "sd";
 	status = "okay";
 };
 
diff --git a/arch/arm/dts/armada-38x-solidrun-microsom.dtsi b/arch/arm/dts/armada-38x-solidrun-microsom.dtsi
index a322a28..9bbeafc 100644
--- a/arch/arm/dts/armada-38x-solidrun-microsom.dtsi
+++ b/arch/arm/dts/armada-38x-solidrun-microsom.dtsi
@@ -39,7 +39,6 @@
 
 &eth0 {
 	/* ethernet@70000 */
-	mac-address = [00 50 43 02 02 01];
 	pinctrl-0 = <&ge0_rgmii_pins>;
 	pinctrl-names = "default";
 	phy = <&phy_dedicated>;
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index bc5eaa5..161dee9 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -280,4 +280,6 @@
 	default 0
 	depends on SECURED_MODE_IMAGE
 
+source "board/solidrun/clearfog/Kconfig"
+
 endif
diff --git a/arch/arm/mach-mvebu/arm64-common.c b/arch/arm/mach-mvebu/arm64-common.c
index 40b98db..34cc047 100644
--- a/arch/arm/mach-mvebu/arm64-common.c
+++ b/arch/arm/mach-mvebu/arm64-common.c
@@ -45,54 +45,14 @@
 	return NULL;
 }
 
-/* DRAM init code ... */
-
-#define MV_SIP_DRAM_SIZE	0x82000010
-
-static u64 a8k_dram_scan_ap_sz(void)
-{
-	struct pt_regs pregs;
-
-	pregs.regs[0] = MV_SIP_DRAM_SIZE;
-	pregs.regs[1] = SOC_REGS_PHY_BASE;
-	smc_call(&pregs);
-
-	return pregs.regs[0];
-}
-
-static void a8k_dram_init_banksize(void)
-{
-	/*
-	 * The firmware (ATF) leaves a 1G whole above the 3G mark for IO
-	 * devices. Higher RAM is mapped at 4G.
-	 *
-	 * Config 2 DRAM banks:
-	 * Bank 0 - max size 4G - 1G
-	 * Bank 1 - ram size - 4G + 1G
-	 */
-	phys_size_t max_bank0_size = SZ_4G - SZ_1G;
-
-	gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
-	if (gd->ram_size <= max_bank0_size) {
-		gd->bd->bi_dram[0].size = gd->ram_size;
-		return;
-	}
-
-	gd->bd->bi_dram[0].size = max_bank0_size;
-	if (CONFIG_NR_DRAM_BANKS > 1) {
-		gd->bd->bi_dram[1].start = SZ_4G;
-		gd->bd->bi_dram[1].size = gd->ram_size - max_bank0_size;
-	}
-}
-
 __weak int dram_init_banksize(void)
 {
 	if (CONFIG_IS_ENABLED(ARMADA_8K))
-		a8k_dram_init_banksize();
+		return a8k_dram_init_banksize();
+	else if (CONFIG_IS_ENABLED(ARMADA_3700))
+		return a3700_dram_init_banksize();
 	else
-		fdtdec_setup_memory_banksize();
-
-	return 0;
+		return fdtdec_setup_memory_banksize();
 }
 
 __weak int dram_init(void)
@@ -103,6 +63,9 @@
 			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 c832681..17d2d43 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@denx.de>
+ * Copyright (C) 2020 Marek Behun <marek.behun@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,289 @@
 #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)
+
+#define PCIE_PATH			"/soc/pcie@d0070000"
+
+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;
+}
+
+static u32 find_pcie_window_base(void)
+{
+	int win;
+
+	for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+		u32 base, tgt;
+
+		/* skip disabled windows */
+		if (get_cpu_dec_win(win, &tgt, &base, NULL))
+			continue;
+
+		if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE)
+			return base;
+	}
+
+	return -1;
+}
+
+int a3700_fdt_fix_pcie_regions(void *blob)
+{
+	u32 new_ranges[14], base;
+	const u32 *ranges;
+	int node, len;
+
+	node = fdt_path_offset(blob, PCIE_PATH);
+	if (node < 0)
+		return node;
+
+	ranges = fdt_getprop(blob, node, "ranges", &len);
+	if (!ranges)
+		return -ENOENT;
+
+	if (len != sizeof(new_ranges))
+		return -EINVAL;
+
+	memcpy(new_ranges, ranges, len);
+
+	base = find_pcie_window_base();
+	if (base == -1)
+		return -ENOENT;
+
+	new_ranges[2] = cpu_to_fdt32(base);
+	new_ranges[4] = new_ranges[2];
+
+	new_ranges[9] = cpu_to_fdt32(base + 0x1000000);
+	new_ranges[11] = new_ranges[9];
+
+	return fdt_setprop_inplace(blob, node, "ranges", new_ranges, len);
+}
+
 void reset_cpu(ulong ignored)
 {
 	/*
diff --git a/arch/arm/mach-mvebu/armada8k/Makefile b/arch/arm/mach-mvebu/armada8k/Makefile
index 82cb25b..0a47567 100644
--- a/arch/arm/mach-mvebu/armada8k/Makefile
+++ b/arch/arm/mach-mvebu/armada8k/Makefile
@@ -2,5 +2,4 @@
 #
 # Copyright (C) 2016 Stefan Roese <sr@denx.de>
 
-obj-y = cpu.o
-obj-y += cache_llc.o
+obj-y = cpu.o cache_llc.o dram.o
diff --git a/arch/arm/mach-mvebu/armada8k/dram.c b/arch/arm/mach-mvebu/armada8k/dram.c
new file mode 100644
index 0000000..265a8b0
--- /dev/null
+++ b/arch/arm/mach-mvebu/armada8k/dram.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Stefan Roese <sr@denx.de>
+ */
+
+#include <common.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+#include <asm/system.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define MV_SIP_DRAM_SIZE	0x82000010
+
+u64 a8k_dram_scan_ap_sz(void)
+{
+	struct pt_regs pregs;
+
+	pregs.regs[0] = MV_SIP_DRAM_SIZE;
+	pregs.regs[1] = SOC_REGS_PHY_BASE;
+	smc_call(&pregs);
+
+	return pregs.regs[0];
+}
+
+int a8k_dram_init_banksize(void)
+{
+	/*
+	 * The firmware (ATF) leaves a 1G whole above the 3G mark for IO
+	 * devices. Higher RAM is mapped at 4G.
+	 *
+	 * Config 2 DRAM banks:
+	 * Bank 0 - max size 4G - 1G
+	 * Bank 1 - ram size - 4G + 1G
+	 */
+	phys_size_t max_bank0_size = SZ_4G - SZ_1G;
+
+	gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
+	if (gd->ram_size <= max_bank0_size) {
+		gd->bd->bi_dram[0].size = gd->ram_size;
+		return 0;
+	}
+
+	gd->bd->bi_dram[0].size = max_bank0_size;
+	if (CONFIG_NR_DRAM_BANKS > 1) {
+		gd->bd->bi_dram[1].start = SZ_4G;
+		gd->bd->bi_dram[1].size = gd->ram_size - max_bank0_size;
+	}
+
+	return 0;
+}
diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h
index 2e2d72a..c3f8ad8 100644
--- a/arch/arm/mach-mvebu/include/mach/cpu.h
+++ b/arch/arm/mach-mvebu/include/mach/cpu.h
@@ -166,10 +166,23 @@
 /* Auto Voltage Scaling */
 #if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X)
 void mv_avs_init(void);
+void mv_rtc_config(void);
 #else
 static inline void mv_avs_init(void) {}
+static inline void mv_rtc_config(void) {}
 #endif
 
+/* A8K dram functions */
+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);
+
+/* A3700 PCIe regions fixer for device tree */
+int a3700_fdt_fix_pcie_regions(void *blob);
+
 /*
  * get_ref_clk
  *
diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c
index 33e7056..66409a5 100644
--- a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c
+++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c
@@ -1366,16 +1366,16 @@
 
 	DEBUG_INIT_S("board SerDes lanes topology details:\n");
 
-	DEBUG_INIT_S(" | Lane #  | Speed |  Type       |\n");
+	DEBUG_INIT_S(" | Lane # | Speed |  Type       |\n");
 	DEBUG_INIT_S(" --------------------------------\n");
 	for (lane_num = 0; lane_num < count; lane_num++) {
 		if (serdes_map[lane_num].serdes_type == DEFAULT_SERDES)
 			continue;
 		DEBUG_INIT_S(" |   ");
 		DEBUG_INIT_D(hws_get_physical_serdes_num(lane_num), 1);
-		DEBUG_INIT_S("    |  ");
+		DEBUG_INIT_S("    |   ");
 		DEBUG_INIT_D(serdes_map[lane_num].serdes_speed, 2);
-		DEBUG_INIT_S("   |  ");
+		DEBUG_INIT_S("   | ");
 		DEBUG_INIT_S((char *)
 			     serdes_type_to_string[serdes_map[lane_num].
 						   serdes_type]);
diff --git a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c
index e9dd096..3c4c7e0 100644
--- a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c
+++ b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c
@@ -257,6 +257,23 @@
 	return (value & (REVISON_ID_MASK)) >> REVISON_ID_OFFS;
 }
 
+void mv_rtc_config(void)
+{
+	u32 i, val;
+
+	if (!(IS_ENABLED(CONFIG_ARMADA_38X) || IS_ENABLED(CONFIG_ARMADA_39X)))
+		return;
+
+	/* Activate pipe0 for read/write transaction, and set XBAR client number #1 */
+	val = 0x1 << DFX_PIPE_SELECT_PIPE0_ACTIVE_OFFS |
+	      0x1 << DFX_PIPE_SELECT_XBAR_CLIENT_SEL_OFFS;
+	writel(val, MVEBU_DFX_BASE);
+
+	/* Set new RTC value for all memory wrappers */
+	for (i = 0; i < RTC_MEMORY_WRAPPER_COUNT; i++)
+		reg_write(RTC_MEMORY_WRAPPER_REG(i), RTC_MEMORY_WRAPPER_CTRL_VAL);
+}
+
 void mv_avs_init(void)
 {
 	u32 sar_freq;
diff --git a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h
index 1774a5b..17cd811 100644
--- a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h
+++ b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h
@@ -150,6 +150,19 @@
 #define MPP_UART1_SET_MASK		(~(0xff000))
 #define MPP_UART1_SET_DATA		(0x66000)
 
+#define DFX_PIPE_SELECT_PIPE0_ACTIVE_OFFS	0
+/* DFX_PIPE_SELECT_XBAR_CLIENT_SEL_OFFS: Since address completion in 14bit
+ * address mode, and given that [14:8] => [19:13], the 2 lower bits [9:8] =>
+ * [14:13] are dismissed. hence field offset is also shifted to 10
+ */
+#define DFX_PIPE_SELECT_XBAR_CLIENT_SEL_OFFS	10
+
+#define RTC_MEMORY_CTRL_REG_BASE	0xE6000
+#define RTC_MEMORY_WRAPPER_COUNT	8
+#define RTC_MEMORY_WRAPPER_REG(i)	(RTC_MEMORY_CTRL_REG_BASE + ((i) * 0x40))
+#define RTC_MEMORY_CTRL_PDLVMC_FIELD_OFFS	6
+#define RTC_MEMORY_WRAPPER_CTRL_VAL	(0x1 << RTC_MEMORY_CTRL_PDLVMC_FIELD_OFFS)
+
 #define AVS_DEBUG_CNTR_REG		0xe4124
 #define AVS_DEBUG_CNTR_DEFAULT_VALUE	0x08008073
 
diff --git a/arch/arm/mach-mvebu/spl.c b/arch/arm/mach-mvebu/spl.c
index a99bf16..70fef3b 100644
--- a/arch/arm/mach-mvebu/spl.c
+++ b/arch/arm/mach-mvebu/spl.c
@@ -130,6 +130,9 @@
 	/* Initialize Auto Voltage Scaling */
 	mv_avs_init();
 
+	/* Update read timing control for PCIe */
+	mv_rtc_config();
+
 	/*
 	 * Return to the BootROM to continue the Marvell xmodem
 	 * UART boot protocol. As initiated by the kwboot tool.
diff --git a/board/CZ.NIC/turris_mox/mox_sp.c b/board/CZ.NIC/turris_mox/mox_sp.c
index 0b29ffc..3c23471 100644
--- a/board/CZ.NIC/turris_mox/mox_sp.c
+++ b/board/CZ.NIC/turris_mox/mox_sp.c
@@ -4,15 +4,17 @@
  */
 
 #include <common.h>
+#include <asm/arch/soc.h>
 #include <asm/io.h>
 
-#define RWTM_CMD_PARAM(i)	(size_t)(0xd00b0000 + (i) * 4)
-#define RWTM_CMD		0xd00b0040
-#define RWTM_CMD_RETSTATUS	0xd00b0080
-#define RWTM_CMD_STATUS(i)	(size_t)(0xd00b0084 + (i) * 4)
+#define RWTM_BASE		(MVEBU_REGISTER(0xb0000))
+#define RWTM_CMD_PARAM(i)	(size_t)(RWTM_BASE + (i) * 4)
+#define RWTM_CMD		(RWTM_BASE + 0x40)
+#define RWTM_CMD_RETSTATUS	(RWTM_BASE + 0x80)
+#define RWTM_CMD_STATUS(i)	(size_t)(RWTM_BASE + 0x84 + (i) * 4)
 
-#define RWTM_HOST_INT_RESET	0xd00b00c8
-#define RWTM_HOST_INT_MASK	0xd00b00cc
+#define RWTM_HOST_INT_RESET	(RWTM_BASE + 0xc8)
+#define RWTM_HOST_INT_MASK	(RWTM_BASE + 0xcc)
 #define SP_CMD_COMPLETE		BIT(0)
 
 #define MBOX_STS_SUCCESS		(0x0 << 30)
diff --git a/board/CZ.NIC/turris_mox/turris_mox.c b/board/CZ.NIC/turris_mox/turris_mox.c
index 377191b..470ea32 100644
--- a/board/CZ.NIC/turris_mox/turris_mox.c
+++ b/board/CZ.NIC/turris_mox/turris_mox.c
@@ -4,18 +4,20 @@
  */
 
 #include <common.h>
-#include <init.h>
-#include <asm/gpio.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
 #include <asm/io.h>
-#include <dm.h>
+#include <asm/gpio.h>
 #include <clk.h>
+#include <dm.h>
 #include <env.h>
-#include <spi.h>
-#include <mvebu/comphy.h>
-#include <miiphy.h>
-#include <linux/string.h>
-#include <linux/libfdt.h>
 #include <fdt_support.h>
+#include <init.h>
+#include <linux/libfdt.h>
+#include <linux/string.h>
+#include <miiphy.h>
+#include <mvebu/comphy.h>
+#include <spi.h>
 
 #include "mox_sp.h"
 
@@ -28,32 +30,20 @@
 #define MOX_MODULE_USB3		0x5
 #define MOX_MODULE_PASSPCI	0x6
 
-#define ARMADA_37XX_NB_GPIO_SEL	0xd0013830
-#define ARMADA_37XX_SPI_CTRL	0xd0010600
-#define ARMADA_37XX_SPI_CFG	0xd0010604
-#define ARMADA_37XX_SPI_DOUT	0xd0010608
-#define ARMADA_37XX_SPI_DIN	0xd001060c
+#define ARMADA_37XX_NB_GPIO_SEL	(MVEBU_REGISTER(0x13830))
+#define ARMADA_37XX_SPI_CTRL	(MVEBU_REGISTER(0x10600))
+#define ARMADA_37XX_SPI_CFG	(MVEBU_REGISTER(0x10604))
+#define ARMADA_37XX_SPI_DOUT	(MVEBU_REGISTER(0x10608))
+#define ARMADA_37XX_SPI_DIN	(MVEBU_REGISTER(0x1060c))
 
+#define ETH1_PATH	"/soc/internal-regs@d0000000/ethernet@40000"
+#define MDIO_PATH	"/soc/internal-regs@d0000000/mdio@32004"
+#define SFP_GPIO_PATH	"/soc/internal-regs@d0000000/spi@10600/moxtet@1/gpio@0"
 #define PCIE_PATH	"/soc/pcie@d0070000"
+#define SFP_PATH	"/sfp"
 
 DECLARE_GLOBAL_DATA_PTR;
 
-int dram_init(void)
-{
-	gd->ram_base = 0;
-	gd->ram_size = (phys_size_t)get_ram_size(0, 0x40000000);
-
-	return 0;
-}
-
-int dram_init_banksize(void)
-{
-	gd->bd->bi_dram[0].start = (phys_addr_t)0;
-	gd->bd->bi_dram[0].size = gd->ram_size;
-
-	return 0;
-}
-
 #if defined(CONFIG_OF_BOARD_FIXUP)
 int board_fix_fdt(void *blob)
 {
@@ -67,9 +57,11 @@
 	 * to read SPI by reading/writing SPI registers directly
 	 */
 
-	writel(0x563fa, ARMADA_37XX_NB_GPIO_SEL);
 	writel(0x10df, ARMADA_37XX_SPI_CFG);
-	writel(0x2005b, ARMADA_37XX_SPI_CTRL);
+	/* put pin from GPIO to SPI mode */
+	clrbits_le32(ARMADA_37XX_NB_GPIO_SEL, BIT(12));
+	/* enable SPI CS1 */
+	setbits_le32(ARMADA_37XX_SPI_CTRL, BIT(17));
 
 	while (!(readl(ARMADA_37XX_SPI_CTRL) & 0x2))
 		udelay(1);
@@ -89,7 +81,8 @@
 
 	size = i;
 
-	writel(0x5b, ARMADA_37XX_SPI_CTRL);
+	/* disable SPI CS1 */
+	clrbits_le32(ARMADA_37XX_SPI_CTRL, BIT(17));
 
 	if (size > 1 && (topology[1] == MOX_MODULE_PCI ||
 			 topology[1] == MOX_MODULE_USB3 ||
@@ -112,6 +105,11 @@
 		return 0;
 	}
 
+	if (a3700_fdt_fix_pcie_regions(blob) < 0) {
+		printf("Cannot fix PCIe regions in U-Boot's device tree!\n");
+		return 0;
+	}
+
 	return 0;
 }
 #endif
@@ -456,24 +454,22 @@
 			}
 			break;
 		case MOX_MODULE_PCI:
-			if (pci) {
+			if (pci)
 				printf("Error: Only one Mini-PCIe module is supported!\n");
-			} else if (usb) {
+			else if (usb)
 				printf("Error: Mini-PCIe module cannot come after USB 3.0 module!\n");
-			} else if (i && (i != 1 || !passpci)) {
+			else if (i && (i != 1 || !passpci))
 				printf("Error: Mini-PCIe module should be the first connected module or come right after Passthrough Mini-PCIe module!\n");
-			} else {
+			else
 				++pci;
-			}
 			break;
 		case MOX_MODULE_TOPAZ:
-			if (topaz) {
+			if (topaz)
 				printf("Error: Only one Topaz module is supported!\n");
-			} else if (peridot >= 3) {
+			else if (peridot >= 3)
 				printf("Error: At most two Peridot modules can come before Topaz module!\n");
-			} else {
+			else
 				++topaz;
-			}
 			break;
 		case MOX_MODULE_PERIDOT:
 			if (sfp || topaz) {
@@ -486,24 +482,22 @@
 			}
 			break;
 		case MOX_MODULE_USB3:
-			if (pci) {
+			if (pci)
 				printf("Error: USB 3.0 module cannot come after Mini-PCIe module!\n");
-			} else if (usb) {
+			else if (usb)
 				printf("Error: Only one USB 3.0 module is supported!\n");
-			} else if (i && (i != 1 || !passpci)) {
+			else if (i && (i != 1 || !passpci))
 				printf("Error: USB 3.0 module should be the first connected module or come right after Passthrough Mini-PCIe module!\n");
-			} else {
+			else
 				++usb;
-			}
 			break;
 		case MOX_MODULE_PASSPCI:
-			if (passpci) {
+			if (passpci)
 				printf("Error: Only one Passthrough Mini-PCIe module is supported!\n");
-			} else if (i != 0) {
+			else if (i != 0)
 				printf("Error: Passthrough Mini-PCIe module should be the first connected module!\n");
-			} else {
+			else
 				++passpci;
-			}
 		}
 	}
 
@@ -548,3 +542,267 @@
 
 	return 0;
 }
+
+#if defined(CONFIG_OF_BOARD_SETUP)
+
+static int vnode_by_path(void *blob, const char *fmt, va_list ap)
+{
+	char path[128];
+
+	vsnprintf(path, 128, fmt, ap);
+	return fdt_path_offset(blob, path);
+}
+
+static int node_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int res;
+
+	va_start(ap, fmt);
+	res = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	return res;
+}
+
+static int phandle_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int node, phandle, res;
+
+	va_start(ap, fmt);
+	node = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	if (node < 0)
+		return node;
+
+	phandle = fdt_get_phandle(blob, node);
+	if (phandle > 0)
+		return phandle;
+
+	phandle = fdt_get_max_phandle(blob);
+	if (phandle < 0)
+		return phandle;
+
+	phandle += 1;
+
+	res = fdt_setprop_u32(blob, node, "linux,phandle", phandle);
+	if (res < 0)
+		return res;
+
+	res = fdt_setprop_u32(blob, node, "phandle", phandle);
+	if (res < 0)
+		return res;
+
+	return phandle;
+}
+
+static int enable_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int node;
+
+	va_start(ap, fmt);
+	node = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	if (node < 0)
+		return node;
+
+	return fdt_setprop_string(blob, node, "status", "okay");
+}
+
+static bool is_topaz(int id)
+{
+	return topaz && id == peridot + topaz - 1;
+}
+
+static int switch_addr(int id)
+{
+	return is_topaz(id) ? 0x2 : 0x10 + id;
+}
+
+static int setup_switch(void *blob, int id)
+{
+	int res, addr, i, node, phandle;
+
+	addr = switch_addr(id);
+
+	/* first enable the switch by setting status = "okay" */
+	res = enable_by_path(blob, MDIO_PATH "/switch%i@%x", id, addr);
+	if (res < 0)
+		return res;
+
+	/*
+	 * now if there are more switches or a SFP module coming after,
+	 * enable corresponding ports
+	 */
+	if (id < peridot + topaz - 1) {
+		res = enable_by_path(blob,
+				     MDIO_PATH "/switch%i@%x/ports/port@a",
+				     id, addr);
+	} else if (id == peridot - 1 && !topaz && sfp) {
+		res = enable_by_path(blob,
+				     MDIO_PATH "/switch%i@%x/ports/port-sfp@a",
+				     id, addr);
+	} else {
+		res = 0;
+	}
+	if (res < 0)
+		return res;
+
+	if (id >= peridot + topaz - 1)
+		return 0;
+
+	/* finally change link property if needed */
+	node = node_by_path(blob, MDIO_PATH "/switch%i@%x/ports/port@a", id,
+			    addr);
+	if (node < 0)
+		return node;
+
+	for (i = id + 1; i < peridot + topaz; ++i) {
+		phandle = phandle_by_path(blob,
+					  MDIO_PATH "/switch%i@%x/ports/port@%x",
+					  i, switch_addr(i),
+					  is_topaz(i) ? 5 : 9);
+		if (phandle < 0)
+			return phandle;
+
+		if (i == id + 1)
+			res = fdt_setprop_u32(blob, node, "link", phandle);
+		else
+			res = fdt_appendprop_u32(blob, node, "link", phandle);
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+static int remove_disabled_nodes(void *blob)
+{
+	while (1) {
+		int res, offset;
+
+		offset = fdt_node_offset_by_prop_value(blob, -1, "status",
+						       "disabled", 9);
+		if (offset < 0)
+			break;
+
+		res = fdt_del_node(blob, offset);
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+int ft_board_setup(void *blob, bd_t *bd)
+{
+	int node, phandle, res;
+
+	/*
+	 * If MOX B (PCI), MOX F (USB) or MOX G (Passthrough PCI) modules are
+	 * connected, enable the PCIe node.
+	 */
+	if (pci || usb || passpci) {
+		node = fdt_path_offset(blob, PCIE_PATH);
+		if (node < 0)
+			return node;
+
+		res = fdt_setprop_string(blob, node, "status", "okay");
+		if (res < 0)
+			return res;
+
+		/* Fix PCIe regions for devices with 4 GB RAM */
+		res = a3700_fdt_fix_pcie_regions(blob);
+		if (res < 0)
+			return res;
+	}
+
+	/*
+	 * If MOX C (Topaz switch) and/or MOX E (Peridot switch) are connected,
+	 * enable the eth1 node and setup the switches.
+	 */
+	if (peridot || topaz) {
+		int i;
+
+		res = enable_by_path(blob, ETH1_PATH);
+		if (res < 0)
+			return res;
+
+		for (i = 0; i < peridot + topaz; ++i) {
+			res = setup_switch(blob, i);
+			if (res < 0)
+				return res;
+		}
+	}
+
+	/*
+	 * If MOX D (SFP cage module) is connected, enable the SFP node and eth1
+	 * node. If there is no Peridot switch between MOX A and MOX D, add link
+	 * to the SFP node to eth1 node.
+	 * Also enable and configure SFP GPIO controller node.
+	 */
+	if (sfp) {
+		res = enable_by_path(blob, SFP_PATH);
+		if (res < 0)
+			return res;
+
+		res = enable_by_path(blob, ETH1_PATH);
+		if (res < 0)
+			return res;
+
+		if (!peridot) {
+			phandle = phandle_by_path(blob, SFP_PATH);
+			if (phandle < 0)
+				return res;
+
+			node = node_by_path(blob, ETH1_PATH);
+			if (node < 0)
+				return node;
+
+			res = fdt_setprop_u32(blob, node, "sfp", phandle);
+			if (res < 0)
+				return res;
+
+			res = fdt_setprop_string(blob, node, "phy-mode",
+						 "sgmii");
+			if (res < 0)
+				return res;
+		}
+
+		res = enable_by_path(blob, SFP_GPIO_PATH);
+		if (res < 0)
+			return res;
+
+		if (sfp_pos) {
+			char newname[16];
+
+			/* moxtet-sfp is on non-zero position, change default */
+			node = node_by_path(blob, SFP_GPIO_PATH);
+			if (node < 0)
+				return node;
+
+			res = fdt_setprop_u32(blob, node, "reg", sfp_pos);
+			if (res < 0)
+				return res;
+
+			sprintf(newname, "gpio@%x", sfp_pos);
+
+			res = fdt_set_name(blob, node, newname);
+			if (res < 0)
+				return res;
+		}
+	}
+
+	fdt_fixup_ethernet(blob);
+
+	/* Finally remove disabled nodes, as per Rob Herring's request. */
+	remove_disabled_nodes(blob);
+
+	return 0;
+}
+
+#endif
diff --git a/board/alliedtelesis/x530/x530.c b/board/alliedtelesis/x530/x530.c
index e0fa806..04b053d 100644
--- a/board/alliedtelesis/x530/x530.c
+++ b/board/alliedtelesis/x530/x530.c
@@ -66,7 +66,11 @@
 	BUS_MASK_32BIT_ECC,		/* subphys mask */
 	MV_DDR_CFG_DEFAULT,		/* ddr configuration data source */
 	{ {0} },			/* raw spd data */
-	{0}				/* timing parameters */
+	{0},				/* timing parameters */
+	{ {0} },			/* electrical configuration */
+	{0},				/* electrical parameters */
+	0,				/* Clock enable mask */
+	160				/* Clock delay */
 };
 
 struct mv_ddr_topology_map *mv_ddr_topology_map_get(void)
diff --git a/board/solidrun/clearfog/Kconfig b/board/solidrun/clearfog/Kconfig
new file mode 100644
index 0000000..e8c3f53
--- /dev/null
+++ b/board/solidrun/clearfog/Kconfig
@@ -0,0 +1,62 @@
+menu "ClearFog configuration"
+	depends on TARGET_CLEARFOG
+
+config TARGET_CLEARFOG_BASE
+	bool "Use ClearFog Base static configuration"
+	help
+	  Use the ClearFog Base as the static configuration instead of the
+	  default which uses the ClearFog Pro.
+
+	  Runtime board detection is always attempted and used if available. The
+	  static configuration is used as a fallback in cases where runtime
+	  detection is disabled, is not available in hardware, or otherwise fails.
+
+	  Only newer revisions of the ClearFog product line support runtime
+	  detection via additional EEPROM hardware. This option enables selecting
+	  the Base variant for older hardware revisions.
+
+config CLEARFOG_CON3_SATA
+	bool "Use CON3 slot in SATA mode"
+	help
+	  Use the CON3 port with SATA protocol instead of the default PCIe.
+	  The ClearFog port allows usage of either mSATA or miniPCIe
+	  modules, but the desired protocol must be configured at build
+	  time since it affects the SerDes topology layout.
+
+config CLEARFOG_CON2_SATA
+	bool "Use CON2 slot in SATA mode"
+	depends on !TARGET_CLEARFOG_BASE
+	help
+	  Use the CON2 port with SATA protocol instead of the default PCIe.
+	  The ClearFog port allows usage of either mSATA or miniPCIe
+	  modules, but the desired protocol must be configured at build
+	  time since it affects the SerDes topology layout.
+
+config CLEARFOG_SFP_25GB
+	bool "Enable 2.5 Gbps mode for SFP"
+	help
+	  Set the SFP module connection to support 2.5 Gbps transfer speed for the
+	  SGMII connection (requires a supporting SFP). By default, transfer speed
+	  of 1.25 Gbps is used, suitable for a more common 1 Gbps SFP module.
+
+config ENV_SIZE
+	hex "Environment Size"
+	default 0x10000
+
+config ENV_OFFSET
+	hex "Environment offset"
+	default 0xF0000
+
+config ENV_SECT_SIZE
+	hex "Environment Sector-Size"
+	# Use SPI flash erase block size of 4 KiB
+	default 0x1000 if MVEBU_SPL_BOOT_DEVICE_SPI
+	# Use optimistic 64 KiB erase block, will vary between actual media
+	default 0x10000 if MVEBU_SPL_BOOT_DEVICE_MMC
+
+config SYS_SPI_U_BOOT_OFFS
+	hex "address of u-boot payload in SPI flash"
+	default 0x20000
+	depends on MVEBU_SPL_BOOT_DEVICE_SPI
+
+endmenu
diff --git a/board/solidrun/clearfog/clearfog.c b/board/solidrun/clearfog/clearfog.c
index e268ef5..443751b 100644
--- a/board/solidrun/clearfog/clearfog.c
+++ b/board/solidrun/clearfog/clearfog.c
@@ -42,6 +42,7 @@
 	read_tlv_data(&cf_tlv_data);
 }
 
+/* The starting board_serdes_map reflects original Clearfog Pro usage */
 static struct serdes_map board_serdes_map[] = {
 	{SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0},
 	{SGMII1, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0},
@@ -51,20 +52,60 @@
 	{SGMII2, SERDES_SPEED_1_25_GBPS, SERDES_DEFAULT_MODE, 0, 0},
 };
 
+void config_cfbase_serdes_map(void)
+{
+	board_serdes_map[4].serdes_type = USB3_HOST0;
+	board_serdes_map[4].serdes_speed = SERDES_SPEED_5_GBPS;
+	board_serdes_map[4].serdes_mode = SERDES_DEFAULT_MODE;
+}
+
 int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count)
 {
 	cf_read_tlv_data();
 
+	/* Apply build configuration options before runtime configuration */
+	if (IS_ENABLED(CONFIG_CLEARFOG_SFP_25GB))
+		board_serdes_map[5].serdes_speed = SERDES_SPEED_3_125_GBPS;
+
+	if (IS_ENABLED(CONFIG_CLEARFOG_CON2_SATA)) {
+		board_serdes_map[4].serdes_type = SATA2;
+		board_serdes_map[4].serdes_speed = SERDES_SPEED_3_GBPS;
+		board_serdes_map[4].serdes_mode = SERDES_DEFAULT_MODE;
+		board_serdes_map[4].swap_rx = 1;
+	}
+
+	if (IS_ENABLED(CONFIG_CLEARFOG_CON3_SATA)) {
+		board_serdes_map[2].serdes_type = SATA1;
+		board_serdes_map[2].serdes_speed = SERDES_SPEED_3_GBPS;
+		board_serdes_map[2].serdes_mode = SERDES_DEFAULT_MODE;
+		board_serdes_map[2].swap_rx = 1;
+	}
+
+	/* Apply runtime detection changes */
 	if (sr_product_is(&cf_tlv_data, "Clearfog GTR")) {
 		board_serdes_map[0].serdes_type = PEX0;
 		board_serdes_map[0].serdes_speed = SERDES_SPEED_5_GBPS;
 		board_serdes_map[0].serdes_mode = PEX_ROOT_COMPLEX_X1;
-	}
-
-	if (sr_product_is(&cf_tlv_data, "Clearfog Base")) {
-		board_serdes_map[4].serdes_type = USB3_HOST0;
-		board_serdes_map[4].serdes_speed = SERDES_SPEED_5_GBPS;
-		board_serdes_map[4].serdes_mode = SERDES_DEFAULT_MODE;
+	} else if (sr_product_is(&cf_tlv_data, "Clearfog Pro")) {
+		/* handle recognized product as noop, no adjustment required */
+	} else if (sr_product_is(&cf_tlv_data, "Clearfog Base")) {
+		config_cfbase_serdes_map();
+	} else {
+		/*
+		 * Fallback to static default. EEPROM TLV support is not
+		 * enabled, runtime detection failed, hardware support is not
+		 * present, EEPROM is corrupt, or an unrecognized product name
+		 * is present.
+		 */
+		if (IS_ENABLED(CONFIG_SPL_CMD_TLV_EEPROM))
+			puts("EEPROM TLV detection failed: ");
+		puts("Using static config for ");
+		if (IS_ENABLED(CONFIG_TARGET_CLEARFOG_BASE)) {
+			puts("Clearfog Base.\n");
+			config_cfbase_serdes_map();
+		} else {
+			puts("Clearfog Pro.\n");
+		}
 	}
 
 	*serdes_map_array = board_serdes_map;
@@ -170,7 +211,9 @@
 
 int checkboard(void)
 {
-	char *board = "ClearFog";
+	char *board = "Clearfog Pro";
+	if (IS_ENABLED(CONFIG_TARGET_CLEARFOG_BASE))
+		board = "Clearfog Base";
 
 	cf_read_tlv_data();
 	if (strlen(cf_tlv_data.tlv_product_name[0]) > 0)
@@ -200,6 +243,10 @@
 		env_set("fdtfile", "armada-385-clearfog-gtr-s4.dtb");
 	else if (sr_product_is(&cf_tlv_data, "Clearfog GTR L8"))
 		env_set("fdtfile", "armada-385-clearfog-gtr-l8.dtb");
+	else if (IS_ENABLED(CONFIG_TARGET_CLEARFOG_BASE))
+		env_set("fdtfile", "armada-388-clearfog-base.dtb");
+	else
+		env_set("fdtfile", "armada-388-clearfog-pro.dtb");
 
 	return 0;
 }
diff --git a/configs/clearfog_defconfig b/configs/clearfog_defconfig
index c938448..6db8b8a 100644
--- a/configs/clearfog_defconfig
+++ b/configs/clearfog_defconfig
@@ -9,8 +9,6 @@
 CONFIG_SYS_MALLOC_F_LEN=0x2000
 CONFIG_TARGET_CLEARFOG=y
 CONFIG_MVEBU_SPL_BOOT_DEVICE_MMC=y
-CONFIG_ENV_SIZE=0x10000
-CONFIG_ENV_OFFSET=0xF0000
 CONFIG_DM_GPIO=y
 CONFIG_SPL_MMC_SUPPORT=y
 CONFIG_SPL_SERIAL_SUPPORT=y
diff --git a/configs/turris_mox_defconfig b/configs/turris_mox_defconfig
index 2e63704..d786255 100644
--- a/configs/turris_mox_defconfig
+++ b/configs/turris_mox_defconfig
@@ -8,7 +8,7 @@
 CONFIG_ENV_SECT_SIZE=0x10000
 CONFIG_ENV_OFFSET=0x180000
 CONFIG_DM_GPIO=y
-CONFIG_NR_DRAM_BANKS=1
+CONFIG_NR_DRAM_BANKS=2
 CONFIG_DEBUG_UART_BASE=0xd0012000
 CONFIG_DEBUG_UART_CLOCK=25804800
 CONFIG_DEBUG_UART=y
@@ -37,6 +37,7 @@
 CONFIG_CMD_EXT4_WRITE=y
 CONFIG_MAC_PARTITION=y
 CONFIG_OF_BOARD_FIXUP=y
+CONFIG_OF_BOARD_SETUP=y
 CONFIG_DEFAULT_DEVICE_TREE="armada-3720-turris-mox"
 CONFIG_ENV_IS_IN_SPI_FLASH=y
 CONFIG_SYS_RELOC_GD_ENV_ADDR=y
diff --git a/drivers/ddr/marvell/a38x/ddr3_init.c b/drivers/ddr/marvell/a38x/ddr3_init.c
index 22c8f9c..a971cc1 100644
--- a/drivers/ddr/marvell/a38x/ddr3_init.c
+++ b/drivers/ddr/marvell/a38x/ddr3_init.c
@@ -106,8 +106,10 @@
 	struct tune_train_params params;
 	int status;
 	u32 cs_num;
+	int ck_delay;
 
 	cs_num = mv_ddr_cs_num_get();
+	ck_delay = mv_ddr_ck_delay_get();
 
 	/* NOTE: do not remove any field initilization */
 	params.ck_delay = TUNE_TRAINING_PARAMS_CK_DELAY;
@@ -131,6 +133,9 @@
 		params.g_odt_config = TUNE_TRAINING_PARAMS_ODT_CONFIG_2CS;
 	}
 
+	if (ck_delay > 0)
+		params.ck_delay = ck_delay;
+
 	status = ddr3_tip_tune_training_params(dev_num, &params);
 	if (MV_OK != status) {
 		printf("%s Training Sequence - FAILED\n", ddr_type);
diff --git a/drivers/ddr/marvell/a38x/ddr_topology_def.h b/drivers/ddr/marvell/a38x/ddr_topology_def.h
index 950f296..34196b1 100644
--- a/drivers/ddr/marvell/a38x/ddr_topology_def.h
+++ b/drivers/ddr/marvell/a38x/ddr_topology_def.h
@@ -127,6 +127,9 @@
 
 	/* Clock enable mask */
 	u32 clk_enable;
+
+	/* Clock delay */
+	int ck_delay;
 };
 
 enum mv_ddr_iface_mode {
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_topology.c b/drivers/ddr/marvell/a38x/mv_ddr_topology.c
index ef3b658..09840b1 100644
--- a/drivers/ddr/marvell/a38x/mv_ddr_topology.c
+++ b/drivers/ddr/marvell/a38x/mv_ddr_topology.c
@@ -229,6 +229,16 @@
 		return 0;
 }
 
+int mv_ddr_ck_delay_get(void)
+{
+	struct mv_ddr_topology_map *tm = mv_ddr_topology_map_get();
+
+	if (tm->ck_delay)
+		return tm->ck_delay;
+
+	return -1;
+}
+
 /* translate topology map definition to real memory size in bits */
 static unsigned int mem_size[] = {
 	ADDR_SIZE_512MB,
diff --git a/drivers/ddr/marvell/a38x/mv_ddr_topology.h b/drivers/ddr/marvell/a38x/mv_ddr_topology.h
index 766f25d..4fca476 100644
--- a/drivers/ddr/marvell/a38x/mv_ddr_topology.h
+++ b/drivers/ddr/marvell/a38x/mv_ddr_topology.h
@@ -319,6 +319,7 @@
 unsigned int mv_ddr_if_bus_width_get(void);
 unsigned int mv_ddr_cs_num_get(void);
 int mv_ddr_is_ecc_ena(void);
+int mv_ddr_ck_delay_get(void);
 unsigned long long mv_ddr_mem_sz_per_cs_get(void);
 unsigned long long mv_ddr_mem_sz_get(void);
 unsigned int mv_ddr_rtt_nom_get(void);
diff --git a/drivers/ddr/marvell/axp/ddr3_init.c b/drivers/ddr/marvell/axp/ddr3_init.c
index 13df912..30ad5d4 100644
--- a/drivers/ddr/marvell/axp/ddr3_init.c
+++ b/drivers/ddr/marvell/axp/ddr3_init.c
@@ -435,10 +435,6 @@
 #endif
 
 #if defined(ECC_SUPPORT) && defined(AUTO_DETECTION_SUPPORT)
-	ecc = DRAM_ECC;
-#endif
-
-#if defined(ECC_SUPPORT) && defined(AUTO_DETECTION_SUPPORT)
 	ecc = 0;
 	if (ddr3_check_config(BUS_WIDTH_ECC_TWSI_ADDR, CONFIG_ECC))
 		ecc = 1;
diff --git a/include/configs/clearfog.h b/include/configs/clearfog.h
index 633187d..8314956 100644
--- a/include/configs/clearfog.h
+++ b/include/configs/clearfog.h
@@ -104,15 +104,59 @@
 #define BOOT_TARGET_DEVICES_MMC(func)
 #endif
 
+#ifdef CONFIG_SCSI
+#define BOOT_TARGET_DEVICES_SCSI(func) func(SCSI, scsi, 0)
+#else
+#define BOOT_TARGET_DEVICES_SCSI(func)
+#endif
+
 #ifdef CONFIG_USB_STORAGE
 #define BOOT_TARGET_DEVICES_USB(func) func(USB, usb, 0)
 #else
 #define BOOT_TARGET_DEVICES_USB(func)
 #endif
 
+#ifndef CONFIG_SCSI
+#define BOOT_TARGET_DEVICES_SCSI_BUS0(func)
+#define BOOT_TARGET_DEVICES_SCSI_BUS1(func)
+#define BOOT_TARGET_DEVICES_SCSI_BUS2(func)
+#else
+/*
+ * With SCSI enabled, M.2 SATA is always located on bus 0
+ */
+#define BOOT_TARGET_DEVICES_SCSI_BUS0(func) func(SCSI, scsi, 0)
+
+/*
+ * Either one or both mPCIe slots may be configured as mSATA interfaces. The
+ * SCSI bus ids are assigned based on sequence of hardware present, not always
+ * tied to hardware slot ids. As such, use second SCSI bus if either slot is
+ * set for SATA, and only use third SCSI bus if both slots are SATA enabled.
+ */
+#if defined (CONFIG_CLEARFOG_CON2_SATA) || defined (CONFIG_CLEARFOG_CON3_SATA)
+#define BOOT_TARGET_DEVICES_SCSI_BUS1(func) func(SCSI, scsi, 1)
+#else
+#define BOOT_TARGET_DEVICES_SCSI_BUS1(func)
+#endif
+
+#if defined (CONFIG_CLEARFOG_CON2_SATA) && defined (CONFIG_CLEARFOG_CON3_SATA)
+#define BOOT_TARGET_DEVICES_SCSI_BUS2(func) func(SCSI, scsi, 2)
+#else
+#define BOOT_TARGET_DEVICES_SCSI_BUS2(func)
+#endif
+
+#endif /* CONFIG_SCSI */
+
+/*
+ * The SCSI buses are attempted in increasing bus order, there is no current
+ * mechanism to alter the default bus priority order for booting.
+ */
 #define BOOT_TARGET_DEVICES(func) \
 	BOOT_TARGET_DEVICES_MMC(func) \
+	BOOT_TARGET_DEVICES_SCSI(func) \
 	BOOT_TARGET_DEVICES_USB(func) \
+	BOOT_TARGET_DEVICES_SCSI_BUS0(func) \
+	BOOT_TARGET_DEVICES_SCSI_BUS1(func) \
+	BOOT_TARGET_DEVICES_SCSI_BUS2(func) \
 	func(PXE, pxe, na) \
 	func(DHCP, dhcp, na)
 
@@ -134,7 +178,6 @@
 #define CONFIG_EXTRA_ENV_SETTINGS \
 	RELOCATION_LIMITS_ENV_SETTINGS \
 	LOAD_ADDRESS_ENV_SETTINGS \
-	"fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \
 	"console=ttyS0,115200\0" \
 	BOOTENV