diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -148,6 +148,10 @@ SYSCTL_INT(_hw_pci, OID_AUTO, clear_pcib, CTLFLAG_RDTUN, &pci_clear_pcib, 0, "Clear firmware-assigned resources for PCI-PCI bridge I/O windows."); +static int pci_dll_timeout = 1; +SYSCTL_INT(_hw_pci, OID_AUTO, dll_link_timeout, CTLFLAG_RWTUN, &pci_dll_timeout, + 0, "Timeout in seconds for DLL active"); + /* * Is a resource from a child device sub-allocated from one of our * resource managers? @@ -1046,11 +1050,14 @@ { /* Pretend the card isn't present if a detach is forced. */ - if (sc->flags & PCIB_DETACHING) + if (sc->state == PCIB_STATE_DETACHING) return (false); - /* Card must be present in the slot. */ - if ((sc->pcie_slot_sta & PCIEM_SLOT_STA_PDS) == 0) + /* XXX RL seems the starship/matisse bridge can report link active and + * no presence detect when a device comes up on the link */ + /* Card must be present in the slot, or the link must be active. */ + if ((sc->pcie_slot_sta & PCIEM_SLOT_STA_PDS) == 0 && + (sc->pcie_link_sta & PCIEM_LINK_STA_DL_ACTIVE) == 0) return (false); /* XXX KDM Viking AMD chassis keeps returning power fault */ @@ -1091,10 +1098,13 @@ { bool card_inserted, ei_engaged = false; - /* Clear DETACHING if Presence Detect has cleared. */ + /* Transition to DETACHED if Presence Detect has cleared and we were + detaching. */ if ((sc->pcie_slot_sta & (PCIEM_SLOT_STA_PDC | PCIEM_SLOT_STA_PDS)) == - PCIEM_SLOT_STA_PDC) - sc->flags &= ~PCIB_DETACHING; + PCIEM_SLOT_STA_PDC) { + sc->state = PCIB_STATE_DETACHED; + goto task_schedule; + } card_inserted = pcib_hotplug_inserted(sc); @@ -1103,7 +1113,7 @@ mask |= PCIEM_SLOT_CTL_PIC; if (card_inserted) val |= PCIEM_SLOT_CTL_PI_ON; - else if (sc->flags & PCIB_DETACH_PENDING) + else if (sc->state == PCIB_STATE_DETACH_PENDING) val |= PCIEM_SLOT_CTL_PI_BLINK; else val |= PCIEM_SLOT_CTL_PI_OFF; @@ -1146,13 +1156,16 @@ device_printf(sc->dev, "Data Link Layer inactive\n"); else - callout_reset(&sc->pcie_dll_timer, hz, + callout_reset(&sc->pcie_dll_timer, pci_dll_timeout * hz, pcib_pcie_dll_timeout, sc); - } else if (sc->pcie_link_sta & PCIEM_LINK_STA_DL_ACTIVE) + } else if (sc->pcie_link_sta & PCIEM_LINK_STA_DL_ACTIVE) { callout_stop(&sc->pcie_dll_timer); + sc->state = PCIB_STATE_ATTACHED; + } pcib_pcie_hotplug_command(sc, val, mask); +task_schedule: /* * During attach the child "pci" device is added synchronously; * otherwise, the task is scheduled to manage the child @@ -1185,16 +1198,16 @@ if ((sc->pcie_slot_sta & PCIEM_SLOT_STA_ABP) && (pci_enable_attn != 0)) { - if (sc->flags & PCIB_DETACH_PENDING) { + if (sc->state == PCIB_STATE_DETACH_PENDING) { device_printf(dev, "Attention Button Pressed: Detach Cancelled\n"); - sc->flags &= ~PCIB_DETACH_PENDING; + sc->state = PCIB_STATE_ATTACHED; callout_stop(&sc->pcie_ab_timer); } else if (old_slot_sta & PCIEM_SLOT_STA_PDS) { /* Only initiate detach sequence if device present. */ device_printf(dev, "Attention Button Pressed: Detaching in 5 seconds\n"); - sc->flags |= PCIB_DETACH_PENDING; + sc->state = PCIB_STATE_DETACH_PENDING; callout_reset(&sc->pcie_ab_timer, 5 * hz, pcib_pcie_ab_timeout, sc); } @@ -1257,9 +1270,8 @@ sc = arg; PCIB_HP_LOCK_ASSERT(sc); - if (sc->flags & PCIB_DETACH_PENDING) { - sc->flags |= PCIB_DETACHING; - sc->flags &= ~PCIB_DETACH_PENDING; + if (sc->state == PCIB_STATE_DETACH_PENDING) { + sc->state = PCIB_STATE_DETACHING; pcib_pcie_hotplug_update(sc, 0, 0, true); } } @@ -1299,7 +1311,8 @@ if (!(sta & PCIEM_LINK_STA_DL_ACTIVE)) { device_printf(dev, "Timed out waiting for Data Link Layer Active\n"); - sc->flags |= PCIB_DETACHING; + if (sc->state == PCIB_STATE_DETACH_PENDING) + sc->state = PCIB_STATE_DETACHING; pcib_pcie_hotplug_update(sc, 0, 0, true); } else if (sta != sc->pcie_link_sta) { device_printf(dev, @@ -1430,11 +1443,10 @@ int error; /* Disable the card in the slot and force it to detach. */ - if (sc->flags & PCIB_DETACH_PENDING) { - sc->flags &= ~PCIB_DETACH_PENDING; + if (sc->state == PCIB_STATE_DETACH_PENDING) { callout_stop(&sc->pcie_ab_timer); } - sc->flags |= PCIB_DETACHING; + sc->state = PCIB_STATE_DETACHING; if (sc->flags & PCIB_HOTPLUG_CMD_PENDING) { callout_stop(&sc->pcie_cc_timer); diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h --- a/sys/dev/pci/pcib_private.h +++ b/sys/dev/pci/pcib_private.h @@ -114,8 +114,11 @@ #define PCIB_ENABLE_ARI 0x8 #define PCIB_HOTPLUG 0x10 #define PCIB_HOTPLUG_CMD_PENDING 0x20 -#define PCIB_DETACH_PENDING 0x40 -#define PCIB_DETACHING 0x80 + uint32_t state; +#define PCIB_STATE_DETACHED 0 +#define PCIB_STATE_ATTACHED 1 +#define PCIB_STATE_DETACH_PENDING 2 +#define PCIB_STATE_DETACHING 3 u_int domain; /* domain number */ u_int pribus; /* primary bus number */ struct pcib_secbus bus; /* secondary bus numbers */