[Skiboot] [PATCH v11 15/23] hw/phb3: Support PHB slot
Gavin Shan
gwshan at linux.vnet.ibm.com
Tue May 31 16:33:30 AEST 2016
On Tue, May 31, 2016 at 03:37:23PM +1000, Russell Currey wrote:
>On Fri, 2016-05-20 at 16:32 +1000, Gavin Shan wrote:
>> 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 at linux.vnet.ibm.com>
>
>Looks pretty good, and a bit simpler overall, which is nice.
>
>Reviewed-by: Russell Currey <ruscur at russell.cc>
>
>> ---
>> hw/phb3.c | 688 +++++++++++++++++++++++---------------------------------
>> -
>> include/phb3.h | 54 ++---
>> 2 files changed, 305 insertions(+), 437 deletions(-)
>>
>> diff --git a/hw/phb3.c b/hw/phb3.c
>> index 1baa7ad..84907d5 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
>
>These weren't actually fixed, right? If these features are no longer relevant
>then the comment can be removed, but it should be in a separate patch at least.
>
Apart from the I2C-based power management, the left aren't needed any more.
The I2C-based power management is implemtned in firenze platform after this
series of patches are applied. I agree it should be in separate patch and
will do in next revision.
>> - */
>>
>> #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>
>> @@ -70,19 +58,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)
>> {
>> @@ -594,38 +569,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)
>> {
>> @@ -2004,139 +1947,146 @@ 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 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
>> + */
>
>a few spelling/phrasing fixups
>
>"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
>kerne. 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;
>>
>> + /* Test for PHB in error state ? */
>
>I don't see how this comment is necessary, checking if "state" is "BROKEN" seems
>pretty clear.
>
Indeed, will drop it.
>> 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)
>> -{
>> - 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)
>> +static int64_t phb3_retry_state(struct pci_slot *slot)
>> {
>> - 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
>> @@ -2148,94 +2098,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
>> @@ -2244,118 +2188,78 @@ 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;
>
>I get that this is how it was before, and the value is hardcoded in a few
>places, but it'd be nice to pull this out into a constant somewhere, with a bit
>shift or something, big hardcoded numbers are gross. That can be done
>separately later, though.
>
Yeah, it's something to be done later, but I'm not sure it's worthy or not
as PHB4 is on the way, meaning PHB3 hits the maintainance cycle soon ;-)
>> + 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);
>>
>> + return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
>
>I'm guessing this 1s is still for CAPP reasons? If so, the above comment should
>probably be kept around.
>
Right. I will keep it.
>> + 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;
>> @@ -2512,26 +2416,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)
>> @@ -2560,114 +2453,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,
>> @@ -3605,7 +3476,6 @@ static const struct phb_ops phb3_ops = {
>> .choose_bus = phb3_choose_bus,
>> .get_info = phb3_get_info,
>> .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,
>> @@ -3620,14 +3490,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,
>> @@ -4417,6 +4279,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;
>> @@ -4482,6 +4345,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);
>> diff --git a/include/phb3.h b/include/phb3.h
>> index bdc7291..cf4b910 100644
>> --- a/include/phb3.h
>> +++ b/include/phb3.h
>> @@ -14,8 +14,6 @@
>> * limitations under the License.
>> */
>>
>> -/*
>> -*/
>
>So...this was just an empty comment block?
>
Yes, I think so.
>> #ifndef __PHB3_H
>> #define __PHB3_H
>>
>> @@ -206,27 +204,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_START,
>> - 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_NORMAL 0x00000000
>> +#define PHB3_SLOT_LINK 0x00000100
>> +#define PHB3_SLOT_LINK_START 0x00000101
>> +#define PHB3_SLOT_LINK_WAIT_ELECTRICAL 0x00000102
>> +#define PHB3_SLOT_LINK_WAIT 0x00000103
>> +#define PHB3_SLOT_HRESET 0x00000200
>> +#define PHB3_SLOT_HRESET_START 0x00000201
>> +#define PHB3_SLOT_HRESET_DELAY 0x00000202
>> +#define PHB3_SLOT_HRESET_DELAY2 0x00000203
>> +#define PHB3_SLOT_FRESET 0x00000300
>> +#define PHB3_SLOT_FRESET_START 0x00000301
>> +#define PHB3_SLOT_PFRESET 0x00000400
>> +#define PHB3_SLOT_PFRESET_START 0x00000401
>> +#define PHB3_SLOT_PFRESET_ASSERT_DELAY 0x00000402
>> +#define PHB3_SLOT_PFRESET_DEASSERT_DELAY 0x00000403
>> +#define PHB3_SLOT_CRESET 0x00000500
>> +#define PHB3_SLOT_CRESET_START 0x00000501
>> +#define PHB3_SLOT_CRESET_WAIT_CQ 0x00000502
>> +#define PHB3_SLOT_CRESET_REINIT 0x00000503
>> +#define PHB3_SLOT_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
>> @@ -256,13 +262,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
>> @@ -290,10 +296,6 @@ struct phb3 {
>>
>> bool skip_perst; /* Skip first perst */
>> bool has_link;
>> - enum phb3_state state;
>> - enum phb3_state retry_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;
>
>
More information about the Skiboot
mailing list