[Skiboot] [RFC PATCH 06/23] core/pci-slot: Add pci_slot_generic_freset()

Oliver O'Halloran oohall at gmail.com
Wed Apr 3 20:09:03 AEDT 2019


Add a generic function that can use the assert_perst() and
set_power_state() slot operations to do fundemental reset of the
slot.

Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
---
 core/pci-slot.c    | 72 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/phb4.c          |  5 ++--
 include/pci-slot.h |  6 ++++
 3 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/core/pci-slot.c b/core/pci-slot.c
index 497d0a47f426..14dd95ec0dd1 100644
--- a/core/pci-slot.c
+++ b/core/pci-slot.c
@@ -19,6 +19,7 @@
 #include <pci-cfg.h>
 #include <pci.h>
 #include <pci-slot.h>
+#include <stack.h>
 
 /* Debugging options */
 #define PCI_SLOT_PREFIX	"PCI-SLOT-%016llx "
@@ -114,6 +115,77 @@ static int64_t pci_slot_run_sm(struct pci_slot *slot)
 	return ret;
 }
 
+int64_t pci_slot_generic_freset(struct pci_slot *slot)
+{
+	uint16_t bdfn = slot->pd ? slot->pd->bdfn : 0;
+	struct phb *phb = slot->phb;
+	int64_t rc;
+
+	/* We need atleast one of them to do an FRESET */
+	assert(slot->ops.set_power_state || slot->ops.assert_perst);
+	// not strictly required...
+	assert(slot->ops.prepare_link_change);
+
+	switch(slot->state) {
+	case PCI_SLOT_STATE_NORMAL:
+	case PCI_SLOT_FRESET_START:
+		PCIERR(phb, bdfn, "FRESET: Prepare for link down\n");
+
+		// FIXME: Handle errors
+		if (slot->ops.prepare_link_change)
+			slot->ops.prepare_link_change(slot, false);
+		if (slot->ops.assert_perst)
+			slot->ops.assert_perst(slot, true);
+
+		/*
+		 * If we don't have power control then skip those states
+		 * and go straight to de-assert after 250ms.
+		 */
+		if (!slot->ops.set_power_state) {
+			pci_slot_set_state(slot, PCI_SLOT_FRESET_LIFT_PERST);
+			return pci_slot_set_sm_timeout(slot, msecs_to_tb(250));
+		}
+
+		pci_slot_set_state(slot, PCI_SLOT_FRESET_POWER_OFF);
+
+		/* fallthrough */
+	case PCI_SLOT_FRESET_POWER_OFF:
+		rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF);
+		if (rc > 0)
+			return pci_slot_set_sm_timeout(slot, msecs_to_tb(rc));
+		/* XXX: error handling */
+
+		/* leave power off for 250ms before turning the slot back on */
+		PCIERR(phb, bdfn, "FRESET: Slot powered off\n");
+		pci_slot_set_state(slot, PCI_SLOT_FRESET_POWER_ON);
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(250));
+
+	case PCI_SLOT_FRESET_POWER_ON:
+		rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_ON);
+		if (rc > 0)
+			return pci_slot_set_sm_timeout(slot, msecs_to_tb(rc));
+		/* XXX: error handling */
+		PCIERR(phb, bdfn, "FRESET: Slot powered on\n");
+
+		/* fallthrough */
+	case PCI_SLOT_FRESET_LIFT_PERST:
+		PCIDBG(phb, bdfn, "FRESET: Deassert\n");
+		if (slot->ops.assert_perst)
+			slot->ops.assert_perst(slot, false);
+
+		pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_START_POLL);
+		return slot->ops.poll_link(slot);
+	default:
+		PCIERR(phb, bdfn, "Unexpected slot state %08x\n", slot->state);
+	}
+
+	PCIERR(phb, bdfn, "Uhh how did we get here???\n");
+	backtrace();
+	pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+
+	return OPAL_HARDWARE;
+}
+
 void pci_slot_add_dt_properties(struct pci_slot *slot,
 				struct dt_node *np)
 {
diff --git a/hw/phb4.c b/hw/phb4.c
index bcd998d910af..591872c31c02 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -2939,6 +2939,9 @@ static void phb4_assert_perst(struct pci_slot *slot, bool assert)
 	} else {
 		linkctl &= ~PCICAP_EXP_LCTL_LINK_DIS;
 		reg |= PHB_PCIE_CRESET_PERST_N;
+
+		/* Clear link errors before we deassert PERST */
+		phb4_err_clear_regb(p);
 	}
 
 	out_be64(p->regs + PHB_PCIE_CRESET, reg);
@@ -3034,8 +3037,6 @@ static int64_t phb4_freset(struct pci_slot *slot)
 		p->skip_perst = false;
 		/* fall through */
 	case PHB4_SLOT_FRESET_ASSERT_DELAY:
-		/* Clear link errors before we deassert PERST */
-		phb4_err_clear_regb(p);
 
 		if (pci_tracing) {
 			/* Enable tracing */
diff --git a/include/pci-slot.h b/include/pci-slot.h
index cb36d7b6df5a..fec11ccf46c2 100644
--- a/include/pci-slot.h
+++ b/include/pci-slot.h
@@ -134,6 +134,10 @@ struct pci_slot_ops {
 #define   PCI_SLOT_STATE_HRESET_HOLD		(PCI_SLOT_STATE_HRESET + 2)
 #define PCI_SLOT_STATE_FRESET			0x00000300
 #define   PCI_SLOT_STATE_FRESET_POWER_OFF	(PCI_SLOT_STATE_FRESET + 1)
+#define   PCI_SLOT_FRESET_START			(PCI_SLOT_STATE_FRESET + 1)
+#define   PCI_SLOT_FRESET_POWER_OFF		(PCI_SLOT_STATE_FRESET + 2)
+#define   PCI_SLOT_FRESET_POWER_ON		(PCI_SLOT_STATE_FRESET + 3)
+#define   PCI_SLOT_FRESET_LIFT_PERST		(PCI_SLOT_STATE_FRESET + 4)
 #define PCI_SLOT_STATE_CRESET			0x00000400
 #define   PCI_SLOT_STATE_CRESET_START		(PCI_SLOT_STATE_CRESET + 1)
 #define PCI_SLOT_STATE_GPOWER			0x00000500
@@ -282,6 +286,8 @@ extern struct pci_slot *pci_slot_find(uint64_t id);
 extern void pci_slot_add_loc(struct pci_slot *slot,
 			struct dt_node *np, const char *label);
 
+int64_t pci_slot_generic_freset(struct pci_slot *slot);
+
 /* DT based slot map */
 
 extern struct dt_node *dt_slots;
-- 
2.20.1



More information about the Skiboot mailing list