@@ -12,9 +12,11 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
+#include <linux/pci.h>
#include <linux/pci-ecam.h>
#include <linux/platform_device.h>
+#include "../pci.h"
#include "pci-host-common.h"
static void gen_pci_unmap_cfg(void *ptr)
@@ -96,5 +98,61 @@ void pci_host_common_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(pci_host_common_remove);
+#if IS_ENABLED(CONFIG_PCIEAER)
+static pci_ers_result_t pci_host_reset_slot(struct pci_dev *dev)
+{
+ int ret;
+
+ ret = pci_bus_error_reset(dev);
+ if (ret) {
+ pci_err(dev, "Failed to reset slot: %d\n", ret);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_info(dev, "Slot has been reset\n");
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void pci_host_recover_slots(struct pci_host_bridge *host)
+{
+ struct pci_bus *bus = host->bus;
+ struct pci_dev *dev;
+
+ for_each_pci_bridge(dev, bus) {
+ if (!pci_is_root_bus(bus))
+ continue;
+
+ pcie_do_recovery(dev, pci_channel_io_frozen,
+ pci_host_reset_slot);
+ }
+}
+#else
+static void pci_host_recover_slots(struct pci_host_bridge *host)
+{
+ struct pci_bus *bus = host->bus;
+ struct pci_dev *dev;
+ int ret;
+
+ for_each_pci_bridge(dev, bus) {
+ if (!pci_is_root_bus(bus))
+ continue;
+
+ ret = pci_bus_error_reset(dev);
+ if (ret)
+ pci_err(dev, "Failed to reset slot: %d\n", ret);
+ else
+ pci_info(dev, "Slot has been reset\n");
+ }
+}
+#endif
+
+void pci_host_handle_link_down(struct pci_host_bridge *bridge)
+{
+ dev_info(&bridge->dev, "Recovering slots due to Link Down\n");
+ pci_host_recover_slots(bridge);
+}
+EXPORT_SYMBOL_GPL(pci_host_handle_link_down);
+
MODULE_DESCRIPTION("Common library for PCI host controller drivers");
MODULE_LICENSE("GPL v2");
@@ -12,5 +12,6 @@
int pci_host_common_probe(struct platform_device *pdev);
void pci_host_common_remove(struct platform_device *pdev);
+void pci_host_handle_link_down(struct pci_host_bridge *bridge);
#endif
@@ -5781,6 +5781,7 @@ int pci_bus_error_reset(struct pci_dev *bridge)
mutex_unlock(&pci_slot_mutex);
return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET);
}
+EXPORT_SYMBOL_GPL(pci_bus_error_reset);
/**
* pci_probe_reset_bus - probe whether a PCI bus can be reset
@@ -270,3 +270,4 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
return status;
}
+EXPORT_SYMBOL_GPL(pcie_do_recovery);