PCI/DPC: Add eDPC support
authorDongdong Liu <liudongdong3@huawei.com>
Sat, 19 Aug 2017 09:07:20 +0000 (17:07 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 24 Aug 2017 16:28:44 +0000 (11:28 -0500)
Add eDPC support. Get and print the RP PIO error information when the
trigger condition is RP PIO error.

For more information on eDPC, please see PCI Express Base Specification
Revision 3.1, section 6.2.10.3, or view the PCI-SIG eDPC ECN here:
https://pcisig.com/sites/default/files/specification_documents/ECN_Enhanced_DPC_2012-11-19_final.pdf

Signed-off-by: Dongdong Liu <liudongdong3@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Keith Busch <keith.busch@intel.com>
drivers/pci/pcie/pcie-dpc.c
include/uapi/linux/pci_regs.h

index c39f32e42b4da0bab16102ea621f8db5c52bb689..8428d7455e495dba0b379beb12127102ac711996 100644 (file)
 #include <linux/pcieport_if.h>
 #include "../pci.h"
 
+struct rp_pio_header_log_regs {
+       u32 dw0;
+       u32 dw1;
+       u32 dw2;
+       u32 dw3;
+};
+
+struct dpc_rp_pio_regs {
+       u32 status;
+       u32 mask;
+       u32 severity;
+       u32 syserror;
+       u32 exception;
+
+       struct rp_pio_header_log_regs header_log;
+       u32 impspec_log;
+       u32 tlp_prefix_log[4];
+       u32 log_size;
+       u16 first_error;
+};
+
 struct dpc_dev {
        struct pcie_device      *dev;
        struct work_struct      work;
        int                     cap_pos;
        bool                    rp;
+       u32                     rp_pio_status;
+};
+
+static const char * const rp_pio_error_string[] = {
+       "Configuration Request received UR Completion",  /* Bit Position 0  */
+       "Configuration Request received CA Completion",  /* Bit Position 1  */
+       "Configuration Request Completion Timeout",      /* Bit Position 2  */
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "I/O Request received UR Completion",            /* Bit Position 8  */
+       "I/O Request received CA Completion",            /* Bit Position 9  */
+       "I/O Request Completion Timeout",                /* Bit Position 10 */
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "Memory Request received UR Completion",         /* Bit Position 16 */
+       "Memory Request received CA Completion",         /* Bit Position 17 */
+       "Memory Request Completion Timeout",             /* Bit Position 18 */
 };
 
 static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
@@ -79,10 +123,124 @@ static void interrupt_event_handler(struct work_struct *work)
        dpc_wait_link_inactive(pdev);
        if (dpc->rp && dpc_wait_rp_inactive(dpc))
                return;
+       if (dpc->rp && dpc->rp_pio_status) {
+               pci_write_config_dword(pdev,
+                                     dpc->cap_pos + PCI_EXP_DPC_RP_PIO_STATUS,
+                                     dpc->rp_pio_status);
+               dpc->rp_pio_status = 0;
+       }
+
        pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
                PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
 }
 
+static void dpc_rp_pio_print_tlp_header(struct device *dev,
+                                       struct rp_pio_header_log_regs *t)
+{
+       dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n",
+               t->dw0, t->dw1, t->dw2, t->dw3);
+}
+
+static void dpc_rp_pio_print_error(struct dpc_dev *dpc,
+                                  struct dpc_rp_pio_regs *rp_pio)
+{
+       struct device *dev = &dpc->dev->device;
+       int i;
+       u32 status;
+
+       dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n",
+               rp_pio->status, rp_pio->mask);
+
+       dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n",
+               rp_pio->severity, rp_pio->syserror, rp_pio->exception);
+
+       status = (rp_pio->status & ~rp_pio->mask);
+
+       for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
+               if (!(status & (1 << i)))
+                       continue;
+
+               dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i],
+                       rp_pio->first_error == i ? " (First)" : "");
+       }
+
+       dpc_rp_pio_print_tlp_header(dev, &rp_pio->header_log);
+       if (rp_pio->log_size == 4)
+               return;
+       dev_err(dev, "RP PIO ImpSpec Log %#010x\n", rp_pio->impspec_log);
+
+       for (i = 0; i < rp_pio->log_size - 5; i++)
+               dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i,
+                       rp_pio->tlp_prefix_log[i]);
+}
+
+static void dpc_rp_pio_get_info(struct dpc_dev *dpc,
+                               struct dpc_rp_pio_regs *rp_pio)
+{
+       struct pci_dev *pdev = dpc->dev->port;
+       struct device *dev = &dpc->dev->device;
+       int i;
+       u16 cap;
+       u16 status;
+
+       pci_read_config_dword(pdev, dpc->cap_pos + PCI_EXP_DPC_RP_PIO_STATUS,
+                             &rp_pio->status);
+       pci_read_config_dword(pdev, dpc->cap_pos + PCI_EXP_DPC_RP_PIO_MASK,
+                             &rp_pio->mask);
+
+       pci_read_config_dword(pdev, dpc->cap_pos + PCI_EXP_DPC_RP_PIO_SEVERITY,
+                             &rp_pio->severity);
+       pci_read_config_dword(pdev, dpc->cap_pos + PCI_EXP_DPC_RP_PIO_SYSERROR,
+                             &rp_pio->syserror);
+       pci_read_config_dword(pdev, dpc->cap_pos + PCI_EXP_DPC_RP_PIO_EXCEPTION,
+                             &rp_pio->exception);
+
+       /* Get First Error Pointer */
+       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
+       rp_pio->first_error = (status & 0x1f00) >> 8;
+
+       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
+       rp_pio->log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8;
+       if (rp_pio->log_size < 4 || rp_pio->log_size > 9) {
+               dev_err(dev, "RP PIO log size %u is invalid\n",
+                       rp_pio->log_size);
+               return;
+       }
+
+       pci_read_config_dword(pdev,
+                             dpc->cap_pos + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
+                             &rp_pio->header_log.dw0);
+       pci_read_config_dword(pdev,
+                             dpc->cap_pos + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4,
+                             &rp_pio->header_log.dw1);
+       pci_read_config_dword(pdev,
+                             dpc->cap_pos + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8,
+                             &rp_pio->header_log.dw2);
+       pci_read_config_dword(pdev,
+                             dpc->cap_pos + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12,
+                             &rp_pio->header_log.dw3);
+       if (rp_pio->log_size == 4)
+               return;
+
+       pci_read_config_dword(pdev,
+                             dpc->cap_pos + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG,
+                             &rp_pio->impspec_log);
+       for (i = 0; i < rp_pio->log_size - 5; i++)
+               pci_read_config_dword(pdev,
+                       dpc->cap_pos + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG,
+                       &rp_pio->tlp_prefix_log[i]);
+}
+
+static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
+{
+       struct dpc_rp_pio_regs rp_pio_regs;
+
+       dpc_rp_pio_get_info(dpc, &rp_pio_regs);
+       dpc_rp_pio_print_error(dpc, &rp_pio_regs);
+
+       dpc->rp_pio_status = rp_pio_regs.status;
+}
+
 static irqreturn_t dpc_irq(int irq, void *context)
 {
        struct dpc_dev *dpc = (struct dpc_dev *)context;
@@ -109,6 +267,10 @@ static irqreturn_t dpc_irq(int irq, void *context)
                         (ext_reason == 0) ? "RP PIO error" :
                         (ext_reason == 1) ? "software trigger" :
                                             "reserved error");
+               /* show RP PIO error detail information */
+               if (reason == 3 && ext_reason == 0)
+                       dpc_process_rp_pio_error(dpc);
+
                schedule_work(&dpc->work);
        }
        return IRQ_HANDLED;
index c22d3ebaca20aedf12aa9cab30b0c8cc50b99bf5..1ce96275531cf75261f74f4b231eb554d272b30e 100644 (file)
 #define  PCI_EXP_DPC_CAP_RP_EXT                0x20    /* Root Port Extensions for DPC */
 #define  PCI_EXP_DPC_CAP_POISONED_TLP  0x40    /* Poisoned TLP Egress Blocking Supported */
 #define  PCI_EXP_DPC_CAP_SW_TRIGGER    0x80    /* Software Triggering Supported */
+#define  PCI_EXP_DPC_RP_PIO_LOG_SIZE   0xF00   /* RP PIO log size */
 #define  PCI_EXP_DPC_CAP_DL_ACTIVE     0x1000  /* ERR_COR signal on DL_Active supported */
 
 #define PCI_EXP_DPC_CTL                        6       /* DPC control */
 
 #define PCI_EXP_DPC_SOURCE_ID          10      /* DPC Source Identifier */
 
+#define PCI_EXP_DPC_RP_PIO_STATUS       0x0C   /* RP PIO Status */
+#define PCI_EXP_DPC_RP_PIO_MASK                 0x10   /* RP PIO MASK */
+#define PCI_EXP_DPC_RP_PIO_SEVERITY     0x14   /* RP PIO Severity */
+#define PCI_EXP_DPC_RP_PIO_SYSERROR     0x18   /* RP PIO SysError */
+#define PCI_EXP_DPC_RP_PIO_EXCEPTION    0x1C   /* RP PIO Exception */
+#define PCI_EXP_DPC_RP_PIO_HEADER_LOG   0x20   /* RP PIO Header Log */
+#define PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG  0x30   /* RP PIO ImpSpec Log */
+#define PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG 0x34  /* RP PIO TLP Prefix Log */
+
 /* Precision Time Measurement */
 #define PCI_PTM_CAP                    0x04        /* PTM Capability */
 #define  PCI_PTM_CAP_REQ               0x00000001  /* Requester capable */