@@ -1670,6 +1670,7 @@ int pci_save_state(struct pci_dev *dev)
pci_save_dpc_state(dev);
pci_save_aer_state(dev);
pci_save_ptm_state(dev);
+ pci_save_tph_state(dev);
return pci_save_vc_state(dev);
}
EXPORT_SYMBOL(pci_save_state);
@@ -1782,6 +1783,7 @@ void pci_restore_state(struct pci_dev *dev)
pci_restore_rebar_state(dev);
pci_restore_dpc_state(dev);
pci_restore_ptm_state(dev);
+ pci_restore_tph_state(dev);
pci_aer_clear_status(dev);
pci_restore_aer_state(dev);
@@ -514,6 +514,16 @@ static inline void pci_restore_ptm_state(struct pci_dev *dev) { }
static inline void pci_disable_ptm(struct pci_dev *dev) { }
#endif
+#ifdef CONFIG_PCIE_TPH
+void pci_save_tph_state(struct pci_dev *dev);
+void pci_restore_tph_state(struct pci_dev *dev);
+void pci_tph_init(struct pci_dev *dev);
+#else
+static inline void pci_save_tph_state(struct pci_dev *dev) { }
+static inline void pci_restore_tph_state(struct pci_dev *dev) { }
+static inline void pci_tph_init(struct pci_dev *dev) { }
+#endif
+
unsigned long pci_cardbus_resource_alignment(struct resource *);
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
@@ -142,3 +142,11 @@ 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_TPH
+ bool "PCI TPH (Transaction Layer Packet Processing Hints) capability support"
+ help
+ This enables PCI Express TPH (Transaction Layer Packet Processing Hints) by
+ making sure they are saved and restored across resets. Enable this if your
+ hardware uses the PCI Express TPH capabilities. For more information please
+ refer to PCIe r6.0 sec 6.17.
@@ -13,3 +13,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_TPH) += tph.o
new file mode 100644
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Express Transaction Layer Packet Processing Hints
+ * Copyright (c) 2022, Intel Corporation.
+ */
+
+#include "../pci.h"
+
+static unsigned int pci_get_tph_st_num_entries(struct pci_dev *dev, u16 tph)
+{
+ int num_entries = 0;
+ u32 cap;
+
+ pci_read_config_dword(dev, tph + PCI_TPH_CAP, &cap);
+ if ((cap & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) {
+ /* per PCI spec, table entries is encoded as N-1 */
+ num_entries = ((cap & PCI_TPH_CAP_ST_MASK) >> PCI_TPH_CAP_ST_SHIFT) + 1;
+ }
+
+ return num_entries;
+}
+
+void pci_save_tph_state(struct pci_dev *dev)
+{
+ struct pci_cap_saved_state *save_state;
+ int tph, num_entries, i, offset;
+ u16 *st_entry;
+ u32 *cap;
+
+ tph = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_TPH);
+ if (!tph)
+ return;
+
+ save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_TPH);
+ if (!save_state)
+ return;
+
+ /* Save control register as well as all ST entries */
+ cap = &save_state->cap.data[0];
+ pci_read_config_dword(dev, tph + PCI_TPH_CTL, cap++);
+ st_entry = (u16 *)cap;
+ offset = PCI_TPH_ST_TBL;
+ num_entries = pci_get_tph_st_num_entries(dev, tph);
+ for (i = 0; i < num_entries; i++) {
+ pci_read_config_word(dev, tph + offset, st_entry++);
+ offset += sizeof(u16);
+ }
+}
+
+void pci_restore_tph_state(struct pci_dev *dev)
+{
+ struct pci_cap_saved_state *save_state;
+ int num_entries, i, offset;
+ u16 *st_entry, tph;
+ u32 *cap;
+
+ tph = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_TPH);
+ if (!tph)
+ return;
+
+ save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_TPH);
+ if (!save_state)
+ return;
+
+ /* Restore control register as well as all ST entries */
+ cap = &save_state->cap.data[0];
+ pci_write_config_dword(dev, tph + PCI_TPH_CTL, *cap++);
+ st_entry = (u16 *)cap;
+ offset = PCI_TPH_ST_TBL;
+ num_entries = pci_get_tph_st_num_entries(dev, tph);
+ for (i = 0; i < num_entries; i++) {
+ pci_write_config_word(dev, tph + offset, *st_entry++);
+ offset += sizeof(u16);
+ }
+}
+
+void pci_tph_init(struct pci_dev *dev)
+{
+ int num_entries;
+ u32 save_size;
+ u16 tph;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ tph = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_TPH);
+ if (!tph)
+ return;
+
+ num_entries = pci_get_tph_st_num_entries(dev, tph);
+ save_size = sizeof(int) + num_entries * sizeof(u16);
+ pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_TPH, save_size);
+}
@@ -2464,6 +2464,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_aer_init(dev); /* Advanced Error Reporting */
pci_dpc_init(dev); /* Downstream Port Containment */
pci_rcec_init(dev); /* Root Complex Event Collector */
+ pci_tph_init(dev); /* Transaction Layer Packet Processing Hints */
pcie_report_downtraining(dev);
pci_init_reset_methods(dev);
@@ -1020,6 +1020,8 @@
#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */
#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST table mask */
#define PCI_TPH_CAP_ST_SHIFT 16 /* ST table shift */
+#define PCI_TPH_CTL 8 /* control offset */
+#define PCI_TPH_ST_TBL 0xc /* ST table offset */
#define PCI_TPH_BASE_SIZEOF 0xc /* size with no ST table */
/* Downstream Port Containment */