Add BL2 support for Broadcom stingray platform

Change-Id: I5daa3f2b4b9d85cb857547a588571a9aa8ad05c2
Signed-off-by: Sheetal Tigadoli <sheetal.tigadoli@broadcom.com>
diff --git a/plat/brcm/board/stingray/bcm958742t-ns3.mk b/plat/brcm/board/stingray/bcm958742t-ns3.mk
index f6df3b7..5164eeb 100644
--- a/plat/brcm/board/stingray/bcm958742t-ns3.mk
+++ b/plat/brcm/board/stingray/bcm958742t-ns3.mk
@@ -14,3 +14,9 @@
 ifneq (${BL33_OVERRIDE_LOAD_ADDR},)
 $(eval $(call add_define_val,BL33_OVERRIDE_LOAD_ADDR,0xFF000000))
 endif
+
+# Nitro DDR secure memory
+# Nitro FW and config 0x8AE00000 - 0x8B000000
+# Nitro Crash dump 0x8B000000 - 0x8D000000
+DDR_NITRO_SECURE_REGION_START := 0x8AE00000
+DDR_NITRO_SECURE_REGION_END := 0x8D000000
diff --git a/plat/brcm/board/stingray/driver/ddr/soc/include/board_family.h b/plat/brcm/board/stingray/driver/ddr/soc/include/board_family.h
new file mode 100644
index 0000000..b2427cf
--- /dev/null
+++ b/plat/brcm/board/stingray/driver/ddr/soc/include/board_family.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef BOARD_FAMILY_H
+#define BOARD_FAMILY_H
+
+#if defined(DRIVER_SPD_ENABLE) && !defined(DRIVER_SPD_SPOOF)
+#include <spd.h>
+#endif
+
+#ifdef USE_GPIO
+/* max number of supported GPIOs to construct the bitmap for board detection */
+#define MAX_NR_GPIOS           4
+
+/* max GPIO bitmap value */
+#define MAX_GPIO_BITMAP_VAL    (BIT(MAX_NR_GPIOS) - 1)
+#endif
+
+struct mcb_ref_group {
+	uint32_t mcb_ref;
+	unsigned int *mcb_cfg;
+};
+
+#define MCB_REF_GROUP(ref)		\
+{					\
+	.mcb_ref =  0x ## ref,		\
+	.mcb_cfg = mcb_ ## ref,		\
+}
+
+#endif
diff --git a/plat/brcm/board/stingray/driver/ext_sram_init/ext_sram_init.c b/plat/brcm/board/stingray/driver/ext_sram_init/ext_sram_init.c
new file mode 100644
index 0000000..74d2077
--- /dev/null
+++ b/plat/brcm/board/stingray/driver/ext_sram_init/ext_sram_init.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2016-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <arch_helpers.h>
+#include <common/bl_common.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include <platform_def.h>
+
+static void brcm_stingray_pnor_pinmux_init(void)
+{
+	unsigned int i;
+
+	INFO(" - pnor pinmux init start.\n");
+
+	/* Set PNOR_ADV_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2dc),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_BAA_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2e0),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_BLS_0_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2e4),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_BLS_1_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2e8),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_CRE_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2ec),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_CS_2_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2f0),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_CS_1_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2f4),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_CS_0_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2f8),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_WE_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2fc),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_OE_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x300),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_INTR_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x304),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set PNOR_DAT_x_MODE_SEL_CONTROL.fsel = 0x2 */
+	for (i = 0; i < 0x40; i += 0x4) {
+		mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x308 + i),
+				   MODE_SEL_CONTROL_FSEL_MASK,
+				   MODE_SEL_CONTROL_FSEL_MODE2);
+	}
+
+	/* Set NAND_CE1_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x348),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set NAND_CE0_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x34c),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set NAND_WE_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x350),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set NAND_WP_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x354),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set NAND_RE_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x358),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set NAND_RDY_BSY_N_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x35c),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set NAND_IOx_0_MODE_SEL_CONTROL.fsel = 0x2 */
+	for (i = 0; i < 0x40; i += 0x4) {
+		mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x360 + i),
+				   MODE_SEL_CONTROL_FSEL_MASK,
+				   MODE_SEL_CONTROL_FSEL_MODE2);
+	}
+
+	/* Set NAND_ALE_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x3a0),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	/* Set NAND_CLE_MODE_SEL_CONTROL.fsel = 0x2 */
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x3a4),
+			   MODE_SEL_CONTROL_FSEL_MASK,
+			   MODE_SEL_CONTROL_FSEL_MODE2);
+
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x40), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x44), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x48), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x4c), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x50), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x54), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x58), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x5c), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x60), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x64), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x68), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x6c), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x70), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x74), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x78), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x7c), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x80), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x84), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x88), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x8c), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x90), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x94), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x98), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x9c), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0xa0), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0xa4), (7 << 1), 0x8);
+	mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0xa8), (7 << 1), 0x8);
+
+	INFO(" - pnor pinmux init done.\n");
+}
+
+#if BL2_TEST_EXT_SRAM
+#define SRAM_CHECKS_GRANUL	0x100000
+#define SRAM_CHECKS_CNT		8
+static unsigned int sram_checks[SRAM_CHECKS_CNT] = {
+	/* offset, magic */
+	0xd00dfeed,
+	0xfadebabe,
+	0xc001d00d,
+	0xa5a5b5b5,
+	0x5a5a5b5b,
+	0xc5c5d5d5,
+	0x5c5c5d5d,
+	0xe5e5f5f5,
+};
+#endif
+
+static void brcm_stingray_pnor_sram_init(void)
+{
+	unsigned int val, tmp;
+#if BL2_TEST_EXT_SRAM
+	unsigned int off, i;
+#endif
+	INFO(" - pnor sram init start.\n");
+
+	/* Enable PNOR Clock */
+	INFO(" -- enable pnor clock\n");
+	mmio_write_32((uintptr_t)(PNOR_IDM_IO_CONTROL_DIRECT), 0x1);
+	udelay(500);
+
+	/* Reset PNOR */
+	INFO(" -- reset pnor\n");
+	mmio_setbits_32((uintptr_t)(PNOR_IDM_IO_RESET_CONTROL), 0x1);
+	udelay(500);
+	mmio_clrbits_32((uintptr_t)(PNOR_IDM_IO_RESET_CONTROL), 0x1);
+	udelay(500);
+
+	/* Configure slave address to chip-select mapping */
+	INFO(" -- configure pnor slave address to chip-select mapping\n");
+	/* 0x74000000-0x75ffffff => CS0 (32MB) */
+	val = (0xfe << PNOR_ICFG_CS_x_MASK0_SHIFT);
+	val |= (0x74);
+	mmio_write_32((uintptr_t)(PNOR_ICFG_CS_0), val);
+	/* 0x76000000-0x77ffffff => CS1 (32MB) */
+	val = (0xfe << PNOR_ICFG_CS_x_MASK0_SHIFT);
+	val |= (0x76);
+	mmio_write_32((uintptr_t)(PNOR_ICFG_CS_1), val);
+	/* 0xffffffff-0xffffffff => CS2 (0MB) */
+	val = (0x00 << PNOR_ICFG_CS_x_MASK0_SHIFT);
+	val |= (0xff);
+	mmio_write_32((uintptr_t)(PNOR_ICFG_CS_2), val);
+
+	/* Print PNOR ID */
+	tmp = 0x0;
+	val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID0));
+	tmp |= (val & PNOR_REG_PERIPH_IDx_MASK);
+	val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID1));
+	tmp |= ((val & PNOR_REG_PERIPH_IDx_MASK) << 8);
+	val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID2));
+	tmp |= ((val & PNOR_REG_PERIPH_IDx_MASK) << 16);
+	val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID3));
+	tmp |= ((val & PNOR_REG_PERIPH_IDx_MASK) << 24);
+	INFO(" -- pnor primecell_id = 0x%x\n", tmp);
+
+	/* PNOR set_cycles */
+#ifdef EMULATION_SETUP
+	val = 0x00129A44;
+#else
+	val = 0x00125954; /* 0x00002DEF; */
+#endif
+	mmio_write_32((uintptr_t)(PNOR_REG_SET_CYCLES), val);
+	INFO(" -- pnor set_cycles = 0x%x\n", val);
+
+	/* PNOR set_opmode */
+	val = 0x0;
+#ifdef EMULATION_SETUP
+	/* TODO: Final values to be provided by DV folks */
+	val &= ~(0x7 << 7); /* set_wr_bl */
+	val &= ~(0x7 << 3);  /* set_rd_bl */
+	val &= ~(0x3);
+	val |= (0x1); /* set_mw */
+#else
+	/* TODO: Final values to be provided by DV folks */
+	val &= ~(0x7 << 7); /* set_wr_bl */
+	val &= ~(0x7 << 3);  /* set_rd_bl */
+	val &= ~(0x3);
+	val |= (0x1); /* set_mw */
+#endif
+	mmio_write_32((uintptr_t)(PNOR_REG_SET_OPMODE), val);
+	INFO(" -- pnor set_opmode = 0x%x\n", val);
+
+#ifndef EMULATION_SETUP
+	/* Actual SRAM chip will require self-refresh */
+	val = 0x1;
+	mmio_write_32((uintptr_t)(PNOR_REG_REFRESH_0), val);
+	INFO(" -- pnor refresh_0 = 0x%x\n", val);
+#endif
+
+#if BL2_TEST_EXT_SRAM
+	/* Check PNOR SRAM access */
+	for (off = 0; off < NOR_SIZE; off += SRAM_CHECKS_GRANUL) {
+		i = (off / SRAM_CHECKS_GRANUL) % SRAM_CHECKS_CNT;
+		val = sram_checks[i];
+		INFO(" -- pnor sram write addr=0x%lx value=0x%lx\n",
+		     (unsigned long)(NOR_BASE_ADDR + off),
+		     (unsigned long)val);
+		mmio_write_32((uintptr_t)(NOR_BASE_ADDR + off), val);
+	}
+	tmp = 0;
+	for (off = 0; off < NOR_SIZE; off += SRAM_CHECKS_GRANUL) {
+		i = (off / SRAM_CHECKS_GRANUL) % SRAM_CHECKS_CNT;
+		val = mmio_read_32((uintptr_t)(NOR_BASE_ADDR + off));
+		INFO(" -- pnor sram read addr=0x%lx value=0x%lx\n",
+		     (unsigned long)(NOR_BASE_ADDR + off),
+		     (unsigned long)val);
+		if (val == sram_checks[i])
+			tmp++;
+	}
+	INFO(" -- pnor sram checks pass=%d total=%d\n",
+	     tmp, (NOR_SIZE / SRAM_CHECKS_GRANUL));
+
+	if (tmp != (NOR_SIZE / SRAM_CHECKS_GRANUL)) {
+		INFO(" - pnor sram init failed.\n");
+		while (1)
+			;
+	} else {
+		INFO(" - pnor sram init done.\n");
+	}
+#endif
+}
+
+void ext_sram_init(void)
+{
+	INFO("%s start.\n", __func__);
+
+	brcm_stingray_pnor_pinmux_init();
+
+	brcm_stingray_pnor_sram_init();
+
+	INFO("%s done.\n", __func__);
+}
diff --git a/plat/brcm/board/stingray/driver/ext_sram_init/ext_sram_init.h b/plat/brcm/board/stingray/driver/ext_sram_init/ext_sram_init.h
new file mode 100644
index 0000000..8508653
--- /dev/null
+++ b/plat/brcm/board/stingray/driver/ext_sram_init/ext_sram_init.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2016-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef EXT_SRAM_INIT_H
+#define EXT_SRAM_INIT_H
+
+void ext_sram_init(void);
+#endif
diff --git a/plat/brcm/board/stingray/driver/swreg.c b/plat/brcm/board/stingray/driver/swreg.c
new file mode 100644
index 0000000..2b7c53b
--- /dev/null
+++ b/plat/brcm/board/stingray/driver/swreg.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include <sr_utils.h>
+#include <swreg.h>
+
+#define MIN_VOLT                760000
+#define MAX_VOLT                1060000
+
+#define BSTI_WRITE              0x1
+#define BSTI_READ               0x2
+#define BSTI_COMMAND_TA         0x2
+#define BSTI_COMMAND_DATA       0xFF
+#define BSTI_CONTROL_VAL        0x81
+#define BSTI_CONTROL_BUSY       0x100
+#define BSTI_TOGGLE_BIT         0x2
+#define BSTI_CONFI_DONE_MASK    0xFFFFFFFD
+#define BSTI_REG_DATA_MASK      0xFFFF
+#define BSTI_CMD(sb, op, pa, ra, ta, data) \
+	((((sb) & 0x3) << 30) | (((op) & 0x3) << 28) | \
+	(((pa) & 0x1F) << 23) | (((ra) & 0x1F) << 18) | \
+	(((ta) & 0x3) << 16) | (data))
+
+#define PHY_REG0        0x0
+#define PHY_REG1        0x1
+#define PHY_REG4        0x4
+#define PHY_REG5        0x5
+#define PHY_REG6        0x6
+#define PHY_REG7        0x7
+#define PHY_REGC        0xc
+
+#define IHOST_VDDC_DATA 0x560
+#define DDR_CORE_DATA   0x2560
+#define UPDATE_POS_EDGE(data, set)    ((data) | ((set) << 1))
+
+/*
+ * Formula for SR A2 reworked board:
+ * step = ((vol/(1.4117 * 0.98)) - 500000)/3125
+ * where,
+ *      vol    - input voltage
+ *      500000 - Reference voltage
+ *      3125   - one step value
+ */
+#define A2_VOL_REF         500000
+#define ONE_STEP_VALUE  3125
+#define VOL_DIV(vol)    (((vol*10000ull)/(14117*98ull)) * 100ull)
+#define STEP_VALUE(vol) \
+	((((((VOL_DIV(vol)) - A2_VOL_REF) / ONE_STEP_VALUE) & 0xFF) << 8) | 4)
+
+#define B0_VOL_REF         ((500000/100)*98)
+#define B0_ONE_STEP_VALUE  3125
+/*
+ * Formula for SR B0 chip for IHOST12/03 and VDDC_CORE
+ * step = ((vol/1.56) - (500000 * 0.98))/3125
+ * where,
+ *      vol    - input voltage
+ *      500000 - Reference voltage
+ *      3125   - one step value
+ */
+#define B0_VOL_DIV(vol)    (((vol)*100ull)/156)
+#define B0_STEP_VALUE(vol) \
+	((((((B0_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
+		& 0xFF) << 8) | 4)
+
+/*
+ * Formula for SR B0 chip for DDR-CORE
+ * step = ((vol/1) - (500000 * 0.98))/3125
+ * where,
+ *      vol    - input voltage
+ *      500000 - Reference voltage
+ *      3125   - one step value
+ */
+#define B0_DDR_VDDC_VOL_DIV(vol)    ((vol)/1)
+#define B0_DDR_VDDC_STEP_VALUE(vol) \
+	((((((B0_DDR_VDDC_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
+		& 0xFF) << 8) | 4)
+
+#define MAX_SWREG_CNT       8
+#define MAX_ADDR_PER_SWREG  16
+#define MAX_REG_ADDR        0xF
+#define MIN_REG_ADDR        0x0
+
+static const char *sw_reg_name[MAX_SWREG_CNT] = {
+	"DDR_VDDC",
+	"IHOST03",
+	"IHOST12",
+	"IHOST_ARRAY",
+	"DDRIO_SLAVE",
+	"VDDC_CORE",
+	"VDDC1",
+	"DDRIO_MASTER"
+};
+
+/* firmware values for all SWREG for 3.3V input operation */
+static const uint16_t swreg_fm_data_bx[MAX_SWREG_CNT][MAX_ADDR_PER_SWREG] = {
+	/* DDR logic: Power Domains independent of 12v or 3p3v */
+	{0x25E0, 0x2D54, 0x0EC6, 0x01EC, 0x28BB, 0x1144, 0x0200, 0x69C0,
+	 0x0010, 0x0EDF, 0x90D7, 0x8000, 0x820C, 0x0003, 0x0001, 0x0000},
+
+	/* ihost03, 3p3V */
+	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
+	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
+
+	/* ihost12 3p3v */
+	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
+	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
+
+	/* ihost array */
+	{0x25E0, 0x2D94, 0x0EC6, 0x01EC, 0x2ABB, 0x1144, 0x0340, 0x69C0,
+	 0x0010, 0x0EDF, 0x90D7, 0x8000, 0x860C, 0x0003, 0x0001, 0x0000},
+
+	/* ddr io slave : 3p3v */
+	{0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
+	 0x003F, 0x0FFF, 0x10D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},
+
+	/* core master 3p3v */
+	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
+	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
+
+	/* core slave 3p3v */
+	{0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
+	 0x003F, 0x0FFF, 0x10D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
+
+	/* ddr io master : 3p3v */
+	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
+	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},
+};
+
+#define FM_DATA swreg_fm_data_bx
+
+static int swreg_poll(void)
+{
+	uint32_t data;
+	int retry = 100;
+
+	do {
+		data = mmio_read_32(BSTI_CONTROL_OFFSET);
+		if ((data & BSTI_CONTROL_BUSY) != BSTI_CONTROL_BUSY)
+			return 0;
+		retry--;
+		udelay(1);
+	} while (retry > 0);
+
+	return -ETIMEDOUT;
+}
+
+static int write_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t data)
+{
+	uint32_t cmd;
+	int ret;
+
+	cmd = BSTI_CMD(0x1, BSTI_WRITE, reg_id, addr, BSTI_COMMAND_TA, data);
+	mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
+	mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
+	ret = swreg_poll();
+	if (ret) {
+		ERROR("Failed to write swreg %s addr 0x%x\n",
+			sw_reg_name[reg_id-1], addr);
+		return ret;
+	}
+	return ret;
+}
+
+static int read_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t *data)
+{
+	uint32_t cmd;
+	int ret;
+
+	cmd = BSTI_CMD(0x1, BSTI_READ, reg_id, addr, BSTI_COMMAND_TA, PHY_REG0);
+	mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
+	mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
+	ret = swreg_poll();
+	if (ret) {
+		ERROR("Failed to read swreg %s addr 0x%x\n",
+			sw_reg_name[reg_id-1], addr);
+		return ret;
+	}
+
+	*data = mmio_read_32(BSTI_COMMAND_OFFSET);
+	*data &= BSTI_REG_DATA_MASK;
+	return ret;
+}
+
+static int swreg_config_done(enum sw_reg reg_id)
+{
+	uint32_t read_data;
+	int ret;
+
+	ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
+	if (ret)
+		return ret;
+
+	read_data &= BSTI_CONFI_DONE_MASK;
+	read_data |= BSTI_TOGGLE_BIT;
+	ret = write_swreg_config(reg_id, PHY_REG0, read_data);
+	if (ret)
+		return ret;
+
+	ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
+	if (ret)
+		return ret;
+
+	read_data &= BSTI_CONFI_DONE_MASK;
+	ret = write_swreg_config(reg_id, PHY_REG0, read_data);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+#ifdef DUMP_SWREG
+static void dump_swreg_firmware(void)
+{
+	enum sw_reg reg_id;
+	uint32_t data;
+	int addr;
+	int ret;
+
+	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
+		INFO("SWREG: %s\n", sw_reg_name[reg_id - 1]);
+		for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
+			ret = read_swreg_config(reg_id, addr, &data);
+			if (ret)
+				ERROR("Failed to read offset %d\n", addr);
+			INFO("\t0x%x: 0x%04x\n", addr, data);
+		}
+	}
+}
+#endif
+
+int set_swreg(enum sw_reg reg_id, uint32_t micro_volts)
+{
+	uint32_t step, programmed_step;
+	uint32_t data = IHOST_VDDC_DATA;
+	int ret;
+
+	if ((micro_volts > MAX_VOLT) || (micro_volts < MIN_VOLT)) {
+		ERROR("input voltage out-of-range\n");
+		ret = -EINVAL;
+		goto failed;
+	}
+
+	ret = read_swreg_config(reg_id, PHY_REGC, &programmed_step);
+	if (ret)
+		goto failed;
+
+	if (reg_id == DDR_VDDC)
+		step = B0_DDR_VDDC_STEP_VALUE(micro_volts);
+	else
+		step = B0_STEP_VALUE(micro_volts);
+
+	if ((step >> 8) != (programmed_step >> 8)) {
+		ret = write_swreg_config(reg_id, PHY_REGC, step);
+		if (ret)
+			goto failed;
+
+		if (reg_id == DDR_VDDC)
+			data = DDR_CORE_DATA;
+
+		ret = write_swreg_config(reg_id, PHY_REG0,
+					UPDATE_POS_EDGE(data, 1));
+		if (ret)
+			goto failed;
+
+		ret = write_swreg_config(reg_id, PHY_REG0,
+					UPDATE_POS_EDGE(data, 0));
+		if (ret)
+			goto failed;
+	}
+
+	INFO("%s voltage updated to %duV\n", sw_reg_name[reg_id-1],
+		micro_volts);
+	return ret;
+
+failed:
+	/*
+	 * Stop booting if voltages are not set
+	 * correctly. Booting will fail at random point
+	 * if we continue with wrong voltage settings.
+	 */
+	ERROR("Failed to set %s voltage to %duV\n", sw_reg_name[reg_id-1],
+		micro_volts);
+	assert(0);
+
+	return ret;
+}
+
+/* Update SWREG firmware for all power doman for A2 chip */
+int swreg_firmware_update(void)
+{
+	enum sw_reg reg_id;
+	uint32_t data;
+	int addr;
+	int ret;
+
+	/* write firmware values */
+	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
+		/* write higher location first */
+		for (addr = MAX_REG_ADDR; addr >= MIN_REG_ADDR; addr--) {
+			ret = write_swreg_config(reg_id, addr,
+						 FM_DATA[reg_id - 1][addr]);
+			if (ret)
+				goto exit;
+		}
+	}
+
+	/* trigger SWREG firmware update */
+	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
+		/*
+		 * Slave regulator doesn't have to be updated,
+		 * Updating Master is enough
+		 */
+		if ((reg_id == DDRIO_SLAVE) || (reg_id == VDDC1))
+			continue;
+
+		ret = swreg_config_done(reg_id);
+		if (ret) {
+			ERROR("Failed to trigger SWREG firmware update for %s\n"
+				, sw_reg_name[reg_id-1]);
+			return ret;
+		}
+	}
+
+	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
+		/*
+		 * IHOST_ARRAY will be used on some boards like STRATUS and
+		 * there will not be any issue even if it is updated on other
+		 * boards where it is not used.
+		 */
+		if (reg_id == IHOST_ARRAY)
+			continue;
+
+		for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
+			ret = read_swreg_config(reg_id, addr, &data);
+			if (ret || (!ret &&
+				(data != FM_DATA[reg_id - 1][addr]))) {
+				ERROR("swreg fm update failed: %s at off %d\n",
+					sw_reg_name[reg_id - 1], addr);
+				ERROR("Read val: 0x%x, expected val: 0x%x\n",
+					data, FM_DATA[reg_id - 1][addr]);
+				return -1;
+			}
+		}
+	}
+
+	INFO("Updated SWREG firmware\n");
+
+#ifdef DUMP_SWREG
+	dump_swreg_firmware();
+#endif
+	return ret;
+
+exit:
+	/*
+	 * Stop booting if swreg firmware update fails.
+	 * Booting will fail at random point if we
+	 * continue with wrong voltage settings.
+	 */
+	ERROR("Failed to update firmware for %s SWREG\n",
+		sw_reg_name[reg_id-1]);
+	assert(0);
+
+	return ret;
+}
diff --git a/plat/brcm/board/stingray/include/board_info.h b/plat/brcm/board/stingray/include/board_info.h
new file mode 100644
index 0000000..8901259
--- /dev/null
+++ b/plat/brcm/board/stingray/include/board_info.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef BOARD_INFO_H
+#define BOARD_INFO_H
+
+#define IHOST_REG_INTEGRATED 0
+#define IHOST_REG_EXT_PROGRAMMABLE 1
+#define IHOST_REG_EXT_FIXED 2
+
+#if defined(IHOST_REG_TYPE)
+	#if ((IHOST_REG_TYPE != IHOST_REG_INTEGRATED) && \
+		(IHOST_REG_TYPE != IHOST_REG_EXT_PROGRAMMABLE) && \
+		(IHOST_REG_TYPE != IHOST_REG_EXT_FIXED))
+		#error "IHOST_REG_TYPE not valid"
+	#endif
+#else
+	#define IHOST_REG_TYPE IHOST_REG_INTEGRATED
+#endif
+
+#define VDDC_REG_INTEGRATED 0
+#define VDDC_REG_EXT_PROGRAMMABLE 1
+#define VDDC_REG_EXT_FIXED 2
+
+#if defined(VDDC_REG_TYPE)
+	#if ((VDDC_REG_TYPE != VDDC_REG_INTEGRATED) && \
+		(VDDC_REG_TYPE != VDDC_REG_EXT_PROGRAMMABLE) && \
+		(VDDC_REG_TYPE != VDDC_REG_EXT_FIXED))
+		#error "VDDC_REG_TYPE not valid"
+	#endif
+#else
+	#define VDDC_REG_TYPE VDDC_REG_INTEGRATED
+#endif
+
+#endif
diff --git a/plat/brcm/board/stingray/include/ddr_init.h b/plat/brcm/board/stingray/include/ddr_init.h
new file mode 100644
index 0000000..0c135b1
--- /dev/null
+++ b/plat/brcm/board/stingray/include/ddr_init.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DDR_INIT_H
+#define DDR_INIT_H
+
+#include <fru.h>
+
+#pragma weak ddr_initialize
+#pragma weak ddr_secure_region_config
+#pragma weak ddr_info_save
+#pragma weak get_active_ddr_channel
+#pragma weak is_warmboot
+
+void ddr_initialize(struct ddr_info *ddr)
+{
+}
+
+void ddr_secure_region_config(uint64_t start, uint64_t end)
+{
+}
+
+void ddr_info_save(void)
+{
+}
+
+unsigned char get_active_ddr_channel(void)
+{
+	return 0;
+}
+
+static inline unsigned int is_warmboot(void)
+{
+	return 0;
+}
+#endif
diff --git a/plat/brcm/board/stingray/include/platform_def.h b/plat/brcm/board/stingray/include/platform_def.h
index d61a737..4742124 100644
--- a/plat/brcm/board/stingray/include/platform_def.h
+++ b/plat/brcm/board/stingray/include/platform_def.h
@@ -12,10 +12,8 @@
 #include <plat/common/common_def.h>
 
 #include <brcm_def.h>
-
-#include <cmn_plat_def.h>
-
 #include "sr_def.h"
+#include <cmn_plat_def.h>
 
 /*
  * Most platform porting definitions provided by included headers
diff --git a/plat/brcm/board/stingray/include/platform_sotp.h b/plat/brcm/board/stingray/include/platform_sotp.h
new file mode 100644
index 0000000..0389f38
--- /dev/null
+++ b/plat/brcm/board/stingray/include/platform_sotp.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLATFORM_SOTP_H
+#define PLATFORM_SOTP_H
+
+#define SOTP_DEVICE_SECURE_CFG0_ROW 17
+#define SOTP_DEVICE_SECURE_CFG1_ROW 18
+#define SOTP_DEVICE_SECURE_CFG2_ROW 19
+#define SOTP_DEVICE_SECURE_CFG3_ROW 20
+#define SOTP_BRCM_SOFTWARE_CFG0_ROW 21
+#define SOTP_BRCM_SOFTWARE_CFG1_ROW 22
+#define SOTP_BRCM_SOFTWARE_CFG2_ROW 23
+#define SOTP_BRCM_SOFTWARE_CFG3_ROW 24
+#define SOTP_CUSTOMER_ID_CFG0_ROW 25
+#define SOTP_CUSTOMER_ID_CFG1_ROW 26
+#define SOTP_CUSTOMER_ID_CFG2_ROW 27
+#define SOTP_CUSTOMER_ID_CFG3_ROW 28
+#define SOTP_CUSTOMER_DEV_CFG0_ROW 29
+#define SOTP_CUSTOMER_DEV_CFG1_ROW 30
+#define SOTP_CUSTOMER_DEV_CFG2_ROW 31
+#define SOTP_CUSTOMER_DEV_CFG3_ROW 32
+#define SOTP_DAUTH_ROW 33
+#define SOTP_K_HMAC_ROW 45
+#define SOTP_K_AES_ROW 57
+#define SOTP_NVCOUNTER_ROW 69
+
+#define SOTP_BRCM_CFG_ECC_ERROR_MASK 0x100000
+#define SOTP_DAUTH_ECC_ERROR_MASK 0x800000
+#define SOTP_K_HMAC_ECC_ERROR_MASK 0x1000000
+#define SOTP_K_AES_ECC_ERROR_MASK 0x2000000
+
+#endif
diff --git a/plat/brcm/board/stingray/include/scp_cmd.h b/plat/brcm/board/stingray/include/scp_cmd.h
new file mode 100644
index 0000000..806ef56
--- /dev/null
+++ b/plat/brcm/board/stingray/include/scp_cmd.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SCP_CMD_H
+#define SCP_SMD_H
+
+#include <stdint.h>
+
+typedef struct {
+	int cmd;
+	int completed;
+	int ret;
+} crmu_response_t;
+
+
+#define SCP_CMD_MASK 0xffff
+#define SCP_CMD_DEFAULT_TIMEOUT_US 1000
+#define SCP_CMD_SCP_BOOT_TIMEOUT_US 5000
+
+int scp_send_cmd(uint32_t cmd, uint32_t param, uint32_t timeout);
+
+#endif
diff --git a/plat/brcm/board/stingray/include/scp_utils.h b/plat/brcm/board/stingray/include/scp_utils.h
new file mode 100644
index 0000000..c39b18c
--- /dev/null
+++ b/plat/brcm/board/stingray/include/scp_utils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SCP_UTILS_H
+#define SCP_UTILS_H
+
+#include <common/bl_common.h>
+#include <lib/mmio.h>
+
+#include <m0_cfg.h>
+
+int plat_bcm_bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info);
+
+bool is_crmu_alive(void);
+bool bcm_scp_issue_sys_reset(void);
+
+#define SCP_READ_CFG(cfg) mmio_read_32(CRMU_CFG_BASE + \
+		offsetof(M0CFG, cfg))
+#define SCP_WRITE_CFG(cfg, value) mmio_write_32(CRMU_CFG_BASE + \
+		offsetof(M0CFG, cfg), value)
+
+#define SCP_READ_CFG16(cfg) mmio_read_16(CRMU_CFG_BASE + \
+		offsetof(M0CFG, cfg))
+#define SCP_WRITE_CFG16(cfg, value) mmio_write_16(CRMU_CFG_BASE + \
+		offsetof(M0CFG, cfg), value)
+
+#define SCP_READ_CFG8(cfg) mmio_read_8(CRMU_CFG_BASE + \
+		offsetof(M0CFG, cfg))
+#define SCP_WRITE_CFG8(cfg, value) mmio_write_8(CRMU_CFG_BASE + \
+		offsetof(M0CFG, cfg), value)
+#endif
diff --git a/plat/brcm/board/stingray/include/sr_utils.h b/plat/brcm/board/stingray/include/sr_utils.h
new file mode 100644
index 0000000..b3fc735
--- /dev/null
+++ b/plat/brcm/board/stingray/include/sr_utils.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SR_UTILS_H
+#define SR_UTILS_H
+
+#include <lib/mmio.h>
+
+#include <chip_id.h>
+#include <cmn_plat_util.h>
+#include <sr_def.h>
+
+static inline void brcm_stingray_set_qspi_mux(int enable_ap)
+{
+	mmio_write_32(QSPI_HOLD_N_MODE_SEL_CONTROL, enable_ap);
+	mmio_write_32(QSPI_WP_N_MODE_SEL_CONTROL, enable_ap);
+	mmio_write_32(QSPI_SCK_MODE_SEL_CONTROL, enable_ap);
+	mmio_write_32(QSPI_CS_N_MODE_SEL_CONTROL, enable_ap);
+	mmio_write_32(QSPI_MOSI_MODE_SEL_CONTROL, enable_ap);
+	mmio_write_32(QSPI_MISO_MODE_SEL_CONTROL, enable_ap);
+}
+
+static inline void brcm_stingray_set_straps(uint32_t boot_source)
+{
+	/* Enable software strap override */
+	mmio_setbits_32(CDRU_CHIP_STRAP_CTRL,
+			BIT(CDRU_CHIP_STRAP_CTRL__SOFTWARE_OVERRIDE));
+
+	/* set straps to the next boot source */
+	mmio_clrsetbits_32(CDRU_CHIP_STRAP_DATA,
+			   BOOT_SOURCE_MASK,
+			   boot_source);
+
+	/* Disable software strap override */
+	mmio_clrbits_32(CDRU_CHIP_STRAP_CTRL,
+			BIT(CDRU_CHIP_STRAP_CTRL__SOFTWARE_OVERRIDE));
+}
+
+#endif
diff --git a/plat/brcm/board/stingray/include/swreg.h b/plat/brcm/board/stingray/include/swreg.h
new file mode 100644
index 0000000..6e971ce
--- /dev/null
+++ b/plat/brcm/board/stingray/include/swreg.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SWREG_H
+#define SWREG_H
+
+/* default voltage if no valid OTP */
+#define VDDC_CORE_DEF_VOLT      910000  /* 0.91v */
+#define IHOST_DEF_VOLT          940000  /* 0.94v */
+
+#define B0_VDDC_CORE_DEF_VOLT   950000  /* 0.95v */
+#define B0_IHOST_DEF_VOLT       950000  /* 0.95v */
+#define B0_DDR_VDDC_DEF_VOLT    1000000 /* 1v */
+
+#define SWREG_IHOST1_DIS        4
+#define SWREG_IHOST1_REG_RESETB 5
+#define SWREG_IHOST1_PMU_STABLE 2
+
+enum sw_reg {
+	DDR_VDDC = 1,
+	IHOST03,
+	IHOST12,
+	IHOST_ARRAY,
+	DDRIO_SLAVE,
+	VDDC_CORE,
+	VDDC1,
+	DDRIO_MASTER
+};
+
+int set_swreg(enum sw_reg reg_id, uint32_t micro_volts);
+int swreg_firmware_update(void);
+
+#endif
diff --git a/plat/brcm/board/stingray/platform.mk b/plat/brcm/board/stingray/platform.mk
index 83e502d..f4b665c 100644
--- a/plat/brcm/board/stingray/platform.mk
+++ b/plat/brcm/board/stingray/platform.mk
@@ -4,6 +4,19 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
+# Set the toc_flags to 1 for 100% speed operation
+# Set the toc_flags to 2 for 50% speed operation
+# Set the toc_flags to 3 for 25% speed operation
+# Set the toc_flags bit 3 to indicate ignore the fip in UEFI copy mode
+PLAT_TOC_FLAGS := 0x0
+
+# Set the IHOST_PLL_FREQ to,
+# 1 for full speed
+# 2 for 50% speed
+# 3 for 25% speed
+# 0 for bypass
+$(eval $(call add_define_val,IHOST_PLL_FREQ,1))
+
 # Enable workaround for ERRATA_A72_859971
 ERRATA_A72_859971 := 1
 
@@ -16,16 +29,42 @@
 
 USE_CRMU_SRAM := yes
 
+# Enable error logging by default for Stingray
+BCM_ELOG := yes
+
+# Enable FRU support by default for Stingray
+ifeq (${USE_FRU},)
+USE_FRU := no
+endif
+
 # Use single cluster
 ifeq (${USE_SINGLE_CLUSTER},yes)
 $(info Using Single Cluster)
 $(eval $(call add_define,USE_SINGLE_CLUSTER))
 endif
 
+# Use DDR
+ifeq (${USE_DDR},yes)
+$(info Using DDR)
+$(eval $(call add_define,USE_DDR))
+endif
+
 ifeq (${BOARD_CFG},)
 BOARD_CFG := bcm958742k
 endif
 
+# Use NAND
+ifeq (${USE_NAND},$(filter yes, ${USE_NAND}))
+$(info Using NAND)
+$(eval $(call add_define,USE_NAND))
+endif
+
+# Enable Broadcom error logging support
+ifeq (${BCM_ELOG},yes)
+$(info Using BCM_ELOG)
+$(eval $(call add_define,BCM_ELOG))
+endif
+
 # BL31 build for standalone mode
 ifeq (${STANDALONE_BL31},yes)
 RESET_TO_BL31 := 1
@@ -43,6 +82,9 @@
 # Default soft reset is L3
 $(eval $(call add_define,CONFIG_SOFT_RESET_L3))
 
+# Enable Chip OTP driver
+DRIVER_OCOTP_ENABLE := 1
+
 include plat/brcm/board/common/board_common.mk
 
 SOC_DIR			:= 	brcm/board/stingray
@@ -58,6 +100,17 @@
 				drivers/arm/tzc/tzc400.c \
 				plat/${SOC_DIR}/src/topology.c
 
+BL2_SOURCES		+=	plat/${SOC_DIR}/driver/ihost_pll_config.c \
+				plat/${SOC_DIR}/src/bl2_setup.c \
+				plat/${SOC_DIR}/driver/swreg.c
+
+
+ifeq (${USE_DDR},yes)
+PLAT_INCLUDES		+=	-Iplat/${SOC_DIR}/driver/ddr/soc/include
+else
+PLAT_INCLUDES		+=	-Iplat/${SOC_DIR}/driver/ext_sram_init
+BL2_SOURCES		+=	plat/${SOC_DIR}/driver/ext_sram_init/ext_sram_init.c
+endif
 
 # Include GICv3 driver files
 include drivers/arm/gic/v3/gicv3.mk
@@ -77,6 +130,12 @@
 ifdef SCP_BL2
 PLAT_INCLUDES		+=	-Iplat/brcm/common/
 
+BL2_SOURCES		+=	plat/brcm/common/brcm_mhu.c \
+				plat/brcm/common/brcm_scpi.c \
+				plat/${SOC_DIR}/src/scp_utils.c \
+				plat/${SOC_DIR}/src/scp_cmd.c \
+				drivers/brcm/scp.c
+
 BL31_SOURCES		+=	plat/brcm/common/brcm_mhu.c \
 				plat/brcm/common/brcm_scpi.c \
 				plat/${SOC_DIR}/src/brcm_pm_ops.c
@@ -85,5 +144,19 @@
 				plat/${SOC_DIR}/src/pm.c
 endif
 
+ifeq (${ELOG_SUPPORT},1)
+ifeq (${ELOG_STORE_MEDIA},DDR)
+BL2_SOURCES		+=	plat/brcm/board/common/bcm_elog_ddr.c
+endif
+endif
+
 # Do not execute the startup code on warm reset.
 PROGRAMMABLE_RESET_ADDRESS	:=	1
+
+# Nitro FW, config and Crash log uses secure DDR memory
+# Inaddition to above, Nitro master and slave is also secure
+ifneq ($(NITRO_SECURE_ACCESS),)
+$(eval $(call add_define,NITRO_SECURE_ACCESS))
+$(eval $(call add_define,DDR_NITRO_SECURE_REGION_START))
+$(eval $(call add_define,DDR_NITRO_SECURE_REGION_END))
+endif
diff --git a/plat/brcm/board/stingray/src/bl2_setup.c b/plat/brcm/board/stingray/src/bl2_setup.c
new file mode 100644
index 0000000..0b0a3ff
--- /dev/null
+++ b/plat/brcm/board/stingray/src/bl2_setup.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright (c) 2016-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <common/bl_common.h>
+#include <common/debug.h>
+#include <drivers/arm/sp805.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include <chimp.h>
+#include <chip_id.h>
+#include <cmn_plat_util.h>
+#include <dmu.h>
+#include <fru.h>
+#ifdef USE_GPIO
+#include <drivers/gpio.h>
+#include <iproc_gpio.h>
+#endif
+#include <platform_def.h>
+#include <sotp.h>
+#include <swreg.h>
+#include <sr_utils.h>
+#ifdef USE_DDR
+#include <ddr_init.h>
+#else
+#include <ext_sram_init.h>
+#endif
+#if DRIVER_OCOTP_ENABLE
+#include <ocotp.h>
+#endif
+#include "board_info.h"
+
+#define WORD_SIZE              8
+#define SWREG_AVS_OTP_OFFSET   (13 * WORD_SIZE) /* 13th row byte offset */
+#define AON_GPIO_OTP_OFFSET    (28 * WORD_SIZE) /* 28th row byte offset */
+#define BYTES_TO_READ          8
+
+/* OTP voltage step definitions */
+#define MVOLT_STEP_MAX         0x18  /* 1v */
+#define MVOLT_PER_STEP         10    /* 0.01mv per step */
+#define MVOLT_BASE             760   /* 0.76v */
+
+#define STEP_TO_UVOLTS(step) \
+	((MVOLT_BASE + (MVOLT_PER_STEP * (step))) * 1000)
+
+#define GET_BITS(first, last, data) \
+	((data >> first) & ((1 << (last - first + 1)) - 1))
+
+/*
+ * SW-REG OTP encoding:
+ *
+ * SWREG_bits[11:0]  = OTP 13th row 12 bits[55:44]
+ * SWREG_bits[11:10] - Valid Bits (0x2 - valid, if not 0x2 - Invalid)
+ * SWREG_bits[9:5]   - iHost03, iHost12
+ * SWREG_bits[4:0]   - Core VDDC
+ */
+#define SWREG_OTP_BITS_START        12    /* 44th bit in MSB 32-bits */
+#define SWREG_OTP_BITS_END          23    /* 55th bit in MSB 32-bits */
+#define SWREG_VDDC_FIELD_START      0
+#define SWREG_VDDC_FIELD_END        4
+#define SWREG_IHOST_FIELD_START     5
+#define SWREG_IHOST_FIELD_END       9
+#define SWREG_VALID_BIT_START       10
+#define SWREG_VALID_BIT_END         11
+#define SWREG_VALID_BITS            0x2
+
+/*
+ * Row 13 bit 56 is programmed as '1' today. It is not being used, so plan
+ * is to flip this bit to '0' for B1 rev. Hence SW can leverage this bit
+ * to identify Bx chip to program different sw-regulators.
+ */
+#define SPARE_BIT             24
+
+#define IS_SR_B0(data)        (((data) >> SPARE_BIT) & 0x1)
+
+#if DRIVER_OCOTP_ENABLE
+static struct otpc_map otp_stingray_map = {
+	.otpc_row_size = 2,
+	.data_r_offset = {0x10, 0x5c},
+	.data_w_offset = {0x2c, 0x64},
+	.word_size = 8,
+	.stride = 8,
+};
+#endif
+
+void plat_bcm_bl2_early_platform_setup(void)
+{
+	/* Select UART0 for AP via mux setting*/
+	if (PLAT_BRCM_BOOT_UART_BASE == UART0_BASE_ADDR) {
+		mmio_write_32(UART0_SIN_MODE_SEL_CONTROL, 1);
+		mmio_write_32(UART0_SOUT_MODE_SEL_CONTROL, 1);
+	}
+}
+
+#ifdef USE_NAND
+static void brcm_stingray_nand_init(void)
+{
+	unsigned int val;
+	unsigned int nand_idm_reset_control = 0x68e0a800;
+
+	VERBOSE(" stingray nand init start.\n");
+
+	/* Reset NAND */
+	VERBOSE(" - reset nand\n");
+	val = mmio_read_32((uintptr_t)(nand_idm_reset_control + 0x0));
+	mmio_write_32((uintptr_t)(nand_idm_reset_control + 0x0), val | 0x1);
+	udelay(500);
+	val = mmio_read_32((uintptr_t)(nand_idm_reset_control + 0x0));
+	mmio_write_32((uintptr_t)(nand_idm_reset_control + 0x0), val & ~0x1);
+	udelay(500);
+
+	VERBOSE(" stingray nand init done.\n");
+}
+#endif
+
+#if defined(USE_PAXB) || defined(USE_PAXC) || defined(USE_SATA)
+#define PCIE_RESCAL_CFG_0 0x40000130
+#define PCIE_CFG_RESCAL_RSTB_R (1 << 16)
+#define PCIE_CFG_RESCAL_PWRDNB_R (1 << 8)
+#define PCIE_RESCAL_STATUS_0 0x4000014c
+#define PCIE_STAT_PON_VALID_R (1 << 0)
+#define PCIE_RESCAL_OUTPUT_STATUS 0x40000154
+#define CDRU_PCIE_RESET_N_R (1 << CDRU_MISC_RESET_CONTROL__CDRU_PCIE_RESET_N_R)
+
+#ifdef EMULATION_SETUP
+static void brcm_stingray_pcie_reset(void)
+{
+}
+#else
+static void brcm_stingray_pcie_reset(void)
+{
+	unsigned int data;
+	int try;
+
+	if (bcm_chimp_is_nic_mode()) {
+		INFO("NIC mode detected; PCIe reset/rescal not executed\n");
+		return;
+	}
+
+	mmio_clrbits_32(CDRU_MISC_RESET_CONTROL, CDRU_PCIE_RESET_N_R);
+	mmio_setbits_32(CDRU_MISC_RESET_CONTROL, CDRU_PCIE_RESET_N_R);
+	/* Release reset */
+	mmio_setbits_32(PCIE_RESCAL_CFG_0, PCIE_CFG_RESCAL_RSTB_R);
+	mdelay(1);
+	/* Power UP */
+	mmio_setbits_32(PCIE_RESCAL_CFG_0,
+			(PCIE_CFG_RESCAL_RSTB_R | PCIE_CFG_RESCAL_PWRDNB_R));
+
+	try = 1000;
+	do {
+		udelay(1);
+		data = mmio_read_32(PCIE_RESCAL_STATUS_0);
+		try--;
+	} while ((data & PCIE_STAT_PON_VALID_R) == 0x0 && (try > 0));
+
+	if (try <= 0)
+		ERROR("PCIE_RESCAL_STATUS_0: 0x%x\n", data);
+
+	VERBOSE("PCIE_SATA_RESCAL_STATUS_0 0x%x.\n",
+			mmio_read_32(PCIE_RESCAL_STATUS_0));
+	VERBOSE("PCIE_SATA_RESCAL_OUTPUT_STATUS 0x%x.\n",
+			mmio_read_32(PCIE_RESCAL_OUTPUT_STATUS));
+	INFO("PCIE SATA Rescal Init done\n");
+}
+#endif /* EMULATION_SETUP */
+#endif /* USE_PAXB || USE_PAXC || USE_SATA */
+
+#ifdef USE_PAXC
+void brcm_stingray_chimp_check_and_fastboot(void)
+{
+	int fastboot_init_result;
+
+	if (bcm_chimp_is_nic_mode())
+		/* Do not wait here */
+		return;
+
+#if WARMBOOT_DDR_S3_SUPPORT
+	/*
+	 * Currently DDR shmoo parameters and QSPI boot source are
+	 * tied. DDR shmoo parameters are stored in QSPI, which is
+	 * used for warmboot.
+	 * Do not reset nitro for warmboot
+	 */
+	if (is_warmboot() && (boot_source_get() == BOOT_SOURCE_QSPI))
+		return;
+#endif /* WARMBOOT_DDR_S3_SUPPORT */
+
+	/*
+	 * Not in NIC mode,
+	 * initiate fastboot (if enabled)
+	 */
+	if (FASTBOOT_TYPE == CHIMP_FASTBOOT_NITRO_RESET) {
+
+		VERBOSE("Bring up Nitro/ChiMP\n");
+
+		if (boot_source_get() == BOOT_SOURCE_QSPI)
+			WARN("Nitro boots from QSPI when AP has booted from QSPI.\n");
+		brcm_stingray_set_qspi_mux(0);
+		VERBOSE("Nitro controls the QSPI\n");
+	}
+
+	fastboot_init_result = bcm_chimp_initiate_fastboot(FASTBOOT_TYPE);
+	if (fastboot_init_result && boot_source_get() != BOOT_SOURCE_QSPI)
+		ERROR("Nitro init error %d. Status: 0x%x; bpe_mod reg: 0x%x\n"
+			"fastboot register: 0x%x; handshake register 0x%x\n",
+			fastboot_init_result,
+			bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_STAT_REG),
+			bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_MODE_REG),
+			bcm_chimp_read_ctrl(CHIMP_REG_CTRL_FSTBOOT_PTR_REG),
+			bcm_chimp_read(CHIMP_REG_ECO_RESERVED));
+
+	/*
+	 * CRMU watchdog kicks is an example, which is L1 reset,
+	 * does not clear Nitro scratch pad ram.
+	 * For Nitro resets: Clear the Nitro health status memory.
+	 */
+	bcm_chimp_write((CHIMP_REG_CHIMP_SCPAD + CHIMP_HEALTH_STATUS_OFFSET),
+			0);
+}
+#endif
+
+void set_ihost_vddc_swreg(uint32_t ihost_uvolts, uint32_t vddc_uvolts)
+{
+	NOTICE("ihost_uvolts: %duv, vddc_uvolts: %duv\n",
+	       ihost_uvolts, vddc_uvolts);
+
+	set_swreg(VDDC_CORE, vddc_uvolts);
+	set_swreg(IHOST03, ihost_uvolts);
+	set_swreg(IHOST12, ihost_uvolts);
+}
+
+/*
+ * Reads SWREG AVS OTP bits (13th row) with ECC enabled and get voltage
+ * defined in OTP if valid OTP is found
+ */
+void read_avs_otp_bits(uint32_t *ihost_uvolts, uint32_t *vddc_uvolts)
+{
+	uint32_t offset = SWREG_AVS_OTP_OFFSET;
+	uint32_t ihost_step, vddc_step;
+	uint32_t avs_bits;
+	uint32_t buf[2];
+
+	if (bcm_otpc_read(offset, &buf[0], BYTES_TO_READ, 1) == -1)
+		return;
+
+	VERBOSE("AVS OTP %d ROW: 0x%x.0x%x\n",
+		offset/WORD_SIZE, buf[1], buf[0]);
+
+	/* get voltage readings from AVS OTP bits */
+	avs_bits = GET_BITS(SWREG_OTP_BITS_START,
+			    SWREG_OTP_BITS_END,
+			    buf[1]);
+
+	/* check for valid otp bits */
+	if (GET_BITS(SWREG_VALID_BIT_START, SWREG_VALID_BIT_END, avs_bits) !=
+	    SWREG_VALID_BITS) {
+		WARN("Invalid AVS OTP bits at %d row\n", offset/WORD_SIZE);
+		return;
+	}
+
+	/* get ihost and vddc step value */
+	vddc_step = GET_BITS(SWREG_VDDC_FIELD_START,
+			     SWREG_VDDC_FIELD_END,
+			     avs_bits);
+
+	ihost_step = GET_BITS(SWREG_IHOST_FIELD_START,
+			      SWREG_IHOST_FIELD_END,
+			      avs_bits);
+
+	if ((ihost_step > MVOLT_STEP_MAX) || (vddc_step > MVOLT_STEP_MAX)) {
+		WARN("OTP entry invalid\n");
+		return;
+	}
+
+	/* get voltage in micro-volts */
+	*ihost_uvolts = STEP_TO_UVOLTS(ihost_step);
+	*vddc_uvolts = STEP_TO_UVOLTS(vddc_step);
+}
+
+/*
+ * This api reads otp bits and program internal swreg's - ihos12, ihost03,
+ * vddc_core and ddr_core based on different chip. External swreg's
+ * programming will be done from crmu.
+ *
+ * For A2 chip:
+ *   Read OTP row 20, bit 50. This bit will be set for A2 chip. Once A2 chip is
+ *   found, read AVS OTP row 13, 12bits[55:44], if valid otp bits are found
+ *   then set ihost and vddc according to avs otp bits else set them to 0.94v
+ *   and 0.91v respectively. Also update the firmware after setting voltage.
+ *
+ * For B0 chip:
+ *   Read OTP row 13, bit 56. This bit will be set for B0 chip. Once B0 chip is
+ *   found then set ihost and vddc to 0.95v and ddr_core to 1v. No AVS OTP bits
+ *   are used get ihost/vddc voltages.
+ *
+ * For B1 chip:
+ *   Read AVS OTP row 13, 12bits[55:44], if valid otp bits are found then set
+ *   ihost and vddc according to avs otp bits else set them to 0.94v and 0.91v
+ *   respectively.
+ */
+void set_swreg_based_on_otp(void)
+{
+	/* default voltage if no valid OTP */
+	uint32_t vddc_uvolts = VDDC_CORE_DEF_VOLT;
+	uint32_t ihost_uvolts = IHOST_DEF_VOLT;
+	uint32_t ddrc_uvolts;
+	uint32_t offset;
+	uint32_t buf[2];
+
+	offset = SWREG_AVS_OTP_OFFSET;
+	if (bcm_otpc_read(offset, &buf[0], BYTES_TO_READ, 1) == -1)
+		return;
+
+	VERBOSE("OTP %d ROW: 0x%x.0x%x\n",
+		offset/WORD_SIZE, buf[1], buf[0]);
+
+	if (IS_SR_B0(buf[1])) {
+		/* don't read AVS OTP for B0 */
+		ihost_uvolts = B0_IHOST_DEF_VOLT;
+		vddc_uvolts = B0_VDDC_CORE_DEF_VOLT;
+		ddrc_uvolts = B0_DDR_VDDC_DEF_VOLT;
+	} else {
+		read_avs_otp_bits(&ihost_uvolts, &vddc_uvolts);
+	}
+
+#if (IHOST_REG_TYPE == IHOST_REG_INTEGRATED) && \
+	(VDDC_REG_TYPE == VDDC_REG_INTEGRATED)
+	/* enable IHOST12 cluster before changing voltage */
+	NOTICE("Switching on the Regulator idx: %u\n",
+	       SWREG_IHOST1_DIS);
+	mmio_clrsetbits_32(CRMU_SWREG_CTRL_ADDR,
+			   BIT(SWREG_IHOST1_DIS),
+			   BIT(SWREG_IHOST1_REG_RESETB));
+
+	/* wait for regulator supply gets stable */
+	while (!(mmio_read_32(CRMU_SWREG_STATUS_ADDR) &
+	       (1 << SWREG_IHOST1_PMU_STABLE)))
+		;
+
+	INFO("Regulator supply got stable\n");
+
+#ifndef DEFAULT_SWREG_CONFIG
+	swreg_firmware_update();
+#endif
+
+	set_ihost_vddc_swreg(ihost_uvolts, vddc_uvolts);
+#endif
+	if (IS_SR_B0(buf[1])) {
+		NOTICE("ddrc_uvolts: %duv\n", ddrc_uvolts);
+		set_swreg(DDR_VDDC, ddrc_uvolts);
+	}
+}
+
+#ifdef USE_DDR
+static struct ddr_info ddr_info;
+#endif
+#ifdef USE_FRU
+static struct fru_area_info fru_area[FRU_MAX_NR_AREAS];
+static struct fru_board_info board_info;
+static struct fru_time fru_tm;
+static uint8_t fru_tbl[BCM_MAX_FRU_LEN];
+
+static void board_detect_fru(void)
+{
+	uint32_t i, result;
+	int ret = -1;
+
+	result = bcm_emmc_init(false);
+	if (!result) {
+		ERROR("eMMC init failed\n");
+		return;
+	}
+
+	/* go through eMMC boot partitions looking for FRU table */
+	for (i = EMMC_BOOT_PARTITION1; i <= EMMC_BOOT_PARTITION2; i++) {
+		result = emmc_partition_select(i);
+		if (!result) {
+			ERROR("Switching to eMMC part %u failed\n", i);
+			return;
+		}
+
+		result = emmc_read(BCM_FRU_TBL_OFFSET, (uintptr_t)fru_tbl,
+				   BCM_MAX_FRU_LEN, BCM_MAX_FRU_LEN);
+		if (!result) {
+			ERROR("Failed to read from eMMC part %u\n", i);
+			return;
+		}
+
+		/*
+		 * Run sanity check and checksum to make sure valid FRU table
+		 * is detected
+		 */
+		ret = fru_validate(fru_tbl, fru_area);
+		if (ret < 0) {
+			WARN("FRU table not found in eMMC part %u\n", i);
+			continue;
+		}
+
+		/* parse DDR information from FRU table */
+		ret = fru_parse_ddr(fru_tbl, &fru_area[FRU_AREA_INTERNAL],
+				    &ddr_info);
+		if (ret < 0) {
+			WARN("No FRU DDR info found in eMMC part %u\n", i);
+			continue;
+		}
+
+		/* parse board information from FRU table */
+		ret = fru_parse_board(fru_tbl, &fru_area[FRU_AREA_BOARD_INFO],
+				      &board_info);
+		if (ret < 0) {
+			WARN("No FRU board info found in eMMC part %u\n", i);
+			continue;
+		}
+
+		/* if we reach here, valid FRU table is parsed */
+		break;
+	}
+
+	if (ret < 0) {
+		WARN("FRU table missing for this board\n");
+		return;
+	}
+
+	for (i = 0; i < BCM_MAX_NR_DDR; i++) {
+		INFO("DDR channel index: %d\n", ddr_info.mcb[i].idx);
+		INFO("DDR size %u GB\n", ddr_info.mcb[i].size_mb / 1024);
+		INFO("DDR ref ID by SW (Not MCB Ref ID) 0x%x\n",
+		     ddr_info.mcb[i].ref_id);
+	}
+
+	fru_format_time(board_info.mfg_date, &fru_tm);
+
+	INFO("**** FRU board information ****\n");
+	INFO("Language 0x%x\n", board_info.lang);
+	INFO("Manufacturing Date %u.%02u.%02u, %02u:%02u\n",
+	     fru_tm.year, fru_tm.month, fru_tm.day,
+	     fru_tm.hour, fru_tm.min);
+	INFO("Manufacturing Date(Raw) 0x%x\n", board_info.mfg_date);
+	INFO("Manufacturer %s\n", board_info.manufacturer);
+	INFO("Product Name %s\n", board_info.product_name);
+	INFO("Serial number %s\n", board_info.serial_number);
+	INFO("Part number %s\n", board_info.part_number);
+	INFO("File ID %s\n", board_info.file_id);
+}
+#endif /* USE_FRU */
+
+#ifdef USE_GPIO
+
+#define INVALID_GPIO    0xffff
+
+static const int gpio_cfg_bitmap[MAX_NR_GPIOS] = {
+#ifdef BRD_DETECT_GPIO_BIT0
+	BRD_DETECT_GPIO_BIT0,
+#else
+	INVALID_GPIO,
+#endif
+#ifdef BRD_DETECT_GPIO_BIT1
+	BRD_DETECT_GPIO_BIT1,
+#else
+	INVALID_GPIO,
+#endif
+#ifdef BRD_DETECT_GPIO_BIT2
+	BRD_DETECT_GPIO_BIT2,
+#else
+	INVALID_GPIO,
+#endif
+#ifdef BRD_DETECT_GPIO_BIT3
+	BRD_DETECT_GPIO_BIT3,
+#else
+	INVALID_GPIO,
+#endif
+};
+
+static uint8_t gpio_bitmap;
+
+/*
+ * Use an odd number to avoid potential conflict with public GPIO level
+ * defines
+ */
+#define GPIO_STATE_FLOAT         15
+
+/*
+ * If GPIO_SUPPORT_FLOAT_DETECTION is disabled, simply return GPIO level
+ *
+ * If GPIO_SUPPORT_FLOAT_DETECTION is enabled, add additional test for possible
+ * pin floating (unconnected) scenario. This support is assuming externally
+ * applied pull up / pull down will have a stronger pull than the internal pull
+ * up / pull down.
+ */
+static uint8_t gpio_get_state(int gpio)
+{
+	uint8_t val;
+
+	/* set direction to GPIO input */
+	gpio_set_direction(gpio, GPIO_DIR_IN);
+
+#ifndef GPIO_SUPPORT_FLOAT_DETECTION
+	if (gpio_get_value(gpio) == GPIO_LEVEL_HIGH)
+		val = GPIO_LEVEL_HIGH;
+	else
+		val = GPIO_LEVEL_LOW;
+
+	return val;
+#else
+	/*
+	 * Enable internal pull down. If GPIO level is still high, there must
+	 * be an external pull up
+	 */
+	gpio_set_pull(gpio, GPIO_PULL_DOWN);
+	if (gpio_get_value(gpio) == GPIO_LEVEL_HIGH) {
+		val = GPIO_LEVEL_HIGH;
+		goto exit;
+	}
+
+	/*
+	 * Enable internal pull up. If GPIO level is still low, there must
+	 * be an external pull down
+	 */
+	gpio_set_pull(gpio, GPIO_PULL_UP);
+	if (gpio_get_value(gpio) == GPIO_LEVEL_LOW) {
+		val = GPIO_LEVEL_LOW;
+		goto exit;
+	}
+
+	/* if reached here, the pin must be not connected */
+	val = GPIO_STATE_FLOAT;
+
+exit:
+	/* make sure internall pull is disabled */
+	if (gpio_get_pull(gpio) != GPIO_PULL_NONE)
+		gpio_set_pull(gpio, GPIO_PULL_NONE);
+
+	return val;
+#endif
+}
+
+static void board_detect_gpio(void)
+{
+	unsigned int i, val;
+	int gpio;
+
+	iproc_gpio_init(IPROC_GPIO_S_BASE, IPROC_GPIO_NR,
+			IPROC_IOPAD_MODE_BASE, HSLS_IOPAD_BASE);
+
+	gpio_bitmap = 0;
+	for (i = 0; i < MAX_NR_GPIOS; i++) {
+		if (gpio_cfg_bitmap[i] == INVALID_GPIO)
+			continue;
+
+		/*
+		 * Construct the bitmap based on GPIO value. Floating pin
+		 * detection is a special case. As soon as a floating pin is
+		 * detected, a special value of MAX_GPIO_BITMAP_VAL is
+		 * assigned and we break out of the loop immediately
+		 */
+		gpio = gpio_cfg_bitmap[i];
+		val = gpio_get_state(gpio);
+		if (val == GPIO_STATE_FLOAT) {
+			gpio_bitmap = MAX_GPIO_BITMAP_VAL;
+			break;
+		}
+
+		if (val == GPIO_LEVEL_HIGH)
+			gpio_bitmap |= BIT(i);
+	}
+
+	memcpy(&ddr_info, &gpio_ddr_info[gpio_bitmap], sizeof(ddr_info));
+	INFO("Board detection GPIO bitmap = 0x%x\n", gpio_bitmap);
+}
+#endif /* USE_GPIO */
+
+static void bcm_board_detect(void)
+{
+#ifdef DDR_LEGACY_MCB_SUPPORTED
+	/* Loading default DDR info */
+	memcpy(&ddr_info, &default_ddr_info, sizeof(ddr_info));
+#endif
+#ifdef USE_FRU
+	board_detect_fru();
+#endif
+#ifdef USE_GPIO
+	board_detect_gpio();
+#endif
+}
+
+static void dump_persistent_regs(void)
+{
+	NOTICE("pr0: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG0));
+	NOTICE("pr1: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG1));
+	NOTICE("pr2: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG2));
+	NOTICE("pr3: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG3));
+	NOTICE("pr4: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG4));
+	NOTICE("pr5: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG5));
+	NOTICE("pr6: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG6));
+	NOTICE("pr7: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG7));
+	NOTICE("pr8: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG8));
+	NOTICE("pr9: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG9));
+	NOTICE("pr10: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG10));
+	NOTICE("pr11: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG11));
+}
+
+void plat_bcm_bl2_plat_arch_setup(void)
+{
+	if (chip_get_rev_id_major() == CHIP_REV_MAJOR_AX) {
+		if (!(sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC) &
+		      SOTP_ATF_WATCHDOG_ENABLE_MASK)) {
+			/*
+			 * Stop sp805 watchdog timer immediately.
+			 * It might has been set up by MCU patch earlier for
+			 * eMMC workaround.
+			 *
+			 * Note the watchdog timer started in CRMU has a very
+			 * short timeout and needs to be stopped immediately.
+			 * Down below we restart it with a much longer timeout
+			 * for BL2 and BL31
+			 */
+			sp805_stop(ARM_SP805_TWDG_BASE);
+		}
+	}
+
+#if !BRCM_DISABLE_TRUSTED_WDOG
+	/*
+	 * start secure watchdog for BL2 and BL31.
+	 * Note that UART download can take a longer time,
+	 * so do not allow watchdog for UART download,
+	 * as this boot source is not a standard modus operandi.
+	 */
+	if (boot_source_get() != BOOT_SOURCE_UART)
+		sp805_start(ARM_SP805_TWDG_BASE, ARM_TWDG_LOAD_VAL);
+#endif
+
+#ifdef BCM_ELOG
+	/* Ensure logging is started out fresh in BL2. */
+	mmio_write_32(BCM_ELOG_BL2_BASE, 0);
+#endif
+	/*
+	 * In BL2, since we have very limited space to store logs, we only
+	 * save logs that are >= the WARNING level.
+	 */
+	bcm_elog_init((void *)BCM_ELOG_BL2_BASE, BCM_ELOG_BL2_SIZE,
+		      LOG_LEVEL_WARNING);
+
+	dump_persistent_regs();
+
+	/* Read CRMU mailbox 0 */
+	NOTICE("RESET (reported by CRMU): 0x%x\n",
+	       mmio_read_32(CRMU_READ_MAIL_BOX0));
+
+	/*
+	 * All non-boot-source PADs are in forced input-mode at
+	 * reset so clear the force on non-boot-source PADs using
+	 * CDRU register.
+	 */
+	mmio_clrbits_32((uintptr_t)CDRU_CHIP_IO_PAD_CONTROL,
+		(1 << CDRU_CHIP_IO_PAD_CONTROL__CDRU_IOMUX_FORCE_PAD_IN_R));
+
+#if DRIVER_OCOTP_ENABLE
+	bcm_otpc_init(&otp_stingray_map);
+#endif
+
+	set_swreg_based_on_otp();
+
+#if IHOST_PLL_FREQ != 0
+	bcm_set_ihost_pll_freq(0x0, IHOST_PLL_FREQ);
+#endif
+
+#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE
+	/* The erasable unit of the eMMC is the "Erase Group";
+	 * Erase group is measured in write blocks which are the
+	 * basic writable units of the Device.
+	 * The size of the Erase Group is a Device specific parameter
+	 */
+	emmc_erase(EMMC_ERASE_START_BLOCK, EMMC_ERASE_BLOCK_COUNT,
+		   EMMC_ERASE_PARTITION);
+#endif
+
+	bcm_board_detect();
+#ifdef DRIVER_EMMC_ENABLE
+	/* Initialize the card, if it is not */
+	if (bcm_emmc_init(true) < 0)
+		WARN("eMMC Card Initialization Failed!!!\n");
+#endif
+
+#if BL2_TEST_I2C
+	i2c_test();
+#endif
+
+#ifdef USE_DDR
+	ddr_initialize(&ddr_info);
+
+	ddr_secure_region_config(SECURE_DDR_BASE_ADDRESS,
+				 SECURE_DDR_END_ADDRESS);
+#ifdef NITRO_SECURE_ACCESS
+	ddr_secure_region_config(DDR_NITRO_SECURE_REGION_START,
+				 DDR_NITRO_SECURE_REGION_END);
+#endif
+#else
+	ext_sram_init();
+#endif
+
+#if BL2_TEST_MEM
+	ddr_test();
+#endif
+
+#ifdef USE_NAND
+	brcm_stingray_nand_init();
+#endif
+
+#if defined(USE_PAXB) || defined(USE_PAXC) || defined(USE_SATA)
+	brcm_stingray_pcie_reset();
+#endif
+
+#ifdef USE_PAXC
+	if (boot_source_get() != BOOT_SOURCE_QSPI)
+		brcm_stingray_chimp_check_and_fastboot();
+#endif
+
+#if ((!CLEAN_DDR || MMU_DISABLED))
+	/*
+	 * Now DDR has been initialized. We want to copy all the logs in SRAM
+	 * into DDR so we will have much more space to store the logs in the
+	 * next boot stage
+	 */
+	bcm_elog_copy_log((void *)BCM_ELOG_BL31_BASE,
+			   MIN(BCM_ELOG_BL2_SIZE, BCM_ELOG_BL31_SIZE)
+			 );
+
+	/*
+	 * We are not yet at the end of BL2, but we can stop log here so we do
+	 * not need to add 'bcm_elog_exit' to the standard BL2 code. The
+	 * benefit of capturing BL2 logs after this is very minimal in a
+	 * production system
+	 * NOTE: BL2 logging must be exited before going forward to setup
+	 * page tables
+	 */
+	bcm_elog_exit();
+#endif
+}
diff --git a/plat/brcm/board/stingray/src/scp_cmd.c b/plat/brcm/board/stingray/src/scp_cmd.c
new file mode 100644
index 0000000..2aa9519
--- /dev/null
+++ b/plat/brcm/board/stingray/src/scp_cmd.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <common/bl_common.h>
+#include <drivers/delay_timer.h>
+
+#include <platform_def.h>
+#include <scp.h>
+#include <scp_cmd.h>
+
+#include "m0_ipc.h"
+
+/*
+ * Reads a response from CRMU MAILBOX
+ * Assumes that access has been granted and locked.
+ * Note that this is just a temporary implementation until
+ * channels are introduced
+ */
+static void scp_read_response(crmu_response_t *resp)
+{
+	uint32_t code;
+
+	code = mmio_read_32(CRMU_MAIL_BOX0);
+	resp->completed = code & MCU_IPC_CMD_DONE_MASK;
+	resp->cmd = code & SCP_CMD_MASK;
+	resp->ret = (code & MCU_IPC_CMD_REPLY_MASK) >> MCU_IPC_CMD_REPLY_SHIFT;
+}
+
+/*
+ * Send a command to SCP and wait for timeout us.
+ * Return:  0 on success
+ *         -1 if there was no proper reply from SCP
+ *         >0 if there was a response from MCU, but
+ *            command completed with an error.
+ */
+int scp_send_cmd(uint32_t cmd, uint32_t param, uint32_t timeout)
+{
+	int ret = -1;
+
+	mmio_write_32(CRMU_MAIL_BOX0, cmd);
+	mmio_write_32(CRMU_MAIL_BOX1, param);
+	do {
+		crmu_response_t scp_resp;
+
+		udelay(1);
+		scp_read_response(&scp_resp);
+		if (scp_resp.completed &&
+			(scp_resp.cmd == cmd)) {
+			/* This command has completed */
+			ret = scp_resp.ret;
+			break;
+		}
+	} while (--timeout);
+
+	return ret;
+}
diff --git a/plat/brcm/board/stingray/src/scp_utils.c b/plat/brcm/board/stingray/src/scp_utils.c
new file mode 100644
index 0000000..1d82cef
--- /dev/null
+++ b/plat/brcm/board/stingray/src/scp_utils.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2017-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+
+#include <arch_helpers.h>
+#include <common/bl_common.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+
+#include <bcm_elog_ddr.h>
+#include <brcm_mhu.h>
+#include <brcm_scpi.h>
+#include <chimp.h>
+#include <cmn_plat_util.h>
+#include <ddr_init.h>
+#include <scp.h>
+#include <scp_cmd.h>
+#include <scp_utils.h>
+
+#include "m0_cfg.h"
+#include "m0_ipc.h"
+
+#ifdef BCM_ELOG
+static void prepare_elog(void)
+{
+#if (CLEAN_DDR && !defined(MMU_DISABLED))
+	/*
+	 * Now DDR has been initialized. We want to copy all the logs in SRAM
+	 * into DDR so we will have much more space to store the logs in the
+	 * next boot stage
+	 */
+	bcm_elog_copy_log((void *)BCM_ELOG_BL31_BASE,
+			   MIN(BCM_ELOG_BL2_SIZE, BCM_ELOG_BL31_SIZE)
+			 );
+
+	/*
+	 * We are almost at the end of BL2, and we can stop log here so we do
+	 * not need to add 'bcm_elog_exit' to the standard BL2 code. The
+	 * benefit of capturing BL2 logs after this is very minimal in a
+	 * production system.
+	 */
+	bcm_elog_exit();
+#endif
+
+	/*
+	 * Notify CRMU that now it should pull logs from DDR instead of from
+	 * FS4 SRAM.
+	 */
+	SCP_WRITE_CFG(flash_log.can_use_ddr, 1);
+}
+#endif
+
+bool is_crmu_alive(void)
+{
+	return (scp_send_cmd(MCU_IPC_MCU_CMD_NOP, 0, SCP_CMD_DEFAULT_TIMEOUT_US)
+		== 0);
+}
+
+bool bcm_scp_issue_sys_reset(void)
+{
+	return (scp_send_cmd(MCU_IPC_MCU_CMD_L1_RESET, 0,
+			     SCP_CMD_DEFAULT_TIMEOUT_US));
+}
+
+/*
+ * Note that this is just a temporary implementation until
+ * channels are introduced
+ */
+
+int plat_bcm_bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info)
+{
+	int scp_patch_activated, scp_patch_version;
+#ifndef EMULATION_SETUP
+	uint8_t active_ch_bitmap, i;
+#endif
+	uint32_t reset_state = 0;
+	uint32_t mcu_ap_init_param = 0;
+
+	/*
+	 * First check if SCP patch has already been loaded
+	 * Send NOP command and see if there is a valid response
+	 */
+	scp_patch_activated =
+		(scp_send_cmd(MCU_IPC_MCU_CMD_NOP, 0,
+		SCP_CMD_DEFAULT_TIMEOUT_US) == 0);
+	if (scp_patch_activated) {
+		INFO("SCP Patch is already active.\n");
+
+		reset_state =  SCP_READ_CFG(board_cfg.reset_state);
+		mcu_ap_init_param = SCP_READ_CFG(board_cfg.mcu_init_param);
+
+		/* Clear reset state, it's been already read */
+		SCP_WRITE_CFG(board_cfg.reset_state, 0);
+
+		if (mcu_ap_init_param & MCU_PATCH_LOADED_BY_NITRO) {
+			/*
+			 * Reset "MCU_PATCH_LOADED_BY_NITRO" flag, but
+			 * Preserve any other flags we don't deal with here
+			 */
+			INFO("AP booted by Nitro\n");
+			SCP_WRITE_CFG(
+					board_cfg.mcu_init_param,
+					mcu_ap_init_param &
+						~MCU_PATCH_LOADED_BY_NITRO
+				      );
+		}
+	} else {
+		/*
+		 * MCU Patch not loaded, so load it.
+		 * MCU patch stamps critical points in REG9 (debug test-point)
+		 * Display its last content here. This helps to locate
+		 * where crash occurred if a CRMU watchdog kicked in.
+		 */
+		int ret;
+
+		INFO("MCU Patch Point: 0x%x\n",
+			mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG9));
+
+		ret = download_scp_patch((void *)scp_bl2_image_info->image_base,
+				scp_bl2_image_info->image_size);
+		if (ret != 0)
+			return ret;
+
+		VERBOSE("SCP Patch loaded OK.\n");
+
+		ret = scp_send_cmd(MCU_IPC_MCU_CMD_INIT,
+				MCU_PATCH_LOADED_BY_AP,
+				SCP_CMD_SCP_BOOT_TIMEOUT_US);
+		if (ret) {
+			ERROR("SCP Patch could not initialize; error %d\n",
+				ret);
+			return ret;
+		}
+
+		INFO("SCP Patch successfully initialized.\n");
+	}
+
+	scp_patch_version = scp_send_cmd(MCU_IPC_MCU_CMD_GET_FW_VERSION, 0,
+				SCP_CMD_DEFAULT_TIMEOUT_US);
+	INFO("SCP Patch version :0x%x\n", scp_patch_version);
+
+	/* Next block just reports current AVS voltages (if applicable) */
+	{
+		uint16_t vcore_mv, ihost03_mv, ihost12_mv;
+
+		vcore_mv = SCP_READ_CFG16(vcore.millivolts) +
+				SCP_READ_CFG8(vcore.avs_cfg.additive_margin);
+		ihost03_mv = SCP_READ_CFG16(ihost03.millivolts) +
+				SCP_READ_CFG8(ihost03.avs_cfg.additive_margin);
+		ihost12_mv = SCP_READ_CFG16(ihost12.millivolts) +
+				SCP_READ_CFG8(ihost12.avs_cfg.additive_margin);
+
+		if (vcore_mv || ihost03_mv || ihost12_mv) {
+			INFO("AVS voltages from cfg (including margin)\n");
+			if (vcore_mv > 0)
+				INFO("%s\tVCORE: %dmv\n",
+					SCP_READ_CFG8(vcore.avs_cfg.avs_set) ?
+					"*" : "n/a", vcore_mv);
+			if (ihost03_mv > 0)
+				INFO("%s\tIHOST03: %dmv\n",
+				SCP_READ_CFG8(ihost03.avs_cfg.avs_set) ?
+					"*" : "n/a", ihost03_mv);
+			if (ihost12_mv > 0)
+				INFO("%s\tIHOST12: %dmv\n",
+				SCP_READ_CFG8(ihost12.avs_cfg.avs_set) ?
+					"*" : "n/a", ihost12_mv);
+		} else {
+			INFO("AVS settings not applicable\n");
+		}
+	}
+
+#if (CLEAN_DDR && !defined(MMU_DISABLED) && !defined(EMULATION_SETUP))
+	/* This will clean the DDR and enable ECC if set */
+	check_ddr_clean();
+#endif
+
+#if (WARMBOOT_DDR_S3_SUPPORT && ELOG_STORE_MEDIA_DDR)
+	elog_init_ddr_log();
+#endif
+
+#ifdef BCM_ELOG
+	/* Prepare ELOG to use DDR */
+	prepare_elog();
+#endif
+
+#ifndef EMULATION_SETUP
+	/* Ask ddr_init to save obtained DDR information into DDR */
+	ddr_info_save();
+#endif
+
+	/*
+	 * Configure TMON DDR address.
+	 * This cfg is common for all cases
+	 */
+	SCP_WRITE_CFG(tmon_cfg.ddr_desc, TMON_SHARED_DDR_ADDRESS);
+
+	if (reset_state == SOFT_RESET_L3 && !mcu_ap_init_param) {
+		INFO("SCP configuration after L3 RESET done.\n");
+		return 0;
+	}
+
+	if (bcm_chimp_is_nic_mode())
+		/* Configure AP WDT to not reset the NIC interface */
+		SCP_WRITE_CFG(board_cfg.apwdt_reset_type, SOFT_RESET_L3);
+
+#if (WARMBOOT_DDR_S3_SUPPORT && ELOG_STORE_MEDIA_DDR)
+	/* When AP WDog triggers perform L3 reset if DDR err logging enabled */
+	SCP_WRITE_CFG(board_cfg.apwdt_reset_type, SOFT_RESET_L3);
+#endif
+
+#ifndef EMULATION_SETUP
+
+#ifdef DDR_SCRUB_ENA
+	ddr_scrub_enable();
+#endif
+	/* Fill the Active channel information */
+	active_ch_bitmap = get_active_ddr_channel();
+	for (i = 0; i < MAX_NR_DDR_CH; i++)
+		SCP_WRITE_CFG(ddr_cfg.ddr_cfg[i],
+			      (active_ch_bitmap & BIT(i)) ? 1 : 0);
+#endif
+	return 0;
+}