pcie_read_tlp_log() handles only 4 Header Log DWORDs but TLP Prefix Log
(PCIe r6.1 secs 7.8.4.12 & 7.9.14.13) may also be present.

Generalize pcie_read_tlp_log() and struct pcie_tlp_log to handle also
TLP Prefix Log. The relevant registers are formatted identically in AER
and DPC Capability, but has these variations:

a) The offsets of TLP Prefix Log registers vary.
b) DPC RP PIO TLP Prefix Log register can be < 4 DWORDs.

Therefore callers must pass the offset of the TLP Prefix Log register
and the entire length to pcie_read_tlp_log() to be able to read the
correct number of TLP Prefix DWORDs from the correct offset.

Signed-off-by: Ilpo Järvinen <ilpo.jarvi...@linux.intel.com>
---
 drivers/pci/pci.h             |  5 +++-
 drivers/pci/pcie/aer.c        |  4 ++-
 drivers/pci/pcie/dpc.c        | 13 +++++-----
 drivers/pci/pcie/tlp.c        | 49 +++++++++++++++++++++++++++++++----
 include/linux/aer.h           |  1 +
 include/uapi/linux/pci_regs.h |  1 +
 6 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 0e9917f8bf3f..7afdd71f9026 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -420,7 +420,9 @@ struct aer_err_info {
 int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
 
-int pcie_read_tlp_log(struct pci_dev *dev, int where, struct pcie_tlp_log 
*log);
+int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
+                     unsigned int tlp_len, struct pcie_tlp_log *log);
+unsigned int aer_tlp_log_len(struct pci_dev *dev);
 #endif /* CONFIG_PCIEAER */
 
 #ifdef CONFIG_PCIEPORTBUS
@@ -439,6 +441,7 @@ void pci_dpc_init(struct pci_dev *pdev);
 void dpc_process_error(struct pci_dev *pdev);
 pci_ers_result_t dpc_reset_link(struct pci_dev *pdev);
 bool pci_dpc_recovered(struct pci_dev *pdev);
+unsigned int dpc_tlp_log_len(struct pci_dev *dev);
 #else
 static inline void pci_save_dpc_state(struct pci_dev *dev) { }
 static inline void pci_restore_dpc_state(struct pci_dev *dev) { }
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index ac6293c24976..ecc1dea5a208 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -1245,7 +1245,9 @@ int aer_get_device_error_info(struct pci_dev *dev, struct 
aer_err_info *info)
 
                if (info->status & AER_LOG_TLP_MASKS) {
                        info->tlp_header_valid = 1;
-                       pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, 
&info->tlp);
+                       pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG,
+                                         aer + PCI_ERR_PREFIX_LOG,
+                                         aer_tlp_log_len(dev), &info->tlp);
                }
        }
 
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
index a668820696dc..5056cc6961ec 100644
--- a/drivers/pci/pcie/dpc.c
+++ b/drivers/pci/pcie/dpc.c
@@ -190,7 +190,7 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 static void dpc_process_rp_pio_error(struct pci_dev *pdev)
 {
        u16 cap = pdev->dpc_cap, dpc_status, first_error;
-       u32 status, mask, sev, syserr, exc, log, prefix;
+       u32 status, mask, sev, syserr, exc, log;
        struct pcie_tlp_log tlp_log;
        int i;
 
@@ -217,20 +217,19 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev)
 
        if (pdev->dpc_rp_log_size < 4)
                goto clear_status;
-       pcie_read_tlp_log(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, &tlp_log);
+       pcie_read_tlp_log(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
+                         cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG,
+                         dpc_tlp_log_len(pdev), &tlp_log);
        pci_err(pdev, "TLP Header: %#010x %#010x %#010x %#010x\n",
                tlp_log.dw[0], tlp_log.dw[1], tlp_log.dw[2], tlp_log.dw[3]);
+       for (i = 0; i < pdev->dpc_rp_log_size - 5; i++)
+               pci_err(pdev, "TLP Prefix Header: dw%d, %#010x\n", i, 
tlp_log.prefix[i]);
 
        if (pdev->dpc_rp_log_size < 5)
                goto clear_status;
        pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log);
        pci_err(pdev, "RP PIO ImpSpec Log %#010x\n", log);
 
-       for (i = 0; i < pdev->dpc_rp_log_size - 5; i++) {
-               pci_read_config_dword(pdev,
-                       cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG + i * 4, 
&prefix);
-               pci_err(pdev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix);
-       }
  clear_status:
        pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, status);
 }
diff --git a/drivers/pci/pcie/tlp.c b/drivers/pci/pcie/tlp.c
index 65ac7b5d8a87..def9dd7b73e8 100644
--- a/drivers/pci/pcie/tlp.c
+++ b/drivers/pci/pcie/tlp.c
@@ -11,26 +11,65 @@
 
 #include "../pci.h"
 
+/**
+ * aer_tlp_log_len - Calculates AER Capability TLP Header/Prefix Log length
+ * @dev: PCIe device
+ *
+ * Return: TLP Header/Prefix Log length
+ */
+unsigned int aer_tlp_log_len(struct pci_dev *dev)
+{
+       return 4 + dev->eetlp_prefix_max;
+}
+
+#ifdef CONFIG_PCIE_DPC
+/**
+ * dpc_tlp_log_len - Calculates DPC RP PIO TLP Header/Prefix Log length
+ * @dev: PCIe device
+ *
+ * Return: TLP Header/Prefix Log length
+ */
+unsigned int dpc_tlp_log_len(struct pci_dev *dev)
+{
+       /* Remove ImpSpec Log register from the count */
+       if (dev->dpc_rp_log_size >= 5)
+               return dev->dpc_rp_log_size - 1;
+
+       return dev->dpc_rp_log_size;
+}
+#endif
+
 /**
  * pcie_read_tlp_log - read TLP Header Log
  * @dev: PCIe device
  * @where: PCI Config offset of TLP Header Log
+ * @where2: PCI Config offset of TLP Prefix Log
+ * @tlp_len: TLP Log length (Header Log + TLP Prefix Log in DWORDs)
  * @log: TLP Log structure to fill
  *
  * Fill @log from TLP Header Log registers, e.g., AER or DPC.
  *
  * Return: 0 on success and filled TLP Log structure, <0 on error.
  */
-int pcie_read_tlp_log(struct pci_dev *dev, int where,
-                     struct pcie_tlp_log *log)
+int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
+                     unsigned int tlp_len, struct pcie_tlp_log *log)
 {
        unsigned int i;
-       int ret;
+       int off, ret;
+       u32 *to;
 
        memset(log, 0, sizeof(*log));
 
-       for (i = 0; i < 4; i++) {
-               ret = pci_read_config_dword(dev, where + i * 4, &log->dw[i]);
+       for (i = 0; i < tlp_len; i++) {
+               if (i < 4) {
+                       off = where + i * 4;
+                       to = &log->dw[i];
+               } else {
+                       off = where2 + (i - 4) * 4;
+                       to = &log->prefix[i - 4];
+               }
+
+               ret = pci_read_config_dword(dev, off, to);
                if (ret)
                        return pcibios_err_to_errno(ret);
        }
diff --git a/include/linux/aer.h b/include/linux/aer.h
index 190a0a2061cd..dc498adaa1c8 100644
--- a/include/linux/aer.h
+++ b/include/linux/aer.h
@@ -20,6 +20,7 @@ struct pci_dev;
 
 struct pcie_tlp_log {
        u32 dw[4];
+       u32 prefix[4];
 };
 
 struct aer_capability_regs {
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 09e0c300c952..cf7a07fa4a3b 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -803,6 +803,7 @@
 #define  PCI_ERR_ROOT_FATAL_RCV                0x00000040 /* Fatal Received */
 #define  PCI_ERR_ROOT_AER_IRQ          0xf8000000 /* Advanced Error Interrupt 
Message Number */
 #define PCI_ERR_ROOT_ERR_SRC   0x34    /* Error Source Identification */
+#define PCI_ERR_PREFIX_LOG     0x38    /* TLP Prefix LOG Register (up to 16 
bytes) */
 
 /* Virtual Channel */
 #define PCI_VC_PORT_CAP1       0x04
-- 
2.39.2

Reply via email to