@@ -22,6 +22,7 @@ struct dpc_dev {
u16 cap_pos;
bool rp_extensions;
u8 rp_log_size;
+ bool edr_enabled; /* EDR mode is supported */
};
static const char * const rp_pio_error_string[] = {
@@ -69,6 +70,14 @@ void pci_save_dpc_state(struct pci_dev *dev)
if (!dpc)
return;
+ /*
+ * If DPC is controlled by firmware then save/restore tasks are also
+ * controller by firmware. So skip rest of the function if DPC is
+ * controlled by firmware.
+ */
+ if (dpc->edr_enabled)
+ return;
+
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
if (!save_state)
return;
@@ -90,6 +99,14 @@ void pci_restore_dpc_state(struct pci_dev *dev)
if (!dpc)
return;
+ /*
+ * If DPC is controlled by firmware then save/restore tasks are also
+ * controller by firmware. So skip rest of the function if DPC is
+ * controlled by firmware.
+ */
+ if (dpc->edr_enabled)
+ return;
+
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
if (!save_state)
return;
@@ -291,24 +308,42 @@ static int dpc_probe(struct pcie_device *dev)
int status;
u16 ctl, cap;
- if (pcie_aer_get_firmware_first(pdev) && !pcie_ports_dpc_native)
- 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;
+ if (pcie_aer_get_firmware_first(pdev) && !pcie_ports_dpc_native)
+ dpc->edr_enabled = 1;
set_service_data(dev, dpc);
- status = devm_request_threaded_irq(device, dev->irq, dpc_irq,
- dpc_handler, IRQF_SHARED,
- "pcie-dpc", dpc);
- if (status) {
- pci_warn(pdev, "request IRQ%d failed: %d\n", dev->irq,
- status);
- return status;
+ /*
+ * As per PCIe spec r5.0, implementation note titled "Determination
+ * of DPC Control", to avoid conflicts over whether platform
+ * firmware or the operating system have control of DPC, it is
+ * recommended that platform firmware and operating systems always link
+ * the control of DPC to the control of Advanced Error Reporting.
+ *
+ * So use AER FF mode check API pcie_aer_get_firmware_first() to decide
+ * whether DPC is controlled by software or firmware.
+ *
+ * If DPC is handled in firmware and ACPI support is not enabled
+ * in OS, skip probe and return error.
+ */
+ if (dpc->edr_enabled && !IS_ENABLED(CONFIG_ACPI))
+ return -ENODEV;
+
+ /* Register interrupt handler only if OS controls DPC */
+ if (!dpc->edr_enabled) {
+ status = devm_request_threaded_irq(device, dev->irq, dpc_irq,
+ dpc_handler, IRQF_SHARED,
+ "pcie-dpc", dpc);
+ if (status) {
+ pci_warn(pdev, "request IRQ%d failed: %d\n", dev->irq,
+ status);
+ return status;
+ }
}
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
@@ -323,9 +358,12 @@ static int dpc_probe(struct pcie_device *dev)
dpc->rp_log_size = 0;
}
}
-
- ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
- pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
+ if (!dpc->edr_enabled) {
+ ctl = (ctl & 0xfff4) |
+ (PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN);
+ pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL,
+ ctl);
+ }
pci_info(pdev, "error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
@@ -343,6 +381,10 @@ static void dpc_remove(struct pcie_device *dev)
struct pci_dev *pdev = dev->port;
u16 ctl;
+ /* Skip updating DPC registers if DPC is controlled by firmware */
+ if (dpc->edr_enabled)
+ return;
+
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN);
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);