device: Clear lane error status

Refer to PCI Express Base rev6.0 v1.0, 4.2.7 Link Training and Status
State Rules, Lane Error Status is normal to record the error when link
training. To make sure Lane Error Status is correct in OS runtime,
add a Kconfig PCIEXP_LANE_ERR_STAT_CLEAR that clears the PCIe lane error
status register at the end of PCIe link training.

Test=On Crater Lake, lspci -vvv shows
bb:01.0 PCI bridge: Intel Corporation Device 352a (rev 03)
(prog-if 00 [Normal decode])
Capabilities: [a30 v1] Secondary PCI Express
	LnkCtl3: LnkEquIntrruptEn- PerformEqu-
	LaneErrStat: LaneErr at lane: 0

Signed-off-by: Wilson Chou <Wilson.Chou@quantatw.com>
Change-Id: I6344223636409d8fc25e365a6375fc81e69f41a5
Reviewed-on: https://review.coreboot.org/c/coreboot/+/67264
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Jonathan Zhang <jonzhang@fb.com>
diff --git a/src/device/Kconfig b/src/device/Kconfig
index b9284cb..c0ba3d1 100644
--- a/src/device/Kconfig
+++ b/src/device/Kconfig
@@ -654,6 +654,13 @@
 
 endif # PCIEXP_SUPPORT_RESIZABLE_BARS
 
+config PCIEXP_LANE_ERR_STAT_CLEAR
+	prompt "Enable Clear PCIe Lane Error Status"
+	bool
+	default n
+	help
+	  Clear the PCIe Lane Error Status at the end of link training.
+
 config PCIEXP_HOTPLUG
 	prompt "Enable PCIe Hotplug Support"
 	bool
diff --git a/src/device/pciexp_device.c b/src/device/pciexp_device.c
index 6023497..b03089b 100644
--- a/src/device/pciexp_device.c
+++ b/src/device/pciexp_device.c
@@ -532,6 +532,30 @@
 	printk(BIOS_INFO, "PCIe: Max_Payload_Size adjusted to %d\n", (1 << (max_payload + 7)));
 }
 
+/*
+ * Clear Lane Error State at the end of PCIe link training.
+ * Lane error status is cleared if PCIEXP_LANE_ERR_STAT_CLEAR is set.
+ * Lane error is normal during link training, so we need to clear it.
+ * At this moment, link has been used, but for a very short duration.
+ */
+static void clear_lane_error_status(struct device *dev)
+{
+	u32 reg32;
+	u16 pos;
+
+	pos = pciexp_find_extended_cap(dev, PCI_EXP_SEC_CAP_ID, 0);
+	if (pos == 0)
+		return;
+
+	reg32 = pci_read_config32(dev, pos + PCI_EXP_SEC_LANE_ERR_STATUS);
+	if (reg32 == 0)
+		return;
+
+	printk(BIOS_DEBUG, "%s: Clear Lane Error Status.\n", dev_path(dev));
+	printk(BIOS_DEBUG, "LaneErrStat:0x%x\n", reg32);
+	pci_write_config32(dev, pos + PCI_EXP_SEC_LANE_ERR_STATUS, reg32);
+}
+
 static void pciexp_tune_dev(struct device *dev)
 {
 	struct device *root = dev->bus->dev;
@@ -561,6 +585,10 @@
 	if (CONFIG(PCIEXP_ASPM))
 		pciexp_enable_aspm(root, root_cap, dev, cap);
 
+	/* Clear PCIe Lane Error Status */
+	if (CONFIG(PCIEXP_LANE_ERR_STAT_CLEAR))
+		clear_lane_error_status(root);
+
 	/* Adjust Max_Payload_Size of link ends. */
 	pciexp_set_max_payload_size(root, root_cap, dev, cap);
 
diff --git a/src/include/device/pci_def.h b/src/include/device/pci_def.h
index 0eedd51..64c1ac2 100644
--- a/src/include/device/pci_def.h
+++ b/src/include/device/pci_def.h
@@ -469,6 +469,11 @@
 #define  PCIE_EXT_CAP_LTR_ID		 0x0018
 #define  PCIE_EXT_CAP_RESIZABLE_BAR	 0x0015
 
+/* Secondary PCI Express Extended Capability Structure */
+#define PCI_EXP_SEC_CAP_ID	0x19
+#define PCI_EXP_SEC_LNK_CTL3	4	/* Link Control 3 */
+#define PCI_EXP_SEC_LANE_ERR_STATUS	8	/* Lane Error Status */
+
 /* Advanced Error Reporting */
 #define PCI_ERR_UNCOR_STATUS	4	/* Uncorrectable Error Status */
 #define  PCI_ERR_UNC_TRAIN	0x00000001	/* Training */