[Skiboot] [PATCH 3/4] hw/phb4: Factor out PERST control

Oliver O'Halloran oohall at gmail.com
Wed Mar 20 19:56:55 AEDT 2019


Some time ago Mikey added some code work around a bug we found where a
certain RAID card wouldn't come back again after a fast-reboot. The
workaround is setting the Link Disable bit before asserting PERST and
clear it after de-asserting PERST.

Currently we do this in the FRESET path, but not in the CRESET path.
This patch moves the PERST control into its own function to reduce
duplication and to the workaround is applied in all circumstances.

Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
---
I'd really like to know why this works at all currently... we added
the hack to make fast-reboot work with that stupid raid card. However,
fast-reboot does a CRESET so when PERST is asserted LD isn't. It's not
until we get to the FRESET handler (~100ms later) when LD is set so
the timing between asserting LD and PERST doesn't seem to matter?

I tried changing the FRESET handler to:

1) Assert PERST
2) Assert LD
3) Deassert PERST
4) De-assert LD

And the card still doesn't come back. Reversing the order of 1) and
2) seems to do the trick though. It's very strange. Either way, this
patch makes the code cleaner and I'm going to add an assert_perst()
slot operation callback soon anway.
---
 hw/phb4.c | 64 +++++++++++++++++++++++++++++++------------------------
 1 file changed, 36 insertions(+), 28 deletions(-)

diff --git a/hw/phb4.c b/hw/phb4.c
index 353f866a5dc1..770c11efb99c 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -2870,6 +2870,34 @@ static unsigned int phb4_get_max_link_speed(struct phb4 *p, struct dt_node *np)
 	return max_link_speed;
 }
 
+static void phb4_assert_perst(struct pci_slot *slot, bool assert)
+{
+	struct phb4 *p = phb_to_phb4(slot->phb);
+	uint16_t linkctl;
+	uint64_t reg;
+
+	/*
+	 * Disable the link before asserting PERST. The Cursed RAID card
+	 * in ozrom1 (9005:028c) has problems coming back if PERST is asserted
+	 * while link is active. To work around the problem we assert the link
+	 * disable bit before asserting PERST. Asserting the secondary reset
+	 * bit in the btctl register also works.
+	 */
+	phb4_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL, &linkctl);
+	reg = in_be64(p->regs + PHB_PCIE_CRESET);
+
+	if (assert) {
+		linkctl |= PCICAP_EXP_LCTL_LINK_DIS;
+		reg &= ~PHB_PCIE_CRESET_PERST_N;
+	} else {
+		linkctl &= ~PCICAP_EXP_LCTL_LINK_DIS;
+		reg |= PHB_PCIE_CRESET_PERST_N;
+	}
+
+	out_be64(p->regs + PHB_PCIE_CRESET, reg);
+	phb4_pcicfg_write16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL, linkctl);
+}
+
 static int64_t phb4_hreset(struct pci_slot *slot)
 {
 	struct phb4 *p = phb_to_phb4(slot->phb);
@@ -2932,7 +2960,6 @@ static int64_t phb4_freset(struct pci_slot *slot)
 {
 	struct phb4 *p = phb_to_phb4(slot->phb);
 	uint64_t reg;
-	uint16_t reg16;
 
 	switch(slot->state) {
 	case PHB4_SLOT_NORMAL:
@@ -2945,19 +2972,11 @@ static int64_t phb4_freset(struct pci_slot *slot)
 		PHBDBG(p, "FRESET: Prepare for link down\n");
 		phb4_prepare_link_change(slot, false);
 
-		phb4_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL,
-				   &reg16);
-		reg16 |= PCICAP_EXP_LCTL_LINK_DIS;
-		phb4_pcicfg_write16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL,
-				    reg16);
-
 		if (!p->skip_perst) {
 			PHBDBG(p, "FRESET: Assert\n");
-			reg = in_be64(p->regs + PHB_PCIE_CRESET);
-			reg &= ~PHB_PCIE_CRESET_PERST_N;
-			out_be64(p->regs + PHB_PCIE_CRESET, reg);
-			pci_slot_set_state(slot,
-				PHB4_SLOT_FRESET_ASSERT_DELAY);
+			phb4_assert_perst(slot, true);
+			pci_slot_set_state(slot, PHB4_SLOT_FRESET_ASSERT_DELAY);
+
 			/* 250ms assert time aligns with powernv */
 			return pci_slot_set_sm_timeout(slot, msecs_to_tb(250));
 		}
@@ -2979,21 +2998,12 @@ static int64_t phb4_freset(struct pci_slot *slot)
 		}
 
 		PHBDBG(p, "FRESET: Deassert\n");
-		reg = in_be64(p->regs + PHB_PCIE_CRESET);
-		reg |= PHB_PCIE_CRESET_PERST_N;
-		out_be64(p->regs + PHB_PCIE_CRESET, reg);
-		pci_slot_set_state(slot,
-			PHB4_SLOT_FRESET_DEASSERT_DELAY);
+		phb4_assert_perst(slot, false);
+		pci_slot_set_state(slot, PHB4_SLOT_FRESET_DEASSERT_DELAY);
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(1));
 
-		/* Move on to link poll right away */
-		return pci_slot_set_sm_timeout(slot, 1);
 	case PHB4_SLOT_FRESET_DEASSERT_DELAY:
 		PHBDBG(p, "FRESET: Starting training\n");
-		phb4_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL,
-				   &reg16);
-		reg16 &= ~(PCICAP_EXP_LCTL_LINK_DIS);
-		phb4_pcicfg_write16(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL,
-				    reg16);
 
 		phb4_training_trace(p);
 
@@ -3212,7 +3222,7 @@ static int64_t phb4_creset(struct pci_slot *slot)
 {
 	struct phb4 *p = phb_to_phb4(slot->phb);
 	struct capp *capp = p->capp;
-	uint64_t pbcq_status, reg;
+	uint64_t pbcq_status;
 	uint64_t creset_time, wait_time;
 
 	/* Don't even try fixing a broken PHB */
@@ -3252,9 +3262,7 @@ static int64_t phb4_creset(struct pci_slot *slot)
 		p->flags |= PHB4_CFG_USE_ASB | PHB4_AIB_FENCED;
 
 		/* Assert PREST before clearing errors */
-		reg = phb4_read_reg(p, PHB_PCIE_CRESET);
-		reg &= ~PHB_PCIE_CRESET_PERST_N;
-		phb4_write_reg(p, PHB_PCIE_CRESET, reg);
+		phb4_assert_perst(slot, true);
 
 		/* Clear errors, following the proper sequence */
 		phb4_err_clear(p);
-- 
2.20.1



More information about the Skiboot mailing list