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;
+}