diff options
author | Dan Crowell <dcrowell@us.ibm.com> | 2014-05-13 16:45:45 -0500 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2014-09-22 13:31:04 -0500 |
commit | d001febf0b5594120dd422bcbe5736221471e0ca (patch) | |
tree | 227678a8341e96ef6bf9fe385e1215cffe613b97 /src/usr | |
parent | e7d14e42caf28c421dfaa4b3b15bda7cf5a77e43 (diff) | |
download | talos-hostboot-d001febf0b5594120dd422bcbe5736221471e0ca.tar.gz talos-hostboot-d001febf0b5594120dd422bcbe5736221471e0ca.zip |
Create LPC Device Driver
Split LPC function out from PNOR DD and incorporate Stradale
changes
Change-Id: I4162db1a9f52ba3c0c973438b7b70baeae00aee2
Origin: Google Shared Technology
RTC: 97494
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/11198
Tested-by: Jenkins Server
Reviewed-by: Michael Baiocchi <baiocchi@us.ibm.com>
Reviewed-by: Douglas R. Gilbert <dgilbert@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/lpc/HBconfig | 4 | ||||
-rw-r--r-- | src/usr/lpc/lpcdd.C | 1182 | ||||
-rw-r--r-- | src/usr/lpc/lpcdd.H | 338 | ||||
-rw-r--r-- | src/usr/lpc/makefile | 34 | ||||
-rw-r--r-- | src/usr/makefile | 1 | ||||
-rw-r--r-- | src/usr/pnor/pnordd.C | 533 | ||||
-rw-r--r-- | src/usr/pnor/pnordd.H | 105 | ||||
-rw-r--r-- | src/usr/pnor/pnorvalid.C | 47 |
8 files changed, 1641 insertions, 603 deletions
diff --git a/src/usr/lpc/HBconfig b/src/usr/lpc/HBconfig new file mode 100644 index 000000000..b273a29ac --- /dev/null +++ b/src/usr/lpc/HBconfig @@ -0,0 +1,4 @@ +config LPC_CONSOLE + default n + help + Enable the Hostboot console over the LPC bus. diff --git a/src/usr/lpc/lpcdd.C b/src/usr/lpc/lpcdd.C new file mode 100644 index 000000000..6c57f6010 --- /dev/null +++ b/src/usr/lpc/lpcdd.C @@ -0,0 +1,1182 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/lpc/lpcdd.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +/** + * @file lpcdd.C + * + * @brief Implementation of the LPC Device Driver + */ + +#include <sys/mmio.h> +#include <sys/task.h> +#include <sys/sync.h> +#include <string.h> +#include <devicefw/driverif.H> +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <targeting/common/targetservice.H> +#include <errl/errlmanager.H> +#include "lpcdd.H" +#include <sys/time.h> +#include <lpc/lpc_reasoncodes.H> +#include <initservice/initserviceif.H> +#include <kernel/console.H> //@todo - RTC:97495 -- Resolve console access +#include <errl/errludlogregister.H> +#include <initservice/taskargs.H> +#include <config.h> + + +trace_desc_t* g_trac_lpc; +TRAC_INIT( & g_trac_lpc, LPC_COMP_NAME, 2*KILOBYTE, TRACE::BUFFER_SLOW); + +// Set to enable LPC tracing. +//#define LPC_TRACING 1 +#ifdef LPC_TRACING +#define LPC_TRACFCOMP(des,printf_string,args...) \ + TRACFCOMP(des,printf_string,##args) +#else +#define LPC_TRACFCOMP(args...) +#endif + +// Device Drive instance used for alt-master access +static LpcDD* g_altLpcDD = NULL; + +namespace LPC +{ +/** + * @brief Performs an LPC Read Operation + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target LPC target + * @param[in/out] io_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] io_buflen Input: size of io_buffer (in bytes) + * Output: + * Read: Size of output data + * Write: Size of data written + * @param[in] i_accessType DeviceFW::AccessType enum (usrif.H) + * @param[in] i_args This is an argument list for DD framework. + * @return errlHndl_t + */ +errlHndl_t lpcRead(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, va_list i_args) +{ + LPC::TransType l_type = static_cast<LPC::TransType>( + va_arg(i_args,uint64_t) ); + uint64_t l_addr = va_arg(i_args,uint64_t); + errlHndl_t l_err = NULL; + + assert( io_buflen == sizeof(uint8_t) || + io_buflen == sizeof(uint16_t) || + io_buflen == sizeof(uint32_t) ); + + // if the request is for something besides the master sentinel + // then we have to use our special side copy of the driver + if( i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) + { + l_err = Singleton<LpcDD>::instance().readLPC( l_type, + l_addr, + io_buffer, + io_buflen ); + } + else + { + if( g_altLpcDD + && (i_target == g_altLpcDD->getProc()) ) + { + l_err = g_altLpcDD->readLPC( l_type, + l_addr, + io_buffer, + io_buflen ); + } + else + { + TRACFCOMP( g_trac_lpc, "Unexpected target for LPC read : i_target=%.8X", TARGETING::get_huid(i_target) ); + uint32_t alt_huid = 0; + if( g_altLpcDD ) + { + alt_huid = TARGETING::get_huid(g_altLpcDD->getProc()); + } + /*@ + * @errortype + * @moduleid LPC::MOD_READLPC + * @reasoncode LPC::RC_BAD_TARGET + * @userdata1[00:31] Requested target + * @userdata1[00:31] Current alt target + * @userdata2 Read address + * @devdesc readLPC> Unexpected target + * @custdesc Firmware error during boot flash diagnostics + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_READLPC, + LPC::RC_BAD_TARGET, + TWO_UINT32_TO_UINT64( + TARGETING::get_huid(i_target), + alt_huid ), + l_addr, + true /*SW error*/); + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(LPC_COMP_NAME); + } + } + + return l_err; +} + +/** + * @brief Performs a LPC Write Operation + * This function performs a LPC Write operation. It follows a pre-defined + * prototype functions in order to be registered with the device-driver + * framework. + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target LPC target + * @param[in/out] io_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] io_buflen Input: size of io_buffer (in bytes) + * Output: + * Read: Size of output data + * Write: Size of data written + * @param[in] i_accessType DeviceFW::AccessType enum (usrif.H) + * @param[in] i_args This is an argument list for DD framework. + * @return errlHndl_t + */ +errlHndl_t lpcWrite(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, va_list i_args) +{ + LPC::TransType l_type = static_cast<LPC::TransType>( + va_arg(i_args,uint64_t) ); + uint64_t l_addr = va_arg(i_args,uint64_t); + errlHndl_t l_err = NULL; + + assert( io_buflen == sizeof(uint8_t) || + io_buflen == sizeof(uint16_t) || + io_buflen == sizeof(uint32_t) ); + + // if the request is for something besides the master sentinel + // then we have to use our special side copy of the driver + if( i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) + { + l_err = Singleton<LpcDD>::instance().writeLPC( l_type, + l_addr, + io_buffer, + io_buflen ); + } + else + { + if( g_altLpcDD + && (i_target == g_altLpcDD->getProc()) ) + { + l_err = g_altLpcDD->writeLPC( l_type, + l_addr, + io_buffer, + io_buflen ); + } + else + { + TRACFCOMP( g_trac_lpc, "Unexpected target for LPC write : i_target=%.8X", TARGETING::get_huid(i_target) ); + uint32_t alt_huid = 0; + if( g_altLpcDD ) + { + alt_huid = TARGETING::get_huid(g_altLpcDD->getProc()); + } + /*@ + * @errortype + * @moduleid LPC::MOD_WRITELPC + * @reasoncode LPC::RC_BAD_TARGET + * @userdata1[00:31] Requested target + * @userdata1[00:31] Current alt target + * @userdata2 Write address + * @devdesc writeLPC> Unexpected target + * @custdesc Firmware error during boot flash diagnostics + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_WRITELPC, + LPC::RC_BAD_TARGET, + TWO_UINT32_TO_UINT64( + TARGETING::get_huid(i_target), + alt_huid ), + l_addr, + true /*SW error*/); + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(LPC_COMP_NAME); + } + } + + return l_err; + +} + +// Register LPC access functions to DD framework +DEVICE_REGISTER_ROUTE( DeviceFW::READ, + DeviceFW::LPC, + TARGETING::TYPE_PROC, + lpcRead ); +DEVICE_REGISTER_ROUTE( DeviceFW::WRITE, + DeviceFW::LPC, + TARGETING::TYPE_PROC, + lpcWrite ); + +/** + * STATIC + * @brief Initialize driver and hardware to ready state + */ +static void init( errlHndl_t& io_rtaskRetErrl ) +{ + TRACFCOMP(g_trac_lpc, "LPC::init> " ); + errlHndl_t l_errl = NULL; + + // Initialize the hardware + l_errl = Singleton<LpcDD>::instance().hwReset(LpcDD::RESET_INIT); + if( l_errl ) + { + TRACFCOMP( g_trac_lpc, "Errors initializing LPC logic... Beware! PLID=%.8X", l_errl->plid() ); + } + + io_rtaskRetErrl = l_errl; +} + + +/** + * @brief Create/delete software objects to support non-master access + */ +errlHndl_t create_altmaster_objects( bool i_create, + TARGETING::Target* i_proc ) +{ + TRACFCOMP(g_trac_lpc, "LPC::create_altmaster_objects> i_create=%d, i_proc=%.8X", i_create, TARGETING::get_huid(i_proc) ); + errlHndl_t l_err = NULL; + + do { + if( i_create && g_altLpcDD ) + { + TRACFCOMP(g_trac_lpc, "LPC::create_altmaster_objects> Alt-master object already exists"); + /*@ + * @errortype + * @moduleid LPC::MOD_CREATE_ALTMASTER + * @reasoncode LPC::RC_ALTMASTER_EXISTS + * @userdata1 Requested proc + * @userdata2 <unused> + * @devdesc create_altmaster_objects> Alt-master object + * already exists + * @custdesc Firmware error during boot flash diagnostics + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_CREATE_ALTMASTER, + LPC::RC_ALTMASTER_EXISTS, + TARGETING::get_huid(i_proc), + 0, + true /*SW error*/); + + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(LPC_COMP_NAME); + break; + } + + if( i_create ) + { + if( i_proc == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) + { + TRACFCOMP(g_trac_lpc, "LPC::create_altmaster_objects> Cannot create another object using master sentinel"); + /*@ + * @errortype + * @moduleid LPC::MOD_CREATE_ALTMASTER + * @reasoncode LPC::RC_CANT_USE_SENTINEL + * @userdata1 <unused> + * @userdata2 <unused> + * @devdesc create_altmaster_objects> Cannot create + * another object using master sentinel + * @custdesc Firmware error during boot flash diagnostics + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_CREATE_ALTMASTER, + LPC::RC_CANT_USE_SENTINEL, + 0, + 0, + true /*SW error*/); + + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(LPC_COMP_NAME); + break; + } + + // Check if input processor is MASTER + TARGETING::ATTR_PROC_MASTER_TYPE_type type_enum = + i_proc->getAttr<TARGETING::ATTR_PROC_MASTER_TYPE>(); + if ( type_enum == TARGETING::PROC_MASTER_TYPE_ACTING_MASTER ) + { + TRACFCOMP(g_trac_lpc, "LPC::create_altmaster_objects> Cannot create another object using master proc"); + /*@ + * @errortype + * @moduleid LPC::MOD_CREATE_ALTMASTER + * @reasoncode LPC::RC_CANT_USE_MASTER + * @userdata1 <unused> + * @userdata2 <unused> + * @devdesc create_altmaster_objects> Cannot create + * another object using master proc + * @custdesc Firmware error during boot flash diagnostics + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_CREATE_ALTMASTER, + LPC::RC_CANT_USE_MASTER, + 0, + 0, + true /*SW error*/); + + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(LPC_COMP_NAME); + break; + } + + g_altLpcDD = new LpcDD( i_proc ); + } + else + { + if( g_altLpcDD ) + { + delete g_altLpcDD; + } + else + { + TRACFCOMP(g_trac_lpc,"LPC::create_altmaster_objects> Nothing to remove, but not a big deal"); + } + } + } while(0); + + return l_err; +} + +}; //namespace LPC + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +/** + * @brief set up _start() task entry procedure + */ +TASK_ENTRY_MACRO( LPC::init ); + +mutex_t LpcDD::cv_mutex = MUTEX_INITIALIZER; + +LpcDD::LpcDD( TARGETING::Target* i_proc ) +: ivp_mutex(NULL) +,iv_proc(i_proc) +,iv_ffdcActive(false) +,iv_errorHandledCount(0) +,iv_errorRecoveryFailed(false) +,iv_resetActive(false) +{ + mutex_init( &iv_mutex ); + + if( i_proc == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) + { + ivp_mutex = &cv_mutex; + } + else + { + // Check if processor is MASTER + TARGETING::ATTR_PROC_MASTER_TYPE_type type_enum = + iv_proc->getAttr<TARGETING::ATTR_PROC_MASTER_TYPE>(); + if ( type_enum == TARGETING::PROC_MASTER_TYPE_ACTING_MASTER ) + { + // Master target needs global mutex to avoid collisions with + // default singleton object used for ddRead/ddWrite with + // TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL + ivp_mutex = &cv_mutex; + // IMPORTANT :: cv_mutex can never be held while accessing any + // code outside of the base image or this could lead to deadlocks + // accessing PNOR + } + else + { + // Just use the local mutex + ivp_mutex = &iv_mutex; + } + } + + +} + +LpcDD::~LpcDD() +{ + mutex_destroy( &iv_mutex ); +} + +/** + * @brief Reset hardware to get into clean state + */ +errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) +{ + TRACFCOMP( g_trac_lpc, ENTER_MRK"LpcDD::hwReset(i_resetLevel=%d)>", i_resetLevel ); + errlHndl_t l_err = NULL; + + // check iv_resetActive to avoid infinite loops + // and don't reset if in the middle of FFDC collection + // and don't bother if we already failed the recovery once + if ( ( iv_resetActive == false ) && + ( iv_ffdcActive == false ) && + ( iv_errorRecoveryFailed == false) ) + { + 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 */ + /***************************************/ + switch(i_resetLevel) + { + 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_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: + { + 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); + + // First read OPB_MASTER_LS_CONTROL_REG + uint32_t lpc_data = 0x0; + l_err = readLPC( LPC::TRANS_REG, + OPB_MASTER_LS_CONTROL_REG, + &lpc_data, + opsize ); + + if (l_err) { break; } + + // Disable 'First Error Data Capture' + // - set bit 29 to 0b1 + lpc_data |= 0x00000004; + + l_err = writeLPC( LPC::TRANS_REG, + OPB_MASTER_LS_CONTROL_REG, + &lpc_data, + opsize ); + 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::TRANS_REG, + OPB_MASTER_LS_CONTROL_REG, + &lpc_data, + opsize ); + + if (l_err) { break; } + + // 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; + } + + case RESET_OPB_LPCHC_HARD: + { + 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 + * @moduleid LPC::MOD_LPCDD_HWRESET + * @reasoncode LPC::RC_UNSUPPORTED_OPERATION + * @userdata1 Unsupported Reset Level Parameter + * @userdata2 <unused> + * @devdesc LpcDD::hwReset> Unsupported Reset Level + * requested + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_HWRESET, + LPC::RC_UNSUPPORTED_OPERATION, + i_resetLevel, + 0, + true /*SW error*/); + + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(LPC_COMP_NAME); + + break; + } + }// end switch + + if ( l_err ) + { + // Indicate that we weren't successful in resetting LPC + iv_errorRecoveryFailed = true; + TRACFCOMP( g_trac_lpc,ERR_MRK"LpcDD::hwReset> Fail doing LPC reset at level 0x%X (recovery count=%d): eid=0x%X", i_resetLevel, iv_errorHandledCount, l_err->eid()); + } + else + { + // Successful, so increment recovery count + iv_errorHandledCount++; + + TRACFCOMP( g_trac_lpc,INFO_MRK"LpcDD::hwReset> Successful LPC reset at level 0x%X (recovery count=%d)", i_resetLevel, iv_errorHandledCount); + } + + + } while(0); + + // reset RESET active flag + iv_resetActive = false; + } + + TRACFCOMP( g_trac_lpc, EXIT_MRK"LpcDD::hwReset()=%.8X:%.4X", ERRL_GETEID_SAFE(l_err), ERRL_GETRC_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Sanity check the input address for a LPC op and return + * full absolute address + */ +errlHndl_t LpcDD::checkAddr(LPC::TransType i_type, + uint32_t i_addr, + uint32_t *o_addr) +{ + bool invalid_address = false; + switch ( i_type ) + { + case LPC::TRANS_IO: + if( i_addr >= 0x10000 ) + { + invalid_address = true; + break; + } + *o_addr = i_addr + LPCHC_IO_SPACE; + break; + case LPC::TRANS_MEM: + if( i_addr >= 0x10000000 ) + { + invalid_address = true; + break; + } + *o_addr = i_addr + LPCHC_MEM_SPACE; + break; + case LPC::TRANS_FW: + if( i_addr < LPCHC_FW_SPACE ) + { + invalid_address = true; + break; + } + *o_addr = i_addr; + break; + case LPC::TRANS_REG: + if( i_addr >= 0x100 ) + { + invalid_address = true; + break; + } + *o_addr = i_addr + LPCHC_REG_SPACE; + break; + case LPC::TRANS_ABS: + //Just use the address as given + *o_addr = i_addr; + break; + default: + invalid_address = true; + } + + if( invalid_address ) + { + /*@ + * @errortype + * @moduleid LPC::MOD_LPCDD_CHECKADDR + * @reasoncode LPC::RC_INVALID_ADDR + * @userdata1[0:31] LPC Address + * @userdata1[32:63] LPC Transaction Type + * @devdesc LpcDD> LPC invalid address + * @custdesc Firmware error accessing internal bus during IPL + */ + return new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_CHECKADDR, + LPC::RC_INVALID_ADDR, + TWO_UINT32_TO_UINT64( + i_addr, i_type), + 0, + true/*SW Error*/); + } + + return NULL; +} + +/** + * @brief Read an address from LPC space + */ +errlHndl_t LpcDD::readLPC(LPC::TransType i_type, + uint32_t i_addr, + void* o_buffer, + size_t& io_buflen) +{ + errlHndl_t l_err = NULL; + uint32_t l_addr = 0; + bool need_unlock = false; + + do { + // Generate the full absolute LPC address + l_err = checkAddr( i_type, i_addr, &l_addr ); + if( l_err ) { break; } + + mutex_lock(ivp_mutex); + need_unlock = true; + + // Execute command. + ControlReg_t eccb_cmd; + eccb_cmd.data_len = io_buflen; + eccb_cmd.read_op = 1; + eccb_cmd.addr_len = sizeof(l_addr); + eccb_cmd.address = l_addr; + size_t scom_size = sizeof(uint64_t); + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &(eccb_cmd.data64), + scom_size, + DEVICE_SCOM_ADDRESS(ECCB_CTL_REG) ); + if( l_err ) { break; } + + // Poll for completion + StatusReg_t eccb_stat; + l_err = pollComplete( eccb_cmd, eccb_stat ); + if( l_err ) { break; } + + // Copy data out to caller's buffer. + if( io_buflen <= sizeof(uint32_t) ) + { + uint32_t tmpbuf = eccb_stat.read_data; + memcpy( o_buffer, &tmpbuf, io_buflen ); + } + else + { + mutex_unlock(ivp_mutex); + need_unlock = false; + + TRACFCOMP( g_trac_lpc, "readLPC> Unsupported buffer size : %d", io_buflen ); + /*@ + * @errortype + * @moduleid LPC::MOD_LPCDD_READLPC + * @reasoncode LPC::RC_BAD_ARG + * @userdata1[0:31] LPC Address + * @userdata1[32:63] LPC Transaction Type + * @userdata2 Requested buffer size + * @devdesc LpcDD::readLPC> Invalid buffer size requested + * (>4 bytes) + * @custdesc Firmware error accessing internal bus during IPL + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_READLPC, + LPC::RC_BAD_ARG, + TWO_UINT32_TO_UINT64( + i_addr, i_type), + io_buflen, + true/*SW Error*/); + break; + } + + } while(0); + + if( need_unlock ) { mutex_unlock(ivp_mutex); }; + + LPC_TRACFCOMP( g_trac_lpc, "readLPC> %08X[%d] = %08X", l_addr, io_buflen, *reinterpret_cast<uint32_t*>( o_buffer ) >> (8 * (4 - io_buflen)) ); + + return l_err; +} + +/** + * @brief Write an address from LPC space + */ +errlHndl_t LpcDD::writeLPC(LPC::TransType i_type, uint32_t i_addr, + const void* i_buffer, + size_t& io_buflen) +{ + errlHndl_t l_err = NULL; + uint32_t l_addr = 0; + bool need_unlock = false; + + do { + // Generate the full absolute LPC address + l_err = checkAddr( i_type, i_addr, &l_addr ); + if( l_err ) { break; } + + uint64_t eccb_data = 0; + // Left-justify user data into data register. + switch ( io_buflen ) + { + case 1: + eccb_data = static_cast<uint64_t>( + *reinterpret_cast<const uint8_t*>( i_buffer ) ) << 56; + break; + case 2: + eccb_data = static_cast<uint64_t>( + *reinterpret_cast<const uint16_t*>( i_buffer ) ) << 48; + break; + case 4: + eccb_data = static_cast<uint64_t>( + *reinterpret_cast<const uint32_t*>( i_buffer ) ) << 32; + break; + default: + TRACFCOMP( g_trac_lpc, "writeLPC> Unsupported buffer size : %d", io_buflen ); + assert( false ); + break; + } + + LPC_TRACFCOMP(g_trac_lpc, "writeLPC> %08X[%d] = %08X", l_addr, io_buflen, + eccb_data >> (32 + 8 * (4 - io_buflen))); + + mutex_lock(ivp_mutex); + need_unlock = true; + + // Write data out + size_t scom_size = sizeof(uint64_t); + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &eccb_data, + scom_size, + DEVICE_SCOM_ADDRESS(ECCB_DATA_REG) ); + if( l_err ) { break; } + + // Execute command. + ControlReg_t eccb_cmd; + eccb_cmd.data_len = io_buflen; + eccb_cmd.read_op = 0; + eccb_cmd.addr_len = sizeof(l_addr); + eccb_cmd.address = l_addr; + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &(eccb_cmd.data64), + scom_size, + DEVICE_SCOM_ADDRESS(ECCB_CTL_REG) ); + if( l_err ) { break; } + + // Poll for completion + StatusReg_t eccb_stat; + l_err = pollComplete( eccb_cmd, eccb_stat ); + if( l_err ) { break; } + + } while(0); + + if( need_unlock ) { mutex_unlock(ivp_mutex); } + + return l_err; +} + +/** + * @brief Poll for completion of LPC operation + */ +errlHndl_t LpcDD::pollComplete(const ControlReg_t &i_ctrl, + StatusReg_t& o_stat) +{ + // Note: Caller must lock mutex before calling this function + errlHndl_t l_err = NULL; + ResetLevels l_resetLevel = RESET_CLEAR; + + do { + uint64_t poll_time = 0; + uint64_t loop = 0; + do + { + size_t scom_size = sizeof(uint64_t); + l_err = deviceOp( DeviceFW::READ, + iv_proc, + &(o_stat.data64), + scom_size, + DEVICE_SCOM_ADDRESS(ECCB_STAT_REG) ); + LPC_TRACFCOMP( g_trac_lpc, "writeLPC> Poll on ECCB Status, " + "poll_time=0x%.16x, stat=0x%.16x", + poll_time, + o_stat.data64 ); + if( l_err ) + { + break; + } + + if( o_stat.op_done ) + { + break; + } + + // want to start out incrementing by small numbers then get bigger + // to avoid a really tight loop in an error case so we'll increase + // the wait each time through + nanosleep( 0, ECCB_POLL_INCR_NS*(++loop) ); + poll_time += ECCB_POLL_INCR_NS * loop; + } while ( poll_time < ECCB_POLL_TIME_NS ); + + // Check for hw errors or timeout if no previous logs + if( (l_err == NULL) && + ((o_stat.data64 & ECCB_STAT_REG_ERROR_MASK) + || (!o_stat.op_done)) ) + { + TRACFCOMP( g_trac_lpc, "LpcDD::pollComplete> LPC error or timeout: " + "addr=0x%.8X, status=0x%.16X", + i_ctrl.address, o_stat.data64 ); + + if( i_ctrl.read_op ) + { + /*@ + * @errortype + * @moduleid LPC::MOD_LPCDD_READLPC + * @reasoncode LPC::RC_ECCB_ERROR + * @userdata1[0:31] LPC Address + * @userdata1[32:63] Total poll time (ns) + * @userdata2 ECCB Status Register + * @devdesc LpcDD::pollComplete> LPC error or timeout + * @custdesc Hardware error accessing internal + * bus during IPL + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_READLPC, + LPC::RC_ECCB_ERROR, + TWO_UINT32_TO_UINT64( + i_ctrl.address, poll_time), + o_stat.data64 ); + } + else + { + /*@ + * @errortype + * @moduleid LPC::MOD_LPCDD_WRITELPC + * @reasoncode LPC::RC_ECCB_ERROR + * @userdata1[0:31] LPC Address + * @userdata1[32:63] Total poll time (ns) + * @userdata2 ECCB Status Register + * @devdesc LpcDD::pollComplete> LPC error or timeout + * @custdesc Hardware error accessing internal + * bus during IPL + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_WRITELPC, + LPC::RC_ECCB_ERROR, + TWO_UINT32_TO_UINT64( + i_ctrl.address, poll_time), + o_stat.data64 ); + } + // Limited in callout: no LPC sub-target, so calling out processor + l_err->addHwCallout( iv_proc, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + addFFDC(l_err); + l_err->collectTrace(LPC_COMP_NAME); + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(XSCOM_COMP_NAME); + + // Reset ECCB - handled below + l_resetLevel = RESET_ECCB; + + break; + } + + // check for errors at OPB level + l_err = checkForOpbErrors( l_resetLevel ); + if( l_err ) { break; } + + } while(0); + + // If we have an error that requires a reset, do that here + if ( l_err && ( l_resetLevel != RESET_CLEAR ) ) + { + errlHndl_t tmp_err = hwReset(l_resetLevel); + + if ( tmp_err ) + { + // Commit reset error since we have original error l_err + TRACFCOMP(g_trac_lpc, "LpcDD::pollComplete> Error from reset() after previous error eid=0x%X. Committing reset() 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->collectTrace(LPC_COMP_NAME); + tmp_err->plid(l_err->plid()); + errlCommit(tmp_err, LPC_COMP_ID); + } + } + + return l_err; +} + +/** + * @brief Add Error Registers to an existing Error Log + */ +void LpcDD::addFFDC(errlHndl_t & io_errl) +{ + // check iv_ffdcActive to avoid infinite loops + if ( iv_ffdcActive == false ) + { + iv_ffdcActive = true; + + TRACFCOMP( g_trac_lpc, "LpcDD::addFFDC> adding FFDC to Error Log EID=0x%X, PLID=0x%X", + io_errl->eid(), io_errl->plid() ); + + ERRORLOG::ErrlUserDetailsLogRegister l_eud(iv_proc); + + do { + // Add ECCB Status Register + l_eud.addData(DEVICE_SCOM_ADDRESS(ECCB_STAT_REG)); + + // Add OPB LPC Master FIR + l_eud.addData(DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG)); + + //@todo - add more LPC regs RTC:37744 + //LPCIRQ_STATUS = 0x38 + //SYS_ERR_ADDR = 0x40 + + } while(0); + + + l_eud.addToLog(io_errl); + + // reset FFDC active flag + iv_ffdcActive = false; + } + + return; +} + +/** + * @brief Check For Errors in OPB and LPCHC Status Registers + */ +errlHndl_t LpcDD::checkForOpbErrors( 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; } + + + // 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_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); + } + + 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); + } + + 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); + } + + 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); + } + + + // 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); + } + + 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); + } + + 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); + } + + + }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_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 + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + LPC::MOD_LPCDD_CHECKFOROPBERRORS, + LPC::RC_OPB_ERROR, + fir_reg.data64, + o_resetLevel ); + + // Limited in callout: no PNOR target, so calling out processor + l_err->addHwCallout( iv_proc, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + + // 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); + + addFFDC(l_err); + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(LPC_COMP_NAME); + + } + + return l_err; + +} + + diff --git a/src/usr/lpc/lpcdd.H b/src/usr/lpc/lpcdd.H new file mode 100644 index 000000000..598c5fab8 --- /dev/null +++ b/src/usr/lpc/lpcdd.H @@ -0,0 +1,338 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/lpc/lpcdd.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __LPC_LPCDD_H +#define __LPC_LPCDD_H + +#include <limits.h> +#include <sys/sync.h> +#include <stdint.h> +#include <errl/errlentry.H> +#include <lpc/lpcif.H> + +/** @file lpcdd.H + * @brief Provides the interfaces to the LPC Device Driver + */ + +/** + * @brief LPC Device Driver Class + * Provides access to the LPC bus for a specific Processor + */ +class LpcDD +{ + public: + /** + * @brief Performs a LPC Read Operation + * + * @param i_trans LPC transaction type + * @param i_address LPC address + * @param o_buffer Buffer to read data into + * @param io_buflen Input: Number of bytes to read, + * Output: Number of bytes actually read + * + * @return Error from operation + */ + errlHndl_t readLPC(LPC::TransType i_type, + uint32_t i_address, + void* o_buffer, + size_t& io_buflen); + + /** + * @brief Performs a LPC Write Operation + * + * @param i_trans LPC transaction type + * @param i_address LPC address + * @param i_buffer Buffer to write data from + * @param io_buflen Input: Number of bytes to write, + * Output: Number of bytes actually written + * + * @return Error from operation + */ + errlHndl_t writeLPC(LPC::TransType i_type, + uint32_t i_address, + const void* i_buffer, + size_t& io_buflen); + + + /** + * @brief 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_INIT = RESET_CLEAR, // Nothing to do for initial boot yet + }; + + + /** + * @brief Reset hardware to get into clean state + * + * @parm i_resetLevel How much LPC logic to reset + * + * @return errlHndl_t NULL on success, else error log + */ + errlHndl_t hwReset( ResetLevels i_resetLevel ); + + /** + * @brief Get the target associated with this dd instance + * + * @return Pointer to target + */ + TARGETING::Target* getProc( void ) + { + return iv_proc; + }; + + /** + * @brief Constructor + * @param[in] Processor target associated with the ECCB logic + */ + LpcDD( TARGETING::Target* i_proc + = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ); + + + /** + * @brief Destructor + */ + ~LpcDD(); + + protected: + /** + * @brief LPC HC Registers + * These are offsets within the LPC Host Controller Register Space + */ + enum LpcRegAddr { + LPC_REG_BAR0 = 0x00, /**< BAR0 : OPB register */ + LPC_REG_BAR1 = 0x04, /**< BAR1 : LPC I/O space */ + LPC_REG_BAR2 = 0x08, /**< BAR2 : LPC Memory space */ + LPC_REG_BAR3 = 0x0C, /**< BAR3 : LPC Firmware space */ + LPC_REG_ABRTCNT = 0x2C, /**< ABORT COUNT */ + }; + + + /** + * @brief Some general constants + * + */ + enum { + LPCHC_FW_SPACE = 0xF0000000, /**< LPC Host Controller FW Space */ + LPCHC_MEM_SPACE = 0xE0000000, /**< LPC Host Controller Mem Space */ + LPCHC_IO_SPACE = 0xD0010000, /**< LPC Host Controller I/O Space */ + LPCHC_REG_SPACE = 0xC0012000, /**< LPC Host Ctlr Register Space */ + + ECCB_NON_FW_RESET_REG = 0x000B0001, /**< ECCB Reset Reg (non-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> + ECCB_CTL_REG_DEFAULT = 0xD400010000000000, + + // Error bits: 41-43, 56 (52=cmd complete) (not 57: only non-fw use) + ECCB_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 = 0x008, /**<OPBM LS Control Reg */ + LPCHC_RESET_REG = 0x0FC, /**<LPC HC Reset Register */ + + ECCB_RESET_LPC_FAST_RESET = 1ULL << 62, /**< bit 1 = Fast reset */ + + ECCB_POLL_TIME_NS = 400000, /**< max time should be 400ms */ + ECCB_POLL_INCR_NS = 10, /**< minimum increment during poll */ + + LPCHC_SYNC_CYCLE_COUNTER_INFINITE = 0xFF000000 + }; + + /** + * @brief ECCB Control Register Layout + */ + union ControlReg_t + { + uint64_t data64; + struct + { + // unused sections should be set to zero + uint64_t magic1 : 4; /**< 0:3 = b1101 per spec */ + uint64_t data_len : 4; /**< 4:7 = b0100 means 4 byte */ + uint64_t unused1 : 7; /**< 8:14 */ + uint64_t read_op : 1; /**< 15 = set for read operation */ + uint64_t unused2 : 7; /**< 16:22 */ + uint64_t addr_len : 3; /**< 23:25 = b100 means 4 byte */ + uint64_t unused3 : 6; /**< 26:31 */ + uint64_t address : 32; /**< 32:63 = LPC Address */ + }; + + ControlReg_t() : data64(ECCB_CTL_REG_DEFAULT) {}; + }; + + /** + * @brief ECCB Status Register Layout + */ + union StatusReg_t + { + uint64_t data64; + struct + { + uint64_t unused : 6; /**< 0:5 */ + uint64_t read_data : 32; /**< 6:37 */ + uint64_t unused1 : 3; /**< 38:40 */ + uint64_t eccb_err : 3; /**< 41:43 = ECCB_Error_Info */ + uint64_t busy : 1; /**< 44 = Operation Busy */ + uint64_t unused2 : 7; /**< 45:51 */ + uint64_t op_done : 1; /**< 52 = Command Complete */ + uint64_t unused3 : 3; /**< 53:55 */ + uint64_t addr_parity_err : 1; /**< 56 = ECC Address Register + Parity Error */ + uint64_t unused4 : 7; /**< 57:63 */ + }; + StatusReg_t() : data64(0) {}; + }; + + /** + * @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 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 + */ + errlHndl_t checkForOpbErrors( ResetLevels &o_resetLevel ); + + /** + * @brief Sanity check the input address for a LPC op and return + * full absolute address + * + * @param[in] i_type LPC range + * @param[in] i_addr Relative LPC address + * @param[out] o_addr Absolute LPC address + * + * @return Error from operation + */ + errlHndl_t checkAddr( LPC::TransType i_type, + uint32_t i_addr, + uint32_t* o_addr ); + + /** + * @brief Poll for completion of LPC operation + * + * @param[in] i_ctrl Control register describing operation + * @param[out] o_stat Status register for failures + * + * @return errlHndl_t NULL on success, else error log + */ + errlHndl_t pollComplete( const ControlReg_t& i_ctrl, + StatusReg_t& o_stat ); + + /** + * @brief Add Error Registers to an existing Error Log + * @param[inout] io_errl Error log to add data to + */ + void addFFDC(errlHndl_t& io_errl); + + private: + /** + * @brief Mutex to prevent concurrent LPC accesses to the master + */ + static mutex_t cv_mutex; + + /** + * @brief Mutex to prevent concurrent LPC accesses to a given + * instance of the LPC logic + * Note: this is unused for Master + */ + mutex_t iv_mutex; + + /** + * @brief Pointer to mutex in use + */ + mutex_t* ivp_mutex; + + /** + * @brief Processor target associated with the ECCB logic + */ + TARGETING::Target* iv_proc; + + /** + * @brief Marker to avoid infinite recursion in error handlers + */ + bool iv_ffdcActive; + + /** + * @brief Number of times recovered from an error + */ + uint32_t iv_errorHandledCount; + + /** + * @brief Indicates recovery from an error has failed + */ + bool iv_errorRecoveryFailed; + + /** + * @brief Indicates if class is currently doing a RESET procedure + */ + bool iv_resetActive; + +}; + + +#endif diff --git a/src/usr/lpc/makefile b/src/usr/lpc/makefile new file mode 100644 index 000000000..4fbad8436 --- /dev/null +++ b/src/usr/lpc/makefile @@ -0,0 +1,34 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/lpc/makefile $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2014 +# [+] Google Inc. +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +ROOTPATH = ../../.. +MODULE = lpc + +OBJS = lpcdd.o + +#@fixme - RTC:107788 Add tests after 37744 is merged +#SUBDIRS = test.d + +include ${ROOTPATH}/config.mk diff --git a/src/usr/makefile b/src/usr/makefile index 2543ed901..34d64fbdf 100644 --- a/src/usr/makefile +++ b/src/usr/makefile @@ -60,5 +60,6 @@ SUBDIRS += secureboot.d SUBDIRS += devtree.d SUBDIRS += sbe.d SUBDIRS += gpio.d +SUBDIRS += lpc.d include ${ROOTPATH}/config.mk diff --git a/src/usr/pnor/pnordd.C b/src/usr/pnor/pnordd.C index 52ebe997b..2d9bc1854 100644 --- a/src/usr/pnor/pnordd.C +++ b/src/usr/pnor/pnordd.C @@ -51,6 +51,7 @@ #include <sys/time.h> #include <initservice/initserviceif.H> #include <util/align.H> +#include <lpc/lpcif.H> #include <config.h> @@ -59,6 +60,7 @@ /*****************************************************************************/ #define PNORDD_MAX_RETRIES 1 +// Initialized in pnorrp.C extern trace_desc_t* g_trac_pnor; namespace PNOR @@ -429,7 +431,6 @@ PnorDD::PnorDD( PnorMode_t i_mode, iv_mutex_ptr = &iv_mutex; mutex_init(iv_mutex_ptr); TRACFCOMP(g_trac_pnor, "PnorDD::PnorDD()> Using i_target=0x%X (non-master) and iv_mutex_ptr", TARGETING::get_huid(i_target)); - } else { @@ -740,17 +741,17 @@ errlHndl_t PnorDD::pollSfcOpComplete(uint64_t i_pollTime) /*@ * @errortype * @moduleid PNOR::MOD_PNORDD_POLLSFCOPCOMPLETE - * @reasoncode PNOR::RC_LPC_ERROR + * @reasoncode PNOR::RC_SFC_ERROR * @userdata1[0:31] NOR Flash Chip ID * @userdata1[32:63] Total poll time (ns) * @userdata2[0:31] ECCB Status Register * @devdesc PnorDD::pollSfcOpComplete> Error or timeout from * SFC Status Register - * @custdesc A problem occurred while accessing the boot flash. + * @custdesc Hardware error accessing flash during IPL */ l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, PNOR::MOD_PNORDD_POLLSFCOPCOMPLETE, - PNOR::RC_LPC_ERROR, + PNOR::RC_SFC_ERROR, TWO_UINT32_TO_UINT64(iv_nor_chipid, poll_time), TWO_UINT32_TO_UINT64(sfc_stat.data32,0)); @@ -970,7 +971,7 @@ errlHndl_t PnorDD::checkForSfcErrors( ResetLevels &o_pnorResetLevel ) * @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. + * @custdesc Hardware error accessing flash during IPL */ l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, PNOR::MOD_PNORDD_CHECKFORSFCERRORS, @@ -996,163 +997,6 @@ errlHndl_t PnorDD::checkForSfcErrors( ResetLevels &o_pnorResetLevel ) } /** - * @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 { - // Read FIR Register - l_err = deviceOp( DeviceFW::READ, - iv_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_CHECKFOROPBERRORS, - PNOR::RC_ERROR_IN_STATUS_REG, - fir_reg.data64, - o_pnorResetLevel ); - - // Limited in callout: no PNOR target, so calling out processor - l_err->addHwCallout( - iv_target, - HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, - HWAS::GARD_NULL ); - - - // Log FIR Register Data - ERRORLOG::ErrlUserDetailsLogRegister - l_eud(iv_target); - - 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 */ void PnorDD::addFFDCRegisters(errlHndl_t & io_errl) @@ -1176,7 +1020,9 @@ void PnorDD::addFFDCRegisters(errlHndl_t & io_errl) l_eud(iv_target); do { - + //@fixme - RTC:107788 Seems like this might not belong here, + // instead only in the LPC layer... + // Add ECCB Status Register tmp_err = deviceOp( DeviceFW::READ, iv_target, &(data64), @@ -1410,7 +1256,7 @@ errlHndl_t PnorDD::micronFlagStatus(uint64_t i_pollTime) * @userdata2[0:31] Micron Flag status register * @devdesc PnorDD::micronFlagStatus> Error or timeout from * Micron Flag Status Register - * @custdesc A problem occurred while accessing the boot flash. + * @custdesc Hardware error accessing flash during IPL */ l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, PNOR::MOD_PNORDD_MICRONFLAGSTATUS, @@ -1929,125 +1775,12 @@ errlHndl_t PnorDD::writeSfcBuffer(size_t i_size, errlHndl_t PnorDD::readLPC(uint32_t i_addr, uint32_t& o_data) { - errlHndl_t l_err = NULL; - ResetLevels pnorResetLevel = RESET_CLEAR; - - do { - - // always read/write 64 bits to SCOM - size_t scom_size = sizeof(uint64_t); - - // write command register with LPC address to read - EccbControlReg_t eccb_cmd; - eccb_cmd.read_op = 1; - eccb_cmd.address = i_addr; - l_err = deviceOp( DeviceFW::WRITE, + size_t reg_size = sizeof(uint32_t); + errlHndl_t l_err = deviceOp( DeviceFW::READ, iv_target, - &(eccb_cmd.data64), - scom_size, - DEVICE_SCOM_ADDRESS(ECCB_CTL_REG) ); - if( l_err ) { break; } - - // poll for complete and get the data back - EccbStatusReg_t eccb_stat; - uint64_t poll_time = 0; - uint64_t loop = 0; - - while( poll_time < ECCB_POLL_TIME_NS ) - { - l_err = deviceOp( DeviceFW::READ, - iv_target, - &(eccb_stat.data64), - scom_size, - DEVICE_SCOM_ADDRESS(ECCB_STAT_REG) ); - if( l_err ) { break; } - - if( eccb_stat.op_done == 1 ) - { - break; - } - - - // want to start out incrementing by small numbers then get bigger - // to avoid a really tight loop in an error case so we'll increase - // 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; - } - if( l_err ) { break; } - - // check for errors or timeout at ECCB level - if( (eccb_stat.data64 & ECCB_LPC_STAT_REG_ERROR_MASK) - || (eccb_stat.op_done == 0) ) - { - TRACFCOMP(g_trac_pnor, "PnorDD::readLPC> Error or timeout from LPC Status Register : i_addr=0x%.8X, status=0x%.16X", i_addr, eccb_stat.data64 ); - - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_READLPC - * @reasoncode PNOR::RC_LPC_ERROR - * @userdata1[0:31] LPC Address - * @userdata1[32:63] Total poll time (ns) - * @userdata2 ECCB Status Register - * @devdesc PnorDD::readLPC> Error or timeout from - * LPC Status Register - * @custdesc A problem occurred while accessing the boot flash. - */ - l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_READLPC, - PNOR::RC_LPC_ERROR, - TWO_UINT32_TO_UINT64(i_addr,poll_time), - eccb_stat.data64); - - // Limited in callout: no PNOR target, so calling out processor - l_err->addHwCallout( - iv_target, - HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, - HWAS::GARD_NULL ); - - addFFDCRegisters(l_err); - l_err->collectTrace(PNOR_COMP_NAME); - l_err->collectTrace(XSCOM_COMP_NAME); - - // 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); - } - } - - + &o_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_ABS,i_addr) ); return l_err; } @@ -2057,136 +1790,14 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr, errlHndl_t PnorDD::writeLPC(uint32_t i_addr, uint32_t i_data) { - errlHndl_t l_err = NULL; - ResetLevels pnorResetLevel = RESET_CLEAR; - - + size_t reg_size = sizeof(uint32_t); + errlHndl_t l_err = deviceOp( DeviceFW::WRITE, + iv_target, + &i_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_ABS,i_addr) ); TRACDCOMP(g_trac_pnor, "writeLPC> %.8X = %.8X", i_addr, i_data ); - do { - - // always read/write 64 bits to SCOM - size_t scom_size = sizeof(uint64_t); - - // write data register - TRACDCOMP(g_trac_pnor, "writeLPC> Write ECCB data register"); - - uint64_t eccb_data = static_cast<uint64_t>(i_data); - eccb_data = eccb_data << 32; //left-justify my data - l_err = deviceOp( DeviceFW::WRITE, - iv_target, - &eccb_data, - scom_size, - DEVICE_SCOM_ADDRESS(ECCB_DATA_REG) ); - if( l_err ) { break; } - - // write command register with LPC address to write - 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 ); - l_err = deviceOp( DeviceFW::WRITE, - iv_target, - &(eccb_cmd.data64), - scom_size, - DEVICE_SCOM_ADDRESS(ECCB_CTL_REG) ); - if( l_err ) { break; } - - - // poll for complete - EccbStatusReg_t eccb_stat; - uint64_t poll_time = 0; - uint64_t loop = 0; - while( poll_time < ECCB_POLL_TIME_NS ) - { - l_err = deviceOp( DeviceFW::READ, - iv_target, - &(eccb_stat.data64), - scom_size, - DEVICE_SCOM_ADDRESS(ECCB_STAT_REG) ); - TRACDCOMP(g_trac_pnor, "writeLPC> Poll on ECCB Status, poll_time=0x%.16x, stat=0x%.16x", eccb_stat.data64, poll_time ); - - if( l_err ) { break; } - - if( eccb_stat.op_done == 1 ) - { - break; - } - - // want to start out incrementing by small numbers then get bigger - // to avoid a really tight loop in an error case so we'll increase - // 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; - } - if( l_err ) { break; } - - // check for errors at ECCB level - if( (eccb_stat.data64 & ECCB_LPC_STAT_REG_ERROR_MASK) - || (eccb_stat.op_done == 0) ) - { - TRACFCOMP(g_trac_pnor, "PnorDD::writeLPC> Error or timeout from LPC Status Register : i_addr=0x%.8X, status=0x%.16X", i_addr, eccb_stat.data64 ); - - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_WRITELPC - * @reasoncode PNOR::RC_LPC_ERROR - * @userdata1 LPC Address - * @userdata2 ECCB Status Register - * @devdesc PnorDD::writeLPC> Error or timeout from - * LPC Status Register - * @custdesc A problem occurred while accessing the boot flash. - */ - l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_WRITELPC, - PNOR::RC_LPC_ERROR, - TWO_UINT32_TO_UINT64(0,i_addr), - eccb_stat.data64); - // Limited in callout: no PNOR target, so calling out processor - l_err->addHwCallout( - iv_target, - HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, - HWAS::GARD_NULL ); - - addFFDCRegisters(l_err); - l_err->collectTrace(PNOR_COMP_NAME); - l_err->collectTrace(XSCOM_COMP_NAME); - - // 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; } @@ -2246,7 +1857,7 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart, if(need_write == false) { //No write actually needed, break out here - TRACFCOMP(g_trac_pnor,"compareAndWriteBlock> NO Write Needed! Exiting FUnction"); + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> NO Write Needed! Exiting Function"); break; } @@ -2347,7 +1958,7 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart, delete[] read_data; } - TRACFCOMP(g_trac_pnor,"<<compareAndWriteBlock() Exit"); + TRACDCOMP(g_trac_pnor,"<<compareAndWriteBlock() Exit"); return l_err; @@ -2370,15 +1981,15 @@ errlHndl_t PnorDD::eraseFlash(uint32_t i_address) /*@ * @errortype * @moduleid PNOR::MOD_PNORDD_ERASEFLASH - * @reasoncode PNOR::RC_LPC_ERROR + * @reasoncode PNOR::RC_INVALID_ADDRESS * @userdata1 LPC Address * @userdata2 Nearest Erase Boundary * @devdesc PnorDD::eraseFlash> Address not on erase boundary - * @custdesc A problem occurred while accessing the boot flash. + * @custdesc Firmware error accessing flash during IPL */ l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, PNOR::MOD_PNORDD_ERASEFLASH, - PNOR::RC_LPC_ERROR, + PNOR::RC_INVALID_ADDRESS, TWO_UINT32_TO_UINT64(0,i_address), findEraseBlock(i_address), true /*Add HB SW Callout*/ ); @@ -2652,15 +2263,11 @@ errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel ) TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> i_pnorResetLevel=0x%.8X", i_pnorResetLevel); do { - - // always read/write 64 bits to SCOM - uint64_t scom_data_64 = 0x0; - size_t scom_size = sizeof(uint64_t); - +#if 0 // 32 bits for address and data for LPC operations uint32_t lpc_addr=0; uint32_t lpc_data=0; - +#endif // To Avoid Infinite Loop - skip recovery if it already failed if ( iv_error_recovery_failed == true ) @@ -2679,85 +2286,7 @@ errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel ) 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, - iv_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, - iv_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, - iv_target, - &(scom_data_64), - scom_size, - DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_WOX_AND_REG) ); - - break; - } - + //@fixme RTC:109859 -- All SFC recovery should move into SFC classes case RESET_LPC_SLAVE: { // @todo RTC 109999 - Skipping because SFC resets can @@ -2774,6 +2303,7 @@ errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel ) break; } + //@fixme RTC:109859 -- All SFC recovery should move into SFC classes case RESET_LPC_SLAVE_ERRS: { // @todo RTC 109999 - Skipping because SFC resets can @@ -2790,6 +2320,7 @@ errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel ) } + //@fixme RTC:109859 -- All SFC recovery should move into SFC classes case RESET_SFC_LOCAL_BUS: { // @todo RTC 109999 - Skipping because SFC resets can @@ -2809,6 +2340,7 @@ errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel ) break; } + //@fixme RTC:109859 -- All SFC recovery should move into SFC classes case RESET_SFCBUS_LPCSLAVE_ERRS: { // @todo RTC 109999 - Skipping because SFC resets can @@ -2883,6 +2415,7 @@ errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel ) +//@fixme RTC:109859 -- All SFC recovery should move into SFC classes errlHndl_t PnorDD::reinitializeSfc( void ) { TRACFCOMP(g_trac_pnor, "PnorDD::reinitializeSfc>"); diff --git a/src/usr/pnor/pnordd.H b/src/usr/pnor/pnordd.H index 25915b294..31b3d8d7d 100644 --- a/src/usr/pnor/pnordd.H +++ b/src/usr/pnor/pnordd.H @@ -36,7 +36,7 @@ namespace PNOR { class UdPnorDDParms; } /** * @brief PNOR Device Driver Class - * Provides access to the PNOR flash via the ECCB/LPC/SPI hardware + * Provides access to the PNOR flash via the ECCB/LPC hardware */ class PnorDD { @@ -126,17 +126,6 @@ class PnorDD }; /** - * @brief LPC HC Registers - * These are offsets within the LPC Host Controller Register Space - */ - enum LpcRegAddr { - LPC_REG_BAR0 = 0x00, /**< BAR0 : OPB register */ - LPC_REG_BAR1 = 0x04, /**< BAR1 : LPC I/O space */ - LPC_REG_BAR2 = 0x08, /**< BAR2 : LPC Memory space */ - LPC_REG_BAR3 = 0x0C, /**< BAR3 : LPC Firmware space */ - }; - - /** * @brief SPI Config Info * OP Codes and other MISC info for configuring SFC */ @@ -282,9 +271,6 @@ class PnorDD */ 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 */ @@ -416,36 +402,6 @@ 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 @@ -610,17 +566,7 @@ class PnorDD #endif LPC_TOP_OF_FLASH_OFFSET = 0xFFFFFFFF, - 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> - ECCB_LPC_CTL_REG_DEFAULT = 0xD400010000000000, - - // Error bits: 41-43, 56 (52=cmd complete) (not 57: only non-fw use) - ECCB_LPC_STAT_REG_ERROR_MASK = 0x0000000000700080, /**< Error Bits */ + ECCB_STAT_REG = 0x000B0022, /**< ECCB Status Reg (FW) */ /**< OPB LPCM Sync FIR Reg - used to read the FIR*/ OPB_LPCM_FIR_REG = 0x01010C00, @@ -639,8 +585,6 @@ class PnorDD 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 */ }; /** @@ -784,51 +728,6 @@ class PnorDD void addFFDCRegisters(errlHndl_t & io_errl); /** - * @brief ECCB Control Register Layout - */ - union EccbControlReg_t - { - uint64_t data64; - struct - { - // unused sections should be set to zero - uint64_t magic1 : 8; /**< 0:7 = b11010100 per spec */ - uint64_t unused1 : 7; /**< 8:14 */ - uint64_t read_op : 1; /**< 15 = set for read operation */ - uint64_t unused2 : 7; /**< 16:22 */ - uint64_t addr_len : 3; /**< 23:25 = 100 means 4 byte */ - uint64_t unused3 : 6; /**< 26:31 */ - uint64_t address : 32; /**< 32:63 = LPC Address */ - }; - - EccbControlReg_t() : data64(ECCB_LPC_CTL_REG_DEFAULT) {}; - }; - - /** - * @brief ECCB Status Register Layout - */ - union EccbStatusReg_t - { - uint64_t data64; - struct - { - uint64_t unused : 6; /**< 0:5 */ - uint64_t read_data : 32; /**< 6:37 */ - uint64_t unused1 : 3; /**< 38:40 */ - uint64_t eccb_err : 3; /**< 41:43 = ECCB_Error_Info */ - uint64_t busy : 1; /**< 44 = Operation Busy */ - uint64_t unused2 : 7; /**< 45:51 */ - uint64_t op_done : 1; /**< 52 = Command Complete */ - uint64_t unused3 : 3; /**< 53:55 */ - uint64_t addr_parity_err : 1; /**< 56 = ECC Address Register - Parity Error */ - uint64_t unused4 : 7; /**< 57:63 */ - }; - EccbStatusReg_t() : data64(0) {}; - }; - - - /** * @brief Reset PNOR Logic At The Specified Level * * @parm i_pnorResetLevel Level of PNOR to Reset diff --git a/src/usr/pnor/pnorvalid.C b/src/usr/pnor/pnorvalid.C index 22232729d..b690fd5fc 100644 --- a/src/usr/pnor/pnorvalid.C +++ b/src/usr/pnor/pnorvalid.C @@ -6,6 +6,7 @@ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -46,6 +47,7 @@ #include "pnordd.H" #include <pnor/pnorif.H> #include <pnor/pnor_reasoncodes.H> +#include <lpc/lpcif.H> // Used for creating an Invalid TOC ("PNOR") @@ -139,7 +141,38 @@ errlHndl_t validateAltMaster( void ) { delete pnordd; pnordd = NULL; + + // Delete the LPC objects we already used + l_err = LPC::create_altmaster_objects( false, NULL ); + if ( l_err ) + { + // Commit Error Log, but continue the test + TRACFCOMP( g_trac_pnor, INFO_MRK"PNOR::validateAltMaster> Could not delete LPC objects. eid=0x%X, rc=0x%X. Committing log and Continuing", l_err->eid(), l_err->reasonCode()); + + l_err->collectTrace(PNOR_COMP_NAME); + + // if there was an error, commit here and then proceed to + // the next processor + errlCommit(l_err,PNOR_COMP_ID); + continue; + } + } + + // Create the LPC objects we're going to use + l_err = LPC::create_altmaster_objects( true, procList[i] ); + if ( l_err ) + { + // Commit Error Log, but continue the test + TRACFCOMP( g_trac_pnor, INFO_MRK"PNOR::validateAltMaster> Could not create LPC objects for %.8X. eid=0x%X, rc=0x%X. Committing log and Continuing", TARGETING::get_huid(procList[i]), l_err->eid(), l_err->reasonCode()); + + l_err->collectTrace(PNOR_COMP_NAME); + + // if there was an error, commit here and then proceed to + // the next processor + errlCommit(l_err,PNOR_COMP_ID); + continue; } + pnordd = new PnorDD(PnorDD::MODEL_REAL_MMIO, 0, 0, procList[i]); // Read Flash @@ -248,6 +281,20 @@ errlHndl_t validateAltMaster( void ) { delete pnordd; pnordd = NULL; + + // Delete the LPC objects we used + l_err = LPC::create_altmaster_objects( false, NULL ); + if ( l_err ) + { + // Commit Error Log, but continue the test + TRACFCOMP( g_trac_pnor, INFO_MRK"PNOR::validateAltMaster> Could not delete LPC objects. eid=0x%X, rc=0x%X. Committing log and Continuing", l_err->eid(), l_err->reasonCode()); + + l_err->collectTrace(PNOR_COMP_NAME); + + // if there was an error, commit here and then proceed to + // the next processor + errlCommit(l_err,PNOR_COMP_ID); + } } if(tocBuffer != NULL) |