@@ -966,6 +966,7 @@ int pci_aer_clear_status(struct pci_dev *dev);
int pci_aer_raw_clear_status(struct pci_dev *dev);
void pci_save_aer_state(struct pci_dev *dev);
void pci_restore_aer_state(struct pci_dev *dev);
+void pcie_do_recover_slots(struct pci_host_bridge *host);
#else
static inline void pci_no_aer(void) { }
static inline void pci_aer_init(struct pci_dev *d) { }
@@ -975,6 +976,26 @@ static inline int pci_aer_clear_status(struct pci_dev *dev) { return -EINVAL; }
static inline int pci_aer_raw_clear_status(struct pci_dev *dev) { return -EINVAL; }
static inline void pci_save_aer_state(struct pci_dev *dev) { }
static inline void pci_restore_aer_state(struct pci_dev *dev) { }
+static inline void pcie_do_recover_slots(struct pci_host_bridge *host)
+{
+ struct pci_bus *bus = host->bus;
+ struct pci_dev *dev;
+ int ret;
+
+ if (!host->reset_slot)
+ dev_warn(&host->dev, "Missing reset_slot() callback\n");
+
+ 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
#ifdef CONFIG_ACPI
@@ -270,3 +270,30 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
return status;
}
+
+static pci_ers_result_t pcie_do_slot_reset(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;
+}
+
+void pcie_do_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))
+ pcie_do_recovery(dev, pci_channel_io_frozen,
+ pcie_do_slot_reset);
+ }
+}
@@ -3249,6 +3249,13 @@ int pci_host_probe(struct pci_host_bridge *bridge)
}
EXPORT_SYMBOL_GPL(pci_host_probe);
+void pci_host_handle_link_down(struct pci_host_bridge *bridge)
+{
+ dev_info(&bridge->dev, "Recovering slots due to Link Down\n");
+ pcie_do_recover_slots(bridge);
+}
+EXPORT_SYMBOL_GPL(pci_host_handle_link_down);
+
int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
{
struct resource *res = &b->busn_res;
@@ -1157,6 +1157,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
struct pci_ops *ops, void *sysdata,
struct list_head *resources);
int pci_host_probe(struct pci_host_bridge *bridge);
+void pci_host_handle_link_down(struct pci_host_bridge *bridge);
int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int busmax);
int pci_bus_update_busn_res_end(struct pci_bus *b, int busmax);
void pci_bus_release_busn_res(struct pci_bus *b);