soc/mediatek/mt8196: Add tracker driver

Tracker is a debugging tool, including AP/INFRA/PERI tracker. When bus
timeout occurs, the system reboots and latches some values which could
be used for debug.

Rename VLPCFG_BASE to VLP_CFG_BASE.

TEST=Build pass, When we encounter a bus hang and HW watchdog triggers
a reset to the platform, the tracker will print the
latched information:
[INFO ]  **Dump %s aw debug register start**
[INFO ]  xxxxxx, 0x1c600000, 0x0, 63
This means that the 63rd entry latch accessing 0x1c600000 has a bus
timeout.

BUG=b:317009620

Signed-off-by: Xiwen Shao <xiwen.shao@mediatek.corp-partner.google.com>
Change-Id: Ib9784a370acec45ce36a800f3955b9cf96651298
Reviewed-on: https://review.coreboot.org/c/coreboot/+/84929
Reviewed-by: Yidi Lin <yidilin@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
diff --git a/src/soc/mediatek/mt8196/Makefile.mk b/src/soc/mediatek/mt8196/Makefile.mk
index 13ba5c3..2af541f 100644
--- a/src/soc/mediatek/mt8196/Makefile.mk
+++ b/src/soc/mediatek/mt8196/Makefile.mk
@@ -19,6 +19,7 @@
 bootblock-y += ../common/mmu_operations.c
 bootblock-y +=  mtcmos.c
 bootblock-$(CONFIG_PCI) += ../common/pcie.c pcie.c
+bootblock-y += tracker.c
 bootblock-y += ../common/wdt.c ../common/wdt_req.c wdt.c
 
 romstage-y += ../common/cbmem.c
diff --git a/src/soc/mediatek/mt8196/bootblock.c b/src/soc/mediatek/mt8196/bootblock.c
index d675a5a..95555d1 100644
--- a/src/soc/mediatek/mt8196/bootblock.c
+++ b/src/soc/mediatek/mt8196/bootblock.c
@@ -8,12 +8,14 @@
 #include <soc/mmu_operations.h>
 #include <soc/pll.h>
 #include <soc/spm_mtcmos.h>
+#include <soc/tracker.h>
 #include <soc/wdt.h>
 
 void bootblock_soc_init(void)
 {
 	booker_init();
 	mtk_mmu_init();
+	bustracker_init();
 	lastbus_init();
 	mtk_wdt_init();
 	mt_pll_init();
diff --git a/src/soc/mediatek/mt8196/include/soc/addressmap.h b/src/soc/mediatek/mt8196/include/soc/addressmap.h
index 3de16fd..dc21f60 100644
--- a/src/soc/mediatek/mt8196/include/soc/addressmap.h
+++ b/src/soc/mediatek/mt8196/include/soc/addressmap.h
@@ -143,7 +143,7 @@
 	APINFRA_SSR_AO_DEBUG_BASE	= IO_PHYS + 0x080F1000,
 	AUDIO_BASE		= IO_PHYS + 0x0A110000,
 	VLP_AO_BASE		= IO_PHYS + 0x0C000000,
-	VLPCFG_BASE		= IO_PHYS + 0x0C001000,
+	VLP_CFG_BASE		= IO_PHYS + 0x0C001000,
 	SPM_BASE		= IO_PHYS + 0x0C004000,
 	SPM_PBUS_BASE		= IO_PHYS + 0x0C00D000,
 	RGU_BASE		= IO_PHYS + 0x0C010000,
diff --git a/src/soc/mediatek/mt8196/include/soc/tracker.h b/src/soc/mediatek/mt8196/include/soc/tracker.h
new file mode 100644
index 0000000..c82ee83
--- /dev/null
+++ b/src/soc/mediatek/mt8196/include/soc/tracker.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+
+#ifndef SOC_MEDIATEK_MT8196_TRACKER_H
+#define SOC_MEDIATEK_MT8196_TRACKER_H
+
+#include <soc/tracker_common.h>
+#include <stdint.h>
+
+#define BUS_DBG_CON			0x000
+#define BUS_TRACE_CON_AO_PRESCALE	0x8f8
+#define BUS_TRACE_CON_AO_1_PRESCALE	0x9f8
+#define VLP_TRACE_CON_AO_PRESCALE	0x114
+#define BUS_TRACE_CON_1			0x800
+#define BUS_TRACE_CON_AO_1		0x8FC
+#define BUS_TRACE_CON_2			0x900
+#define BUS_TRACE_CON_AO_2		0x9FC
+#define VLP_CON_AO			0x30C
+#define BUS_TRACE_EN			16
+
+#define SYS_TRACK_ENTRY			64
+#define INFRA_ENTRY_NUM			32
+#define VLP_ENTRY_NUM			4
+
+#define AR_TRACK_LOG_OFFSET		0x0200
+#define AR_ENTRY_ID_OFFSET		0x0300
+#define AR_TRACK_L_OFFSET		0x0400
+#define AR_TRACK_H_OFFSET		0x0600
+#define AW_TRACK_LOG_OFFSET		0x0800
+#define AW_ENTRY_ID_OFFSET		0x0900
+#define AW_TRACK_L_OFFSET		0x0A00
+#define AW_TRACK_H_OFFSET		0x0C00
+
+#define BUSTRACKER_TIMEOUT		0x300
+
+enum {
+	TRACKER_SYSTRACKER = 0,
+	TRACKER_INFRATRACKER,
+	TRACKER_VLPSYSTRACKER,
+	TRACKER_NUM,
+};
+
+#endif
diff --git a/src/soc/mediatek/mt8196/tracker.c b/src/soc/mediatek/mt8196/tracker.c
new file mode 100644
index 0000000..1fc03b99
--- /dev/null
+++ b/src/soc/mediatek/mt8196/tracker.c
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <commonlib/bsd/helpers.h>
+#include <console/console.h>
+#include <device/mmio.h>
+#include <soc/addressmap.h>
+#include <soc/tracker.h>
+
+/*
+ * for systracker:
+ * offset[0] dump from offset 0x100 ~ 0x2F8
+ * offset[1] dump from offset 0x300 ~ 0x4FC
+ *
+ * for infra tracker:
+ * offset[0] dump from offset 0x100 ~ 0x1F8
+ * offset[1] dump from offset 0x300 ~ 0x3FC
+ *
+ * for vlpsys tracker:
+ * offset[0] dump from offset 0x100 ~ 0x2F8
+ * offset[1] dump from offset 0x300 ~ 0x4FC
+ */
+
+static const u32 offsets[] = {
+	AR_TRACK_LOG_OFFSET, AR_ENTRY_ID_OFFSET, AR_TRACK_L_OFFSET,
+	AR_TRACK_H_OFFSET, AW_TRACK_LOG_OFFSET, AW_ENTRY_ID_OFFSET,
+	AW_TRACK_L_OFFSET, AW_TRACK_H_OFFSET,
+};
+
+struct tracker tracker_data[TRACKER_NUM] = {
+	[TRACKER_SYSTRACKER] = {
+		.base_addr = INFRA_TRACKER_BASE,
+		.timeout = BUS_DBG_CON_TIMEOUT,
+		.entry = SYS_TRACK_ENTRY,
+		.offsets = offsets,
+		.offsets_size = ARRAY_SIZE(offsets),
+		.str = "systracker",
+	},
+	[TRACKER_INFRATRACKER] = {
+		.base_addr = INFRA_TRACKER_BASE,
+		.timeout = BUS_DBG_CON_TIMEOUT,
+		.entry = INFRA_ENTRY_NUM,
+		.offsets = offsets,
+		.offsets_size = ARRAY_SIZE(offsets),
+		.str = "infra_tracker",
+	},
+	[TRACKER_VLPSYSTRACKER] = {
+		.base_addr = VLP_TRACKER_BASE,
+		.timeout = BUS_DBG_CON_TIMEOUT,
+		.entry = VLP_ENTRY_NUM,
+		.offsets = offsets,
+		.offsets_size = ARRAY_SIZE(offsets),
+		.str = "vlp_tracker",
+	},
+};
+
+void tracker_setup(void)
+{
+	u32 val;
+	/*
+	 * Set infra/peri tracker timeout.
+	 * timeout = clock_in_mhz / 15 * timeout_in_us
+	 *
+	 * timeout: 10ms
+	 * ap tracker clock: 26MHz
+	 * infra tracker clock: 26MHz
+	 * vlp tracker clock: 26MHz
+	 */
+	val = 26 / 15 * 10000;
+
+	write32p(BUS_TRACE_MONITOR_BASE + BUS_TRACE_CON_AO_PRESCALE, val);
+	write32p(BUS_TRACE_MONITOR_BASE + BUS_TRACE_CON_AO_1_PRESCALE, val);
+	write32p(VLP_CFG_BASE + VLP_TRACE_CON_AO_PRESCALE, val);
+	/*
+	 * Enable infra/peri tracker.
+	 * bit[0] - BUS_DBG_EN
+	 * bit[1] - TIMEOUT_EN
+	 * bit[2] - SLV_ERR_EN
+	 * bit[13] - HALT_ON_TIMEOUT_EN
+	 * bit[14] - BUS_OT_WEN_CTRL
+	 */
+	val = BIT(0) | BIT(1) | BIT(2) | BIT(13) | BIT(14);
+	write32p(VLP_AO_BASE + VLP_CON_AO, val);
+	write32p(BUS_TRACE_MONITOR_BASE + BUS_TRACE_CON_AO_1, val);
+	write32p(BUS_TRACE_MONITOR_BASE + BUS_TRACE_CON_AO_2, val);
+}
+
+static void tracker_dump_data(void)
+{
+	u64 reg_entry;
+	int i, j;
+	uintptr_t reg_log, reg_id, reg_low, reg_high;
+	struct tracker *tra;
+
+	for (j = 0; j < TRACKER_NUM; j++) {
+		tra = &tracker_data[j];
+
+		if (!(read32p(tra->base_addr) & tra->timeout))
+			continue;
+		printk(BIOS_INFO, "**Dump %s ar debug register start**\n", tra->str);
+		for (i = 0; i < tra->entry; i++) {
+			reg_log = tra->base_addr + tra->offsets[0] + i * 4;
+			reg_id = tra->base_addr + tra->offsets[1] + i * 4;
+			reg_low = tra->base_addr + tra->offsets[2] + i * 4;
+			reg_high = tra->base_addr + tra->offsets[3] + i * 4;
+			reg_entry = ((u64)read32p(reg_high)) << 32 | read32p(reg_low);
+			printk(BIOS_INFO, "%#lx:%#x:%#x:%#x:%#x:%#llx\n",
+			       reg_low, read32p(reg_log), read32p(reg_id), read32p(reg_low),
+			       read32p(reg_high), reg_entry);
+		}
+		printk(BIOS_INFO, "**Dump %s aw debug register start**\n", tra->str);
+		for (i = 0; i < tra->entry; i++) {
+			reg_log = tra->base_addr + tra->offsets[4] + i * 4;
+			reg_id = tra->base_addr + tra->offsets[5] + i * 4;
+			reg_low = tra->base_addr + tra->offsets[6] + i * 4;
+			reg_high = tra->base_addr + tra->offsets[7] + i * 4;
+			reg_entry = ((u64)read32p(reg_high)) << 32 | read32p(reg_low);
+			printk(BIOS_INFO, "%#lx:%#x:%#x:%#x:%#x:%#llx\n",
+			       reg_low, read32p(reg_log), read32p(reg_id), read32p(reg_low),
+			       read32p(reg_high), reg_entry);
+		}
+		printk(BIOS_INFO, "**Dump %s debug register end**\n", tra->str);
+	}
+}
+
+void bustracker_init(void)
+{
+	tracker_dump_data();
+	tracker_setup();
+}