Add BL2 support for Broadcom stingray platform
Change-Id: I5daa3f2b4b9d85cb857547a588571a9aa8ad05c2
Signed-off-by: Sheetal Tigadoli <sheetal.tigadoli@broadcom.com>
diff --git a/drivers/brcm/iproc_gpio.c b/drivers/brcm/iproc_gpio.c
new file mode 100644
index 0000000..f61a3bc
--- /dev/null
+++ b/drivers/brcm/iproc_gpio.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2019-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <drivers/gpio.h>
+#include <lib/mmio.h>
+#include <plat/common/platform.h>
+
+#include <iproc_gpio.h>
+#include <platform_def.h>
+
+#define IPROC_GPIO_DATA_IN_OFFSET 0x00
+#define IPROC_GPIO_DATA_OUT_OFFSET 0x04
+#define IPROC_GPIO_OUT_EN_OFFSET 0x08
+#define IPROC_GPIO_PAD_RES_OFFSET 0x34
+#define IPROC_GPIO_RES_EN_OFFSET 0x38
+
+#define PINMUX_OFFSET(gpio) ((gpio) * 4)
+#define PINCONF_OFFSET(gpio) ((gpio) * 4)
+#define PINCONF_PULL_UP BIT(4)
+#define PINCONF_PULL_DOWN BIT(5)
+
+/*
+ * iProc GPIO bank is always 0x200 per bank,
+ * with each bank supporting 32 GPIOs.
+ */
+#define GPIO_BANK_SIZE 0x200
+#define NGPIOS_PER_BANK 32
+#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
+
+#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
+#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
+
+#define MUX_GPIO_MODE 0x3
+
+/*
+ * @base: base address of the gpio controller
+ * @pinconf_base: base address of the pinconf
+ * @pinmux_base: base address of the mux controller
+ * @nr_gpios: maxinum number of GPIOs
+ */
+struct iproc_gpio {
+ uintptr_t base;
+ uintptr_t pinconf_base;
+ uintptr_t pinmux_base;
+ int nr_gpios;
+};
+
+static struct iproc_gpio iproc_gpio;
+
+static void gpio_set_bit(uintptr_t base, unsigned int reg, int gpio, bool set)
+{
+ unsigned int offset = IPROC_GPIO_REG(gpio, reg);
+ unsigned int shift = IPROC_GPIO_SHIFT(gpio);
+ uint32_t val;
+
+ val = mmio_read_32(base + offset);
+ if (set)
+ val |= BIT(shift);
+ else
+ val &= ~BIT(shift);
+
+ mmio_write_32(base + offset, val);
+}
+
+static bool gpio_get_bit(uintptr_t base, unsigned int reg, int gpio)
+{
+ unsigned int offset = IPROC_GPIO_REG(gpio, reg);
+ unsigned int shift = IPROC_GPIO_SHIFT(gpio);
+
+ return !!(mmio_read_32(base + offset) & BIT(shift));
+}
+
+static void mux_to_gpio(struct iproc_gpio *g, int gpio)
+{
+ /* mux pad to GPIO if IOPAD configuration is mandatory */
+ if (g->pinmux_base)
+ mmio_write_32(g->pinmux_base + PINMUX_OFFSET(gpio),
+ MUX_GPIO_MODE);
+}
+
+static void set_direction(int gpio, int direction)
+{
+ struct iproc_gpio *g = &iproc_gpio;
+ bool dir = (direction == GPIO_DIR_OUT) ? true : false;
+
+ assert(gpio < g->nr_gpios);
+
+ mux_to_gpio(g, gpio);
+ gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, dir);
+}
+
+static int get_direction(int gpio)
+{
+ struct iproc_gpio *g = &iproc_gpio;
+ int dir;
+
+ assert(gpio < g->nr_gpios);
+
+ mux_to_gpio(g, gpio);
+ dir = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
+ GPIO_DIR_OUT : GPIO_DIR_IN;
+
+ return dir;
+}
+
+static int get_value(int gpio)
+{
+ struct iproc_gpio *g = &iproc_gpio;
+ unsigned int offset;
+
+ assert(gpio < g->nr_gpios);
+
+ mux_to_gpio(g, gpio);
+
+ /*
+ * If GPIO is configured as output, read from the GPIO_OUT register;
+ * otherwise, read from the GPIO_IN register
+ */
+ offset = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
+ IPROC_GPIO_DATA_OUT_OFFSET : IPROC_GPIO_DATA_IN_OFFSET;
+
+ return gpio_get_bit(g->base, offset, gpio);
+}
+
+static void set_value(int gpio, int val)
+{
+ struct iproc_gpio *g = &iproc_gpio;
+
+ assert(gpio < g->nr_gpios);
+
+ mux_to_gpio(g, gpio);
+
+ /* make sure GPIO is configured to output, and then set the value */
+ gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, true);
+ gpio_set_bit(g->base, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
+}
+
+static int get_pull(int gpio)
+{
+ struct iproc_gpio *g = &iproc_gpio;
+ uint32_t val;
+
+ assert(gpio < g->nr_gpios);
+ mux_to_gpio(g, gpio);
+
+ /* when there's a valid pinconf_base, use it */
+ if (g->pinconf_base) {
+ val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
+
+ if (val & PINCONF_PULL_UP)
+ return GPIO_PULL_UP;
+ else if (val & PINCONF_PULL_DOWN)
+ return GPIO_PULL_DOWN;
+ else
+ return GPIO_PULL_NONE;
+ }
+
+ /* no pinconf_base. fall back to GPIO internal pull control */
+ if (!gpio_get_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio))
+ return GPIO_PULL_NONE;
+
+ return gpio_get_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio) ?
+ GPIO_PULL_UP : GPIO_PULL_DOWN;
+}
+
+static void set_pull(int gpio, int pull)
+{
+ struct iproc_gpio *g = &iproc_gpio;
+ uint32_t val;
+
+ assert(gpio < g->nr_gpios);
+ mux_to_gpio(g, gpio);
+
+ /* when there's a valid pinconf_base, use it */
+ if (g->pinconf_base) {
+ val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
+
+ if (pull == GPIO_PULL_NONE) {
+ val &= ~(PINCONF_PULL_UP | PINCONF_PULL_DOWN);
+ } else if (pull == GPIO_PULL_UP) {
+ val |= PINCONF_PULL_UP;
+ val &= ~PINCONF_PULL_DOWN;
+ } else if (pull == GPIO_PULL_DOWN) {
+ val |= PINCONF_PULL_DOWN;
+ val &= ~PINCONF_PULL_UP;
+ } else {
+ return;
+ }
+ mmio_write_32(g->pinconf_base + PINCONF_OFFSET(gpio), val);
+ }
+
+ /* no pinconf_base. fall back to GPIO internal pull control */
+ if (pull == GPIO_PULL_NONE) {
+ gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, false);
+ return;
+ }
+
+ /* enable pad register and pull up or down */
+ gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, true);
+ gpio_set_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio,
+ !!(pull == GPIO_PULL_UP));
+}
+
+const gpio_ops_t iproc_gpio_ops = {
+ .get_direction = get_direction,
+ .set_direction = set_direction,
+ .get_value = get_value,
+ .set_value = set_value,
+ .get_pull = get_pull,
+ .set_pull = set_pull,
+};
+
+void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
+ uintptr_t pinconf_base)
+{
+ iproc_gpio.base = base;
+ iproc_gpio.nr_gpios = nr_gpios;
+
+ /* pinmux/pinconf base is optional for some SoCs */
+ if (pinmux_base)
+ iproc_gpio.pinmux_base = pinmux_base;
+
+ if (pinconf_base)
+ iproc_gpio.pinconf_base = pinconf_base;
+
+ gpio_init(&iproc_gpio_ops);
+}
diff --git a/drivers/brcm/ocotp.c b/drivers/brcm/ocotp.c
new file mode 100644
index 0000000..6ff8554
--- /dev/null
+++ b/drivers/brcm/ocotp.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include <ocotp.h>
+#include <platform_def.h>
+
+#define OTP_MAP 2
+#define OTP_NUM_WORDS 2048
+/*
+ * # of tries for OTP Status. The time to execute a command varies. The slowest
+ * commands are writes which also vary based on the # of bits turned on. Writing
+ * 0xffffffff takes ~3800 us.
+ */
+#define OTPC_RETRIES_US 5000
+
+/* Sequence to enable OTP program */
+#define OTPC_PROG_EN_SEQ { 0xf, 0x4, 0x8, 0xd }
+
+/* OTPC Commands */
+#define OTPC_CMD_READ 0x0
+#define OTPC_CMD_OTP_PROG_ENABLE 0x2
+#define OTPC_CMD_OTP_PROG_DISABLE 0x3
+#define OTPC_CMD_PROGRAM 0x8
+#define OTPC_CMD_ECC 0x10
+#define OTPC_ECC_ADDR 0x1A
+#define OTPC_ECC_VAL 0x00EC0000
+
+/* OTPC Status Bits */
+#define OTPC_STAT_CMD_DONE BIT(1)
+#define OTPC_STAT_PROG_OK BIT(2)
+
+/* OTPC register definition */
+#define OTPC_MODE_REG_OFFSET 0x0
+#define OTPC_MODE_REG_OTPC_MODE 0
+#define OTPC_COMMAND_OFFSET 0x4
+#define OTPC_COMMAND_COMMAND_WIDTH 6
+#define OTPC_CMD_START_OFFSET 0x8
+#define OTPC_CMD_START_START 0
+#define OTPC_CPU_STATUS_OFFSET 0xc
+#define OTPC_CPUADDR_REG_OFFSET 0x28
+#define OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH 16
+#define OTPC_CPU_WRITE_REG_OFFSET 0x2c
+
+#define OTPC_CMD_MASK (BIT(OTPC_COMMAND_COMMAND_WIDTH) - 1)
+#define OTPC_ADDR_MASK (BIT(OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH) - 1)
+
+#define OTPC_MODE_REG OCOTP_REGS_BASE
+
+struct chip_otp_cfg {
+ uint32_t base;
+ uint32_t num_words;
+};
+
+struct chip_otp_cfg ocotp_cfg = {
+ .base = OTPC_MODE_REG,
+ .num_words = 2048,
+};
+
+struct otpc_priv {
+ uint32_t base;
+ struct otpc_map *map;
+ int size;
+ int state;
+};
+
+struct otpc_priv otpc_info;
+
+static inline void set_command(uint32_t base, uint32_t command)
+{
+ mmio_write_32(base + OTPC_COMMAND_OFFSET, command & OTPC_CMD_MASK);
+}
+
+static inline void set_cpu_address(uint32_t base, uint32_t addr)
+{
+ mmio_write_32(base + OTPC_CPUADDR_REG_OFFSET, addr & OTPC_ADDR_MASK);
+}
+
+static inline void set_start_bit(uint32_t base)
+{
+ mmio_write_32(base + OTPC_CMD_START_OFFSET, 1 << OTPC_CMD_START_START);
+}
+
+static inline void reset_start_bit(uint32_t base)
+{
+ mmio_write_32(base + OTPC_CMD_START_OFFSET, 0);
+}
+
+static inline void write_cpu_data(uint32_t base, uint32_t value)
+{
+ mmio_write_32(base + OTPC_CPU_WRITE_REG_OFFSET, value);
+}
+
+static int poll_cpu_status(uint32_t base, uint32_t value)
+{
+ uint32_t status;
+ uint32_t retries;
+
+ for (retries = 0; retries < OTPC_RETRIES_US; retries++) {
+ status = mmio_read_32(base + OTPC_CPU_STATUS_OFFSET);
+ if (status & value)
+ break;
+ udelay(1);
+ }
+ if (retries == OTPC_RETRIES_US)
+ return -1;
+
+ return 0;
+}
+
+static int bcm_otpc_ecc(uint32_t enable)
+{
+ struct otpc_priv *priv = &otpc_info;
+ int ret;
+
+ set_command(priv->base, OTPC_CMD_ECC);
+ set_cpu_address(priv->base, OTPC_ECC_ADDR);
+
+ if (!enable)
+ write_cpu_data(priv->base, OTPC_ECC_VAL);
+ else
+ write_cpu_data(priv->base, ~OTPC_ECC_VAL);
+
+ set_start_bit(priv->base);
+ ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
+ if (ret) {
+ ERROR("otp ecc op error: 0x%x", ret);
+ return -1;
+ }
+ reset_start_bit(priv->base);
+
+ return 0;
+}
+
+/*
+ * bcm_otpc_read read otp data in the size of 8 byte rows.
+ * bytes has to be the multiple of 8.
+ * return -1 in error case, return read bytes in success.
+ */
+int bcm_otpc_read(unsigned int offset, void *val, uint32_t bytes,
+ uint32_t ecc_flag)
+{
+ struct otpc_priv *priv = &otpc_info;
+ uint32_t *buf = val;
+ uint32_t bytes_read;
+ uint32_t address = offset / priv->map->word_size;
+ int i, ret;
+
+ if (!priv->state) {
+ ERROR("OCOTP read failed\n");
+ return -1;
+ }
+
+ bcm_otpc_ecc(ecc_flag);
+
+ for (bytes_read = 0; (bytes_read + priv->map->word_size) <= bytes;) {
+ set_command(priv->base, OTPC_CMD_READ);
+ set_cpu_address(priv->base, address++);
+ set_start_bit(priv->base);
+ ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
+ if (ret) {
+ ERROR("otp read error: 0x%x", ret);
+ return -1;
+ }
+
+ for (i = 0; i < priv->map->otpc_row_size; i++) {
+ *buf++ = mmio_read_32(priv->base +
+ priv->map->data_r_offset[i]);
+ bytes_read += sizeof(*buf);
+ }
+
+ reset_start_bit(priv->base);
+ }
+
+ return bytes_read;
+}
+
+int bcm_otpc_init(struct otpc_map *map)
+{
+ struct otpc_priv *priv;
+
+ priv = &otpc_info;
+ priv->base = ocotp_cfg.base;
+ priv->map = map;
+
+ priv->size = 4 * ocotp_cfg.num_words;
+
+ /* Enable CPU access to OTPC. */
+ mmio_setbits_32(priv->base + OTPC_MODE_REG_OFFSET,
+ BIT(OTPC_MODE_REG_OTPC_MODE));
+ reset_start_bit(priv->base);
+ priv->state = 1;
+ VERBOSE("OTPC Initialization done\n");
+
+ return 0;
+}
diff --git a/drivers/brcm/scp.c b/drivers/brcm/scp.c
new file mode 100644
index 0000000..6196073
--- /dev/null
+++ b/drivers/brcm/scp.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+
+/* MCU binary image structure: <header> <data>
+ *
+ * Header structure:
+ * <magic-start>
+ * <num-sections>
+ * {<src-offset> <src-size> <dst-addr>}*
+ * <magic-end>
+ *
+ * MCU data (<data>) consists of several sections of code/data, to be
+ * installed (copied) into MCU memories.
+ * Header (<header>) gives information about sections contained in <data>.
+ *
+ * The installer code iterates over sections in MCU binary.
+ * For each section, it copies the section into MCU memory.
+ *
+ * The header contains:
+ * - <magic-start> - 32-bit magic number to mark header start
+ * - <num-sections> - number of sections in <data>
+ * - <num-sections> tuples. Each tuple describes a section.
+ * A tuple contains three 32-bit words.
+ * - <magic-end> - 32-bit magic number to mark header end
+ *
+ * Each section is describes by a tuple, consisting of three 32-bit words:
+ * - offset of section within MCU binary (relative to beginning of <data>)
+ * - section size (in bytes) in MCU binary
+ * - target address (in MCU memory). Section is copied to this location.
+ *
+ * All fields are 32-bit unsigned integers in little endian format.
+ * All sizes are assumed to be 32-bit aligned.
+ */
+
+#define SCP_BIN_HEADER_MAGIC_START 0xfa587D01
+#define SCP_BIN_HEADER_MAGIC_END 0xf3e06a85
+
+int download_scp_patch(void *image, unsigned int image_size)
+{
+ unsigned int *pheader = (unsigned int *)(image);
+ unsigned int header_size;
+ unsigned char *pdata;
+ void *dest;
+ unsigned int num_sections;
+ unsigned int section_src_offset;
+ unsigned int section_size;
+
+ if (pheader && (pheader[0] != SCP_BIN_HEADER_MAGIC_START)) {
+ ERROR("SCP: Could not find SCP header.\n");
+ return -1;
+ }
+
+ num_sections = pheader[1];
+ INFO("...Number of sections: %d\n", num_sections);
+ header_size = 4 * (1 + 1 + 3 * num_sections + 1);
+
+ if (image_size < header_size) {
+ ERROR("SCP: Wrong size.\n");
+ return -1;
+ }
+
+ if (*(pheader + header_size/4 - 1) != SCP_BIN_HEADER_MAGIC_END) {
+ ERROR("SCP: Could not find SCP footer.\n");
+ return -1;
+ }
+
+ VERBOSE("SCP image header validated successfully\n");
+ pdata = (unsigned char *)pheader + header_size;
+
+ for (pheader += 2; num_sections > 0; num_sections--) {
+
+ section_src_offset = pheader[0];
+ section_size = pheader[1];
+ dest = (void *)(unsigned long)pheader[2];
+
+ INFO("section: src:0x%x, size:%d, dst:0x%x\n",
+ section_src_offset, section_size, pheader[2]);
+
+ if ((section_src_offset + section_size) > image_size) {
+ ERROR("SCP: Section points to outside of patch.\n");
+ return -1;
+ }
+
+ /* copy from source to target section */
+ memcpy(dest, pdata + section_src_offset, section_size);
+ flush_dcache_range((uintptr_t)dest, section_size);
+
+ /* next section */
+ pheader += 3;
+ }
+ return 0;
+}
diff --git a/drivers/brcm/sotp.c b/drivers/brcm/sotp.c
new file mode 100644
index 0000000..63c4820
--- /dev/null
+++ b/drivers/brcm/sotp.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2016-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+
+#include <common/debug.h>
+#include <lib/mmio.h>
+#include <sotp.h>
+
+#include <platform_def.h>
+#include <platform_sotp.h>
+
+#ifdef USE_SOFT_SOTP
+extern uint64_t soft_sotp[];
+#endif
+
+#define SOTP_PROG_CONTROL (SOTP_REGS_OTP_BASE + 0x0000)
+#define SOTP_PROG_CONTROL__OTP_CPU_MODE_EN 15
+#define SOTP_PROG_CONTROL__OTP_DISABLE_ECC 9
+#define SOTP_PROG_CONTROL__OTP_ECC_WREN 8
+
+#define SOTP_WRDATA_0 (SOTP_REGS_OTP_BASE + 0x0004)
+#define SOTP_WRDATA_1 (SOTP_REGS_OTP_BASE + 0x0008)
+
+#define SOTP_ADDR (SOTP_REGS_OTP_BASE + 0x000c)
+#define SOTP_ADDR__OTP_ROW_ADDR_R 6
+#define SOTP_ADDR_MASK 0x3FF
+
+#define SOTP_CTRL_0 (SOTP_REGS_OTP_BASE + 0x0010)
+#define SOTP_CTRL_0__START 0
+#define SOTP_CTRL_0__OTP_CMD 1
+
+#define SOTP_STATUS_0 (SOTP_REGS_OTP_BASE + 0x0018)
+#define SOTP_STATUS__FDONE 3
+
+#define SOTP_STATUS_1 (SOTP_REGS_OTP_BASE + 0x001c)
+#define SOTP_STATUS_1__CMD_DONE 1
+#define SOTP_STATUS_1__ECC_DET 17
+
+#define SOTP_RDDATA_0 (SOTP_REGS_OTP_BASE + 0x0020)
+#define SOTP_RDDATA_1 (SOTP_REGS_OTP_BASE + 0x0024)
+
+#define SOTP_READ 0
+
+#define SOTP_PROG_WORD 10
+#define SOTP_STATUS__PROGOK 2
+#define SOTP_PROG_ENABLE 2
+
+#define SOTP_ROW_DATA_MASK 0xffffffff
+#define SOTP_ECC_ERR_BITS_MASK 0x1ff00000000
+
+#define SOTP_CHIP_CTRL_SW_OVERRIDE_CHIP_STATES 4
+#define SOTP_CHIP_CTRL_SW_MANU_PROG 5
+#define SOTP_CHIP_CTRL_SW_CID_PROG 6
+#define SOTP_CHIP_CTRL_SW_AB_DEVICE 8
+#define SOTP_CHIP_CTRL_SW_AB_DEV_MODE 9
+#define CHIP_STATE_UNPROGRAMMED 0x1
+#define CHIP_STATE_UNASSIGNED 0x2
+
+uint64_t sotp_mem_read(uint32_t offset, uint32_t sotp_add_ecc)
+{
+#ifdef USE_SOFT_SOTP
+ (void)sotp_add_ecc;
+
+ return soft_sotp[offset];
+#else
+ uint64_t read_data = 0;
+ uint64_t read_data1 = 0;
+ uint64_t read_data2 = 0;
+
+ /* Check for FDONE status */
+ while ((mmio_read_32(SOTP_STATUS_0) & BIT(SOTP_STATUS__FDONE)) !=
+ BIT(SOTP_STATUS__FDONE))
+ ;
+
+ /* Enable OTP access by CPU */
+ mmio_setbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
+
+ if (sotp_add_ecc == 1) {
+ mmio_clrbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_DISABLE_ECC));
+ }
+
+ if (sotp_add_ecc == 0) {
+ mmio_setbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_DISABLE_ECC));
+ }
+
+ mmio_write_32(SOTP_ADDR,
+ ((offset & SOTP_ADDR_MASK) << SOTP_ADDR__OTP_ROW_ADDR_R));
+ mmio_write_32(SOTP_CTRL_0, (SOTP_READ << SOTP_CTRL_0__OTP_CMD));
+
+ /* Start bit to tell SOTP to send command to the OTP controller */
+ mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
+
+ /* Wait for SOTP command done to be set */
+ while ((mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__CMD_DONE)) !=
+ BIT(SOTP_STATUS_1__CMD_DONE))
+ ;
+
+ /* Clr Start bit after command done */
+ mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
+
+ if ((offset > SOTP_DEVICE_SECURE_CFG3_ROW) &&
+ (mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__ECC_DET))) {
+ ERROR("SOTP ECC ERROR Detected row offset %d\n", offset);
+ read_data = SOTP_ECC_ERR_DETECT;
+ } else {
+ read_data1 = (uint64_t)mmio_read_32(SOTP_RDDATA_0);
+ read_data1 = read_data1 & 0xFFFFFFFF;
+ read_data2 = (uint64_t)mmio_read_32(SOTP_RDDATA_1);
+ read_data2 = (read_data2 & 0x1ff) << 32;
+ read_data = read_data1 | read_data2;
+ }
+
+ /* Command done is cleared */
+ mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
+
+ /* disable OTP access by CPU */
+ mmio_clrbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
+
+ return read_data;
+#endif
+}
+
+void sotp_mem_write(uint32_t addr, uint32_t sotp_add_ecc, uint64_t wdata)
+{
+#ifdef USE_SOFT_SOTP
+ (void)sotp_add_ecc;
+
+ soft_sotp[addr] = wdata;
+#else
+ uint32_t loop;
+ uint8_t prog_array[4] = { 0x0F, 0x04, 0x08, 0x0D };
+
+ uint32_t chip_state_default =
+ (CHIP_STATE_UNASSIGNED|CHIP_STATE_UNPROGRAMMED);
+ uint32_t chip_state = mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES);
+ uint32_t chip_ctrl_default = 0;
+
+ /*
+ * The override settings is required to allow the customer to program
+ * the application specific keys into SOTP, before the conversion to
+ * one of the AB modes.
+ * At the end of write operation, the chip ctrl settings will restored
+ * to the state prior to write call
+ */
+ if (chip_state & chip_state_default) {
+ uint32_t chip_ctrl;
+
+ chip_ctrl_default = mmio_read_32(SOTP_CHIP_CTRL);
+ INFO("SOTP: enable special prog mode\n");
+
+ chip_ctrl = BIT(SOTP_CHIP_CTRL_SW_OVERRIDE_CHIP_STATES) |
+ BIT(SOTP_CHIP_CTRL_SW_MANU_PROG) |
+ BIT(SOTP_CHIP_CTRL_SW_CID_PROG) |
+ BIT(SOTP_CHIP_CTRL_SW_AB_DEVICE);
+ mmio_write_32(SOTP_CHIP_CTRL, chip_ctrl);
+ }
+
+ /* Check for FDONE status */
+ while ((mmio_read_32(SOTP_STATUS_0) & BIT(SOTP_STATUS__FDONE)) !=
+ BIT(SOTP_STATUS__FDONE))
+ ;
+
+ /* Enable OTP acces by CPU */
+ mmio_setbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
+
+ if (addr > SOTP_DEVICE_SECURE_CFG3_ROW) {
+ if (sotp_add_ecc == 0) {
+ mmio_clrbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
+ }
+ if (sotp_add_ecc == 1) {
+ mmio_setbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
+ }
+ } else {
+ mmio_clrbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
+ }
+
+ mmio_write_32(SOTP_CTRL_0, (SOTP_PROG_ENABLE << 1));
+
+ /*
+ * In order to avoid unintentional writes / programming of the OTP
+ * array, the OTP Controller must be put into programming mode before
+ * it will accept program commands. This is done by writing 0xF, 0x4,
+ * 0x8, 0xD with program commands prior to starting the actual
+ * programming sequence
+ */
+ for (loop = 0; loop < 4; loop++) {
+ mmio_write_32(SOTP_WRDATA_0, prog_array[loop]);
+
+ /*
+ * Start bit to tell SOTP to send command to the OTP controller
+ */
+ mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
+
+ /* Wait for SOTP command done to <-- be set */
+ while ((mmio_read_32(SOTP_STATUS_1) &
+ BIT(SOTP_STATUS_1__CMD_DONE)) !=
+ BIT(SOTP_STATUS_1__CMD_DONE))
+ ;
+
+ /* Command done is cleared w1c */
+ mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
+
+ /* Clr Start bit after command done */
+ mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
+ }
+
+ /* Check for PROGOK */
+ while ((mmio_read_32(SOTP_STATUS_0) & 0x4) != BIT(SOTP_STATUS__PROGOK))
+ ;
+
+ /* Set 10 bit row address */
+ mmio_write_32(SOTP_ADDR,
+ ((addr & SOTP_ADDR_MASK) << SOTP_ADDR__OTP_ROW_ADDR_R));
+
+ /* Set SOTP Row data */
+ mmio_write_32(SOTP_WRDATA_0, (wdata & SOTP_ROW_DATA_MASK));
+
+ /* Set SOTP ECC and error bits */
+ mmio_write_32(SOTP_WRDATA_1, ((wdata & SOTP_ECC_ERR_BITS_MASK) >> 32));
+
+ /* Set prog_word command */
+ mmio_write_32(SOTP_CTRL_0, (SOTP_PROG_WORD << 1));
+
+ /* Start bit to tell SOTP to send command to the OTP controller */
+ mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
+
+ /* Wait for SOTP command done to be set */
+ while ((mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__CMD_DONE)) !=
+ BIT(SOTP_STATUS_1__CMD_DONE))
+ ;
+
+ /* Command done is cleared w1c */
+ mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
+
+ /* disable OTP acces by CPU */
+ mmio_clrbits_32(SOTP_PROG_CONTROL,
+ BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
+
+ /* Clr Start bit after command done */
+ mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
+
+ if (chip_state & chip_state_default)
+ mmio_write_32(SOTP_CHIP_CTRL, chip_ctrl_default);
+
+#endif
+}
+
+int sotp_read_key(uint8_t *key, size_t keysize, int start_row, int end_row)
+{
+ int row;
+ uint32_t status = 0;
+ uint32_t status2 = 0xFFFFFFFF;
+ uint64_t row_data;
+ uint32_t data;
+ uint32_t *temp_key = (uint32_t *)key;
+
+ row = start_row;
+ while ((keysize > 0) && (row <= end_row)) {
+ row_data = sotp_mem_read(row, SOTP_ROW_ECC);
+ if (!(row_data & (SOTP_ECC_ERR_DETECT | SOTP_FAIL_BITS))) {
+ memcpy(temp_key++, &row_data, sizeof(uint32_t));
+ keysize -= sizeof(uint32_t);
+ data = (uint32_t)(row_data & SOTP_ROW_DATA_MASK);
+ status |= data;
+ status2 &= data;
+ }
+ row++;
+ }
+
+ if ((status2 == 0xFFFFFFFF) || (status == 0) || (row > end_row))
+ return -1;
+
+ return 0;
+}
+
+int sotp_key_erased(void)
+{
+ uint64_t row_data;
+ int status = 0;
+
+ row_data = sotp_mem_read(SOTP_DEVICE_SECURE_CFG0_ROW, 0);
+ if (row_data & SOTP_DEVICE_SECURE_CFG0_OTP_ERASED_MASK)
+ status = 1;
+
+ else if (mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES) &
+ SOTP_REGS_SOTP_CHIP_STATES_OTP_ERASED_MASK)
+ status = 1;
+
+ return status;
+}
+
+/*
+ * This function optimise the SOTP redundancy
+ * by considering the 00- zero and 01,10,11 - one
+ */
+uint32_t sotp_redundancy_reduction(uint32_t sotp_row_data)
+{
+ uint32_t opt_data;
+ uint32_t opt_loop;
+ uint32_t temp_data;
+
+ opt_data = 0;
+
+ for (opt_loop = 0; opt_loop < 16; opt_loop = opt_loop + 1) {
+ temp_data = ((sotp_row_data >> (opt_loop * 2)) & 0x3);
+
+ if (temp_data != 0x0)
+ opt_data = (opt_data | (1 << opt_loop));
+ }
+ return opt_data;
+}
diff --git a/include/drivers/brcm/chimp.h b/include/drivers/brcm/chimp.h
new file mode 100644
index 0000000..02d528b
--- /dev/null
+++ b/include/drivers/brcm/chimp.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SR_CHIMP_H
+#define SR_CHIMP_H
+
+#include <common/bl_common.h>
+#include <common/debug.h>
+#include <lib/mmio.h>
+
+#include <platform_def.h>
+
+#define CHIMP_WINDOW_SIZE 0x400000
+#define CHIMP_ERROR_OFFSET 28
+#define CHIMP_ERROR_MASK 0xf0000000
+
+#ifndef EMULATION_SETUP
+#define CHIMP_HANDSHAKE_TIMEOUT_MS 10000
+#else
+/*
+ * 1hr timeout for test in emulator
+ * By doing this ChiMP is given a chance to boot
+ * fully from the QSPI
+ * (on Palladium this takes upto 50 min depending on QSPI clk)
+ */
+
+#define CHIMP_HANDSHAKE_TIMEOUT_MS 3600000
+#endif
+
+#define CHIMP_BPE_MODE_ID_PATTERN (0x25000000)
+#define CHIMP_BPE_MODE_ID_MASK (0x7f000000)
+#define NIC_RESET_RELEASE_TIMEOUT_US (10)
+
+/* written by M0, used by ChiMP ROM */
+#define SR_IN_SMARTNIC_MODE_BIT 0
+/* written by M0, used by ChiMP ROM */
+#define SR_CHIMP_SECURE_BOOT_BIT 1
+/* cleared by AP, set by ChiMP BC2 code */
+#define SR_FLASH_ACCESS_DONE_BIT 2
+
+#ifdef USE_CHIMP
+void bcm_chimp_write(uintptr_t addr, uint32_t value);
+uint32_t bcm_chimp_read(uintptr_t addr);
+uint32_t bcm_chimp_read_ctrl(uint32_t offset);
+void bcm_chimp_clrbits(uintptr_t addr, uint32_t bits);
+void bcm_chimp_setbits(uintptr_t addr, uint32_t bits);
+int bcm_chimp_is_nic_mode(void);
+void bcm_chimp_fru_prog_done(bool status);
+int bcm_chimp_handshake_done(void);
+int bcm_chimp_wait_handshake(void);
+/* Fastboot-related*/
+int bcm_chimp_initiate_fastboot(int fastboot_type);
+#else
+static inline void bcm_chimp_write(uintptr_t addr, uint32_t value)
+{
+}
+static inline uint32_t bcm_chimp_read(uintptr_t addr)
+{
+ return 0;
+}
+static inline uint32_t bcm_chimp_read_ctrl(uint32_t offset)
+{
+ return 0;
+}
+static inline void bcm_chimp_clrbits(uintptr_t addr, uint32_t bits)
+{
+}
+static inline void bcm_chimp_setbits(uintptr_t addr, uint32_t bits)
+{
+}
+static inline int bcm_chimp_is_nic_mode(void)
+{
+ return 0;
+}
+static inline void bcm_chimp_fru_prog_done(bool status)
+{
+}
+static inline int bcm_chimp_handshake_done(void)
+{
+ return 0;
+}
+static inline int bcm_chimp_wait_handshake(void)
+{
+ return 0;
+}
+static inline int bcm_chimp_initiate_fastboot(int fastboot_type)
+{
+ return 0;
+}
+#endif /* USE_CHIMP */
+#endif
diff --git a/include/drivers/brcm/fru.h b/include/drivers/brcm/fru.h
new file mode 100644
index 0000000..ee863b4
--- /dev/null
+++ b/include/drivers/brcm/fru.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2019-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FRU_H
+#define FRU_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* max string length */
+#define FRU_MAX_STR_LEN 32
+
+/* max number of DDR channels */
+#define BCM_MAX_NR_DDR 3
+
+/* max supported FRU table size */
+#define BCM_MAX_FRU_LEN 512
+
+/* FRU table starting offset */
+#define BCM_FRU_TBL_OFFSET 0x300000
+
+/* FRU time constants */
+#define MINS_PER_DAY 1440
+#define MINS_PER_HOUR 60
+#define FRU_YEAR_START 1996
+#define FRU_MONTH_START 1
+#define FRU_DAY_START 1
+#define MONTHS_PER_YEAR 12
+
+/*
+ * FRU areas based on the spec
+ */
+enum fru_area_name {
+ FRU_AREA_INTERNAL = 0,
+ FRU_AREA_CHASSIS_INFO,
+ FRU_AREA_BOARD_INFO,
+ FRU_AREA_PRODUCT_INFO,
+ FRU_AREA_MRECORD_INFO,
+ FRU_MAX_NR_AREAS
+};
+
+/*
+ * FRU area information
+ *
+ * @use: indicate this area is being used
+ * @version: format version
+ * @offset: offset of this area from the beginning of the FRU table
+ * @len: total length of the area
+ */
+struct fru_area_info {
+ bool use;
+ uint8_t version;
+ unsigned int offset;
+ unsigned int len;
+};
+
+/*
+ * DDR MCB information
+ *
+ * @idx: DDR channel index
+ * @size_mb: DDR size of this channel in MB
+ * @ref_id: DDR MCB reference ID
+ */
+struct ddr_mcb {
+ unsigned int idx;
+ unsigned int size_mb;
+ uint32_t ref_id;
+};
+
+/*
+ * DDR information
+ *
+ * @ddr_info: array that contains MCB related info for each channel
+ */
+struct ddr_info {
+ struct ddr_mcb mcb[BCM_MAX_NR_DDR];
+};
+
+/*
+ * FRU board area information
+ *
+ * @lang: Language code
+ * @mfg_date: Manufacturing date
+ * @manufacturer: Manufacturer
+ * @product_name: Product name
+ * @serial_number: Serial number
+ * @part_number: Part number
+ * @file_id: FRU file ID
+ */
+struct fru_board_info {
+ unsigned char lang;
+ unsigned int mfg_date;
+ unsigned char manufacturer[FRU_MAX_STR_LEN];
+ unsigned char product_name[FRU_MAX_STR_LEN];
+ unsigned char serial_number[FRU_MAX_STR_LEN];
+ unsigned char part_number[FRU_MAX_STR_LEN];
+ unsigned char file_id[FRU_MAX_STR_LEN];
+};
+
+/*
+ * FRU manufacture date in human readable format
+ */
+struct fru_time {
+ unsigned int min;
+ unsigned int hour;
+ unsigned int day;
+ unsigned int month;
+ unsigned int year;
+};
+
+#ifdef USE_FRU
+int fru_validate(uint8_t *data, struct fru_area_info *fru_area);
+int fru_parse_ddr(uint8_t *data, struct fru_area_info *area,
+ struct ddr_info *ddr);
+int fru_parse_board(uint8_t *data, struct fru_area_info *area,
+ struct fru_board_info *board);
+void fru_format_time(unsigned int min, struct fru_time *tm);
+#else
+static inline int fru_validate(uint8_t *data, struct fru_area_info *fru_area)
+{
+ return -1;
+}
+
+static inline int fru_parse_ddr(uint8_t *data, struct fru_area_info *area,
+ struct ddr_info *ddr)
+{
+ return -1;
+}
+
+static inline int fru_parse_board(uint8_t *data, struct fru_area_info *area,
+ struct fru_board_info *board)
+{
+ return -1;
+}
+
+static inline void fru_format_time(unsigned int min, struct fru_time *tm)
+{
+}
+#endif /* USE_FRU */
+
+#endif /* FRU_H */
diff --git a/include/drivers/brcm/iproc_gpio.h b/include/drivers/brcm/iproc_gpio.h
new file mode 100644
index 0000000..be971f6
--- /dev/null
+++ b/include/drivers/brcm/iproc_gpio.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2019-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef IPROC_GPIO_H
+#define IPROC_GPIO_H
+
+#ifdef USE_GPIO
+void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
+ uintptr_t pinconf_base);
+#else
+static void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
+ uintptr_t pinconf_base)
+{
+}
+#endif /* IPROC_GPIO */
+
+#endif /* IPROC_GPIO_H */
diff --git a/include/drivers/brcm/ocotp.h b/include/drivers/brcm/ocotp.h
new file mode 100644
index 0000000..830b3e4
--- /dev/null
+++ b/include/drivers/brcm/ocotp.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef OCOTP_H
+#define OCOTP_H
+
+#include <stdint.h>
+
+struct otpc_map {
+ /* in words. */
+ uint32_t otpc_row_size;
+ /* 128 bit row / 4 words support. */
+ uint16_t data_r_offset[4];
+ /* 128 bit row / 4 words support. */
+ uint16_t data_w_offset[4];
+ int word_size;
+ int stride;
+};
+
+int bcm_otpc_init(struct otpc_map *map);
+int bcm_otpc_read(unsigned int offset, void *val, uint32_t bytes,
+ uint32_t ecc_flag);
+
+#endif /* OCOTP_H */
diff --git a/include/drivers/brcm/scp.h b/include/drivers/brcm/scp.h
new file mode 100644
index 0000000..b7b5bad
--- /dev/null
+++ b/include/drivers/brcm/scp.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SCP_H_
+#define SCP_H
+
+#include <stdint.h>
+
+int download_scp_patch(void *image, unsigned int image_size);
+
+#endif /* SCP_H */
diff --git a/include/drivers/brcm/sotp.h b/include/drivers/brcm/sotp.h
new file mode 100644
index 0000000..a93d687
--- /dev/null
+++ b/include/drivers/brcm/sotp.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016-2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SOTP_H
+#define SOTP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <platform_sotp.h>
+
+#define SOTP_ROW_NO_ECC 0
+#define SOTP_ROW_ECC 1
+
+#define SOTP_STATUS_1 (SOTP_REGS_OTP_BASE + 0x001c)
+#define SOTP_FAIL_BITS 0x18000000000
+#define SOTP_ECC_ERR_DETECT 0x8000000000000000
+
+#define SOTP_REGS_SOTP_CHIP_STATES (SOTP_REGS_OTP_BASE + 0x0028)
+#define SOTP_REGS_OTP_WR_LOCK (SOTP_REGS_OTP_BASE + 0x0038)
+
+#define SOTP_CHIP_STATES_MANU_DEBUG_MASK (1 << 8)
+#define SOTP_DEVICE_SECURE_CFG0_OTP_ERASED_MASK (3 << 16)
+#define SOTP_REGS_SOTP_CHIP_STATES_OTP_ERASED_MASK (1 << 16)
+
+#define SOTP_DEVICE_SECURE_CFG0_CID_MASK (3 << 2)
+#define SOTP_DEVICE_SECURE_CFG0_AB_MASK (3 << 6)
+#define SOTP_DEVICE_SECURE_CFG0_DEV_MASK (3 << 8)
+
+#define SOTP_BOOT_SOURCE_SHIFT 8
+/* bits 14 and 15 */
+#define SOTP_BOOT_SOURCE_ENABLE_MASK (0xC0 << SOTP_BOOT_SOURCE_SHIFT)
+/* bits 8 to 13 */
+#define SOTP_BOOT_SOURCE_BITS0 (0x03 << SOTP_BOOT_SOURCE_SHIFT)
+#define SOTP_BOOT_SOURCE_BITS1 (0x0C << SOTP_BOOT_SOURCE_SHIFT)
+#define SOTP_BOOT_SOURCE_BITS2 (0x30 << SOTP_BOOT_SOURCE_SHIFT)
+#define SOTP_BOOT_SOURCE_MASK (0x3F << SOTP_BOOT_SOURCE_SHIFT)
+
+#define SOTP_ATF_CFG_ROW_ID SOTP_DEVICE_SECURE_CFG2_ROW
+/* bits 28 and 29 */
+#define SOTP_SBL_MASK (3 << 28)
+/* bits 30 and 31 */
+#define SOTP_ATF_NVCOUNTER_ENABLE_MASK ((uint64_t)3 << 30)
+/* bits 32 and 33 */
+#define SOTP_ATF_WATCHDOG_ENABLE_MASK ((uint64_t)3 << 32)
+/* bits 34 and 35 */
+#define SOTP_ATF_PLL_ON ((uint64_t)3 << 34)
+/* bits 36 and 37 */
+#define SOTP_ATF_RESET_RETRY ((uint64_t)3 << 36)
+/* bits 38 to 40 */
+#define SOTP_ATF_LOG_LEVEL_SHIFT 38
+#define SOTP_ATF_LOG_LEVEL ((uint64_t)7 << SOTP_ATF_LOG_LEVEL_SHIFT)
+
+#define SOTP_ATF2_CFG_ROW_ID SOTP_DEVICE_SECURE_CFG3_ROW
+/* bits 16 and 17 */
+#define SOTP_ROMKEY_MASK (3 << 16)
+/* bits 18 and 19 */
+#define SOTP_EC_EN_MASK (3 << 18)
+
+#define SOTP_ENC_DEV_TYPE_AB_DEV ((uint64_t)0x19999800000)
+#define SOTP_ENC_DEV_TYPE_MASK ((uint64_t)0x1ffff800000)
+
+uint64_t sotp_mem_read(uint32_t offset, uint32_t sotp_add_ecc);
+void sotp_mem_write(uint32_t addr, uint32_t sotp_add_ecc, uint64_t wdata);
+int sotp_read_key(uint8_t *key, size_t keysize, int start_row, int end_row);
+int sotp_key_erased(void);
+uint32_t sotp_redundancy_reduction(uint32_t sotp_row_data);
+#endif
diff --git a/include/plat/brcm/common/bcm_elog.h b/include/plat/brcm/common/bcm_elog.h
new file mode 100644
index 0000000..ea4b169
--- /dev/null
+++ b/include/plat/brcm/common/bcm_elog.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef BCM_ELOG_H
+#define BCM_ELOG_H
+
+#ifndef __ASSEMBLER__
+
+#include <stdint.h>
+
+#if defined(BCM_ELOG) && (defined(IMAGE_BL2) || defined(IMAGE_BL31))
+int bcm_elog_init(void *base, uint32_t size, unsigned int level);
+void bcm_elog_exit(void);
+int bcm_elog_copy_log(void *dst, uint32_t max_size);
+void bcm_elog(const char *fmt, ...);
+#else
+static inline int bcm_elog_init(void *base, uint32_t size,
+ unsigned int level)
+{
+ return 0;
+}
+static inline void bcm_elog_exit(void)
+{
+}
+static inline int bcm_elog_copy_log(void *dst, uint32_t max_size)
+{
+ return 0;
+}
+static inline void bcm_elog(const char *fmt, ...)
+{
+}
+#endif /* BCM_ELOG */
+
+#endif /* __ASSEMBLER__ */
+#endif /* BCM_ELOG_H */
diff --git a/plat/brcm/board/common/bcm_elog.c b/plat/brcm/board/common/bcm_elog.c
new file mode 100644
index 0000000..093157e
--- /dev/null
+++ b/plat/brcm/board/common/bcm_elog.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2018 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <plat/common/platform.h>
+
+#include <bcm_elog.h>
+
+/* error logging signature */
+#define BCM_ELOG_SIG_OFFSET 0x0000
+#define BCM_ELOG_SIG_VAL 0x75767971
+
+/* current logging offset that points to where new logs should be added */
+#define BCM_ELOG_OFF_OFFSET 0x0004
+
+/* current logging length (excluding header) */
+#define BCM_ELOG_LEN_OFFSET 0x0008
+
+#define BCM_ELOG_HEADER_LEN 12
+
+/*
+ * @base: base address of memory where log is saved
+ * @max_size: max size of memory reserved for logging
+ * @is_active: indicates logging is currently active
+ * @level: current logging level
+ */
+struct bcm_elog {
+ uintptr_t base;
+ uint32_t max_size;
+ unsigned int is_active;
+ unsigned int level;
+};
+
+static struct bcm_elog global_elog;
+
+extern void memcpy16(void *dst, const void *src, unsigned int len);
+
+/*
+ * Log one character
+ */
+static void elog_putchar(struct bcm_elog *elog, unsigned char c)
+{
+ uint32_t offset, len;
+
+ offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
+ len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
+ mmio_write_8(elog->base + offset, c);
+ offset++;
+
+ /* log buffer is now full and need to wrap around */
+ if (offset >= elog->max_size)
+ offset = BCM_ELOG_HEADER_LEN;
+
+ /* only increment length when log buffer is not full */
+ if (len < elog->max_size - BCM_ELOG_HEADER_LEN)
+ len++;
+
+ mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
+ mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
+}
+
+static void elog_unsigned_num(struct bcm_elog *elog, unsigned long unum,
+ unsigned int radix)
+{
+ /* Just need enough space to store 64 bit decimal integer */
+ unsigned char num_buf[20];
+ int i = 0, rem;
+
+ do {
+ rem = unum % radix;
+ if (rem < 0xa)
+ num_buf[i++] = '0' + rem;
+ else
+ num_buf[i++] = 'a' + (rem - 0xa);
+ } while (unum /= radix);
+
+ while (--i >= 0)
+ elog_putchar(elog, num_buf[i]);
+}
+
+static void elog_string(struct bcm_elog *elog, const char *str)
+{
+ while (*str)
+ elog_putchar(elog, *str++);
+}
+
+/*
+ * Routine to initialize error logging
+ */
+int bcm_elog_init(void *base, uint32_t size, unsigned int level)
+{
+ struct bcm_elog *elog = &global_elog;
+ uint32_t val;
+
+ elog->base = (uintptr_t)base;
+ elog->max_size = size;
+ elog->is_active = 1;
+ elog->level = level / 10;
+
+ /*
+ * If a valid signature can be found, it means logs have been copied
+ * into designated memory by another software. In this case, we should
+ * not re-initialize the entry header in the designated memory
+ */
+ val = mmio_read_32(elog->base + BCM_ELOG_SIG_OFFSET);
+ if (val != BCM_ELOG_SIG_VAL) {
+ mmio_write_32(elog->base + BCM_ELOG_SIG_OFFSET,
+ BCM_ELOG_SIG_VAL);
+ mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET,
+ BCM_ELOG_HEADER_LEN);
+ mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Routine to disable error logging
+ */
+void bcm_elog_exit(void)
+{
+ struct bcm_elog *elog = &global_elog;
+
+ if (!elog->is_active)
+ return;
+
+ elog->is_active = 0;
+
+ flush_dcache_range(elog->base, elog->max_size);
+}
+
+/*
+ * Routine to copy error logs from current memory to 'dst' memory and continue
+ * logging from the new 'dst' memory.
+ * dst and base addresses must be 16-bytes aligned.
+ */
+int bcm_elog_copy_log(void *dst, uint32_t max_size)
+{
+ struct bcm_elog *elog = &global_elog;
+ uint32_t offset, len;
+
+ if (!elog->is_active || ((uintptr_t)dst == elog->base))
+ return -1;
+
+ /* flush cache before copying logs */
+ flush_dcache_range(elog->base, max_size);
+
+ /*
+ * If current offset exceeds the new max size, then that is considered
+ * as a buffer overflow situation. In this case, we reset the offset
+ * back to the beginning
+ */
+ offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
+ if (offset >= max_size) {
+ offset = BCM_ELOG_HEADER_LEN;
+ mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
+ }
+
+ /* note payload length does not include header */
+ len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
+ if (len > max_size - BCM_ELOG_HEADER_LEN) {
+ len = max_size - BCM_ELOG_HEADER_LEN;
+ mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
+ }
+
+ /* Need to copy everything including the header. */
+ memcpy16(dst, (const void *)elog->base, len + BCM_ELOG_HEADER_LEN);
+ elog->base = (uintptr_t)dst;
+ elog->max_size = max_size;
+
+ return 0;
+}
+
+/*
+ * Main routine to save logs into memory
+ */
+void bcm_elog(const char *fmt, ...)
+{
+ va_list args;
+ const char *prefix_str;
+ int bit64;
+ int64_t num;
+ uint64_t unum;
+ char *str;
+ struct bcm_elog *elog = &global_elog;
+
+ /* We expect the LOG_MARKER_* macro as the first character */
+ unsigned int level = fmt[0];
+
+ if (!elog->is_active || level > elog->level)
+ return;
+
+ prefix_str = plat_log_get_prefix(level);
+
+ while (*prefix_str != '\0') {
+ elog_putchar(elog, *prefix_str);
+ prefix_str++;
+ }
+
+ va_start(args, fmt);
+ fmt++;
+ while (*fmt) {
+ bit64 = 0;
+
+ if (*fmt == '%') {
+ fmt++;
+ /* Check the format specifier */
+loop:
+ switch (*fmt) {
+ case 'i': /* Fall through to next one */
+ case 'd':
+ if (bit64)
+ num = va_arg(args, int64_t);
+ else
+ num = va_arg(args, int32_t);
+
+ if (num < 0) {
+ elog_putchar(elog, '-');
+ unum = (unsigned long)-num;
+ } else
+ unum = (unsigned long)num;
+
+ elog_unsigned_num(elog, unum, 10);
+ break;
+ case 's':
+ str = va_arg(args, char *);
+ elog_string(elog, str);
+ break;
+ case 'x':
+ if (bit64)
+ unum = va_arg(args, uint64_t);
+ else
+ unum = va_arg(args, uint32_t);
+
+ elog_unsigned_num(elog, unum, 16);
+ break;
+ case 'l':
+ bit64 = 1;
+ fmt++;
+ goto loop;
+ case 'u':
+ if (bit64)
+ unum = va_arg(args, uint64_t);
+ else
+ unum = va_arg(args, uint32_t);
+
+ elog_unsigned_num(elog, unum, 10);
+ break;
+ default:
+ /* Exit on any other format specifier */
+ goto exit;
+ }
+ fmt++;
+ continue;
+ }
+ elog_putchar(elog, *fmt++);
+ }
+exit:
+ va_end(args);
+}
diff --git a/plat/brcm/board/common/bcm_elog_ddr.c b/plat/brcm/board/common/bcm_elog_ddr.c
new file mode 100644
index 0000000..89e7bff
--- /dev/null
+++ b/plat/brcm/board/common/bcm_elog_ddr.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2019-2020 Broadcom.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+
+#include <ddr_init.h>
+#include <scp_cmd.h>
+#include <scp_utils.h>
+#include <platform_def.h>
+
+#include "bcm_elog_ddr.h"
+#include "m0_cfg.h"
+#include "m0_ipc.h"
+
+void elog_init_ddr_log(void)
+{
+ struct elog_setup setup = {0};
+ struct elog_global_header global;
+ struct elog_meta_record rec;
+ unsigned int rec_idx = 0;
+ uint32_t log_offset;
+ uintptr_t metadata;
+ char *rec_desc[ELOG_SUPPORTED_REC_CNT] = {"SYSRESET", "THERMAL",
+ "DDR_ECC", "APBOOTLG",
+ "IDM"};
+
+ /*
+ * If this is warm boot, return immediately.
+ * We expect metadata to be initialized already
+ */
+ if (is_warmboot()) {
+ WARN("Warmboot detected, skip ELOG metadata initialization\n");
+ return;
+ }
+
+ memset(&global, 0, sizeof(global));
+
+ global.sector_size = ELOG_SECTOR_SIZE;
+ global.signature = ELOG_GLOBAL_META_HDR_SIG;
+ global.rec_count = ELOG_SUPPORTED_REC_CNT;
+
+ /* Start of logging area in DDR memory */
+ log_offset = ELOG_STORE_OFFSET;
+
+ /* Shift to the first RECORD header */
+ log_offset += 2 * global.sector_size;
+
+ /* Temporary place to hold metadata */
+ metadata = TMP_ELOG_METADATA_BASE;
+
+ memcpy((void *)metadata, &global, sizeof(global));
+ metadata += sizeof(global);
+
+ while (rec_idx < global.rec_count) {
+ memset(&rec, 0, sizeof(rec));
+
+ rec.type = rec_idx;
+ if (rec_idx == ELOG_REC_UART_LOG) {
+ rec.format = ELOG_REC_FMT_ASCII;
+ rec.src_mem_type = ELOG_SRC_MEM_TYPE_DDR;
+ rec.alt_src_mem_type = ELOG_SRC_MEM_TYPE_FS4_SCRATCH;
+ rec.src_mem_addr = BCM_ELOG_BL31_BASE;
+ rec.alt_src_mem_addr = BCM_ELOG_BL2_BASE;
+ rec.rec_size = ELOG_APBOOTLG_REC_SIZE;
+ } else if (rec_idx == ELOG_REC_IDM_LOG) {
+ rec.type = IDM_ELOG_REC_TYPE;
+ rec.format = ELOG_REC_FMT_CUSTOM;
+ rec.src_mem_type = ELOG_SRC_MEM_TYPE_DDR;
+ rec.alt_src_mem_type = ELOG_SRC_MEM_TYPE_CRMU_SCRATCH;
+ rec.src_mem_addr = ELOG_IDM_SRC_MEM_ADDR;
+ rec.alt_src_mem_addr = 0x0;
+ rec.rec_size = ELOG_DEFAULT_REC_SIZE;
+ } else {
+ rec.format = ELOG_REC_FMT_CUSTOM;
+ rec.src_mem_type = ELOG_SRC_MEM_TYPE_CRMU_SCRATCH;
+ rec.alt_src_mem_type = ELOG_SRC_MEM_TYPE_CRMU_SCRATCH;
+ rec.src_mem_addr = ELOG_USE_DEFAULT_MEM_ADDR;
+ rec.alt_src_mem_addr = ELOG_USE_DEFAULT_MEM_ADDR;
+ rec.rec_size = ELOG_DEFAULT_REC_SIZE;
+ }
+
+ rec.nvm_type = LOG_MEDIA_DDR;
+ rec.sector_size = ELOG_SECTOR_SIZE;
+
+ rec.rec_addr = (uint64_t)log_offset;
+ log_offset += rec.rec_size;
+
+ /* Sanity checks */
+ if (rec.type > ELOG_MAX_REC_COUNT ||
+ rec.format > ELOG_MAX_REC_FORMAT ||
+ (rec.nvm_type > ELOG_MAX_NVM_TYPE &&
+ rec.nvm_type != ELOG_NVM_DEFAULT) ||
+ !rec.rec_size ||
+ !rec.sector_size ||
+ rec_idx >= ELOG_SUPPORTED_REC_CNT) {
+ ERROR("Invalid ELOG record(%u) detected\n", rec_idx);
+ return;
+ }
+
+ memset(rec.rec_desc, ' ', sizeof(rec.rec_desc));
+
+ memcpy(rec.rec_desc, rec_desc[rec_idx],
+ strlen(rec_desc[rec_idx]));
+
+ memcpy((void *)metadata, &rec, sizeof(rec));
+ metadata += sizeof(rec);
+
+ rec_idx++;
+ }
+
+ setup.params[0] = TMP_ELOG_METADATA_BASE;
+ setup.params[1] = (sizeof(global) + global.rec_count * sizeof(rec));
+ setup.cmd = ELOG_SETUP_CMD_WRITE_META;
+
+ flush_dcache_range((uintptr_t)&setup, sizeof(struct elog_setup));
+ flush_dcache_range((uintptr_t)setup.params[0], setup.params[1]);
+
+ /* initialize DDR Logging METADATA if this is NOT warmboot */
+ if (!is_warmboot()) {
+ if (scp_send_cmd(MCU_IPC_MCU_CMD_ELOG_SETUP,
+ (uint32_t)(uintptr_t)(&setup),
+ SCP_CMD_DEFAULT_TIMEOUT_US)) {
+ ERROR("scp_send_cmd: timeout/error for elog setup\n");
+ return;
+ }
+ }
+
+ NOTICE("MCU Error logging initialized\n");
+}
diff --git a/plat/brcm/board/common/bcm_elog_ddr.h b/plat/brcm/board/common/bcm_elog_ddr.h
new file mode 100644
index 0000000..6f21a68
--- /dev/null
+++ b/plat/brcm/board/common/bcm_elog_ddr.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019-2020 Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef BCM_ELOG_DDR_H
+#define BCM_ELOG_DDR_H
+
+#define ELOG_GLOBAL_META_HDR_SIG 0x45524c47
+#define ELOG_MAX_REC_COUNT 13
+#define ELOG_MAX_REC_FORMAT 1
+#define ELOG_MAX_NVM_TYPE 4
+/* Use a default NVM, set by m0 configuration */
+#define ELOG_NVM_DEFAULT 0xff
+
+/* Max. number of cmd parameters per elog spec */
+#define ELOG_PARAM_COUNT 3
+/*
+ * Number of supported RECORD Types-
+ * SYSRESET, THERMAL, DDR_ECC, APBOOTLG, IDM
+ */
+#define ELOG_SUPPORTED_REC_CNT 5
+
+#define ELOG_REC_DESC_LENGTH 8
+
+#define ELOG_SECTOR_SIZE 0x1000
+
+/* Default Record size for all record types except APBOOTLOG */
+#define ELOG_DEFAULT_REC_SIZE 0x10000
+
+/* Default record size for APBOOTLOG record */
+#define ELOG_APBOOTLG_REC_SIZE 0x60000
+
+/* Use default CRMU provided mem address */
+#define ELOG_USE_DEFAULT_MEM_ADDR 0x0
+
+/* Temporary place to hold metadata */
+#define TMP_ELOG_METADATA_BASE (ELOG_AP_UART_LOG_BASE + \
+ BCM_ELOG_BL2_SIZE)
+/* IDM ELOG source memory address */
+#define ELOG_IDM_SRC_MEM_ADDR 0x8f213000
+
+#define IDM_ELOG_REC_TYPE 5
+
+enum elog_record_type {
+ ELOG_REC_SYS_RESET_EVT = 0,
+ ELOG_REC_THERMAL_EVT,
+ ELOG_REC_DDR_ECC,
+ ELOG_REC_UART_LOG,
+ ELOG_REC_IDM_LOG,
+ ELOG_REC_MAX
+};
+
+enum elog_record_format {
+ ELOG_REC_FMT_ASCII = 0,
+ ELOG_REC_FMT_CUSTOM
+};
+
+enum elog_src_memory_type {
+ ELOG_SRC_MEM_TYPE_CRMU_SCRATCH = 0,
+ ELOG_SRC_MEM_TYPE_FS4_SCRATCH,
+ ELOG_SRC_MEM_TYPE_DDR,
+ ELOG_SRC_MEM_TYPE_CHIMP_SCRATCH
+};
+
+enum elog_setup_cmd {
+ ELOG_SETUP_CMD_VALIDATE_META,
+ ELOG_SETUP_CMD_WRITE_META,
+ ELOG_SETUP_CMD_ERASE,
+ ELOG_SETUP_CMD_READ,
+ ELOG_SETUP_CMD_CHECK
+};
+
+struct elog_setup {
+ uint32_t cmd;
+ uint32_t params[ELOG_PARAM_COUNT];
+ uint32_t result;
+ uint32_t ret_code;
+};
+
+struct elog_meta_record {
+ uint8_t type;
+ uint8_t format;
+ uint8_t src_mem_type;
+ uint8_t alt_src_mem_type;
+ uint8_t nvm_type;
+ char rec_desc[ELOG_REC_DESC_LENGTH];
+ uint64_t src_mem_addr;
+ uint64_t alt_src_mem_addr;
+ uint64_t rec_addr;
+ uint32_t rec_size;
+ uint32_t sector_size;
+ uint8_t padding[3];
+} __packed;
+
+struct elog_global_header {
+ uint32_t signature;
+ uint32_t sector_size;
+ uint8_t revision;
+ uint8_t rec_count;
+ uint16_t padding;
+} __packed;
+
+void elog_init_ddr_log(void);
+
+#endif /* BCM_ELOG_DDR_H */
diff --git a/plat/brcm/board/common/board_arm_trusted_boot.c b/plat/brcm/board/common/board_arm_trusted_boot.c
new file mode 100644
index 0000000..7a4dad0
--- /dev/null
+++ b/plat/brcm/board/common/board_arm_trusted_boot.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright 2015 - 2020 Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <common/debug.h>
+#include <lib/mmio.h>
+#include <plat/common/platform.h>
+#include <tools_share/tbbr_oid.h>
+
+#include <sbl_util.h>
+#include <sotp.h>
+
+/* Weak definition may be overridden in specific platform */
+#pragma weak plat_match_rotpk
+#pragma weak plat_get_nv_ctr
+#pragma weak plat_set_nv_ctr
+
+/* SHA256 algorithm */
+#define SHA256_BYTES 32
+
+/* ROTPK locations */
+#define ARM_ROTPK_REGS_ID 1
+#define ARM_ROTPK_DEVEL_RSA_ID 2
+#define BRCM_ROTPK_SOTP_RSA_ID 3
+
+#if !ARM_ROTPK_LOCATION_ID
+ #error "ARM_ROTPK_LOCATION_ID not defined"
+#endif
+
+static const unsigned char rotpk_hash_hdr[] =
+ "\x30\x31\x30\x0D\x06\x09\x60\x86\x48"
+ "\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20";
+static const unsigned int rotpk_hash_hdr_len = sizeof(rotpk_hash_hdr) - 1;
+static unsigned char rotpk_hash_der[sizeof(rotpk_hash_hdr) - 1 + SHA256_BYTES];
+
+#if (ARM_ROTPK_LOCATION_ID == ARM_ROTPK_DEVEL_RSA_ID)
+static const unsigned char arm_devel_rotpk_hash[] =
+ "\xB0\xF3\x82\x09\x12\x97\xD8\x3A"
+ "\x37\x7A\x72\x47\x1B\xEC\x32\x73"
+ "\xE9\x92\x32\xE2\x49\x59\xF6\x5E"
+ "\x8B\x4A\x4A\x46\xD8\x22\x9A\xDA";
+#endif
+
+#pragma weak plat_rotpk_hash
+const unsigned char plat_rotpk_hash[] =
+ "\xdb\x06\x67\x95\x4f\x88\x2b\x88"
+ "\x49\xbf\x70\x3f\xde\x50\x4a\x96"
+ "\xd8\x17\x69\xd4\xa0\x6c\xba\xee"
+ "\x66\x3e\x71\x82\x2d\x95\x69\xe4";
+
+#pragma weak rom_slice
+const unsigned char rom_slice[] =
+ "\x77\x06\xbc\x98\x40\xbe\xfd\xab"
+ "\x60\x4b\x74\x3c\x9a\xb3\x80\x75"
+ "\x39\xb6\xda\x27\x07\x2e\x5b\xbf"
+ "\x5c\x47\x91\xc9\x95\x26\x26\x0c";
+
+#if (ARM_ROTPK_LOCATION_ID == BRCM_ROTPK_SOTP_RSA_ID)
+static int plat_is_trusted_boot(void)
+{
+ uint64_t section3_row0_data;
+
+ section3_row0_data = sotp_mem_read(SOTP_DEVICE_SECURE_CFG0_ROW, 0);
+
+ if ((section3_row0_data & SOTP_DEVICE_SECURE_CFG0_AB_MASK) == 0) {
+ INFO("NOT AB\n");
+ return 0;
+ }
+
+ INFO("AB\n");
+ return TRUSTED_BOARD_BOOT;
+}
+
+/*
+ * FAST AUTH is enabled if all following conditions are met:
+ * - AB part
+ * - SOTP.DEV != 0
+ * - SOTP.CID != 0
+ * - SOTP.ENC_DEV_TYPE = ENC_AB_DEV
+ * - Manuf_debug strap set high
+ */
+static int plat_fast_auth_enabled(void)
+{
+ uint32_t chip_state;
+ uint64_t section3_row0_data;
+ uint64_t section3_row1_data;
+
+ section3_row0_data =
+ sotp_mem_read(SOTP_DEVICE_SECURE_CFG0_ROW, 0);
+ section3_row1_data =
+ sotp_mem_read(SOTP_DEVICE_SECURE_CFG1_ROW, 0);
+
+ chip_state = mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES);
+
+ if (plat_is_trusted_boot() &&
+ (section3_row0_data & SOTP_DEVICE_SECURE_CFG0_DEV_MASK) &&
+ (section3_row0_data & SOTP_DEVICE_SECURE_CFG0_CID_MASK) &&
+ ((section3_row1_data & SOTP_ENC_DEV_TYPE_MASK) ==
+ SOTP_ENC_DEV_TYPE_AB_DEV) &&
+ (chip_state & SOTP_CHIP_STATES_MANU_DEBUG_MASK))
+ return 1;
+
+ return 0;
+}
+#endif
+
+/*
+ * Return the ROTPK hash in the following ASN.1 structure in DER format:
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING
+ * }
+ */
+int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len,
+ unsigned int *flags)
+{
+ uint8_t *dst;
+
+ assert(key_ptr != NULL);
+ assert(key_len != NULL);
+ assert(flags != NULL);
+
+ *flags = 0;
+
+ /* Copy the DER header */
+ memcpy(rotpk_hash_der, rotpk_hash_hdr, rotpk_hash_hdr_len);
+ dst = (uint8_t *)&rotpk_hash_der[rotpk_hash_hdr_len];
+
+#if (ARM_ROTPK_LOCATION_ID == ARM_ROTPK_DEVEL_RSA_ID)
+ memcpy(dst, arm_devel_rotpk_hash, SHA256_BYTES);
+#elif (ARM_ROTPK_LOCATION_ID == ARM_ROTPK_REGS_ID)
+ uint32_t *src, tmp;
+ unsigned int words, i;
+
+ /*
+ * Append the hash from Trusted Root-Key Storage registers. The hash has
+ * not been written linearly into the registers, so we have to do a bit
+ * of byte swapping:
+ *
+ * 0x00 0x04 0x08 0x0C 0x10 0x14 0x18 0x1C
+ * +---------------------------------------------------------------+
+ * | Reg0 | Reg1 | Reg2 | Reg3 | Reg4 | Reg5 | Reg6 | Reg7 |
+ * +---------------------------------------------------------------+
+ * | ... ... | | ... ... |
+ * | +--------------------+ | +-------+
+ * | | | |
+ * +----------------------------+ +----------------------------+
+ * | | | |
+ * +-------+ | +--------------------+ |
+ * | | | |
+ * v v v v
+ * +---------------------------------------------------------------+
+ * | | |
+ * +---------------------------------------------------------------+
+ * 0 15 16 31
+ *
+ * Additionally, we have to access the registers in 32-bit words
+ */
+ words = SHA256_BYTES >> 3;
+
+ /* Swap bytes 0-15 (first four registers) */
+ src = (uint32_t *)TZ_PUB_KEY_HASH_BASE;
+ for (i = 0 ; i < words ; i++) {
+ tmp = src[words - 1 - i];
+ /* Words are read in little endian */
+ *dst++ = (uint8_t)((tmp >> 24) & 0xFF);
+ *dst++ = (uint8_t)((tmp >> 16) & 0xFF);
+ *dst++ = (uint8_t)((tmp >> 8) & 0xFF);
+ *dst++ = (uint8_t)(tmp & 0xFF);
+ }
+
+ /* Swap bytes 16-31 (last four registers) */
+ src = (uint32_t *)(TZ_PUB_KEY_HASH_BASE + SHA256_BYTES / 2);
+ for (i = 0 ; i < words ; i++) {
+ tmp = src[words - 1 - i];
+ *dst++ = (uint8_t)((tmp >> 24) & 0xFF);
+ *dst++ = (uint8_t)((tmp >> 16) & 0xFF);
+ *dst++ = (uint8_t)((tmp >> 8) & 0xFF);
+ *dst++ = (uint8_t)(tmp & 0xFF);
+ }
+#elif (ARM_ROTPK_LOCATION_ID == BRCM_ROTPK_SOTP_RSA_ID)
+{
+ int i;
+ int ret = -1;
+
+ /*
+ * In non-AB mode, we do not read the key.
+ * In AB mode:
+ * - The Dauth is in BL11 if SBL is enabled
+ * - The Dauth is in SOTP if SBL is disabled.
+ */
+ if (plat_is_trusted_boot() == 0) {
+
+ INFO("NON-AB: Do not read DAUTH!\n");
+ *flags = ROTPK_NOT_DEPLOYED;
+ ret = 0;
+
+ } else if ((sbl_status() == SBL_ENABLED) &&
+ (mmio_read_32(BL11_DAUTH_BASE) == BL11_DAUTH_ID)) {
+
+ /* Read hash from BL11 */
+ INFO("readKeys (DAUTH) from BL11\n");
+
+ memcpy(dst,
+ (void *)(BL11_DAUTH_BASE + sizeof(uint32_t)),
+ SHA256_BYTES);
+
+ for (i = 0; i < SHA256_BYTES; i++)
+ if (dst[i] != 0)
+ break;
+
+ if (i >= SHA256_BYTES)
+ ERROR("Hash not valid from BL11\n");
+ else
+ ret = 0;
+
+ } else if (sotp_key_erased()) {
+
+ memcpy(dst, plat_rotpk_hash, SHA256_BYTES);
+
+ INFO("SOTP erased, Use internal key hash.\n");
+ ret = 0;
+
+ } else if (plat_fast_auth_enabled()) {
+
+ INFO("AB DEV: FAST AUTH!\n");
+ *flags = ROTPK_NOT_DEPLOYED;
+ ret = 0;
+
+ } else if (!(mmio_read_32(SOTP_STATUS_1) & SOTP_DAUTH_ECC_ERROR_MASK)) {
+
+ /* Read hash from SOTP */
+ ret = sotp_read_key(dst,
+ SHA256_BYTES,
+ SOTP_DAUTH_ROW,
+ SOTP_K_HMAC_ROW-1);
+
+ INFO("sotp_read_key (DAUTH): %i\n", ret);
+
+ } else {
+
+ uint64_t row_data;
+ uint32_t k;
+
+ for (k = 0; k < (SOTP_K_HMAC_ROW - SOTP_DAUTH_ROW); k++) {
+ row_data = sotp_mem_read(SOTP_DAUTH_ROW + k,
+ SOTP_ROW_NO_ECC);
+
+ if (row_data != 0)
+ break;
+ }
+
+ if (k == (SOTP_K_HMAC_ROW - SOTP_DAUTH_ROW)) {
+ INFO("SOTP NOT PROGRAMMED: Do not use DAUTH!\n");
+
+ if (sotp_mem_read(SOTP_ATF2_CFG_ROW_ID,
+ SOTP_ROW_NO_ECC) & SOTP_ROMKEY_MASK) {
+ memcpy(dst, plat_rotpk_hash, SHA256_BYTES);
+
+ INFO("Use internal key hash.\n");
+ ret = 0;
+ } else {
+ *flags = ROTPK_NOT_DEPLOYED;
+ ret = 0;
+ }
+ } else {
+ INFO("No hash found in SOTP\n");
+ }
+ }
+ if (ret)
+ return ret;
+}
+#endif
+
+ *key_ptr = (void *)rotpk_hash_der;
+ *key_len = (unsigned int)sizeof(rotpk_hash_der);
+ *flags |= ROTPK_IS_HASH;
+
+ return 0;
+}
+
+#define SOTP_NUM_BITS_PER_ROW 41
+#define SOTP_NVCTR_ROW_ALL_ONES 0x1ffffffffff
+#define SOTP_NVCTR_TRUSTED_IN_USE \
+ ((uint64_t)0x3 << (SOTP_NUM_BITS_PER_ROW-2))
+#define SOTP_NVCTR_NON_TRUSTED_IN_USE ((uint64_t)0x3)
+#define SOTP_NVCTR_TRUSTED_NEAR_END SOTP_NVCTR_NON_TRUSTED_IN_USE
+#define SOTP_NVCTR_NON_TRUSTED_NEAR_END SOTP_NVCTR_TRUSTED_IN_USE
+
+#define SOTP_NVCTR_ROW_START 64
+#define SOTP_NVCTR_ROW_END 75
+
+/*
+ * SOTP NVCTR are stored in section 10 of SOTP (rows 64-75).
+ * Each row of SOTP is 41 bits.
+ * NVCTR's are stored in a bitstream format.
+ * We are tolerant to consecutive bit errors.
+ * Trusted NVCTR starts at the top of row 64 in bitstream format.
+ * Non Trusted NVCTR starts at the bottom of row 75 in reverse bitstream.
+ * Each row can only be used by 1 of the 2 counters. This is determined
+ * by 2 zeros remaining at the beginning or end of the last available row.
+ * If one counter has already starting using a row, the other will be
+ * prevent from writing to that row.
+ *
+ * Example counter values for SOTP programmed below:
+ * Trusted Counter (rows64-69) = 5 * 41 + 40 = 245
+ * NonTrusted Counter (row75-71) = 3 * 41 + 4 = 127
+ * 40 39 38 37 36 ..... 5 4 3 2 1 0
+ * row 64 1 1 1 1 1 1 1 1 1 1 1
+ * row 65 1 1 1 1 1 1 1 1 1 1 1
+ * row 66 1 1 1 1 1 1 1 1 1 1 1
+ * row 67 1 1 1 1 1 1 1 1 1 1 1
+ * row 68 1 1 1 1 1 1 1 1 1 1 1
+ * row 69 1 1 1 1 1 1 1 1 1 1 0
+ * row 71 0 0 0 0 0 0 0 0 0 0 0
+ * row 71 0 0 0 0 0 0 0 0 0 0 0
+ * row 71 0 0 0 0 0 0 0 1 1 1 1
+ * row 73 1 1 1 1 1 1 1 1 1 1 1
+ * row 74 1 1 1 1 1 1 1 1 1 1 1
+ * row 75 1 1 1 1 1 1 1 1 1 1 1
+ *
+ */
+
+#if (DEBUG == 1)
+/*
+ * Dump sotp rows
+ */
+void sotp_dump_rows(uint32_t start_row, uint32_t end_row)
+{
+ int32_t rownum;
+ uint64_t rowdata;
+
+ for (rownum = start_row; rownum <= end_row; rownum++) {
+ rowdata = sotp_mem_read(rownum, SOTP_ROW_NO_ECC);
+ INFO("%d 0x%llx\n", rownum, rowdata);
+ }
+}
+#endif
+
+/*
+ * Get SOTP Trusted nvctr
+ */
+unsigned int sotp_get_trusted_nvctr(void)
+{
+ uint64_t rowdata;
+ uint64_t nextrowdata;
+ uint32_t rownum;
+ unsigned int nvctr;
+
+ rownum = SOTP_NVCTR_ROW_START;
+ nvctr = SOTP_NUM_BITS_PER_ROW;
+
+ /*
+ * Determine what row has last valid data for trusted ctr
+ */
+ rowdata = sotp_mem_read(rownum, SOTP_ROW_NO_ECC);
+ while ((rowdata & SOTP_NVCTR_TRUSTED_IN_USE) &&
+ (rowdata & SOTP_NVCTR_TRUSTED_NEAR_END) &&
+ (rownum < SOTP_NVCTR_ROW_END)) {
+ /*
+ * Current row in use and has data in last 2 bits as well.
+ * Check if next row also has data for this counter
+ */
+ nextrowdata = sotp_mem_read(rownum+1, SOTP_ROW_NO_ECC);
+ if (nextrowdata & SOTP_NVCTR_TRUSTED_IN_USE) {
+ /* Next row also has data so increment rownum */
+ rownum++;
+ nvctr += SOTP_NUM_BITS_PER_ROW;
+ rowdata = nextrowdata;
+ } else {
+ /* Next row does not have data */
+ break;
+ }
+ }
+
+ if (rowdata & SOTP_NVCTR_TRUSTED_IN_USE) {
+ while ((rowdata & 0x1) == 0) {
+ nvctr--;
+ rowdata >>= 1;
+ }
+ } else
+ nvctr -= SOTP_NUM_BITS_PER_ROW;
+
+ INFO("CTR %i\n", nvctr);
+ return nvctr;
+}
+
+/*
+ * Get SOTP NonTrusted nvctr
+ */
+unsigned int sotp_get_nontrusted_nvctr(void)
+{
+ uint64_t rowdata;
+ uint64_t nextrowdata;
+ uint32_t rownum;
+ unsigned int nvctr;
+
+ nvctr = SOTP_NUM_BITS_PER_ROW;
+ rownum = SOTP_NVCTR_ROW_END;
+
+ /*
+ * Determine what row has last valid data for nontrusted ctr
+ */
+ rowdata = sotp_mem_read(rownum, SOTP_ROW_NO_ECC);
+ while ((rowdata & SOTP_NVCTR_NON_TRUSTED_NEAR_END) &&
+ (rowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE) &&
+ (rownum > SOTP_NVCTR_ROW_START)) {
+ /*
+ * Current row in use and has data in last 2 bits as well.
+ * Check if next row also has data for this counter
+ */
+ nextrowdata = sotp_mem_read(rownum-1, SOTP_ROW_NO_ECC);
+ if (nextrowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE) {
+ /* Next row also has data so decrement rownum */
+ rownum--;
+ nvctr += SOTP_NUM_BITS_PER_ROW;
+ rowdata = nextrowdata;
+ } else {
+ /* Next row does not have data */
+ break;
+ }
+ }
+
+ if (rowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE) {
+ while ((rowdata & ((uint64_t)0x1 << (SOTP_NUM_BITS_PER_ROW-1)))
+ ==
+ 0) {
+ nvctr--;
+ rowdata <<= 1;
+ }
+ } else
+ nvctr -= SOTP_NUM_BITS_PER_ROW;
+
+ INFO("NCTR %i\n", nvctr);
+ return nvctr;
+}
+
+/*
+ * Set SOTP Trusted nvctr
+ */
+int sotp_set_trusted_nvctr(unsigned int nvctr)
+{
+ int numrows_available;
+ uint32_t nontrusted_rownum;
+ uint32_t trusted_rownum;
+ uint64_t rowdata;
+ unsigned int maxnvctr;
+
+ /*
+ * Read SOTP to find out how many rows are used by the
+ * NON Trusted nvctr
+ */
+ nontrusted_rownum = SOTP_NVCTR_ROW_END;
+ do {
+ rowdata = sotp_mem_read(nontrusted_rownum, SOTP_ROW_NO_ECC);
+ if (rowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE)
+ nontrusted_rownum--;
+ else
+ break;
+ } while (nontrusted_rownum >= SOTP_NVCTR_ROW_START);
+
+ /*
+ * Calculate maximum value we can have for nvctr based on
+ * number of available rows.
+ */
+ numrows_available = nontrusted_rownum - SOTP_NVCTR_ROW_START + 1;
+ maxnvctr = numrows_available * SOTP_NUM_BITS_PER_ROW;
+ if (maxnvctr) {
+ /*
+ * Last 2 bits of counter can't be written or it will
+ * overflow with nontrusted counter
+ */
+ maxnvctr -= 2;
+ }
+
+ if (nvctr > maxnvctr) {
+ /* Error - not enough room */
+ WARN("tctr not set\n");
+ return 1;
+ }
+
+ /*
+ * It is safe to write the nvctr, fill all 1's up to the
+ * last row and then fill the last row with partial bitstream
+ */
+ trusted_rownum = SOTP_NVCTR_ROW_START;
+ rowdata = SOTP_NVCTR_ROW_ALL_ONES;
+
+ while (nvctr >= SOTP_NUM_BITS_PER_ROW) {
+ sotp_mem_write(trusted_rownum, SOTP_ROW_NO_ECC, rowdata);
+ nvctr -= SOTP_NUM_BITS_PER_ROW;
+ trusted_rownum++;
+ }
+ rowdata <<= (SOTP_NUM_BITS_PER_ROW - nvctr);
+ sotp_mem_write(trusted_rownum, SOTP_ROW_NO_ECC, rowdata);
+ return 0;
+}
+
+/*
+ * Set SOTP NonTrusted nvctr
+ */
+int sotp_set_nontrusted_nvctr(unsigned int nvctr)
+{
+ int numrows_available;
+ uint32_t nontrusted_rownum;
+ uint32_t trusted_rownum;
+ uint64_t rowdata;
+ unsigned int maxnvctr;
+
+ /*
+ * Read SOTP to find out how many rows are used by the
+ * Trusted nvctr
+ */
+ trusted_rownum = SOTP_NVCTR_ROW_START;
+ do {
+ rowdata = sotp_mem_read(trusted_rownum, SOTP_ROW_NO_ECC);
+ if (rowdata & SOTP_NVCTR_TRUSTED_IN_USE)
+ trusted_rownum++;
+ else
+ break;
+ } while (trusted_rownum <= SOTP_NVCTR_ROW_END);
+
+ /*
+ * Calculate maximum value we can have for nvctr based on
+ * number of available rows.
+ */
+ numrows_available = SOTP_NVCTR_ROW_END - trusted_rownum + 1;
+ maxnvctr = numrows_available * SOTP_NUM_BITS_PER_ROW;
+ if (maxnvctr) {
+ /*
+ * Last 2 bits of counter can't be written or it will
+ * overflow with nontrusted counter
+ */
+ maxnvctr -= 2;
+ }
+
+ if (nvctr > maxnvctr) {
+ /* Error - not enough room */
+ WARN("nctr not set\n");
+ return 1;
+ }
+
+ /*
+ * It is safe to write the nvctr, fill all 1's up to the
+ * last row and then fill the last row with partial bitstream
+ */
+ nontrusted_rownum = SOTP_NVCTR_ROW_END;
+ rowdata = SOTP_NVCTR_ROW_ALL_ONES;
+
+ while (nvctr >= SOTP_NUM_BITS_PER_ROW) {
+ sotp_mem_write(nontrusted_rownum, SOTP_ROW_NO_ECC, rowdata);
+ nvctr -= SOTP_NUM_BITS_PER_ROW;
+ nontrusted_rownum--;
+ }
+ rowdata >>= (SOTP_NUM_BITS_PER_ROW - nvctr);
+ sotp_mem_write(nontrusted_rownum, SOTP_ROW_NO_ECC, rowdata);
+ return 0;
+}
+
+/*
+ * Return the non-volatile counter value stored in the platform. The cookie
+ * will contain the OID of the counter in the certificate.
+ *
+ * Return: 0 = success, Otherwise = error
+ */
+int plat_get_nv_ctr(void *cookie, unsigned int *nv_ctr)
+{
+ const char *oid;
+
+ assert(cookie != NULL);
+ assert(nv_ctr != NULL);
+
+ *nv_ctr = 0;
+ if ((sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC) &
+ SOTP_ATF_NVCOUNTER_ENABLE_MASK)) {
+ oid = (const char *)cookie;
+ if (strcmp(oid, TRUSTED_FW_NVCOUNTER_OID) == 0)
+ *nv_ctr = sotp_get_trusted_nvctr();
+ else if (strcmp(oid, NON_TRUSTED_FW_NVCOUNTER_OID) == 0)
+ *nv_ctr = sotp_get_nontrusted_nvctr();
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Store a new non-volatile counter value.
+ *
+ * Return: 0 = success, Otherwise = error
+ */
+int plat_set_nv_ctr(void *cookie, unsigned int nv_ctr)
+{
+ const char *oid;
+
+ if (sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC) &
+ SOTP_ATF_NVCOUNTER_ENABLE_MASK) {
+ INFO("set CTR %i\n", nv_ctr);
+ oid = (const char *)cookie;
+ if (strcmp(oid, TRUSTED_FW_NVCOUNTER_OID) == 0)
+ return sotp_set_trusted_nvctr(nv_ctr);
+ else if (strcmp(oid, NON_TRUSTED_FW_NVCOUNTER_OID) == 0)
+ return sotp_set_nontrusted_nvctr(nv_ctr);
+ return 1;
+ }
+ return 0;
+}
+
+int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size)
+{
+ return get_mbedtls_heap_helper(heap_addr, heap_size);
+}
diff --git a/plat/brcm/board/common/board_common.mk b/plat/brcm/board/common/board_common.mk
index 7c9cf77..3a879de 100644
--- a/plat/brcm/board/common/board_common.mk
+++ b/plat/brcm/board/common/board_common.mk
@@ -28,6 +28,17 @@
$(eval $(call add_define,SYSCNT_FREQ))
endif
+# By default, Trusted Watchdog is always enabled unless SPIN_ON_BL1_EXIT is set
+ifeq (${BRCM_DISABLE_TRUSTED_WDOG},)
+BRCM_DISABLE_TRUSTED_WDOG := 0
+endif
+ifeq (${SPIN_ON_BL1_EXIT}, 1)
+BRCM_DISABLE_TRUSTED_WDOG := 1
+endif
+
+$(eval $(call assert_boolean,BRCM_DISABLE_TRUSTED_WDOG))
+$(eval $(call add_define,BRCM_DISABLE_TRUSTED_WDOG))
+
# Process ARM_BL31_IN_DRAM flag
ifeq (${ARM_BL31_IN_DRAM},)
ARM_BL31_IN_DRAM := 0
@@ -36,6 +47,7 @@
$(eval $(call add_define,ARM_BL31_IN_DRAM))
ifeq (${STANDALONE_BL2},yes)
+BL2_LOG_LEVEL := 40
$(eval $(call add_define,MMU_DISABLED))
endif
@@ -45,6 +57,29 @@
$(eval $(call add_define,RUN_BL2_FROM_QSPI))
endif
+# BL2 XIP from NAND
+RUN_BL2_FROM_NAND := 0
+ifeq (${RUN_BL2_FROM_NAND},1)
+$(eval $(call add_define,RUN_BL2_FROM_NAND))
+endif
+
+ifneq (${ELOG_AP_UART_LOG_BASE},)
+$(eval $(call add_define,ELOG_AP_UART_LOG_BASE))
+endif
+
+ifeq (${ELOG_SUPPORT},1)
+ifeq (${ELOG_STORE_MEDIA},DDR)
+$(eval $(call add_define,ELOG_STORE_MEDIA_DDR))
+ifneq (${ELOG_STORE_OFFSET},)
+$(eval $(call add_define,ELOG_STORE_OFFSET))
+endif
+endif
+endif
+
+ifneq (${BL2_LOG_LEVEL},)
+$(eval $(call add_define,BL2_LOG_LEVEL))
+endif
+
# Use CRMU SRAM from iHOST
ifneq (${USE_CRMU_SRAM},)
$(eval $(call add_define,USE_CRMU_SRAM))
@@ -63,15 +98,18 @@
PLAT_BL_COMMON_SOURCES += plat/brcm/common/brcm_common.c \
plat/brcm/board/common/cmn_sec.c \
plat/brcm/board/common/bcm_console.c \
+ plat/brcm/board/common/brcm_mbedtls.c \
plat/brcm/board/common/plat_setup.c \
plat/brcm/board/common/platform_common.c \
drivers/arm/sp804/sp804_delay_timer.c \
+ drivers/brcm/sotp.c \
drivers/delay_timer/delay_timer.c \
drivers/io/io_fip.c \
drivers/io/io_memmap.c \
drivers/io/io_storage.c \
plat/brcm/common/brcm_io_storage.c \
plat/brcm/board/common/err.c \
+ plat/brcm/board/common/sbl_util.c \
drivers/arm/sp805/sp805.c
BL2_SOURCES += plat/brcm/common/brcm_bl2_mem_params_desc.c \
@@ -82,6 +120,88 @@
BL31_SOURCES += plat/brcm/common/brcm_bl31_setup.c
+ifeq (${BCM_ELOG},yes)
+ELOG_SOURCES += plat/brcm/board/common/bcm_elog.c
+BL2_SOURCES += ${ELOG_SOURCES}
+endif
+
+ifeq (${DRIVER_OCOTP_ENABLE},1)
+$(eval $(call add_define,DRIVER_OCOTP_ENABLE))
+BL2_SOURCES += drivers/brcm/ocotp.c
+endif
+
+# Enable FRU table support
+ifeq (${USE_FRU},yes)
+$(eval $(call add_define,USE_FRU))
+BL2_SOURCES += drivers/brcm/fru.c
+endif
+
+# Enable GPIO support
+ifeq (${USE_GPIO},yes)
+$(eval $(call add_define,USE_GPIO))
+BL2_SOURCES += drivers/gpio/gpio.c
+BL2_SOURCES += drivers/brcm/iproc_gpio.c
+ifeq (${GPIO_SUPPORT_FLOAT_DETECTION},yes)
+$(eval $(call add_define,GPIO_SUPPORT_FLOAT_DETECTION))
+endif
+endif
+
+# Include mbedtls if it can be located
+MBEDTLS_DIR := mbedtls
+MBEDTLS_CHECK := $(shell find ${MBEDTLS_DIR}/include -name '${MBEDTLS_DIR}')
+
+ifneq (${MBEDTLS_CHECK},)
+$(info Found mbedTLS at ${MBEDTLS_DIR})
+PLAT_INCLUDES += -I${MBEDTLS_DIR}/include/mbedtls
+# Specify mbedTLS configuration file
+MBEDTLS_CONFIG_FILE := "<brcm_mbedtls_config.h>"
+
+# By default, use RSA keys
+KEY_ALG := rsa_1_5
+
+# Include common TBB sources
+AUTH_SOURCES += drivers/auth/auth_mod.c \
+ drivers/auth/crypto_mod.c \
+ drivers/auth/img_parser_mod.c \
+ drivers/auth/tbbr/tbbr_cot.c
+
+BL2_SOURCES += ${AUTH_SOURCES}
+
+# Use ATF framework for MBEDTLS
+TRUSTED_BOARD_BOOT := 1
+CRYPTO_LIB_MK := drivers/auth/mbedtls/mbedtls_crypto.mk
+IMG_PARSER_LIB_MK := drivers/auth/mbedtls/mbedtls_x509.mk
+$(info Including ${CRYPTO_LIB_MK})
+include ${CRYPTO_LIB_MK}
+$(info Including ${IMG_PARSER_LIB_MK})
+include ${IMG_PARSER_LIB_MK}
+
+# Use ATF secure boot functions
+# Use Hardcoded hash for devel
+
+ARM_ROTPK_LOCATION=arm_rsa
+ifeq (${ARM_ROTPK_LOCATION}, arm_rsa)
+ARM_ROTPK_LOCATION_ID=ARM_ROTPK_DEVEL_RSA_ID
+ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem
+else ifeq (${ARM_ROTPK_LOCATION}, brcm_rsa)
+ARM_ROTPK_LOCATION_ID=BRCM_ROTPK_SOTP_RSA_ID
+ifeq (${ROT_KEY},)
+ROT_KEY=plat/brcm/board/common/rotpk/rsa_dauth2048_key.pem
+endif
+KEY_FIND := $(shell m="${ROT_KEY}"; [ -f "$$m" ] && echo "$$m")
+ifeq (${KEY_FIND},)
+$(error Error: No ${ROT_KEY} located)
+else
+$(info Using ROT_KEY: ${ROT_KEY})
+endif
+else
+$(error "Unsupported ARM_ROTPK_LOCATION value")
+endif
+
+$(eval $(call add_define,ARM_ROTPK_LOCATION_ID))
+PLAT_BL_COMMON_SOURCES+=plat/brcm/board/common/board_arm_trusted_boot.c
+endif
+
#M0 runtime firmware
ifdef SCP_BL2
$(eval $(call add_define,NEED_SCP_BL2))
diff --git a/plat/brcm/board/common/brcm_mbedtls.c b/plat/brcm/board/common/brcm_mbedtls.c
new file mode 100644
index 0000000..af42b86
--- /dev/null
+++ b/plat/brcm/board/common/brcm_mbedtls.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2015 - 2020 Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+
+void tls_exit(int code)
+{
+ INFO("%s: 0x%x\n", __func__, code);
+}
diff --git a/plat/brcm/board/common/chip_id.h b/plat/brcm/board/common/chip_id.h
new file mode 100644
index 0000000..842ac1f
--- /dev/null
+++ b/plat/brcm/board/common/chip_id.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CHIP_ID_H
+#define CHIP_ID_H
+
+#include <lib/mmio.h>
+
+#include <platform_def.h>
+
+#define CHIP_REV_MAJOR_MASK 0xF0
+#define CHIP_REV_MAJOR_AX 0x00
+#define CHIP_REV_MAJOR_BX 0x10
+#define CHIP_REV_MAJOR_CX 0x20
+#define CHIP_REV_MAJOR_DX 0x30
+
+/* Get Chip ID (product number) of the chip */
+static inline unsigned int chip_get_product_id(void)
+{
+ return PLAT_CHIP_ID_GET;
+}
+
+/* Get Revision ID (major and minor) number of the chip */
+static inline unsigned int chip_get_rev_id(void)
+{
+ return PLAT_CHIP_REV_GET;
+}
+
+static inline unsigned int chip_get_rev_id_major(void)
+{
+ return (chip_get_rev_id() & CHIP_REV_MAJOR_MASK);
+}
+
+#endif
diff --git a/plat/brcm/board/common/cmn_plat_def.h b/plat/brcm/board/common/cmn_plat_def.h
index 41d9222..8aa7fd4 100644
--- a/plat/brcm/board/common/cmn_plat_def.h
+++ b/plat/brcm/board/common/cmn_plat_def.h
@@ -7,6 +7,56 @@
#ifndef CMN_PLAT_DEF_H
#define CMN_PLAT_DEF_H
+#include <bcm_elog.h>
+
+#ifndef GET_LOG_LEVEL
+#define GET_LOG_LEVEL() LOG_LEVEL
+#endif
+
+#ifndef SET_LOG_LEVEL
+#define SET_LOG_LEVEL(x) ((void)(x))
+#endif
+
+#define PLAT_LOG_NOTICE(...) \
+ do { \
+ if (GET_LOG_LEVEL() >= LOG_LEVEL_NOTICE) { \
+ bcm_elog(LOG_MARKER_NOTICE __VA_ARGS__); \
+ tf_log(LOG_MARKER_NOTICE __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define PLAT_LOG_ERROR(...) \
+ do { \
+ if (GET_LOG_LEVEL() >= LOG_LEVEL_ERROR) { \
+ bcm_elog(LOG_MARKER_ERROR, __VA_ARGS__); \
+ tf_log(LOG_MARKER_ERROR __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define PLAT_LOG_WARN(...) \
+ do { \
+ if (GET_LOG_LEVEL() >= LOG_LEVEL_WARNING) { \
+ bcm_elog(LOG_MARKER_WARNING, __VA_ARGS__);\
+ tf_log(LOG_MARKER_WARNING __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define PLAT_LOG_INFO(...) \
+ do { \
+ if (GET_LOG_LEVEL() >= LOG_LEVEL_INFO) { \
+ bcm_elog(LOG_MARKER_INFO __VA_ARGS__); \
+ tf_log(LOG_MARKER_INFO __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define PLAT_LOG_VERBOSE(...) \
+ do { \
+ if (GET_LOG_LEVEL() >= LOG_LEVEL_VERBOSE) { \
+ bcm_elog(LOG_MARKER_VERBOSE __VA_ARGS__);\
+ tf_log(LOG_MARKER_VERBOSE __VA_ARGS__); \
+ } \
+ } while (0)
+
/* Print file and line number on assert */
#define PLAT_LOG_LEVEL_ASSERT LOG_LEVEL_INFO
diff --git a/plat/brcm/board/common/platform_common.c b/plat/brcm/board/common/platform_common.c
index 9bae83a..f4c9a73 100644
--- a/plat/brcm/board/common/platform_common.c
+++ b/plat/brcm/board/common/platform_common.c
@@ -6,14 +6,65 @@
#include <arch_helpers.h>
#include <common/debug.h>
+#include <drivers/brcm/sotp.h>
#include <cmn_plat_util.h>
#include <platform_def.h>
uint32_t boot_source_get(void)
{
- /* For now return BOOT_SOURCE_QSPI */
- return BOOT_SOURCE_QSPI;
+ uint32_t data;
+
+#ifdef FORCE_BOOTSOURCE
+ data = FORCE_BOOTSOURCE;
+#else
+ /* Read primary boot strap from CRMU persistent registers */
+ data = mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG1);
+ if (data & BOOT_SOURCE_SOFT_ENABLE_MASK) {
+ data >>= BOOT_SOURCE_SOFT_DATA_OFFSET;
+ } else {
+ uint64_t sotp_atf_row;
+
+ sotp_atf_row =
+ sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC);
+
+ if (sotp_atf_row & SOTP_BOOT_SOURCE_ENABLE_MASK) {
+ /* Construct the boot source based on SOTP bits */
+ data = 0;
+ if (sotp_atf_row & SOTP_BOOT_SOURCE_BITS0)
+ data |= 0x1;
+ if (sotp_atf_row & SOTP_BOOT_SOURCE_BITS1)
+ data |= 0x2;
+ if (sotp_atf_row & SOTP_BOOT_SOURCE_BITS2)
+ data |= 0x4;
+ } else {
+
+ /*
+ * This path is for L0 reset with
+ * Primary Boot source disabled in SOTP.
+ * BOOT_SOURCE_FROM_PR_ON_L1 compile flag will allow
+ * to never come back here so that the
+ * external straps will not be read on L1 reset.
+ */
+
+ /* Use the external straps */
+ data = mmio_read_32(ROM_S0_IDM_IO_STATUS);
+
+#ifdef BOOT_SOURCE_FROM_PR_ON_L1
+ /* Enable boot source read from PR#1 */
+ mmio_setbits_32(CRMU_IHOST_SW_PERSISTENT_REG1,
+ BOOT_SOURCE_SOFT_ENABLE_MASK);
+
+ /* set boot source */
+ data &= BOOT_SOURCE_MASK;
+ mmio_clrsetbits_32(CRMU_IHOST_SW_PERSISTENT_REG1,
+ BOOT_SOURCE_MASK << BOOT_SOURCE_SOFT_DATA_OFFSET,
+ data << BOOT_SOURCE_SOFT_DATA_OFFSET);
+#endif
+ }
+ }
+#endif
+ return (data & BOOT_SOURCE_MASK);
}
void __dead2 plat_soft_reset(uint32_t reset)
diff --git a/plat/brcm/board/common/sbl_util.c b/plat/brcm/board/common/sbl_util.c
new file mode 100644
index 0000000..06e5b33
--- /dev/null
+++ b/plat/brcm/board/common/sbl_util.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+
+#include <common/debug.h>
+
+#include <platform_def.h>
+#include <sbl_util.h>
+#include <sotp.h>
+
+#pragma weak plat_sbl_status
+
+int plat_sbl_status(uint64_t sbl_status)
+{
+ return sbl_status ? 1:0;
+}
+
+int sbl_status(void)
+{
+ uint64_t sbl_sotp = 0;
+ int ret = SBL_DISABLED;
+
+ sbl_sotp = sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC);
+
+ if (sbl_sotp != SOTP_ECC_ERR_DETECT) {
+
+ sbl_sotp &= SOTP_SBL_MASK;
+
+ if (plat_sbl_status(sbl_sotp))
+ ret = SBL_ENABLED;
+ }
+
+ VERBOSE("SBL status: %d\n", ret);
+
+ return ret;
+}
diff --git a/plat/brcm/board/common/sbl_util.h b/plat/brcm/board/common/sbl_util.h
new file mode 100644
index 0000000..0747389
--- /dev/null
+++ b/plat/brcm/board/common/sbl_util.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 - 2020, Broadcom
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SBL_UTIL_H
+#define SBL_UTIL_H
+
+#include <stdint.h>
+
+#include <sotp.h>
+
+#define SBL_DISABLED 0
+#define SBL_ENABLED 1
+
+int sbl_status(void);
+
+#endif /* #ifdef SBL_UTIL_H */
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;
+}