Index: sys/amd64/vmm/io/ppt.c =================================================================== --- sys/amd64/vmm/io/ppt.c +++ sys/amd64/vmm/io/ppt.c @@ -356,25 +356,12 @@ static void ppt_pci_reset(device_t dev) { - int ps; if (pcie_flr(dev, - max(pcie_get_max_completion_timeout(dev) / 1000, 10), - true)) + max(pcie_get_max_completion_timeout(dev) / 1000, 10), true)) return; - /* - * If FLR fails, attempt a power-management reset by cycling - * the device in/out of D3 state. - * PCI spec says we can only go into D3 state from D0 state. - * Transition from D[12] into D0 before going to D3 state. - */ - ps = pci_get_powerstate(dev); - if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3) - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D3) - pci_set_powerstate(dev, PCI_POWERSTATE_D3); - pci_set_powerstate(dev, ps); + pci_hard_reset(dev, false); } int Index: sys/dev/pci/pci.c =================================================================== --- sys/dev/pci/pci.c +++ sys/dev/pci/pci.c @@ -6365,6 +6365,135 @@ return (true); } +static bool +pcie_with_uplink_port(device_t dev, int *pcie_location) +{ + device_t bus, pcib; + devclass_t pci_class; + struct pci_devinfo *pdinfo; + + pci_class = devclass_find("pci"); + bus = device_get_parent(dev); + if (bus == NULL) + return (false); + if (device_get_devclass(bus) != pci_class) + return (false); + pcib = device_get_parent(bus); + if (pcib == NULL || + device_get_devclass(device_get_parent(pcib)) != pci_class) + return (false); + pdinfo = device_get_ivars(pcib); + if (pdinfo->cfg.pcie.pcie_location == 0) + return (false); + if (pdinfo->cfg.pcie.pcie_type != PCIEM_TYPE_DOWNSTREAM_PORT && + pdinfo->cfg.pcie.pcie_type != PCIEM_TYPE_ROOT_PORT) + return (false); + *pcie_location = pdinfo->cfg.pcie.pcie_location; + return (true); +} + +/* + * Try link drop and retrain of the downstream port of upstream + * switch, for PCIe. According to the PCIe 3.0 spec 6.6.1, this must + * cause Conventional Hot reset of the device in the slot. + * Alternative, for PCIe, could be the secondary bus reset initiatied + * on the upstream switch PCIR_BRIDGECTL_1, bit 6. + */ +static void +pcie_link_reset(device_t port, int pcie_location) +{ + uint16_t v; + + v = pci_read_config(port, pcie_location + PCIER_LINK_CTL, 2); + v |= PCIEM_LINK_CTL_LINK_DIS; + pci_write_config(port, pcie_location + PCIER_LINK_CTL, v, 2); + pause("pcier1", hz / 50); /* 20 ms */ + v &= ~PCIEM_LINK_CTL_LINK_DIS; + v |= PCIEM_LINK_CTL_RETRAIN_LINK; + pci_write_config(port, pcie_location + PCIER_LINK_CTL, v, 2); + pause("pcier2", hz / 10); /* 100 ms */ +} + +/* + * Issue Conventional Hot reset, and if not possible (e.g. device is + * not PCIe or link reset failed), power reset. + */ +void +pci_hard_reset(device_t dev, bool link_level) +{ + int pcie_location, ps; + + pci_save_state(dev); + if (link_level && pcie_with_uplink_port(dev, &pcie_location)) { + pcie_link_reset(device_get_parent(device_get_parent(dev)), + pcie_location); + } else { + /* + * If not PCIe or link method is not allowed, try + * power reset + */ + pci_save_state(dev); + ps = pci_get_powerstate(dev); + if (ps != PCI_POWERSTATE_D0 && ps != PCI_POWERSTATE_D3) + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + pci_set_powerstate(dev, PCI_POWERSTATE_D3); + pci_set_powerstate(dev, ps); + } + pci_restore_state(dev); +} + +/* + * Issue Conventional Hot reset to the device, while detaching drivers + * from the device itself and all sibling PCIe functions in the same + * slot. PCI(e) configuration is saved around the reset. + * + * Typical Conventional reset affects all functions of multi-function + * device. More, drivers accessing functions during the reset get + * very confused, and could corrupt the hardware state when it brought + * up from the cold. + */ +int +pcie_down_port_link_reset(device_t dev) +{ + device_t bus, dev1, *children; + int error, i, nchildren, pcie_location; + + error = EINVAL; + children = NULL; + nchildren = 0; + + mtx_lock(&Giant); + if (!pcie_with_uplink_port(dev, &pcie_location)) + goto done; + + bus = device_get_parent(dev); + error = device_get_children(bus, &children, &nchildren); + if (error != 0) + goto done; + for (i = 0; i < nchildren; i++) { + dev1 = children[i]; + error = device_quiesce(dev1); + if (error == 0) + error = device_detach(dev1); + pci_save_state(dev1); + } + + pcie_link_reset(device_get_parent(bus), pcie_location); + + for (i = 0; i < nchildren; i++) { + dev1 = children[i]; + pci_restore_state(dev1); + if (device_get_state(dev1) != DS_ATTACHED) + device_probe_and_attach(dev1); + } + error = 0; + +done: + mtx_unlock(&Giant); + free(children, M_TEMP); + return (error); +} + const struct pci_device_table * pci_match_device(device_t child, const struct pci_device_table *id, size_t nelt) { Index: sys/dev/pci/pcivar.h =================================================================== --- sys/dev/pci/pcivar.h +++ sys/dev/pci/pcivar.h @@ -671,6 +671,7 @@ device_t pci_find_pcie_root_port(device_t dev); int pci_get_max_payload(device_t dev); int pci_get_max_read_req(device_t dev); +void pci_hard_reset(device_t dev, bool link_level); void pci_restore_state(device_t dev); void pci_save_state(device_t dev); int pci_set_max_read_req(device_t dev, int size); @@ -679,6 +680,7 @@ uint32_t pcie_adjust_config(device_t dev, int reg, uint32_t mask, uint32_t value, int width); bool pcie_flr(device_t dev, u_int max_delay, bool force); +int pcie_down_port_link_reset(device_t dev); int pcie_get_max_completion_timeout(device_t dev); bool pcie_wait_for_pending_transactions(device_t dev, u_int max_delay);