summaryrefslogtreecommitdiffstats
path: root/src/usr
diff options
context:
space:
mode:
authorDan Crowell <dcrowell@us.ibm.com>2014-05-13 16:45:45 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2014-09-22 13:31:04 -0500
commitd001febf0b5594120dd422bcbe5736221471e0ca (patch)
tree227678a8341e96ef6bf9fe385e1215cffe613b97 /src/usr
parente7d14e42caf28c421dfaa4b3b15bda7cf5a77e43 (diff)
downloadtalos-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/HBconfig4
-rw-r--r--src/usr/lpc/lpcdd.C1182
-rw-r--r--src/usr/lpc/lpcdd.H338
-rw-r--r--src/usr/lpc/makefile34
-rw-r--r--src/usr/makefile1
-rw-r--r--src/usr/pnor/pnordd.C533
-rw-r--r--src/usr/pnor/pnordd.H105
-rw-r--r--src/usr/pnor/pnorvalid.C47
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)
OpenPOWER on IntegriCloud