From patchwork Thu Jun 4 06:19:04 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gavin Shan X-Patchwork-Id: 480427 X-Patchwork-Delegate: benh@kernel.crashing.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4B7701402B1 for ; Thu, 4 Jun 2015 16:20:15 +1000 (AEST) Received: from ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id A0FF81A086D for ; Thu, 4 Jun 2015 16:20:14 +1000 (AEST) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from e23smtp06.au.ibm.com (e23smtp06.au.ibm.com [202.81.31.148]) (using TLSv1 with cipher CAMELLIA256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 1D2EF1A004D for ; Thu, 4 Jun 2015 16:20:11 +1000 (AEST) Received: from /spool/local by e23smtp06.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 4 Jun 2015 16:20:10 +1000 Received: from d23dlp03.au.ibm.com (202.81.31.214) by e23smtp06.au.ibm.com (202.81.31.212) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 4 Jun 2015 16:20:09 +1000 Received: from d23relay06.au.ibm.com (d23relay06.au.ibm.com [9.185.63.219]) by d23dlp03.au.ibm.com (Postfix) with ESMTP id 246F43578052 for ; Thu, 4 Jun 2015 16:20:09 +1000 (EST) Received: from d23av02.au.ibm.com (d23av02.au.ibm.com [9.190.235.138]) by d23relay06.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id t546K0M640173644 for ; Thu, 4 Jun 2015 16:20:08 +1000 Received: from d23av02.au.ibm.com (localhost [127.0.0.1]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id t546JaYw030255 for ; Thu, 4 Jun 2015 16:19:36 +1000 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id t546JZKu029443; Thu, 4 Jun 2015 16:19:36 +1000 Received: from bran.ozlabs.ibm.com (unknown [9.192.254.114]) by ozlabs.au.ibm.com (Postfix) with ESMTP id 27748A03E2; Thu, 4 Jun 2015 16:19:14 +1000 (AEST) Received: from gwshan (shangw.ozlabs.ibm.com [10.61.2.199]) by bran.ozlabs.ibm.com (Postfix) with ESMTP id 31E42E387C; Thu, 4 Jun 2015 16:19:14 +1000 (AEST) Received: by gwshan (Postfix, from userid 1000) id 260B39422B2; Thu, 4 Jun 2015 16:19:14 +1000 (AEST) From: Gavin Shan To: skiboot@lists.ozlabs.org Date: Thu, 4 Jun 2015 16:19:04 +1000 Message-Id: <1433398749-15096-13-git-send-email-gwshan@linux.vnet.ibm.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1433398749-15096-1-git-send-email-gwshan@linux.vnet.ibm.com> References: <1433398749-15096-1-git-send-email-gwshan@linux.vnet.ibm.com> X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 15060406-0021-0000-0000-0000015A9BBB Subject: [Skiboot] [PATCH v7 12/17] hw/p7ioc: Support PHB slot X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" The patch 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 --- hw/p7ioc-phb.c | 1147 ++++++++++++++++++++++--------------------------------- include/p7ioc.h | 48 ++- 2 files changed, 483 insertions(+), 712 deletions(-) diff --git a/hw/p7ioc-phb.c b/hw/p7ioc-phb.c index 1e2493e..80202cb 100644 --- a/hw/p7ioc-phb.c +++ b/hw/p7ioc-phb.c @@ -20,8 +20,9 @@ #include #include #include -#include #include +#include +#include #include #include #include @@ -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; -} - /* * Lock callbacks. Allows the OPAL API handlers to lock the * PHB around calls such as config space, EEH, etc... @@ -186,670 +174,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, 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); - - /* 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, - 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); - 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, 0x7e00000000000000); - 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, 0x7e00000000000000); - - /* 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, ®32); - 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, 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); - - /* 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, 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); - 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,459 @@ static int64_t p7ioc_papr_errinjct_reset(struct phb *phb) return OPAL_SUCCESS; } +static int64_t p7ioc_get_presence_status(struct pci_slot *slot, + uint8_t *val) +{ + struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb); + uint64_t reg; + + /* By default, there're nothing connected */ + *val = 0; + + reg = in_be64(p->regs + PHB_PCIE_SLOTCTL2); + if (reg & PHB_PCIE_SLOTCTL2_PRSTN_STAT) + *val = 1; + + return OPAL_SUCCESS; +} + +static int64_t p7ioc_get_link_status(struct pci_slot *slot, + uint8_t *val) +{ + struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb); + uint64_t reg64; + uint16_t lstat; + int64_t rc; + + /* Default case */ + *val = 0; + + /* 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)) + return OPAL_SUCCESS; + + /* Grab link width from PCIe capability */ + rc = p7ioc_pcicfg_read16(&p->phb, 0, + p->ecap + PCICAP_EXP_LSTAT, &lstat); + if (rc < 0) { + PHBERR(p, "%s: Error %lld reading link status\n", + __func__, rc); + return OPAL_HARDWARE; + } + + if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) + return OPAL_SUCCESS; + + *val = GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat); + return OPAL_SUCCESS; +} + +static int64_t p7ioc_get_power_status(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 = 1; + else + *val = 0; + + return OPAL_SUCCESS; +} + +static int64_t p7ioc_set_power_status(struct pci_slot *slot, + uint8_t val) +{ + struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb); + uint64_t reg64; + uint8_t power_status = 0; + + if (val != 0 && val != 1) + 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) + power_status = 1; + if (power_status == val) + return OPAL_SUCCESS; + + /* Power on/off */ + if (val) { + 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 is_up) +{ + struct p7ioc_phb *p = phb_to_p7ioc_phb(slot->phb); + uint64_t ci_idx = p->index + 2; + uint32_t cfg32; + + if (!is_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_status) + slot->ops.get_presence_status(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_status) + slot->ops.get_presence_status(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_status = p7ioc_get_presence_status; + slot->ops.get_link_status = p7ioc_get_link_status; + slot->ops.get_power_status = p7ioc_get_power_status; + slot->ops.get_attention_status = NULL; + slot->ops.get_latch_status = NULL; + slot->ops.set_power_status = p7ioc_set_power_status; + slot->ops.set_attention_status = 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 = { .lock = p7ioc_phb_lock, .unlock = p7ioc_phb_unlock, @@ -2606,15 +2383,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 */ @@ -2894,6 +2662,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; @@ -2934,6 +2703,10 @@ void p7ioc_phb_setup(struct p7ioc *ioc, uint8_t index) * get a useful OPAL ID for it */ pci_register_phb(&p->phb); + 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 c35ee11..25e53b3 100644 --- a/include/p7ioc.h +++ b/include/p7ioc.h @@ -205,31 +205,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 @@ -287,12 +288,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 */ @@ -304,9 +305,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];