/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #define PHBDBG(p, fmt, a...) prlog(PR_DEBUG, "PHB#%04x: " fmt, \ (p)->phb.opal_id, ## a) #define PHBERR(p, fmt, a...) prlog(PR_ERR, "PHB#%04x: " fmt, \ (p)->phb.opal_id, ## a) /* Helper to select an IODA table entry */ static inline void p7ioc_phb_ioda_sel(struct p7ioc_phb *p, uint32_t table, uint32_t addr, bool autoinc) { out_be64(p->regs + PHB_IODA_ADDR, (autoinc ? PHB_IODA_AD_AUTOINC : 0) | SETFIELD(PHB_IODA_AD_TSEL, 0ul, table) | SETFIELD(PHB_IODA_AD_TADR, 0ul, addr)); } static bool p7ioc_phb_fenced(struct p7ioc_phb *p) { struct p7ioc *ioc = p->ioc; uint64_t fence, fbits; fbits = 0x0003000000000000UL >> (p->index * 4); fence = in_be64(ioc->regs + P7IOC_CHIP_FENCE_SHADOW); return (fence & fbits) != 0; } /* * Configuration space access * * The PHB lock is assumed to be already held */ static int64_t p7ioc_pcicfg_check(struct p7ioc_phb *p, uint32_t bdfn, uint32_t offset, uint32_t size) { uint32_t sm = size - 1; if (offset > 0xfff || bdfn > 0xffff) return OPAL_PARAMETER; if (offset & sm) return OPAL_PARAMETER; /* The root bus only has a device at 0 and we get into an * error state if we try to probe beyond that, so let's * avoid that and just return an error to Linux */ if ((bdfn >> 8) == 0 && (bdfn & 0xff)) return OPAL_HARDWARE; /* Check PHB state */ if (p->broken) return OPAL_HARDWARE; return OPAL_SUCCESS; } #define P7IOC_PCI_CFG_READ(size, type) \ static int64_t p7ioc_pcicfg_read##size(struct phb *phb, uint32_t bdfn, \ uint32_t offset, type *data) \ { \ struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); \ uint64_t addr; \ void *base = p->regs; \ int64_t rc; \ \ /* Initialize data in case of error */ \ *data = (type)0xffffffff; \ \ rc = p7ioc_pcicfg_check(p, bdfn, offset, sizeof(type)); \ if (rc) \ return rc; \ \ if (p7ioc_phb_fenced(p)) { \ if (!(p->flags & P7IOC_PHB_CFG_USE_ASB)) \ return OPAL_HARDWARE; \ \ base = p->regs_asb; \ } else if ((p->flags & P7IOC_PHB_CFG_BLOCKED) && bdfn != 0) { \ return OPAL_HARDWARE; \ } \ \ addr = PHB_CA_ENABLE; \ addr = SETFIELD(PHB_CA_BDFN, addr, bdfn); \ addr = SETFIELD(PHB_CA_REG, addr, offset); \ out_be64(base + PHB_CONFIG_ADDRESS, addr); \ *data = in_le##size(base + PHB_CONFIG_DATA + \ (offset & (4 - sizeof(type)))); \ \ return OPAL_SUCCESS; \ } #define P7IOC_PCI_CFG_WRITE(size, type) \ static int64_t p7ioc_pcicfg_write##size(struct phb *phb, uint32_t bdfn, \ uint32_t offset, type data) \ { \ struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); \ void *base = p->regs; \ uint64_t addr; \ int64_t rc; \ \ rc = p7ioc_pcicfg_check(p, bdfn, offset, sizeof(type)); \ if (rc) \ return rc; \ \ if (p7ioc_phb_fenced(p)) { \ if (!(p->flags & P7IOC_PHB_CFG_USE_ASB)) \ return OPAL_HARDWARE; \ \ base = p->regs_asb; \ } else if ((p->flags & P7IOC_PHB_CFG_BLOCKED) && bdfn != 0) { \ return OPAL_HARDWARE; \ } \ \ addr = PHB_CA_ENABLE; \ addr = SETFIELD(PHB_CA_BDFN, addr, bdfn); \ addr = SETFIELD(PHB_CA_REG, addr, offset); \ out_be64(base + PHB_CONFIG_ADDRESS, addr); \ out_le##size(base + PHB_CONFIG_DATA + \ (offset & (4 - sizeof(type))), data); \ \ return OPAL_SUCCESS; \ } P7IOC_PCI_CFG_READ(8, uint8_t) P7IOC_PCI_CFG_READ(16, uint16_t) P7IOC_PCI_CFG_READ(32, uint32_t) P7IOC_PCI_CFG_WRITE(8, uint8_t) P7IOC_PCI_CFG_WRITE(16, uint16_t) P7IOC_PCI_CFG_WRITE(32, uint32_t) static void p7ioc_eeh_read_phb_status(struct p7ioc_phb *p, struct OpalIoP7IOCPhbErrorData *stat) { uint16_t tmp16; unsigned int i; memset(stat, 0, sizeof(struct OpalIoP7IOCPhbErrorData)); /* Error data common part */ stat->common.version = OPAL_PHB_ERROR_DATA_VERSION_1; stat->common.ioType = OPAL_PHB_ERROR_DATA_TYPE_P7IOC; stat->common.len = sizeof(struct OpalIoP7IOCPhbErrorData); /* * We read some registers using config space through AIB. * * Get to other registers using ASB when possible to get to them * through a fence if one is present. * * Note that the OpalIoP7IOCPhbErrorData has oddities, such as the * bridge control being 32-bit and the UTL registers being 32-bit * (which they really are, but they use the top 32-bit of a 64-bit * register so we need to be a bit careful). */ /* Use ASB to access PCICFG if the PHB has been fenced */ p->flags |= P7IOC_PHB_CFG_USE_ASB; /* Grab RC bridge control, make it 32-bit */ p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_BRCTL, &tmp16); stat->brdgCtl = tmp16; /* Grab UTL status registers */ stat->portStatusReg = hi32(in_be64(p->regs_asb + UTL_PCIE_PORT_STATUS)); stat->rootCmplxStatus = hi32(in_be64(p->regs_asb + UTL_RC_STATUS)); stat->busAgentStatus = hi32(in_be64(p->regs_asb + UTL_SYS_BUS_AGENT_STATUS)); /* * Grab various RC PCIe capability registers. All device, slot * and link status are 16-bit, so we grab the pair control+status * for each of them */ p7ioc_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_DEVCTL, &stat->deviceStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_SLOTCTL, &stat->slotStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->ecap + PCICAP_EXP_LCTL, &stat->linkStatus); /* * I assume those are the standard config space header, cmd & status * together makes 32-bit. Secondary status is 16-bit so I'll clear * the top on that one */ p7ioc_pcicfg_read32(&p->phb, 0, PCI_CFG_CMD, &stat->devCmdStatus); p7ioc_pcicfg_read16(&p->phb, 0, PCI_CFG_SECONDARY_STATUS, &tmp16); stat->devSecStatus = tmp16; /* Grab a bunch of AER regs */ p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_RERR_STA, &stat->rootErrorStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_UE_STATUS, &stat->uncorrErrorStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_CE_STATUS, &stat->corrErrorStatus); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG0, &stat->tlpHdr1); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG1, &stat->tlpHdr2); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG2, &stat->tlpHdr3); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_HDR_LOG3, &stat->tlpHdr4); p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_SRCID, &stat->sourceId); /* Restore to AIB */ p->flags &= ~P7IOC_PHB_CFG_USE_ASB; /* * No idea what that that is supposed to be, opal.h says * "Record data about the call to allocate a buffer." * * Let's leave them alone for now... * * uint64_t errorClass; * uint64_t correlator; */ /* P7IOC MMIO Error Regs */ stat->p7iocPlssr = in_be64(p->regs_asb + PHB_CPU_LOADSTORE_STATUS); stat->p7iocCsr = in_be64(p->regs_asb + PHB_DMA_CHAN_STATUS); stat->lemFir = in_be64(p->regs_asb + PHB_LEM_FIR_ACCUM); stat->lemErrorMask = in_be64(p->regs_asb + PHB_LEM_ERROR_MASK); stat->lemWOF = in_be64(p->regs_asb + PHB_LEM_WOF); stat->phbErrorStatus = in_be64(p->regs_asb + PHB_ERR_STATUS); stat->phbFirstErrorStatus = in_be64(p->regs_asb + PHB_ERR1_STATUS); stat->phbErrorLog0 = in_be64(p->regs_asb + PHB_ERR_LOG_0); stat->phbErrorLog1 = in_be64(p->regs_asb + PHB_ERR_LOG_1); stat->mmioErrorStatus = in_be64(p->regs_asb + PHB_OUT_ERR_STATUS); stat->mmioFirstErrorStatus = in_be64(p->regs_asb + PHB_OUT_ERR1_STATUS); stat->mmioErrorLog0 = in_be64(p->regs_asb + PHB_OUT_ERR_LOG_0); stat->mmioErrorLog1 = in_be64(p->regs_asb + PHB_OUT_ERR_LOG_1); stat->dma0ErrorStatus = in_be64(p->regs_asb + PHB_INA_ERR_STATUS); stat->dma0FirstErrorStatus = in_be64(p->regs_asb + PHB_INA_ERR1_STATUS); stat->dma0ErrorLog0 = in_be64(p->regs_asb + PHB_INA_ERR_LOG_0); stat->dma0ErrorLog1 = in_be64(p->regs_asb + PHB_INA_ERR_LOG_1); stat->dma1ErrorStatus = in_be64(p->regs_asb + PHB_INB_ERR_STATUS); stat->dma1FirstErrorStatus = in_be64(p->regs_asb + PHB_INB_ERR1_STATUS); stat->dma1ErrorLog0 = in_be64(p->regs_asb + PHB_INB_ERR_LOG_0); stat->dma1ErrorLog1 = in_be64(p->regs_asb + PHB_INB_ERR_LOG_1); /* Grab PESTA & B content */ p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, 0, true); for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) stat->pestA[i] = in_be64(p->regs_asb + PHB_IODA_DATA0); p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, 0, true); for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) stat->pestB[i] = in_be64(p->regs_asb + PHB_IODA_DATA0); } static int64_t p7ioc_eeh_freeze_status(struct phb *phb, uint64_t pe_number, uint8_t *freeze_state, uint16_t *pci_error_type, uint16_t *severity) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t peev_bit = PPC_BIT(pe_number & 0x3f); uint64_t peev, pesta, pestb; /* Defaults: not frozen */ *freeze_state = OPAL_EEH_STOPPED_NOT_FROZEN; *pci_error_type = OPAL_EEH_NO_ERROR; /* Check dead */ if (p->broken) { *freeze_state = OPAL_EEH_STOPPED_MMIO_DMA_FREEZE; *pci_error_type = OPAL_EEH_PHB_ERROR; if (severity) *severity = OPAL_EEH_SEV_PHB_DEAD; return OPAL_SUCCESS; } /* Check fence */ if (p7ioc_phb_fenced(p)) { /* Should be OPAL_EEH_STOPPED_TEMP_UNAVAIL ? */ *freeze_state = OPAL_EEH_STOPPED_MMIO_DMA_FREEZE; *pci_error_type = OPAL_EEH_PHB_ERROR; if (severity) *severity = OPAL_EEH_SEV_PHB_FENCED; return OPAL_SUCCESS; } /* Check the PEEV */ p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev = in_be64(p->regs + PHB_IODA_DATA0); if (pe_number > 63) peev = in_be64(p->regs + PHB_IODA_DATA0); if (!(peev & peev_bit)) return OPAL_SUCCESS; /* Indicate that we have an ER pending */ p7ioc_phb_set_err_pending(p, true); if (severity) *severity = OPAL_EEH_SEV_PE_ER; /* Read the PESTA & PESTB */ p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, pe_number, false); pesta = in_be64(p->regs + PHB_IODA_DATA0); p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, pe_number, false); pestb = in_be64(p->regs + PHB_IODA_DATA0); /* Convert them */ if (pesta & IODA_PESTA_MMIO_FROZEN) *freeze_state |= OPAL_EEH_STOPPED_MMIO_FREEZE; if (pestb & IODA_PESTB_DMA_STOPPED) *freeze_state |= OPAL_EEH_STOPPED_DMA_FREEZE; /* XXX Handle more causes */ if (pesta & IODA_PESTA_MMIO_CAUSE) *pci_error_type = OPAL_EEH_PE_MMIO_ERROR; else *pci_error_type = OPAL_EEH_PE_DMA_ERROR; return OPAL_SUCCESS; } static int64_t p7ioc_eeh_next_error(struct phb *phb, uint64_t *first_frozen_pe, uint16_t *pci_error_type, uint16_t *severity) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); struct p7ioc *ioc = p->ioc; uint64_t fir, peev0, peev1; uint32_t cfg32, i; /* Check if there're pending errors on the IOC. */ if (p7ioc_err_pending(ioc) && p7ioc_check_LEM(ioc, pci_error_type, severity)) return OPAL_SUCCESS; /* Clear result */ *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; *first_frozen_pe = (uint64_t)-1; /* Check dead */ if (p->broken) { *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_DEAD; return OPAL_SUCCESS; } /* Check fence */ if (p7ioc_phb_fenced(p)) { /* Should be OPAL_EEH_STOPPED_TEMP_UNAVAIL ? */ *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_FENCED; p7ioc_phb_set_err_pending(p, false); return OPAL_SUCCESS; } /* * If we don't have pending errors, which might be moved * from IOC to the PHB, then check if there has any frozen PEs. */ if (!p7ioc_phb_err_pending(p)) { p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); if (peev0 || peev1) { p->err.err_src = P7IOC_ERR_SRC_PHB0 + p->index; p->err.err_class = P7IOC_ERR_CLASS_ER; p->err.err_bit = 0; p7ioc_phb_set_err_pending(p, true); } } /* Check the pending errors, which might come from IOC */ if (p7ioc_phb_err_pending(p)) { /* * If the frozen PE is caused by a malfunctioning TLP, we * need reset the PHB. So convert ER to PHB-fatal error * for the case. */ if (p->err.err_class == P7IOC_ERR_CLASS_ER) { fir = in_be64(p->regs_asb + PHB_LEM_FIR_ACCUM); if (fir & PPC_BIT(60)) { p7ioc_pcicfg_read32(&p->phb, 0, p->aercap + PCIECAP_AER_UE_STATUS, &cfg32); if (cfg32 & PCIECAP_AER_UE_MALFORMED_TLP) p->err.err_class = P7IOC_ERR_CLASS_PHB; } } /* * Map P7IOC internal error class to that one OS can handle. * For P7IOC_ERR_CLASS_ER, we also need figure out the frozen * PE. */ switch (p->err.err_class) { case P7IOC_ERR_CLASS_PHB: *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_PHB_FENCED; p7ioc_phb_set_err_pending(p, false); break; case P7IOC_ERR_CLASS_MAL: case P7IOC_ERR_CLASS_INF: *pci_error_type = OPAL_EEH_PHB_ERROR; *severity = OPAL_EEH_SEV_INF; p7ioc_phb_set_err_pending(p, false); break; case P7IOC_ERR_CLASS_ER: *pci_error_type = OPAL_EEH_PE_ERROR; *severity = OPAL_EEH_SEV_PE_ER; p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); for (i = 0 ; i < 64; i++) { if (PPC_BIT(i) & peev1) { *first_frozen_pe = i + 64; break; } } for (i = 0 ; *first_frozen_pe == (uint64_t)-1 && i < 64; i++) { if (PPC_BIT(i) & peev0) { *first_frozen_pe = i; break; } } /* No frozen PE? */ if (*first_frozen_pe == (uint64_t)-1) { *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; p7ioc_phb_set_err_pending(p, false); } break; default: *pci_error_type = OPAL_EEH_NO_ERROR; *severity = OPAL_EEH_SEV_NO_ERROR; p7ioc_phb_set_err_pending(p, false); } } return OPAL_SUCCESS; } static void p7ioc_ER_err_clear(struct p7ioc_phb *p) { u64 err, lem; u32 val; /* Rec 1,2 */ lem = in_be64(p->regs + PHB_LEM_FIR_ACCUM); /* Rec 3,4,5 AER registers (could use cfg space accessors) */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000001c00000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0x10000000); /* Rec 6,7,8 XXX DOC whacks payload & req size ... we don't */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000005000000000ull); val = in_be32(p->regs + PHB_CONFIG_DATA); out_be32(p->regs + PHB_CONFIG_DATA, (val & 0xe0700000) | 0x0f000f00); /* Rec 9,10,11 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000010400000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 12,13,14 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000011000000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 23,24,25 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000013000000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0xffffffff); /* Rec 26,27,28 */ out_be64(p->regs + PHB_CONFIG_ADDRESS, 0x8000004000000000ull); out_be32(p->regs + PHB_CONFIG_DATA, 0x470100f8); /* Rec 29..34 UTL registers */ err = in_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS); out_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS, err); err = in_be64(p->regs + UTL_PCIE_PORT_STATUS); out_be64(p->regs + UTL_PCIE_PORT_STATUS, err); err = in_be64(p->regs + UTL_RC_STATUS); out_be64(p->regs + UTL_RC_STATUS, err); /* PHB error traps registers */ err = in_be64(p->regs + PHB_ERR_STATUS); out_be64(p->regs + PHB_ERR_STATUS, err); out_be64(p->regs + PHB_ERR1_STATUS, 0); out_be64(p->regs + PHB_ERR_LOG_0, 0); out_be64(p->regs + PHB_ERR_LOG_1, 0); err = in_be64(p->regs + PHB_OUT_ERR_STATUS); out_be64(p->regs + PHB_OUT_ERR_STATUS, err); out_be64(p->regs + PHB_OUT_ERR1_STATUS, 0); out_be64(p->regs + PHB_OUT_ERR_LOG_0, 0); out_be64(p->regs + PHB_OUT_ERR_LOG_1, 0); err = in_be64(p->regs + PHB_INA_ERR_STATUS); out_be64(p->regs + PHB_INA_ERR_STATUS, err); out_be64(p->regs + PHB_INA_ERR1_STATUS, 0); out_be64(p->regs + PHB_INA_ERR_LOG_0, 0); out_be64(p->regs + PHB_INA_ERR_LOG_1, 0); err = in_be64(p->regs + PHB_INB_ERR_STATUS); out_be64(p->regs + PHB_INB_ERR_STATUS, err); out_be64(p->regs + PHB_INB_ERR1_STATUS, 0); out_be64(p->regs + PHB_INB_ERR_LOG_0, 0); out_be64(p->regs + PHB_INB_ERR_LOG_1, 0); /* Rec 67, 68 LEM */ out_be64(p->regs + PHB_LEM_FIR_AND_MASK, ~lem); out_be64(p->regs + PHB_LEM_WOF, 0); } static int64_t p7ioc_eeh_freeze_clear(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t peev0, peev1; /* XXX Now this is a heavy hammer, coming roughly from the P7IOC doc * and my old "pseudopal" code. It will need to be refined. In general * error handling will have to be reviewed and probably done properly * "from scratch" based on the description in the p7IOC spec. * * XXX Additionally, when handling interrupts, we might want to consider * masking while processing and/or ack'ing interrupt bits etc... */ u64 err; /* Summary. If nothing, move to clearing the PESTs which can * contain a freeze state from a previous error or simply set * explicitly by the user */ err = in_be64(p->regs + PHB_ETU_ERR_SUMMARY); if (err == 0) goto clear_pest; p7ioc_ER_err_clear(p); clear_pest: /* XXX We just clear the whole PESTA for MMIO clear and PESTB * for DMA clear. We might want to only clear the frozen bit * as to not clobber the rest of the state. However, we expect * the state to have been harvested before the clear operations * so this might not be an issue */ if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, 0); } if (eeh_action_token & OPAL_EEH_ACTION_CLEAR_FREEZE_DMA) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, 0); } /* Update ER pending indication */ p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); if (peev0 || peev1) { p->err.err_src = P7IOC_ERR_SRC_PHB0 + p->index; p->err.err_class = P7IOC_ERR_CLASS_ER; p->err.err_bit = 0; p7ioc_phb_set_err_pending(p, true); } else p7ioc_phb_set_err_pending(p, false); return OPAL_SUCCESS; } static int64_t p7ioc_eeh_freeze_set(struct phb *phb, uint64_t pe_number, uint64_t eeh_action_token) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t data; if (pe_number > 127) return OPAL_PARAMETER; if (eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_MMIO && eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_DMA && eeh_action_token != OPAL_EEH_ACTION_SET_FREEZE_ALL) return OPAL_PARAMETER; if (eeh_action_token & OPAL_EEH_ACTION_SET_FREEZE_MMIO) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, pe_number, false); data = in_be64(p->regs + PHB_IODA_DATA0); data |= IODA_PESTA_MMIO_FROZEN; out_be64(p->regs + PHB_IODA_DATA0, data); } if (eeh_action_token & OPAL_EEH_ACTION_SET_FREEZE_DMA) { p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, pe_number, false); data = in_be64(p->regs + PHB_IODA_DATA0); data |= IODA_PESTB_DMA_STOPPED; out_be64(p->regs + PHB_IODA_DATA0, data); } return OPAL_SUCCESS; } static int64_t p7ioc_err_inject_finalize(struct p7ioc_phb *p, uint64_t addr, uint64_t mask, uint64_t ctrl, bool is_write) { if (is_write) ctrl |= PHB_PAPR_ERR_INJ_CTL_WR; else ctrl |= PHB_PAPR_ERR_INJ_CTL_RD; /* HW100549: Take read and write for outbound errors * on DD10 chip */ if (p->rev == P7IOC_REV_DD10) ctrl |= (PHB_PAPR_ERR_INJ_CTL_RD | PHB_PAPR_ERR_INJ_CTL_WR); out_be64(p->regs + PHB_PAPR_ERR_INJ_ADDR, addr); out_be64(p->regs + PHB_PAPR_ERR_INJ_MASK, mask); out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, ctrl); return OPAL_SUCCESS; } static int64_t p7ioc_err_inject_mem32(struct p7ioc_phb *p, uint64_t pe_number, uint64_t addr, uint64_t mask, bool is_write) { uint64_t a, m, prefer, base; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_OUTB; int32_t index; a = 0x0ull; prefer = 0x0ull; for (index = 0; index < 128; index++) { if (GETFIELD(IODA_XXDT_PE, p->m32d_cache[index]) != pe_number) continue; base = p->m32_base + M32_PCI_START + (M32_PCI_SIZE / 128) * index; /* Update preferred address */ if (!prefer) { prefer = GETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, base); prefer = SETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, 0x0ull, prefer); } /* The input address matches ? */ if (addr >= base && addr < base + (M32_PCI_SIZE / 128)) { a = addr; break; } } /* Invalid PE number */ if (!prefer) return OPAL_PARAMETER; /* Specified address is out of range */ if (!a) { a = prefer; m = PHB_PAPR_ERR_INJ_MASK_MMIO; } else { m = mask; } return p7ioc_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t p7ioc_err_inject_io32(struct p7ioc_phb *p, uint64_t pe_number, uint64_t addr, uint64_t mask, bool is_write) { uint64_t a, m, prefer, base; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_OUTB; int32_t index; a = 0x0ull; prefer = 0x0ull; for (index = 0; index < 128; index++) { if (GETFIELD(IODA_XXDT_PE, p->iod_cache[index]) != pe_number) continue; base = p->io_base + (PHB_IO_SIZE / 128) * index; /* Update preferred address */ if (!prefer) { prefer = GETFIELD(PHB_PAPR_ERR_INJ_MASK_IO, base); prefer = SETFIELD(PHB_PAPR_ERR_INJ_MASK_IO, 0x0ull, prefer); } /* The input address matches ? */ if (addr >= base && addr < base + (PHB_IO_SIZE / 128)) { a = addr; break; } } /* Invalid PE number */ if (!prefer) return OPAL_PARAMETER; /* Specified address is out of range */ if (!a) { a = prefer; m = PHB_PAPR_ERR_INJ_MASK_IO; } else { m = mask; } return p7ioc_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t p7ioc_err_inject_cfg(struct p7ioc_phb *p, uint64_t pe_number, uint64_t addr, uint64_t mask, bool is_write) { uint64_t a, m; uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_CFG; uint8_t v_bits, base, bus_no; /* Looking into PELTM to see if the PCI bus# is owned * by the PE#. Otherwise, we have to figure one out. */ base = GETFIELD(IODA_PELTM_BUS, p->peltm_cache[pe_number]); v_bits = GETFIELD(IODA_PELTM_BUS_VALID, p->peltm_cache[pe_number]); switch (v_bits) { case IODA_BUS_VALID_3_BITS: case IODA_BUS_VALID_4_BITS: case IODA_BUS_VALID_5_BITS: case IODA_BUS_VALID_6_BITS: case IODA_BUS_VALID_7_BITS: case IODA_BUS_VALID_ALL: base = GETFIELD(IODA_PELTM_BUS, p->peltm_cache[pe_number]); base &= (0xff - (((1 << (7 - v_bits)) - 1))); a = SETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, 0x0ul, base); m = PHB_PAPR_ERR_INJ_MASK_CFG; bus_no = GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, addr); bus_no &= (0xff - (((1 << (7 - v_bits)) - 1))); if (base == bus_no) { a = addr; m = mask; } break; case IODA_BUS_VALID_ANY: default: return OPAL_PARAMETER; } return p7ioc_err_inject_finalize(p, a, m, ctrl, is_write); } static int64_t p7ioc_err_inject_dma(struct p7ioc_phb *p, uint64_t pe_number, uint64_t addr, uint64_t mask, bool is_write) { uint64_t ctrl = PHB_PAPR_ERR_INJ_CTL_INB; int32_t index; /* For DMA, we just pick address from TVT */ for (index = 0; index < 128; index++) { if (GETFIELD(IODA_TVT1_PE_NUM, p->tve_hi_cache[index]) != pe_number) continue; addr = SETFIELD(PHB_PAPR_ERR_INJ_MASK_DMA, 0ul, index); mask = PHB_PAPR_ERR_INJ_MASK_DMA; break; } /* Some PE might not have DMA capability */ if (index >= 128) return OPAL_PARAMETER; return p7ioc_err_inject_finalize(p, addr, mask, ctrl, is_write); } static int64_t p7ioc_err_inject(struct phb *phb, uint64_t pe_number, uint32_t type, uint32_t func, uint64_t addr, uint64_t mask) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); int64_t (*handler)(struct p7ioc_phb *p, uint64_t pe_number, uint64_t addr, uint64_t mask, bool is_write); bool is_write; /* To support 64-bits error later */ if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) return OPAL_UNSUPPORTED; /* We can't inject error to the reserved PE#127 */ if (pe_number > 126) return OPAL_PARAMETER; /* Clear the leftover from last time */ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); /* Check if PE number is valid one in PELTM cache */ if (p->peltm_cache[pe_number] == 0x0001f80000000000ull) return OPAL_PARAMETER; /* Clear the leftover from last time */ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); switch (func) { case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_DATA: is_write = false; handler = p7ioc_err_inject_mem32; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_DATA: is_write = true; handler = p7ioc_err_inject_mem32; break; case OPAL_ERR_INJECT_FUNC_IOA_LD_IO_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_IO_DATA: is_write = false; handler = p7ioc_err_inject_io32; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_IO_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_IO_DATA: is_write = true; handler = p7ioc_err_inject_io32; break; case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_DATA: is_write = false; handler = p7ioc_err_inject_cfg; break; case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_DATA: is_write = true; handler = p7ioc_err_inject_cfg; break; case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_DATA: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_MASTER: case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_TARGET: is_write = false; handler = p7ioc_err_inject_dma; break; case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_ADDR: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_DATA: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_MASTER: case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET: is_write = true; handler = p7ioc_err_inject_dma; break; default: return OPAL_PARAMETER; } return handler(p, pe_number, addr, mask, is_write); } static int64_t p7ioc_get_diag_data(struct phb *phb, void *diag_buffer, uint64_t diag_buffer_len) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); struct OpalIoP7IOCPhbErrorData *diag = diag_buffer; if (diag_buffer_len < sizeof(struct OpalIoP7IOCPhbErrorData)) return OPAL_PARAMETER; /* Specific error data */ p7ioc_eeh_read_phb_status(p, diag); /* * We're running to here probably because of errors (MAL * or INF class) from IOC. For the case, we need clear * the pending errors and mask the error bit for MAL class * error. Fortunately, we shouldn't get MAL class error from * IOC on P7IOC. */ if (p7ioc_phb_err_pending(p) && p->err.err_class == P7IOC_ERR_CLASS_INF && p->err.err_src >= P7IOC_ERR_SRC_PHB0 && p->err.err_src <= P7IOC_ERR_SRC_PHB5) { p7ioc_ER_err_clear(p); p7ioc_phb_set_err_pending(p, false); } return OPAL_SUCCESS; } /* * We don't support address remapping now since all M64 * BARs are sharing on remapping base address. We might * introduce flag to the PHB in order to trace that. The * flag allows to be changed for once. It's something to * do in future. */ static int64_t p7ioc_set_phb_mem_window(struct phb *phb, uint16_t window_type, uint16_t window_num, uint64_t base, uint64_t __unused pci_base, uint64_t size) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t data64; switch (window_type) { case OPAL_IO_WINDOW_TYPE: case OPAL_M32_WINDOW_TYPE: return OPAL_UNSUPPORTED; case OPAL_M64_WINDOW_TYPE: if (window_num >= 16) return OPAL_PARAMETER; /* The base and size should be 16MB aligned */ if (base & 0xFFFFFF || size & 0xFFFFFF) return OPAL_PARAMETER; data64 = p->m64b_cache[window_num]; data64 = SETFIELD(IODA_M64BT_BASE, data64, base >> 24); size = (size >> 24); data64 = SETFIELD(IODA_M64BT_MASK, data64, 0x1000000 - size); break; default: return OPAL_PARAMETER; } /* * If the M64 BAR hasn't enabled yet, we needn't flush * the setting to hardware and just keep it to the cache */ p->m64b_cache[window_num] = data64; if (!(data64 & IODA_M64BT_ENABLE)) return OPAL_SUCCESS; p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, window_num, false); out_be64(p->regs + PHB_IODA_DATA0, data64); return OPAL_SUCCESS; } /* * We can't enable or disable I/O and M32 dynamically, even * unnecessary. So the function only support M64 BARs. */ static int64_t p7ioc_phb_mmio_enable(struct phb *phb, uint16_t window_type, uint16_t window_num, uint16_t enable) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t data64, base, mask; switch (window_type) { case OPAL_IO_WINDOW_TYPE: case OPAL_M32_WINDOW_TYPE: return OPAL_UNSUPPORTED; case OPAL_M64_WINDOW_TYPE: if (window_num >= 16 || enable >= OPAL_ENABLE_M64_NON_SPLIT) return OPAL_PARAMETER; break; default: return OPAL_PARAMETER; } /* * While enabling one specific M64 BAR, we should have * the base/size configured correctly. Otherwise, it * probably incurs fenced AIB. */ data64 = p->m64b_cache[window_num]; if (enable == OPAL_ENABLE_M64_SPLIT) { base = GETFIELD(IODA_M64BT_BASE, data64); base = (base << 24); mask = GETFIELD(IODA_M64BT_MASK, data64); if (base < p->m64_base || mask == 0x0ul) return OPAL_PARTIAL; data64 |= IODA_M64BT_ENABLE; } else if (enable == OPAL_DISABLE_M64) { data64 &= ~IODA_M64BT_ENABLE; } p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, window_num, false); out_be64(p->regs + PHB_IODA_DATA0, data64); p->m64b_cache[window_num] = data64; return OPAL_SUCCESS; } static int64_t p7ioc_map_pe_mmio_window(struct phb *phb, uint64_t pe_number, uint16_t window_type, uint16_t window_num, uint16_t segment_num) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t tbl, index; uint64_t *cache; if (pe_number > 127) return OPAL_PARAMETER; switch(window_type) { case OPAL_IO_WINDOW_TYPE: if (window_num != 0 || segment_num > 127) return OPAL_PARAMETER; tbl = IODA_TBL_IODT; index = segment_num; cache = &p->iod_cache[index]; break; case OPAL_M32_WINDOW_TYPE: if (window_num != 0 || segment_num > 127) return OPAL_PARAMETER; tbl = IODA_TBL_M32DT; index = segment_num; cache = &p->m32d_cache[index]; break; case OPAL_M64_WINDOW_TYPE: if (window_num > 15 || segment_num > 7) return OPAL_PARAMETER; tbl = IODA_TBL_M64DT; index = window_num << 3 | segment_num; cache = &p->m64d_cache[index]; break; default: return OPAL_PARAMETER; } p7ioc_phb_ioda_sel(p, tbl, index, false); out_be64(p->regs + PHB_IODA_DATA0, SETFIELD(IODA_XXDT_PE, 0ull, pe_number)); /* Update cache */ *cache = SETFIELD(IODA_XXDT_PE, 0ull, pe_number); return OPAL_SUCCESS; } static int64_t p7ioc_set_pe(struct phb *phb, uint64_t pe_number, uint64_t bdfn, uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare, uint8_t pe_action) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t pelt; uint64_t *cache = &p->peltm_cache[pe_number]; if (pe_number > 127 || bdfn > 0xffff) return OPAL_PARAMETER; if (pe_action != OPAL_MAP_PE && pe_action != OPAL_UNMAP_PE) return OPAL_PARAMETER; if (bus_compare > 7) return OPAL_PARAMETER; if (pe_action == OPAL_MAP_PE) { pelt = SETFIELD(IODA_PELTM_BUS, 0ul, bdfn >> 8); pelt |= SETFIELD(IODA_PELTM_DEV, 0ul, (bdfn >> 3) & 0x1f); pelt |= SETFIELD(IODA_PELTM_FUNC, 0ul, bdfn & 0x7); pelt |= SETFIELD(IODA_PELTM_BUS_VALID, 0ul, bus_compare); if (dev_compare) pelt |= IODA_PELTM_DEV_VALID; if (func_compare) pelt |= IODA_PELTM_FUNC_VALID; } else pelt = 0; p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, pe_number, false); out_be64(p->regs + PHB_IODA_DATA0, pelt); /* Update cache */ *cache = pelt; return OPAL_SUCCESS; } static int64_t p7ioc_set_peltv(struct phb *phb, uint32_t parent_pe, uint32_t child_pe, uint8_t state) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint32_t reg; uint64_t mask, peltv; uint64_t *cache; if (parent_pe > 127 || child_pe > 127) return OPAL_PARAMETER; cache = (child_pe >> 6) ? &p->peltv_hi_cache[parent_pe] : &p->peltv_lo_cache[parent_pe]; reg = (child_pe >> 6) ? PHB_IODA_DATA1 : PHB_IODA_DATA0; child_pe &= 0x2f; mask = 1ull << (63 - child_pe); p7ioc_phb_ioda_sel(p, IODA_TBL_PELTV, parent_pe, false); peltv = in_be64(p->regs + reg); if (state) peltv |= mask; else peltv &= ~mask; out_be64(p->regs + reg, peltv); /* Update cache */ *cache = peltv; return OPAL_SUCCESS; } static int64_t p7ioc_map_pe_dma_window(struct phb *phb, uint64_t pe_number, uint16_t window_id, uint16_t tce_levels, uint64_t tce_table_addr, uint64_t tce_table_size, uint64_t tce_page_size) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t tvt0, tvt1, t, pelt; uint64_t dma_window_size; uint64_t *cache_lo, *cache_hi; if (pe_number > 127 || window_id > 127 || tce_levels != 1) return OPAL_PARAMETER; cache_lo = &p->tve_lo_cache[window_id]; cache_hi = &p->tve_hi_cache[window_id]; /* Encode table size */ dma_window_size = tce_page_size * (tce_table_size >> 3); t = ilog2(dma_window_size); if (t < 27) return OPAL_PARAMETER; tvt0 = SETFIELD(IODA_TVT0_TCE_TABLE_SIZE, 0ul, (t - 26)); /* Encode TCE page size */ switch(tce_page_size) { case 0x1000: /* 4K */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 1ul); break; case 0x10000: /* 64K */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 5ul); break; case 0x1000000: /* 16M */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 13ul); break; case 0x400000000UL: /* 16G */ tvt1 = SETFIELD(IODA_TVT1_IO_PSIZE, 0ul, 23ul); break; default: return OPAL_PARAMETER; } /* XXX Hub number ... leave 0 for now */ /* Shift in the address. The table address is "off by 4 bits" * but since the field is itself shifted by 16, we basically * need to write the address >> 12, which basically boils down * to writing a 4k page address */ tvt0 = SETFIELD(IODA_TVT0_TABLE_ADDR, tvt0, tce_table_addr >> 12); /* Read the PE filter info from the PELT-M */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, pe_number, false); pelt = in_be64(p->regs + PHB_IODA_DATA0); /* Copy in filter bits from PELT */ tvt0 = SETFIELD(IODA_TVT0_BUS_VALID, tvt0, GETFIELD(IODA_PELTM_BUS_VALID, pelt)); tvt0 = SETFIELD(IODA_TVT0_BUS_NUM, tvt0, GETFIELD(IODA_PELTM_BUS, pelt)); tvt1 = SETFIELD(IODA_TVT1_DEV_NUM, tvt1, GETFIELD(IODA_PELTM_DEV, pelt)); tvt1 = SETFIELD(IODA_TVT1_FUNC_NUM, tvt1, GETFIELD(IODA_PELTM_FUNC, pelt)); if (pelt & IODA_PELTM_DEV_VALID) tvt1 |= IODA_TVT1_DEV_VALID; if (pelt & IODA_PELTM_FUNC_VALID) tvt1 |= IODA_TVT1_FUNC_VALID; tvt1 = SETFIELD(IODA_TVT1_PE_NUM, tvt1, pe_number); /* Write the TVE */ p7ioc_phb_ioda_sel(p, IODA_TBL_TVT, window_id, false); out_be64(p->regs + PHB_IODA_DATA1, tvt1); out_be64(p->regs + PHB_IODA_DATA0, tvt0); /* Update cache */ *cache_lo = tvt0; *cache_hi = tvt1; return OPAL_SUCCESS; } static int64_t p7ioc_map_pe_dma_window_real(struct phb *phb __unused, uint64_t pe_number __unused, uint16_t dma_window_num __unused, uint64_t pci_start_addr __unused, uint64_t pci_mem_size __unused) { /* XXX Not yet implemented (not yet used by Linux) */ return OPAL_UNSUPPORTED; } static int64_t p7ioc_set_mve(struct phb *phb, uint32_t mve_number, uint64_t pe_number) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t pelt, mve = 0; uint64_t *cache = &p->mve_cache[mve_number]; if (pe_number > 127 || mve_number > 255) return OPAL_PARAMETER; /* Read the PE filter info from the PELT-M */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, pe_number, false); pelt = in_be64(p->regs + PHB_IODA_DATA0); mve = SETFIELD(IODA_MVT_BUS_VALID, mve, GETFIELD(IODA_PELTM_BUS_VALID, pelt)); mve = SETFIELD(IODA_MVT_BUS_NUM, mve, GETFIELD(IODA_PELTM_BUS, pelt)); mve = SETFIELD(IODA_MVT_DEV_NUM, mve, GETFIELD(IODA_PELTM_DEV, pelt)); mve = SETFIELD(IODA_MVT_FUNC_NUM, mve, GETFIELD(IODA_PELTM_FUNC, pelt)); if (pelt & IODA_PELTM_DEV_VALID) mve |= IODA_MVT_DEV_VALID; if (pelt & IODA_PELTM_FUNC_VALID) mve |= IODA_MVT_FUNC_VALID; mve = SETFIELD(IODA_MVT_PE_NUM, mve, pe_number); p7ioc_phb_ioda_sel(p, IODA_TBL_MVT, mve_number, false); out_be64(p->regs + PHB_IODA_DATA0, mve); /* Update cache */ *cache = mve; return OPAL_SUCCESS; } static int64_t p7ioc_set_mve_enable(struct phb *phb, uint32_t mve_number, uint32_t state) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t mve; uint64_t *cache = &p->mve_cache[mve_number]; if (mve_number > 255) return OPAL_PARAMETER; p7ioc_phb_ioda_sel(p, IODA_TBL_MVT, mve_number, false); mve = in_be64(p->regs + PHB_IODA_DATA0); if (state) mve |= IODA_MVT_VALID; else mve &= ~IODA_MVT_VALID; out_be64(p->regs + PHB_IODA_DATA0, mve); /* Update cache */ *cache = mve; return OPAL_SUCCESS; } static int64_t p7ioc_set_xive_pe(struct phb *phb, uint64_t pe_number, uint32_t xive_num) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); uint64_t xive; if (pe_number > 127 || xive_num > 255) return OPAL_PARAMETER; /* Update MXIVE cache */ xive = p->mxive_cache[xive_num]; xive = SETFIELD(IODA_XIVT_PENUM, xive, pe_number); p->mxive_cache[xive_num] = xive; /* Update HW */ p7ioc_phb_ioda_sel(p, IODA_TBL_MXIVT, xive_num, false); xive = in_be64(p->regs + PHB_IODA_DATA0); xive = SETFIELD(IODA_XIVT_PENUM, xive, pe_number); out_be64(p->regs + PHB_IODA_DATA0, xive); return OPAL_SUCCESS; } static int64_t p7ioc_get_xive_source(struct phb *phb, uint32_t xive_num, int32_t *interrupt_source_number) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); if (xive_num > 255 || !interrupt_source_number) return OPAL_PARAMETER; *interrupt_source_number = (p->buid_msi << 4) | xive_num; return OPAL_SUCCESS; } static int64_t p7ioc_get_msi_32(struct phb *phb __unused, uint64_t mve_number, uint32_t xive_num, uint8_t msi_range, uint32_t *msi_address, uint32_t *message_data) { if (mve_number > 255 || xive_num > 255 || msi_range != 1) return OPAL_PARAMETER; *msi_address = 0xffff0000 | (mve_number << 4); *message_data = xive_num; return OPAL_SUCCESS; } static int64_t p7ioc_get_msi_64(struct phb *phb __unused, uint64_t mve_number, uint32_t xive_num, uint8_t msi_range, uint64_t *msi_address, uint32_t *message_data) { if (mve_number > 255 || xive_num > 255 || msi_range != 1) return OPAL_PARAMETER; *msi_address = (9ul << 60) | (((u64)mve_number) << 48); *message_data = xive_num; return OPAL_SUCCESS; } static void p7ioc_root_port_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_PERR_RESP); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 |= (PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT); pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* Mask various unrecoverable errors */ if (!aercap) return; pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, &val32); val32 |= (PCIECAP_AER_UE_MASK_POISON_TLP | PCIECAP_AER_UE_MASK_COMPL_TIMEOUT | PCIECAP_AER_UE_MASK_COMPL_ABORT | PCIECAP_AER_UE_MASK_ECRC); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, val32); /* Report various unrecoverable errors as fatal errors */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, &val32); val32 |= (PCIECAP_AER_UE_SEVERITY_DLLP | PCIECAP_AER_UE_SEVERITY_SURPRISE_DOWN | PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_UNEXP_COMPL | PCIECAP_AER_UE_SEVERITY_RECV_OVFLOW | PCIECAP_AER_UE_SEVERITY_MALFORMED_TLP); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, val32); /* Mask various recoverable errors */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, &val32); val32 |= PCIECAP_AER_CE_MASK_ADV_NONFATAL; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, val32); /* Enable ECRC check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= (PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); /* Enable all error reporting */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_RERR_CMD, &val32); val32 |= (PCIECAP_AER_RERR_CMD_FE | PCIECAP_AER_RERR_CMD_NFE | PCIECAP_AER_RERR_CMD_CE); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_RERR_CMD, val32); } static void p7ioc_switch_port_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking and disable INTx */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_INTx_DIS); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Disable partity error and enable system error */ pci_cfg_read16(phb, bdfn, PCI_CFG_BRCTL, &val16); val16 &= ~PCI_CFG_BRCTL_PERR_RESP_EN; val16 |= PCI_CFG_BRCTL_SERR_EN; pci_cfg_write16(phb, bdfn, PCI_CFG_BRCTL, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 |= (PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT); pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* Unmask all unrecoverable errors */ if (!aercap) return; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, 0x0); /* Severity of unrecoverable errors */ if (dev->dev_type == PCIE_TYPE_SWITCH_UPPORT) val32 = (PCIECAP_AER_UE_SEVERITY_DLLP | PCIECAP_AER_UE_SEVERITY_SURPRISE_DOWN | PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_RECV_OVFLOW | PCIECAP_AER_UE_SEVERITY_MALFORMED_TLP | PCIECAP_AER_UE_SEVERITY_INTERNAL); else val32 = (PCIECAP_AER_UE_SEVERITY_FLOW_CTL_PROT | PCIECAP_AER_UE_SEVERITY_INTERNAL); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_SEVERITY, val32); /* Mask various correctable errors */ val32 = PCIECAP_AER_CE_MASK_ADV_NONFATAL; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CE_MASK, val32); /* Enable ECRC generation and disable ECRC check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= PCIECAP_AER_CAPCTL_ECRCG_EN; val32 &= ~PCIECAP_AER_CAPCTL_ECRCC_EN; pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); } static void p7ioc_endpoint_init(struct phb *phb, struct pci_device *dev, int ecap, int aercap) { uint16_t bdfn = dev->bdfn; uint16_t val16; uint32_t val32; /* Enable SERR and parity checking */ pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16); val16 |= (PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_SERR_EN); pci_cfg_write16(phb, bdfn, PCI_CFG_CMD, val16); /* Enable reporting various errors */ if (!ecap) return; pci_cfg_read16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, &val16); val16 &= ~PCICAP_EXP_DEVCTL_CE_REPORT; val16 |= (PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT); pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16); /* Enable ECRC generation and check */ pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, &val32); val32 |= (PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); } static int p7ioc_device_init(struct phb *phb, struct pci_device *dev, void *data __unused) { int ecap, aercap; /* Common initialization for the device */ pci_device_init(phb, dev); ecap = pci_cap(dev, PCI_CFG_CAP_ID_EXP, false); aercap = pci_cap(dev, PCIECAP_ID_AER, true); if (dev->dev_type == PCIE_TYPE_ROOT_PORT) p7ioc_root_port_init(phb, dev, ecap, aercap); else if (dev->dev_type == PCIE_TYPE_SWITCH_UPPORT || dev->dev_type == PCIE_TYPE_SWITCH_DNPORT) p7ioc_switch_port_init(phb, dev, ecap, aercap); else p7ioc_endpoint_init(phb, dev, ecap, aercap); return 0; } static int64_t p7ioc_pci_reinit(struct phb *phb, uint64_t scope, uint64_t data) { struct pci_device *pd; uint16_t bdfn = data; int ret; if (scope != OPAL_REINIT_PCI_DEV) return OPAL_PARAMETER; pd = pci_find_dev(phb, bdfn); if (!pd) return OPAL_PARAMETER; ret = p7ioc_device_init(phb, pd, NULL); if (ret) return OPAL_HARDWARE; return OPAL_SUCCESS; } static uint8_t p7ioc_choose_bus(struct phb *phb __unused, struct pci_device *bridge, uint8_t candidate, uint8_t *max_bus, bool *use_max) { uint8_t m, al; int i; /* Bus number selection is nasty on P7IOC. Our EEH HW can only cope * with bus ranges that are naturally aligned powers of two. It also * has "issues" with dealing with more than 32 bus numbers. * * On the other hand we can deal with overlaps to some extent as * the PELT-M entries are ordered. * * We also don't need to bother with the busses between the upstream * and downstream ports of switches. * * For now we apply this simple mechanism which matche what OFW does * under OPAL: * * - Top level bus (PHB to RC) is 0 * - RC to first device is 1..ff * - Then going down, a switch gets (N = parent bus, M = parent max) * * Upstream bridge is N+1, M, use_max = false * * Downstream bridge is closest power of two from 32 down and * * use max * * XXX NOTE: If we have access to HW VPDs, we could know whether * this is a bridge with a single device on it such as IPR and * limit ourselves to a single bus number. */ /* Default use_max is false (legacy) */ *use_max = false; /* If we are the root complex or we are not in PCIe land anymore, just * use legacy algorithm */ if (!bridge || !pci_has_cap(bridge, PCI_CFG_CAP_ID_EXP, false)) return candidate; /* Figure out the bridge type */ switch(bridge->dev_type) { case PCIE_TYPE_PCIX_TO_PCIE: /* PCI-X to PCIE ... hrm, let's not bother too much with that */ return candidate; case PCIE_TYPE_SWITCH_UPPORT: case PCIE_TYPE_ROOT_PORT: /* Upstream port, we use legacy handling as well */ return candidate; case PCIE_TYPE_SWITCH_DNPORT: case PCIE_TYPE_PCIE_TO_PCIX: /* That leaves us with the interesting cases that we handle */ break; default: /* Should not happen, treat as legacy */ prerror("PCI: Device %04x has unsupported type %d in choose_bus\n", bridge->bdfn, bridge->dev_type); return candidate; } /* Ok, let's find a power of two that fits, fallback to 1 */ for (i = 5; i >= 0; i--) { m = (1 << i) - 1; al = (candidate + m) & ~m; if (al <= *max_bus && (al + m) <= *max_bus) break; } if (i < 0) return 0; *use_max = true; *max_bus = al + m; return al; } static int64_t p7ioc_get_reserved_pe_number(struct phb *phb __unused) { return 127; } /* p7ioc_phb_init_ioda_cache - Reset the IODA cache values */ static void p7ioc_phb_init_ioda_cache(struct p7ioc_phb *p) { unsigned int i; for (i = 0; i < 8; i++) p->lxive_cache[i] = SETFIELD(IODA_XIVT_PRIORITY, 0ull, 0xff); for (i = 0; i < 256; i++) { p->mxive_cache[i] = SETFIELD(IODA_XIVT_PRIORITY, 0ull, 0xff); p->mve_cache[i] = 0; } for (i = 0; i < 16; i++) p->m64b_cache[i] = 0; /* * Since there is only one root port under the PHB, * We make all PELTM entries except last one to be * invalid by configuring their RID to 00:00.1. The * last entry is to encompass all RIDs. */ for (i = 0; i < 127; i++) p->peltm_cache[i] = 0x0001f80000000000UL; p->peltm_cache[127] = 0x0ul; for (i = 0; i < 128; i++) { p->peltv_lo_cache[i] = 0; p->peltv_hi_cache[i] = 0; p->tve_lo_cache[i] = 0; p->tve_hi_cache[i] = 0; p->iod_cache[i] = 0; p->m32d_cache[i] = 0; p->m64d_cache[i] = 0; } } /* p7ioc_phb_ioda_reset - Reset the IODA tables * * @purge: If true, the cache is cleared and the cleared values * are applied to HW. If false, the cached values are * applied to HW * * This reset the IODA tables in the PHB. It is called at * initialization time, on PHB reset, and can be called * explicitly from OPAL */ static int64_t p7ioc_ioda_reset(struct phb *phb, bool purge) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); unsigned int i; uint64_t reg64; uint64_t data64, data64_hi; uint8_t prio; uint16_t server; uint64_t m_server, m_prio; /* If the "purge" argument is set, we clear the table cache */ if (purge) p7ioc_phb_init_ioda_cache(p); /* Init_18..19: Setup the HRT * * XXX NOTE: I still don't completely get that HRT business so * I'll just mimmic BML and put the PHB number + 1 in there */ p7ioc_phb_ioda_sel(p, IODA_TBL_HRT, 0, true); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); out_be64(p->regs + PHB_IODA_DATA0, p->index + 1); /* Init_20..21: Cleanup the LXIVT * * We set the priority to FF (masked) and clear everything * else. That means we leave the HRT index to 0 which is * going to remain unmodified... for now. */ p7ioc_phb_ioda_sel(p, IODA_TBL_LXIVT, 0, true); for (i = 0; i < 8; i++) { data64 = p->lxive_cache[i]; server = GETFIELD(IODA_XIVT_SERVER, data64); prio = GETFIELD(IODA_XIVT_PRIORITY, data64); /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } data64 = SETFIELD(IODA_XIVT_SERVER, data64, m_server); data64 = SETFIELD(IODA_XIVT_PRIORITY, data64, m_prio); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_22..23: Cleanup the MXIVT * * We set the priority to FF (masked) and clear everything * else. That means we leave the HRT index to 0 which is * going to remain unmodified... for now. */ p7ioc_phb_ioda_sel(p, IODA_TBL_MXIVT, 0, true); for (i = 0; i < 256; i++) { data64 = p->mxive_cache[i]; server = GETFIELD(IODA_XIVT_SERVER, data64); prio = GETFIELD(IODA_XIVT_PRIORITY, data64); /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } data64 = SETFIELD(IODA_XIVT_SERVER, data64, m_server); data64 = SETFIELD(IODA_XIVT_PRIORITY, data64, m_prio); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_24..25: Cleanup the MVT */ p7ioc_phb_ioda_sel(p, IODA_TBL_MVT, 0, true); for (i = 0; i < 256; i++) { data64 = p->mve_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_26..27: Cleanup the PELTM * * A completely clear PELTM should make everything match PE 0 */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTM, 0, true); for (i = 0; i < 127; i++) { data64 = p->peltm_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_28..30: Cleanup the PELTV */ p7ioc_phb_ioda_sel(p, IODA_TBL_PELTV, 0, true); for (i = 0; i < 127; i++) { data64 = p->peltv_lo_cache[i]; data64_hi = p->peltv_hi_cache[i]; out_be64(p->regs + PHB_IODA_DATA1, data64_hi); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_31..33: Cleanup the TVT */ p7ioc_phb_ioda_sel(p, IODA_TBL_TVT, 0, true); for (i = 0; i < 127; i++) { data64 = p->tve_lo_cache[i]; data64_hi = p->tve_hi_cache[i]; out_be64(p->regs + PHB_IODA_DATA1, data64_hi); out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_34..35: Cleanup the M64BT * * We don't enable M64 BARs by default. However, * we shouldn't purge the hw and cache for it in * future. */ p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, 0, true); for (i = 0; i < 16; i++) out_be64(p->regs + PHB_IODA_DATA0, 0); /* Init_36..37: Cleanup the IODT */ p7ioc_phb_ioda_sel(p, IODA_TBL_IODT, 0, true); for (i = 0; i < 127; i++) { data64 = p->iod_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_38..39: Cleanup the M32DT */ p7ioc_phb_ioda_sel(p, IODA_TBL_M32DT, 0, true); for (i = 0; i < 127; i++) { data64 = p->m32d_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Init_40..41: Cleanup the M64DT */ p7ioc_phb_ioda_sel(p, IODA_TBL_M64BT, 0, true); for (i = 0; i < 16; i++) { data64 = p->m64b_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } p7ioc_phb_ioda_sel(p, IODA_TBL_M64DT, 0, true); for (i = 0; i < 127; i++) { data64 = p->m64d_cache[i]; out_be64(p->regs + PHB_IODA_DATA0, data64); } /* Clear up the TCE cache */ reg64 = in_be64(p->regs + PHB_PHB2_CONFIG); reg64 &= ~PHB_PHB2C_64B_TCE_EN; out_be64(p->regs + PHB_PHB2_CONFIG, reg64); reg64 |= PHB_PHB2C_64B_TCE_EN; out_be64(p->regs + PHB_PHB2_CONFIG, reg64); in_be64(p->regs + PHB_PHB2_CONFIG); /* Clear PEST & PEEV */ for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) { uint64_t pesta, pestb; p7ioc_phb_ioda_sel(p, IODA_TBL_PESTA, i, false); pesta = in_be64(p->regs + PHB_IODA_DATA0); out_be64(p->regs + PHB_IODA_DATA0, 0); p7ioc_phb_ioda_sel(p, IODA_TBL_PESTB, i, false); pestb = in_be64(p->regs + PHB_IODA_DATA0); out_be64(p->regs + PHB_IODA_DATA0, 0); if ((pesta & IODA_PESTA_MMIO_FROZEN) || (pestb & IODA_PESTB_DMA_STOPPED)) PHBDBG(p, "Frozen PE#%x (%s - %s)\n", i, (pestb & IODA_PESTB_DMA_STOPPED) ? "DMA" : "", (pesta & IODA_PESTA_MMIO_FROZEN) ? "MMIO" : ""); } p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); for (i = 0; i < 2; i++) out_be64(p->regs + PHB_IODA_DATA0, 0); return OPAL_SUCCESS; } /* * Clear anything we have in PAPR Error Injection registers. Though * the spec says the PAPR error injection should be one-shot without * the "sticky" bit. However, that's false according to the experiments * I had. So we have to clear it at appropriate point in kernel to * avoid endless frozen PE. */ static int64_t p7ioc_papr_errinjct_reset(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul); out_be64(p->regs + PHB_PAPR_ERR_INJ_ADDR, 0x0ul); out_be64(p->regs + PHB_PAPR_ERR_INJ_MASK, 0x0ul); 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, 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); /* 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, 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); /* 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); pci_slot_set_state(slot, P7IOC_SLOT_LINK_START); return slot->ops.poll_link(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.creset = p7ioc_creset; return slot; } static const struct phb_ops p7ioc_phb_ops = { .cfg_read8 = p7ioc_pcicfg_read8, .cfg_read16 = p7ioc_pcicfg_read16, .cfg_read32 = p7ioc_pcicfg_read32, .cfg_write8 = p7ioc_pcicfg_write8, .cfg_write16 = p7ioc_pcicfg_write16, .cfg_write32 = p7ioc_pcicfg_write32, .choose_bus = p7ioc_choose_bus, .get_reserved_pe_number = p7ioc_get_reserved_pe_number, .device_init = p7ioc_device_init, .device_remove = NULL, .pci_reinit = p7ioc_pci_reinit, .eeh_freeze_status = p7ioc_eeh_freeze_status, .eeh_freeze_clear = p7ioc_eeh_freeze_clear, .eeh_freeze_set = p7ioc_eeh_freeze_set, .err_inject = p7ioc_err_inject, .get_diag_data = NULL, .get_diag_data2 = p7ioc_get_diag_data, .next_error = p7ioc_eeh_next_error, .phb_mmio_enable = p7ioc_phb_mmio_enable, .set_phb_mem_window = p7ioc_set_phb_mem_window, .map_pe_mmio_window = p7ioc_map_pe_mmio_window, .set_pe = p7ioc_set_pe, .set_peltv = p7ioc_set_peltv, .map_pe_dma_window = p7ioc_map_pe_dma_window, .map_pe_dma_window_real = p7ioc_map_pe_dma_window_real, .set_mve = p7ioc_set_mve, .set_mve_enable = p7ioc_set_mve_enable, .set_xive_pe = p7ioc_set_xive_pe, .get_xive_source = p7ioc_get_xive_source, .get_msi_32 = p7ioc_get_msi_32, .get_msi_64 = p7ioc_get_msi_64, .ioda_reset = p7ioc_ioda_reset, .papr_errinjct_reset = p7ioc_papr_errinjct_reset, }; /* p7ioc_phb_get_xive - Interrupt control from OPAL */ static int64_t p7ioc_msi_get_xive(struct irq_source *is, uint32_t isn, uint16_t *server, uint8_t *prio) { struct p7ioc_phb *p = is->data; uint32_t irq, fbuid = P7_IRQ_FBUID(isn); uint64_t xive; if (fbuid < p->buid_msi || fbuid >= (p->buid_msi + 0x10)) return OPAL_PARAMETER; irq = isn & 0xff; xive = p->mxive_cache[irq]; *server = GETFIELD(IODA_XIVT_SERVER, xive); *prio = GETFIELD(IODA_XIVT_PRIORITY, xive); return OPAL_SUCCESS; } /* p7ioc_phb_set_xive - Interrupt control from OPAL */ static int64_t p7ioc_msi_set_xive(struct irq_source *is, uint32_t isn, uint16_t server, uint8_t prio) { struct p7ioc_phb *p = is->data; uint32_t irq, fbuid = P7_IRQ_FBUID(isn); uint64_t xive, m_server, m_prio; if (fbuid < p->buid_msi || fbuid >= (p->buid_msi + 0x10)) return OPAL_PARAMETER; /* We cache the arguments because we have to mangle * it in order to hijack 3 bits of priority to extend * the server number */ irq = isn & 0xff; xive = p->mxive_cache[irq]; xive = SETFIELD(IODA_XIVT_SERVER, xive, server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, prio); p->mxive_cache[irq] = xive; /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } /* We use HRT entry 0 always for now */ p7ioc_phb_ioda_sel(p, IODA_TBL_MXIVT, irq, false); xive = in_be64(p->regs + PHB_IODA_DATA0); xive = SETFIELD(IODA_XIVT_SERVER, xive, m_server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, m_prio); out_be64(p->regs + PHB_IODA_DATA0, xive); return OPAL_SUCCESS; } /* p7ioc_phb_get_xive - Interrupt control from OPAL */ static int64_t p7ioc_lsi_get_xive(struct irq_source *is, uint32_t isn, uint16_t *server, uint8_t *prio) { struct p7ioc_phb *p = is->data; uint32_t irq = (isn & 0x7); uint32_t fbuid = P7_IRQ_FBUID(isn); uint64_t xive; if (fbuid != p->buid_lsi) return OPAL_PARAMETER; xive = p->lxive_cache[irq]; *server = GETFIELD(IODA_XIVT_SERVER, xive); *prio = GETFIELD(IODA_XIVT_PRIORITY, xive); return OPAL_SUCCESS; } /* p7ioc_phb_set_xive - Interrupt control from OPAL */ static int64_t p7ioc_lsi_set_xive(struct irq_source *is, uint32_t isn, uint16_t server, uint8_t prio) { struct p7ioc_phb *p = is->data; uint32_t irq = (isn & 0x7); uint32_t fbuid = P7_IRQ_FBUID(isn); uint64_t xive, m_server, m_prio; if (fbuid != p->buid_lsi) return OPAL_PARAMETER; xive = SETFIELD(IODA_XIVT_SERVER, 0ull, server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, prio); /* * We cache the arguments because we have to mangle * it in order to hijack 3 bits of priority to extend * the server number */ p->lxive_cache[irq] = xive; /* Now we mangle the server and priority */ if (prio == 0xff) { m_server = 0; m_prio = 0xff; } else { m_server = server >> 3; m_prio = (prio >> 3) | ((server & 7) << 5); } /* We use HRT entry 0 always for now */ p7ioc_phb_ioda_sel(p, IODA_TBL_LXIVT, irq, false); xive = in_be64(p->regs + PHB_IODA_DATA0); xive = SETFIELD(IODA_XIVT_SERVER, xive, m_server); xive = SETFIELD(IODA_XIVT_PRIORITY, xive, m_prio); out_be64(p->regs + PHB_IODA_DATA0, xive); return OPAL_SUCCESS; } static void p7ioc_phb_err_interrupt(struct irq_source *is, uint32_t isn) { struct p7ioc_phb *p = is->data; uint64_t peev0, peev1; PHBDBG(p, "Got interrupt 0x%04x\n", isn); opal_pci_eeh_set_evt(p->phb.opal_id); /* If the PHB is broken, go away */ if (p->broken) return; /* * Check if there's an error pending and update PHB fence * state and return, the ER error is drowned at this point */ phb_lock(&p->phb); if (p7ioc_phb_fenced(p)) { PHBERR(p, "ER error ignored, PHB fenced\n"); phb_unlock(&p->phb); return; } /* * If we already had pending errors, which might be * moved from IOC, then we needn't check PEEV to avoid * overwriting the errors from IOC. */ if (!p7ioc_phb_err_pending(p)) { phb_unlock(&p->phb); return; } /* * We don't have pending errors from IOC, it's safe * to check PEEV for frozen PEs. */ p7ioc_phb_ioda_sel(p, IODA_TBL_PEEV, 0, true); peev0 = in_be64(p->regs + PHB_IODA_DATA0); peev1 = in_be64(p->regs + PHB_IODA_DATA0); if (peev0 || peev1) { p->err.err_src = P7IOC_ERR_SRC_PHB0 + p->index; p->err.err_class = P7IOC_ERR_CLASS_ER; p->err.err_bit = 0; p7ioc_phb_set_err_pending(p, true); } phb_unlock(&p->phb); } static uint64_t p7ioc_lsi_attributes(struct irq_source *is __unused, uint32_t isn) { uint32_t irq = (isn & 0x7); if (irq == PHB_LSI_PCIE_ERROR) return IRQ_ATTR_TARGET_OPAL | IRQ_ATTR_TARGET_RARE | IRQ_ATTR_TYPE_LSI; return IRQ_ATTR_TARGET_LINUX; } /* MSIs (OS owned) */ static const struct irq_source_ops p7ioc_msi_irq_ops = { .get_xive = p7ioc_msi_get_xive, .set_xive = p7ioc_msi_set_xive, }; /* LSIs (OS owned) */ static const struct irq_source_ops p7ioc_lsi_irq_ops = { .get_xive = p7ioc_lsi_get_xive, .set_xive = p7ioc_lsi_set_xive, .attributes = p7ioc_lsi_attributes, .interrupt = p7ioc_phb_err_interrupt, }; static void p7ioc_pcie_add_node(struct p7ioc_phb *p) { uint64_t reg[2], iob, m32b, m64b, tkill; uint32_t lsibase, icsp = get_ics_phandle(); struct dt_node *np; reg[0] = cleanup_addr((uint64_t)p->regs); reg[1] = 0x100000; np = dt_new_addr(p->ioc->dt_node, "pciex", reg[0]); if (!np) return; p->phb.dt_node = np; dt_add_property_strings(np, "compatible", "ibm,p7ioc-pciex", "ibm,ioda-phb"); dt_add_property_strings(np, "device_type", "pciex"); dt_add_property(np, "reg", reg, sizeof(reg)); dt_add_property_cells(np, "#address-cells", 3); dt_add_property_cells(np, "#size-cells", 2); dt_add_property_cells(np, "#interrupt-cells", 1); dt_add_property_cells(np, "bus-range", 0, 0xff); dt_add_property_cells(np, "clock-frequency", 0x200, 0); /* ??? */ dt_add_property_cells(np, "interrupt-parent", icsp); /* XXX FIXME: add slot-name */ //dt_property_cell("bus-width", 8); /* Figure it out from VPD ? */ /* "ranges", we only expose IO and M32 * * Note: The kernel expects us to have chopped of 64k from the * M32 size (for the 32-bit MSIs). If we don't do that, it will * get confused (OPAL does it) */ iob = cleanup_addr(p->io_base); m32b = cleanup_addr(p->m32_base + M32_PCI_START); dt_add_property_cells(np, "ranges", /* IO space */ 0x01000000, 0x00000000, 0x00000000, hi32(iob), lo32(iob), 0, PHB_IO_SIZE, /* M32 space */ 0x02000000, 0x00000000, M32_PCI_START, hi32(m32b), lo32(m32b), 0,M32_PCI_SIZE - 0x10000); /* XXX FIXME: add opal-memwin32, dmawins, etc... */ m64b = cleanup_addr(p->m64_base); dt_add_property_u64s(np, "ibm,opal-m64-window", m64b, m64b, PHB_M64_SIZE); dt_add_property_cells(np, "ibm,opal-msi-ports", 256); dt_add_property_cells(np, "ibm,opal-num-pes", 128); dt_add_property_cells(np, "ibm,opal-reserved-pe", 127); dt_add_property_cells(np, "ibm,opal-msi-ranges", p->buid_msi << 4, 0x100); tkill = reg[0] + PHB_TCE_KILL; dt_add_property_cells(np, "ibm,opal-tce-kill", hi32(tkill), lo32(tkill)); dt_add_property_cells(np, "ibm,supported-tce-sizes", 12, // 4K 16, // 64K 24, // 16M 34); // 16G /* * Linux may use this property to allocate the diag data buffer, which * can be used for either of these structs. Pass the largest to ensure * they can both fit in this buffer. */ dt_add_property_cells(np, "ibm,phb-diag-data-size", MAX(sizeof(struct OpalIoP7IOCPhbErrorData), sizeof(struct OpalIoP7IOCErrorData))); /* Add associativity properties */ add_chip_dev_associativity(np); /* The interrupt maps will be generated in the RC node by the * PCI code based on the content of this structure: */ lsibase = p->buid_lsi << 4; p->phb.lstate.int_size = 2; p->phb.lstate.int_val[0][0] = lsibase + PHB_LSI_PCIE_INTA; p->phb.lstate.int_val[0][1] = 1; p->phb.lstate.int_val[1][0] = lsibase + PHB_LSI_PCIE_INTB; p->phb.lstate.int_val[1][1] = 1; p->phb.lstate.int_val[2][0] = lsibase + PHB_LSI_PCIE_INTC; p->phb.lstate.int_val[2][1] = 1; p->phb.lstate.int_val[3][0] = lsibase + PHB_LSI_PCIE_INTD; p->phb.lstate.int_val[3][1] = 1; p->phb.lstate.int_parent[0] = icsp; p->phb.lstate.int_parent[1] = icsp; p->phb.lstate.int_parent[2] = icsp; p->phb.lstate.int_parent[3] = icsp; } /* p7ioc_phb_setup - Setup a p7ioc_phb data structure * * WARNING: This is called before the AIB register routing is * established. If this wants to access PHB registers, it must * use the ASB hard coded variant (slower) */ 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; p->gen = 2; /* Operate in Gen2 mode by default */ p->phb.ops = &p7ioc_phb_ops; p->phb.phb_type = phb_type_pcie_v2; p->regs_asb = ioc->regs + PHBn_ASB_BASE(index); p->regs = ioc->regs + PHBn_AIB_BASE(index); p->buid_lsi = buid_base + PHB_BUID_LSI_OFFSET; p->buid_msi = buid_base + PHB_BUID_MSI_OFFSET; p->io_base = ioc->mmio1_win_start + PHBn_IO_BASE(index); p->m32_base = ioc->mmio2_win_start + PHBn_M32_BASE(index); p->m64_base = ioc->mmio2_win_start + PHBn_M64_BASE(index); p->phb.scan_map = 0x1; /* Only device 0 to scan */ /* Find P7IOC base location code in IOC */ p->phb.base_loc_code = dt_prop_get_def(ioc->dt_node, "ibm,io-base-loc-code", NULL); if (!p->phb.base_loc_code) prerror("P7IOC: Base location code not found !\n"); /* Create device node for PHB */ p7ioc_pcie_add_node(p); /* Register OS interrupt sources */ register_irq_source(&p7ioc_msi_irq_ops, p, p->buid_msi << 4, 256); register_irq_source(&p7ioc_lsi_irq_ops, p, p->buid_lsi << 4, 8); /* Initialize IODA table caches */ p7ioc_phb_init_ioda_cache(p); /* We register the PHB before we initialize it so we * 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#%x slot\n", p->phb.opal_id); /* Platform additional setup */ if (platform.pci_setup_phb) platform.pci_setup_phb(&p->phb, p->index); } static bool p7ioc_phb_wait_dlp_reset(struct p7ioc_phb *p) { unsigned int i; uint64_t val; /* * Firmware cannot access the UTL core regs or PCI config space * until the cores are out of DL_PGRESET. * DL_PGRESET should be polled until it is inactive with a value * of '0'. The recommended polling frequency is once every 1ms. * Firmware should poll at least 200 attempts before giving up. * MMIO Stores to the link are silently dropped by the UTL core if * the link is down. * MMIO Loads to the link will be dropped by the UTL core and will * eventually time-out and will return an all ones response if the * link is down. */ #define DLP_RESET_ATTEMPTS 400 printf("P7IOC: Waiting for DLP PG reset to complete...\n"); for (i = 0; i < DLP_RESET_ATTEMPTS; i++) { val = in_be64(p->regs + PHB_PCIE_DLP_TRAIN_CTL); if (!(val & PHB_PCIE_DLP_TC_DL_PGRESET)) break; time_wait_ms(1); } if (val & PHB_PCIE_DLP_TC_DL_PGRESET) { PHBERR(p, "Timeout waiting for DLP PG reset !\n"); return false; } return true; } /* p7ioc_phb_init_rc - Initialize the Root Complex config space */ static bool p7ioc_phb_init_rc_cfg(struct p7ioc_phb *p) { int64_t ecap, aercap; /* XXX Handle errors ? */ /* Init_51..51: * * Set primary bus to 0, secondary to 1 and subordinate to 0xff */ p7ioc_pcicfg_write32(&p->phb, 0, PCI_CFG_PRIMARY_BUS, 0x00ff0100); /* Init_52..57 * * IO and Memory base & limits are set to base > limit, which * allows all inbounds. * * XXX This has the potential of confusing the OS which might * think that nothing is forwarded downstream. We probably need * to fix this to match the IO and M32 PHB windows */ p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_IO_BASE, 0x0010); p7ioc_pcicfg_write32(&p->phb, 0, PCI_CFG_MEM_BASE, 0x00000010); p7ioc_pcicfg_write32(&p->phb, 0, PCI_CFG_PREF_MEM_BASE, 0x00000010); /* Init_58..: Setup bridge control to enable forwarding of CORR, FATAL, * and NONFATAL errors */ p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_BRCTL, PCI_CFG_BRCTL_SERR_EN); /* Init_60..61 * * PCIE Device control/status, enable error reporting, disable relaxed * ordering, set MPS to 128 (see note), clear errors. * * Note: The doc recommends to set MPS to 4K. This has proved to have * some issues as it requires specific claming of MRSS on devices and * we've found devices in the field that misbehave when doing that. * * We currently leave it all to 128 bytes (minimum setting) at init * time. The generic PCIe probing later on might apply a different * value, or the kernel will, but we play it safe at early init */ if (p->ecap <= 0) { ecap = pci_find_cap(&p->phb, 0, PCI_CFG_CAP_ID_EXP); if (ecap < 0) { PHBERR(p, "Can't locate PCI-E capability\n"); return false; } p->ecap = ecap; } else { ecap = p->ecap; } p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DEVSTAT, PCICAP_EXP_DEVSTAT_CE | PCICAP_EXP_DEVSTAT_NFE | PCICAP_EXP_DEVSTAT_FE | PCICAP_EXP_DEVSTAT_UE); p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DEVCTL, PCICAP_EXP_DEVCTL_CE_REPORT | PCICAP_EXP_DEVCTL_NFE_REPORT | PCICAP_EXP_DEVCTL_FE_REPORT | PCICAP_EXP_DEVCTL_UR_REPORT | SETFIELD(PCICAP_EXP_DEVCTL_MPS, 0, PCIE_MPS_128B)); /* Init_62..63 * * Root Control Register. Enable error reporting * * Note: Added CRS visibility. */ p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_RC, PCICAP_EXP_RC_SYSERR_ON_CE | PCICAP_EXP_RC_SYSERR_ON_NFE | PCICAP_EXP_RC_SYSERR_ON_FE | PCICAP_EXP_RC_CRS_VISIBLE); /* Init_64..65 * * Device Control 2. Enable ARI fwd, set timer */ p7ioc_pcicfg_write16(&p->phb, 0, ecap + PCICAP_EXP_DCTL2, SETFIELD(PCICAP_EXP_DCTL2_CMPTOUT, 0, 2) | PCICAP_EXP_DCTL2_ARI_FWD); /* Init_66..81 * * AER inits */ aercap = pci_find_ecap(&p->phb, 0, PCIECAP_ID_AER, NULL); if (aercap < 0) { /* Shouldn't happen */ PHBERR(p, "Failed to locate AER capability in bridge\n"); return false; } p->aercap = aercap; /* Clear all UE status */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_STATUS, 0xffffffff); /* Disable some error reporting as per the P7IOC spec */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_MASK, PCIECAP_AER_UE_POISON_TLP | PCIECAP_AER_UE_COMPL_TIMEOUT | PCIECAP_AER_UE_COMPL_ABORT | PCIECAP_AER_UE_ECRC); /* Report some errors as fatal */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_UE_SEVERITY, PCIECAP_AER_UE_DLP | PCIECAP_AER_UE_SURPRISE_DOWN | PCIECAP_AER_UE_FLOW_CTL_PROT | PCIECAP_AER_UE_UNEXP_COMPL | PCIECAP_AER_UE_RECV_OVFLOW | PCIECAP_AER_UE_MALFORMED_TLP); /* Clear all CE status */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CE_STATUS, 0xffffffff); /* Disable some error reporting as per the P7IOC spec */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CE_MASK, PCIECAP_AER_CE_ADV_NONFATAL); /* Enable ECRC generation & checking */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_CAPCTL, PCIECAP_AER_CAPCTL_ECRCG_EN | PCIECAP_AER_CAPCTL_ECRCC_EN); /* Enable reporting in root error control */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_RERR_CMD, PCIECAP_AER_RERR_CMD_FE | PCIECAP_AER_RERR_CMD_NFE | PCIECAP_AER_RERR_CMD_CE); /* Clear root error status */ p7ioc_pcicfg_write32(&p->phb, 0, aercap + PCIECAP_AER_RERR_STA, 0xffffffff); return true; } static void p7ioc_phb_init_utl(struct p7ioc_phb *p) { /* Init_82..84: Clear spurious errors and assign errors to the * right "interrupt" signal */ out_be64(p->regs + UTL_SYS_BUS_AGENT_STATUS, 0xffffffffffffffffUL); out_be64(p->regs + UTL_SYS_BUS_AGENT_ERR_SEVERITY, 0x0000000000000000UL); out_be64(p->regs + UTL_SYS_BUS_AGENT_IRQ_EN, 0xac80000000000000UL); /* Init_85..89: Setup buffer allocations */ out_be64(p->regs + UTL_OUT_POST_DAT_BUF_ALLOC, 0x0400000000000000UL); out_be64(p->regs + UTL_IN_POST_HDR_BUF_ALLOC, 0x1000000000000000UL); out_be64(p->regs + UTL_IN_POST_DAT_BUF_ALLOC, 0x4000000000000000UL); out_be64(p->regs + UTL_PCIE_TAGS_ALLOC, 0x0800000000000000UL); out_be64(p->regs + UTL_GBIF_READ_TAGS_ALLOC, 0x0800000000000000UL); /* Init_90: PCI Express port control */ out_be64(p->regs + UTL_PCIE_PORT_CONTROL, 0x8480000000000000UL); /* Init_91..93: Clean & setup port errors */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xff7fffffffffffffUL); out_be64(p->regs + UTL_PCIE_PORT_ERROR_SEV, 0x00e0000000000000UL); out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0x7e65000000000000UL); /* Init_94 : Cleanup RC errors */ out_be64(p->regs + UTL_RC_STATUS, 0xffffffffffffffffUL); } static void p7ioc_phb_init_errors(struct p7ioc_phb *p) { /* Init_98: LEM Error Mask : Temporarily disable error interrupts */ out_be64(p->regs + PHB_LEM_ERROR_MASK, 0xffffffffffffffffUL); /* Init_99..107: Configure main error traps & clear old state */ out_be64(p->regs + PHB_ERR_STATUS, 0xffffffffffffffffUL); out_be64(p->regs + PHB_ERR1_STATUS, 0x0000000000000000UL); out_be64(p->regs + PHB_ERR_LEM_ENABLE, 0xffffffffefffffffUL); out_be64(p->regs + PHB_ERR_FREEZE_ENABLE, 0x0000000061c00000UL); out_be64(p->regs + PHB_ERR_AIB_FENCE_ENABLE, 0xffffffc58c000000UL); out_be64(p->regs + PHB_ERR_LOG_0, 0x0000000000000000UL); out_be64(p->regs + PHB_ERR_LOG_1, 0x0000000000000000UL); out_be64(p->regs + PHB_ERR_STATUS_MASK, 0x0000000000000000UL); out_be64(p->regs + PHB_ERR1_STATUS_MASK, 0x0000000000000000UL); /* Init_108_116: Configure MMIO error traps & clear old state */ out_be64(p->regs + PHB_OUT_ERR_STATUS, 0xffffffffffffffffUL); out_be64(p->regs + PHB_OUT_ERR1_STATUS, 0x0000000000000000UL); out_be64(p->regs + PHB_OUT_ERR_LEM_ENABLE, 0xffffffffffffffffUL); out_be64(p->regs + PHB_OUT_ERR_FREEZE_ENABLE, 0x0000430803000000UL); out_be64(p->regs + PHB_OUT_ERR_AIB_FENCE_ENABLE, 0x9df3bc00f0f0700fUL); out_be64(p->regs + PHB_OUT_ERR_LOG_0, 0x0000000000000000UL); out_be64(p->regs + PHB_OUT_ERR_LOG_1, 0x0000000000000000UL); out_be64(p->regs + PHB_OUT_ERR_STATUS_MASK, 0x0000000000000000UL); out_be64(p->regs + PHB_OUT_ERR1_STATUS_MASK, 0x0000000000000000UL); /* Init_117_125: Configure DMA_A error traps & clear old state */ out_be64(p->regs + PHB_INA_ERR_STATUS, 0xffffffffffffffffUL); out_be64(p->regs + PHB_INA_ERR1_STATUS, 0x0000000000000000UL); out_be64(p->regs + PHB_INA_ERR_LEM_ENABLE, 0xffffffffffffffffUL); out_be64(p->regs + PHB_INA_ERR_FREEZE_ENABLE, 0xc00003ff01006000UL); out_be64(p->regs + PHB_INA_ERR_AIB_FENCE_ENABLE, 0x3fff50007e559fd8UL); out_be64(p->regs + PHB_INA_ERR_LOG_0, 0x0000000000000000UL); out_be64(p->regs + PHB_INA_ERR_LOG_1, 0x0000000000000000UL); out_be64(p->regs + PHB_INA_ERR_STATUS_MASK, 0x0000000000000000UL); out_be64(p->regs + PHB_INA_ERR1_STATUS_MASK, 0x0000000000000000UL); /* Init_126_134: Configure DMA_B error traps & clear old state */ out_be64(p->regs + PHB_INB_ERR_STATUS, 0xffffffffffffffffUL); out_be64(p->regs + PHB_INB_ERR1_STATUS, 0x0000000000000000UL); out_be64(p->regs + PHB_INB_ERR_LEM_ENABLE, 0xffffffffffffffffUL); out_be64(p->regs + PHB_INB_ERR_FREEZE_ENABLE, 0x0000000000000000UL); out_be64(p->regs + PHB_INB_ERR_AIB_FENCE_ENABLE, 0x18ff80ffff7f0000UL); out_be64(p->regs + PHB_INB_ERR_LOG_0, 0x0000000000000000UL); out_be64(p->regs + PHB_INB_ERR_LOG_1, 0x0000000000000000UL); out_be64(p->regs + PHB_INB_ERR_STATUS_MASK, 0x0000000000000000UL); out_be64(p->regs + PHB_INB_ERR1_STATUS_MASK, 0x0000000000000000UL); /* Init_135..138: Cleanup & configure LEM */ out_be64(p->regs + PHB_LEM_FIR_ACCUM, 0x0000000000000000UL); out_be64(p->regs + PHB_LEM_ACTION0, 0xffffffffffffffffUL); out_be64(p->regs + PHB_LEM_ACTION1, 0x0000000000000000UL); out_be64(p->regs + PHB_LEM_WOF, 0x0000000000000000UL); } /* p7ioc_phb_init - Initialize the PHB hardware * * This is currently only called at boot time. It will eventually * be called at runtime, for example in some cases of error recovery * after a PHB reset in which case we might need locks etc... */ int64_t p7ioc_phb_init(struct p7ioc_phb *p) { uint64_t val; PHBDBG(p, "Initializing PHB %x...\n", p->index); /* * We re-init the PHB on a creset (and a few other cases) * so clear the broken flag */ p->broken = false; /* For some reason, the doc wants us to read the version * register, so let's do it. We shoud probably check that * the value makes sense... */ val = in_be64(p->regs_asb + PHB_VERSION); p->rev = ((val >> 16) & 0xffff) | (val & 0xffff); PHBDBG(p, "PHB version: %08x\n", p->rev); /* * Configure AIB operations * * This register maps upbound commands to AIB channels. * DMA Write=0, DMA Read=2, MMIO Load Response=1, * Interrupt Request=1, TCE Read=3. */ /* Init_1: AIB TX Channel Mapping */ out_be64(p->regs_asb + PHB_AIB_TX_CHAN_MAPPING, 0x0211300000000000UL); /* * This group of steps initializes the AIB RX credits for * the CI block’s port that is attached to this PHB. * * Channel 0 (Dkill): 32 command credits, 0 data credits * (effectively infinite command credits) * Channel 1 (DMA/TCE Read Responses): 32 command credits, 32 data * credits (effectively infinite * command and data credits) * Channel 2 (Interrupt Reissue/Return): 32 command, 0 data credits * (effectively infinite * command credits) * Channel 3 (MMIO Load/Stores, EOIs): 1 command, 1 data credit */ /* Init_2: AIB RX Command Credit */ out_be64(p->regs_asb + PHB_AIB_RX_CMD_CRED, 0x0020002000200001UL); /* Init_3: AIB RX Data Credit */ out_be64(p->regs_asb + PHB_AIB_RX_DATA_CRED, 0x0000002000000001UL); /* Init_4: AXIB RX Credit Init Timer */ out_be64(p->regs_asb + PHB_AIB_RX_CRED_INIT_TIMER, 0xFF00000000000000UL); /* * Enable all 32 AIB and TCE tags. * * AIB tags are used for DMA read requests. * TCE tags are used for every internal transaction as well as TCE * read requests. */ /* Init_5: PHB - AIB Tag Enable Register */ out_be64(p->regs_asb + PHB_AIB_TAG_ENABLE, 0xFFFFFFFF00000000UL); /* Init_6: PHB – TCE Tag Enable Register */ out_be64(p->regs_asb + PHB_TCE_TAG_ENABLE, 0xFFFFFFFF00000000UL); /* Init_7: PCIE - System Configuration Register * * This is the default value out of reset. This register can be * modified to change the following fields if needed: * * bits 04:09 - SYS_EC0C_MAXLINKWIDTH[5:0] * The default link width is x8. This can be reduced * to x1 or x4, if needed. * * bits 10:12 - SYS_EC04_MAX_PAYLOAD[2:0] * * The default max payload size is 4KB. This can be * reduced to the allowed ranges from 128B * to 2KB if needed. */ out_be64(p->regs + PHB_PCIE_SYSTEM_CONFIG, 0x422800FC20000000UL); /* Init_8: PHB - PCI-E Reset Register * * This will deassert reset for the PCI-E cores, including the * PHY and HSS macros. The TLDLP core will begin link training * shortly after this register is written. * This will also assert reset for the internal scan-only error * report macros. The error report macro reset will be deasserted * in a later step. * Firmware will verify in a later step whether the PCI-E link * has been established. * * NOTE: We perform a PERST at the end of the init sequence so * we could probably skip that link training. */ out_be64(p->regs + PHB_RESET, 0xE800000000000000UL); /* Init_9: BUID * * Only the top 5 bit of the MSI field are implemented, the bottom * are always 0. Our buid_msi value should also be a multiple of * 16 so it should all fit well */ val = SETFIELD(PHB_BUID_LSI, 0ul, P7_BUID_BASE(p->buid_lsi)); val |= SETFIELD(PHB_BUID_MSI, 0ul, P7_BUID_BASE(p->buid_msi)); out_be64(p->regs + PHB_BUID, val); /* Init_10..12: IO Space */ out_be64(p->regs + PHB_IO_BASE_ADDR, p->io_base); out_be64(p->regs + PHB_IO_BASE_MASK, ~(PHB_IO_SIZE - 1)); out_be64(p->regs + PHB_IO_START_ADDR, 0); /* Init_13..15: M32 Space */ out_be64(p->regs + PHB_M32_BASE_ADDR, p->m32_base + M32_PCI_START); out_be64(p->regs + PHB_M32_BASE_MASK, ~(M32_PCI_SIZE - 1)); out_be64(p->regs + PHB_M32_START_ADDR, M32_PCI_START); /* Init_16: PCIE-E Outbound Request Upper Address */ out_be64(p->regs + PHB_M64_UPPER_BITS, 0); /* Init_17: PCIE-E PHB2 Configuration * * We enable IO, M32, 32-bit MSI and 64-bit MSI */ out_be64(p->regs + PHB_PHB2_CONFIG, PHB_PHB2C_32BIT_MSI_EN | PHB_PHB2C_IO_EN | PHB_PHB2C_64BIT_MSI_EN | PHB_PHB2C_M32_EN | PHB_PHB2C_64B_TCE_EN); /* Init_18..xx: Reset all IODA tables */ p7ioc_ioda_reset(&p->phb, false); /* Init_42..47: Clear UTL & DLP error log regs */ out_be64(p->regs + PHB_PCIE_UTL_ERRLOG1, 0xffffffffffffffffUL); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG2, 0xffffffffffffffffUL); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG3, 0xffffffffffffffffUL); out_be64(p->regs + PHB_PCIE_UTL_ERRLOG4, 0xffffffffffffffffUL); out_be64(p->regs + PHB_PCIE_DLP_ERRLOG1, 0xffffffffffffffffUL); out_be64(p->regs + PHB_PCIE_DLP_ERRLOG2, 0xffffffffffffffffUL); /* Init_48: Wait for DLP core to be out of reset */ if (!p7ioc_phb_wait_dlp_reset(p)) goto failed; /* Init_49 - Clear port status */ out_be64(p->regs + UTL_PCIE_PORT_STATUS, 0xffffffffffffffffUL); /* Init_50..81: Init root complex config space */ if (!p7ioc_phb_init_rc_cfg(p)) goto failed; /* Init_82..94 : Init UTL */ p7ioc_phb_init_utl(p); /* Init_95: PCI-E Reset, deassert reset for internal error macros */ out_be64(p->regs + PHB_RESET, 0xe000000000000000UL); /* Init_96: PHB Control register. Various PHB settings: * * - Enable ECC for various internal RAMs * - Enable all TCAM entries * - Set failed DMA read requests to return Completer Abort on error */ out_be64(p->regs + PHB_CONTROL, 0x7f38000000000000UL); /* Init_97: Legacy Control register * * The spec sets bit 0 to enable DKill to flush the TCEs. We do not * use that mechanism however, we require the OS to directly access * the TCE Kill register, so we leave that bit set to 0 */ out_be64(p->regs + PHB_LEGACY_CTRL, 0x0000000000000000); /* Init_98..138 : Setup error registers */ p7ioc_phb_init_errors(p); /* Init_139: Read error summary */ val = in_be64(p->regs + PHB_ETU_ERR_SUMMARY); if (val) { PHBERR(p, "Errors detected during PHB init: 0x%16llx\n", val); goto failed; } /* Steps Init_140..142 have been removed from the spec. */ /* Init_143..144: Enable IO, MMIO, Bus master etc... and clear * status bits */ p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_STAT, PCI_CFG_STAT_SENT_TABORT | PCI_CFG_STAT_RECV_TABORT | PCI_CFG_STAT_RECV_MABORT | PCI_CFG_STAT_SENT_SERR | PCI_CFG_STAT_RECV_PERR); p7ioc_pcicfg_write16(&p->phb, 0, PCI_CFG_CMD, PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_PERR_RESP | PCI_CFG_CMD_BUS_MASTER_EN | PCI_CFG_CMD_MEM_EN | PCI_CFG_CMD_IO_EN); /* At this point, the spec suggests doing a bus walk. However we * haven't powered up the slots with the SHCP controller. We'll * deal with that and link training issues later, for now, let's * enable the full range of error detection */ /* Init_145..149: Enable error interrupts and LEM */ out_be64(p->regs + PHB_ERR_IRQ_ENABLE, 0x0000000061c00000UL); out_be64(p->regs + PHB_OUT_ERR_IRQ_ENABLE, 0x0000430803000000UL); out_be64(p->regs + PHB_INA_ERR_IRQ_ENABLE, 0xc00003ff01006000UL); out_be64(p->regs + PHB_INB_ERR_IRQ_ENABLE, 0x0000000000000000UL); out_be64(p->regs + PHB_LEM_ERROR_MASK, 0x1249a1147f500f2cUL); /* Init_150: Enable DMA read/write TLP address speculation */ out_be64(p->regs + PHB_TCE_PREFETCH, 0x0000c00000000000UL); /* Init_151..152: Set various timeouts */ out_be64(p->regs + PHB_TIMEOUT_CTRL1, 0x1611112010200000UL); out_be64(p->regs + PHB_TIMEOUT_CTRL2, 0x0000561300000000UL); return OPAL_SUCCESS; failed: PHBERR(p, "Initialization failed\n"); p->broken = true; return OPAL_HARDWARE; } void p7ioc_phb_reset(struct phb *phb) { struct p7ioc_phb *p = phb_to_p7ioc_phb(phb); struct p7ioc *ioc = p->ioc; uint64_t ci_idx, rreg; unsigned int i; bool fenced; /* Check our fence status. The fence bits we care about are * two bits per PHB at IBM bit location 14 and 15 + 4*phb */ fenced = p7ioc_phb_fenced(p); PHBDBG(p, "PHB reset... (fenced: %d)\n", (int)fenced); /* * If not fenced and already functional, let's do an IODA reset * to clear pending DMAs and wait a bit for thing to settle. It's * notable that the IODA table cache won't be emptied so that we * can restore them during error recovery. */ if (!p->broken && !fenced) { PHBDBG(p, " ioda reset ...\n"); p7ioc_ioda_reset(&p->phb, false); time_wait_ms(100); } /* CI port index */ ci_idx = p->index + 2; /* Reset register bits for this PHB */ rreg = 0;/*PPC_BIT(8 + ci_idx * 2);*/ /* CI port config reset */ rreg |= PPC_BIT(9 + ci_idx * 2); /* CI port func reset */ rreg |= PPC_BIT(32 + p->index); /* PHBn config reset */ /* Mask various errors during reset and clear pending errors */ out_be64(ioc->regs + P7IOC_CIn_LEM_ERR_MASK(ci_idx), 0xa4f4000000000000ul); out_be64(p->regs_asb + PHB_LEM_ERROR_MASK, 0xadb650c9808dd051ul); out_be64(ioc->regs + P7IOC_CIn_LEM_FIR(ci_idx), 0); /* We need to retry in case the fence doesn't lift due to a * problem with lost credits (HW guys). How many times ? */ #define MAX_PHB_RESET_RETRIES 5 for (i = 0; i < MAX_PHB_RESET_RETRIES; i++) { PHBDBG(p, " reset try %d...\n", i); /* Apply reset */ out_be64(ioc->regs + P7IOC_CCRR, rreg); time_wait_ms(1); out_be64(ioc->regs + P7IOC_CCRR, 0); /* Check if fence lifed */ fenced = p7ioc_phb_fenced(p); PHBDBG(p, " fenced: %d...\n", (int)fenced); if (!fenced) break; } /* Reset failed, not much to do, maybe add an error return */ if (fenced) { PHBERR(p, "Reset failed, fence still set !\n"); p->broken = true; return; } /* Wait a bit */ time_wait_ms(100); /* Re-initialize the PHB */ p7ioc_phb_init(p); /* Restore the CI error mask */ out_be64(ioc->regs + P7IOC_CIn_LEM_ERR_MASK_AND(ci_idx), 0); }