drivers/ipmi/ocp: add PCIe SEL support

Add Kconfig SOC_RAS_BMS_SEL and corresponding support for
generating PCIe error SEL records and sending them to BMC.

Add PCIe error definitions.

This is needed for SMM, so build the ipmi kcs driver in SMM.

Signed-off-by: Tim Chu <Tim.Chu@quantatw.com>
Signed-off-by: Rocky Phagura <rphagura@fb.com>
Signed-off-by: Jonathan Zhang <jonzhang@meta.com>
Change-Id: I1ee46c8da7dbccbe1e2cc00bfe62e5df2f072d65
Reviewed-on: https://review.coreboot.org/c/coreboot/+/68758
Reviewed-by: Christian Walter <christian.walter@9elements.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/drivers/ipmi/ocp/ipmi_sel.c b/src/drivers/ipmi/ocp/ipmi_sel.c
new file mode 100644
index 0000000..55c0c3e
--- /dev/null
+++ b/src/drivers/ipmi/ocp/ipmi_sel.c
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <console/console.h>
+#include <drivers/ipmi/ipmi_if.h>
+#include <string.h>
+
+#include "ipmi_ocp.h"
+
+static int ipmi_add_sel_entry(int port, unsigned char *data, int size)
+{
+	return ipmi_message(port, IPMI_NETFN_STORAGE, 0, IPMI_ADD_SEL_ENTRY, data, size,
+				    NULL, 0);
+}
+
+void ipmi_send_to_bmc(unsigned char *data, size_t size)
+{
+	if (CONFIG(IPMI_KCS)) {
+		_Static_assert(CONFIG_BMC_KCS_BASE != 0,
+			"\tBMC_ERROR: Unable to send record: Port #:0\n");
+
+		ipmi_add_sel_entry(CONFIG_BMC_KCS_BASE, data, size);
+	}
+}
+
+void ipmi_send_sel_iio_err(uint8_t iio_stack_num, uint8_t err_id)
+{
+	struct ipmi_sel_iio_err ubslp = {
+		.record_id = SEL_RECORD_ID,
+		.record_type = CONFIG_RAS_SEL_VENDOR_ID,
+		.general_info = SEL_PCIE_IIO_ERR,
+		.iio_stack_num = iio_stack_num,
+		.iio_err_id = err_id,
+	};
+
+	ipmi_send_to_bmc((unsigned char *)&ubslp, sizeof(ubslp));
+	printk(BIOS_DEBUG, "\tsending PCIE IIO device error record to BMC\n");
+	printk(BIOS_DEBUG, "\tstack # = %x\n", ubslp.iio_stack_num);
+}
+
+void ipmi_send_sel_pcie_dev_err(pci_devfn_t bdf, uint16_t prmry_cnt, uint8_t sec_id,
+				uint8_t prmry_id)
+{
+	struct pci_dev_fn *inbdf = (struct pci_dev_fn *)&bdf;
+	struct ipmi_sel_pcie_dev_err ubslp = {
+		.record_id = SEL_RECORD_ID,
+		.record_type = CONFIG_RAS_SEL_VENDOR_ID,
+		.general_info = SEL_PCIE_DEV_ERR,
+		.timestamp = 0, /* BMC will apply timestamp */
+		.aux_loc = 0,
+		.bdf.bus = inbdf->bus,
+		.bdf.dev = inbdf->dev,
+		.bdf.func = inbdf->func >> 12,
+		.primary_err_count = prmry_cnt,
+		.primary_id = prmry_id,
+		.secondary_id = sec_id,
+	};
+
+	ipmi_send_to_bmc((unsigned char *)&ubslp, sizeof(ubslp));
+	printk(BIOS_DEBUG, "\tsending PCIE device error record to BMC\n");
+	printk(BIOS_DEBUG, "\tbdf = %x:%x:%x\n", ubslp.bdf.bus, ubslp.bdf.dev, ubslp.bdf.func);
+	printk(BIOS_DEBUG, "\tubslp.primary_id = %x\n", ubslp.primary_id);
+	printk(BIOS_DEBUG, "\tsecondary_id = %x\n", ubslp.secondary_id);
+}
+
+void ipmi_send_sel_pcie_dev_fail(uint16_t sts_reg, uint16_t src_id, enum fail_type code)
+{
+	struct ipmi_sel_pcie_dev_fail ubslp = {
+		.record_id = SEL_RECORD_ID,
+		.record_type = CONFIG_RAS_SEL_VENDOR_ID,
+		.general_info = SEL_PCIE_DEV_FAIL_ID,
+		.timestamp = 0, /* BMC will apply timestamp */
+		.type = code,
+		.failure_details1 = sts_reg,
+		.failure_details2 = src_id,
+	};
+
+	ipmi_send_to_bmc((unsigned char *)&ubslp, sizeof(ubslp));
+	printk(BIOS_DEBUG, "\tsending PCI device FAILURE record to BMC\n");
+	printk(BIOS_DEBUG, "\terror_code = %x, src_id = %x\n", ubslp.type,
+	       ubslp.failure_details2);
+}