@@ -267,6 +267,9 @@ int64_t pcie_slot_sm_restore_buses(struct pci_slot *slot)
struct pci_device *pd, *parent;
uint32_t link_cap = 0;
uint16_t link_sts = 0;
+ uint32_t slot_cap = 0;
+ uint16_t slot_ctl = 0;
+ uint8_t power_state;
int32_t ecap = 0;
struct phb *phb;
uint32_t vdid;
@@ -296,9 +299,8 @@ int64_t pcie_slot_sm_restore_buses(struct pci_slot *slot)
if (!pd->is_vf && !(pd->bdfn & 7) && pd->parent != NULL &&
pd->parent->dev_type == PCIE_TYPE_SWITCH_DNPORT) {
PCIDBG(phb, pd->bdfn, "BUSES: Behind a switch\n");
- slot->retries = PCI_BUS_SWITCH_LINK_RETRIES;
pci_slot_set_state(slot,
- PCI_SLOT_BUSES_PD_WAIT_SWITCH_LINK);
+ PCI_SLOT_BUSES_PD_SWITCH_POWER);
return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
} else {
PCIDBG(phb, pd->bdfn, "BUSES: Not behind a switch\n");
@@ -306,6 +308,55 @@ int64_t pcie_slot_sm_restore_buses(struct pci_slot *slot)
pci_slot_set_state(slot, PCI_SLOT_BUSES_PD_WAIT_CRS);
return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
}
+ case PCI_SLOT_BUSES_PD_SWITCH_POWER:
+ if (pci_has_cap(parent, PCI_CFG_CAP_ID_EXP, false)) {
+ ecap = pci_cap(parent, PCI_CFG_CAP_ID_EXP, false);
+ pci_cfg_read32(phb, parent->bdfn,
+ ecap + PCICAP_EXP_SLOTCAP, &slot_cap);
+ }
+
+ if (!(slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) {
+ PCIDBG(phb, parent->bdfn,
+ "BUSES: Parent: No power control\n");
+ slot->retries = PCI_BUS_SWITCH_LINK_RETRIES;
+ pci_slot_set_state(slot, PCI_SLOT_BUSES_PD_WAIT_SWITCH_LINK);
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
+ }
+
+ PCIDBG(phb, parent->bdfn, "BUSES: Parent: Have power control\n");
+ /* Always turn it off */
+ PCIDBG(phb, parent->bdfn, "BUSES: Parent: Turning slot off\n");
+ pci_cfg_read16(phb, parent->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, &slot_ctl);
+ slot_ctl |= PCICAP_EXP_SLOTCTL_PWRCTLR;
+ slot_ctl |= SETFIELD(PCICAP_EXP_SLOTCTL_PWRI, 0, PCIE_INDIC_OFF);
+ pci_cfg_write16(phb, parent->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, slot_ctl);
+ pci_slot_set_state(slot, PCI_SLOT_BUSES_PD_WAIT_SWITCH_OFF);
+ return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
+ case PCI_SLOT_BUSES_PD_WAIT_SWITCH_OFF:
+ parent->slot->ops.get_power_state(parent->slot, &power_state);
+ if (power_state == PCI_SLOT_POWER_OFF) {
+ PCIDBG(phb, parent->bdfn,
+ "BUSES: Parent: Slot power state is off. Nothing to do\n");
+ slot->phb->current_pd = pci_device_iter_next(slot->phb,
+ NULL);
+ pci_slot_set_state(slot, PCI_SLOT_BUSES_PD_START);
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
+ }
+
+ PCIDBG(phb, parent->bdfn, "BUSES: Parent: Turning slot on\n");
+ ecap = pci_cap(parent, PCI_CFG_CAP_ID_EXP, false);
+ pci_cfg_read16(phb, parent->bdfn, ecap + PCICAP_EXP_SLOTCTL,
+ &slot_ctl);
+ slot_ctl &= ~PCICAP_EXP_SLOTCTL_PWRCTLR;
+ slot_ctl |= SETFIELD(PCICAP_EXP_SLOTCTL_PWRI, 0, PCIE_INDIC_ON);
+ pci_cfg_write16(phb, parent->bdfn,
+ ecap + PCICAP_EXP_SLOTCTL, slot_ctl);
+
+ slot->retries = PCI_BUS_SWITCH_LINK_RETRIES;
+ pci_slot_set_state(slot, PCI_SLOT_BUSES_PD_WAIT_SWITCH_LINK);
+ return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
case PCI_SLOT_BUSES_PD_WAIT_SWITCH_LINK:
PCIDBG(phb, pd->bdfn, "BUSES: Wait for Switch Link\n");
@@ -134,9 +134,11 @@ struct pci_slot_ops {
#define PCI_SLOT_BUSES PCI_SLOT_STATE_BUSES
#define PCI_SLOT_BUSES_START (PCI_SLOT_BUSES + 1)
#define PCI_SLOT_BUSES_PD_START (PCI_SLOT_BUSES + 2)
-#define PCI_SLOT_BUSES_PD_WAIT_SWITCH_LINK (PCI_SLOT_BUSES + 3)
-#define PCI_SLOT_BUSES_PD_WAIT_CRS (PCI_SLOT_BUSES + 4)
-#define PCI_SLOT_BUSES_PD_END (PCI_SLOT_BUSES + 5)
+#define PCI_SLOT_BUSES_PD_SWITCH_POWER (PCI_SLOT_BUSES + 3)
+#define PCI_SLOT_BUSES_PD_WAIT_SWITCH_OFF (PCI_SLOT_BUSES + 4)
+#define PCI_SLOT_BUSES_PD_WAIT_SWITCH_LINK (PCI_SLOT_BUSES + 5)
+#define PCI_SLOT_BUSES_PD_WAIT_CRS (PCI_SLOT_BUSES + 6)
+#define PCI_SLOT_BUSES_PD_END (PCI_SLOT_BUSES + 7)
#define PCI_BUS_CRS_RETRIES 40
#define PCI_BUS_SWITCH_LINK_RETRIES 100
After a fundamental reset we currently assume that each slot's power controllers maintain their correct state. This is an invalid assumption. We need to make sure that this state gets restored. If the power controller does not get turned on but we think it is on things will go badly. We can handle this with extra states in our restore_buses() state machine function. If a PCI devices is behind a switch, check for a power controller and if present restore the power state before waiting for the link to activate. Signed-off-by: Jordan Niethe <jniethe5@gmail.com> --- v2: - Make sure we actually change to PHB4_SLOT_BUSES_PD_WAIT_SWITCH_OFF state - Use get_power_state() for reading the slot power state core/pcie-slot.c | 55 ++++++++++++++++++++++++++++++++++++++++++++-- include/pci-slot.h | 8 ++++--- 2 files changed, 58 insertions(+), 5 deletions(-)