[Skiboot] [RFC PATCH 3/3] hw/phb4: Restore slot power controller state

Jordan Niethe jniethe5 at gmail.com
Tue Oct 22 14:32:02 AEDT 2019


After a fundamental reset we currently assume that slot's power
controllers maintain their correct state. There is no reason to believe
that this assumption is true, so we need to make sure that this state
gets restored. If the state does not get restored to on when we expect
it, the link will not be able to activate, leading to unexpected
behavior.

We can handle this with extra states in our restore_buses() state
machine. 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 at gmail.com>
---
 hw/phb4.c      | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 include/phb4.h |  8 +++++---
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/hw/phb4.c b/hw/phb4.c
index 487965c15036..5092b045f642 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -2765,7 +2765,9 @@ static int64_t phb4_restore_buses(struct pci_slot *slot)
 	struct phb4 *p = phb_to_phb4(slot->phb);
 	struct pci_device *pd, *parent;
 	uint32_t link_cap = 0;
+	uint32_t slot_cap = 0;
 	uint16_t link_sts = 0;
+	uint16_t slot_ctl;
 	int32_t ecap = 0;
 	struct phb *phb;
 	uint32_t vdid;
@@ -2797,9 +2799,8 @@ static int64_t phb4_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 = PHB4_BUS_SWITCH_LINK_RETRIES;
 			pci_slot_set_state(slot,
-					   PHB4_SLOT_BUSES_PD_WAIT_SWITCH_LINK);
+					   PHB4_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");
@@ -2807,6 +2808,48 @@ static int64_t phb4_restore_buses(struct pci_slot *slot)
 			pci_slot_set_state(slot, PHB4_SLOT_BUSES_PD_WAIT_CRS);
 			return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
 		}
+	case PHB4_SLOT_BUSES_PD_SWITCH_POWER:
+		PCIDBG(phb, pd->bdfn, "BUSES: PCI Device Switch power\n");
+		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: no power control\n");
+			slot->retries = PHB4_BUS_SWITCH_LINK_RETRIES;
+			pci_slot_set_state(slot, PHB4_SLOT_BUSES_PD_WAIT_SWITCH_LINK);
+			return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
+		}
+
+		/* Always turn it off */
+		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);
+		return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
+	case PHB4_SLOT_BUSES_PD_WAIT_SWITCH_OFF:
+		PCIDBG(phb, pd->bdfn, "BUSES: PCI Device wait switch off\n");
+
+		if (parent->slot->power_state == PCI_SLOT_POWER_OFF) {
+			PHBDBG(p, "BUSES: No power, Doing next device\n");
+			slot->phb->current_pd = pci_device_iter_next(slot->phb, NULL);
+			pci_slot_set_state(slot, PHB4_SLOT_BUSES_PD_START);
+			return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
+		}
+
+		/* Turn it back on */
+		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 = PHB4_BUS_SWITCH_LINK_RETRIES;
+		pci_slot_set_state(slot, PHB4_SLOT_BUSES_PD_WAIT_SWITCH_LINK);
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
 	case PHB4_SLOT_BUSES_PD_WAIT_SWITCH_LINK:
 		PCIDBG(phb, pd->bdfn, "BUSES: Wait for switch link\n");
 
diff --git a/include/phb4.h b/include/phb4.h
index d30733a02ce5..7d9c7fff7308 100644
--- a/include/phb4.h
+++ b/include/phb4.h
@@ -118,9 +118,11 @@
 #define PHB4_SLOT_BUSES				PCI_SLOT_STATE_BUSES
 #define   PHB4_SLOT_BUSES_START			(PHB4_SLOT_BUSES + 1)
 #define   PHB4_SLOT_BUSES_PD_START		(PHB4_SLOT_BUSES + 2)
-#define   PHB4_SLOT_BUSES_PD_WAIT_SWITCH_LINK   (PHB4_SLOT_BUSES + 3)
-#define   PHB4_SLOT_BUSES_PD_WAIT_CRS		(PHB4_SLOT_BUSES + 4)
-#define   PHB4_SLOT_BUSES_PD_END		(PHB4_SLOT_BUSES + 5)
+#define   PHB4_SLOT_BUSES_PD_SWITCH_POWER	(PHB4_SLOT_BUSES + 3)
+#define   PHB4_SLOT_BUSES_PD_WAIT_SWITCH_OFF	(PHB4_SLOT_BUSES + 4)
+#define   PHB4_SLOT_BUSES_PD_WAIT_SWITCH_LINK   (PHB4_SLOT_BUSES + 5)
+#define   PHB4_SLOT_BUSES_PD_WAIT_CRS		(PHB4_SLOT_BUSES + 6)
+#define   PHB4_SLOT_BUSES_PD_END		(PHB4_SLOT_BUSES + 7)
 
 /*
  * PHB4 error descriptor. Errors from all components (PBCQ, PHB)
-- 
2.20.1



More information about the Skiboot mailing list