|
@@ -73,29 +73,30 @@ static void dpc_wait_link_inactive(struct dpc_dev *dpc)
|
|
|
pcie_wait_for_link(pdev, false);
|
|
|
}
|
|
|
|
|
|
-static void dpc_work(struct work_struct *work)
|
|
|
+static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
|
|
|
{
|
|
|
- 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,
|
|
|
- bus_list) {
|
|
|
- pci_dev_get(dev);
|
|
|
- pci_dev_set_disconnected(dev, NULL);
|
|
|
- if (pci_has_subordinate(dev))
|
|
|
- pci_walk_bus(dev->subordinate,
|
|
|
- pci_dev_set_disconnected, NULL);
|
|
|
- pci_stop_and_remove_bus_device(dev);
|
|
|
- pci_dev_put(dev);
|
|
|
- }
|
|
|
- pci_unlock_rescan_remove();
|
|
|
-
|
|
|
+ struct dpc_dev *dpc;
|
|
|
+ struct pcie_device *pciedev;
|
|
|
+ struct device *devdpc;
|
|
|
+ u16 cap, ctl;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * DPC disables the Link automatically in hardware, so it has
|
|
|
+ * already been reset by the time we get here.
|
|
|
+ */
|
|
|
+ devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
|
|
|
+ pciedev = to_pcie_device(devdpc);
|
|
|
+ dpc = get_service_data(pciedev);
|
|
|
+ cap = dpc->cap_pos;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Wait until the Link is inactive, then clear DPC Trigger Status
|
|
|
+ * to allow the Port to leave DPC.
|
|
|
+ */
|
|
|
dpc_wait_link_inactive(dpc);
|
|
|
+
|
|
|
if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
|
|
|
- return;
|
|
|
+ return PCI_ERS_RESULT_DISCONNECT;
|
|
|
if (dpc->rp_extensions && dpc->rp_pio_status) {
|
|
|
pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
|
|
|
dpc->rp_pio_status);
|
|
@@ -108,6 +109,17 @@ static void dpc_work(struct work_struct *work)
|
|
|
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);
|
|
|
+
|
|
|
+ return PCI_ERS_RESULT_RECOVERED;
|
|
|
+}
|
|
|
+
|
|
|
+static void dpc_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
|
|
|
+ struct pci_dev *pdev = dpc->dev->port;
|
|
|
+
|
|
|
+ /* We configure DPC so it only triggers on ERR_FATAL */
|
|
|
+ pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC);
|
|
|
}
|
|
|
|
|
|
static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
|
|
@@ -288,6 +300,7 @@ static struct pcie_port_service_driver dpcdriver = {
|
|
|
.service = PCIE_PORT_SERVICE_DPC,
|
|
|
.probe = dpc_probe,
|
|
|
.remove = dpc_remove,
|
|
|
+ .reset_link = dpc_reset_link,
|
|
|
};
|
|
|
|
|
|
static int __init dpc_service_init(void)
|