diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index ac53edbc9613..d658dfa53b87 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -92,7 +92,7 @@ config PCIE_PME config PCIE_DPC bool "PCIe Downstream Port Containment support" - depends on PCIEPORTBUS + depends on PCIEPORTBUS && PCIEAER default n help This enables PCI Express Downstream Port Containment (DPC) diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index 2d976a623ddc..7eb9bc7d4bfd 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -15,34 +15,15 @@ #include #include #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; -}; +#include "aer/aerdrv.h" struct dpc_dev { struct pcie_device *dev; struct work_struct work; - int cap_pos; - bool rp; + u16 cap_pos; + bool rp_extensions; u32 rp_pio_status; + u8 rp_log_size; }; static const char * const rp_pio_error_string[] = { @@ -72,13 +53,13 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc) unsigned long timeout = jiffies + HZ; struct pci_dev *pdev = dpc->dev->port; struct device *dev = &dpc->dev->device; - u16 status; + u16 cap = dpc->cap_pos, status; - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status); + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); while (status & PCI_EXP_DPC_RP_BUSY && !time_after(jiffies, timeout)) { msleep(10); - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status); + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); } if (status & PCI_EXP_DPC_RP_BUSY) { dev_warn(dev, "DPC root port still busy\n"); @@ -104,11 +85,12 @@ static void dpc_wait_link_inactive(struct dpc_dev *dpc) dev_warn(dev, "Link state not disabled for DPC event\n"); } -static void interrupt_event_handler(struct work_struct *work) +static void dpc_work(struct work_struct *work) { struct dpc_dev *dpc = container_of(work, struct dpc_dev, work); struct pci_dev *dev, *temp, *pdev = dpc->dev->port; struct pci_bus *parent = pdev->subordinate; + u16 cap = dpc->cap_pos, ctl; pci_lock_rescan_remove(); list_for_each_entry_safe_reverse(dev, temp, &parent->devices, @@ -124,124 +106,77 @@ static void interrupt_event_handler(struct work_struct *work) pci_unlock_rescan_remove(); dpc_wait_link_inactive(dpc); - if (dpc->rp && dpc_wait_rp_inactive(dpc)) + if (dpc->rp_extensions && 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); + if (dpc->rp_extensions && dpc->rp_pio_status) { + pci_write_config_dword(pdev, cap + 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_write_config_word(pdev, cap + 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]); + pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); + pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, + ctl | PCI_EXP_DPC_CTL_INT_EN); } static void dpc_process_rp_pio_error(struct dpc_dev *dpc) { - struct dpc_rp_pio_regs rp_pio_regs; + struct device *dev = &dpc->dev->device; + struct pci_dev *pdev = dpc->dev->port; + u16 cap = dpc->cap_pos, dpc_status, first_error; + u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix; + int i; - dpc_rp_pio_get_info(dpc, &rp_pio_regs); - dpc_rp_pio_print_error(dpc, &rp_pio_regs); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask); + dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n", + status, mask); - dpc->rp_pio_status = rp_pio_regs.status; + dpc->rp_pio_status = status; + + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc); + dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n", + sev, syserr, exc); + + /* Get First Error Pointer */ + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status); + first_error = (dpc_status & 0x1f00) >> 8; + + status &= ~mask; + for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) { + if (status & (1 << i)) + dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i], + first_error == i ? " (First)" : ""); + } + + if (dpc->rp_log_size < 4) + return; + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, + &dw0); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4, + &dw1); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8, + &dw2); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12, + &dw3); + dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n", + dw0, dw1, dw2, dw3); + + if (dpc->rp_log_size < 5) + return; + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log); + dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log); + + for (i = 0; i < dpc->rp_log_size - 5; i++) { + pci_read_config_dword(pdev, + cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix); + dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix); + } } static irqreturn_t dpc_irq(int irq, void *context) @@ -249,34 +184,49 @@ static irqreturn_t dpc_irq(int irq, void *context) struct dpc_dev *dpc = (struct dpc_dev *)context; struct pci_dev *pdev = dpc->dev->port; struct device *dev = &dpc->dev->device; - u16 status, source; + u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason; - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status); - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_SOURCE_ID, - &source); - if (!status || status == (u16)(~0)) + pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); + + if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0)) return IRQ_NONE; + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + + if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT)) + return IRQ_NONE; + + if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { + pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_INTERRUPT); + return IRQ_HANDLED; + } + + pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, + ctl & ~PCI_EXP_DPC_CTL_INT_EN); + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID, + &source); + dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n", status, source); - if (status & PCI_EXP_DPC_STATUS_TRIGGER) { - u16 reason = (status >> 1) & 0x3; - u16 ext_reason = (status >> 5) & 0x3; + reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1; + ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5; - dev_warn(dev, "DPC %s detected, remove downstream devices\n", - (reason == 0) ? "unmasked uncorrectable error" : - (reason == 1) ? "ERR_NONFATAL" : - (reason == 2) ? "ERR_FATAL" : - (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); + dev_warn(dev, "DPC %s detected, remove downstream devices\n", + (reason == 0) ? "unmasked uncorrectable error" : + (reason == 1) ? "ERR_NONFATAL" : + (reason == 2) ? "ERR_FATAL" : + (ext_reason == 0) ? "RP PIO error" : + (ext_reason == 1) ? "software trigger" : + "reserved error"); + /* show RP PIO error detail information */ + if (dpc->rp_extensions && reason == 3 && ext_reason == 0) + dpc_process_rp_pio_error(dpc); + + schedule_work(&dpc->work); - schedule_work(&dpc->work); - } return IRQ_HANDLED; } @@ -289,13 +239,16 @@ static int dpc_probe(struct pcie_device *dev) int status; u16 ctl, cap; + if (pcie_aer_get_firmware_first(pdev)) + return -ENOTSUPP; + dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL); if (!dpc) return -ENOMEM; dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC); dpc->dev = dev; - INIT_WORK(&dpc->work, interrupt_event_handler); + INIT_WORK(&dpc->work, dpc_work); set_service_data(dev, dpc); status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED, @@ -309,15 +262,23 @@ static int dpc_probe(struct pcie_device *dev) pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap); pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); - dpc->rp = (cap & PCI_EXP_DPC_CAP_RP_EXT); + dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT); + if (dpc->rp_extensions) { + dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8; + if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) { + dev_err(device, "RP PIO log size %u is invalid\n", + dpc->rp_log_size); + dpc->rp_log_size = 0; + } + } ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN; pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", - cap & 0xf, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT), + cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT), FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP), - FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), (cap >> 8) & 0xf, + FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size, FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE)); return status; } diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index a59210350c44..ef3bad4ad010 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -216,9 +216,9 @@ static int get_port_device_capability(struct pci_dev *dev) return 0; cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP - | PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC; + | PCIE_PORT_SERVICE_VC; if (pci_aer_available()) - cap_mask |= PCIE_PORT_SERVICE_AER; + cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC; if (pcie_ports_auto) pcie_port_platform_notify(dev, &cap_mask); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 70c2b2ade048..66d71461d2f0 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -966,26 +966,28 @@ /* Downstream Port Containment */ #define PCI_EXP_DPC_CAP 4 /* DPC Capability */ -#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */ -#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_IRQ 0x001F /* Interrupt Message Number */ +#define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */ +#define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */ +#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */ +#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* 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_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */ -#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */ +#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */ +#define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */ #define PCI_EXP_DPC_STATUS 8 /* DPC Status */ -#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */ -#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */ -#define PCI_EXP_DPC_RP_BUSY 0x10 /* Root Port Busy */ +#define PCI_EXP_DPC_STATUS_TRIGGER 0x0001 /* Trigger Status */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN 0x0006 /* Trigger Reason */ +#define PCI_EXP_DPC_STATUS_INTERRUPT 0x0008 /* Interrupt Status */ +#define PCI_EXP_DPC_RP_BUSY 0x0010 /* Root Port Busy */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT 0x0060 /* Trig Reason Extension */ #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_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 */