[Skiboot] [PATCH v11 14/23] hw/p7ioc: Support PHB slot

Russell Currey ruscur at russell.cc
Tue May 31 14:49:19 AEST 2016


On Fri, 2016-05-20 at 16:32 +1000, Gavin Shan wrote:
> This refactors functions used for PHB slot management for P7IOC.
> 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>

This looks OK to me but I'm not really all that familiar with P7IOC.

> ---
>  hw/p7ioc-phb.c  | 1134 ++++++++++++++++++++++------------------------------
> ---
>  include/p7ioc.h |   48 ++-
>  2 files changed, 470 insertions(+), 712 deletions(-)
> 
> diff --git a/hw/p7ioc-phb.c b/hw/p7ioc-phb.c
> index f750ec8..cb09e29 100644
> --- a/hw/p7ioc-phb.c
> +++ b/hw/p7ioc-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 <opal.h>
>  #include <ccan/str/str.h>
> @@ -41,19 +42,6 @@ static inline void p7ioc_phb_ioda_sel(struct p7ioc_phb *p,
> uint32_t table,
>  		 SETFIELD(PHB_IODA_AD_TADR, 0ul, addr));
>  }
>  
> -/* Helper to set the state machine timeout */
> -static inline uint64_t p7ioc_set_sm_timeout(struct p7ioc_phb *p, uint64_t
> dur)
> -{
> -	uint64_t target, now = mftb();
> -
> -	target = now + dur;
> -	if (target == 0)
> -		target++;
> -	p->delay_tgt_tb = target;
> -
> -	return dur;
> -}
> -
>  static bool p7ioc_phb_fenced(struct p7ioc_phb *p)
>  {
>  	struct p7ioc *ioc = p->ioc;
> @@ -168,670 +156,6 @@ P7IOC_PCI_CFG_WRITE(8, uint8_t)
>  P7IOC_PCI_CFG_WRITE(16, uint16_t)
>  P7IOC_PCI_CFG_WRITE(32, uint32_t)
>  
> -static int64_t p7ioc_presence_detect(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
> -	uint64_t reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> -
> -	/* XXX Test for PHB in error state ? */
> -
> -	if (reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)
> -		return OPAL_SHPC_DEV_PRESENT;
> -
> -	return OPAL_SHPC_DEV_NOT_PRESENT;
> -}
> -
> -static int64_t p7ioc_link_state(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(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 = p7ioc_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 p7ioc_sm_freset(struct p7ioc_phb *p)
> -{
> -	uint64_t reg;
> -	uint32_t cfg32;
> -	uint64_t ci_idx = p->index + 2;
> -
> -	switch(p->state) {
> -	case P7IOC_PHB_STATE_FUNCTIONAL:
> -		/* If the slot isn't present, we needn't do it */
> -		reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> -		if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) {
> -			PHBDBG(p, "Slot freset: no device\n");
> -			return OPAL_CLOSED;
> -		}
> -
> -		/* Mask PCIE port interrupts and AER receiver error */
> -		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
> 0x7E00000000000000UL);
> -		p7ioc_pcicfg_read32(&p->phb, 0,
> -			p->aercap + PCIECAP_AER_CE_MASK, &cfg32);
> -		cfg32 |= PCIECAP_AER_CE_RECVR_ERR;
> -		p7ioc_pcicfg_write32(&p->phb, 0,
> -			p->aercap + PCIECAP_AER_CE_MASK, cfg32);
> -
> -		/* Mask CI port error and clear it */
> -		out_be64(p->ioc->regs + P7IOC_CIn_LEM_ERR_MASK(ci_idx),
> -			 0xa4f4000000000000ul);
> -		out_be64(p->regs + PHB_LEM_ERROR_MASK,
> -			 0xadb650c9808dd051ul);
> -		out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx),
> -			 0x0ul);
> -
> -		/* Disable link to avoid training issues */
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		reg |= PHB_PCIE_DLP_TCTX_DISABLE;
> -		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg);
> -		PHBDBG(p, "Slot freset: disable link training\n");
> -
> -		p->state = P7IOC_PHB_STATE_FRESET_DISABLE_LINK;
> -		p->retries = 12;
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	case P7IOC_PHB_STATE_FRESET_DISABLE_LINK:
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		if (reg & PHB_PCIE_DLP_TCRX_DISABLED) {
> -			/* Turn on freset */
> -			reg = in_be64(p->regs + PHB_RESET);
> -			reg &= ~0x2000000000000000ul;
> -			out_be64(p->regs + PHB_RESET, reg);
> -			PHBDBG(p, "Slot freset: assert\n");
> -
> -			p->state = P7IOC_PHB_STATE_FRESET_ASSERT_DELAY;
> -			return p7ioc_set_sm_timeout(p, secs_to_tb(1));
> -		}
> -
> -		if (p->retries-- == 0) {
> -			PHBDBG(p, "Slot freset: timeout to disable link
> training\n");
> -			goto error;
> -		}
> -
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	case P7IOC_PHB_STATE_FRESET_ASSERT_DELAY:
> -		/* Turn off freset */
> -		reg = in_be64(p->regs + PHB_RESET);
> -		reg |= 0x2000000000000000ul;
> -		out_be64(p->regs + PHB_RESET, reg);
> -		PHBDBG(p, "Slot freset: deassert\n");
> -
> -		p->state = P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY;
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(200));
> -	case P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY:
> -		/* Restore link control */
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		reg &= ~PHB_PCIE_DLP_TCTX_DISABLE;
> -		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg);
> -		PHBDBG(p, "Slot freset: enable link training\n");
> -
> -		p->state = P7IOC_PHB_STATE_FRESET_WAIT_LINK;
> -		p->retries = 100;
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	case P7IOC_PHB_STATE_FRESET_WAIT_LINK:
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) {
> -			/*
> -			 * Clear spurious errors and enable PCIE port
> -			 * interrupts
> -			 */
> -			out_be64(p->regs + UTL_PCIE_PORT_STATUS,
> -				 0x00E0000000000000UL);
> -                        out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
> -				 0xFE65000000000000UL);
> -
> -			/* Clear AER receiver error status */
> -			p7ioc_pcicfg_write32(&p->phb, 0,
> -				p->aercap + PCIECAP_AER_CE_STATUS,
> -				PCIECAP_AER_CE_RECVR_ERR);
> -			/* Unmask receiver error status in AER */
> -			p7ioc_pcicfg_read32(&p->phb, 0,
> -				p->aercap + PCIECAP_AER_CE_MASK, &cfg32);
> -			cfg32 &= ~PCIECAP_AER_CE_RECVR_ERR;
> -			p7ioc_pcicfg_write32(&p->phb, 0,
> -				p->aercap + PCIECAP_AER_CE_MASK, cfg32);
> -			/* Clear and Unmask CI port and PHB errors */
> -			out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx),
> -				 0x0ul);
> -			out_be64(p->regs + PHB_LEM_FIR_ACCUM,
> -				 0x0ul);
> -			out_be64(p->ioc->regs +
> P7IOC_CIn_LEM_ERR_MASK_AND(ci_idx),
> -				 0x0ul);
> -			out_be64(p->regs + PHB_LEM_ERROR_MASK,
> -				 0x1249a1147f500f2cul);
> -			PHBDBG(p, "Slot freset: link up!\n");
> -
> -			p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -			p->flags &= ~P7IOC_PHB_CFG_BLOCKED;
> -
> -			/*
> -			 * We might be required to restore bus numbers for
> PCI bridges
> -			 * for complete reset
> -			 */
> -			if (p->flags & P7IOC_RESTORE_BUS_NUM) {
> -				p->flags &= ~P7IOC_RESTORE_BUS_NUM;
> -				pci_restore_bridge_buses(&p->phb, NULL);
> -			}
> -
> -			return OPAL_SUCCESS;
> -		}
> -
> -		if (p->retries-- == 0) {
> -			uint16_t val;
> -
> -			if (p->gen == 1) {
> -				PHBDBG(p, "Slot freset: timeout for link up
> in Gen1 mode!\n");
> -				goto error;
> -			}
> -
> -			PHBDBG(p, "Slot freset: timeout for link up.\n");
> -			PHBDBG(p, "Slot freset: fallback to Gen1.\n");
> -			p->gen --;
> -
> -			/* Limit speed to 2.5G */
> -			p7ioc_pcicfg_read16(&p->phb, 0,
> -					p->ecap + PCICAP_EXP_LCTL2, &val);
> -			val = SETFIELD(PCICAP_EXP_LCTL2_TLSPD, val, 1);
> -			p7ioc_pcicfg_write16(&p->phb, 0,
> -					p->ecap + PCICAP_EXP_LCTL2,
> -					val);
> -
> -			/* Retrain */
> -			p7ioc_pcicfg_read16(&p->phb, 0,
> -					p->ecap + PCICAP_EXP_LCTL, &val);
> -			p7ioc_pcicfg_write16(&p->phb, 0,
> -					p->ecap + PCICAP_EXP_LCTL,
> -					val | PCICAP_EXP_LCTL_LINK_RETRAIN);
> -
> -			/* Enter FRESET_WAIT_LINK, again */
> -			p->state = P7IOC_PHB_STATE_FRESET_WAIT_LINK;
> -			p->retries = 100;
> -			return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -		}
> -
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	default:
> -		break;
> -	}
> -
> -error:
> -	p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -	return OPAL_HARDWARE;
> -}
> -
> -static int64_t p7ioc_freset(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
> -
> -	if (p->state != P7IOC_PHB_STATE_FUNCTIONAL)
> -		return OPAL_HARDWARE;
> -
> -	p->flags |= P7IOC_PHB_CFG_BLOCKED;
> -	return p7ioc_sm_freset(p);
> -}
> -
> -static int64_t p7ioc_power_state(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_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;
> -}
> -
> -static int64_t p7ioc_sm_slot_power_off(struct p7ioc_phb *p)
> -{
> -	uint64_t reg;
> -
> -	switch(p->state) {
> -	case P7IOC_PHB_STATE_FUNCTIONAL:
> -		/*
> -		 * Check the presence and power status. If be not
> -		 * be present or power down, we stop here.
> -		 */
> -		reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> -		if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) {
> -			PHBDBG(p, "Slot power off: no device\n");
> -			return OPAL_CLOSED;
> -		}
> -		reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> -		if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) {
> -			PHBDBG(p, "Slot power off: already off\n");
> -			p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -			return OPAL_SUCCESS;
> -		}
> -
> -		/*
> -		 * Mask PCIE port interrupt and turn power off
> -		 *
> -		 * We have to set bit 0 and clear it explicitly on PHB
> -		 * hotplug override register when doing power-off on the
> -		 * PHB slot. Otherwise, it won't take effect. That's the
> -		 * similar thing as we did for power-on.
> -		 */
> -		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
> 0x7e00000000000000UL);
> -		reg = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE);
> -		reg &= ~(0x8c00000000000000ul);
> -		reg |= 0x8400000000000000ul;
> -		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg);
> -		reg &= ~(0x8c00000000000000ul);
> -		reg |= 0x0c00000000000000ul;
> -		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg);
> -		PHBDBG(p, "Slot power off: powering off...\n");
> -
> -		p->state = P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY;
> -		return p7ioc_set_sm_timeout(p, secs_to_tb(2));
> -	case P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY:
> -		/*
> -		 * The link should be stabilized after 2 seconds.
> -		 * We still need poll registers to make sure the
> -		 * power is really down every 1ms until limited
> -		 * 1000 times.
> -		 */
> -		p->retries = 1000;
> -		p->state = P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS;
> -		PHBDBG(p, "Slot power off: waiting for power off\n");
> -	case P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS:
> -		reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> -		if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) {
> -			/*
> -			 * We completed the task. Clear link errors
> -			 * and restore PCIE port interrupts.
> -			 */
> -			out_be64(p->regs + UTL_PCIE_PORT_STATUS,
> -				0x00E0000000000000ul);
> -			out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
> -				0xFE65000000000000ul);
> -
> -			PHBDBG(p, "Slot power off: power off completely\n");
> -			p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -			return OPAL_SUCCESS;
> -		}
> -
> -		if (p->retries-- == 0) {
> -			PHBERR(p, "Timeout powering off\n");
> -			goto error;
> -		}
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(1));
> -	default:
> -		break;
> -	}
> -
> -error:
> -	p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -	return OPAL_HARDWARE;
> -}
> -
> -static int64_t p7ioc_slot_power_off(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
> -
> -	if (p->state != P7IOC_PHB_STATE_FUNCTIONAL)
> -		return OPAL_BUSY;
> -
> -	/* run state machine */
> -	return p7ioc_sm_slot_power_off(p);
> -}
> -
> -static int64_t p7ioc_sm_slot_power_on(struct p7ioc_phb *p)
> -{
> -	uint64_t reg;
> -	uint32_t reg32;
> -	uint64_t ci_idx = p->index + 2;
> -
> -	switch(p->state) {
> -	case P7IOC_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,
> 0x7e00000000000000UL);
> -
> -		/* If the power is not on, turn it on now */
> -		if (!(reg & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)) {
> -			/*
> -			 * The hotplug override register will not properly
> -			 * initiate the poweron sequence unless bit 0
> -			 * transitions from 0 to 1. Since it can already be
> -			 * set to 1 as a result of a previous power-on
> -			 * operation (even if the slot power is now off)
> -			 * we need to first clear it, then set it to 1 or
> -			 * nothing will happen
> -			 */
> -			reg = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE);
> -			reg &= ~(0x8c00000000000000ul);
> -			out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg);
> -			reg |= 0x8400000000000000ul;
> -			out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg);
> -			p->state = P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY;
> -			PHBDBG(p, "Slot power on: powering on...\n");
> -			return p7ioc_set_sm_timeout(p, secs_to_tb(2));
> -		}
> -		/* Power is already on */
> -	power_ok:
> -		/* Mask AER receiver error */
> -		p7ioc_pcicfg_read32(&p->phb, 0,
> -			p->aercap + PCIECAP_AER_CE_MASK, &reg32);
> -		reg32 |= PCIECAP_AER_CE_RECVR_ERR;
> -		p7ioc_pcicfg_write32(&p->phb, 0,
> -			p->aercap + PCIECAP_AER_CE_MASK, reg32);
> -
> -		/* Mask CI port error and clear it */
> -		out_be64(p->ioc->regs + P7IOC_CIn_LEM_ERR_MASK(ci_idx),
> -			 0xa4f4000000000000ul);
> -		out_be64(p->regs + PHB_LEM_ERROR_MASK,
> -			 0xadb650c9808dd051ul);
> -		out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx),
> -			 0x0ul);
> -
> -		/* Disable link to avoid training issues */
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		reg |= PHB_PCIE_DLP_TCTX_DISABLE;
> -		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg);
> -		PHBDBG(p, "Slot power on: disable link training\n");
> -
> -		/* Switch to state machine of fundamental reset */
> -                p->state = P7IOC_PHB_STATE_FRESET_DISABLE_LINK;
> -		p->retries = 12;
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	case P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY:
> -		/* Come here after the 2s delay after power up */
> -		p->retries = 1000;
> -		p->state = P7IOC_PHB_STATE_SPUP_SLOT_STATUS;
> -		PHBDBG(p, "Slot power on: waiting for power\n");
> -		/* Fall through */
> -	case P7IOC_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 p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	default:
> -		break;
> -	}
> -
> -	/* Unknown state, hardware error ? */
> - error:
> -	p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -	return OPAL_HARDWARE;
> -}
> -
> -static int64_t p7ioc_slot_power_on(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
> -
> -	if (p->state != P7IOC_PHB_STATE_FUNCTIONAL)
> -		return OPAL_BUSY;
> -
> -	/* run state machine */
> -	return p7ioc_sm_slot_power_on(p);
> -}
> -
> -/*
> - * 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.
> - */
> -static int64_t p7ioc_complete_reset(struct phb *phb, uint8_t assert)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
> -	struct p7ioc *ioc = p->ioc;
> -	uint64_t val64;
> -
> -	if (assert == OPAL_ASSERT_RESET) {
> -		if (p->state != P7IOC_PHB_STATE_FUNCTIONAL &&
> -		    p->state != P7IOC_PHB_STATE_FENCED)
> -			return OPAL_HARDWARE;
> -
> -		p->flags |= P7IOC_PHB_CFG_BLOCKED;
> -		p7ioc_phb_reset(phb);
> -
> -		/*
> -		 * According to the experiment, we probably still have
> -		 * the fenced state with the corresponding PHB in the Fence
> -		 * WOF and we need clear that explicitly. Besides, the RGC
> -		 * might already have informational error and we should clear
> -		 * that explicitly as well. Otherwise, RGC XIVE#0 won't issue
> -		 * interrupt any more.
> -		 */
> -		val64 = in_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF);
> -		val64 &= ~PPC_BIT(15 + p->index * 4);
> -		out_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF, val64);
> -
> -		/* Clear informational error from RGC */
> -		val64 = in_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> P7IOC_LEM_WOF_OFFSET);
> -		val64 &= ~PPC_BIT(18);
> -		out_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> P7IOC_LEM_WOF_OFFSET, val64);
> -		val64 = in_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> P7IOC_LEM_FIR_OFFSET);
> -		val64 &= ~PPC_BIT(18);
> -		out_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> P7IOC_LEM_FIR_OFFSET, val64);
> -
> -		return p7ioc_sm_slot_power_off(p);
> -	} else {
> -		if (p->state != P7IOC_PHB_STATE_FUNCTIONAL)
> -			return OPAL_HARDWARE;
> -
> -		/* Restore bus numbers for bridges */
> -		p->flags |= P7IOC_RESTORE_BUS_NUM;
> -
> -		return p7ioc_sm_slot_power_on(p);
> -	}
> -
> -	/* We shouldn't run to here */
> -	return OPAL_PARAMETER;
> -}
> -
> -/*
> - * We have to mask errors prior to disabling link training.
> - * Otherwise it would cause infinite frozen PEs. Also, we
> - * should have some delay after enabling link training. It's
> - * the conclusion from experiment and no document mentioned
> - * it.
> - */
> -static int64_t p7ioc_sm_hot_reset(struct p7ioc_phb *p)
> -{
> -	uint64_t reg;
> -	uint32_t cfg32;
> -	uint16_t brctl;
> -
> -	switch(p->state) {
> -	case P7IOC_PHB_STATE_FUNCTIONAL:
> -		/* If the slot isn't present, we needn't do it */
> -		reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> -		if (!(reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)) {
> -			PHBDBG(p, "Slot hot reset: no device\n");
> -			return OPAL_CLOSED;
> -		}
> -
> -		/* Mask PCIE port interrupts and AER receiver error */
> -		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
> 0x7E00000000000000UL);
> -		p7ioc_pcicfg_read32(&p->phb, 0,
> -			p->aercap + PCIECAP_AER_CE_MASK, &cfg32);
> -		cfg32 |= PCIECAP_AER_CE_RECVR_ERR;
> -		p7ioc_pcicfg_write32(&p->phb, 0,
> -			p->aercap + PCIECAP_AER_CE_MASK, cfg32);
> -
> -		/* Disable link to avoid training issues */
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		reg |= PHB_PCIE_DLP_TCTX_DISABLE;
> -		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg);
> -		PHBDBG(p, "Slot hot reset: disable link training\n");
> -
> -		p->state = P7IOC_PHB_STATE_HRESET_DISABLE_LINK;
> -		p->retries = 12;
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	case P7IOC_PHB_STATE_HRESET_DISABLE_LINK:
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		if (reg & PHB_PCIE_DLP_TCRX_DISABLED) {
> -			/* Turn on host reset */
> -			p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL,
> &brctl);
> -			brctl |= PCI_CFG_BRCTL_SECONDARY_RESET;
> -			p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL,
> brctl);
> -			PHBDBG(p, "Slot hot reset: assert reset\n");
> -
> -			p->state = P7IOC_PHB_STATE_HRESET_DELAY;
> -			return p7ioc_set_sm_timeout(p, secs_to_tb(1));
> -		}
> -
> -		if (p->retries-- == 0) {
> -			PHBDBG(p, "Slot hot reset: timeout to disable link
> training\n");
> -			return OPAL_HARDWARE;
> -		}
> -
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	case P7IOC_PHB_STATE_HRESET_DELAY:
> -		/* Turn off host reset */
> -		p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
> -		brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET;
> -		p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
> -		PHBDBG(p, "Slot hot reset: deassert reset\n");
> -
> -		p->state = P7IOC_PHB_STATE_HRESET_ENABLE_LINK;
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(200));
> -	case P7IOC_PHB_STATE_HRESET_ENABLE_LINK:
> -		/* Restore link control */
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -		reg &= ~PHB_PCIE_DLP_TCTX_DISABLE;
> -		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg);
> -		PHBDBG(p, "Slot hot reset: enable link training\n");
> -
> -		p->state = P7IOC_PHB_STATE_HRESET_WAIT_LINK;
> -		p->retries = 100;
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	case P7IOC_PHB_STATE_HRESET_WAIT_LINK:
> -		reg = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> -                if (reg & PHB_PCIE_DLP_TC_DL_LINKACT) {
> -			/*
> -			 * Clear spurious errors and enable PCIE port
> -			 * interrupts
> -			 */
> -			out_be64(p->regs + UTL_PCIE_PORT_STATUS,
> 0x00E0000000000000UL);
> -			out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
> 0xFE65000000000000UL);
> -
> -			/* Clear AER receiver error status */
> -			p7ioc_pcicfg_write32(&p->phb, 0,
> -				p->aercap + PCIECAP_AER_CE_STATUS,
> -				PCIECAP_AER_CE_RECVR_ERR);
> -			/* Unmask receiver error status in AER */
> -			p7ioc_pcicfg_read32(&p->phb, 0,
> -				p->aercap + PCIECAP_AER_CE_MASK, &cfg32);
> -			cfg32 &= ~PCIECAP_AER_CE_RECVR_ERR;
> -			p7ioc_pcicfg_write32(&p->phb, 0,
> -				p->aercap + PCIECAP_AER_CE_MASK, cfg32);
> -			PHBDBG(p, "Slot hot reset: link up!\n");
> -
> -			p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -			p->flags &= ~P7IOC_PHB_CFG_BLOCKED;
> -			return OPAL_SUCCESS;
> -		}
> -
> -		if (p->retries-- == 0) {
> -			PHBDBG(p, "Slot hot reset: timeout for link up\n");
> -			goto error;
> -		}
> -
> -		return p7ioc_set_sm_timeout(p, msecs_to_tb(10));
> -	default:
> -		break;
> -	}
> -
> -	/* Unknown state, hardware error ? */
> -error:
> -	p->state = P7IOC_PHB_STATE_FUNCTIONAL;
> -	return OPAL_HARDWARE;
> -}
> -
> -static int64_t p7ioc_hot_reset(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
> -
> -	if (p->state != P7IOC_PHB_STATE_FUNCTIONAL)
> -		return OPAL_HARDWARE;
> -
> -	p->flags |= P7IOC_PHB_CFG_BLOCKED;
> -	return p7ioc_sm_hot_reset(p);
> -}
> -
> -static int64_t p7ioc_poll(struct phb *phb)
> -{
> -	struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
> -	uint64_t now = mftb();
> -
> -	if (p->state == P7IOC_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;
> -
> -	/* Dispatch to the right state machine */
> -	switch(p->state) {
> -	case P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY:
> -	case P7IOC_PHB_STATE_SPUP_SLOT_STATUS:
> -		return p7ioc_sm_slot_power_on(p);
> -	case P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY:
> -	case P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS:
> -		return p7ioc_sm_slot_power_off(p);
> -	case P7IOC_PHB_STATE_FRESET_DISABLE_LINK:
> -	case P7IOC_PHB_STATE_FRESET_ASSERT_DELAY:
> -	case P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY:
> -	case P7IOC_PHB_STATE_FRESET_WAIT_LINK:
> -		return p7ioc_sm_freset(p);
> -	case P7IOC_PHB_STATE_HRESET_DISABLE_LINK:
> -	case P7IOC_PHB_STATE_HRESET_ASSERT:
> -	case P7IOC_PHB_STATE_HRESET_DELAY:
> -	case P7IOC_PHB_STATE_HRESET_ENABLE_LINK:
> -	case P7IOC_PHB_STATE_HRESET_WAIT_LINK:
> -		return p7ioc_sm_hot_reset(p);
> -	default:
> -		break;
> -	}
> -
> -	/* Unknown state, could be a HW error */
> -	return OPAL_HARDWARE;
> -}
> -
>  static void p7ioc_eeh_read_phb_status(struct p7ioc_phb *p,
>  				      struct OpalIoP7IOCPhbErrorData *stat)
>  {
> @@ -2572,6 +1896,446 @@ static int64_t p7ioc_papr_errinjct_reset(struct phb
> *phb)
>  	return OPAL_SUCCESS;
>  }
>  
> +static int64_t p7ioc_get_presence_state(struct pci_slot *slot, uint8_t *val)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint64_t reg;
> +
> +	reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> +	if (reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT)
> +		*val = OPAL_PCI_SLOT_PRESENT;
> +	else
> +		*val = OPAL_PCI_SLOT_EMPTY;
> +
> +	return OPAL_SUCCESS;
> +}
> +
> +static int64_t p7ioc_get_link_state(struct pci_slot *slot, uint8_t *val)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint64_t reg64;
> +	uint16_t state;
> +	int64_t rc;
> +
> +	/* Check if the link training is completed */
> +	reg64 = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> +	if (!(reg64 & PHB_PCIE_DLP_TC_DL_LINKACT)) {
> +		*val = 0;
> +		return OPAL_SUCCESS;
> +	}
> +
> +	/* Grab link width from PCIe capability */
> +	rc = p7ioc_pcicfg_read16(&p->phb, 0, p->ecap + PCICAP_EXP_LSTAT,
> +				 &state);
> +	if (rc < 0) {
> +		PHBERR(p, "%s: Error %lld reading link status\n",
> +		       __func__, rc);
> +		return OPAL_HARDWARE;
> +	}
> +
> +	if (state & PCICAP_EXP_LSTAT_DLLL_ACT)
> +		*val = ((state & PCICAP_EXP_LSTAT_WIDTH) >> 4);
> +	else
> +		*val = 0;
> +
> +	return OPAL_SUCCESS;
> +}
> +
> +static int64_t p7ioc_get_power_state(struct pci_slot *slot, uint8_t *val)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint64_t reg64;
> +
> +	reg64 = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> +	if (reg64 & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)
> +		*val = PCI_SLOT_POWER_ON;
> +	else
> +		*val = PCI_SLOT_POWER_OFF;
> +
> +	return OPAL_SUCCESS;
> +}
> +
> +static int64_t p7ioc_set_power_state(struct pci_slot *slot, uint8_t val)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint64_t reg64;
> +	uint8_t state = PCI_SLOT_POWER_OFF;
> +
> +	if (val != PCI_SLOT_POWER_OFF && val != PCI_SLOT_POWER_ON)
> +		return OPAL_PARAMETER;
> +
> +	/* If the power state has been put into the requested one */
> +	reg64 = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> +	if (reg64 & PHB_PCIE_SLOTCTL2_PWR_EN_STAT)
> +		state = PCI_SLOT_POWER_ON;
> +	if (state == val)
> +		return OPAL_SUCCESS;
> +
> +	/* Power on/off */
> +	if (val == PCI_SLOT_POWER_ON) {
> +		reg64 &= ~(0x8c00000000000000ul);
> +		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +		reg64 |= 0x8400000000000000ul;
> +		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +	} else {
> +		reg64 &= ~(0x8c00000000000000ul);
> +		reg64 |= 0x8400000000000000ul;
> +		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +		reg64 &= ~(0x8c00000000000000ul);
> +		reg64 |= 0x0c00000000000000ul;
> +		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +	}
> +
> +	return OPAL_SUCCESS;
> +}
> +
> +static void p7ioc_prepare_link_change(struct pci_slot *slot, bool up)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint64_t ci_idx = p->index + 2;
> +	uint32_t cfg32;
> +
> +	if (!up) {
> +		/* Mask PCIE port interrupts and AER receiver error */
> +		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7E00000000000000);
> +		p7ioc_pcicfg_read32(&p->phb, 0,
> +				    p->aercap + PCIECAP_AER_CE_MASK, &cfg32);
> +		cfg32 |= PCIECAP_AER_CE_RECVR_ERR;
> +		p7ioc_pcicfg_write32(&p->phb, 0,
> +				     p->aercap + PCIECAP_AER_CE_MASK, cfg32);
> +
> +		/* Mask CI port error and clear it */
> +		out_be64(p->ioc->regs + P7IOC_CIn_LEM_ERR_MASK(ci_idx),
> +			 0xa4f4000000000000ul);
> +		out_be64(p->regs + PHB_LEM_ERROR_MASK,
> +			 0xadb650c9808dd051ul);
> +		out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx),
> +			 0x0ul);
> +
> +		/* Block access to PCI-CFG space */
> +		p->flags |= P7IOC_PHB_CFG_BLOCKED;
> +	} else {
> +		/* Clear spurious errors and enable PCIE port interrupts */
> +		out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0x00E0000000000000);
> +		out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xFE65000000000000);
> +
> +		/* Clear AER receiver error status */
> +		p7ioc_pcicfg_write32(&p->phb, 0,
> +				     p->aercap + PCIECAP_AER_CE_STATUS,
> +				     PCIECAP_AER_CE_RECVR_ERR);
> +		/* Unmask receiver error status in AER */
> +		p7ioc_pcicfg_read32(&p->phb, 0,
> +				    p->aercap + PCIECAP_AER_CE_MASK, &cfg32);
> +		cfg32 &= ~PCIECAP_AER_CE_RECVR_ERR;
> +		p7ioc_pcicfg_write32(&p->phb, 0,
> +				     p->aercap + PCIECAP_AER_CE_MASK, cfg32);
> +		/* Clear and Unmask CI port and PHB errors */
> +		out_be64(p->ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx), 0x0ul);
> +		out_be64(p->regs + PHB_LEM_FIR_ACCUM, 0x0ul);
> +		out_be64(p->ioc->regs + P7IOC_CIn_LEM_ERR_MASK_AND(ci_idx),
> +			 0x0ul);
> +		out_be64(p->regs + PHB_LEM_ERROR_MASK, 0x1249a1147f500f2cul);
> +
> +		/* Don't block access to PCI-CFG space */
> +		p->flags &= ~P7IOC_PHB_CFG_BLOCKED;
> +
> +		/* Restore slot's state */
> +		pci_slot_set_state(slot, P7IOC_SLOT_NORMAL);
> +
> +		/*
> +		 * 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(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 p7ioc_poll_link(struct pci_slot *slot)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint64_t reg64;
> +
> +	switch (slot->state) {
> +	case P7IOC_SLOT_NORMAL:
> +	case P7IOC_SLOT_LINK_START:
> +		PHBDBG(p, "LINK: Start polling\n");
> +		reg64 = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> +		reg64 &= ~PHB_PCIE_DLP_TCTX_DISABLE;
> +		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg64);
> +		slot->retries = 100;
> +		pci_slot_set_state(slot, P7IOC_SLOT_LINK_WAIT);
> +		return pci_slot_set_sm_timeout(slot, msecs_to_tb(10));
> +	case P7IOC_SLOT_LINK_WAIT:
> +		reg64 = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> +		if (reg64 & PHB_PCIE_DLP_TC_DL_LINKACT) {
> +			PHBDBG(p, "LINK: Up\n");
> +			slot->ops.prepare_link_change(slot, true);
> +			return OPAL_SUCCESS;
> +		}
> +
> +		if (slot->retries-- == 0) {
> +			PHBERR(p, "LINK: Timeout waiting for link up\n");
> +			goto out;
> +		}
> +		return pci_slot_set_sm_timeout(slot, msecs_to_tb(10));
> +	default:
> +		PHBERR(p, "LINK: Unexpected slot state %08x\n",
> +		       slot->state);
> +	}
> +
> +out:
> +	pci_slot_set_state(slot, P7IOC_SLOT_NORMAL);
> +	return OPAL_HARDWARE;
> +}
> +
> +static int64_t p7ioc_hreset(struct pci_slot *slot)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint8_t presence = 1;
> +	uint16_t brctl;
> +	uint64_t reg64;
> +
> +	switch (slot->state) {
> +	case P7IOC_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;
> +		}
> +
> +		PHBDBG(p, "HRESET: Prepare for link down\n");
> +		slot->ops.prepare_link_change(slot, false);
> +
> +		/* Disable link to avoid training issues */
> +		PHBDBG(p, "HRESET: Disable link training\n");
> +		reg64 = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> +		reg64 |= PHB_PCIE_DLP_TCTX_DISABLE;
> +		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg64);
> +		pci_slot_set_state(slot, P7IOC_SLOT_HRESET_TRAINING);
> +		slot->retries = 15;
> +		/* fall through */
> +	case P7IOC_SLOT_HRESET_TRAINING:
> +		reg64 = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> +		if (!(reg64 & PHB_PCIE_DLP_TCRX_DISABLED)) {
> +			if (slot->retries -- == 0) {
> +				PHBERR(p, "HRESET: Timeout disabling link
> training\n");
> +				goto out;
> +			}
> +
> +			return pci_slot_set_sm_timeout(slot,
> msecs_to_tb(10));
> +		}
> +		/* fall through */
> +	case P7IOC_SLOT_HRESET_START:
> +		PHBDBG(p, "HRESET: Assert\n");
> +		p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
> +		brctl |= PCI_CFG_BRCTL_SECONDARY_RESET;
> +		p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
> +
> +		pci_slot_set_state(slot, P7IOC_SLOT_HRESET_DELAY);
> +		return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
> +	case P7IOC_SLOT_HRESET_DELAY:
> +		PHBDBG(p, "HRESET: Deassert\n");
> +		p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &brctl);
> +		brctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET;
> +		p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, brctl);
> +		pci_slot_set_state(slot, P7IOC_SLOT_HRESET_DELAY2);
> +		return pci_slot_set_sm_timeout(slot, msecs_to_tb(200));
> +	case P7IOC_SLOT_HRESET_DELAY2:
> +		pci_slot_set_state(slot, P7IOC_SLOT_LINK_START);
> +		return slot->ops.poll_link(slot);
> +	default:
> +		PHBERR(p, "HRESET: Unexpected slot state %08x\n",
> +		       slot->state);
> +	}
> +
> +out:
> +	pci_slot_set_state(slot, P7IOC_SLOT_NORMAL);
> +	return OPAL_HARDWARE;
> +}
> +
> +static int64_t p7ioc_freset(struct pci_slot *slot)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	uint8_t presence = 1;
> +	uint64_t reg64;
> +
> +	switch (slot->state) {
> +	case P7IOC_SLOT_NORMAL:
> +	case P7IOC_SLOT_FRESET_START:
> +		PHBDBG(p, "FRESET: Starts\n");
> +		if (slot->ops.get_presence_state)
> +			slot->ops.get_presence_state(slot, &presence);
> +		if (!presence) {
> +			PHBDBG(p, "FRESET: No device\n");
> +			pci_slot_set_state(slot, P7IOC_SLOT_NORMAL);
> +			return OPAL_SUCCESS;
> +		}
> +
> +		PHBDBG(p, "FRESET: Prepare for link down\n");
> +		slot->ops.prepare_link_change(slot, false);
> +
> +		/* Check power state */
> +		reg64 = in_be64(p->regs + PHB_PCIE_SLOTCTL2);
> +		if (reg64 & PHB_PCIE_SLOTCTL2_PWR_EN_STAT) {
> +			PHBDBG(p, "FRESET: Power on, turn off\n");
> +			reg64 = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE);
> +			reg64 &= ~(0x8c00000000000000ul);
> +			reg64 |= 0x8400000000000000ul;
> +			out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +			reg64 &= ~(0x8c00000000000000ul);
> +			reg64 |= 0x0c00000000000000ul;
> +			out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +			pci_slot_set_state(slot,
> P7IOC_SLOT_FRESET_POWER_OFF);
> +			return pci_slot_set_sm_timeout(slot, secs_to_tb(2));
> +		}
> +		/* fall through */
> +	case P7IOC_SLOT_FRESET_POWER_OFF:
> +		PHBDBG(p, "FRESET: Power off, turn on\n");
> +		reg64 = in_be64(p->regs + PHB_HOTPLUG_OVERRIDE);
> +		reg64 &= ~(0x8c00000000000000ul);
> +		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +		reg64 |= 0x8400000000000000ul;
> +		out_be64(p->regs + PHB_HOTPLUG_OVERRIDE, reg64);
> +		pci_slot_set_state(slot, P7IOC_SLOT_FRESET_POWER_ON);
> +		return pci_slot_set_sm_timeout(slot, secs_to_tb(2));
> +	case P7IOC_SLOT_FRESET_POWER_ON:
> +		PHBDBG(p, "FRESET: Disable link training\n");
> +		reg64 = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> +		reg64 |= PHB_PCIE_DLP_TCTX_DISABLE;
> +		out_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL, reg64);
> +		pci_slot_set_state(slot, P7IOC_SLOT_HRESET_TRAINING);
> +		slot->retries = 200;
> +		/* fall through */
> +	case P7IOC_SLOT_HRESET_TRAINING:
> +		reg64 = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL);
> +		if (!(reg64 & PHB_PCIE_DLP_TCRX_DISABLED)) {
> +			if (slot->retries -- == 0) {
> +				PHBERR(p, "HRESET: Timeout disabling link
> training\n");
> +				goto out;
> +			}
> +
> +			return pci_slot_set_sm_timeout(slot,
> msecs_to_tb(10));
> +		}
> +
> +		PHBDBG(p, "FRESET: Assert\n");
> +		reg64 = in_be64(p->regs + PHB_RESET);
> +		reg64 &= ~0x2000000000000000ul;
> +		out_be64(p->regs + PHB_RESET, reg64);
> +		pci_slot_set_state(slot, P7IOC_SLOT_FRESET_ASSERT);
> +		return pci_slot_set_sm_timeout(slot, secs_to_tb(1));
> +	case P7IOC_SLOT_FRESET_ASSERT:
> +		PHBDBG(p, "FRESET: Deassert\n");
> +		reg64 = in_be64(p->regs + PHB_RESET);
> +		reg64 |= 0x2000000000000000ul;
> +		out_be64(p->regs + PHB_RESET, reg64);
> +		if (slot->ops.pfreset) {
> +			pci_slot_set_state(slot,
> +					   P7IOC_SLOT_PFRESET_START);
> +			return slot->ops.pfreset(slot);
> +		}
> +
> +		pci_slot_set_state(slot, P7IOC_SLOT_HRESET_START);
> +		return slot->ops.hreset(slot);
> +	default:
> +		PHBERR(p, "FRESET: Unexpected slot state %08x\n",
> +		       slot->state);
> +	}
> +
> +out:
> +	pci_slot_set_state(slot, P7IOC_SLOT_NORMAL);
> +	return OPAL_HARDWARE;
> +}
> +
> +static int64_t p7ioc_creset(struct pci_slot *slot)
> +{
> +	struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb);
> +	struct p7ioc *ioc = p->ioc;
> +	uint64_t reg64;
> +
> +	switch (slot->state) {
> +	case P7IOC_SLOT_NORMAL:
> +		PHBDBG(p, "CRESET: Starts\n");
> +		p->flags |= P7IOC_PHB_CFG_BLOCKED;
> +		p7ioc_phb_reset(slot->phb);
> +
> +		/*
> +		 * According to the experiment, we probably still have the
> +		 * fenced state with the corresponding PHB in the Fence WOF
> +		 * and we need clear that explicitly. Besides, the RGC might
> +		 * already have informational error and we should clear that
> +		 * explicitly as well. Otherwise, RGC XIVE#0 won't issue
> +		 * interrupt any more.
> +		 */
> +		reg64 = in_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF);
> +		reg64 &= ~PPC_BIT(15 + p->index * 4);
> +		out_be64(ioc->regs + P7IOC_CHIP_FENCE_WOF, reg64);
> +
> +		/* Clear informational error from RGC */
> +		reg64 = in_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> +				P7IOC_LEM_WOF_OFFSET);
> +		reg64 &= ~PPC_BIT(18);
> +		out_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> +			 P7IOC_LEM_WOF_OFFSET, reg64);
> +		reg64 = in_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> +				P7IOC_LEM_FIR_OFFSET);
> +		reg64 &= ~PPC_BIT(18);
> +		out_be64(ioc->regs + P7IOC_RGC_LEM_BASE +
> +			 P7IOC_LEM_FIR_OFFSET, reg64);
> +
> +		/* Swith to fundamental reset */
> +		pci_slot_set_state(slot, P7IOC_SLOT_FRESET_START);
> +		return slot->ops.freset(slot);
> +	default:
> +		PHBERR(p, "CRESET: Unexpected slot state %08x\n",
> +		       slot->state);
> +	}
> +
> +	pci_slot_set_state(slot, P7IOC_SLOT_NORMAL);
> +	return OPAL_HARDWARE;
> +}
> +
> +static struct pci_slot *p7ioc_phb_slot_create(struct phb *phb)
> +{
> +	struct pci_slot *slot;
> +
> +	slot = pci_slot_alloc(phb, NULL);
> +	if (!slot)
> +		return NULL;
> +
> +	/* Elementary functions */
> +	slot->ops.get_presence_state   = p7ioc_get_presence_state;
> +	slot->ops.get_link_state       = p7ioc_get_link_state;
> +	slot->ops.get_power_state      = p7ioc_get_power_state;
> +	slot->ops.get_attention_state  = NULL;
> +	slot->ops.get_latch_state      = NULL;
> +	slot->ops.set_power_state      = p7ioc_set_power_state;
> +	slot->ops.set_attention_state  = NULL;
> +
> +	/*
> +	 * 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  = p7ioc_prepare_link_change;
> +	slot->ops.poll_link            = p7ioc_poll_link;
> +	slot->ops.hreset               = p7ioc_hreset;
> +	slot->ops.freset               = p7ioc_freset;
> +	slot->ops.pfreset              = NULL;
> +	slot->ops.creset               = p7ioc_creset;
> +
> +	return slot;
> +}
> +
>  static const struct phb_ops p7ioc_phb_ops = {
>  	.cfg_read8		= p7ioc_pcicfg_read8,
>  	.cfg_read16		= p7ioc_pcicfg_read16,
> @@ -2605,15 +2369,6 @@ static const struct phb_ops p7ioc_phb_ops = {
>  	.get_msi_64		= p7ioc_get_msi_64,
>  	.ioda_reset		= p7ioc_ioda_reset,
>  	.papr_errinjct_reset	= p7ioc_papr_errinjct_reset,
> -	.presence_detect	= p7ioc_presence_detect,
> -	.link_state		= p7ioc_link_state,
> -	.power_state		= p7ioc_power_state,
> -	.slot_power_off		= p7ioc_slot_power_off,
> -	.slot_power_on		= p7ioc_slot_power_on,
> -	.complete_reset		= p7ioc_complete_reset,
> -	.hot_reset		= p7ioc_hot_reset,
> -	.fundamental_reset	= p7ioc_freset,
> -	.poll			= p7ioc_poll,
>  };
>  
>  /* p7ioc_phb_get_xive - Interrupt control from OPAL */
> @@ -2893,6 +2648,7 @@ void p7ioc_phb_setup(struct p7ioc *ioc, uint8_t index)
>  {
>  	struct p7ioc_phb *p = &ioc->phbs[index];
>  	unsigned int buid_base = ioc->buid_base + PHBn_BUID_BASE(index);
> +	struct pci_slot *slot;
>  
>  	p->index = index;
>  	p->ioc = ioc;
> @@ -2933,6 +2689,10 @@ void p7ioc_phb_setup(struct p7ioc *ioc, uint8_t index)
>  	 * get a useful OPAL ID for it
>  	 */
>  	pci_register_phb(&p->phb, OPAL_DYNAMIC_PHB_ID);
> +	slot = p7ioc_phb_slot_create(&p->phb);
> +	if (!slot)
> +		prlog(PR_NOTICE, "P7IOC: Cannot create PHB#%d slot\n",
> +		      p->phb.opal_id);
>  
>  	/* Platform additional setup */
>  	if (platform.pci_setup_phb)
> diff --git a/include/p7ioc.h b/include/p7ioc.h
> index 85ea591..3b57a9c 100644
> --- a/include/p7ioc.h
> +++ b/include/p7ioc.h
> @@ -204,31 +204,32 @@ enum p7ioc_phb_state {
>  	/* PHB turned off by FSP (no clocks) */
>  	P7IOC_PHB_STATE_OFF,
>  
> -	/* Slot Power up state machine */
> -	P7IOC_PHB_STATE_SPUP_STABILIZE_DELAY,		/* Step 3 Delay
> 2s		*/
> -	P7IOC_PHB_STATE_SPUP_SLOT_STATUS,		/* Step 4 waiting
> for status	*/
> -
> -	/* Slot Power down state machine */
> -	P7IOC_PHB_STATE_SPDOWN_STABILIZE_DELAY,	/* Step 2 Delay 2s	
> 	*/
> -	P7IOC_PHB_STATE_SPDOWN_SLOT_STATUS,	/* Step 3 waiting for
> status	*/
> -
> -	/* Fundamental reset sequence */
> -	P7IOC_PHB_STATE_FRESET_DISABLE_LINK,	/* Disable link training	
> 	*/
> -	P7IOC_PHB_STATE_FRESET_ASSERT_DELAY,	/* Delay on fundamental
> reset assert	*/
> -	P7IOC_PHB_STATE_FRESET_DEASSERT_DELAY,	/* Delay on fundamental
> reset deassert	*/
> -	P7IOC_PHB_STATE_FRESET_WAIT_LINK,	/* Wait for link up		
> 	*/
> -
> -	/* Hot Reset sequence */
> -	P7IOC_PHB_STATE_HRESET_DISABLE_LINK,	/* Disable Link training	
> */
> -	P7IOC_PHB_STATE_HRESET_ASSERT,		/* Hot reset assert	
> 	*/
> -	P7IOC_PHB_STATE_HRESET_DELAY,		/* Hot reset delay	
> 	*/
> -	P7IOC_PHB_STATE_HRESET_ENABLE_LINK,	/* Enable Link training	
> 	*/
> -	P7IOC_PHB_STATE_HRESET_WAIT_LINK,	/* Wait link traing		
> */
> -
>  	/* Normal PHB functional state */
>  	P7IOC_PHB_STATE_FUNCTIONAL,
>  };
>  
> +/* P7IOC PHB slot states */
> +#define P7IOC_SLOT_NORMAL		0x00000000
> +#define P7IOC_SLOT_LINK			0x00000100
> +#define   P7IOC_SLOT_LINK_START		0x00000101
> +#define   P7IOC_SLOT_LINK_WAIT		0x00000102
> +#define P7IOC_SLOT_HRESET		0x00000200
> +#define   P7IOC_SLOT_HRESET_START	0x00000201
> +#define   P7IOC_SLOT_HRESET_TRAINING	0x00000202
> +#define   P7IOC_SLOT_HRESET_DELAY	0x00000203
> +#define   P7IOC_SLOT_HRESET_DELAY2	0x00000204
> +#define P7IOC_SLOT_FRESET		0x00000300
> +#define   P7IOC_SLOT_FRESET_START	0x00000301
> +#define   P7IOC_SLOT_FRESET_TRAINING	0x00000302
> +#define   P7IOC_SLOT_FRESET_POWER_OFF	0x00000303
> +#define   P7IOC_SLOT_FRESET_POWER_ON	0x00000304
> +#define   P7IOC_SLOT_FRESET_ASSERT	0x00000305
> +#define   P7IOC_SLOT_FRESET_DEASSERT	0x00000306
> +#define P7IOC_SLOT_PFRESET		0x00000400
> +#define   P7IOC_SLOT_PFRESET_START	0x00000401
> +#define P7IOC_SLOT_CRESET		0x00000500
> +#define   P7IOC_SLOT_CRESET_START	0x00000501
> +
>  /*
>   * In order to support error detection and recovery on different
>   * types of IOCs (e.g. P5IOC, P7IOC, P8IOC), the best bet would
> @@ -286,12 +287,12 @@ struct p7ioc;
>  
>  #define P7IOC_PHB_CFG_USE_ASB	0x00000001 /* ASB to access PCI-
> CFG     */
>  #define P7IOC_PHB_CFG_BLOCKED	0x00000002 /* PCI-CFG blocked except 0	
> */
> -#define P7IOC_RESTORE_BUS_NUM	0x00000004 /* Restore buses after reset
> */
>  
>  struct p7ioc_phb {
>  	uint8_t				index;	/* 0..5 index
> inside p7ioc */
>  	uint8_t				gen;
>  	uint32_t			flags;
> +	enum p7ioc_phb_state		state;
>  #define P7IOC_REV_DD10	0x00a20001
>  #define P7IOC_REV_DD11	0x00a20002
>  	uint32_t			rev;	/* Both major and minor
> have 2 bytes */
> @@ -302,9 +303,6 @@ struct p7ioc_phb {
>  	uint64_t			io_base;
>  	uint64_t			m32_base;
>  	uint64_t			m64_base;
> -	enum p7ioc_phb_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 */
>  	uint64_t			lxive_cache[8];



More information about the Skiboot mailing list