[Skiboot] [PATCH v4 03/10] hw/p5ioc: PHB slot

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


The patch implement function p5ioc2_phb_slot_create() to create
P5IOC2 PHB slot, which is expected to be populated if necessary
by platform when calling to platform.pci_setup_phb(). The P5IOC2
PHB slot might be patched for a bit with platform specific info.

Signed-off-by: Gavin Shan <gwshan at linux.vnet.ibm.com>
---
 hw/p5ioc2-phb.c  | 367 ++++++++-----------------------------------------------
 include/p5ioc2.h |  10 +-
 2 files changed, 56 insertions(+), 321 deletions(-)

diff --git a/hw/p5ioc2-phb.c b/hw/p5ioc2-phb.c
index 0dcf0ce..3a098ec 100644
--- a/hw/p5ioc2-phb.c
+++ b/hw/p5ioc2-phb.c
@@ -20,8 +20,9 @@
 #include <io.h>
 #include <timebase.h>
 #include <affinity.h>
-#include <pci.h>
 #include <pci-cfg.h>
+#include <pci.h>
+#include <pci-slot.h>
 #include <interrupts.h>
 #include <ccan/str/str.h>
 
@@ -30,19 +31,6 @@
 #define PHBERR(p, fmt, a...)	prlog(PR_ERR, "PHB%d: " fmt, \
 				      (p)->phb.opal_id, ## a)
 
-/* Helper to set the state machine timeout */
-static inline uint64_t p5ioc2_set_sm_timeout(struct p5ioc2_phb *p, uint64_t dur)
-{
-	uint64_t target, now = mftb();
-
-	target = now + dur;
-	if (target == 0)
-		target++;
-	p->delay_tgt_tb = target;
-
-	return dur;
-}
-
 /*
  * Lock callbacks. Allows the OPAL API handlers to lock the
  * PHB around calls such as config space, EEH, etc...
@@ -205,10 +193,11 @@ static int64_t p5ioc2_pcicfg_write32(struct phb *phb, uint32_t bdfn,
 	return OPAL_SUCCESS;
 }
 
-static int64_t p5ioc2_presence_detect(struct phb *phb)
+static int64_t p5ioc2_get_presence_status(struct pci_slot *slot,
+					  uint8_t *val)
 {
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
-	uint16_t slotstat;
+	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(slot->phb);
+	uint16_t sts;
 	int64_t rc;
 
 	if (!p->is_pcie) {
@@ -217,315 +206,74 @@ static int64_t p5ioc2_presence_detect(struct phb *phb)
 		lsr = in_be32(p->regs + SHPC_LOGICAL_SLOT);
 		if (GETFIELD(SHPC_LOGICAL_SLOT_PRSNT, lsr)
 		    != SHPC_SLOT_STATE_EMPTY)
-			return OPAL_SHPC_DEV_PRESENT;
+			*val = 1;
 		else
-		return OPAL_SHPC_DEV_NOT_PRESENT;
+			*val = 0;
+		return OPAL_SUCCESS;
 	}
 
-	rc = p5ioc2_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_SLOTSTAT,
-				 &slotstat);
-	if (rc || !(slotstat & PCICAP_EXP_SLOTSTAT_PDETECTST))
-		return OPAL_SHPC_DEV_NOT_PRESENT;
-	return OPAL_SHPC_DEV_PRESENT;
+	rc = p5ioc2_pcicfg_read16(&p->phb, 0,
+				  p->ecap + PCICAP_EXP_SLOTSTAT, &sts);
+	if (rc == OPAL_SUCCESS &&
+	    (sts & PCICAP_EXP_SLOTSTAT_PDETECTST))
+		*val = 1;
+	else
+		*val = 0;
+	return OPAL_SUCCESS;
 }
 
-static int64_t p5ioc2_link_state(struct phb *phb)
+static int64_t p5ioc2_get_link_status(struct pci_slot *slot,
+				      uint8_t *val)
 {
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
+	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(slot->phb);
 	uint16_t lstat;
 	int64_t rc;
 
-	/* XXX Test for PHB in error state ? */
-	if (!p->is_pcie)
-		return OPAL_SHPC_LINK_UP_x1;
-
-	rc = p5ioc2_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;
-	return GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat);
-}
-
-static int64_t p5ioc2_power_state(struct phb *phb __unused)
-{
-	/* XXX FIXME */
-#if 0
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
-	uint64_t reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
-
-	/* XXX Test for PHB in error state ? */
-
-	if (reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)
-		return OPAL_SHPC_POWER_ON;
-
-	return OPAL_SHPC_POWER_OFF;
-#else
-	return OPAL_SHPC_POWER_ON;
-#endif
-}
-
-/* p5ioc2_sm_slot_power_off - Slot power off state machine
- */
-static int64_t p5ioc2_sm_slot_power_off(struct p5ioc2_phb *p)
-{
-	switch(p->state) {
-	default:
-		break;
+	if (!p->is_pcie) {
+		*val = 1;
+		return OPAL_SUCCESS;
 	}
 
-	/* Unknown state, hardware error ? */
-	return OPAL_HARDWARE;
-}
-
-static int64_t p5ioc2_slot_power_off(struct phb *phb)
-{
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
-
-	if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL)
-		return OPAL_BUSY;
-
-	/* run state machine */
-	return p5ioc2_sm_slot_power_off(p);
-}
-
-static int64_t p5ioc2_sm_slot_power_on(struct p5ioc2_phb *p __unused)
-{
-#if 0
-	uint64_t reg;
-	uint32_t reg32;
-	uint16_t brctl;
-
-	switch(p->state) {
-	case P5IOC2_PHB_STATE_FUNCTIONAL:
-		/* Check presence */
-		reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
-		if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) {
-			PHBDBG(p, "Slot power on: no device\n");
-			return OPAL_CLOSED;
-		}
-
-		/* Adjust UTL interrupt settings to disable various
-		 * errors that would interfere with the process
-		 */
-		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7e00000000000000);
-
-		/* If the power is not on, turn it on now */
-		if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) {
-			reg = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE);
-			reg &= ~(0x8c00000000000000ul);
-			reg |= 0x8400000000000000ul;
-			out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg);
-			p->state = PHB_STATE_SPUP_STABILIZE_DELAY;
-			PHBDBG(p, "Slot power on: powering on...\n");
-			return p5ioc2_set_sm_timeout(p, secs_to_tb(2));
-		}
-		/* Power is already on */
-	power_ok:
-		/* Ensure hot reset is deasserted */
-		p5ioc2_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
-		brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET;
-		p5ioc2_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
-		p->retries = 40;
-		p->state = PHB_STATE_SPUP_WAIT_LINK;
-		PHBDBG(p, "Slot power on: waiting for link\n");
-		/* Fall through */
-	case PHB_STATE_SPUP_WAIT_LINK:
-		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
-		/* Link is up ? Complete */
-
-		/* XXX TODO: Check link width problem and if present
-		 * go straight to the host reset code path.
-		 */
-		if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) {
-			/* Restore UTL interrupts */
-			out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
-				 0xfe65000000000000);
-			p->state = PHB_STATE_FUNCTIONAL;
-			PHBDBG(p, "Slot power on: up !\n");
-			return OPAL_SUCCESS;
-		}
-		/* Retries */
-		p->retries--;
-		if (p->retries == 0) {
-			/* XXX Improve logging */
-			PHBERR(p,"Slot power on: Timeout waiting for link\n");
-			goto error;
-		}
-		/* Check time elapsed */
-		if ((p->retries % 20) != 0)
-			return p5ioc2_set_sm_timeout(p, msecs_to_tb(10));
-
-		/* >200ms, time to try a hot reset after clearing the
-		 * link status bit (doco says to do so)
-		 */
-		out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x0080000000000000);
-
-		/* Mask receiver error status in AER */
-		p5ioc2_pcicfg_read32(&p->phb, 0,
-				    p->aercap + PCIECAP_AER_CE_MASK, &reg32);
-		reg32 |= PCIECAP_AER_CE_RECVR_ERR;
-		p5ioc2_pcicfg_write32(&p->phb, 0,
-				     p->aercap + PCIECAP_AER_CE_MASK, reg32);
-
-		/* Turn on host reset */
-		p5ioc2_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
-		brctl |= PCI_CFG_BRCTL_SECONDARY_RESET;
-		p5ioc2_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
-		p->state = PHB_STATE_SPUP_HOT_RESET_DELAY;
-		PHBDBG(p, "Slot power on: soft reset...\n");
-		return p5ioc2_set_sm_timeout(p, secs_to_tb(1));
-	case PHB_STATE_SPUP_HOT_RESET_DELAY:
-		/* Turn off host reset */
-		p5ioc2_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
-		brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET;
-		p5ioc2_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
-		/* Clear spurious errors */
-		out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x00e0000000000000);
-		p5ioc2_pcicfg_write32(&p->phb, 0,
-				     p->aercap + PCIECAP_AER_CE_STATUS,
-				     PCIECAP_AER_CE_RECVR_ERR);
-		/* Unmask receiver error status in AER */
-		p5ioc2_pcicfg_read32(&p->phb, 0,
-				    p->aercap + PCIECAP_AER_CE_MASK, &reg32);
-		reg32 &= ~PCIECAP_AER_CE_RECVR_ERR;
-		p5ioc2_pcicfg_write32(&p->phb, 0,
-				     p->aercap + PCIECAP_AER_CE_MASK, reg32);
-		/* Go back to waiting for link */
-		p->state = PHB_STATE_SPUP_WAIT_LINK;
-		PHBDBG(p, "Slot power on: waiting for link (2)\n");
-		return p5ioc2_set_sm_timeout(p, msecs_to_tb(10));
-
-	case PHB_STATE_SPUP_STABILIZE_DELAY:
-		/* Come here after the 2s delay after power up */
-		p->retries = 1000;
-		p->state = PHB_STATE_SPUP_SLOT_STATUS;
-		PHBDBG(p, "Slot power on: waiting for power\n");
-		/* Fall through */
-	case PHB_STATE_SPUP_SLOT_STATUS:
-		reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
-
-		/* Doc says to check LED status, but we ignore that, there
-		 * no point really and it's easier that way
-		 */
-		if (reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)
-			goto power_ok;
-		if (p->retries-- == 0) {
-			/* XXX Improve error logging */
-			PHBERR(p, "Timeout powering up slot\n");
-			goto error;
-		}
-		return p5ioc2_set_sm_timeout(p, msecs_to_tb(10));
-	default:
-		break;
-	}
+	rc = p5ioc2_pcicfg_read16(&p->phb, 0,
+				  p->ecap + PCICAP_EXP_LSTAT, &lstat);
+	if (rc == OPAL_SUCCESS &&
+	    (lstat & PCICAP_EXP_LSTAT_DLLL_ACT))
+		*val = GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat);
+	else
+		*val = 0;
 
-	/* Unknown state, hardware error ? */
- error:
-	p->state = PHB_STATE_FUNCTIONAL;
-	return OPAL_HARDWARE;
-#else
 	return OPAL_SUCCESS;
-#endif
-}
-
-static int64_t p5ioc2_slot_power_on(struct phb *phb)
-{
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
-
-	if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL)
-		return OPAL_BUSY;
-
-	/* run state machine */
-	return p5ioc2_sm_slot_power_on(p);
 }
 
-static int64_t p5ioc2_sm_hot_reset(struct p5ioc2_phb *p)
+struct pci_slot *p5ioc2_phb_slot_create(struct phb *phb)
 {
-	switch(p->state) {
-	default:
-		break;
-	}
+	struct pci_slot *slot;
 
-	/* Unknown state, hardware error ? */
-	return OPAL_HARDWARE;
-}
+	slot = pci_slot_alloc(phb, NULL);
+	if (!slot)
+		return NULL;
 
-static int64_t p5ioc2_hot_reset(struct phb *phb)
-{
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
+	/* Elementary functions */
+	slot->ops.get_presence_status	= p5ioc2_get_presence_status;
+	slot->ops.get_link_status	= p5ioc2_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;
 
-	if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL)
-		return OPAL_BUSY;
-
-	/* run state machine */
-	return p5ioc2_sm_hot_reset(p);
-}
-
-static int64_t p5ioc2_sm_freset(struct p5ioc2_phb *p)
-{
-	switch(p->state) {
-	default:
-		break;
-	}
-
-	/* XXX Not implemented, return success to make
-	 * pci.c happy, otherwise probing of slots will
-	 * fail
+	/*
+	 * SM-based functions. We don't support various reset
+	 * requests on P5IOC yet.
 	 */
-	return OPAL_SUCCESS;
-}
+	slot->ops.prepare_link_change	= NULL;
+	slot->ops.poll_link		= NULL;
+	slot->ops.hreset		= NULL;
+	slot->ops.freset		= NULL;
+	slot->ops.pfreset		= NULL;
+	slot->ops.creset		= NULL;
 
-static int64_t p5ioc2_freset(struct phb *phb)
-{
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
-
-	if (p->state != P5IOC2_PHB_STATE_FUNCTIONAL)
-		return OPAL_BUSY;
-
-	/* run state machine */
-	return p5ioc2_sm_freset(p);
-}
-
-static int64_t p5ioc2_poll(struct phb *phb)
-{
-	struct p5ioc2_phb *p = phb_to_p5ioc2_phb(phb);
-	uint64_t now = mftb();
-
-	if (p->state == P5IOC2_PHB_STATE_FUNCTIONAL)
-		return OPAL_SUCCESS;
-
-	/* 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;
-
-#if 0
-	/* Dispatch to the right state machine */
-	switch(p->state) {
-	case PHB_STATE_SPUP_STABILIZE_DELAY:
-	case PHB_STATE_SPUP_SLOT_STATUS:
-	case PHB_STATE_SPUP_WAIT_LINK:
-	case PHB_STATE_SPUP_HOT_RESET_DELAY:
-		return p5ioc2_sm_slot_power_on(p);
-	case PHB_STATE_SPDOWN_STABILIZE_DELAY:
-	case PHB_STATE_SPDOWN_SLOT_STATUS:
-		return p5ioc2_sm_slot_power_off(p);
-	case PHB_STATE_HRESET_DELAY:
-		return p5ioc2_sm_hot_reset(p);
-	default:
-		break;
-	}
-#endif
-	/* Unknown state, could be a HW error */
-	return OPAL_HARDWARE;
+	return slot;
 }
 
 static int64_t p5ioc2_eeh_freeze_status(struct phb *phb, uint64_t pe_number,
@@ -775,14 +523,6 @@ static const struct phb_ops p5ioc2_phb_ops = {
 	.get_msi_64		= p5ioc2_get_msi_64,
 	.ioda_reset		= p5ioc2_ioda_reset,
 	.set_phb_tce_memory	= p5ioc2_set_phb_tce_memory,
-	.presence_detect	= p5ioc2_presence_detect,
-	.link_state		= p5ioc2_link_state,
-	.power_state		= p5ioc2_power_state,
-	.slot_power_off		= p5ioc2_slot_power_off,
-	.slot_power_on		= p5ioc2_slot_power_on,
-	.hot_reset		= p5ioc2_hot_reset,
-	.fundamental_reset	= p5ioc2_freset,
-	.poll			= p5ioc2_poll,
 };
 
 /* p5ioc2_phb_get_xive - Interrupt control from OPAL */
@@ -1215,4 +955,3 @@ void p5ioc2_phb_setup(struct p5ioc2 *ioc, struct p5ioc2_phb *p,
 	if (platform.pci_setup_phb)
 		platform.pci_setup_phb(&p->phb, p->index);
 }
-
diff --git a/include/p5ioc2.h b/include/p5ioc2.h
index fb9ed1b..946e0d8 100644
--- a/include/p5ioc2.h
+++ b/include/p5ioc2.h
@@ -91,17 +91,15 @@ enum p5ioc2_phb_state {
 	P5IOC2_PHB_STATE_FUNCTIONAL,
 };
 
-/*
- * Structure for a PHB
- */
+/* Structure for a PHB */
 
 struct p5ioc2;
-
 struct p5ioc2_phb {
 	bool				active;	/* Is this PHB functional ? */
 	bool				is_pcie;
 	uint8_t				ca;	/* CA0 or CA1 */
 	uint8_t				index;	/* 0..3 index inside CA */
+	enum p5ioc2_phb_state		state;
 	void				*ca_regs;  /* Calgary regs */
 	void				*regs;	   /* PHB regs */
 	struct lock			lock;
@@ -110,9 +108,6 @@ struct p5ioc2_phb {
 	uint64_t			io_base;
 	int64_t				ecap;	/* cached PCI-E cap offset */
 	int64_t				aercap; /* cached AER ecap offset */
-	enum p5ioc2_phb_state		state;
-	uint64_t			delay_tgt_tb;
-	uint64_t			retries;
 	uint64_t			xive_cache[16];
 	struct p5ioc2			*ioc;
 	struct phb			phb;
@@ -126,6 +121,7 @@ static inline struct p5ioc2_phb *phb_to_p5ioc2_phb(struct phb *phb)
 extern void p5ioc2_phb_setup(struct p5ioc2 *ioc, struct p5ioc2_phb *p,
 			     uint8_t ca, uint8_t index, bool active,
 			     uint32_t buid);
+extern struct pci_slot *p5ioc2_phb_slot_create(struct phb *phb);
 
 /*
  * State structure for P5IOC2 IO HUB
-- 
1.8.3.2



More information about the Skiboot mailing list