summaryrefslogtreecommitdiffstats
path: root/src/usr/pnor
diff options
context:
space:
mode:
authorMike Baiocchi <baiocchi@us.ibm.com>2014-05-07 14:27:56 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2014-08-13 14:38:39 -0500
commit2f89769eb7f06b4c13f0cd90d2df3d5139fd487b (patch)
treec1cd994d459bb798741ff91d79518ddbc812cd45 /src/usr/pnor
parentd6fcfc8472232a57b57e73ec3479fbee7f94837e (diff)
downloadtalos-hostboot-2f89769eb7f06b4c13f0cd90d2df3d5139fd487b.tar.gz
talos-hostboot-2f89769eb7f06b4c13f0cd90d2df3d5139fd487b.zip
Handle PNOR Errors
This code diagnoses additional PNOR errors and attempts to recover from them. It also contains improvements to FFDC. Change-Id: Ifb1e692a79a0d017e1c4807e0d0bcddbbcd04f6c Backport: release-fips820 RTC: 37744 Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/11253 Tested-by: Jenkins Server Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr/pnor')
-rw-r--r--src/usr/pnor/pnordd.C1286
-rw-r--r--src/usr/pnor/pnordd.H205
2 files changed, 1234 insertions, 257 deletions
diff --git a/src/usr/pnor/pnordd.C b/src/usr/pnor/pnordd.C
index 878194a48..326f14de0 100644
--- a/src/usr/pnor/pnordd.C
+++ b/src/usr/pnor/pnordd.C
@@ -35,12 +35,15 @@
#include <sys/mmio.h>
#include <sys/task.h>
#include <sys/sync.h>
+#include <sys/time.h>
#include <string.h>
+#include <stdio.h>
#include <devicefw/driverif.H>
#include <trace/interface.H>
#include <errl/errlentry.H>
#include <errl/errlmanager.H>
#include <errl/errludlogregister.H>
+#include <errl/errludstring.H>
#include <targeting/common/targetservice.H>
#include "pnordd.H"
#include <pnor/pnorif.H>
@@ -51,8 +54,12 @@
#include <config.h>
-extern trace_desc_t* g_trac_pnor;
+/*****************************************************************************/
+// D e f i n e s
+/*****************************************************************************/
+#define PNORDD_MAX_RETRIES 1
+extern trace_desc_t* g_trac_pnor;
namespace PNOR
{
@@ -156,7 +163,7 @@ errlHndl_t ddWrite(DeviceFW::OperationType i_opType,
l_addr);
if(l_err)
{
- break;
+ break;
}
}while(0);
@@ -177,6 +184,7 @@ bool usingL3Cache()
return Singleton<PnorDD>::instance().usingL3Cache();
}
+
// Register PNORDD access functions to DD framework
DEVICE_REGISTER_ROUTE(DeviceFW::READ,
DeviceFW::PNOR,
@@ -236,7 +244,7 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer,
size_t& io_buflen,
uint64_t i_address)
{
- TRACFCOMP(g_trac_pnor, "PnorDD::writeFlash(i_address=0x%llx)> ", i_address);
+ TRACFCOMP(g_trac_pnor, ENTER_MRK"PnorDD::writeFlash(i_address=0x%llx)> ", i_address);
errlHndl_t l_err = NULL;
do{
@@ -316,14 +324,16 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer,
// write a single block of data out to flash efficiently
mutex_lock(&cv_mutex);
- l_err = compareAndWriteBlock(cur_blkStart_addr,
- cur_writeStart_addr,
- write_bytes,
- (void*)((uint64_t)i_buffer + ((uint64_t)cur_writeStart_addr-l_address)));
+ l_err = compareAndWriteBlock(
+ cur_blkStart_addr,
+ cur_writeStart_addr,
+ write_bytes,
+ (void*)((uint64_t)i_buffer +
+ ((uint64_t)cur_writeStart_addr-l_address)));
+
mutex_unlock(&cv_mutex);
if( l_err ) { break; }
- //@todo (RTC:37744) - How should we handle PNOR errors?
cur_blkStart_addr = cur_blkEnd_addr; //move start to end of current erase block
cur_blkEnd_addr += iv_erasesize_bytes;; //increment end by erase block size.
@@ -342,7 +352,7 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer,
{
io_buflen = 0;
}
- TRACDCOMP(g_trac_pnor,"PnorDD::writeFlash> io_buflen=%.8X",io_buflen);
+ TRACFCOMP(g_trac_pnor,EXIT_MRK"PnorDD::writeFlash(i_address=0x%llx)> io_buflen=%.8X", i_address, io_buflen);
return l_err;
}
@@ -370,6 +380,9 @@ PnorDD::PnorDD( PnorMode_t i_mode,
uint64_t i_fakeSize )
: iv_mode(i_mode)
, iv_ffdc_active(false)
+, iv_error_handled_count(0x0)
+, iv_error_recovery_failed(false)
+, iv_reset_active(false)
{
iv_erasesize_bytes = ERASESIZE_BYTES_DEFAULT;
@@ -458,6 +471,21 @@ void PnorDD::sfcInit( )
{
#ifdef CONFIG_BMC_DOES_SFC_INIT
+ // Set OPB LPCM FIR Mask - hostboot will monitor these FIR bits
+ //@todo (RTC:36950) - add non-master support
+ TARGETING::Target* scom_target =
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL;
+ size_t scom_size = sizeof(uint64_t);
+ uint64_t fir_mask_data = OPB_LPCM_FIR_ERROR_MASK;
+
+ // Read FIR Register
+ l_err = deviceOp( DeviceFW::WRITE,
+ scom_target,
+ &(fir_mask_data),
+ scom_size,
+ DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_MASK_WO_OR_REG));
+ if( l_err ) { break; }
+
//Determine NOR Flash type - triggers vendor specific workarounds
//We also use the chipID in some FFDC situations.
l_err = getNORChipId(cv_nor_chipid);
@@ -471,7 +499,7 @@ void PnorDD::sfcInit( )
SFC_REG_ERASMS,
iv_erasesize_bytes);
if(l_err) { break; }
- TRACFCOMP(g_trac_pnor,"iv_erasesize_bytes=%X",iv_erasesize_bytes);
+ TRACFCOMP(g_trac_pnor,"PnorDD::sfcInit: iv_erasesize_bytes=%X",iv_erasesize_bytes);
#else //==!CONFIG_BMC_DOES_SFC_INIT
@@ -623,6 +651,8 @@ errlHndl_t PnorDD::readRegSfc(SfcRange i_range,
errlHndl_t PnorDD::pollSfcOpComplete(uint64_t i_pollTime)
{
errlHndl_t l_err = NULL;
+ ResetLevels pnorResetLevel = RESET_CLEAR;
+
TRACDCOMP( g_trac_pnor, "PnorDD::pollSfcOpComplete> i_pollTime=0x%.8x",
i_pollTime );
@@ -654,11 +684,9 @@ errlHndl_t PnorDD::pollSfcOpComplete(uint64_t i_pollTime)
}
if( l_err ) { break; }
-
- l_err = checkForErrors();
+ l_err = checkForSfcErrors( pnorResetLevel );
if( l_err ) { break; }
-
// If no errors AND done bit not set, call out undefined error
if( (sfc_stat.done == 0) )
{
@@ -694,33 +722,56 @@ errlHndl_t PnorDD::pollSfcOpComplete(uint64_t i_pollTime)
addFFDCRegisters(l_err);
l_err->collectTrace(PNOR_COMP_NAME);
l_err->collectTrace(XSCOM_COMP_NAME);
- //@todo (RTC:37744) - Any cleanup or recovery needed?
+
+ // Reset LPC Slave since it appears to be hung - handled below
+ pnorResetLevel = RESET_LPC_SLAVE;
+
break;
}
TRACDCOMP(g_trac_pnor,"pollSfcOpComplete> command took %d ns", poll_time);
}while(0);
+ // If we have an error that requires a reset, do that here
+ if ( l_err && ( pnorResetLevel != RESET_CLEAR ) )
+ {
+ errlHndl_t tmp_err = NULL;
+ tmp_err = resetPnor(pnorResetLevel);
+
+ if ( tmp_err )
+ {
+ // Commit reset error as informational since we have
+ // original error l_err
+ TRACFCOMP(g_trac_pnor, "PnorDD::pollSfcOpComplete> Error from resetPnor() after previous error eid=0x%X. Committing resetPnor() error log eid=0x%X.",
+ l_err->eid(), tmp_err->eid());
+ tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL);
+ tmp_err->collectTrace(PNOR_COMP_NAME);
+ tmp_err->plid(l_err->plid());
+ errlCommit(tmp_err, PNOR_COMP_ID);
+ }
+ }
+
return l_err;
}
/**
- * @brief Check For Errors in Status Registers
+ * @brief Check For Errors in SFC Status Registers
*/
-errlHndl_t PnorDD::checkForErrors( void )
+errlHndl_t PnorDD::checkForSfcErrors( ResetLevels &o_pnorResetLevel )
{
-
errlHndl_t l_err = NULL;
bool errorFound = false;
+ // Used to set Reset Levels, if necessary
+ o_pnorResetLevel = RESET_CLEAR;
+
// Default status values in case we fail in reading the registers
LpcSlaveStatReg_t lpc_slave_stat;
lpc_slave_stat.data32 = 0xDEADBEEF;
SfcStatReg_t sfc_stat;
sfc_stat.data32 = 0xDEADBEEF;
-
do {
// First Read LPC Slave Status Register
@@ -731,56 +782,67 @@ errlHndl_t PnorDD::checkForErrors( void )
// If we can't read status register, exit out
if( l_err ) { break; }
- TRACDCOMP( g_trac_pnor, INFO_MRK"PnorDD::checkForErrors> LPC Slave status reg: 0x%08llx",
+ TRACDCOMP( g_trac_pnor, INFO_MRK"PnorDD::checkForSfcErrors> LPC Slave status reg: 0x%08llx",
lpc_slave_stat.data32);
+ // Start with lighter reset level
if( 1 == lpc_slave_stat.lbusparityerror )
{
errorFound = true;
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> LPC Slave Local Bus Parity Error: status reg: 0x%08llx",
- lpc_slave_stat.data32);
+ o_pnorResetLevel = RESET_LPC_SLAVE_ERRS;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LPC Slave Local Bus Parity Error: status reg: 0x%08llx, ResetLevel=%d",
+ lpc_slave_stat.data32, o_pnorResetLevel);
}
+ // Check for more stronger reset level
if( 0 != lpc_slave_stat.lbus2opberr )
{
errorFound = true;
+ // All of these errors require the SFC Local Bus Reset
+ o_pnorResetLevel = RESET_SFC_LOCAL_BUS;
if ( LBUS2OPB_ADDR_PARITY_ERR == lpc_slave_stat.lbus2opberr )
{
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> LBUS2OPB Address Parity Error: LPC Slave status reg: 0x%08llx",
- lpc_slave_stat.data32);
+ // This error also requires LPC Slave Errors to be Cleared
+ o_pnorResetLevel = RESET_SFCBUS_LPCSLAVE_ERRS;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Address Parity Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d",
+ lpc_slave_stat.data32, o_pnorResetLevel);
}
else if ( LBUS2OPB_INVALID_SELECT_ERR == lpc_slave_stat.lbus2opberr)
{
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> LBUS2OPB Invalid Select Error: LPC Slave status reg: 0x%08llx",
- lpc_slave_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Invalid Select Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d",
+ lpc_slave_stat.data32, o_pnorResetLevel);
}
else if ( LBUS2OPB_DATA_PARITY_ERR == lpc_slave_stat.lbus2opberr )
{
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> LBUS2OPB Data Parity Error: LPC Slave status reg: 0x%08llx",
- lpc_slave_stat.data32);
+ // This error also requires LPC Slave Errors to be Cleared
+ o_pnorResetLevel = RESET_SFCBUS_LPCSLAVE_ERRS;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Data Parity Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d",
+ lpc_slave_stat.data32, o_pnorResetLevel);
}
else if ( LBUS2OPB_MONITOR_ERR == lpc_slave_stat.lbus2opberr )
{
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> LBUS2OPB Monitor Error: LPC Slave status reg: 0x%08llx",
- lpc_slave_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Monitor Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d",
+ lpc_slave_stat.data32, o_pnorResetLevel);
}
else if ( LBUS2OPB_TIMEOUT_ERR == lpc_slave_stat.lbus2opberr )
{
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> LBUS2OPB Timeout Error: LPC Slave status reg: 0x%08llx",
- lpc_slave_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Timeout Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d",
+ lpc_slave_stat.data32, o_pnorResetLevel);
}
else
{
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> LBUS2OPB UNKNOWN Error: LPC Slave status reg: 0x%08llx",
- lpc_slave_stat.data32);
+ // Just in case, clear LPC Slave Errors
+ o_pnorResetLevel = RESET_LPC_SLAVE_ERRS;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB UNKNOWN Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d",
+ lpc_slave_stat.data32, o_pnorResetLevel);
}
}
@@ -793,49 +855,51 @@ errlHndl_t PnorDD::checkForErrors( void )
// If we can't read status register, exit out
if( l_err ) { break; }
- TRACDCOMP( g_trac_pnor, INFO_MRK"PnorDD::checkForErrors> SFC status reg(0x%X): 0x%08llx",
+ TRACDCOMP( g_trac_pnor, INFO_MRK"PnorDD::checkForSfcErrors> SFC status reg(0x%X): 0x%08llx",
SFC_CMD_SPACE|SFC_REG_STATUS,sfc_stat.data32);
+ // No resets needed for these errors
if( 1 == sfc_stat.eccerrcntr )
{
errorFound = true;
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> Threshold of SRAM ECC Errors Reached: SFC status reg: 0x%08llx",
- sfc_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Threshold of SRAM ECC Errors Reached: SFC status reg: 0x%08llx, ResetLevel=%d",
+ sfc_stat.data32, o_pnorResetLevel);
}
if( 1 == sfc_stat.eccues )
{
errorFound = true;
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> SRAM Command Uncorrectable ECC Error: SFC status reg: 0x%08llx",
- sfc_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> SRAM Command Uncorrectable ECC Error: SFC status reg: 0x%08llx, ResetLevel=%d",
+ sfc_stat.data32, o_pnorResetLevel);
}
if( 1 == sfc_stat.illegal )
{
errorFound = true;
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> Previous Operation was Illegal: SFC status reg: 0x%08llx",
- sfc_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Previous Operation was Illegal: SFC status reg: 0x%08llx, ResetLevel=%d",
+ sfc_stat.data32, o_pnorResetLevel);
}
if( 1 == sfc_stat.eccerrcntn )
{
errorFound = true;
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> Threshold for Flash ECC Errors Reached: SFC status reg: 0x%08llx",
- sfc_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Threshold for Flash ECC Errors Reached: SFC status reg: 0x%08llx, ResetLevel=%d",
+
+ sfc_stat.data32, o_pnorResetLevel);
}
if( 1 == sfc_stat.eccuen )
{
errorFound = true;
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> Flash Command Uncorrectable ECC Error: SFC status reg: 0x%08llx",
- sfc_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Flash Command Uncorrectable ECC Error: SFC status reg: 0x%08llx, ResetLevel=%d",
+ sfc_stat.data32, o_pnorResetLevel);
}
if( 1 == sfc_stat.timeout )
{
errorFound = true;
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> Timeout: SFC status reg: 0x%08llx",
- sfc_stat.data32);
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Timeout: SFC status reg: 0x%08llx, ResetLevel=%d",
+ sfc_stat.data32, o_pnorResetLevel);
}
}while(0);
@@ -849,32 +913,29 @@ errlHndl_t PnorDD::checkForErrors( void )
// for the found error
if ( l_err )
{
- TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForErrors> Deleting register read error. Returning error created for the found error");
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Deleting register read error. Returning error created for the found error");
delete l_err;
}
/*@
* @errortype
- * @moduleid PNOR::MOD_PNORDD_CHECKFORERRORS
+ * @moduleid PNOR::MOD_PNORDD_CHECKFORSFCERRORS
* @reasoncode PNOR::RC_ERROR_IN_STATUS_REG
* @userdata1[0:31] SFC Status Register
- * @userdata1[32:63] <unused>
- * @userdata2[0:31] LPC Slave Status Register
- * @userdata2[32:63] <unused>
- * @devdesc PnorDD::checkForErrors> Error(s) found in SFC
+ * @userdata1[32:63] LPC Slave Status Register
+ * @userdata2 Reset Level
+ * @devdesc PnorDD::checkForSfcErrors> Error(s) found in SFC
* and/or LPC Slave Status Registers
* @custdesc A problem occurred while accessing the boot flash.
*/
l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
- PNOR::MOD_PNORDD_CHECKFORERRORS,
+ PNOR::MOD_PNORDD_CHECKFORSFCERRORS,
PNOR::RC_ERROR_IN_STATUS_REG,
TWO_UINT32_TO_UINT64(
sfc_stat.data32,
- 0),
- TWO_UINT32_TO_UINT64(
- lpc_slave_stat.data32,
- 0));
+ lpc_slave_stat.data32),
+ o_pnorResetLevel );
// Limited in callout: no PNOR target, so calling out processor
l_err->addHwCallout(
@@ -891,6 +952,168 @@ errlHndl_t PnorDD::checkForErrors( void )
}
+/**
+ * @brief Check For Errors in OPB and LPCHC Status Registers
+ */
+errlHndl_t PnorDD::checkForOpbErrors( ResetLevels &o_pnorResetLevel )
+{
+ errlHndl_t l_err = NULL;
+ bool errorFound = false;
+
+ // Used to set Reset Levels, if necessary
+ o_pnorResetLevel = 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 {
+
+ //@todo (RTC:36950) - add non-master support
+ TARGETING::Target* scom_target =
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL;
+
+ // Read FIR Register
+ l_err = deviceOp( DeviceFW::READ,
+ scom_target,
+ &(fir_data),
+ scom_size,
+ DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG) );
+ if( l_err ) { break; }
+
+
+ // Mask data to just the FIR bits we care about
+ fir_reg.data64 = fir_data & OPB_LPCM_FIR_ERROR_MASK;
+
+ // First look for SOFT errors
+ if( 1 == fir_reg.rxits )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_SOFT;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> Invalid Transfer Size: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+ if( 1 == fir_reg.rxicmd )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_SOFT;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> Invalid Command: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+ if( 1 == fir_reg.rxiaa )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_SOFT;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> Invalid Address Alignment: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+ if( 1 == fir_reg.rxcbpe )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_SOFT;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> Command Buffer Parity Error: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+ if( 1 == fir_reg.rxdbpe )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_SOFT;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> Data Buffer Parity Error: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+
+ // Now look for HARD errors that will override SOFT errors reset Level
+ if( 1 == fir_reg.rxhopbe )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_HARD;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> OPB Bus Error: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+ if( 1 == fir_reg.rxhopbt )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_HARD;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> OPB Bus Timeout: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+ if( 1 == fir_reg.rxctgtel )
+ {
+ errorFound = true;
+ o_pnorResetLevel = RESET_OPB_LPCHC_HARD;
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> CI Load/CI Store/OPB Master Hang Timeout: OPB_LPCM_FIR_REG=0x%.16X, ResetLevel=%d",
+ fir_reg.data64, o_pnorResetLevel);
+ }
+
+
+ }while(0);
+
+
+ // 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 )
+ {
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForOpbErrors> Deleting register read error. Returning error created for the found error");
+ delete l_err;
+ }
+
+ /*@
+ * @errortype
+ * @moduleid PNOR::MOD_PNORDD_CHECKFOROPBERRORS
+ * @reasoncode PNOR::RC_ERROR_IN_STATUS_REG
+ * @userdata1 OPB FIR Register Data
+ * @userdata2 Reset Level
+ * @devdesc PnorDD::checkForOpbErrors> Error(s) found in OPB
+ * and/or LPCHC Status Register
+ */
+ l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ PNOR::MOD_PNORDD_CHECKFORSFCERRORS,
+ PNOR::RC_ERROR_IN_STATUS_REG,
+ fir_reg.data64,
+ o_pnorResetLevel );
+
+ // Limited in callout: no PNOR target, so calling out processor
+ l_err->addHwCallout(
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
+ HWAS::SRCI_PRIORITY_HIGH,
+ HWAS::NO_DECONFIG,
+ HWAS::GARD_NULL );
+
+
+ // Log FIR Register Data
+ ERRORLOG::ErrlUserDetailsLogRegister
+ l_eud(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL);
+
+ l_eud.addDataBuffer(&fir_data, scom_size,
+ DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG));
+
+ l_eud.addToLog(l_err);
+
+ addFFDCRegisters(l_err);
+ l_err->collectTrace(PNOR_COMP_NAME);
+
+ }
+
+ return l_err;
+
+}
+
+
/**
* @brief Add Error Registers to an existing Error Log
@@ -909,7 +1132,7 @@ void PnorDD::addFFDCRegisters(errlHndl_t & io_errl)
{
iv_ffdc_active = true;
- TRACFCOMP( g_trac_pnor, "PnorDD::addFFDCRegisters> adding FFDC to Error Log EID=0x%X, PLID=0x%X",
+ TRACFCOMP( g_trac_pnor, ENTER_MRK"PnorDD::addFFDCRegisters> adding FFDC to Error Log EID=0x%X, PLID=0x%X",
io_errl->eid(), io_errl->plid() );
ERRORLOG::ErrlUserDetailsLogRegister
@@ -938,6 +1161,24 @@ void PnorDD::addFFDCRegisters(errlHndl_t & io_errl)
DEVICE_SCOM_ADDRESS(ECCB_STAT_REG));
}
+ // Add OPB Fir Register
+ tmp_err = deviceOp( DeviceFW::READ,
+ scom_target,
+ &(data64),
+ size64,
+ DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG) );
+
+ if( tmp_err )
+ {
+ delete tmp_err;
+ TRACFCOMP( g_trac_pnor, "PnorDD::addFFDCRegisters> Fail reading OPB_LPCM_FIR_REG");
+ }
+ else
+ {
+ l_eud.addDataBuffer(&data64, size64,
+ DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG));
+ }
+
// Add LPC Slave Status Register
LpcSlaveStatReg_t lpc_slave_stat;
@@ -1013,9 +1254,10 @@ void PnorDD::addFFDCRegisters(errlHndl_t & io_errl)
}while(0);
-
l_eud.addToLog(io_errl);
+ TRACFCOMP( g_trac_pnor, EXIT_MRK"PnorDD::addFFDCRegisters> Information added to error log");
+
// reset FFDC active flag
iv_ffdc_active = false;
}
@@ -1215,110 +1457,120 @@ errlHndl_t PnorDD::getNORChipId(uint32_t& o_chipId,
uint32_t i_spiOpcode)
{
errlHndl_t l_err = NULL;
+ errlHndl_t original_err = NULL;
+ uint8_t retry = 0;
+
TRACFCOMP( g_trac_pnor, "PnorDD::getNORChipId> i_spiOpcode=0x%.8x",
i_spiOpcode );
- do {
- //Configure Get Chip ID opcode
- uint32_t confData = i_spiOpcode << 24;
- confData |= 0x00800003; // 8-> read, 3->3 bytes
- TRACDCOMP( g_trac_pnor, "PnorDD::getNORChipId> confData=0x%.8x",
- confData );
- l_err = writeRegSfc(SFC_CMD_SPACE,
- SFC_REG_CHIPIDCONF,
- confData);
- if(l_err) { break; }
-
- //Issue Get Chip ID command
- SfcCmdReg_t sfc_cmd;
- sfc_cmd.opcode = SFC_OP_CHIPID;
- sfc_cmd.length = 0;
- l_err = writeRegSfc(SFC_CMD_SPACE,
- SFC_REG_CMD,
- sfc_cmd.data32);
- if(l_err) { break; }
+ // reset class variable since we're starting a new operation
+ iv_error_recovery_failed = false;
- //Poll for complete status
- l_err = pollSfcOpComplete();
- if(l_err) { break; }
+ /***********************************************************/
+ /* This do-while loop supports retries */
+ /***********************************************************/
+ do {
- //Read the ChipID from the Command Buffer
- l_err = readRegSfc(SFC_CMDBUF_SPACE,
- 0, //Offset into CMD BUFF space in bytes
- o_chipId);
- if(l_err) {
- break;
- }
+ // group this block together with do-while
+ do{
+ //Configure Get Chip ID opcode
+ uint32_t confData = i_spiOpcode << 24;
+ confData |= 0x00800003; // 8-> read, 3->3 bytes
+ TRACDCOMP( g_trac_pnor, "PnorDD::getNORChipId> confData=0x%.8x",
+ confData );
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_CHIPIDCONF,
+ confData);
+ if(l_err) { break; }
- // Only look at a portion of the data that is returned
- o_chipId &= ID_MASK;
+ //Issue Get Chip ID command
+ SfcCmdReg_t sfc_cmd;
+ sfc_cmd.opcode = SFC_OP_CHIPID;
+ sfc_cmd.length = 0;
- //Some micron chips require a special workaround required
- //so need to set a flag for later use
- //We can't read all 6 bytes above because not all MFG
- //support that operation.
- if( o_chipId == MICRON_NOR_ID )
- {
- // Assume all Micron chips have this bug
- cv_hw_workaround |= HWWK_MICRON_EXT_READ;
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_CMD,
+ sfc_cmd.data32);
+ if(l_err) { break; }
- uint32_t outdata[4];
+ //Poll for complete status
+ l_err = pollSfcOpComplete();
+ if(l_err) { break; }
- //Change ChipID command to read back 6 bytes.
- SfcCustomReg_t new_cmd;
- new_cmd.opcode = SPI_GET_CHIPID_OP;
- new_cmd.read = 1;
- new_cmd.length = 6;
- l_err = readRegFlash( new_cmd,
- outdata );
+ //Read the ChipID from the Command Buffer
+ l_err = readRegSfc(SFC_CMDBUF_SPACE,
+ 0, //Offset into CMD BUFF space in bytes
+ o_chipId);
if(l_err) { break; }
- //If bit 1 is set in 2nd word of cmd buffer data, then
- //We must do the workaround.
- //Ex: CCCCCCLL 40000000
- // CCCCCC -> Industry Standard Chip ID
- // LL -> Length of Micron extended data
- // 4 -> Bit to indicate we must do erase/write workaround
- TRACFCOMP( g_trac_pnor, "PnorDD::getNORChipId> ExtId = %.8X %.8X", outdata[0], outdata[1] );
- if((outdata[1] & 0x40000000) == 0x00000000)
+ // Only look at a portion of the data that is returned
+ o_chipId &= ID_MASK;
+
+ //Some micron chips require a special workaround required
+ //so need to set a flag for later use
+ //We can't read all 6 bytes above because not all MFG
+ //support that operation.
+ if( o_chipId == MICRON_NOR_ID )
{
- TRACFCOMP( g_trac_pnor,
- "PnorDD::getNORChipId> Setting Micron workaround flag"
- );
- //Set Micron workaround flag
- cv_hw_workaround |= HWWK_MICRON_WRT_ERASE;
- }
+ // Assume all Micron chips have this bug
+ cv_hw_workaround |= HWWK_MICRON_EXT_READ;
+
+ uint32_t outdata[4];
+
+ //Change ChipID command to read back 6 bytes.
+ SfcCustomReg_t new_cmd;
+ new_cmd.opcode = SPI_GET_CHIPID_OP;
+ new_cmd.read = 1;
+ new_cmd.length = 6;
+ l_err = readRegFlash( new_cmd,
+ outdata );
+ if(l_err) { break; }
+
+ //If bit 1 is set in 2nd word of cmd buffer data, then
+ //We must do the workaround.
+ //Ex: CCCCCCLL 40000000
+ // CCCCCC -> Industry Standard Chip ID
+ // LL -> Length of Micron extended data
+ // 4 -> Bit to indicate we must do erase/write workaround
+ TRACFCOMP( g_trac_pnor, "PnorDD::getNORChipId> ExtId = %.8X %.8X", outdata[0], outdata[1] );
+ if((outdata[1] & 0x40000000) == 0x00000000)
+ {
+ TRACFCOMP( g_trac_pnor,"PnorDD::getNORChipId> Setting Micron workaround flag");
+ //Set Micron workaround flag
+ cv_hw_workaround |= HWWK_MICRON_WRT_ERASE;
+ }
- //Read SFDP for FFDC
- new_cmd.data32 = 0;
- new_cmd.opcode = SPI_MICRON_READ_SFDP;
- new_cmd.read = 1;
- new_cmd.needaddr = 1;
- new_cmd.clocks = 8;
- new_cmd.length = 16;
- l_err = readRegFlash( new_cmd,
- outdata,
- 0 );
- if(l_err) { break; }
+ //Read SFDP for FFDC
+ new_cmd.data32 = 0;
+ new_cmd.opcode = SPI_MICRON_READ_SFDP;
+ new_cmd.read = 1;
+ new_cmd.needaddr = 1;
+ new_cmd.clocks = 8;
+ new_cmd.length = 16;
+ l_err = readRegFlash( new_cmd,
+ outdata,
+ 0 );
+ if(l_err) { break; }
- //Loop around and grab all 16 bytes
- for( size_t x=0; x<4; x++ )
- {
- TRACFCOMP( g_trac_pnor, "SFDP[%d]=%.8X", x, outdata[x] );
- }
+ //Loop around and grab all 16 bytes
+ for( size_t x=0; x<4; x++ )
+ {
+ TRACFCOMP( g_trac_pnor, "SFDP[%d]=%.8X", x, outdata[x] );
+ }
- //Prove this works
- l_err = micronFlagStatus();
- if(l_err) { delete l_err; }
- }
+ //Prove this works
+ l_err = micronFlagStatus();
+ if(l_err) { delete l_err; }
+ }
+ }while(0); // group of commands
- }while(0);
+ // end of operation - check for retry
+ } while ( shouldRetry(RETRY_getNORChipId, l_err, original_err, retry) );
return l_err;
-
}
@@ -1412,16 +1664,25 @@ errlHndl_t PnorDD::bufferedSfcRead(uint32_t i_addr,
size_t i_size,
void* o_data)
{
- errlHndl_t l_err = NULL;
TRACDCOMP( g_trac_pnor, "PnorDD::bufferedSfcRead> i_addr=0x%.8x, i_size=0x%.8x",
i_addr, i_size );
- do{
+ errlHndl_t l_err = NULL;
+ errlHndl_t original_err = NULL;
+ uint8_t retry = 0;
+
+ // reset class variable since we're starting a new operation
+ iv_error_recovery_failed = false;
+
+ /***********************************************************/
+ /* This do-while loop supports retries */
+ /***********************************************************/
+ do {
switch(iv_mode)
{
case MODEL_REAL_MMIO:
- {
+ {
//Read directly from MMIO space
uint32_t* word_ptr = static_cast<uint32_t*>(o_data);
uint32_t word_size = i_size/4;
@@ -1429,9 +1690,10 @@ errlHndl_t PnorDD::bufferedSfcRead(uint32_t i_addr,
words_read < word_size;
words_read ++ )
{
- l_err = readRegSfc(SFC_MMIO_SPACE,
- i_addr+words_read*4, //MMIO Address offset
- word_ptr[words_read]);
+ l_err = readRegSfc(
+ SFC_MMIO_SPACE,
+ i_addr+words_read*4, //MMIO Address offset
+ word_ptr[words_read]);
if( l_err ) { break; }
}
@@ -1458,8 +1720,9 @@ errlHndl_t PnorDD::bufferedSfcRead(uint32_t i_addr,
if(l_err) { break;}
//read SFC CMD Buffer via MMIO
- l_err = readSfcBuffer(chunk_size,
- (void*)((uint64_t)o_data + (addr-i_addr)));
+ l_err = readSfcBuffer(
+ chunk_size,
+ (void*)((uint64_t)o_data + (addr-i_addr)));
if(l_err) { break;}
addr += chunk_size;
@@ -1484,7 +1747,8 @@ errlHndl_t PnorDD::bufferedSfcRead(uint32_t i_addr,
}
} //end switch
- }while(0);
+ // end of operation - check for retry
+ } while ( shouldRetry(RETRY_bufferedSfcRead, l_err, original_err, retry) );
return l_err;
@@ -1503,8 +1767,16 @@ errlHndl_t PnorDD::bufferedSfcWrite(uint32_t i_addr,
i_addr, i_size );
errlHndl_t l_err = NULL;
+ errlHndl_t original_err = NULL;
+ uint8_t retry = 0;
- do{
+ // reset class variable since we're starting a new operation
+ iv_error_recovery_failed = false;
+
+ /***********************************************************/
+ /* This do-while loop supports retries */
+ /***********************************************************/
+ do {
switch(iv_mode)
{
case MODEL_REAL_CMD:
@@ -1528,7 +1800,7 @@ errlHndl_t PnorDD::bufferedSfcWrite(uint32_t i_addr,
//write data to SFC CMD Buffer via MMIO
l_err = writeSfcBuffer(chunk_size,
- (void*)((uint64_t)i_data + (addr-i_addr)));
+ (void*)((uint64_t)i_data + (addr-i_addr)));
if(l_err) { break;}
//Fetch bits into SFC CMD Buffer
@@ -1554,11 +1826,12 @@ errlHndl_t PnorDD::bufferedSfcWrite(uint32_t i_addr,
//Can't function without PNOR, initiate shutdown.
INITSERVICE::doShutdown( PNOR::RC_UNSUPPORTED_MODE);
}
- }
- }while(0);
+ } // end of switch statement
- return l_err;
+ // end of operation - check for retry
+ } while ( shouldRetry(RETRY_bufferedSfcWrite, l_err, original_err, retry) );
+ return l_err;
}
@@ -1624,6 +1897,7 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr,
uint32_t& o_data)
{
errlHndl_t l_err = NULL;
+ ResetLevels pnorResetLevel = RESET_CLEAR;
do {
@@ -1635,7 +1909,7 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr,
size_t scom_size = sizeof(uint64_t);
// write command register with LPC address to read
- ControlReg_t eccb_cmd;
+ EccbControlReg_t eccb_cmd;
eccb_cmd.read_op = 1;
eccb_cmd.address = i_addr;
l_err = deviceOp( DeviceFW::WRITE,
@@ -1646,7 +1920,7 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr,
if( l_err ) { break; }
// poll for complete and get the data back
- StatusReg_t eccb_stat;
+ EccbStatusReg_t eccb_stat;
uint64_t poll_time = 0;
uint64_t loop = 0;
@@ -1669,11 +1943,11 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr,
// the wait each time through
//TODO tmp remove for VPO, need better polling strategy -- RTC43738
//nanosleep( 0, ECCB_POLL_INCR_NS*(++loop) );
- poll_time += ECCB_POLL_INCR_NS*loop;
+ poll_time += ECCB_POLL_INCR_NS*++loop;
}
if( l_err ) { break; }
- // check for errors or timeout
+ // check for errors or timeout at ECCB level
if( (eccb_stat.data64 & ECCB_LPC_STAT_REG_ERROR_MASK)
|| (eccb_stat.op_done == 0) )
{
@@ -1706,17 +1980,44 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr,
addFFDCRegisters(l_err);
l_err->collectTrace(PNOR_COMP_NAME);
l_err->collectTrace(XSCOM_COMP_NAME);
- //@todo (RTC:37744) - Any cleanup or recovery needed?
+
+ // Reset ECCB - handled below
+ pnorResetLevel = RESET_ECCB;
+
break;
}
+ // check for errors at OPB level
+ l_err = checkForOpbErrors( pnorResetLevel );
+ if( l_err ) { break; }
// copy data out to caller's buffer
o_data = eccb_stat.read_data;
} while(0);
+
+ // If we have an error that requires a reset, do that here
+ if ( l_err && ( pnorResetLevel != RESET_CLEAR ) )
+ {
+ errlHndl_t tmp_err = NULL;
+ tmp_err = resetPnor(pnorResetLevel);
+
+ if ( tmp_err )
+ {
+ // Commit reset error since we have original error l_err
+ TRACFCOMP(g_trac_pnor, "PnorDD::readLPC Error from resetPnor() after previous error eid=0x%X. Committing resetPnor() error log eid=0x%X.",
+ l_err->eid(), tmp_err->eid());
+
+ tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL);
+ tmp_err->collectTrace(PNOR_COMP_NAME);
+ tmp_err->plid(l_err->plid());
+ errlCommit(tmp_err, PNOR_COMP_ID);
+ }
+ }
+
+
return l_err;
}
@@ -1727,6 +2028,8 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr,
uint32_t i_data)
{
errlHndl_t l_err = NULL;
+ ResetLevels pnorResetLevel = RESET_CLEAR;
+
TRACDCOMP(g_trac_pnor, "writeLPC> %.8X = %.8X", i_addr, i_data );
@@ -1752,7 +2055,7 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr,
if( l_err ) { break; }
// write command register with LPC address to write
- ControlReg_t eccb_cmd;
+ EccbControlReg_t eccb_cmd;
eccb_cmd.read_op = 0;
eccb_cmd.address = i_addr;
TRACDCOMP(g_trac_pnor, "writeLPC> Write ECCB command register, cmd=0x%.16x", eccb_cmd.data64 );
@@ -1765,7 +2068,7 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr,
// poll for complete
- StatusReg_t eccb_stat;
+ EccbStatusReg_t eccb_stat;
uint64_t poll_time = 0;
uint64_t loop = 0;
while( poll_time < ECCB_POLL_TIME_NS )
@@ -1789,11 +2092,11 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr,
// the wait each time through
//TODO tmp remove for VPO, need better polling strategy -- RTC43738
//nanosleep( 0, ECCB_POLL_INCR_NS*(++loop) );
- poll_time += ECCB_POLL_INCR_NS*loop;
+ poll_time += ECCB_POLL_INCR_NS*++loop;
}
if( l_err ) { break; }
- // check for errors
+ // check for errors at ECCB level
if( (eccb_stat.data64 & ECCB_LPC_STAT_REG_ERROR_MASK)
|| (eccb_stat.op_done == 0) )
{
@@ -1824,13 +2127,39 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr,
addFFDCRegisters(l_err);
l_err->collectTrace(PNOR_COMP_NAME);
l_err->collectTrace(XSCOM_COMP_NAME);
- //@todo (RTC:37744) - Any cleanup or recovery needed?
+
+ // Reset ECCB - handled below
+ pnorResetLevel = RESET_ECCB;
+
break;
}
+ // check for errors at OPB level
+ l_err = checkForOpbErrors( pnorResetLevel );
+
+ if( l_err ) { break; }
} while(0);
+ // If we have an error that requires a reset, do that here
+ if ( l_err && ( pnorResetLevel != RESET_CLEAR ) )
+ {
+ errlHndl_t tmp_err = NULL;
+ tmp_err = resetPnor(pnorResetLevel);
+
+ if ( tmp_err )
+ {
+ // Commit reset error since we have original error l_err
+ TRACFCOMP(g_trac_pnor, "PnorDD::writeLPC Error from resetPnor() after previous error eid=0x%X. Committing resetPnor() error log eid=0x%X.",
+ l_err->eid(), tmp_err->eid());
+
+ tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL);
+ tmp_err->collectTrace(PNOR_COMP_NAME);
+ tmp_err->plid(l_err->plid());
+ errlCommit(tmp_err, PNOR_COMP_ID);
+ }
+ }
+
return l_err;
}
@@ -1895,6 +2224,7 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart,
break;
}
+
//STEP 3: If the need to erase was detected, read out the rest of the Erase block
if(need_erase)
{
@@ -1929,9 +2259,10 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart,
l_err = eraseFlash( i_blockStart );
if( l_err ) { break; }
- //STEP 4: Write the data back out - need to write everything since we erased the block
- //re-write data before new data to write
+ //STEP 4: Write the data back out -
+ // need to write everything since we erased the block
+ // re-write data before new data to write
if(i_writeStart > i_blockStart)
{
TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Writing beginning data i_blockStart=0x%.8x, readLen=0x%.8x",
@@ -1990,7 +2321,7 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart,
delete[] read_data;
}
- TRACDCOMP(g_trac_pnor,"<<compareAndWriteBlock() Exit");
+ TRACFCOMP(g_trac_pnor,"<<compareAndWriteBlock() Exit");
return l_err;
@@ -2001,9 +2332,12 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart,
*/
errlHndl_t PnorDD::eraseFlash(uint32_t i_address)
{
- errlHndl_t l_err = NULL;
TRACFCOMP(g_trac_pnor, ">>PnorDD::eraseFlash> Block 0x%.8X", i_address );
+ errlHndl_t l_err = NULL;
+ errlHndl_t original_err = NULL;
+ uint8_t retry = 0;
+
do {
if( findEraseBlock(i_address) != i_address )
{
@@ -2026,98 +2360,121 @@ errlHndl_t PnorDD::eraseFlash(uint32_t i_address)
break;
}
- for(uint32_t idx = 0; idx < ERASE_COUNT_MAX; idx++ )
- {
- if(iv_erases[idx].addr == i_address)
+ /***********************************************************/
+ /* Attempt erase multiple times */
+ /***********************************************************/
+ // reset class variable since we're starting a new operation
+ iv_error_recovery_failed = false;
+
+
+ // This do-while loop supports retries
+ do {
+
+ for(uint32_t idx = 0; idx < ERASE_COUNT_MAX; idx++ )
{
- iv_erases[idx].count++;
- TRACFCOMP(g_trac_pnor,
- "PnorDD::eraseFlash> Block 0x%.8X has %d erasures",
- i_address, iv_erases[idx].count );
- break;
+ if(iv_erases[idx].addr == i_address)
+ {
+ iv_erases[idx].count++;
+ TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Block 0x%.8X has %d erasures",
+ i_address, iv_erases[idx].count );
+ break;
+ }
+ //iv_erases is init to all 0xff,
+ // so can use ~0 to check for an unused position
+ else if(iv_erases[idx].addr == ~0u)
+ {
+ iv_erases[idx].addr = i_address;
+ iv_erases[idx].count = 1;
+ TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Block 0x%.8X has %d erasures",
+ i_address, iv_erases[idx].count );
+ break;
+ }
+ else if( idx == (ERASE_COUNT_MAX - 1))
+ {
+ TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Erase counter full! Block 0x%.8X Erased",
+ i_address );
+ break;
+ }
}
- //iv_erases is init to all 0xff,
- // so can use ~0 to check for an unused position
- else if(iv_erases[idx].addr == ~0u)
+
+ if( (MODEL_MEMCPY == iv_mode) || (MODEL_LPC_MEM == iv_mode))
{
- iv_erases[idx].addr = i_address;
- iv_erases[idx].count = 1;
- TRACFCOMP(g_trac_pnor,
- "PnorDD::eraseFlash> Block 0x%.8X has %d erasures",
- i_address, iv_erases[idx].count );
- break;
+ erase_fake_pnor( i_address, iv_erasesize_bytes );
+ break; //all done
}
- else if( idx == (ERASE_COUNT_MAX - 1))
+
+ else if(cv_nor_chipid != 0)
{
- TRACFCOMP(g_trac_pnor,
- "PnorDD::eraseFlash> Erase counter full! Block 0x%.8X Erased",
- i_address );
- break;
- }
- }
- if( (MODEL_MEMCPY == iv_mode) || (MODEL_LPC_MEM == iv_mode))
- {
- erase_fake_pnor( i_address, iv_erasesize_bytes );
- break; //all done
- }
+ // group this block together with do-while
+ do{
+ TRACDCOMP(g_trac_pnor, "PnorDD::eraseFlash> Erasing flash for cv_nor_chipid=0x%.8x, iv_mode=0x%.8x",
+ cv_nor_chipid, iv_mode);
- else if(cv_nor_chipid != 0)
- {
- TRACDCOMP(g_trac_pnor,
- "PnorDD::eraseFlash> Erasing flash for cv_nor_chipid=0x%.8x, iv_mode=0x%.8x",
- cv_nor_chipid, iv_mode);
+ //Write erase address to ADR reg
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_ADR,
+ i_address);
- //Write erase address to ADR reg
- l_err = writeRegSfc(SFC_CMD_SPACE,
- SFC_REG_ADR,
- i_address);
+ if(l_err) { break; }
- //Issue Erase command
- SfcCmdReg_t sfc_cmd;
- sfc_cmd.opcode = SFC_OP_ERASM;
- sfc_cmd.length = 0; //Not used for erase
+ //Issue Erase command
+ SfcCmdReg_t sfc_cmd;
+ sfc_cmd.opcode = SFC_OP_ERASM;
+ sfc_cmd.length = 0; //Not used for erase
- l_err = writeRegSfc(SFC_CMD_SPACE,
- SFC_REG_CMD,
- sfc_cmd.data32);
- if(l_err) { break; }
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_CMD,
+ sfc_cmd.data32);
+ if(l_err) { break; }
- //Poll for complete status
- l_err = pollSfcOpComplete();
- if(l_err) { break; }
+ //Poll for complete status
+ l_err = pollSfcOpComplete();
+ if(l_err) { break; }
+
+ //check for special Micron Flag Status reg
+ if(cv_hw_workaround & HWWK_MICRON_WRT_ERASE)
+ {
+ l_err = micronFlagStatus();
+ if(l_err) { break; }
+ }
- //check for special Micron Flag Status reg
- if(cv_hw_workaround & HWWK_MICRON_WRT_ERASE)
+ }while(0); // group of commands
+
+ }
+ else
{
- l_err = micronFlagStatus();
+ TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Erase not supported for cv_nor_chipid=%d",
+ cv_nor_chipid );
+
+ /*@
+ * @errortype
+ * @moduleid PNOR::MOD_PNORDD_ERASEFLASH
+ * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION
+ * @userdata1 NOR Chip ID
+ * @userdata2 LPC Address to erase
+ * @devdesc PnorDD::eraseFlash> No support for MODEL_REAL
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ PNOR::MOD_PNORDD_ERASEFLASH,
+ PNOR::RC_UNSUPPORTED_OPERATION,
+ static_cast<uint64_t>(cv_nor_chipid),
+ i_address,
+ true /*Add HB SW Callout*/ );
+ l_err->collectTrace(PNOR_COMP_NAME);
+
+ // this error can't be fixed with a retry, so just break
if(l_err) { break; }
+
}
- }
- else
- {
- TRACFCOMP(g_trac_pnor,
- "PnorDD::eraseFlash> Erase not supported for cv_nor_chipid=%d",
- cv_nor_chipid );
- /*@
- * @errortype
- * @moduleid PNOR::MOD_PNORDD_ERASEFLASH
- * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION
- * @userdata1 NOR Chip ID
- * @userdata2 LPC Address to erase
- * @devdesc PnorDD::eraseFlash> No support for MODEL_REAL yet
- * @custdesc A problem occurred while accessing the boot flash.
- */
- l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE,
- PNOR::MOD_PNORDD_ERASEFLASH,
- PNOR::RC_UNSUPPORTED_OPERATION,
- static_cast<uint64_t>(cv_nor_chipid),
- i_address,
- true /*Add HB SW Callout*/ );
- l_err->collectTrace(PNOR_COMP_NAME);
- }
+ // end of operation - check for retry
+ } while ( shouldRetry(RETRY_eraseFlash, l_err, original_err, retry) );
+
+ if(l_err) { break;}
+
} while(0);
return l_err;
@@ -2255,3 +2612,442 @@ void PnorDD::erase_fake_pnor( uint64_t i_pnorAddr,
}
+
+errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel )
+{
+ errlHndl_t l_err = NULL;
+
+ // check iv_reset_active to avoid infinite loops
+ // and don't reset if in the middle of FFDC collection
+ if ( ( iv_reset_active == false ) &&
+ ( iv_ffdc_active == false ) )
+ {
+ iv_reset_active = true;
+
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> i_pnorResetLevel=0x%.8X", i_pnorResetLevel);
+
+ do {
+
+ // Setup common scom target
+ //@todo (RTC:36950) - add non-master support
+ TARGETING::Target* scom_target =
+ TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL;
+
+ // always read/write 64 bits to SCOM
+ uint64_t scom_data_64 = 0x0;
+ size_t scom_size = sizeof(uint64_t);
+
+ // 32 bits for address and data for LPC operations
+ uint32_t lpc_addr=0;
+ uint32_t lpc_data=0;
+
+
+ // To Avoid Infinite Loop - skip recovery if it already failed
+ if ( iv_error_recovery_failed == true )
+ {
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Recovery Previously Failed (%d). Skipping reset to avoid infinite loop", iv_error_recovery_failed);
+ break;
+ }
+
+ /***************************************/
+ /* Handle the different reset levels */
+ /***************************************/
+ switch(i_pnorResetLevel)
+ {
+ case RESET_CLEAR:
+ {// Nothing to do here, so just break
+ break;
+ }
+
+ case RESET_ECCB:
+ {
+ // Write Reset Register to reset FW Logic registers
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing ECCB_RESET_REG to reset ECCB FW Logic");
+ scom_data_64 = 0x0;
+ l_err = deviceOp( DeviceFW::WRITE,
+ scom_target,
+ &(scom_data_64),
+ scom_size,
+ DEVICE_SCOM_ADDRESS(ECCB_RESET_REG) );
+
+ break;
+ }
+
+ case RESET_OPB_LPCHC_SOFT:
+ {
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing OPB_MASTER_LS_CONTROL_REG to disable then enable First Error Data Capture");
+
+ // First read OPB_MASTER_LS_CONTROL_REG
+ lpc_addr = OPB_MASTER_LS_CONTROL_REG;
+ lpc_data = 0x0;
+
+ l_err = readLPC(lpc_addr, lpc_data);
+
+ if (l_err) { break; }
+
+ // Disable 'First Error Data Capture' - set bit 29 to 0b1
+ lpc_data |= 0x00000004;
+
+ l_err = writeLPC(lpc_addr, lpc_data);
+
+ if (l_err) { break; }
+
+
+ // Enable 'First Error Data Capture' - set bit 29 to 0b0
+ // No wait-time needed
+ lpc_data &= 0xFFFFFFFB;
+
+ l_err = writeLPC(lpc_addr, lpc_data);
+
+ if (l_err) { break; }
+
+ // Clear FIR register
+ scom_data_64 = ~(OPB_LPCM_FIR_ERROR_MASK);
+ l_err = deviceOp(
+ DeviceFW::WRITE,
+ scom_target,
+ &(scom_data_64),
+ scom_size,
+ DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_WOX_AND_REG) );
+ break;
+ }
+
+ case RESET_OPB_LPCHC_HARD:
+ {
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing LPCHC_RESET_REG to reset LPCHC Logic");
+ lpc_addr = LPCHC_RESET_REG;
+ lpc_data = 0x0;
+
+ l_err = writeLPC(lpc_addr, lpc_data);
+
+ // sleep 1ms for LPCHC to execute internal
+ // reset+init sequence
+ nanosleep( 0, NS_PER_MSEC );
+
+ if (l_err) { break; }
+
+ // Clear FIR register
+ scom_data_64 = ~(OPB_LPCM_FIR_ERROR_MASK);
+ l_err = deviceOp(
+ DeviceFW::WRITE,
+ scom_target,
+ &(scom_data_64),
+ scom_size,
+ DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_WOX_AND_REG) );
+
+ break;
+ }
+
+ case RESET_LPC_SLAVE:
+ {
+ // @todo RTC 109999 - Skipping because SFC resets can
+ // cause problems on subsequent reads and writes
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_LPC_SLAVE");
+
+#if 0
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bit0 of LPC_SLAVE_REG_RESET to reset LPC Slave Logic");
+ lpc_addr = LPC_SLAVE_REG_RESET;
+ lpc_data = 0x80000000;
+
+ l_err = writeLPC(lpc_addr, lpc_data);
+#endif
+ break;
+ }
+
+ case RESET_LPC_SLAVE_ERRS:
+ {
+ // @todo RTC 109999 - Skipping because SFC resets can
+ // cause problems on subsequent reads and writes
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_LPC_SLAVE_ERRS");
+#if 0
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bit1 of LPC_SLAVE_REG_RESET to reset LPC Slave Errors");
+ lpc_addr = LPC_SLAVE_REG_RESET;
+ lpc_data = 0x40000000;
+
+ l_err = writeLPC(lpc_addr, lpc_data);
+#endif
+ break;
+ }
+
+
+ case RESET_SFC_LOCAL_BUS:
+ {
+ // @todo RTC 109999 - Skipping because SFC resets can
+ // cause problems on subsequent reads and writes
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_SFC_LPC_LOCAL_BUS");
+#if 0
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bit2 of LPC_SLAVE_REG_RESET to reset Local SFC Bus. Requires PNOR reinitialization");
+ lpc_addr = LPC_SLAVE_REG_RESET;
+ lpc_data = 0x20000000;
+
+ l_err = writeLPC(lpc_addr, lpc_data);
+
+ if (l_err) { break; }
+
+ l_err = PnorDD::reinitializeSfc();
+#endif
+ break;
+ }
+
+ case RESET_SFCBUS_LPCSLAVE_ERRS:
+ {
+ // @todo RTC 109999 - Skipping because SFC resets can
+ // cause problems on subsequent reads and writes
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_SFCBUS_LPCSLAVE_ERRS");
+#if 0
+ // Must handle both errors
+ TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bits1,2 of LPC_SLAVE_REG_RESET to reset LPC Slave Errors and Local SFC Bus. Requires PNOR reinitialization");
+ lpc_addr = LPC_SLAVE_REG_RESET;
+ lpc_data = 0x60000000;
+
+ l_err = writeLPC(lpc_addr, lpc_data);
+
+ if (l_err) { break; }
+
+ l_err = PnorDD::reinitializeSfc();
+#endif
+ break;
+ }
+
+ // else - unsupported reset level
+ default:
+ {
+
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::resetPnor> Unsupported Reset Level Passed In: 0x%X", i_pnorResetLevel);
+
+ /*@
+ * @errortype
+ * @moduleid PNOR::MOD_PNORDD_RESETPNOR
+ * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION
+ * @userdata1 Unsupported Reset Level Parameter
+ * @userdata2 <unused>
+ * @devdesc PnorDD::resetPnor> Unsupported Reset Level
+ * requested
+ */
+ l_err = new ERRORLOG::ErrlEntry(
+ ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ PNOR::MOD_PNORDD_RESETPNOR,
+ PNOR::RC_UNSUPPORTED_OPERATION,
+ i_pnorResetLevel,
+ 0,
+ true /*SW error*/);
+
+ l_err->collectTrace(PNOR_COMP_NAME);
+ break;
+ }
+ }// end switch
+
+ if ( l_err )
+ {
+ // Indicate that we weren't successful in resetting PNOR
+ iv_error_recovery_failed = true;
+ TRACFCOMP( g_trac_pnor,ERR_MRK"PnorDD::resetPnor>> Fail doing PNOR reset at level 0x%X (recovery count=%d): eid=0x%X", i_pnorResetLevel, iv_error_handled_count, l_err->eid());
+ }
+ else
+ {
+ // Successful, so increment recovery count
+ iv_error_handled_count++;
+
+ TRACFCOMP( g_trac_pnor,INFO_MRK"PnorDD::resetPnor>> Successful PNOR reset at level 0x%X (recovery count=%d)", i_pnorResetLevel, iv_error_handled_count);
+ }
+
+
+ } while(0);
+
+ // reset RESET active flag
+ iv_reset_active = false;
+ }
+
+ return l_err;
+}
+
+
+
+errlHndl_t PnorDD::reinitializeSfc( void )
+{
+ TRACFCOMP(g_trac_pnor, "PnorDD::reinitializeSfc>");
+
+ errlHndl_t l_err = NULL;
+
+ //Initial configuration settings for SFC:
+ #define oadrnb_init 0x0C000000 //Set MMIO/Direct window to start at 64MB
+ #define oadrns_init 0x0000000F //Set the MMIO/Direct window size to 64MB
+ #define adrcbf_init 0x00000000 //Set the flash index to 0
+ #define adrcmf_init 0x0000000F //Set the flash size to 64MB
+ #define conf_init 0x00000002 //Disable Direct Access Cache
+
+ do {
+
+#ifdef PNORDD_FSPATTACHED
+
+ TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::reinitializeSfc> Need to Re-Initialize SFC. Calling doShutdown(PNOR::RC_REINITIALIZE_SFC)");
+
+ l_err = NULL; // to avoid compiler warnings
+
+ //Shutdown if SFC needs to be re-initialized
+ INITSERVICE::doShutdown( PNOR::RC_REINITIALIZE_SFC);
+
+#else
+
+
+ // Clear member variable in case we don't successfully
+ // reinitialize PNOR
+ cv_sfcInitDone = false;
+
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_OADRNB,
+ oadrnb_init);
+ if(l_err) { break; }
+
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_OADRNS,
+ oadrns_init);
+ if(l_err) { break; }
+
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_ADRCBF,
+ adrcbf_init);
+ if(l_err) { break; }
+
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_ADRCMF,
+ adrcmf_init);
+ if(l_err) { break; }
+
+ l_err = writeRegSfc(SFC_CMD_SPACE,
+ SFC_REG_CONF,
+ conf_init);
+ if(l_err) { break; }
+
+
+ //Determine NOR Flash type - triggers vendor specific workarounds
+ //We also use the chipID in some FFDC situations.
+ l_err = getNORChipId(cv_nor_chipid);
+ if(l_err) { break; }
+
+ TRACFCOMP(g_trac_pnor,
+ "PnorDD::reinitializeSfc> cv_nor_chipid=0x%.8x> ",
+ cv_nor_chipid );
+
+ l_err = readRegSfc(SFC_CMD_SPACE,
+ SFC_REG_ERASMS,
+ iv_erasesize_bytes);
+ if(l_err) { break; }
+
+ // Good path if you made it here: reset class variable
+ cv_sfcInitDone = true;
+
+#endif
+
+ } while(0);
+
+ if (l_err)
+ {
+ TRACFCOMP( g_trac_pnor,INFO_MRK"PnorDD::reinitializeSfc> SFC reinitialization FAILED - l_err rc=0x%X, eid=%d", l_err->reasonCode(), l_err->eid());
+ iv_error_recovery_failed = true;
+
+ }
+
+ return l_err;
+}
+
+
+bool PnorDD::shouldRetry( RetryOp i_op,
+ errlHndl_t& io_err,
+ errlHndl_t& io_original_err,
+ uint8_t& io_retry_count )
+{
+ TRACDCOMP(g_trac_pnor, ENTER_MRK"PnorDD::shouldRetry(%d)", i_op);
+
+ bool should_retry = false;
+
+ if ( ( io_err == NULL ) || ( iv_error_recovery_failed == true ) )
+ {
+ // Either Operation was successful OR error recovery failed -
+ // either way don't retry
+ should_retry = false;
+
+ // Error logs handled below
+ }
+ else
+ {
+ // Operation failed
+ // If op will be attempted again: save log and continue
+ if ( io_retry_count < PNORDD_MAX_RETRIES )
+ {
+ // Save original error - and only original error
+ if ( io_original_err == NULL )
+ {
+ io_original_err = io_err;
+ io_err = NULL;
+
+ TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::shouldRetry(%d)> Error rc=0x%X, eid=0x%X, retry/MAX=%d/%d. Save error and retry", i_op, io_original_err->reasonCode(), io_original_err->eid(), io_retry_count, PNORDD_MAX_RETRIES);
+
+ io_original_err->collectTrace(PNOR_COMP_NAME);
+ }
+ else
+ {
+ // Add data to original error
+ TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::shouldRetry(%d)> Another Error rc=0x%X, eid=0x%X, plid=%d, retry/MAX=%d/%d. Delete error and retry", i_op, io_err->reasonCode(), io_err->eid(), io_err->plid(), io_retry_count, PNORDD_MAX_RETRIES);
+
+ char err_str[80];
+ snprintf(err_str, sizeof(err_str), "Another fail: Deleted "
+ "Retried Error Log rc=0x%.8X eid=0x%.8X",
+ io_err->reasonCode(), io_err->eid());
+
+ ERRORLOG::ErrlUserDetailsString(err_str)
+ .addToLog(io_original_err);
+
+ // Delete this new error
+ delete io_err;
+ io_err = NULL;
+ }
+
+ should_retry = true;
+ io_retry_count++;
+
+ }
+ else // no more retries: trace and break
+ {
+ should_retry = false;
+
+ TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::shouldRetry(%d)> Another Error rc=0x%X, eid=0x%X, No More Retries (retry/MAX=%d/%d). Returning Original Error (rc=0x%X, eid=0x%X)", i_op, io_err->reasonCode(), io_err->eid(), io_retry_count, PNORDD_MAX_RETRIES, io_original_err->reasonCode(), io_original_err->eid());
+
+ // error logs handled below
+ }
+ }
+
+ // Handle saved error if we're not retrying
+ if ( ( io_original_err != NULL) && ( should_retry == false ) )
+ {
+ if (io_err)
+ {
+ // commit l_err with original error PLID as informational
+ io_err->plid(io_original_err->plid());
+ io_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL);
+ TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::shouldRetry(%d)> Committing latest io_err eid=0x%X with plid of original err (eid=0x%X): plid=0x%X", i_op, io_err->eid(), io_original_err->plid(), io_err->plid());
+
+ io_err->collectTrace(PNOR_COMP_NAME);
+
+ errlCommit(io_err, PNOR_COMP_ID);
+
+ // return original error
+ io_err = io_original_err;
+
+ // set io_original_err to NULL to avoid dual references
+ io_original_err = NULL;
+ }
+ else
+ {
+ // Since we eventually succeeded, delete original error
+ TRACFCOMP(g_trac_pnor, "PnorDD::shouldRetry(%d)> Op successful, deleting saved err eid=0x%X, plid=0x%X", i_op, io_original_err->eid(), io_original_err->plid());
+
+ delete io_original_err;
+ io_original_err = NULL;
+ }
+ }
+
+ TRACDCOMP(g_trac_pnor, EXIT_MRK"PnorDD::shouldRetry(%d)> return %d (io_retry_count=%d)", i_op, should_retry, io_retry_count);
+
+ return should_retry;
+}
diff --git a/src/usr/pnor/pnordd.H b/src/usr/pnor/pnordd.H
index b5628930d..93d03d117 100644
--- a/src/usr/pnor/pnordd.H
+++ b/src/usr/pnor/pnordd.H
@@ -28,6 +28,7 @@
#include <limits.h>
#include <config.h>
+namespace PNOR { class UdPnorDDParms; }
/** @file pnordd.H
* @brief Provides the interfaces to the PNOR Device Driver
@@ -256,7 +257,6 @@ class PnorDD
};
-
/**
* Flags used to trigger Hardware workarounds
*/
@@ -271,6 +271,33 @@ class PnorDD
};
/**
+ * Enums for different levels of resetting PNOR communication levels
+ */
+ 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_LPC_SLAVE = 0x00000008, /**< LPC Slave Logic on SFC */
+ RESET_LPC_SLAVE_ERRS = 0x00000010, /**< LPC Slave Errors on SFC */
+ RESET_SFC_LOCAL_BUS = 0x00000020, /**< SFC Local Bus */
+ // Known possible combination:
+ RESET_SFCBUS_LPCSLAVE_ERRS = 0x00000030, /**< Bus and LPC Slave Errs */
+ };
+
+ /**
+ * Enums for different operations that might be re-tried
+ */
+ enum RetryOp {
+ RETRY_NOOP = 0,
+ RETRY_getNORChipId = 1,
+ RETRY_bufferedSfcRead = 2,
+ RETRY_bufferedSfcWrite = 3,
+ RETRY_eraseFlash = 4,
+ };
+
+
+ /**
* @brief SFC Command Register Layout
*/
union SfcCmdReg_t
@@ -383,6 +410,36 @@ class PnorDD
};
/**
+ * @brief OPB-LPCM FIR Register Layout
+ */
+ union OpbLpcmFirReg_t
+ {
+ uint64_t data64;
+ 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;
+ };
+ OpbLpcmFirReg_t() : data64(0) {};
+ };
+
+ /**
+ * @brief LPCHC Registers
+ * These are offsets within the LPCHC Register Space
+ */
+ enum LpcHcRegAddr {
+ LPCHC_REG_RESET = 0xFC, /**< RESET : write-only */
+ };
+
+
+ /**
* @brief Write a SFC Register
*
* @parm i_range SFC Address Range
@@ -547,9 +604,10 @@ class PnorDD
#endif
LPC_TOP_OF_FLASH_OFFSET = 0xFFFFFFFF,
- ECCB_CTL_REG = 0x000B0020, /**< ECCB Control Reg (FW) */
- ECCB_STAT_REG = 0x000B0022, /**< ECCB Status Reg (FW) */
- ECCB_DATA_REG = 0x000B0023, /**< ECCB Data Reg (FW) */
+ ECCB_CTL_REG = 0x000B0020, /**< ECCB Control Reg (FW) */
+ ECCB_RESET_REG = 0x000B0021, /**< ECCB Reset Reg (FW) */
+ ECCB_STAT_REG = 0x000B0022, /**< ECCB Status Reg (FW) */
+ ECCB_DATA_REG = 0x000B0023, /**< ECCB Data Reg (FW) */
// Default Values to set for all operations
// 1101.0100.0000.000x.0000.0001.0000.0000.<address>
@@ -558,13 +616,27 @@ class PnorDD
// Error bits: 41-43, 56 (52=cmd complete) (not 57: only non-fw use)
ECCB_LPC_STAT_REG_ERROR_MASK = 0x0000000000700080, /**< Error Bits */
+ /**< 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 = 0xC0010008, /**<OPBM LS Control Reg */
+ LPCHC_RESET_REG = 0xC00120FC, /**<LPC HC Reset Register */
+
ERASE_COUNT_MAX = 64, /**<Max number of tracked erase blocks */
ERASESIZE_BYTES_DEFAULT = 4 * KILOBYTE, /**< Min Erase Block (bytes) */
ECCB_POLL_TIME_NS = 400000, /**< max time from Manfred Walz is 400ms */
ECCB_POLL_INCR_NS = 10, /**< minimum increment during poll */
};
-
/**
* @brief Read an address from LPC space
*
@@ -579,7 +651,7 @@ class PnorDD
/**
* @brief Write an address from LPC space
*
- * @parm i_addr Absolute LPC Address
+
* @parm o_data Data to write
*
* @return Error from operation
@@ -679,11 +751,22 @@ class PnorDD
size_t i_size );
/**
- * @brief Check For Errors in Status Registers
+ * @brief Check For Errors in SFC Status Registers
+ *
+ * @parm o_pnorResetLevel if error, reset level to clear error
+ * @return Error log if error found
+ */
+ errlHndl_t checkForSfcErrors( ResetLevels &o_pnorResetLevel );
+
+
+ /**
+ * @brief Check For Errors in OPB and LPCHC Status Registers
*
+ * @parm o_pnorResetLevel if error, reset level to clear error
* @return Error log if error found
*/
- errlHndl_t checkForErrors( void );
+ errlHndl_t checkForOpbErrors( ResetLevels &o_pnorResetLevel );
+
/**
* @brief Add FFDC Error Registers to an existing Error Log
@@ -696,7 +779,7 @@ class PnorDD
/**
* @brief ECCB Control Register Layout
*/
- union ControlReg_t
+ union EccbControlReg_t
{
uint64_t data64;
struct
@@ -711,13 +794,13 @@ class PnorDD
uint64_t address : 32; /**< 32:63 = LPC Address */
};
- ControlReg_t() : data64(ECCB_LPC_CTL_REG_DEFAULT) {};
+ EccbControlReg_t() : data64(ECCB_LPC_CTL_REG_DEFAULT) {};
};
/**
* @brief ECCB Status Register Layout
*/
- union StatusReg_t
+ union EccbStatusReg_t
{
uint64_t data64;
struct
@@ -734,10 +817,87 @@ class PnorDD
Parity Error */
uint64_t unused4 : 7; /**< 57:63 */
};
- StatusReg_t() : data64(0) {};
+ EccbStatusReg_t() : data64(0) {};
};
+ /**
+ * @brief Reset PNOR Logic At The Specified Level
+ *
+ * @parm i_pnorResetLevel Level of PNOR to Reset
+ *
+ * @return Error log if error found
+ */
+ errlHndl_t resetPnor( ResetLevels i_pnorResetLevel );
+
+
+ /**
+ * @brief Reinitialize the SFC
+ *
+ * @return Error log if error found
+ */
+ errlHndl_t reinitializeSfc( void );
+
+
+ /**
+ * @brief Returns if an operation should be retried and handles
+ * the error logs
+ *
+ * @pre iv_error_recovery_failed should be 'false' before starting
+ * the retry loop as it gets checked in the function
+ *
+ * @parm i_op Operation being retried
+ * @parm io_err Error log from current attempt of the operation
+ * @parm io_original_err Error log representing original error
+ * @parm io_retry_count Count of retries attempted
+ * NOTE: count incremented when function returns true
+ * @return bool true if retry is necessary; false if not
+ *
+ * @post Logic Table:
+ *
+ * |retries| io_err | io_original_err- | io_retry_count | function |
+ * | left | | related actions | | returns |
+ * |-------|--------|----------------------|----------------|----------|
+ * | N/A | NULL | if NULL then nothing | <nothing> | false |
+ * | | | if !NULL then | | |
+ * | | | a) io_original_err | | |
+ * | | | deleted | | |
+ * | | | b) io_original_err | | |
+ * | | | set to NULL | | |
+ * |-------|--------|----------------------|----------------|----------|
+ * | No | !NULL | if NULL then nothing | <nothing> | false |
+ * | | | if !NULL then: | | |
+ * | | | a) io_err committed | | |
+ * | | | b) io_err set to | | |
+ * | | | io_original_err | | |
+ * | | | c) io_original_err | | |
+ * | | | set to NULL | | |
+ * |-------|--------|----------------------|----------------|----------|
+ * | Yes | !NULL | if NULL then | incremented | true |
+ * | | | a) io_err saved as | | |
+ * | | | io_original_err | | |
+ * | | | b) io_err set to | | |
+ * | | | NULL | | |
+ * | | | if !NULL then | | |
+ * | | | a) io_err info | | |
+ * | | | added to | | |
+ * | | | io_original_err | | |
+ * | | | b) then io_err | | |
+ * | | | deleted and set | | |
+ * | | | to NULL | | |
+ * |-------|--------|----------------------|----------------|----------|
+ *
+ * NOTES: -- if iv_error_recovery_failed is set then no retries
+ * -- otherwise retries left determined by io_retry_count and
+ * PNORDD_MAX_RETRIES
+ */
+ bool shouldRetry( RetryOp i_op,
+ errlHndl_t& io_err,
+ errlHndl_t& io_original_err,
+ uint8_t& io_retry_count );
+
+
+
private: // Variables
// NOTE: The layout of the variables in this class must be maintained
@@ -808,8 +968,29 @@ class PnorDD
*/
bool iv_ffdc_active;
+ /**
+ * @brief Number of times recovered from an error
+ *
+ */
+ uint32_t iv_error_handled_count;
+
+ /**
+ * @brief Indicates recovery from an error has failed
+ *
+ */
+ bool iv_error_recovery_failed;
+
+ /**
+ * @brief Indicates if class is currently doing a RESET procedure
+ *
+ */
+ bool iv_reset_active;
+
// Needed for testcases
friend class PnorDdTest;
+
+ // let the UserDetails classes see internal structures
+ friend class PNOR::UdPnorDDParms;
};
OpenPOWER on IntegriCloud