diff options
author | Gavin Shan <gwshan@linux.vnet.ibm.com> | 2016-06-10 15:03:45 +1000 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2016-06-14 16:00:16 +1000 |
commit | e1922cba179857c33bef034c1b382c33ab0f2e03 (patch) | |
tree | ad377da01a1da3cd1c702d9aec0cab942f4fb2ab /hw/phb3.c | |
parent | 9338d3b5735e4bf2d437268d48a836cc100d1fd2 (diff) | |
download | skiboot-e1922cba179857c33bef034c1b382c33ab0f2e03.zip skiboot-e1922cba179857c33bef034c1b382c33ab0f2e03.tar.gz skiboot-e1922cba179857c33bef034c1b382c33ab0f2e03.tar.bz2 |
hw/phb3: Support PHB slot
The patch refactors functions used for PHB slot management for
PHB3. Also, PHB slots are created before platform's PHB setup
hook (platform.pci_setup_phb()). That means the platforms can
override the properties or methods of the PHB slot if necessary.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Reviewed-by: Russell Currey <ruscur@russell.cc>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/phb3.c')
-rw-r--r-- | hw/phb3.c | 677 |
1 files changed, 278 insertions, 399 deletions
@@ -16,8 +16,9 @@ #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> @@ -56,19 +57,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) { @@ -570,38 +558,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); - uint64_t hp_override; - - /* Test for PHB in error state ? */ - if (p->state == PHB3_STATE_BROKEN) - return OPAL_HARDWARE; - - /* XXX Check bifurcation stuff ? */ - - /* Read hotplug override */ - hp_override = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE); - - PHBDBG(p, "hp_override: 0x%016llx\n", hp_override); - - /* - * On P8, the slot status isn't wired up properly, we have to - * use the hotplug override A/B bits. - */ - if ((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) { @@ -1980,139 +1936,145 @@ 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; - - /* XXX Test for PHB in error state ? */ - - /* Link is up, let's find the actual speed */ - if (!(reg & PHB_PCIE_DLP_TC_DL_LINKACT)) - return OPAL_SHPC_LINK_DOWN; - - 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; + struct phb3 *p = phb_to_phb3(slot->phb); + uint32_t reg32; - return GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat); -} + p->has_link = is_up; + if (!is_up) { + /* Mask PCIE port interrupts */ + out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, + 0xad42800000000000); -static int64_t phb3_power_state(struct phb __unused *phb) -{ - /* XXX Test for PHB in error state ? */ + /* Mask AER receiver error */ + phb3_pcicfg_read32(&p->phb, 0, + p->aercap + PCIECAP_AER_CE_MASK, ®32); + reg32 |= PCIECAP_AER_CE_RECVR_ERR; + phb3_pcicfg_write32(&p->phb, 0, + p->aercap + PCIECAP_AER_CE_MASK, reg32); - /* XXX TODO - External power control ? */ + /* 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, ®32); + 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 OPAL_SHPC_POWER_ON; + /* + * We might lose the bus numbers during the reset operation + * and we need to restore them. Otherwise, some adapters (e.g. + * IPR) can't be probed properly by the kernel. We don't need + * to restore bus numbers for every kind of reset, however, + * it's not harmful to always restore the bus numbers, which + * simplifies the logic. + */ + pci_restore_bridge_buses(slot->phb, slot->pd); + if (slot->phb->ops->device_init) + pci_walk_dev(slot->phb, slot->pd, + slot->phb->ops->device_init, NULL); + } } -static int64_t phb3_slot_power_off(struct phb *phb) +static int64_t phb3_get_presence_state(struct pci_slot *slot, uint8_t *val) { - struct phb3 *p = phb_to_phb3(phb); + struct phb3 *p = phb_to_phb3(slot->phb); + uint64_t hp_override; if (p->state == PHB3_STATE_BROKEN) return OPAL_HARDWARE; - if (p->state != PHB3_STATE_FUNCTIONAL) - return OPAL_BUSY; - /* XXX TODO - External power control ? */ + /* + * On P8, the slot status isn't wired up properly, we have + * to use the hotplug override A/B bits. + */ + hp_override = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE); + if ((hp_override & PHB_HPOVR_PRESENCE_A) && + (hp_override & PHB_HPOVR_PRESENCE_B)) + *val = OPAL_PCI_SLOT_EMPTY; + else + *val = OPAL_PCI_SLOT_PRESENT; return OPAL_SUCCESS; } -static int64_t phb3_slot_power_on(struct phb *phb) +static int64_t phb3_get_link_state(struct pci_slot *slot, uint8_t *val) { - struct phb3 *p = phb_to_phb3(phb); + struct phb3 *p = phb_to_phb3(slot->phb); + uint64_t reg; + uint16_t state; + int64_t rc; - if (p->state == PHB3_STATE_BROKEN) + /* 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; + } + + rc = phb3_pcicfg_read16(&p->phb, 0, + p->ecap + PCICAP_EXP_LSTAT, &state); + if (rc != OPAL_SUCCESS) { + PHBERR(p, "%s: Error %lld getting link state\n", __func__, rc); return OPAL_HARDWARE; - if (p->state != PHB3_STATE_FUNCTIONAL) - return OPAL_BUSY; + } - /* XXX TODO - External power control ? */ + if (state & PCICAP_EXP_LSTAT_DLLL_ACT) + *val = ((state & PCICAP_EXP_LSTAT_WIDTH) >> 4); + else + *val = 0; return OPAL_SUCCESS; } -static void phb3_setup_for_link_down(struct phb3 *p) +static int64_t phb3_retry_state(struct pci_slot *slot) { - 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, ®32); - 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, ®32); - reg32 &= ~PCIECAP_AER_CE_RECVR_ERR; - phb3_pcicfg_write32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_MASK, reg32); + struct phb3 *p = phb_to_phb3(slot->phb); - /* 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, 0xad52800000000000); - - /* Mark link up */ - p->has_link = true; - - /* Don't block PCI-CFG */ - p->flags &= ~PHB3_CFG_BLOCKED; - - /* - * 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); - } -} - -static int64_t phb3_retry_state(struct phb3 *p) -{ - if (p->retry_state <= PHB3_STATE_FUNCTIONAL) + if (slot->retry_state == PCI_SLOT_STATE_NORMAL) return OPAL_WRONG_STATE; - p->delay_tgt_tb = 0; - p->state = p->retry_state; - return p->phb.ops->poll(&p->phb); + PHBDBG(p, "Retry state %08x\n", slot->retry_state); + slot->delay_tgt_tb = 0; + pci_slot_set_state(slot, slot->retry_state); + slot->retry_state = PCI_SLOT_STATE_NORMAL; + return slot->ops.poll(slot); } -static int64_t phb3_sm_link_poll(struct phb3 *p) +static int64_t phb3_poll_link(struct pci_slot *slot) { - int64_t rc; + struct phb3 *p = phb_to_phb3(slot->phb); uint64_t reg; + int64_t rc; - /* 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_NORMAL: + case PHB3_SLOT_LINK_START: + PHBDBG(p, "LINK: Start polling\n"); + slot->retries = PHB3_LINK_ELECTRICAL_RETRIES; + pci_slot_set_state(slot, PHB3_SLOT_LINK_WAIT_ELECTRICAL); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(100)); + case PHB3_SLOT_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 @@ -2124,94 +2086,88 @@ 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); - rc = phb3_retry_state(p); + PHBDBG(p, "LINK: Electrical link detected\n"); + pci_slot_set_state(slot, PHB3_SLOT_LINK_WAIT); + slot->retries = PHB3_LINK_WAIT_RETRIES; + return pci_slot_set_sm_timeout(slot, msecs_to_tb(100)); + } + + if (slot->retries-- == 0) { + PHBDBG(p, "LINK: Timeout waiting for electrical link\n"); + PHBDBG(p, "LINK: DLP train control: 0x%016llx\n", reg); + rc = phb3_retry_state(slot); if (rc >= OPAL_SUCCESS) return rc; - /* No link, we still mark the PHB as functional */ - p->state = PHB3_STATE_FUNCTIONAL; + pci_slot_set_state(slot, PHB3_SLOT_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_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_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); - rc = phb3_retry_state(p); + + if (slot->retries-- == 0) { + PHBDBG(p, "LINK: Timeout waiting for link up\n"); + PHBDBG(p, "LINK: DLP train control: 0x%016llx\n", reg); + rc = phb3_retry_state(slot); if (rc >= OPAL_SUCCESS) return rc; - /* No link, we still mark the PHB as functional */ - p->state = PHB3_STATE_FUNCTIONAL; + pci_slot_set_state(slot, PHB3_SLOT_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 two seconds 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_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_NORMAL: + PHBDBG(p, "HRESET: Starts\n"); + if (slot->ops.get_presence_state) + slot->ops.get_presence_state(slot, &presence); + if (!presence) { + PHBDBG(p, "HRESET: No device\n"); + return OPAL_SUCCESS; } - /* Prepare for link going down */ - phb3_setup_for_link_down(p); + 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_HRESET_START: + PHBDBG(p, "HRESET: Assert\n"); - /* Turn on hot reset */ 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"); + pci_slot_set_state(slot, PHB3_SLOT_HRESET_DELAY); + + return pci_slot_set_sm_timeout(slot, secs_to_tb(1)); + case PHB3_SLOT_HRESET_DELAY: + PHBDBG(p, "HRESET: Deassert\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 */ 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 @@ -2220,118 +2176,79 @@ 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_HRESET_DELAY2); + return pci_slot_set_sm_timeout(slot, secs_to_tb(1)); + case PHB3_SLOT_HRESET_DELAY2: + pci_slot_set_state(slot, PHB3_SLOT_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_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_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) { - PHBDBG(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: - /* Prepare for link going down */ - phb3_setup_for_link_down(p); - /* Fall-through */ - case PHB3_STATE_FRESET_START: - if (p->state == PHB3_STATE_FRESET_START) { - PHBDBG(p, "Slot freset: Retrying\n"); - p->retry_state = 0; + /* Nothing to do without adapter connected */ + if (slot->ops.get_presence_state) + slot->ops.get_presence_state(slot, &presence); + if (!presence) { + PHBDBG(p, "PFRESET: No device\n"); + return OPAL_SUCCESS; } - /* 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"); - - /* 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"); + slot->retry_state = PHB3_SLOT_PFRESET_START; + if (slot->ops.prepare_link_change) + slot->ops.prepare_link_change(slot, false); + /* fall through */ + case PHB3_SLOT_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_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_PFRESET_ASSERT_DELAY); + p->skip_perst = false; + /* fall through */ + case PHB3_SLOT_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_PFRESET_DEASSERT_DELAY); + + /* CAPP FPGA requires 1s to flash before polling link */ + return pci_slot_set_sm_timeout(slot, secs_to_tb(1)); + case PHB3_SLOT_PFRESET_DEASSERT_DELAY: + pci_slot_set_state(slot, PHB3_SLOT_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_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; - } - - /* Allow to retry fundamental reset */ - p->retry_state = PHB3_STATE_FRESET_START; - p->flags |= PHB3_CFG_BLOCKED; - return phb3_sm_fundamental_reset(p); -} - struct lock capi_lock = LOCK_UNLOCKED; static struct { uint32_t ec_level; @@ -2488,26 +2405,15 @@ static void do_capp_recovery_scoms(struct phb3 *p) xscom_write(p->chip_id, CAPP_ERR_STATUS_CTRL + offset, reg); } -/* - * 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_NORMAL: + case PHB3_SLOT_CRESET_START: + PHBDBG(p, "CRESET: Starts\n"); /* do steps 3-5 of capp recovery procedure */ if (p->flags & PHB3_CAPP_RECOVERY) @@ -2536,114 +2442,92 @@ static int64_t phb3_sm_complete_reset(struct phb3 *p) 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_CRESET_WAIT_CQ); + slot->retries = 500; + return pci_slot_set_sm_timeout(slot, msecs_to_tb(10)); + case PHB3_SLOT_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)) { + 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_CRESET_REINIT); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(100)); } - if (p->retries-- == 0) { + if (slot->retries-- == 0) { PHBERR(p, "Timeout waiting for pending transaction\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_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, false); - 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_CRESET_FRESET); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(100)); + case PHB3_SLOT_CRESET_FRESET: + pci_slot_set_state(slot, PHB3_SLOT_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. + */ +static 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_START: - 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_state = phb3_get_presence_state; + slot->ops.get_link_state = phb3_get_link_state; + slot->ops.get_power_state = NULL; + slot->ops.get_attention_state = NULL; + slot->ops.get_latch_state = NULL; + slot->ops.set_power_state = NULL; + slot->ops.set_attention_state = 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, @@ -3634,7 +3518,6 @@ static const struct phb_ops phb3_ops = { .choose_bus = phb3_choose_bus, .get_reserved_pe_number = phb3_get_reserved_pe_number, .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, @@ -3649,14 +3532,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, @@ -4446,6 +4321,7 @@ static void phb3_create(struct dt_node *np) { const struct dt_property *prop; struct phb3 *p = zalloc(sizeof(struct phb3)); + struct pci_slot *slot; size_t lane_eq_len; struct dt_node *iplp; struct proc_chip *chip; @@ -4511,6 +4387,9 @@ static void phb3_create(struct dt_node *np) else opal_id = p->chip_id * 4 + p->index; pci_register_phb(&p->phb, opal_id); + slot = phb3_slot_create(&p->phb); + if (!slot) + PHBERR(p, "Cannot create PHB slot\n"); /* Hello ! */ path = dt_get_path(np); |