diff options
Diffstat (limited to 'src/usr/hwas/hwasPlat.C')
-rw-r--r-- | src/usr/hwas/hwasPlat.C | 548 |
1 files changed, 532 insertions, 16 deletions
diff --git a/src/usr/hwas/hwasPlat.C b/src/usr/hwas/hwasPlat.C index bea5b4d8f..1cea60415 100644 --- a/src/usr/hwas/hwasPlat.C +++ b/src/usr/hwas/hwasPlat.C @@ -43,13 +43,16 @@ #include <sys/misc.h> #include <pnor/pnorif.H> +#include <fapiwrap/fapiWrapif.H> #include <hwas/common/hwas_reasoncodes.H> #include <targeting/common/utilFilter.H> #include <fsi/fsiif.H> -#include <config.h> #include <targeting/common/targetservice.H> #include <chipids.H> +#include <vpd/spdenums.H> + +#include <map> #ifdef CONFIG_SUPPORT_EEPROM_CACHING #include <i2c/eepromif.H> @@ -85,13 +88,19 @@ errlHndl_t platReadIDEC(const TargetHandle_t &i_target) // Call over to the target-specific layer since every chip can have // unique registers size_t sz = 0; - errlHndl_t l_errl = - DeviceFW::deviceWrite(i_target, - nullptr, - sz, - DEVICE_IDEC_ADDRESS()); + errlHndl_t errl = nullptr; - return l_errl; + // Pass a 1 as va_arg to signal phase 1 of ocmbIDEC to execute. + // Other IDEC functions will ignore this argument. + const uint64_t Phase1 = 1; + errl = DeviceFW::deviceWrite(i_target, + nullptr, + sz, + DEVICE_IDEC_ADDRESS(), + Phase1); + + + return errl; } /** @@ -234,6 +243,70 @@ DEVICE_REGISTER_ROUTE(DeviceFW::WRITE, TARGETING::TYPE_MEMBUF, cfamIDEC); +/** + * @brief During early IPL the OCMB isn't able to be read from so this function, + * executed during discover targets, will read from the SPD and set the + * CHIP_ID, EC, and HDAT_EC attributes with what is found there. + * + * @param[in] i_target Presence detect target + * + * @return errlHndl_t An error log if reading from the SPD failed. + * Otherwise, other errors are predictive and + * committed. So nullptr will be returned in those + * cases and on success. + */ +errlHndl_t ocmbIdecPhase1(const TARGETING::TargetHandle_t& i_target); + +/** + * @brief Once the OCMB is able to be read from the second phase will execute + * and cross-check the data given from the SPD is consistent with what + * was read from the chip itself. If the data is not consistent then the + * CHIP_ID, EC, and HDAT_EC attributes will be updated with what was + * found from the OCMB read since that data would be correct. + * + * @param[in] i_target Presence detect target + * + * @return errlHndl_t An error log if reading from the OCMB ID/EC + * register failed. Otherwise, other errors are + * predictive and committed. So nullptr will be + * returned in those cases and on success. + */ +errlHndl_t ocmbIdecPhase2(const TARGETING::TargetHandle_t& i_target); + +/** + * @brief Read the chipid and EC/DD-level for OCMB chips and set the attributes. + * In this function there are two phases that are executed at different + * times during IPL. The OCMB is held in reset and unable to be read from + * during early IPL. So the first phase, executed during discover + * targets, will read from the SPD and set the attributes with what is + * found there. Once the OCMB is able to be read from the second phase + * will execute and cross-check the data given from the SPD is consistent + * with what was read from the chip itself. If the data is not consistent + * then the attributes will be updated with what was found from the OCMB + * read since that data would be correct. + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * + * @param[in] i_target Presence detect target + * + * @param[in/out] io_buffer Unused by this function + * + * @param[in/out] io_buflen Unused by this function + * + * @param[in] i_accessType DeviceFW::AccessType enum (userif.H) + * + * @param[in] i_args This is an argument list for DD framework. + * In this function, there is one argument to + * signal which phase to execute. + * + * @return errlHndl_t If there is an issue while reading from the SPD + * or the OCMB chip, or an unexpected memory + * interface type then this function will return an + * error. Otherwise, all other errors are + * predictive and committed. So nullptr will be + * returned in that case or on success. + */ errlHndl_t ocmbIDEC(DeviceFW::OperationType i_opType, TARGETING::Target* i_target, void* io_buffer, @@ -241,16 +314,456 @@ errlHndl_t ocmbIDEC(DeviceFW::OperationType i_opType, int64_t i_accessType, va_list i_args) { - // for now just hardcode the answer to something explicitly invalid - uint8_t l_ec = INVALID__ATTR_EC; - i_target->setAttr<TARGETING::ATTR_EC>(l_ec); - i_target->setAttr<TARGETING::ATTR_HDAT_EC>(l_ec); + errlHndl_t error = nullptr; + + // Determine which phase of this function to run. + uint64_t phase = va_arg(i_args, uint64_t); + + // Execute the correct phase based on the va_arg given. + if (phase == 1) + { + error = ocmbIdecPhase1(i_target); + } + else + { + error = ocmbIdecPhase2(i_target); + } + + + return error; +} + +/** + * @brief This is a small helper function that the ocmb IDEC functions use to + * add all the proper callouts and commit errorlogs. + * + * @param[in] i_target Presence detect target + * + * @param[in] io_error The error log to be committed + * + */ +void ocmbErrlCommit(const TARGETING::TargetHandle_t& i_target, + errlHndl_t& io_error) +{ + io_error->addHwCallout(i_target, + SRCI_PRIORITY_HIGH, + NO_DECONFIG, + GARD_NULL); + + io_error->addPartCallout(i_target, + VPD_PART_TYPE, + SRCI_PRIORITY_MED, + NO_DECONFIG, + GARD_NULL); + + io_error->addProcedureCallout(EPUB_PRC_HB_CODE, + SRCI_PRIORITY_LOW); - // we can assume this is an Explorer chip though - uint32_t l_id = POWER_CHIPID::EXPLORER_16; - i_target->setAttr<TARGETING::ATTR_CHIP_ID>(l_id); + ERRORLOG::errlCommit(io_error, HWAS_COMP_ID); + +} + +/** + * @brief This helper function will lookup the chip id and ec levels of + * a given OCMB based on what is found in a provided SPD buffer. + * The target is passed along for trace information. + * + * @param[in] i_target OCMB target we are looking up IDEC for + * + * @param[in] i_spdBuffer Buffer of at least SPD::OCMB_SPD_EFD_COMBINED_SIZE + * bytes of the given OCMB's SPD + * + * @param[out] o_chipId Chip Id associated with the given OCMB + * (see src/import/chips/common/utils/chipids.H) + * + * @param[out] o_ec EC level associated with the given OCMB + * + * @return nullptr if success, error log otherwise + * + */ +errlHndl_t getOcmbIdecFromSpd(const TARGETING::TargetHandle_t& i_target, + uint8_t * i_spdBuffer, + uint16_t& o_chipId, + uint8_t& o_ec) +{ + errlHndl_t l_errl = nullptr; + // These bytes are used for FFDC and verification purposes. + const size_t SPD_REVISION_OFFSET = 1; + const size_t DRAM_INTERFACE_TYPE_OFFSET = 2; + const size_t MEMORY_MODULE_INTERFACE_TYPE_OFFSET = 3; + + // This is the value that signifies the SPD we read is for a DDIMM. + const uint8_t DDIMM_MEMORY_INTERFACE_TYPE = 0x0A; + + const uint8_t l_spdModuleRevision = + *(i_spdBuffer + SPD_REVISION_OFFSET); + + const uint8_t l_spdDRAMInterfaceType = + *(i_spdBuffer + DRAM_INTERFACE_TYPE_OFFSET); + + const uint8_t l_spdMemoryInterfaceType = + *(i_spdBuffer + MEMORY_MODULE_INTERFACE_TYPE_OFFSET); + + // Byte 1 SPD Module Revision + // Byte 2 DRAM Interface Type Presented or Emulated + // Byte 3 Memory Module Interface Type + const uint32_t SPD_FFDC_BYTES = TWO_UINT16_TO_UINT32( + TWO_UINT8_TO_UINT16(l_spdModuleRevision, l_spdDRAMInterfaceType), + TWO_UINT8_TO_UINT16(l_spdMemoryInterfaceType, 0)); + + do{ + + // Since the byte offsets used to get the IDEC info out of the SPD are + // specific to the DDIMM interface type we must first verify that we + // read from an SPD of that type. + if (DDIMM_MEMORY_INTERFACE_TYPE != l_spdMemoryInterfaceType) + { + HWAS_ERR("getOcmbIdecFromSpd> memory module interface type " + "didn't match the expected type. " + "Expected 0x%.2X, Actual 0x%.2X", + DDIMM_MEMORY_INTERFACE_TYPE, + l_spdMemoryInterfaceType); + + /*@ + * @errortype + * @severity ERRL_SEV_UNRECOVERABLE + * @moduleid MOD_OCMB_IDEC + * @reasoncode RC_OCMB_INTERFACE_TYPE_MISMATCH + * @userdata1[0:7] SPD Module Revision + * @userdata1[8:15] DRAM Interface Type Presented or Emulated + * @userdata1[16:23] Memory Module Interface Type + * @userdata1[24:31] Unused + * @userdata1[32:63] Expected memory interface type + * @userdata2 HUID of OCMB target + * @devdesc The memory interface type read from the SPD did + * not match the DDIMM value. Setting the + * appropriate IDEC values for this target cannot + * continue. + * @custdesc Invalid or unsupported memory card installed. + */ + l_errl = hwasError(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + MOD_OCMB_IDEC, + RC_OCMB_INTERFACE_TYPE_MISMATCH, + TWO_UINT32_TO_UINT64(SPD_FFDC_BYTES, + DDIMM_MEMORY_INTERFACE_TYPE), + TARGETING::get_huid(i_target)); + + l_errl->addProcedureCallout(EPUB_PRC_HB_CODE, + SRCI_PRIORITY_LOW); + + l_errl->addHwCallout(i_target, + SRCI_PRIORITY_HIGH, + NO_DECONFIG, + GARD_NULL); + + + break; + } + + // SPD IDEC info is in the following three bytes + const size_t SPD_ID_LEAST_SIGNIFICANT_BYTE_OFFSET = 198; + const size_t SPD_ID_MOST_SIGNIFICANT_BYTE_OFFSET = 199; + const size_t DMB_REV_OFFSET = 200; + + // Get the ID from the SPD and verify that it matches what we read from + // the IDEC register. + uint16_t l_spdId = TWO_UINT8_TO_UINT16( + *(i_spdBuffer + SPD_ID_LEAST_SIGNIFICANT_BYTE_OFFSET), + *(i_spdBuffer + SPD_ID_MOST_SIGNIFICANT_BYTE_OFFSET)); + + // Bytes 200 of the SPD contains the DMB Revision, this is essentially the + // OCMB manufacture's version of the chip. The manufacture can define any + // format for this field and we must add special logic to convert the + // manufacture's DMB_REV to the EC level IBM is familiar with. + uint8_t l_spdDmbRev = *(i_spdBuffer + DMB_REV_OFFSET); + + HWAS_INF("getOcmbIdecFromSpd> OCMB 0x%.8x l_spdId = 0x%.4X l_spdDmbRev = 0x%.2x", + TARGETING::get_huid(i_target), l_spdId, l_spdDmbRev); + + if (DDIMM_DMB_ID::EXPLORER == l_spdId) + { + o_chipId = POWER_CHIPID::EXPLORER_16; + // Must convert Explorer's versioning into IBM-style EC levels. + // Explorer vendor has stated versioning will start at 0xA0 and increment + // 1st nibble for major revisions and 2nd nibble by 1 for minor revisions + // Examples : + // Version 0xA0 = EC 0x10 + // Version 0xA1 = EC 0x11 + // Version 0xB2 = EC 0x22 + + // Resulting formula from pattern in examples above is as follows: + o_ec = (l_spdDmbRev - 0x90); + } + else if (DDIMM_DMB_ID::GEMINI == l_spdId) + { + o_chipId = POWER_CHIPID::GEMINI_16; + + HWAS_ASSERT(l_spdDmbRev == 0x0, + "Invalid Gemini DMB Revision Number, expected to find 0x0 at byte 200 in Gemini SPD"); + + // 0x10 is the only valid EC level for Gemini cards. If we find 0x0 @ byte 200 in + // the Gemini SPD then we will return 0x10 as the EC level + o_ec = 0x10; + } + else + { + HWAS_ERR("getOcmbIdecFromSpd> Unknown OCMB chip type discovered in SPD " + "ID=0x%.4X OCMB HUID 0x%.8x", + l_spdId, + TARGETING::get_huid(i_target)); + + /*@ + * @errortype + * @severity ERRL_SEV_PREDICTIVE + * @moduleid MOD_OCMB_IDEC + * @reasoncode RC_OCMB_UNKNOWN_CHIP_TYPE + * @userdata1[0:7] SPD Module Revision + * @userdata1[8:15] DRAM Interface Type Presented or Emulated + * @userdata1[16:23] Memory Module Interface Type + * @userdata1[24:31] Unused + * @userdata1[32:63] SPD Chip Id + * @userdata2 HUID of OCMB target + * @devdesc The ID read from the SPD didn't match any known + * OCMB chip types. + * @custdesc Unsupported memory installed. + */ + l_errl = hwasError(ERRORLOG::ERRL_SEV_PREDICTIVE, + MOD_OCMB_IDEC_PHASE_1, + RC_OCMB_UNKNOWN_CHIP_TYPE, + TWO_UINT32_TO_UINT64(SPD_FFDC_BYTES, l_spdId), + TARGETING::get_huid(i_target)); + + break; + } + + }while(0); + + return l_errl; + +} + + +errlHndl_t ocmbIdecPhase1(const TARGETING::TargetHandle_t& i_target) +{ + errlHndl_t l_errl = nullptr; + + // Allocate buffer to hold SPD and init to 0 + size_t l_spdBufferSize = SPD::DDIMM_DDR4_SPD_SIZE; + uint8_t* l_spdBuffer = new uint8_t[l_spdBufferSize]; + memset(l_spdBuffer, 0, l_spdBufferSize); + uint16_t l_chipId = 0; + uint8_t l_chipEc = 0; + + do { + + // Read the SPD off the ocmb but skip reading the EFD to save time. + l_errl = deviceRead(i_target, + l_spdBuffer, + l_spdBufferSize, + DEVICE_SPD_ADDRESS(SPD::ENTIRE_SPD_WITHOUT_EFD)); + + // If unable to retrieve the SPD buffer then can't + // extract the IDEC data, so return error. + if (l_errl != nullptr) + { + HWAS_ERR("ocmbIdecPhase1> Error while trying to read " + "ENTIRE SPD from 0x%.08X ", + TARGETING::get_huid(i_target)); + break; + } + + // Make sure we got back the size we were expecting. + assert(l_spdBufferSize == SPD::DDIMM_DDR4_SPD_SIZE, + "ocmbIdecPhase1> OCMB SPD read size %d " + "doesn't match the expected size %d", + l_spdBufferSize, + SPD::DDIMM_DDR4_SPD_SIZE); + + l_errl = getOcmbIdecFromSpd(i_target, + l_spdBuffer, + l_chipId, + l_chipEc); + + // If we were unable to read the IDEC information from the SPD + // then break out early and do not set the associated attributes + if (l_errl != nullptr) + { + HWAS_ERR("ocmbIdecPhase1> Error while trying to parse " + "chip id and ec values from SPD read from OCMB 0x%.08X ", + TARGETING::get_huid(i_target)); + break; + } + + HWAS_INF("ocmbIdecPhase1> Read Chip ID = 0x%x Chip EC = 0x%x from target 0x%.08X", + l_chipId, l_chipEc, TARGETING::get_huid(i_target) ); + + // set the explorer chip EC attributes. + i_target->setAttr<TARGETING::ATTR_EC>(l_chipEc); + i_target->setAttr<TARGETING::ATTR_HDAT_EC>(l_chipEc); + + // set the explorer chip id attribute. + i_target->setAttr<TARGETING::ATTR_CHIP_ID>(l_chipId); + + } while(0); + + delete[] l_spdBuffer; + return l_errl; + +} + +errlHndl_t ocmbIdecPhase2(const TARGETING::TargetHandle_t& i_target) +{ + const uint32_t GEM_IDEC_SCOM_REGISTER = 0x0801240e; + const TARGETING::ATTR_CHIP_ID_type l_chipIdFromSpd = + i_target->getAttr<TARGETING::ATTR_CHIP_ID>(); + + errlHndl_t l_errl = nullptr; + uint64_t l_idec = 0; + size_t l_op_size = sizeof(l_idec); + uint8_t l_ec = 0; + uint16_t l_id = 0; + + do { + + if(l_chipIdFromSpd == POWER_CHIPID::EXPLORER_16) + { + // Call platform independent lookup for Explorer OCMBs + l_errl = FAPIWRAP::explorer_getidec(i_target, l_id, l_ec); + + if (l_errl != nullptr) + { + HWAS_ERR("ocmbIdecPhase2> explorer OCMB 0x%.8X - failed to read ID/EC", + TARGETING::get_huid(i_target)); + + break; + } + } + else + { + // read the register containing IDEC info on Gemini OCMBs + l_errl = DeviceFW::deviceRead(i_target, + &l_idec, + l_op_size, + DEVICE_SCOM_ADDRESS(GEM_IDEC_SCOM_REGISTER)); + + if (l_errl != nullptr) + { + HWAS_ERR("ocmbIdecPhase2> gemini OCMB 0x%.8X - failed to read ID/EC", + TARGETING::get_huid(i_target)); + + break; + } + + // Need to convert Gemini's IDEC register from MmL000CC + // to cfam standard format MLmCC000 + uint32_t l_major = 0xF0000000 & static_cast<uint32_t>(l_idec); + uint32_t l_minor = 0x0F000000 & static_cast<uint32_t>(l_idec); + uint32_t l_location = 0x00F00000 & static_cast<uint32_t>(l_idec); + l_idec = (l_major | (l_location << 4) | (l_minor >> 4) + | ((l_idec & 0x000000FF) << 12)); + // Parse out the information we need + l_ec = POWER_CHIPID::extract_ddlevel(l_idec); + l_id = POWER_CHIPID::extract_chipid16(l_idec); + } + + HWAS_INF("ocmbIdecPhase2> OCMB 0x%.8X - read ID/EC successful. " + "ID = 0x%.4X, EC = 0x%.2X, Full IDEC 0x%x", + TARGETING::get_huid(i_target), + l_id, + l_ec, + l_idec); + + if (l_id != l_chipIdFromSpd) + { + HWAS_ERR("ocmbIdecPhase2> OCMB Chip Id and associated SPD Chip Id " + "don't match: OCMB ID=0x%.4X; SPD ID=0x%.4X;", + l_id, + l_chipIdFromSpd); + + HWAS_ERR("ocmbIdecPhase2> Previous CHIP_ID 0x%.4X was set based on values from " + "SPD read will now be overwritten with values from OCMB IDEC register " + "ID=0x%.4X", + l_chipIdFromSpd, + l_id); + /*@ + * @errortype + * @severity ERRL_SEV_PREDICTIVE + * @moduleid MOD_OCMB_IDEC + * @reasoncode RC_OCMB_CHIP_ID_MISMATCH + * @userdata1[00:31] OCMB IDEC Register ID + * @userdata1[32:63] IDEC ID found in OCMB's SPD + * @userdata2[32:63] HUID of OCMB target + * @devdesc The IDEC info read from the OCMB and SPD + * did not match the expected values. + * @custdesc Firmware Error + */ + l_errl = hwasError(ERRORLOG::ERRL_SEV_PREDICTIVE, + MOD_OCMB_IDEC, + RC_OCMB_CHIP_ID_MISMATCH, + TWO_UINT32_TO_UINT64(l_id, l_chipIdFromSpd), + TARGETING::get_huid(i_target)); + + // Add callouts and commit + ocmbErrlCommit(i_target, l_errl); + + // Since there was an error then the ID values don't agree between + // the OCMB read and the SPD read. Since the OCMB has the correct + // answer, set the attributes to the values read from that instead + // of the SPD. + i_target->setAttr<TARGETING::ATTR_CHIP_ID>(l_id); + } + + const uint8_t l_ecFromSpd = i_target->getAttr<TARGETING::ATTR_EC>(); + + if (l_ec != l_ecFromSpd) + { + HWAS_ERR("ocmbIdecPhase2> OCMB Revision and associated SPD " + "Revision don't match: OCMB EC=0x%.2X; " + "SPD EC=0x%.2X; ", + l_ec, l_ecFromSpd); + + HWAS_ERR("ocmbIdecPhase2> Previous EC and HDAT_EC attributes 0x%.2X," + " which were set with values found in SPD will be overwritten" + " with value from OCMB IDEC register ID=0x%.2X", + l_ecFromSpd, + l_ec); + + /*@ + * @errortype + * @severity ERRL_SEV_PREDICTIVE + * @moduleid MOD_OCMB_IDEC + * @reasoncode RC_OCMB_SPD_REVISION_MISMATCH + * @userdata1[00:31] OCMB IDEC register EC + * @userdata1[32:63] EC found in OCMB's SPD + * @userdata2[00:31] OCMB Chip ID Attribute + * @userdata2[32:63] HUID of OCMB target + * @devdesc The EC (Revision) info read from the OCMB and + * SPD did not match the expected values. + * @custdesc Firmware Error + */ + l_errl = hwasError(ERRORLOG::ERRL_SEV_PREDICTIVE, + MOD_OCMB_IDEC, + RC_OCMB_SPD_REVISION_MISMATCH, + TWO_UINT32_TO_UINT64(l_ec, l_ecFromSpd), + TWO_UINT32_TO_UINT64( + i_target->getAttr<TARGETING::ATTR_CHIP_ID>(), + TARGETING::get_huid(i_target))); + + // Add callouts and commit + ocmbErrlCommit(i_target, l_errl); + + // Since there was an error then the EC values don't agree between + // the OCMB read and the SPD read. Since the OCMB has the correct + // answer, set the attributes to the values read from that instead + // of the SPD. + i_target->setAttr<TARGETING::ATTR_EC>(l_ec); + i_target->setAttr<TARGETING::ATTR_HDAT_EC>(l_ec); + } + + } while(0); + + return l_errl; - return nullptr; } // Register the presence detect function with the device framework @@ -628,7 +1141,10 @@ errlHndl_t platPresenceDetect(TargetHandleList &io_targets) DEVICE_CACHE_EEPROM_ADDRESS(present, EEPROM::VPD_PRIMARY)); errl = deviceRead(pTarget, &present, presentSize, DEVICE_CACHE_EEPROM_ADDRESS(present, EEPROM::VPD_PRIMARY)); - errlCommit(errl, HWAS_COMP_ID); + if( errl ) + { + errlCommit(errl, HWAS_COMP_ID); + } // errl is now null, move on to next target } #endif |