[PATCH 2/5] PCI/FLIT: Add support for Flit Logging Extended Capability
From: Yazen Ghannam
Date: Tue Jul 14 2026 - 14:41:20 EST
From: Avadhut Naik <avadhut.naik@xxxxxxx>
PCIe r6.0 introduced Flit Logging Extended Capability for logging
erroneous flits. This extended capability must be implemented by ports
and RCRBs that support PCIe Gen6 Flit Mode.[1]
Introduce support required for the Extended Capability as a port service
driver.
[1] PCI Express® Base Specification Revision 6.0, section 7.7.8
[Yazen: Fix ups, etc.]
Signed-off-by: Avadhut Naik <avadhut.naik@xxxxxxx>
Co-developed-by: Yazen Ghannam <yazen.ghannam@xxxxxxx>
Signed-off-by: Yazen Ghannam <yazen.ghannam@xxxxxxx>
Assisted-by: Claude:claude-opus-4-8
---
drivers/pci/pci.h | 6 ++
drivers/pci/pcie/Kconfig | 10 ++
drivers/pci/pcie/Makefile | 1 +
drivers/pci/pcie/flit.c | 170 ++++++++++++++++++++++++++++++++++
drivers/pci/pcie/portdrv.c | 15 ++-
drivers/pci/pcie/portdrv.h | 10 +-
drivers/pci/probe.c | 1 +
include/linux/pci.h | 4 +
include/uapi/linux/pci_regs.h | 14 ++-
9 files changed, 227 insertions(+), 4 deletions(-)
create mode 100644 drivers/pci/pcie/flit.c
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4469e1a77f3c..31d04366b980 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -894,6 +894,12 @@ static inline void pci_dpc_init(struct pci_dev *pdev) { }
static inline bool pci_dpc_recovered(struct pci_dev *pdev) { return false; }
#endif
+#ifdef CONFIG_PCIE_FLIT
+void pci_flit_init(struct pci_dev *pdev);
+#else
+static inline void pci_flit_init(struct pci_dev *pdev) { }
+#endif
+
#ifdef CONFIG_PCIEPORTBUS
void pci_rcec_init(struct pci_dev *dev);
void pci_rcec_exit(struct pci_dev *dev);
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 207c2deae35f..1ee3c85e63c1 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -146,3 +146,13 @@ config PCIE_EDR
the PCI Firmware Specification r3.2. Enable this if you want to
support hybrid DPC model which uses both firmware and OS to
implement DPC.
+
+config PCIE_FLIT
+ bool "PCI Express Flit Mode Error Logging support"
+ depends on PCIEPORTBUS
+ help
+ This enables support for the PCI Express Flit Logging Extended
+ Capability, which logs errors encountered by a port operating
+ in PCIe Gen6 Flit Mode. If your system doesn't have this
+ capability or you do not want to use this feature, it is safe
+ to answer N.
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index b0b43a18c304..14a5222920b9 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_PCIE_PME) += pme.o
obj-$(CONFIG_PCIE_DPC) += dpc.o
obj-$(CONFIG_PCIE_PTM) += ptm.o
obj-$(CONFIG_PCIE_EDR) += edr.o
+obj-$(CONFIG_PCIE_FLIT) += flit.o
diff --git a/drivers/pci/pcie/flit.c b/drivers/pci/pcie/flit.c
new file mode 100644
index 000000000000..12f2b6fbf1de
--- /dev/null
+++ b/drivers/pci/pcie/flit.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Service driver for PCIe Flit Logging Capability
+ *
+ * Copyright (c) 2026, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Avadhut Naik <Avadhut.Naik@xxxxxxx>
+ * Yazen Ghannam <Yazen.Ghannam@xxxxxxx>
+ */
+
+#define pr_fmt(fmt) "Flit: " fmt
+#define dev_fmt pr_fmt
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include "portdrv.h"
+#include "../pci.h"
+
+void pci_flit_init(struct pci_dev *pdev)
+{
+ u16 cap;
+
+ if (pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &cap))
+ return;
+
+ if (!(cap & PCI_EXP_FLAGS_FLIT))
+ return;
+
+ pdev->flit_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_FLIT);
+ if (pdev->flit_cap)
+ pci_dbg(pdev, "Flit Mode Error Logging Capability present.\n");
+}
+
+static void flit_cntr_enable(struct pci_dev *pdev)
+{
+ u16 flit = pdev->flit_cap;
+ u16 reg;
+
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, ®);
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, reg);
+
+ /*
+ * NOTE: The "Set Events to Count" and "Trigger Event Count" fields
+ * are left as set by the platform.
+ */
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, ®);
+ reg |= PCI_FLIT_ERR_CNTR_EN;
+ reg |= PCI_FLIT_ERR_CNTR_INTR_EN;
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, reg);
+}
+
+static void flit_cntr_disable(struct pcie_device *dev)
+{
+ struct pci_dev *pdev = dev->port;
+ u16 ctrl, flit = pdev->flit_cap;
+
+ /* Disable both Error Counter and Interrupt generation */
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, &ctrl);
+ ctrl &= ~(PCI_FLIT_ERR_CNTR_EN | PCI_FLIT_ERR_CNTR_INTR_EN);
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, ctrl);
+}
+
+static irqreturn_t flit_isr(int irq, void *context)
+{
+ struct pcie_device *dev = (struct pcie_device *)context;
+ struct pci_dev *pdev = dev->port;
+ u16 flit = pdev->flit_cap;
+ u16 cntr_ctrl, cntr_sta;
+ u32 err_log1, err_log2;
+
+ /* Read and log Counter and Error Log Registers. */
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, &cntr_ctrl);
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, &cntr_sta);
+
+ pci_info(pdev, HW_ERR "Counter Control: 0x%04x Counter Status: 0x%04x\n", cntr_ctrl, cntr_sta);
+
+ do {
+ pci_read_config_dword(pdev, flit + PCI_FLIT_ERR_LOG1, &err_log1);
+
+ if (!(err_log1 & PCI_FLIT_ERR_LOG_VALID))
+ break;
+
+ pci_read_config_dword(pdev, flit + PCI_FLIT_ERR_LOG2, &err_log2);
+ pci_info(pdev, HW_ERR " Error Log1: 0x%08x Error Log2: 0x%08x\n", err_log1, err_log2);
+
+ pci_write_config_dword(pdev, flit + PCI_FLIT_ERR_LOG1, err_log1);
+ } while (err_log1 & PCI_FLIT_ERR_LOG_MORE);
+
+ /*
+ * Re-enabling the counter is the interrupt acknowledgment.
+ *
+ * The status bit checked in flit_irq() (Interrupt Generated based on
+ * Trigger Event Count) is cleared by a 0->1 transition of Flit Error
+ * Counter Enable, per PCIe r6.4 Table 7-96.
+ */
+ cntr_ctrl |= PCI_FLIT_ERR_CNTR_EN;
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, cntr_ctrl);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t flit_irq(int irq, void *context)
+{
+ struct pcie_device *dev = (struct pcie_device *)context;
+ struct pci_dev *pdev = dev->port;
+ u16 flit = pdev->flit_cap;
+ u16 cntr_sta, cntr_ctrl;
+
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, &cntr_sta);
+ if (!(cntr_sta & PCI_FLIT_INTR_GEN_CNTR))
+ return IRQ_NONE;
+
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, &cntr_ctrl);
+ cntr_ctrl &= ~PCI_FLIT_ERR_CNTR_EN;
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, cntr_ctrl);
+ return IRQ_WAKE_THREAD;
+}
+
+static int flit_probe(struct pcie_device *dev)
+{
+ struct device *device = &dev->device;
+ struct pci_dev *pdev = dev->port;
+ int status;
+
+ status = devm_request_threaded_irq(device, dev->irq, flit_irq,
+ flit_isr, IRQF_SHARED,
+ "pcie-flit", dev);
+ if (status) {
+ pci_warn(pdev, "request Flit IRQ%d failed: %d\n", dev->irq, status);
+ return status;
+ }
+
+ flit_cntr_enable(pdev);
+ pci_info(pdev, "enabled with IRQ %d\n", dev->irq);
+
+ return 0;
+}
+
+static int flit_cntr_suspend(struct pcie_device *dev)
+{
+ flit_cntr_disable(dev);
+ return 0;
+}
+
+static int flit_cntr_resume(struct pcie_device *dev)
+{
+ flit_cntr_enable(dev->port);
+ return 0;
+}
+
+static void flit_cntr_remove(struct pcie_device *dev)
+{
+ flit_cntr_disable(dev);
+}
+
+static struct pcie_port_service_driver flitdriver = {
+ .name = "flit",
+ .port_type = PCIE_ANY_PORT,
+ .service = PCIE_PORT_SERVICE_FLIT,
+ .probe = flit_probe,
+ .suspend = flit_cntr_suspend,
+ .resume = flit_cntr_resume,
+ .remove = flit_cntr_remove,
+};
+
+int __init pcie_flit_init(void)
+{
+ return pcie_port_service_register(&flitdriver);
+}
diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
index 53144c99cb3c..1a649d6f4ab8 100644
--- a/drivers/pci/pcie/portdrv.c
+++ b/drivers/pci/pcie/portdrv.c
@@ -31,7 +31,8 @@
#define PCIE_PORT_SERVICE_EXPCAP (PCIE_PORT_SERVICE_PME | \
PCIE_PORT_SERVICE_HP | \
- PCIE_PORT_SERVICE_BWCTRL)
+ PCIE_PORT_SERVICE_BWCTRL | \
+ PCIE_PORT_SERVICE_FLIT)
#define get_descriptor_id(type, service) (((type - 4) << 8) | service)
@@ -151,12 +152,16 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
return nr_entries;
}
- /* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */
+ /*
+ * PME, hotplug, flit error counter, and bandwidth notification share
+ * an MSI/MSI-X vector
+ */
if (mask & PCIE_PORT_SERVICE_EXPCAP) {
pcie_irq = pci_irq_vector(dev, pme);
irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq;
irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq;
irqs[PCIE_PORT_SERVICE_BWCTRL_SHIFT] = pcie_irq;
+ irqs[PCIE_PORT_SERVICE_FLIT_SHIFT] = pcie_irq;
}
if (mask & PCIE_PORT_SERVICE_AER)
@@ -280,6 +285,11 @@ static int get_port_device_capability(struct pci_dev *dev)
services |= PCIE_PORT_SERVICE_BWCTRL;
}
+#ifdef CONFIG_PCIE_FLIT
+ if (dev->flit_cap)
+ services |= PCIE_PORT_SERVICE_FLIT;
+#endif
+
return services;
}
@@ -836,6 +846,7 @@ static void __init pcie_init_services(void)
pcie_dpc_init();
pcie_bwctrl_init();
pcie_hp_init();
+ pcie_flit_init();
}
static int __init pcie_portdrv_init(void)
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index cc58bf2f2c84..db80faecf34c 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -22,8 +22,10 @@
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
#define PCIE_PORT_SERVICE_BWCTRL_SHIFT 4 /* Bandwidth Controller (notifications) */
#define PCIE_PORT_SERVICE_BWCTRL (1 << PCIE_PORT_SERVICE_BWCTRL_SHIFT)
+#define PCIE_PORT_SERVICE_FLIT_SHIFT 5 /* Flit Logging */
+#define PCIE_PORT_SERVICE_FLIT (1 << PCIE_PORT_SERVICE_FLIT_SHIFT)
-#define PCIE_PORT_DEVICE_MAXSERVICES 5
+#define PCIE_PORT_DEVICE_MAXSERVICES 6
extern bool pcie_ports_dpc_native;
@@ -51,6 +53,12 @@ int pcie_dpc_init(void);
static inline int pcie_dpc_init(void) { return 0; }
#endif
+#ifdef CONFIG_PCIE_FLIT
+int pcie_flit_init(void);
+#else
+static inline int pcie_flit_init(void) { return 0; }
+#endif
+
int pcie_bwctrl_init(void);
/* Port Type */
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index dd0abbc63e18..a1ec44b04eb0 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2666,6 +2666,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_pasid_init(dev); /* Process Address Space ID */
pci_acs_init(dev); /* Access Control Services */
pci_ptm_init(dev); /* Precision Time Measurement */
+ pci_flit_init(dev); /* Flit Logging */
pci_aer_init(dev); /* Advanced Error Reporting */
pci_dpc_init(dev); /* Downstream Port Containment */
pci_rcec_init(dev); /* Root Complex Event Collector */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index ebb5b9d76360..a8d8dbac6640 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -592,6 +592,10 @@ struct pci_dev {
u8 tph_mode; /* TPH mode */
u8 tph_req_type; /* TPH requester type */
#endif
+
+#ifdef CONFIG_PCIE_FLIT
+ u16 flit_cap; /* Flit Logging Capabilities */
+#endif
};
static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index facaa324bd86..774eeed80bed 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -763,7 +763,8 @@
#define PCI_EXT_CAP_ID_DEV3 0x2F /* Device 3 Capability/Control/Status */
#define PCI_EXT_CAP_ID_IDE 0x30 /* Integrity and Data Encryption */
#define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */
-#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT
+#define PCI_EXT_CAP_ID_FLIT 0x32 /* Flit Error Logging */
+#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_FLIT
#define PCI_EXT_CAP_DSN_SIZEOF 12
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
@@ -1187,6 +1188,17 @@
/* Physical Layer 64.0 GT/s */
#define PCI_PL_64GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
+/* Flit Mode Error Logging */
+#define PCI_FLIT_ERR_LOG1 0x04 /* Flit Error Log 1 Register */
+#define PCI_FLIT_ERR_LOG_VALID 0x1 /* Flit Error Log Valid */
+#define PCI_FLIT_ERR_LOG_MORE 0x2000 /* More Entries Valid */
+#define PCI_FLIT_ERR_LOG2 0x08 /* Flit Error Log 2 Register */
+#define PCI_FLIT_ERR_CNTR_CTRL 0x0c /* Flit Error Counter Control Register */
+#define PCI_FLIT_ERR_CNTR_EN 0x0001 /* Flit Error Counter Enable */
+#define PCI_FLIT_ERR_CNTR_INTR_EN 0x0002 /* Flit Error Counter Interrupt Enable */
+#define PCI_FLIT_ERR_CNTR_STA 0x0e /* Flit Error Counter Status Register */
+#define PCI_FLIT_INTR_GEN_CNTR 0x0008 /* Interrupt on Trigger Event Count */
+
/* Native PCIe Enclosure Management */
#define PCI_NPEM_CAP 0x04 /* NPEM capability register */
#define PCI_NPEM_CAP_CAPABLE 0x00000001 /* NPEM Capable */
--
2.53.0