soc/intel/alderlake: Add CrashLog implementation for Intel ADL

This enables CrashLog for Intel ADL based platform.

BUG=b:183981959
TEST=CrashLog data generated, extracted, processed and decoded sucessfully on adl-m RVP.

Signed-off-by: Francois Toguo <francois.toguo.fotso@intel.com>
Change-Id: I15ba0b41f73c1772f09584f13bcf5585caa90782
Reviewed-on: https://review.coreboot.org/c/coreboot/+/52454
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/soc/intel/alderlake/Kconfig b/src/soc/intel/alderlake/Kconfig
index 7480a41..79e775d 100644
--- a/src/soc/intel/alderlake/Kconfig
+++ b/src/soc/intel/alderlake/Kconfig
@@ -298,4 +298,11 @@
 	int
 	default 16
 
+config SOC_INTEL_CRASHLOG
+	def_bool n
+	select SOC_INTEL_COMMON_BLOCK_CRASHLOG
+	select ACPI_BERT
+	help
+	  Enables CrashLog.
+
 endif
diff --git a/src/soc/intel/alderlake/Makefile.inc b/src/soc/intel/alderlake/Makefile.inc
index 7252085..9f92c92 100644
--- a/src/soc/intel/alderlake/Makefile.inc
+++ b/src/soc/intel/alderlake/Makefile.inc
@@ -45,6 +45,7 @@
 ramstage-y += soundwire.c
 ramstage-y += systemagent.c
 ramstage-y += xhci.c
+ramstage-$(CONFIG_SOC_INTEL_CRASHLOG) += crashlog.c
 
 smm-y += elog.c
 smm-y += gpio.c
diff --git a/src/soc/intel/alderlake/chipset.cb b/src/soc/intel/alderlake/chipset.cb
index 19411a6..53d75e4 100644
--- a/src/soc/intel/alderlake/chipset.cb
+++ b/src/soc/intel/alderlake/chipset.cb
@@ -34,7 +34,7 @@
 		end
 		device pci 08.0 alias gna off end
 		device pci 09.0 alias north_tracehub off end
-		device pci 0a.0 alias crashlog off end
+		device pci 0a.0 alias crashlog on end
 		device pci 0d.0 alias tcss_xhci off
 			chip drivers/usb/acpi
 				register "type" = "UPC_TYPE_HUB"
diff --git a/src/soc/intel/alderlake/crashlog.c b/src/soc/intel/alderlake/crashlog.c
new file mode 100644
index 0000000..c1eeedf
--- /dev/null
+++ b/src/soc/intel/alderlake/crashlog.c
@@ -0,0 +1,303 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <console/console.h>
+#include <cbmem.h>
+#include <delay.h>
+#include <intelblocks/crashlog.h>
+#include <string.h>
+#include <soc/crashlog.h>
+#include <arch/bert_storage.h>
+#include <soc/iomap.h>
+#include <soc/pci_devs.h>
+
+/* global crashLog info */
+static bool m_pmc_crashLog_support;
+static bool m_pmc_crashLog_present;
+static bool m_cpu_crashLog_support;
+static bool m_cpu_crashLog_present;
+static u32 m_pmc_crashLog_size;
+static u32 m_cpu_crashLog_size;
+static u32 cpu_crash_version;
+static pmc_ipc_discovery_buf_t discovery_buf;
+static pmc_crashlog_desc_table_t descriptor_table;
+static tel_crashlog_devsc_cap_t cpu_cl_devsc_cap;
+static cpu_crashlog_discovery_table_t cpu_cl_disc_tab;
+
+u32 __weak cl_get_cpu_mb_int_addr(void)
+{
+	return CRASHLOG_MAILBOX_INTF_ADDRESS;
+}
+
+bool pmc_cl_discovery(void)
+{
+	u32 tmp_bar_addr = 0, desc_table_addr = 0;
+
+	const struct pmc_ipc_buffer *req = { 0 };
+	struct pmc_ipc_buffer *res = NULL;
+	uint32_t cmd_reg;
+	int r;
+
+	cmd_reg = pmc_make_ipc_cmd(PMC_IPC_CMD_CRASHLOG,
+				PMC_IPC_CMD_ID_CRASHLOG_DISCOVERY,
+				PMC_IPC_CMD_SIZE_SHIFT);
+	printk(BIOS_DEBUG, "cmd_reg from pmc_make_ipc_cmd %d\n", cmd_reg);
+
+	r = pmc_send_ipc_cmd(cmd_reg, req, res);
+
+	if (r < 0) {
+		printk(BIOS_ERR, "pmc_send_ipc_cmd failed in %s\n", __func__);
+		return false;
+	}
+	discovery_buf.val_64_bits = ((u64)res->buf[1] << 32) | res->buf[0];
+
+
+	if (discovery_buf.bits.supported != 1) {
+		printk(BIOS_INFO, "PCH crashlog feature not supported.\n");
+		m_pmc_crashLog_support = false;
+		return false;
+	}
+	m_pmc_crashLog_support = true;
+
+	/* Program BAR 0 and enable command register memory space decoding */
+	tmp_bar_addr = SPI_BASE_ADDRESS;
+	pci_write_config32(PCH_DEV_SRAM, PCI_BASE_ADDRESS_0, tmp_bar_addr);
+	pci_or_config16(PCH_DEV_SRAM, PCI_COMMAND, PCI_COMMAND_MEMORY);
+
+	if (discovery_buf.bits.discov_mechanism == 1) {
+		/* discovery mode */
+		if (discovery_buf.bits.base_offset & BIT(31)) {
+			printk(BIOS_DEBUG, "PCH discovery to be used is disabled.\n");
+			m_pmc_crashLog_present = false;
+			m_pmc_crashLog_size = 0;
+			return false;
+		}
+		desc_table_addr = tmp_bar_addr + discovery_buf.bits.desc_tabl_offset;
+		m_pmc_crashLog_size = pmc_cl_gen_descriptor_table(desc_table_addr,
+								  &descriptor_table);
+		printk(BIOS_DEBUG, "PMC CrashLog size in discovery mode: 0x%X\n",
+		       m_pmc_crashLog_size);
+	} else {
+		/* legacy mode */
+		if (discovery_buf.bits.dis) {
+			printk(BIOS_DEBUG, "PCH crashlog is disabled in legacy mode.\n");
+			m_pmc_crashLog_present = false;
+			return false;
+		}
+		m_pmc_crashLog_size = (discovery_buf.bits.size != 0) ?
+					(discovery_buf.bits.size * sizeof(u32)) : 0xC00;
+		printk(BIOS_DEBUG, "PMC crashlog size in legacy mode = 0x%x\n",
+			m_pmc_crashLog_size);
+	}
+	m_pmc_crashLog_present = true;
+
+	return true;
+}
+
+u32 cl_get_cpu_bar_addr(void)
+{
+	u32 base_addr = 0;
+	if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR0) {
+		base_addr = pci_read_config32(SA_DEV_TMT, PCI_BASE_ADDRESS_0) &
+				~PCI_BASE_ADDRESS_MEM_ATTR_MASK;
+	} else if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR1) {
+		base_addr = pci_read_config32(SA_DEV_TMT, PCI_BASE_ADDRESS_1) &
+				~PCI_BASE_ADDRESS_MEM_ATTR_MASK;
+	} else {
+		printk(BIOS_ERR, "Invalid TEL_CFG_BAR value %d, discovery failure expected.\n",
+			cpu_cl_devsc_cap.discovery_data.fields.t_bir_q);
+	}
+
+	return base_addr;
+}
+
+
+u32 cl_get_cpu_tmp_bar(void)
+{
+	return SPI_BASE_ADDRESS;
+}
+
+bool  cl_pmc_sram_has_mmio_access(void)
+{
+	if (pci_read_config16(PCH_DEV_SRAM, PCI_VENDOR_ID) == 0xFFFF) {
+		printk(BIOS_ERR, "PMC SSRAM PCI device disabled. Can be enabled in device tree.\n");
+		return false;
+	}
+
+	return true;
+}
+
+static bool cpu_cl_get_capability(tel_crashlog_devsc_cap_t *cl_devsc_cap)
+{
+	cl_devsc_cap->cap_data.data = pci_read_config32(SA_DEV_TMT,
+						TEL_DVSEC_OFFSET + TEL_DVSEC_PCIE_CAP_ID);
+	if (cl_devsc_cap->cap_data.fields.pcie_cap_id != TELEMETRY_EXTENDED_CAP_ID) {
+		printk(BIOS_DEBUG, "Read ID for Telemetry: 0x%x differs from expected: 0x%x\n",
+		       cl_devsc_cap->cap_data.fields.pcie_cap_id, TELEMETRY_EXTENDED_CAP_ID);
+		return false;
+	}
+
+	/* walk through the entries until crashLog entry */
+	cl_devsc_cap->devsc_data.data_32[1] = pci_read_config32(SA_DEV_TMT, TEL_DVSEV_ID);
+	int new_offset = 0;
+	while (cl_devsc_cap->devsc_data.fields.devsc_id != CRASHLOG_DVSEC_ID) {
+		if (cl_devsc_cap->cap_data.fields.next_cap_offset == 0
+		     || cl_devsc_cap->cap_data.fields.next_cap_offset == 0xFFFF) {
+			printk(BIOS_DEBUG, "Read invalid pcie_cap_id value: 0x%x\n",
+			       cl_devsc_cap->cap_data.fields.pcie_cap_id);
+			return false;
+		}
+		new_offset = cl_devsc_cap->cap_data.fields.next_cap_offset;
+		cl_devsc_cap->cap_data.data = pci_read_config32(SA_DEV_TMT,
+						new_offset + TEL_DVSEC_PCIE_CAP_ID);
+		cl_devsc_cap->devsc_data.data_32[1] = pci_read_config32(SA_DEV_TMT,
+							new_offset + TEL_DVSEV_ID);
+	}
+	cpu_crash_version = cl_devsc_cap->devsc_data.fields.devsc_ver;
+
+	cl_devsc_cap->discovery_data.data = pci_read_config32(SA_DEV_TMT, new_offset
+						+ TEL_DVSEV_DISCOVERY_TABLE_OFFSET);
+
+	return true;
+}
+
+
+static bool cpu_cl_gen_discovery_table(void)
+{
+	u32 bar_addr = 0, disc_tab_addr = 0;
+	bar_addr = cl_get_cpu_bar_addr();
+	disc_tab_addr = bar_addr +
+			cpu_cl_devsc_cap.discovery_data.fields.discovery_table_offset;
+	memset(&cpu_cl_disc_tab, 0, sizeof(cpu_crashlog_discovery_table_t));
+
+	printk(BIOS_DEBUG, "cpu discovery table offset: 0x%x\n",
+		cpu_cl_devsc_cap.discovery_data.fields.discovery_table_offset);
+
+	cpu_cl_disc_tab.header.data = ((u64)read32((u32 *)disc_tab_addr) +
+				     ((u64)read32((u32 *)(disc_tab_addr + 4)) << 32));
+
+	cpu_cl_disc_tab.cmd_mailbox.data = read32((u32 *)(disc_tab_addr + 8));
+	cpu_cl_disc_tab.mailbox_data = read32((u32 *)(disc_tab_addr + 12));
+
+	printk(BIOS_DEBUG, "cpu_crashlog_discovery_table buffer count: 0x%x\n",
+		cpu_cl_disc_tab.header.fields.count);
+
+	int cur_offset = 0;
+	for (int i = 0; i < cpu_cl_disc_tab.header.fields.count; i++) {
+		cur_offset = 16 + 8*i;
+		cpu_cl_disc_tab.buffers[i].data = ((u64)read32((u32 *)(disc_tab_addr +
+						cur_offset)) + ((u64)read32((u32 *)
+						(disc_tab_addr + cur_offset + 4)) << 32));
+		printk(BIOS_DEBUG, "cpu_crashlog_discovery_table buffer: 0x%x size: "
+			"0x%x offset: 0x%x\n", i,  cpu_cl_disc_tab.buffers[i].fields.size,
+			cpu_cl_disc_tab.buffers[i].fields.offset);
+		m_cpu_crashLog_size += cpu_cl_disc_tab.buffers[i].fields.size * sizeof(u32);
+	}
+
+	m_cpu_crashLog_present = m_cpu_crashLog_size > 0;
+
+	return true;
+}
+
+bool cpu_cl_discovery(void)
+{
+	memset(&cpu_cl_devsc_cap, 0, sizeof(tel_crashlog_devsc_cap_t));
+
+	if (!cpu_cl_get_capability(&cpu_cl_devsc_cap)) {
+		printk(BIOS_ERR, "CPU crashlog capability not found.\n");
+		m_cpu_crashLog_support = false;
+		return false;
+	}
+
+	m_cpu_crashLog_support = true;
+
+	/* Program BAR address and enable command register memory space decoding */
+	u32 tmp_bar_addr = PCH_PWRM_BASE_ADDRESS;
+	printk(BIOS_DEBUG, "tmp_bar_addr: 0x%X\n", tmp_bar_addr);
+
+	if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR0) {
+		pci_write_config32(SA_DEV_TMT, PCI_BASE_ADDRESS_0, tmp_bar_addr);
+	} else if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR1) {
+		pci_write_config32(SA_DEV_TMT, PCI_BASE_ADDRESS_1, tmp_bar_addr);
+	} else {
+		printk(BIOS_DEBUG, "invalid discovery data t_bir_q: 0x%x\n",
+			cpu_cl_devsc_cap.discovery_data.fields.t_bir_q);
+		return false;
+	}
+	pci_or_config16(SA_DEV_TMT, PCI_COMMAND, PCI_COMMAND_MEMORY);
+
+	if (!cpu_cl_gen_discovery_table()) {
+		printk(BIOS_ERR, "CPU crashlog discovery table not valid.\n");
+		m_cpu_crashLog_present = false;
+		return false;
+	}
+	m_cpu_crashLog_present = true;
+
+	return true;
+}
+
+void reset_discovery_buffers(void)
+{
+	memset(&discovery_buf, 0, sizeof(pmc_ipc_discovery_buf_t));
+	memset(&descriptor_table, 0, sizeof(pmc_crashlog_desc_table_t));
+	memset(&cpu_cl_devsc_cap, 0, sizeof(tel_crashlog_devsc_cap_t));
+}
+
+int cl_get_total_data_size(void)
+{
+	return m_pmc_crashLog_size + m_cpu_crashLog_size;
+}
+
+pmc_ipc_discovery_buf_t cl_get_pmc_discovery_buf(void)
+{
+	return discovery_buf;
+}
+
+pmc_crashlog_desc_table_t cl_get_pmc_descriptor_table(void)
+{
+	return descriptor_table;
+}
+
+int cl_get_pmc_record_size(void)
+{
+	return m_pmc_crashLog_size;
+}
+
+int cl_get_cpu_record_size(void)
+{
+	return m_cpu_crashLog_size;
+}
+
+bool cl_cpu_data_present(void)
+{
+	return m_cpu_crashLog_present;
+}
+
+bool cl_pmc_data_present(void)
+{
+	return m_pmc_crashLog_present;
+}
+
+bool cpu_crashlog_support(void)
+{
+	return m_cpu_crashLog_support;
+}
+
+bool pmc_crashlog_support(void)
+{
+	return m_pmc_crashLog_support;
+}
+
+void update_new_pmc_crashlog_size(u32 *pmc_crash_size)
+{
+	m_pmc_crashLog_size = *pmc_crash_size;
+}
+
+cpu_crashlog_discovery_table_t cl_get_cpu_discovery_table(void)
+{
+	return cpu_cl_disc_tab;
+}
+
+void update_new_cpu_crashlog_size(u32 *cpu_crash_size)
+{
+	m_cpu_crashLog_size = *cpu_crash_size;
+}
diff --git a/src/soc/intel/alderlake/include/soc/crashlog.h b/src/soc/intel/alderlake/include/soc/crashlog.h
new file mode 100644
index 0000000..1abbc75
--- /dev/null
+++ b/src/soc/intel/alderlake/include/soc/crashlog.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _SOC_COMMON_BLOCK_CRASHLOG_LIB_H_
+#define _SOC_COMMON_BLOCK_CRASHLIB_LIB_H_
+
+#include <types.h>
+
+
+/* DVSEC capability Registers */
+#define TEL_DVSEC_OFFSET			0x100
+#define TEL_DVSEC_PCIE_CAP_ID			0x0
+#define TEL_DVSEC_NEXT_CAP			0x2
+#define TEL_DVSEV_ID				0x8
+#define TEL_DVSEV_DISCOVERY_TABLE_OFFSET	0xC
+#define TELEMETRY_EXTENDED_CAP_ID		0x23
+#define CRASHLOG_DVSEC_ID			0x04
+#define TEL_DVSEC_TBIR_BAR0			0
+#define TEL_DVSEC_TBIR_BAR1			1
+
+/* CPU CrashLog MMIO Registers */
+#define CRASHLOG_MAILBOX_INTF_ADDRESS		0x6038
+#define CRASHLOG_POINTER_SIZE_FIELD_OFFSET	0x04
+
+#endif /* _SOC_COMMON_BLOCK_CRASHLOG_LIB_H_ */
diff --git a/src/soc/intel/alderlake/include/soc/pci_devs.h b/src/soc/intel/alderlake/include/soc/pci_devs.h
index 23cf248..af5c50e 100644
--- a/src/soc/intel/alderlake/include/soc/pci_devs.h
+++ b/src/soc/intel/alderlake/include/soc/pci_devs.h
@@ -5,12 +5,15 @@
 
 #include <device/pci_def.h>
 
+#define _SA_DEVFN(slot)		PCI_DEVFN(SA_DEV_SLOT_ ## slot, 0)
 #define _PCH_DEVFN(slot, func)	PCI_DEVFN(PCH_DEV_SLOT_ ## slot, func)
 
 #if !defined(__SIMPLE_DEVICE__)
 #include <device/device.h>
+#define _SA_DEV(slot)		pcidev_path_on_root(_SA_DEVFN(slot))
 #define _PCH_DEV(slot, func)	pcidev_path_on_root_debug(_PCH_DEVFN(slot, func), __func__)
 #else
+#define _SA_DEV(slot)		PCI_DEV(0, SA_DEV_SLOT_ ## slot, 0)
 #define _PCH_DEV(slot, func)	PCI_DEV(0, PCH_DEV_SLOT_ ## slot, func)
 #endif
 
@@ -53,6 +56,10 @@
 #define  SA_DEV_TBT2		PCI_DEV(0, SA_DEV_SLOT_TBT, 2)
 #define  SA_DEV_TBT3		PCI_DEV(0, SA_DEV_SLOT_TBT, 3)
 
+#define SA_DEV_SLOT_TMT		0x0A
+#define SA_DEVFN_TMT		_SA_DEVFN(TMT)
+#define SA_DEV_TMT		_SA_DEV(TMT)
+
 #define SA_DEV_SLOT_TCSS	0x0d
 #define NUM_TCSS_DMA_FUNCTIONS	2
 #define SA_DEVFN_TCSS_DMA(x)	PCI_DEVFN(SA_DEV_SLOT_TCSS, ((x) + 2))
diff --git a/src/soc/intel/alderlake/romstage/fsp_params.c b/src/soc/intel/alderlake/romstage/fsp_params.c
index d7d03e1..ba6e036 100644
--- a/src/soc/intel/alderlake/romstage/fsp_params.c
+++ b/src/soc/intel/alderlake/romstage/fsp_params.c
@@ -237,6 +237,12 @@
 
 	/* Skip GPIO configuration from FSP */
 	m_cfg->GpioOverride = 0x1;
+
+	/* CrashLog config */
+	if (CONFIG(SOC_INTEL_CRASHLOG)) {
+		m_cfg->CpuCrashLogDevice = 1;
+		m_cfg->CpuCrashLogEnable = 1;
+	}
 }
 
 void platform_fsp_memory_init_params_cb(FSPM_UPD *mupd, uint32_t version)