diff options
Diffstat (limited to 'src/usr/isteps')
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm_update.C | 106 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm_update.H | 15 | ||||
-rwxr-xr-x | src/usr/isteps/nvdimm/nvdimmdd.C | 39 | ||||
-rwxr-xr-x | src/usr/isteps/nvdimm/nvdimmdd.H | 4 |
4 files changed, 144 insertions, 20 deletions
diff --git a/src/usr/isteps/nvdimm/nvdimm_update.C b/src/usr/isteps/nvdimm/nvdimm_update.C index dc5f7e693..6075a660f 100644 --- a/src/usr/isteps/nvdimm/nvdimm_update.C +++ b/src/usr/isteps/nvdimm/nvdimm_update.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018,2019 */ +/* Contributors Listed Below - COPYRIGHT 2018,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -271,7 +271,8 @@ NvdimmInstalledImage::NvdimmInstalledImage(TARGETING::Target * i_nvDimm) : iv_timeout(INVALID_TIMEOUT), iv_max_blocks_per_region(INVALID_REGION_BLOCK_SIZE), iv_fw_update_mode_enabled(false), - iv_region_write_retries(0) + iv_region_write_retries(0), + iv_blockSizeSupported(INVALID_BLOCK_SIZE) { // initialize to invalid values } @@ -357,6 +358,46 @@ errlHndl_t NvdimmInstalledImage::getVersion(uint16_t & o_version, return l_err; } +errlHndl_t NvdimmInstalledImage::getBlockWriteSizeSupported(uint64_t & o_blockSize) +{ + errlHndl_t l_err = nullptr; + + do { + if (iv_blockSizeSupported == INVALID_BLOCK_SIZE) + { + uint16_t version = INVALID_VERSION; + l_err = getVersion(version, 0); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"getBlockWriteSizeSupported: " + "Failed to get version for 0x%.8X NVDIMM", + TARGETING::get_huid(iv_dimm)); + break; + } + + // The block write is more prone to random system interrupt + // which does something funny to the i2c bus. + // v3.A has the timeout increased to mitigate that + if (version >= 0x3A00) + { + // version supports 32-byte block size + iv_blockSizeSupported = 32; + } + else + { + // default to word size max write + iv_blockSizeSupported = sizeof(uint16_t); + } + TRACFCOMP( g_trac_nvdimm_upd, ERR_MRK"getBlockWriteSizeSupported: " + "block size %d supported for 0x%.8X NVDIMM (version 0x%04X)", + iv_blockSizeSupported, TARGETING::get_huid(iv_dimm), + version ); + } + } while (0); + o_blockSize = iv_blockSizeSupported; + return l_err; +} + errlHndl_t NvdimmInstalledImage::updateImage(NvdimmLidImage * i_lidImage) { errlHndl_t l_err = nullptr; @@ -1081,8 +1122,9 @@ errlHndl_t NvdimmInstalledImage::waitFwOpsBlockReceived() { errlHndl_t l_err = nullptr; - // retry for a total of 100ms - uint32_t timeout_ms_val = 100; + // retry for a total of 500ms + const uint32_t MAX_WAIT_FOR_OPS_BLOCK_RECEIVED = 500; + uint32_t timeout_ms_val = MAX_WAIT_FOR_OPS_BLOCK_RECEIVED; bool blockReceived = false; fw_ops_status_t opStatus; @@ -1098,6 +1140,7 @@ errlHndl_t NvdimmInstalledImage::waitFwOpsBlockReceived() TARGETING::get_huid(iv_dimm), timeout_ms_val); break; } + if (!opStatus.fw_ops_block_received) { // wait 1 millisecond between checking status @@ -1122,6 +1165,12 @@ errlHndl_t NvdimmInstalledImage::waitFwOpsBlockReceived() if (!blockReceived && !l_err) { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"waitFwOpsBlockReceived: " + "NVDIMM 0x%.8X FIRMWARE_OPS_STATUS (timeout: %d ms) " + "-- Last status: 0x%02X", + TARGETING::get_huid(iv_dimm), MAX_WAIT_FOR_OPS_BLOCK_RECEIVED, + opStatus.whole); + /*@ *@errortype *@moduleid WAIT_FW_OPS_BLOCK_RECEIVED @@ -1142,7 +1191,7 @@ errlHndl_t NvdimmInstalledImage::waitFwOpsBlockReceived() ( TWO_UINT8_TO_UINT16( 0x00, opStatus.whole), - 100, + MAX_WAIT_FOR_OPS_BLOCK_RECEIVED, timeout_ms_val ), ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); @@ -1580,15 +1629,29 @@ errlHndl_t NvdimmInstalledImage::byteRegionBlockTransfer(const uint8_t * i_data, TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " "Unable to open page for BLOCK %d transfer of NVDIMM " "0x%.8X", blockNum, TARGETING::get_huid(iv_dimm)); + break; } size_t l_numBytes = BYTES_PER_BLOCK; uint8_t l_reg_addr = ADDRESS(TYPED_BLOCK_DATA_BYTE0); + + // Grab whether word or 32-byte block write is supported + uint64_t blockSizeSupported = INVALID_BLOCK_SIZE; + l_err = getBlockWriteSizeSupported(blockSizeSupported); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " + "Unable to grab maximum block write size for NVDIMM 0x%.8X", + TARGETING::get_huid(iv_dimm)); + break; + } + l_err = DeviceFW::deviceOp( DeviceFW::WRITE, iv_dimm, pCurrentBlockData, l_numBytes, - DEVICE_NVDIMM_RAW_ADDRESS(l_reg_addr) ); + DEVICE_NVDIMM_RAW_ADDRESS_WITH_BLOCKSIZE(l_reg_addr, blockSizeSupported) + ); if (l_err) { TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " @@ -1596,24 +1659,49 @@ errlHndl_t NvdimmInstalledImage::byteRegionBlockTransfer(const uint8_t * i_data, blockNum, l_reg_addr, TARGETING::get_huid(iv_dimm)); break; } - // increment to next block - pCurrentBlockData += BYTES_PER_BLOCK; // After a block has been transferred, verify that the 32-byte block // was received by polling FIRMWARE_OPS_STATUS offset for // FIRMWARE_BLOCK_RECEIVED. - TRACUCOMP(g_trac_nvdimm_upd, ">> waitFwOpsBlockReceived"); l_err = waitFwOpsBlockReceived(); if (l_err) { TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " "Block %d read of FIRMWARE_OPS_STATUS failed on NVDIMM " " 0x%.8X", blockNum, TARGETING::get_huid(iv_dimm)); + + size_t tmpNumBytes = l_numBytes; + uint8_t tmpBuffer[tmpNumBytes]; + errlHndl_t l_err2 = DeviceFW::deviceOp( DeviceFW::READ, + iv_dimm, + tmpBuffer, + tmpNumBytes, + DEVICE_NVDIMM_ADDRESS(l_reg_addr) ); + if (l_err2) + { + TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"byteRegionBlockTransfer: " + "Block %d read from 0x%02X failed on NVDIMM 0x%.8X", + blockNum, l_reg_addr, TARGETING::get_huid(iv_dimm)); + l_err2->plid(l_err->plid()); + l_err2->collectTrace(NVDIMM_COMP_NAME); + l_err2->collectTrace(NVDIMM_UPD); + errlCommit(l_err2, NVDIMM_COMP_ID); + break; + } + else + { + TRACFBIN(g_trac_nvdimm_upd, "byteRegionBlockTransfer: Wrote block", pCurrentBlockData, l_numBytes); + TRACFBIN(g_trac_nvdimm_upd, "byteRegionBlockTransfer: Read-back block", tmpBuffer, l_numBytes); + } + break; } // block of data successfully sent to NV controller TRACUCOMP(g_trac_nvdimm_upd,"byteRegionBlockTransfer: block 0x%02X successfully sent to NV controller", blockNum); + + // increment to next block + pCurrentBlockData += BYTES_PER_BLOCK; blockNum++; } diff --git a/src/usr/isteps/nvdimm/nvdimm_update.H b/src/usr/isteps/nvdimm/nvdimm_update.H index 1e48f7ef8..3f71dff56 100644 --- a/src/usr/isteps/nvdimm/nvdimm_update.H +++ b/src/usr/isteps/nvdimm/nvdimm_update.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018,2019 */ +/* Contributors Listed Below - COPYRIGHT 2018,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -42,6 +42,7 @@ const uint16_t INVALID_ID = 0xFFFF; const uint16_t INVALID_VERSION = 0xFFFF; const uint16_t INVALID_TIMEOUT = 0xFFFF; const uint32_t INVALID_TYPE = 0xFFFFFFFF; +const uint8_t INVALID_BLOCK_SIZE = 0x00; // Type is combination of manufacturer id and product id const uint32_t SMART_NVDIMM_16GB_TYPE = 0x01945377; @@ -245,6 +246,15 @@ class NvdimmInstalledImage } /** + * @brief Accessor for what write size is supported for this installed nvdimm + * Prior to level 0x3A, only word size supported + * Level 0x3A and beyond support 32 byte block writes + * @param[out] maximum number of bytes allowed per write + * @return block write size supported for this current nvdimm level + */ + errlHndl_t getBlockWriteSizeSupported(uint64_t & o_blockSize); + + /** * @brief Update the current NV Controller * @param Update using this image * @return error pointer if failure to update, else nullptr @@ -277,6 +287,9 @@ class NvdimmInstalledImage // retry attempts for all regions uint8_t iv_region_write_retries; + // what size block can be written (2 or 32 byte) + uint64_t iv_blockSizeSupported; + // Helper functions for updating the installed lid /** diff --git a/src/usr/isteps/nvdimm/nvdimmdd.C b/src/usr/isteps/nvdimm/nvdimmdd.C index 5454fcb37..044be454b 100755 --- a/src/usr/isteps/nvdimm/nvdimmdd.C +++ b/src/usr/isteps/nvdimm/nvdimmdd.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2019 */ +/* Contributors Listed Below - COPYRIGHT 2011,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -189,6 +189,7 @@ errlHndl_t nvdimmPerformOp( DeviceFW::OperationType i_opType, nvdimm_addr_t i2cInfo; i2cInfo.offset = va_arg( i_args, uint64_t ); + i2cInfo.blockSize = va_arg( i_args, uint64_t ); TRACDCOMP( g_trac_nvdimm, ENTER_MRK"nvdimmPerformOp()" ); @@ -399,7 +400,7 @@ errlHndl_t crossesNvdimmPageBoundary( uint64_t i_offset, errlHndl_t err = nullptr; - if(i_offset >= NVDIMM_PAGE_SIZE || (i_offset+i_buflen) >= NVDIMM_PAGE_SIZE) + if(i_offset >= NVDIMM_PAGE_SIZE || (i_offset+i_buflen) > NVDIMM_PAGE_SIZE) { TRACFCOMP( g_trac_nvdimm, ERR_MRK"crossesNvdimmPageBoundary() - offset 0x%X, buflen 0x%X" @@ -800,11 +801,13 @@ errlHndl_t nvdimmWrite ( TARGETING::Target * i_target, // Setup a max-size buffer of writePageSize size_t newBufLen = i_i2cInfo.writePageSize; - // Break data into word size transfers if possible - if ( (io_buflen > sizeof(uint16_t)) && - ((io_buflen % sizeof(uint16_t)) == 0) ) + // Break data into max supported i2c transfer size, if possible + // (speeds up i2c operation) + if ( (i_i2cInfo.blockSize != 0) && + (io_buflen >= i_i2cInfo.blockSize) && + ((io_buflen % i_i2cInfo.blockSize) == 0) ) { - newBufLen = sizeof(uint16_t); + newBufLen = i_i2cInfo.blockSize; } assert(newBufLen > 0, "Unable to allocate 0 buffer size for nvdimmWrite()"); @@ -937,15 +940,16 @@ errlHndl_t nvdimmWriteData( TARGETING::Target * i_target, errlHndl_t err = nullptr; errlHndl_t err_retryable = nullptr; size_t data_length; + do { /***********************************************************/ /* Attempt write multiple times ONLY on retryable fails */ /***********************************************************/ - for (uint8_t retry = 0; - retry <= NVDIMM_MAX_RETRIES; - retry++) + for ( uint8_t retry = 0; retry <= NVDIMM_MAX_RETRIES; retry++) { + // use a temporary variable to allow retry as the + // data_length could be altered by deviceOp() failure data_length = i_dataLen; // Do the actual data write @@ -966,6 +970,23 @@ errlHndl_t nvdimmWriteData( TARGETING::Target * i_target, i_i2cInfo.i2cMuxBusSelector, &(i_i2cInfo.i2cMuxPath)) ); } + else if ( i_dataLen == 32 ) + { + err = deviceOp( DeviceFW::WRITE, + i_target, + i_dataToWrite, + data_length, + DeviceFW::I2C, + I2C_SMBUS_RW_W_CMD_PARAMS( + DeviceFW::I2C_SMBUS_BLOCK_NO_BYTE_COUNT, + i_i2cInfo.engine, + i_i2cInfo.port, + i_i2cInfo.devAddr, + *(reinterpret_cast<uint8_t*>(i_byteAddress) + + (i_byteAddressSize-1)), + i_i2cInfo.i2cMuxBusSelector, + &(i_i2cInfo.i2cMuxPath)) ); + } else { err = deviceOp( DeviceFW::WRITE, diff --git a/src/usr/isteps/nvdimm/nvdimmdd.H b/src/usr/isteps/nvdimm/nvdimmdd.H index 6a3f07be0..88bc388c0 100755 --- a/src/usr/isteps/nvdimm/nvdimmdd.H +++ b/src/usr/isteps/nvdimm/nvdimmdd.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ +/* Contributors Listed Below - COPYRIGHT 2019,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -69,6 +69,7 @@ struct nvdimm_addr_t uint64_t devSize_KB; // in kilobytes uint64_t chipCount; // number of chips making up nvdimm device uint64_t writeCycleTime; // in milliseconds + uint8_t blockSize; // size of write block supported for this nvdimm uint8_t i2cMuxBusSelector; TARGETING::EntityPath i2cMuxPath; @@ -86,6 +87,7 @@ struct nvdimm_addr_t devSize_KB(0), chipCount(0), writeCycleTime(0), + blockSize(0), i2cMuxBusSelector(I2C_MUX::NOT_APPLICABLE), i2cMuxPath() { |