diff options
author | Bill Hoffa <wghoffa@us.ibm.com> | 2017-06-16 17:11:20 -0500 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2017-09-18 15:50:35 -0400 |
commit | 54a3bfc6323e7231580da74bcd965988d3e780af (patch) | |
tree | 78b037f38f1400ce71187a7e0bf3184bb61da9bc /src/usr | |
parent | 9511dde54f55257edd1c47ff709742638c21fbe8 (diff) | |
download | talos-hostboot-54a3bfc6323e7231580da74bcd965988d3e780af.tar.gz talos-hostboot-54a3bfc6323e7231580da74bcd965988d3e780af.zip |
Add support for LPC error detection and recovery
Change-Id: Iea9bd4425aeb798acd85484402c627fb623cae94
Also-By: Matt Ploetz <maploetz@us.ibm.com>
RTC: 133649
RTC: 134582
Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/45397
Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
Reviewed-by: Prachi Gupta <pragupta@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/initservice/istepdispatcher/istepdispatcher.C | 1 | ||||
-rw-r--r-- | src/usr/lpc/lpcdd.C | 600 | ||||
-rw-r--r-- | src/usr/lpc/lpcdd.H | 121 | ||||
-rw-r--r-- | src/usr/lpc/test/lpcddtest.H | 68 |
4 files changed, 542 insertions, 248 deletions
diff --git a/src/usr/initservice/istepdispatcher/istepdispatcher.C b/src/usr/initservice/istepdispatcher/istepdispatcher.C index f1dd9ada6..520270be4 100644 --- a/src/usr/initservice/istepdispatcher/istepdispatcher.C +++ b/src/usr/initservice/istepdispatcher/istepdispatcher.C @@ -2097,6 +2097,7 @@ errlHndl_t IStepDispatcher::sendProgressCode(bool i_needsLock) &port80_val, port80_len, DEVICE_LPC_ADDRESS(LPC::TRANS_IO, 0x80)); delete err; // this is debug only, ignore any errors + err = NULL; port80_val++; #endif diff --git a/src/usr/lpc/lpcdd.C b/src/usr/lpc/lpcdd.C index b0dd00fd7..efcfa0346 100644 --- a/src/usr/lpc/lpcdd.C +++ b/src/usr/lpc/lpcdd.C @@ -50,6 +50,9 @@ #include <initservice/taskargs.H> #include <config.h> #include <arch/memorymap.H> +#include <util/misc.H> +#include <errl/errlreasoncodes.H> + trace_desc_t* g_trac_lpc; TRAC_INIT( & g_trac_lpc, LPC_COMP_NAME, 2*KILOBYTE, TRACE::BUFFER_SLOW); @@ -106,6 +109,13 @@ errlHndl_t lpcRead(DeviceFW::OperationType i_opType, // then we have to use our special side copy of the driver if( i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) { + //First check/clear the LPC bus of errors and commit any errors found + l_err = Singleton<LpcDD>::instance().checkForLpcErrors(); + if (l_err) + { + errlCommit(l_err, LPC_COMP_ID); + } + l_err = Singleton<LpcDD>::instance().readLPC( l_type, l_addr, io_buffer, @@ -116,6 +126,14 @@ errlHndl_t lpcRead(DeviceFW::OperationType i_opType, if( g_altLpcDD && (i_target == g_altLpcDD->getProc()) ) { + //First check/clear the LPC bus of errors and commit + // any errors found + l_err = g_altLpcDD->checkForLpcErrors(); + if (l_err) + { + errlCommit(l_err, LPC_COMP_ID); + } + l_err = g_altLpcDD->readLPC( l_type, l_addr, io_buffer, @@ -130,7 +148,7 @@ errlHndl_t lpcRead(DeviceFW::OperationType i_opType, alt_huid = TARGETING::get_huid(g_altLpcDD->getProc()); } /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_READLPC * @reasoncode LPC::RC_BAD_TARGET * @userdata1[00:31] Requested target @@ -197,6 +215,13 @@ errlHndl_t lpcWrite(DeviceFW::OperationType i_opType, // then we have to use our special side copy of the driver if( i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) { + //First check/clear the LPC bus of errors and commit any errors found + l_err = Singleton<LpcDD>::instance().checkForLpcErrors(); + if (l_err) + { + errlCommit(l_err, LPC_COMP_ID); + } + l_err = Singleton<LpcDD>::instance().writeLPC( l_type, l_addr, io_buffer, @@ -207,6 +232,14 @@ errlHndl_t lpcWrite(DeviceFW::OperationType i_opType, if( g_altLpcDD && (i_target == g_altLpcDD->getProc()) ) { + //First check/clear the LPC bus of errors and commit + // any errors found + l_err = g_altLpcDD->checkForLpcErrors(); + if (l_err) + { + errlCommit(l_err, LPC_COMP_ID); + } + l_err = g_altLpcDD->writeLPC( l_type, l_addr, io_buffer, @@ -221,7 +254,7 @@ errlHndl_t lpcWrite(DeviceFW::OperationType i_opType, alt_huid = TARGETING::get_huid(g_altLpcDD->getProc()); } /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_WRITELPC * @reasoncode LPC::RC_BAD_TARGET * @userdata1[00:31] Requested target @@ -272,7 +305,7 @@ errlHndl_t create_altmaster_objects( bool i_create, { TRACFCOMP(g_trac_lpc, "LPC::create_altmaster_objects> Alt-master object already exists"); /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_CREATE_ALTMASTER * @reasoncode LPC::RC_ALTMASTER_EXISTS * @userdata1 Requested proc @@ -300,7 +333,7 @@ errlHndl_t create_altmaster_objects( bool i_create, { TRACFCOMP(g_trac_lpc, "LPC::create_altmaster_objects> Cannot create another object using master sentinel"); /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_CREATE_ALTMASTER * @reasoncode LPC::RC_CANT_USE_SENTINEL * @userdata1 <unused> @@ -329,7 +362,7 @@ errlHndl_t create_altmaster_objects( bool i_create, { TRACFCOMP(g_trac_lpc, "LPC::create_altmaster_objects> Cannot create another object using master proc"); /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_CREATE_ALTMASTER * @reasoncode LPC::RC_CANT_USE_MASTER * @userdata1 <unused> @@ -438,14 +471,15 @@ LpcDD::LpcDD( TARGETING::Target* i_proc ) mmio_dev_map(reinterpret_cast<void *>(baseAddr), LPC_SPACE_SIZE ))); - //@todo RTC:126644 - // Initialize the hardware -// errlHndl_t l_errl = hwReset(LpcDD::RESET_INIT); -// if( l_errl ) -// { -// TRACFCOMP( g_trac_lpc, "Errors initializing LPC logic... Beware! PLID=%.8X", l_errl->plid() ); -// errlCommit(l_errl, LPC_COMP_ID); -// } + /* @todo RTC:126644 + Initialize the hardware + errlHndl_t l_errl = hwReset(LpcDD::RESET_INIT); + if( l_errl ) + { + TRACFCOMP( g_trac_lpc, "Errors initializing LPC logic... Beware! PLID=%.8X", l_errl->plid() ); + errlCommit(l_errl, LPC_COMP_ID); + } + **/ } LpcDD::~LpcDD() @@ -459,11 +493,11 @@ LpcDD::~LpcDD() */ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) { - errlHndl_t l_err = NULL; -// @todo RTC:133649 Support P9 LPC controller - error detection -#if 0 - TRACFCOMP( g_trac_lpc, ENTER_MRK"LpcDD::hwReset(i_resetLevel=%d)>", i_resetLevel ); + errlHndl_t l_err = NULL; + uint32_t i_addr = 0; + uint64_t l_addr = 0; + TRACFCOMP( g_trac_lpc, ENTER_MRK"LpcDD::hwReset(i_resetLevel=%d)>", i_resetLevel ); // check iv_resetActive to avoid infinite loops // and don't reset if in the middle of FFDC collection @@ -475,10 +509,6 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) iv_resetActive = true; do { - // always read/write 64 bits to SCOM - uint64_t scom_data_64 = 0x0; - size_t scom_size = sizeof(uint64_t); - /***************************************/ /* Handle the different reset levels */ /***************************************/ @@ -488,7 +518,8 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) {// Nothing to do here, so just break break; } - case RESET_INIT: +/* @todo - RTC:179179 + case RESET_INIT: { // Set OPB LPCM FIR Mask // hostboot will monitor these FIR bits @@ -503,27 +534,60 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) OPB_LPCM_FIR_MASK_WO_OR_REG)); if( l_err ) { break; } } +**/ - case RESET_ECCB: - { - // Write Reset Register to reset FW Logic registers - TRACFCOMP(g_trac_lpc, "LpcDD::hwReset> Writing ECCB_RESET_REG to reset ECCB FW Logic"); - scom_data_64 = 0x0; - l_err = deviceOp( DeviceFW::WRITE, - iv_proc, - &(scom_data_64), - scom_size, - DEVICE_SCOM_ADDRESS(ECCB_RESET_REG) ); - - break; - } - - case RESET_OPB_LPCHC_SOFT: + case RESET_OPB_LPCHC_HARD: { TRACFCOMP(g_trac_lpc, "LpcDD::hwReset> Writing OPB_MASTER_LS_CONTROL_REG to disable then enable First Error Data Capture"); - size_t opsize = sizeof(uint32_t); + //Clear all error indicators in LPCM OPB Master Actual + // Status Reg + i_addr = OPBM_STATUS_REG; + l_err = checkAddr( LPC::TRANS_ERR, i_addr, &l_addr ); + if (l_err) { break; } + uint32_t * l_status_ptr + = reinterpret_cast<uint32_t*>(l_addr); + //Clear under mask - aka write 1 clears + *l_status_ptr = OPB_ERROR_MASK; + eieio(); + + //Clear related bits in the LPCM OPB Master Accumulated + // Status Reg + i_addr = OPBM_ACCUM_STATUS_REG; + l_err = checkAddr( LPC::TRANS_ERR, i_addr, &l_addr ); + if (l_err) { break; } + uint32_t * l_accum_status_ptr + = reinterpret_cast<uint32_t*>(l_addr); + //Clear under mask - aka write 1 clears + *l_accum_status_ptr = OPB_ERROR_MASK; + eieio(); + //Reset LPCHC Logic + TRACFCOMP(g_trac_lpc, "LpcDD::hwReset> Writing LPCHC_RESET_REG to reset LPCHC Logic"); + i_addr = LPCHC_RESET_REG; + l_err = checkAddr( LPC::TRANS_ERR, i_addr, &l_addr ); + if (l_err) { break; } + uint32_t * l_reset_ptr + = reinterpret_cast<uint32_t*>(l_addr); + //The spec states the act of a write is all that matters + //The data itself doesn't matter, so using an arbitrary + //value + *l_reset_ptr = 0x12345678; + eieio(); + + //Issue LPC Abort + i_addr = LPCHC_LPC_BUS_ABORT_REG; + l_err = checkAddr( LPC::TRANS_ERR, i_addr, &l_addr ); + if (l_err) { break; } + uint32_t * l_abort_ptr + = reinterpret_cast<uint32_t*>(l_addr); + //The spec states the act of a write is all that matters + //The data itself doesn't matter, so using an arbitrary + //value + *l_abort_ptr = 0x12345678; + eieio(); + +/* @todo - RTC:179179 - Re-enable FEDC // First read OPB_MASTER_LS_CONTROL_REG uint32_t lpc_data = 0x0; l_err = _readLPC( LPC::TRANS_REG, @@ -563,47 +627,25 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) scom_size, DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_WOX_AND_REG) ); if (l_err) { break; } - +**/ break; } - case RESET_OPB_LPCHC_HARD: +/* @todo - RTC:179179 - Properly categorize errors into SOFT/HARD errors and + update this implementation based on results + case RESET_OPB_LPCHC_SOFT: { - TRACFCOMP(g_trac_lpc, "LpcDD::hwReset> Writing LPCHC_RESET_REG to reset LPCHC Logic"); - - size_t opsize = sizeof(uint32_t); - uint32_t lpc_data = 0x0; - l_err = _writeLPC( LPC::TRANS_REG, - LPCHC_RESET_REG, - &lpc_data, - opsize ); - if (l_err) { break; } - - // sleep 1ms for LPCHC to execute internal - // reset+init sequence - nanosleep( 0, NS_PER_MSEC ); - - // Clear FIR register - scom_data_64 = ~(OPB_LPCM_FIR_ERROR_MASK); - l_err = deviceOp( - DeviceFW::WRITE, - iv_proc, - &(scom_data_64), - scom_size, - DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_WOX_AND_REG) ); - if (l_err) { break; } - break; } +**/ // else - unsupported reset level default: { - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::hwReset> Unsupported Reset Level Passed In: 0x%X", i_resetLevel); /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_LPCDD_HWRESET * @reasoncode LPC::RC_UNSUPPORTED_OPERATION * @userdata1 Unsupported Reset Level Parameter @@ -651,7 +693,6 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) } TRACFCOMP( g_trac_lpc, EXIT_MRK"LpcDD::hwReset()=%.8X:%.4X", ERRL_GETEID_SAFE(l_err), ERRL_GETRC_SAFE(l_err) ); -#endif return l_err; } @@ -702,6 +743,15 @@ errlHndl_t LpcDD::checkAddr(LPC::TransType i_type, *o_addr = getLPCBaseAddr()+ i_addr + LPC::LPCHC_REG_SPACE- LPC_ADDR_START; break; + case LPC::TRANS_ERR: + if( i_addr >= 0x10000 ) + { + invalid_address = true; + break; + } + *o_addr = + getLPCBaseAddr()+ i_addr + LPC::LPCHC_ERR_SPACE- LPC_ADDR_START; + break; case LPC::TRANS_ABS: //Just use the address as given *o_addr = getLPCBaseAddr() + i_addr; @@ -714,7 +764,7 @@ errlHndl_t LpcDD::checkAddr(LPC::TransType i_type, { TRACFCOMP( g_trac_lpc, "LpcDD::checkAddr() Invalid address : i_type=%d, i_addr=%X", i_type, i_addr ); /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_LPCDD_CHECKADDR * @reasoncode LPC::RC_INVALID_ADDR * @userdata1[0:31] LPC Address @@ -786,7 +836,7 @@ errlHndl_t LpcDD::_readLPC(LPC::TransType i_type, { TRACFCOMP( g_trac_lpc, "readLPC> Unsupported buffer size : %d", io_buflen ); /*@ - * @errortype + * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid LPC::MOD_LPCDD_READLPC * @reasoncode LPC::RC_BAD_ARG * @userdata1[0:31] LPC Address @@ -809,6 +859,9 @@ errlHndl_t LpcDD::_readLPC(LPC::TransType i_type, //Sync the IO op eieio(); + // Check Error bits + l_err = checkForLpcErrors(); + } while(0); LPC_TRACFCOMP( g_trac_lpc, "readLPC> %08X[%d] = %08X", l_addr, io_buflen, *reinterpret_cast<uint32_t*>( o_buffer ) >> (8 * (4 - io_buflen)) ); @@ -861,203 +914,316 @@ errlHndl_t LpcDD::_writeLPC(LPC::TransType i_type, } eieio(); #endif + + // Check Error bits + l_err = checkForLpcErrors(); + } while(0); return l_err; } - - /** * @brief Add Error Registers to an existing Error Log */ void LpcDD::addFFDC(errlHndl_t & io_errl) { + errlHndl_t l_err = nullptr; + uint32_t i_addr = 0; + uint64_t l_addr = 0; + uint32_t lpcAddr_buffer; + size_t l_buflen = sizeof(uint32_t); + // check iv_ffdcActive to avoid infinite loops - if ( iv_ffdcActive == false ) - { - iv_ffdcActive = true; + do{ - TRACFCOMP( g_trac_lpc, "LpcDD::addFFDC> adding FFDC to Error Log EID=0x%X, PLID=0x%X", - io_errl->eid(), io_errl->plid() ); + if ( iv_ffdcActive == false ) + { + iv_ffdcActive = true; - ERRORLOG::ErrlUserDetailsLogRegister l_eud(iv_proc); + TRACFCOMP( g_trac_lpc, "LpcDD::addFFDC> adding FFDC to Error Log EID=0x%X, PLID=0x%X", + io_errl->eid(), io_errl->plid() ); - // @todo RTC:133649 Support P9 LPC controller - error detection - // Add ECCB Status Register - //l_eud.addData(DEVICE_SCOM_ADDRESS(ECCB_STAT_REG)); + i_addr = LPCHC_ERROR_ADDR_REG; + l_err = checkAddr( LPC::TRANS_ERR, i_addr, &l_addr ); + if (l_err) + { + // Additional FFDC is not necessary, just delete error and break + delete l_err; + l_err = nullptr; + break; + } - // Add OPB LPC Master FIR - //l_eud.addData(DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG)); + memcpy(&lpcAddr_buffer, reinterpret_cast<void*>(l_addr), l_buflen); + eieio(); - //@todo - add more LPC regs RTC:37744 - //LPCIRQ_STATUS = 0x38 - //SYS_ERR_ADDR = 0x40 + io_errl->addFFDC(LPC_COMP_ID, + &lpcAddr_buffer, + l_buflen, + 0, // version + ERRORLOG::ERRL_UDT_NOFORMAT, // parser ignores data + false); - l_eud.addToLog(io_errl); + // reset FFDC active flag + iv_ffdcActive = false; + } - // reset FFDC active flag - iv_ffdcActive = false; - } + }while(0); return; } /** - * @brief Check For Errors in OPB and LPCHC Status Registers + * @brief Compute error severity from OPBM Status Register */ -errlHndl_t LpcDD::checkForOpbErrors( ResetLevels &o_resetLevel ) +void LpcDD::computeOpbmErrSev(OpbmErrReg_t i_opbmErrData, + ResetLevels &o_resetLevel) { - errlHndl_t l_err = NULL; - bool errorFound = false; - - // Used to set Reset Levels, if necessary o_resetLevel = RESET_CLEAR; - // Default status values in case we fail in reading the registers - OpbLpcmFirReg_t fir_reg; - fir_reg.data64 = 0xDEADBEEFDEADBEEF; - uint64_t fir_data = 0x0; - - // always read/write 64 bits to SCOM - size_t scom_size = sizeof(uint64_t); - - do { - // Read FIR Register - l_err = deviceOp( DeviceFW::READ, - iv_proc, - &(fir_data), - scom_size, - DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG) ); - if( l_err ) { break; } + // First check the soft errors + /* @todo - RTC:179179 Revisit below Reset Levels **/ + if( i_opbmErrData.rxits ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> Invalid Transfer Size Error: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + if( i_opbmErrData.rxicmd ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> Invalid Command Error: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + if( i_opbmErrData.rxiaa ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> Invalid Address Alignment Error: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + if( i_opbmErrData.rxopbe ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> OPB Error Acknowledged: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + if( i_opbmErrData.rxicmdb ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> OPB Master Command Buffer Parity Error: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + if( i_opbmErrData.rxidatab ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> OPM Master Data Buffer Parity Error: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + // Now look for HARD errors that will override SOFT errors reset Level + if( i_opbmErrData.rxopbt ) + { + o_resetLevel = RESET_OPB_LPCHC_HARD; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> OPM Timeout Error: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + if( i_opbmErrData.rxiaddr ) + { + o_resetLevel = RESET_OPB_LPCHC_HARD; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> Invalid Address Error: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } + if( i_opbmErrData.rxctgtel ) + { + o_resetLevel = RESET_OPB_LPCHC_HARD; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeOpbmErrSev> OPB Master Timeout: OPBM Status Reg =0x%8X, ResetLevel=%d", + i_opbmErrData, o_resetLevel); + } +} - // Mask data to just the FIR bits we care about - fir_reg.data64 = fir_data & OPB_LPCM_FIR_ERROR_MASK; +/** + * @brief Compute error severity from LPCHC Status Register + */ +void LpcDD::computeLpchcErrSev(LpchcErrReg_t i_lpchcErrData, + ResetLevels &o_resetLevel) +{ + o_resetLevel = RESET_CLEAR; - // First look for SOFT errors - if( 1 == fir_reg.rxits ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_SOFT; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> Invalid Transfer Size: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } + // First check the soft errors + // All of these errors are set from bad LPC end points. Setting all to soft + /* @todo - RTC:179179 Revisit below Reset Levels **/ + if( i_lpchcErrData.lreset ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeLpchcErrSev> Lreset Event: LPCHC Status Reg =0x%8X, ResetLevel=%d", + i_lpchcErrData, o_resetLevel); + } + if( i_lpchcErrData.syncab ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeLpchcErrSev> Sync Abnormal Error: LPCHC Status Reg =0x%8X, ResetLevel=%d", + i_lpchcErrData, o_resetLevel); + } + if( i_lpchcErrData.syncnr ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeLpchcErrSev> Sync No Response Error: LPCHC Status Reg =0x%8X, ResetLevel=%d", + i_lpchcErrData, o_resetLevel); + } + if( i_lpchcErrData.syncne ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeLpchcErrSev> Sync Normal Error: LPCHC Status Reg =0x%8X, ResetLevel=%d", + i_lpchcErrData, o_resetLevel); + } + if( i_lpchcErrData.syncto ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeLpchcErrSev> Sync Timeout Error: LPCHC Status Reg =0x%8X, ResetLevel=%d", + i_lpchcErrData, o_resetLevel); + } + if( i_lpchcErrData.tctar ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeLpchcErrSev> Target Cycle TAR Error: LPCHC Status Reg =0x%8X, ResetLevel=%d", + i_lpchcErrData, o_resetLevel); + } + if( i_lpchcErrData.mctar ) + { + o_resetLevel = RESET_OPB_LPCHC_SOFT; + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::computeLpchcErrSev> LPC Bus Master Cycle TAR Error: LPCHC Status Reg =0x%8X, ResetLevel=%d", + i_lpchcErrData, o_resetLevel); + } - if( 1 == fir_reg.rxicmd ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_SOFT; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> Invalid Command: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } +} - if( 1 == fir_reg.rxiaa ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_SOFT; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> Invalid Address Alignment: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } +/** + * @brief Check For Errors in OPB and LPCHC Status Registers + */ +errlHndl_t LpcDD::checkForLpcErrors() +{ + errlHndl_t l_err = NULL; - if( 1 == fir_reg.rxcbpe ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_SOFT; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> Command Buffer Parity Error: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } + uint32_t i_addr = 0; + uint64_t l_addr = 0; + uint32_t opbm_buffer; + uint32_t lpchc_buffer; + size_t l_buflen = sizeof(uint32_t); + ResetLevels l_opbmResetLevel = RESET_CLEAR; + ResetLevels l_lpchcResetLevel = RESET_CLEAR; - if( 1 == fir_reg.rxdbpe ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_SOFT; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> Data Buffer Parity Error: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } + OpbmErrReg_t opbm_err_union; + opbm_err_union.data32 = 0x0; + LpchcErrReg_t lpchc_err_union; + lpchc_err_union.data32 = 0x0; - // Now look for HARD errors that will override SOFT errors reset Level - if( 1 == fir_reg.rxhopbe ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_HARD; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> OPB Bus Error: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } + do { + // LPC error registers are not modeled in FSP simics. + // Skip if we are running simics. + if (Util::isSimicsRunning()) + { break; } - if( 1 == fir_reg.rxhopbt ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_HARD; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> OPB Bus Timeout: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } + // Read OPBM Status Register via MMIO + i_addr = OPBM_ACCUM_STATUS_REG; + l_err = checkAddr( LPC::TRANS_ERR, i_addr, &l_addr ); + if (l_err) { break; } - if( 1 == fir_reg.rxctgtel ) - { - errorFound = true; - o_resetLevel = RESET_OPB_LPCHC_HARD; - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> CI Load/CI Store/OPB Master Hang Timeout: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d", - fir_reg.data64, o_resetLevel); - } + memcpy( &opbm_buffer, reinterpret_cast<void*>(l_addr), l_buflen ); + eieio(); + // Read LPC Host Controller Status register via MMIO + i_addr = LPCHC_REG; + l_err = checkAddr( LPC::TRANS_ERR, i_addr, &l_addr ); + if (l_err) { break; } - }while(0); + memcpy( &lpchc_buffer, reinterpret_cast<void*>(l_addr), l_buflen ); + eieio(); + // Mask error bits + opbm_err_union.data32 = (opbm_buffer & OPB_ERROR_MASK); + lpchc_err_union.data32 = (lpchc_buffer & LPCHC_ERROR_MASK); - // If there is any error create an error log - if ( errorFound ) - { - // If we failed on a register read above, but still found an error, - // delete register read error log and create an original error log - // for the found error - if ( l_err ) + // First look for errors in the OPBM bit mask + if (opbm_err_union.data32 != 0) { - TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForOpbErrors> Deleting register read error. Returning error created for the found error"); - delete l_err; - } - - /*@ - * @errortype - * @moduleid LPC::MOD_LPCDD_CHECKFOROPBERRORS - * @reasoncode LPC::RC_OPB_ERROR - * @userdata1 OPB FIR Register Data - * @userdata2 Reset Level - * @devdesc LpcDD::checkForOpbErrors> Error(s) found in OPB - * and/or LPCHC Status Register - * @custdesc Error(s) found in OPB and/or LPCHC Status Register - */ - l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - LPC::MOD_LPCDD_CHECKFOROPBERRORS, - LPC::RC_OPB_ERROR, - fir_reg.data64, - o_resetLevel ); - - l_err->addHwCallout( iv_proc, - HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, - HWAS::GARD_NULL ); - + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForLpcErrors> Error found in OPB Master Status Register: 0x%8X",opbm_err_union.data32); + computeOpbmErrSev(opbm_err_union, l_opbmResetLevel); - // Log FIR Register Data - ERRORLOG::ErrlUserDetailsLogRegister l_eud(iv_proc); - - l_eud.addDataBuffer(&fir_data, scom_size, - DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG)); - - l_eud.addToLog(l_err); + if(l_opbmResetLevel != RESET_CLEAR) + { + /*@ + * @errortype ERRL_SEV_UNRECOVERABLE + * @moduleid LPC::MOD_LPCDD_CHECKFORLPCERRORS + * @reasoncode LPC::RC_OPB_ERROR + * @userdata1 OPBM Error Status Register + * @userdata2 Reset Level + * @devdesc LpcDD::checkLpcErrors> Error(s) found in OPB + * Status Register + * @custdesc Error(s) found in OPB Status Register + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_CHECKFORLPCERRORS, + LPC::RC_OPB_ERROR, + opbm_buffer, + l_opbmResetLevel); + + // Gather additional ffdc data + addFFDC(l_err); + l_err->addHwCallout( iv_proc, + HWAS::SRCI_PRIORITY_NONE, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + /* @todo - RTC:179179 Use l_opbmResetLevel **/ + hwReset(RESET_OPB_LPCHC_HARD); + } + } + // Check the LPC host controller bit mask, only if there are no errors + // from the OPBM bit mask + if (lpchc_err_union.data32 != 0 && l_err == NULL) + { + TRACFCOMP( g_trac_lpc, ERR_MRK"LpcDD::checkForLpcErrors> Error found in LPC Host Controller Status Register: 0x%8X",lpchc_err_union.data32); + computeLpchcErrSev(lpchc_err_union, l_lpchcResetLevel); - addFFDC(l_err); - l_err->collectTrace(LPC_COMP_NAME); + if(l_lpchcResetLevel != RESET_CLEAR) + { + /*@ + * @errortype ERRL_SEV_UNRECOVERABLE + * @moduleid LPC::MOD_LPCDD_CHECKFORLPCERRORS + * @reasoncode LPC::RC_LPCHC_ERROR + * @userdata1 LPCHC Error Status Register + * @userdata2 Reset Level + * @devdesc LpcDD::checkForLpcErrors> Error(s) found in LPCHC + * Status Register + * @custdesc Error(s) found in LPCHC Status Register + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_CHECKFORLPCERRORS, + LPC::RC_LPCHC_ERROR, + lpchc_buffer, + l_lpchcResetLevel); + + l_err->addHwCallout( iv_proc, + HWAS::SRCI_PRIORITY_NONE, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + // Gather addtional ffdc data + addFFDC(l_err); + l_err->collectTrace(LPC_COMP_NAME); + /* @todo - RTC:179179 Use l_opbmResetLevel **/ + hwReset(RESET_OPB_LPCHC_HARD); + } + } - } + }while(0); return l_err; - } - /** * @brief Read an address from LPC space */ @@ -1070,6 +1236,7 @@ errlHndl_t LpcDD::readLPC(LPC::TransType i_type, mutex_lock(ivp_mutex); errlHndl_t l_err = _readLPC( i_type, i_addr, o_buffer, io_buflen ); mutex_unlock(ivp_mutex); + return l_err; } @@ -1085,5 +1252,6 @@ errlHndl_t LpcDD::writeLPC(LPC::TransType i_type, mutex_lock(ivp_mutex); errlHndl_t l_err = _writeLPC( i_type, i_addr, i_buffer, io_buflen ); mutex_unlock(ivp_mutex); + return l_err; } diff --git a/src/usr/lpc/lpcdd.H b/src/usr/lpc/lpcdd.H index 3cebf07bd..be7af6aef 100644 --- a/src/usr/lpc/lpcdd.H +++ b/src/usr/lpc/lpcdd.H @@ -78,6 +78,12 @@ class LpcDD const void* i_buffer, size_t& io_buflen); + /** + * @brief Check For Errors in OPB and LPCHC Status Registers + * + * @return Error log if error found + */ + errlHndl_t checkForLpcErrors(); /** * @brief Enums for different levels of resetting PNOR communication levels @@ -85,10 +91,9 @@ class LpcDD enum ResetLevels { RESET_CLEAR = 0x00000000, /**< Clear Reset Level */ - RESET_ECCB = 0x00000001, /**< ECCB FW Logic */ - RESET_OPB_LPCHC_SOFT = 0x00000002, /**< OPB LPCHC Clear Errors */ - RESET_OPB_LPCHC_HARD = 0x00000004, /**< OPB LPCHC Reset Logic */ - RESET_INIT = 0x00000008, /**< Initial HW setup */ + RESET_OPB_LPCHC_SOFT = 0x00000001, /**< OPB LPCHC Clear Errors */ + RESET_OPB_LPCHC_HARD = 0x00000002, /**< OPB LPCHC Reset Logic */ + RESET_INIT = 0x00000004, /**< Initial HW setup */ }; @@ -156,12 +161,11 @@ class LpcDD /** * @brief Constructor - * @param[in] Processor target associated with the ECCB logic + * @param[in] Processor target associated with the LPC Master */ LpcDD( TARGETING::Target* i_proc = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ); - /** * @brief Destructor */ @@ -181,55 +185,110 @@ class LpcDD /** Size that LPC takes up (0xC0000000 to 0xFFFFFFFF)*/ LPC_SPACE_SIZE = 0x40000000, - /**< OPB LPCM Sync FIR Reg - used to read the FIR*/ - OPB_LPCM_FIR_REG = 0x01010C00, - /**< OPB LPCM Sync FIR Reg WOX_AND - used to clear the FIR */ OPB_LPCM_FIR_WOX_AND_REG = 0x01010C01, /**< OPB LPCM Sync FIR Mask Reg WO_OR - used to set the mask */ OPB_LPCM_FIR_MASK_WO_OR_REG = 0x01010C05, - OPB_LPCM_FIR_ERROR_MASK = 0xFF00000000000000, /**< Error Bits MASK */ - // LPCHC reset-related registers OPB_MASTER_LS_CONTROL_REG = 0x008, /**<OPBM LS Control Reg */ - LPCHC_RESET_REG = 0x0FC, /**<LPC HC Reset Register */ + LPCHC_SYNC_CYCLE_COUNTER_INFINITE = 0xFF000000, + + /** OPB Master Accumulated Status Register - used to check error bits*/ + OPBM_ACCUM_STATUS_REG = 0x0, + + /** OPB Master Actual Status Register */ + OPBM_STATUS_REG = 0x004C, + + /** LPC Host Controller Status Register - used to check error bits*/ + LPCHC_REG = 0x2038, + + /** LPC Host Controller Error Addr Register - used to check error bits*/ + LPCHC_ERROR_ADDR_REG = 0x2040, + + /** LPC Host Controller Reset Register */ + LPCHC_LPC_BUS_ABORT_REG = 0x20F8, + + /** LPC Host Controller Reset Register */ + LPCHC_RESET_REG = 0x20FC, + + OPB_ERROR_MASK = 0x20000FC3, /**< OPBM Error Bits MASK */ + + LPCHC_ERROR_MASK = 0x000004FC /**< LPCHC Error Bits MASK */ - LPCHC_SYNC_CYCLE_COUNTER_INFINITE = 0xFF000000 }; + /** + * @brief OPB Master Status Register Layout + */ + union OpbmErrReg_t + { + uint32_t data32; + struct + { + uint32_t reserved : 2; // 0:1 reserved + uint32_t rxctgtel : 1; // 2 - OPB master timeout + uint32_t rxfwrdcv : 1; // 3 - FW read cache valid(status only) + uint32_t reserved1 : 16; // 4:19 reserved + uint32_t rxits : 1; // 20 - Invalid Transfer Size + uint32_t rxicmd : 1; // 21 - Invalid Command + uint32_t rxiaa : 1; // 22 - Invalid address alignment + uint32_t rxiaddr : 1; // 23 - Invalid address + uint32_t rxopbe : 1; // 24 - OPB error acknowledge + uint32_t rxopbt : 1; // 25 - OPB timeout + uint32_t reserved2 : 4; // 26:29 reserved + uint32_t rxicmdb : 1; // 30 - Command buffer parity error + uint32_t rxidatab : 1; // 31 - Data buffer parity error + + }; + OpbmErrReg_t() : data32(0) {}; + }; /** - * @brief OPB-LPCM FIR Register Layout + * @brief LPCHC Status Register Layout */ - union OpbLpcmFirReg_t + union LpchcErrReg_t { - uint64_t data64; + uint32_t data32; struct { - uint64_t rxits : 1; // Invalid Transfer Size - uint64_t rxicmd : 1; // Invalid Command - uint64_t rxiaa : 1; // Invalid Address Alignment - uint64_t rxhopbe : 1; // OPB Bus Error - uint64_t rxhopbt : 1; // OPB Bus Timeout - uint64_t rxctgtel : 1; // CI Load/CI Store/OPB Master Hang Timeout - uint64_t rxcbpe : 1; // Command Buffer Parity Error - uint64_t rxdbpe : 1; // Data Buffer Parity Error - uint64_t reserved : 56; + uint32_t reserved : 17; // 0:16 IRQSER status - not used + uint32_t reserved1 : 4; // 17:20 reserved + uint32_t lreset : 1; // 21 - Lreset Event + uint32_t reserved2 : 2; // 22:23 reserved + uint32_t syncab : 1; // 24 - Sync Abnormal + uint32_t syncnr : 1; // 25 - Sync no response + uint32_t syncne : 1; // 26 - Sync normal error + uint32_t syncto : 1; // 27 - Sync timeout + uint32_t tctar : 1; // 28 - Target cycle TAR error + uint32_t mctar : 1; // 29 - LPC bus master cycle TAR error + uint32_t mzeror : 1; // 30 - LPC bus master 0 request + uint32_t moner : 1; // 31 - LPC bus master 1 request }; - OpbLpcmFirReg_t() : data64(0) {}; + LpchcErrReg_t() : data32(0) {}; }; /** - * @brief Check For Errors in OPB and LPCHC Status Registers * - * @parm o_resetLevel if error, reset level to clear error - * @return Error log if error found + * @brief Compute the Severity of the errors from the OPBM Status Register + * + * @parm i_opbmErrData Masked bits of the OPBM error register + * @parm o_resetLevel Level of severity of the error reported + */ + void computeOpbmErrSev(OpbmErrReg_t i_opbmErrData, + ResetLevels &o_resetLevel); + + /** + * @brief Compute the Severity of the errors from the LPCHC Status Register + * + * @parm i_lpchcErrData Masked bits of the LPCHC error register + * @parm o_resetLevel Level of severity of the error reported */ - errlHndl_t checkForOpbErrors( ResetLevels &o_resetLevel ); + void computeLpchcErrSev(LpchcErrReg_t i_lpchcErrData, + ResetLevels &o_resetLevel); /** * @brief Sanity check the input address for a LPC op and return @@ -302,7 +361,7 @@ class LpcDD mutex_t* ivp_mutex; /** - * @brief Processor target associated with the ECCB logic + * @brief Processor target associated with the LPC Master */ TARGETING::Target* iv_proc; diff --git a/src/usr/lpc/test/lpcddtest.H b/src/usr/lpc/test/lpcddtest.H index 0883f83e7..2e7395451 100644 --- a/src/usr/lpc/test/lpcddtest.H +++ b/src/usr/lpc/test/lpcddtest.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2014 */ +/* Contributors Listed Below - COPYRIGHT 2014,2017 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -222,6 +222,72 @@ class LpcDdTest : public CxxTest::TestSuite TS_FAIL("LpcDD::test_hwfail> No error from bad address"); } } + + + /** + * @brief LPC HW Failure + * Test handling of invalid address + * This address has to be within the range to pass checkAddr, but + * not actaully a valid LPC address. + */ + void _test_invalidAddress(void) + { + TRACFCOMP( g_trac_lpc, "LpcDdTest::test_invalidAddress>" ); + errlHndl_t l_err = NULL; + + TARGETING::Target* sentinel = + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + size_t opsize = 4; + uint8_t data[12]; + + // pass a bad address + l_err = deviceRead( sentinel, + data, + opsize, + DEVICE_LPC_ADDRESS(LPC::TRANS_ABS,0xDEADBEEF) ); + if( l_err ) + { + delete l_err; + } + else + { + TS_FAIL("LpcDD::test_invalidAddress> No error from invalid address test"); + } + } + + /** + * @brief LPC HW Failure + * Test handling of invalid address + * This address has to be within the range to pass checkAddr, but + * not actaully a valid LPC address. + */ + void _test_invalidTransferSize(void) + { + TRACFCOMP( g_trac_lpc, "LpcDdTest::test_invalidTransferSize>" ); + errlHndl_t l_err = NULL; + + TARGETING::Target* sentinel = + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + size_t opsize = 4; + uint8_t data[12]; + + // pass a bad address + l_err = deviceRead( sentinel, + data, + opsize, + DEVICE_LPC_ADDRESS(LPC::TRANS_ABS,0x12345678) ); + if( l_err ) + { + delete l_err; + } + else + { + TS_FAIL("LpcDD::test_invalidTransferSize> No error from invalid transfer size test"); + } + } + + + }; #endif |