[Skiboot] [PATCH v4 05/10] hw/phb3: PHB slot

Gavin Shan gwshan at linux.vnet.ibm.com
Tue Feb 17 17:48:52 AEDT 2015


The patch implements function phb3_slot_create() to create PHB3
slot, which is expected to be populated by platform when calling
to platform.pci_setup_phb() if applicable.

Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
 hw/phb3.c      | 711 ++++++++++++++++++++++++---------------------------------
 include/phb3.h |  53 +++--
 2 files changed, 322 insertions(+), 442 deletions(-)

diff --git a/hw/phb3.c b/hw/phb3.c
index 3b781c5..b67d38f 100644
--- a/hw/phb3.c
+++ b/hw/phb3.c
@@ -13,25 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/*
- * PHB3 support
- *
- */
-
-/*
- *
- * FIXME:
- *   More stuff for EEH support:
- *      - PBCQ error reporting interrupt
- *	- I2C-based power management (replacing SHPC)
- *	- Directly detect fenced PHB through one dedicated HW reg
- */
 
 #include <skiboot.h>
 #include <io.h>
 #include <timebase.h>
-#include <pci.h>
 #include <pci-cfg.h>
+#include <pci.h>
+#include <pci-slot.h>
 #include <vpd.h>
 #include <interrupts.h>
 #include <opal.h>
@@ -87,19 +75,6 @@ static inline void phb3_ioda_sel(struct phb3 *p, uint32_t table,
 		 SETFIELD(PHB_IODA_AD_TADR, 0ul, addr));
 }
 
-/* Helper to set the state machine timeout */
-static inline uint64_t phb3_set_sm_timeout(struct phb3 *p, uint64_t dur)
-{
-	uint64_t target, now = mftb();
-
-	target = now + dur;
-	if (target == 0)
-		target++;
-	p->delay_tgt_tb = target;
-
-	return dur;
-}
-
 /* Check if AIB is fenced via PBCQ NFIR */
 static bool phb3_fenced(struct phb3 *p)
 {
@@ -465,54 +440,6 @@ static int64_t phb3_pci_reinit(struct phb *phb, uint64_t scope, uint64_t data)
 	return OPAL_SUCCESS;
 }
 
-static int64_t phb3_presence_detect(struct phb *phb)
-{
-	struct phb3 *p = phb_to_phb3(phb);
-	uint16_t slot_stat;
-	uint64_t hp_override;
-	int64_t rc;
-
-	/* Test for PHB in error state ? */
-	if (p->state == PHB3_STATE_BROKEN)
-		return OPAL_HARDWARE;
-
-	/* XXX Check bifurcation stuff ? */
-
-	/* Read slot status register */
-	rc = phb3_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_SLOTSTAT,
-					&slot_stat);
-	if (rc != OPAL_SUCCESS)
-		return OPAL_HARDWARE;
-
-	/* Read hotplug override */
-	hp_override = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE);
-
-	PHBDBG(p, "slot_stat: 0x%04x, hp_override: 0x%016llx\n",
-	       slot_stat, hp_override);
-
-	/* So if the slot status says nothing connected, we bail out */
-	if (!(slot_stat & PCICAP_EXP_SLOTSTAT_PDETECTST))
-		return OPAL_SHPC_DEV_NOT_PRESENT;
-
-	/*
-	 * At this point, we can have one of those funky IBM
-	 * systems that has the presence bit set in the slot
-	 * status and nothing actually connected. If so, we
-	 * check the hotplug override A/B bits
-	 */
-	if (p->use_ab_detect &&
-	    (hp_override & PHB_HPOVR_PRESENCE_A) &&
-	    (hp_override & PHB_HPOVR_PRESENCE_B))
-		return OPAL_SHPC_DEV_NOT_PRESENT;
-
-	/*
-	 * Anything else, we assume device present, the link state
-	 * machine will perform an early bail out if no electrical
-	 * signaling is established after a second.
-	 */
-	return OPAL_SHPC_DEV_PRESENT;
-}
-
 /* Clear IODA cache tables */
 static void phb3_init_ioda_cache(struct phb3 *p)
 {
@@ -1831,128 +1758,154 @@ static int64_t phb3_set_peltv(struct phb *phb,
 	return OPAL_SUCCESS;
 }
 
-static int64_t phb3_link_state(struct phb *phb)
+static void phb3_prepare_link_change(struct pci_slot *slot,
+				     bool is_up)
 {
-	struct phb3 *p = phb_to_phb3(phb);
-	uint64_t reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
-	uint16_t lstat;
-	int64_t rc;
+	struct phb3 *p = phb_to_phb3(slot->phb);
+	uint32_t reg32;
 
-	/* XXX Test for PHB in error state ? */
+	p->has_link = is_up;
+	if (!is_up) {
+		/* Mask PCIE port interrupts */
+		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
+			 0xad42800000000000);
 
-	/* Link is up, let's find the actual speed */
-	if (!(reg & PHB_PCIE_DLP_TC_DL_LINKACT))
-		return OPAL_SHPC_LINK_DOWN;
+		/* Mask AER receiver error */
+		phb3_pcicfg_read32(&p->phb, 0,
+				   p->aercap + PCIECAP_AER_CE_MASK, &reg32);
+		reg32 |= PCIECAP_AER_CE_RECVR_ERR;
+		phb3_pcicfg_write32(&p->phb, 0,
+				    p->aercap + PCIECAP_AER_CE_MASK, reg32);
 
-	rc = phb3_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LSTAT,
-				&lstat);
-	if (rc < 0) {
-		/* Shouldn't happen */
-		PHBERR(p, "Failed to read link status\n");
-		return OPAL_HARDWARE;
-	}
-	if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT))
-		return OPAL_SHPC_LINK_DOWN;
+		/* Block PCI-CFG access */
+		p->flags |= PHB3_CFG_BLOCKED;
+	} else {
+		/* Clear AER receiver error status */
+		phb3_pcicfg_write32(&p->phb, 0,
+				    p->aercap + PCIECAP_AER_CE_STATUS,
+				    PCIECAP_AER_CE_RECVR_ERR);
+
+		/* Unmask receiver error status in AER */
+		phb3_pcicfg_read32(&p->phb, 0,
+				   p->aercap + PCIECAP_AER_CE_MASK, &reg32);
+		reg32 &= ~PCIECAP_AER_CE_RECVR_ERR;
+		phb3_pcicfg_write32(&p->phb, 0,
+				    p->aercap + PCIECAP_AER_CE_MASK, reg32);
+
+		/* Clear spurrious errors and enable PCIE port interrupts */
+		out_be64(p->regs + UTL_PCIE_PORT_STATUS,
+			 0xffdfffffffffffff);
+		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
+			 0xad5a800000000000);
+
+		/* Don't block PCI-CFG */
+		p->flags &= ~PHB3_CFG_BLOCKED;
 
-	return GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat);
+		/*
+		 * We might lose the bus numbers in the reset and we need
+		 * restore the bus numbers. Otherwise, some adpaters (e.g.
+		 * IPR) can't be probed properly by kernel. We don't need
+		 * restore bus numbers for all kinds of resets. However,
+		 * it's not harmful to restore the bus numbers, which makes
+		 * the logic simplified
+		 */
+		pci_restore_bridge_buses(&p->phb, NULL);
+	}
 }
 
-static int64_t phb3_power_state(struct phb __unused *phb)
+static int64_t phb3_get_presence_status(struct pci_slot *slot,
+					uint8_t *val)
 {
-	/* XXX Test for PHB in error state ? */
-
-	/* XXX TODO - External power control ? */
-
-	return OPAL_SHPC_POWER_ON;
-}
+	struct phb3 *p = phb_to_phb3(slot->phb);
+	uint16_t slot_stat;
+	uint64_t hp_override;
+	int64_t ret;
 
-static int64_t phb3_slot_power_off(struct phb *phb)
-{
-	struct phb3 *p = phb_to_phb3(phb);
+	/* By default, there's nothing connected */
+	*val = 0;
 
+	/* Test for PHB in error state ? */
 	if (p->state == PHB3_STATE_BROKEN)
 		return OPAL_HARDWARE;
-	if (p->state != PHB3_STATE_FUNCTIONAL)
-		return OPAL_BUSY;
-
-	/* XXX TODO - External power control ? */
 
-	return OPAL_SUCCESS;
-}
-
-static int64_t phb3_slot_power_on(struct phb *phb)
-{
-	struct phb3 *p = phb_to_phb3(phb);
+	/* Read slot status register */
+	ret = phb3_pcicfg_read16(&p->phb, 0,
+				 p->ecap + PCICAP_EXP_SLOTSTAT, &slot_stat);
+	if (ret != OPAL_SUCCESS)
+		return ret;
+	if (!(slot_stat & PCICAP_EXP_SLOTSTAT_PDETECTST))
+		return OPAL_SUCCESS;
 
-	if (p->state == PHB3_STATE_BROKEN)
-		return OPAL_HARDWARE;
-	if (p->state != PHB3_STATE_FUNCTIONAL)
-		return OPAL_BUSY;
+	/*
+	 * At this point, we can have one of those funky IBM
+	 * systems that has the presence bit set in the slot
+	 * status and nothing actually connected. If so, we
+	 * check the hotplug override A/B bits
+	 */
+	hp_override = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE);
+	if (p->use_ab_detect &&
+	    (hp_override & PHB_HPOVR_PRESENCE_A) &&
+	    (hp_override & PHB_HPOVR_PRESENCE_B))
+		return OPAL_SUCCESS;
 
-	/* XXX TODO - External power control ? */
+	/*
+	 * Anything else, we assume device present, the link state
+	 * machine will perform an early bail out if no electrical
+	 * signaling is established after a second.
+	 */
+	*val = 1;
 
 	return OPAL_SUCCESS;
 }
 
-static void phb3_setup_for_link_down(struct phb3 *p)
+static int64_t phb3_get_link_status(struct pci_slot *slot,
+				    uint8_t *val)
 {
-	uint32_t reg32;
-
-	/* Mark link down */
-	p->has_link = false;
-
-	/* Mask PCIE port interrupts */
-	out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xad42800000000000);
-
-	/* Mask AER receiver error */
-	phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, &reg32);
-	reg32 |= PCIECAP_AER_CE_RECVR_ERR;
-	phb3_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32);
-}
-
-static void phb3_setup_for_link_up(struct phb3 *p)
-{
-	uint32_t reg32;
-	
-	/* Clear AER receiver error status */
-	phb3_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS,
-			    PCIECAP_AER_CE_RECVR_ERR);
-	/* Unmask receiver error status in AER */
-	phb3_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, &reg32);
-	reg32 &= ~PCIECAP_AER_CE_RECVR_ERR;
-	phb3_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32);
-
-	/* Clear spurrious errors and enable PCIE port interrupts */
-	out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xffdfffffffffffff);
-	out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xad5a800000000000);
-
-	/* Mark link down */
-	p->has_link = true;
+	struct phb3 *p = phb_to_phb3(slot->phb);
+	uint64_t reg;
+	uint16_t lstat;
+	int64_t rc;
 
-	/* Don't block PCI-CFG */
-	p->flags &= ~PHB3_CFG_BLOCKED;
+	/* Link is up, let's find the actual speed */
+	reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
+	if (!(reg & PHB_PCIE_DLP_TC_DL_LINKACT)) {
+		*val = 0;
+		return OPAL_SUCCESS;
+	}
 
-	/*
-	 * For complete reset, we might be required to restore
-	 * bus numbers for PCI bridges.
-	 */
-	if (p->flags & PHB3_RESTORE_BUS_NUM) {
-		p->flags &= ~PHB3_RESTORE_BUS_NUM;
-		pci_restore_bridge_buses(&p->phb, NULL);
+	rc = phb3_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LSTAT,
+				&lstat);
+	if (rc != OPAL_SUCCESS) {
+		*val = 0;
+		PHBERR(p, "%s: Error %lld getting link state\n",
+		       __func__, rc);
+		return OPAL_HARDWARE;
 	}
+	if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) {
+		*val = 0;
+		return OPAL_SUCCESS;
+        }
+
+	/* Grab the link width */
+	*val = GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat);
+	return OPAL_SUCCESS;
 }
 
-static int64_t phb3_sm_link_poll(struct phb3 *p)
+static int64_t phb3_poll_link(struct pci_slot *slot)
 {
+	struct phb3 *p = phb_to_phb3(slot->phb);
 	uint64_t reg;
 
-	/* This is the state machine to wait for the link to come
-	 * up. Currently we just wait until we timeout, eventually
-	 * we want to add retries and fallback to Gen1.
-	 */
-	switch(p->state) {
-	case PHB3_STATE_WAIT_LINK_ELECTRICAL:
-		/* Wait for the link electrical connection to be
+	switch (slot->state) {
+	case PHB3_SLOT_STATE_NORMAL:
+	case PHB3_SLOT_STATE_LINK_START:
+		PHBDBG(p, "LINK: Start polling\n");
+		slot->retries = PHB3_LINK_ELECTRICAL_RETRIES;
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_LINK_WAIT_ELECTRICAL);
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(100));
+	case PHB3_SLOT_STATE_LINK_WAIT_ELECTRICAL:
+		/*
+		 * Wait for the link electrical connection to be
 		 * established (shorter timeout). This allows us to
 		 * workaround spurrious presence detect on some machines
 		 * without waiting 10s each time
@@ -1964,86 +1917,79 @@ static int64_t phb3_sm_link_poll(struct phb3 *p)
 		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
 		if (reg & (PHB_PCIE_DLP_INBAND_PRESENCE |
 			   PHB_PCIE_DLP_TC_DL_LINKACT)) {
-			PHBDBG(p, "Electrical link detected...\n");
-			p->state = PHB3_STATE_WAIT_LINK;
-			p->retries = PHB3_LINK_WAIT_RETRIES;
-		} else if (p->retries-- == 0) {
-			PHBDBG(p, "Timeout waiting for electrical link\n");
-			PHBDBG(p, "DLP train control: 0x%016llx\n", reg);
+			PHBDBG(p, "LINK: Electrical link detected\n");
+			pci_slot_set_state(slot, PHB3_SLOT_STATE_LINK_WAIT);
+			slot->retries = PHB3_LINK_WAIT_RETRIES;
+			return pci_slot_set_sm_timeout(slot, msecs_to_tb(100));
+		}
+
+		if (slot->retries-- == 0) {
 			/* No link, we still mark the PHB as functional */
-			p->state = PHB3_STATE_FUNCTIONAL;
+			PHBDBG(p, "LINK: Timeout waiting for electrical link\n");
+			PHBDBG(p, "LINK: DLP train control: 0x%016llx\n", reg);
+			pci_slot_set_state(slot, PHB3_SLOT_STATE_NORMAL);
 			return OPAL_SUCCESS;
 		}
-		return phb3_set_sm_timeout(p, msecs_to_tb(100));
-	case PHB3_STATE_WAIT_LINK:
-		/* XXX I used the PHB_PCIE_LINK_MANAGEMENT register here but
-		 *     simics doesn't seem to give me anything, so I've switched
-		 *     to PCIE_DLP_TRAIN_CTL which appears more reliable
-		 */
+
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(100));
+	case PHB3_SLOT_STATE_LINK_WAIT:
 		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
 		if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) {
-			/* Setup PHB for link up */
-			phb3_setup_for_link_up(p);
-			PHBDBG(p, "Link is up!\n");
-			p->state = PHB3_STATE_FUNCTIONAL;
+			PHBDBG(p, "LINK: Link is up\n");
+			if (slot->ops.prepare_link_change)
+				slot->ops.prepare_link_change(slot, true);
+			pci_slot_set_state(slot, PHB3_SLOT_STATE_NORMAL);
 			return OPAL_SUCCESS;
 		}
-		if (p->retries-- == 0) {
-			PHBDBG(p, "Timeout waiting for link up\n");
-			PHBDBG(p, "DLP train control: 0x%016llx\n", reg);
-			/* No link, we still mark the PHB as functional */
-			p->state = PHB3_STATE_FUNCTIONAL;
+
+		if (slot->retries-- == 0) {
+			PHBDBG(p, "LINK: Timeout waiting for link up\n");
+			PHBDBG(p, "LINK: DLP train control: 0x%016llx\n", reg);
+			pci_slot_set_state(slot, PHB3_SLOT_STATE_NORMAL);
 			return OPAL_SUCCESS;
 		}
-		return phb3_set_sm_timeout(p, msecs_to_tb(100));
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(100));
 	default:
-		/* How did we get here ? */
-		assert(false);
+		PHBERR(p, "LINK: Unexpected slot state %08x\n",
+		       slot->state);
 	}
-	return OPAL_HARDWARE;
-}
 
-static int64_t phb3_start_link_poll(struct phb3 *p)
-{
-	/*
-	 * Wait for link up to 10s. However, we give up after
-	 * only a second if the electrical connection isn't
-	 * stablished according to the DLP link control register
-	 */
-	p->retries = PHB3_LINK_ELECTRICAL_RETRIES;
-	p->state = PHB3_STATE_WAIT_LINK_ELECTRICAL;
-	return phb3_set_sm_timeout(p, msecs_to_tb(100));
+	pci_slot_set_state(slot, PHB3_SLOT_STATE_NORMAL);
+	return OPAL_HARDWARE;
 }
 
-static int64_t phb3_sm_hot_reset(struct phb3 *p)
+static int64_t phb3_hreset(struct pci_slot *slot)
 {
+	struct phb3 *p = phb_to_phb3(slot->phb);
 	uint16_t brctl;
-
-	switch (p->state) {
-	case PHB3_STATE_FUNCTIONAL:
-		/* We need do nothing with available slot */
-		if (phb3_presence_detect(&p->phb) != OPAL_SHPC_DEV_PRESENT) {
-			PHBDBG(p, "Slot hreset: no device\n");
-			return OPAL_CLOSED;
+	uint8_t presence = 1;
+
+	switch (slot->state) {
+	case PHB3_SLOT_STATE_NORMAL:
+		PHBDBG(p, "HRESET: Starts\n");
+		if (slot->ops.get_presence_status)
+			slot->ops.get_presence_status(slot, &presence);
+		if (!presence) {
+			PHBDBG(p, "HRESET: No device\n");
+			return OPAL_SUCCESS;
 		}
 
-		/* Prepare for link going down */
-		phb3_setup_for_link_down(p);
-
-		/* Turn on hot reset */
+		PHBDBG(p, "HRESET: Prepare for link down\n");
+		if (slot->ops.prepare_link_change)
+			slot->ops.prepare_link_change(slot, false);
+		/* fall through */
+	case PHB3_SLOT_STATE_HRESET_START:
+		PHBDBG(p, "HRESET: Assert\n");
 		phb3_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
 		brctl |= PCI_CFG_BRCTL_SECONDARY_RESET;
 		phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
-		PHBDBG(p, "Slot hreset: assert reset\n");
-
-		p->state = PHB3_STATE_HRESET_DELAY;
-		return phb3_set_sm_timeout(p, secs_to_tb(1));
-	case PHB3_STATE_HRESET_DELAY:
-		/* Turn off hot reset */
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_HRESET_DELAY);
+		return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
+	case PHB3_SLOT_STATE_HRESET_DELAY:
+		PHBDBG(p, "HRESET: Deassert\n");
 		phb3_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
 		brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET;
 		phb3_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
-		PHBDBG(p, "Slot hreset: deassert reset\n");
 
 		/*
 		 * Due to some oddball adapters bouncing the link
@@ -2052,133 +1998,90 @@ static int64_t phb3_sm_hot_reset(struct phb3 *p)
 		 * we can get a spurrious link down interrupt which
 		 * causes us to EEH immediately.
 		 */
-		p->state = PHB3_STATE_HRESET_DELAY2;
-		return phb3_set_sm_timeout(p, secs_to_tb(1));
-	case PHB3_STATE_HRESET_DELAY2:
-		return phb3_start_link_poll(p);
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_HRESET_DELAY2);
+		return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
+	case PHB3_SLOT_STATE_HRESET_DELAY2:
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_LINK_START);
+		return slot->ops.poll_link(slot);
 	default:
-		PHBDBG(p, "Slot hreset: wrong state %d\n", p->state);
-		break;
+		PHBERR(p, "Unexpected slot state %08x\n", slot->state);
 	}
 
-	p->state = PHB3_STATE_FUNCTIONAL;
+	pci_slot_set_state(slot, PHB3_SLOT_STATE_NORMAL);
 	return OPAL_HARDWARE;
 }
 
-static int64_t phb3_hot_reset(struct phb *phb)
-{
-	struct phb3 *p = phb_to_phb3(phb);
-
-	if (p->state != PHB3_STATE_FUNCTIONAL) {
-		PHBDBG(p, "phb3_hot_reset: wrong state %d\n",
-		       p->state);
-		return OPAL_HARDWARE;
-	}
-
-	p->flags |= PHB3_CFG_BLOCKED;
-	return phb3_sm_hot_reset(p);
-}
-
-static int64_t phb3_sm_fundamental_reset(struct phb3 *p)
+static int64_t phb3_pfreset(struct pci_slot *slot)
 {
+	struct phb3 *p = phb_to_phb3(slot->phb);
+	uint8_t presence = 1;
 	uint64_t reg;
 
+	switch(slot->state) {
+	case PHB3_SLOT_STATE_NORMAL:
+		PHBDBG(p, "PFRESET: Starts\n");
 
-	/*
-	 * Check if there's something connected. We do that here
-	 * instead of the switch case below because we want to do
-	 * that before we test the skip_perst
-	 */
-	if (p->state == PHB3_STATE_FUNCTIONAL &&
-	    phb3_presence_detect(&p->phb) != OPAL_SHPC_DEV_PRESENT) {
-		PHBDBG(p, "Slot freset: no device\n");
-		return OPAL_CLOSED;
-	}
-
-	/* Handle boot time skipping of reset */
-	if (p->skip_perst && p->state == PHB3_STATE_FUNCTIONAL) {
-		PHBINF(p, "Cold boot, skipping PERST assertion\n");
-		p->state = PHB3_STATE_FRESET_ASSERT_DELAY;
-		/* PERST skipping happens only once */
-		p->skip_perst = false;
-	}
-
-	switch(p->state) {
-	case PHB3_STATE_FUNCTIONAL:
-		PHBINF(p, "Performing PERST...\n");
-
-		/* Prepare for link going down */
-		phb3_setup_for_link_down(p);
-
-		/* Assert PERST */
-		reg = in_be64(p->regs + PHB_RESET);
-		reg &= ~0x2000000000000000ul;
-		out_be64(p->regs + PHB_RESET, reg);
-		PHBDBG(p, "Slot freset: Asserting PERST\n");
+		/* Nothing to do without adapter connected */
+		if (slot->ops.get_presence_status)
+			slot->ops.get_presence_status(slot, &presence);
+		if (!presence) {
+			PHBDBG(p, "PFRESET: No device\n");
+			return OPAL_SUCCESS;
+		}
 
-		/* XXX Check delay for PERST... doing 1s for now */
-		p->state = PHB3_STATE_FRESET_ASSERT_DELAY;
-		return phb3_set_sm_timeout(p, secs_to_tb(1));
+		PHBDBG(p, "PFRESET: Prepare for link down\n");
+		if (slot->ops.prepare_link_change)
+			slot->ops.prepare_link_change(slot, false);
+		/* fall through */
+	case PHB3_SLOT_STATE_PFRESET_START:
+		if (!p->skip_perst) {
+			PHBDBG(p, "PFRESET: Assert\n");
+			reg = in_be64(p->regs + PHB_RESET);
+			reg &= ~0x2000000000000000ul;
+			out_be64(p->regs + PHB_RESET, reg);
+			pci_slot_set_state(slot,
+				PHB3_SLOT_STATE_PFRESET_ASSERT_DELAY);
+			return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
+		}
 
-	case PHB3_STATE_FRESET_ASSERT_DELAY:
-		/* Deassert PERST */
+		/* To skip the assert during boot time */
+		PHBDBG(p, "PFRESET: Assert skipped\n");
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_PFRESET_ASSERT_DELAY);
+		p->skip_perst = false;
+		/* fall through */
+	case PHB3_SLOT_STATE_PFRESET_ASSERT_DELAY:
+		PHBDBG(p, "PFRESET: Deassert\n");
 		reg = in_be64(p->regs + PHB_RESET);
 		reg |= 0x2000000000000000ul;
 		out_be64(p->regs + PHB_RESET, reg);
-		PHBDBG(p, "Slot freset: Deasserting PERST\n");
-
-		p->state = PHB3_STATE_FRESET_DEASSERT_DELAY;
-		/* CAPP fpga requires 1s to flash before polling link */
-		return phb3_set_sm_timeout(p, secs_to_tb(1));
-
-	case PHB3_STATE_FRESET_DEASSERT_DELAY:
-		/* Switch to generic link poll state machine */
-		return phb3_start_link_poll(p);
+		pci_slot_set_state(slot,
+			PHB3_SLOT_STATE_PFRESET_DEASSERT_DELAY);
 
+		return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
+	case PHB3_SLOT_STATE_PFRESET_DEASSERT_DELAY:
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_HRESET_START);
+		return slot->ops.hreset(slot);
 	default:
-		PHBDBG(p, "Slot freset: wrong state %d\n",
-		       p->state);
-		break;
+		PHBERR(p, "Unexpected slot state %08x\n",
+		       slot->state);
 	}
 
-	p->state = PHB3_STATE_FUNCTIONAL;
+	pci_slot_set_state(slot, PHB3_SLOT_STATE_NORMAL);
 	return OPAL_HARDWARE;
 }
 
-static int64_t phb3_fundamental_reset(struct phb *phb)
-{
-	struct phb3 *p = phb_to_phb3(phb);
-
-	if (p->state != PHB3_STATE_FUNCTIONAL) {
-		PHBDBG(p, "phb3_fundamental_reset: wrong state %d\n", p->state);
-		return OPAL_HARDWARE;
-	}
-
-	p->flags |= PHB3_CFG_BLOCKED;
-	return phb3_sm_fundamental_reset(p);
-}
-
 static void do_capp_recovery_scoms(struct phb3 *phb);
-/*
- * The OS is expected to do fundamental reset after complete
- * reset to make sure the PHB could be recovered from the
- * fenced state. However, the OS needn't do that explicitly
- * since fundamental reset will be done automatically while
- * powering on the PHB.
- *
- *
- * Usually, we need power off/on the PHB. That includes the
- * fundamental reset. However, we don't know how to control
- * the power stuff yet. So skip that and do fundamental reset
- * directly after reinitialization the hardware.
- */
-static int64_t phb3_sm_complete_reset(struct phb3 *p)
+
+static int64_t phb3_creset(struct pci_slot *slot)
 {
+	struct phb3 *p = phb_to_phb3(slot->phb);
 	uint64_t cqsts, val;
 
-	switch (p->state) {
-	case PHB3_STATE_FENCED:
-	case PHB3_STATE_FUNCTIONAL:
+	switch (slot->state) {
+	case PHB3_SLOT_STATE_NORMAL:
+	case PHB3_SLOT_STATE_CRESET_START:
+
+		PHBDBG(p, "CRESET: Starts\n");
 
 		/* do steps 3-5 of capp recovery procedure */
 		if (p->flags & PHB3_CAPP_RECOVERY)
@@ -2199,121 +2102,102 @@ static int64_t phb3_sm_complete_reset(struct phb3 *p)
 		 * have to force fenced PHB prior to ETU reset.
 		 */
 		if (!phb3_fenced(p))
-			xscom_write(p->chip_id, p->pe_xscom + 0x2, 0x000000f000000000ull);
+			xscom_write(p->chip_id,
+				    p->pe_xscom + 0x2, 0x000000f000000000ull);
 
 		/* Clear errors in NFIR and raise ETU reset */
 		xscom_read(p->chip_id, p->pe_xscom + 0x0, &p->nfir_cache);
 
-		xscom_read(p->chip_id, p->spci_xscom + 1, &val);/* HW275117 */
+		xscom_read(p->chip_id, p->spci_xscom + 1, &val); /* HW275117 */
 		xscom_write(p->chip_id, p->pci_xscom + 0xa,
 			    0x8000000000000000);
-		p->state = PHB3_STATE_CRESET_WAIT_CQ;
-		p->retries = 500;
-		return phb3_set_sm_timeout(p, msecs_to_tb(10));
-	case PHB3_STATE_CRESET_WAIT_CQ:
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_CRESET_WAIT_CQ);
+		slot->retries = 500;
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(10));
+	case PHB3_SLOT_STATE_CRESET_WAIT_CQ:
 		xscom_read(p->chip_id, p->pe_xscom + 0x1c, &val);
 		xscom_read(p->chip_id, p->pe_xscom + 0x1d, &val);
 		xscom_read(p->chip_id, p->pe_xscom + 0x1e, &val);
 		xscom_read(p->chip_id, p->pe_xscom + 0xf, &cqsts);
 		if (!(cqsts & 0xC000000000000000)) {
-			xscom_write(p->chip_id, p->pe_xscom + 0x1, ~p->nfir_cache);
+			PHBDBG(p, "CRESET: No pending transactions\n");
+			xscom_write(p->chip_id,
+				    p->pe_xscom + 0x1, ~p->nfir_cache);
 
-			p->state = PHB3_STATE_CRESET_REINIT;
-			return phb3_set_sm_timeout(p, msecs_to_tb(100));
+			pci_slot_set_state(slot, PHB3_SLOT_STATE_CRESET_REINIT);
+			return pci_slot_set_sm_timeout(slot, msecs_to_tb(100));
 		}
 
-		if (p->retries-- == 0) {
-			PHBERR(p, "Timeout waiting for pending transaction\n");
+		if (slot->retries-- == 0) {
+			PHBERR(p, "Timeout waiting for pending transactions\n");
 			goto error;
 		}
-		return phb3_set_sm_timeout(p, msecs_to_tb(10));
-	case PHB3_STATE_CRESET_REINIT:
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(10));
+	case PHB3_SLOT_STATE_CRESET_REINIT:
+		PHBDBG(p, "CRESET: Reinitialization\n");
+
+		/*
+		 * Clear AIB fenced state. Otherwise, we can't access the
+		 * PCI config space of root complex when reinitializing
+		 * the PHB.
+		 */
 		p->flags &= ~PHB3_AIB_FENCED;
 		p->flags &= ~PHB3_CAPP_RECOVERY;
 		phb3_init_hw(p);
 
-		p->state = PHB3_STATE_CRESET_FRESET;
-		return phb3_set_sm_timeout(p, msecs_to_tb(100));
-	case PHB3_STATE_CRESET_FRESET:
-		p->state = PHB3_STATE_FUNCTIONAL;
-		p->flags |= PHB3_CFG_BLOCKED;
-		return phb3_sm_fundamental_reset(p);
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_CRESET_FRESET);
+		return pci_slot_set_sm_timeout(slot, msecs_to_tb(100));
+	case PHB3_SLOT_STATE_CRESET_FRESET:
+		pci_slot_set_state(slot, PHB3_SLOT_STATE_NORMAL);
+		return slot->ops.freset(slot);
 	default:
-		assert(false);
+		PHBERR(p, "CRESET: Unexpected slot state %08x\n",
+		       slot->state);
 	}
 
 	/* Mark the PHB as dead and expect it to be removed */
 error:
 	p->state = PHB3_STATE_BROKEN;
-	return OPAL_PARAMETER;
-}
-
-static int64_t phb3_complete_reset(struct phb *phb, uint8_t assert)
-{
-	struct phb3 *p = phb_to_phb3(phb);
-
-	if ((assert == OPAL_ASSERT_RESET &&
-	    p->state != PHB3_STATE_FUNCTIONAL &&
-	    p->state != PHB3_STATE_FENCED) ||
-	    (assert == OPAL_DEASSERT_RESET &&
-	    p->state != PHB3_STATE_FUNCTIONAL)) {
-		PHBERR(p, "phb3_creset: wrong state %d\n",
-		       p->state);
-		return OPAL_HARDWARE;
-	}
-
-	/* Block PCI-CFG access */
-	p->flags |= PHB3_CFG_BLOCKED;
-
-	if (assert == OPAL_ASSERT_RESET) {
-		PHBINF(p, "Starting PHB reset sequence\n");
-		return phb3_sm_complete_reset(p);
-	} else {
-		/* Restore bus numbers for bridges */
-		p->flags |= PHB3_RESTORE_BUS_NUM;
-
-		return phb3_sm_hot_reset(p);
-	}
+	return OPAL_HARDWARE;
 }
 
-static int64_t phb3_poll(struct phb *phb)
+/*
+ * Initialize root complex slot, which is mainly used to
+ * do fundamental reset before PCI enumeration in PCI core.
+ * When probing root complex and building its real slot,
+ * the operations will be copied over.
+ */
+struct pci_slot *phb3_slot_create(struct phb *phb)
 {
-	struct phb3 *p = phb_to_phb3(phb);
-	uint64_t now = mftb();
+	struct pci_slot *slot;
 
-	if (p->state == PHB3_STATE_FUNCTIONAL)
-		return OPAL_SUCCESS;
+	slot = pci_slot_alloc(phb, NULL);
+	if (!slot)
+		return slot;
 
-	/* Check timer */
-	if (p->delay_tgt_tb &&
-	    tb_compare(now, p->delay_tgt_tb) == TB_ABEFOREB)
-		return p->delay_tgt_tb - now;
-
-	/* Expired (or not armed), clear it */
-	p->delay_tgt_tb = 0;
-
-	/* Dispatch to the right state machine */
-	switch(p->state) {
-	case PHB3_STATE_HRESET_DELAY:
-	case PHB3_STATE_HRESET_DELAY2:
-		return phb3_sm_hot_reset(p);
-	case PHB3_STATE_FRESET_ASSERT_DELAY:
-	case PHB3_STATE_FRESET_DEASSERT_DELAY:
-		return phb3_sm_fundamental_reset(p);
-	case PHB3_STATE_CRESET_WAIT_CQ:
-	case PHB3_STATE_CRESET_REINIT:
-	case PHB3_STATE_CRESET_FRESET:
-		return phb3_sm_complete_reset(p);
-	case PHB3_STATE_WAIT_LINK_ELECTRICAL:
-	case PHB3_STATE_WAIT_LINK:
-		return phb3_sm_link_poll(p);
-	default:
-		PHBDBG(p, "phb3_poll: wrong state %d\n", p->state);
-		break;
-	}
+	/* Elementary functions */
+	slot->ops.get_presence_status	= phb3_get_presence_status;
+	slot->ops.get_link_status	= phb3_get_link_status;
+	slot->ops.get_power_status	= NULL;
+	slot->ops.get_attention_status	= NULL;
+	slot->ops.get_latch_status	= NULL;
+	slot->ops.set_power_status	= NULL;
+	slot->ops.set_attention_status	= NULL;
 
-	/* Unknown state, could be a HW error */
-	return OPAL_HARDWARE;
+	/*
+	 * For PHB slots, we have to split the fundamental reset
+	 * into 2 steps. We might not have the first step which
+	 * is to power off/on the slot, or it's controlled by
+	 * individual platforms.
+	 */
+	slot->ops.prepare_link_change	= phb3_prepare_link_change;
+	slot->ops.poll_link		= phb3_poll_link;
+	slot->ops.hreset		= phb3_hreset;
+	slot->ops.freset		= phb3_pfreset;
+	slot->ops.pfreset		= phb3_pfreset;
+	slot->ops.creset		= phb3_creset;
+
+	return slot;
 }
 
 static int64_t phb3_eeh_freeze_status(struct phb *phb, uint64_t pe_number,
@@ -3299,7 +3183,6 @@ static const struct phb_ops phb3_ops = {
 	.cfg_write32		= phb3_pcicfg_write32,
 	.choose_bus		= phb3_choose_bus,
 	.device_init		= phb3_device_init,
-	.presence_detect	= phb3_presence_detect,
 	.ioda_reset		= phb3_ioda_reset,
 	.papr_errinjct_reset	= phb3_papr_errinjct_reset,
 	.pci_reinit		= phb3_pci_reinit,
@@ -3314,14 +3197,6 @@ static const struct phb_ops phb3_ops = {
 	.get_msi_64		= phb3_get_msi_64,
 	.set_pe			= phb3_set_pe,
 	.set_peltv		= phb3_set_peltv,
-	.link_state		= phb3_link_state,
-	.power_state		= phb3_power_state,
-	.slot_power_off		= phb3_slot_power_off,
-	.slot_power_on		= phb3_slot_power_on,
-	.hot_reset		= phb3_hot_reset,
-	.fundamental_reset	= phb3_fundamental_reset,
-	.complete_reset		= phb3_complete_reset,
-	.poll			= phb3_poll,
 	.eeh_freeze_status	= phb3_eeh_freeze_status,
 	.eeh_freeze_clear	= phb3_eeh_freeze_clear,
 	.eeh_freeze_set		= phb3_eeh_freeze_set,
diff --git a/include/phb3.h b/include/phb3.h
index c004056..6ef5ba2 100644
--- a/include/phb3.h
+++ b/include/phb3.h
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-/*
-*/
 #ifndef __PHB3_H
 #define __PHB3_H
 
@@ -208,26 +206,35 @@ enum phb3_state {
 
 	/* Normal PHB functional state */
 	PHB3_STATE_FUNCTIONAL,
-
-	/* Hot reset */
-	PHB3_STATE_HRESET_DELAY,
-	PHB3_STATE_HRESET_DELAY2,
-
-	/* Fundamental reset */
-	PHB3_STATE_FRESET_ASSERT_DELAY,
-	PHB3_STATE_FRESET_DEASSERT_DELAY,
-
-	/* Complete reset */
-	PHB3_STATE_CRESET_WAIT_CQ,
-	PHB3_STATE_CRESET_REINIT,
-	PHB3_STATE_CRESET_FRESET,
-
-	/* Link state machine */
-	PHB3_STATE_WAIT_LINK_ELECTRICAL,
-	PHB3_STATE_WAIT_LINK,
 };
 
 /*
+ * PHB3 PCI slot state. When you're going to apply any
+ * changes here, please make sure the base state isn't
+ * conflicting with those defined in pci-slot.h
+ */
+#define PHB3_SLOT_STATE_NORMAL				0x00000000
+#define PHB3_SLOT_STATE_LINK				0x00000100
+#define   PHB3_SLOT_STATE_LINK_START			0x00000101
+#define   PHB3_SLOT_STATE_LINK_WAIT_ELECTRICAL		0x00000102
+#define   PHB3_SLOT_STATE_LINK_WAIT			0x00000103
+#define PHB3_SLOT_STATE_HRESET				0x00000200
+#define   PHB3_SLOT_STATE_HRESET_START			0x00000201
+#define   PHB3_SLOT_STATE_HRESET_DELAY			0x00000202
+#define   PHB3_SLOT_STATE_HRESET_DELAY2			0x00000203
+#define PHB3_SLOT_STATE_FRESET				0x00000300
+#define   PHB3_SLOT_STATE_FRESET_START			0x00000301
+#define PHB3_SLOT_STATE_PFRESET				0x00000400
+#define   PHB3_SLOT_STATE_PFRESET_START			0x00000401
+#define   PHB3_SLOT_STATE_PFRESET_ASSERT_DELAY		0x00000402
+#define   PHB3_SLOT_STATE_PFRESET_DEASSERT_DELAY	0x00000403
+#define PHB3_SLOT_STATE_CRESET				0x00000500
+#define   PHB3_SLOT_STATE_CRESET_START			0x00000501
+#define          PHB3_SLOT_STATE_CRESET_WAIT_CQ		0x00000502
+#define   PHB3_SLOT_STATE_CRESET_REINIT			0x00000503
+#define   PHB3_SLOT_STATE_CRESET_FRESET			0x00000504
+
+/*
  * PHB3 error descriptor. Errors from all components (PBCQ, PHB)
  * will be cached to PHB3 instance. However, PBCQ errors would
  * have higher priority than those from PHB
@@ -257,13 +264,13 @@ struct phb3_err {
 #define PHB3_AIB_FENCED		0x00000001
 #define PHB3_CFG_USE_ASB	0x00000002
 #define PHB3_CFG_BLOCKED	0x00000004
-#define PHB3_RESTORE_BUS_NUM	0x00000008
-#define PHB3_CAPP_RECOVERY	0x00000010
+#define PHB3_CAPP_RECOVERY	0x00000008
 
 struct phb3 {
 	unsigned int		index;	    /* 0..2 index inside P8 */
 	unsigned int		flags;
 	unsigned int		chip_id;    /* Chip ID (== GCID on P8) */
+	enum phb3_state		state;
 	unsigned int		rev;        /* 00MMmmmm */
 #define PHB3_REV_MURANO_DD10	0xa30001
 #define PHB3_REV_VENICE_DD10	0xa30002
@@ -292,9 +299,6 @@ struct phb3 {
 	bool			skip_perst; /* Skip first perst */
 	bool			has_link;
 	bool			use_ab_detect;
-	enum phb3_state		state;
-	uint64_t		delay_tgt_tb;
-	uint64_t		retries;
 	int64_t			ecap;	    /* cached PCI-E cap offset */
 	int64_t			aercap;	    /* cached AER ecap offset */
 	const __be64		*lane_eq;
@@ -354,4 +358,5 @@ static inline void phb3_set_err_pending(struct phb3 *p, bool pending)
 	p->err_pending = pending;
 }
 
+extern struct pci_slot *phb3_slot_create(struct phb *phb);
 #endif /* __PHB3_H */
-- 
1.8.3.2



More information about the Skiboot mailing list