summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/controller/pci-host-common.c35
-rw-r--r--drivers/pci/controller/pci-host-common.h1
-rw-r--r--drivers/pci/pci.c1
-rw-r--r--drivers/pci/pcie/err.c1
4 files changed, 38 insertions, 0 deletions
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index 2ce6f4b66133..363d3f970b10 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -13,9 +13,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"
/**
@@ -342,5 +344,38 @@ bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
}
EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible);
+static pci_ers_result_t pci_host_reset_root_port(struct pci_dev *dev)
+{
+ int ret;
+
+ pci_lock_rescan_remove();
+ ret = pci_bus_error_reset(dev);
+ pci_unlock_rescan_remove();
+ if (ret) {
+ pci_err(dev, "Failed to reset Root Port: %d\n", ret);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_info(dev, "Root Port has been reset\n");
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void pci_host_recover_root_port(struct pci_dev *port)
+{
+#if IS_ENABLED(CONFIG_PCIEAER)
+ pcie_do_recovery(port, pci_channel_io_frozen, pci_host_reset_root_port);
+#else
+ pci_host_reset_root_port(port);
+#endif
+}
+
+void pci_host_handle_link_down(struct pci_dev *port)
+{
+ pci_info(port, "Recovering Root Port due to Link Down\n");
+ pci_host_recover_root_port(port);
+}
+EXPORT_SYMBOL_GPL(pci_host_handle_link_down);
+
MODULE_DESCRIPTION("Common library for PCI host controller drivers");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h
index 9f0f36a32221..51afd65d3c4a 100644
--- a/drivers/pci/controller/pci-host-common.h
+++ b/drivers/pci/controller/pci-host-common.h
@@ -48,6 +48,7 @@ int pci_host_common_init(struct platform_device *pdev,
struct pci_host_bridge *bridge,
const struct pci_ecam_ops *ops);
void pci_host_common_remove(struct platform_device *pdev);
+void pci_host_handle_link_down(struct pci_dev *port);
struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 12e099489690..ff6d5d059b21 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -5700,6 +5700,7 @@ int pci_bus_error_reset(struct pci_dev *bridge)
{
return pci_reset_bridge(bridge, PCI_RESET_NO_RESTORE);
}
+EXPORT_SYMBOL_GPL(pci_bus_error_reset);
int pci_try_reset_bridge(struct pci_dev *bridge)
{
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
index 13b9d9eb714f..d77403d8855b 100644
--- a/drivers/pci/pcie/err.c
+++ b/drivers/pci/pcie/err.c
@@ -292,3 +292,4 @@ failed:
return status;
}
+EXPORT_SYMBOL_GPL(pcie_do_recovery);