Add BL2 support for Broadcom stingray platform

Change-Id: I5daa3f2b4b9d85cb857547a588571a9aa8ad05c2
Signed-off-by: Sheetal Tigadoli <sheetal.tigadoli@broadcom.com>
diff --git a/plat/brcm/board/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);
+}