diff mbox series

[RFC,v2,3/3] core/pcie-slot: Restore slot power controller state

Message ID 20191029021106.29396-4-jniethe5@gmail.com
State New
Headers show
Series Changes to restoring buses after PCI reset | expand

Checks

Context Check Description
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (d75e82dbfbb9443efeb3f9a5921ac23605aab469)
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot success Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco success Signed-off-by present

Commit Message

Jordan Niethe Oct. 29, 2019, 2:11 a.m. UTC
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(-)
diff mbox series

Patch

diff --git a/core/pcie-slot.c b/core/pcie-slot.c
index 048a8b279c71..06e80ee3d689 100644
--- a/core/pcie-slot.c
+++ b/core/pcie-slot.c
@@ -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");
 
diff --git a/include/pci-slot.h b/include/pci-slot.h
index 353efc7e96e1..a95a387bf6aa 100644
--- a/include/pci-slot.h
+++ b/include/pci-slot.h
@@ -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