[Skiboot] [PATCH v5 08/15] hw/p5ioc: Support PHB slot
Gavin Shan
gwshan at linux.vnet.ibm.com
Mon Apr 27 16:26:26 AEST 2015
The patch implements 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 0848b99..4913db4 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...
@@ -206,10 +194,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) {
@@ -218,315 +207,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, ®32);
- 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, ®32);
- 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,
@@ -776,14 +524,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 */
@@ -1216,4 +956,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
--
2.1.0
More information about the Skiboot
mailing list