summaryrefslogtreecommitdiffstats
path: root/src/usr/i2c
diff options
context:
space:
mode:
authorAni Bagepalli <abagepa@us.ibm.com>2014-09-29 17:16:44 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2014-10-22 21:11:07 -0500
commitc928dcb161d02921ceff4f2c10801334da8f41ee (patch)
tree22fed87dfdd9abb968a29ed7064fc4e49df2ad56 /src/usr/i2c
parent3b3b3a82d88f9008b5c36818670c45ad003943e8 (diff)
downloadtalos-hostboot-c928dcb161d02921ceff4f2c10801334da8f41ee.tar.gz
talos-hostboot-c928dcb161d02921ceff4f2c10801334da8f41ee.zip
Force I2C reset/unlock
This commit add a method to manually toggle the clock and data lines to reset the I2C bus that is locked. This method will also reset the slave I2C chips. Change-Id: I6353dd24f86ca9b0a649875b11bae47035887c84 RTC:97439 Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/13659 Tested-by: Jenkins Server Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr/i2c')
-rwxr-xr-xsrc/usr/i2c/i2c.C320
-rwxr-xr-xsrc/usr/i2c/i2c.H24
-rwxr-xr-xsrc/usr/i2c/test/i2ctest.H86
3 files changed, 422 insertions, 8 deletions
diff --git a/src/usr/i2c/i2c.C b/src/usr/i2c/i2c.C
index c82ff28a1..4cd4aae7f 100755
--- a/src/usr/i2c/i2c.C
+++ b/src/usr/i2c/i2c.C
@@ -94,6 +94,10 @@ static i2c_addrs_t masterAddrs[] =
I2C_MASTER0_ADDR | 0xA, // Interrupt Register
I2C_MASTER0_ADDR | 0xB, // Status Register (Read)
I2C_MASTER0_ADDR | 0xB, // Reset (Write)
+ I2C_MASTER0_ADDR | 0xD, // Set SCL Register (write)
+ I2C_MASTER0_ADDR | 0xF, // Reset SCL Register (write)
+ I2C_MASTER0_ADDR | 0x10, // Set SDA Register (write)
+ I2C_MASTER0_ADDR | 0x11, // Reset SDA Register (write)
},
{ /* Master 1 */
I2C_MASTER1_ADDR | 0x4, // FIFO
@@ -103,6 +107,10 @@ static i2c_addrs_t masterAddrs[] =
I2C_MASTER1_ADDR | 0xA, // Interrupt Register
I2C_MASTER1_ADDR | 0xB, // Status Register (Read)
I2C_MASTER1_ADDR | 0xB, // Reset (Write)
+ I2C_MASTER1_ADDR | 0xD, // Set SCL Register (write)
+ I2C_MASTER1_ADDR | 0xF, // Reset SCL Register (write)
+ I2C_MASTER1_ADDR | 0x10, // Set SDA Register (write)
+ I2C_MASTER1_ADDR | 0x11, // Reset SDA Register (write)
},
{ /* Master 2 */
I2C_MASTER2_ADDR | 0x4, // FIFO
@@ -112,6 +120,10 @@ static i2c_addrs_t masterAddrs[] =
I2C_MASTER2_ADDR | 0xA, // Interrupt Register
I2C_MASTER2_ADDR | 0xB, // Status Register (Read)
I2C_MASTER2_ADDR | 0xB, // Reset (Write)
+ I2C_MASTER2_ADDR | 0xD, // Set SCL Register (write)
+ I2C_MASTER2_ADDR | 0xF, // Reset SCL Register (write)
+ I2C_MASTER2_ADDR | 0x10, // Set SDA Register (write)
+ I2C_MASTER2_ADDR | 0x11, // Reset SDA Register (write)
}
};
@@ -402,9 +414,20 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType,
// Handle Error from I2C Operation
if( err )
{
+
+ // if it was a bus arbition lost error set the
+ // the reset level so a force unlock reset can be performed
+ i2c_reset_level l_reset_level = BASIC_RESET;
+
+ if ( err->reasonCode() == I2C_ARBITRATION_LOST_ONLY_FOUND )
+ {
+ l_reset_level = FORCE_UNLOCK_RESET;
+ }
+
// Reset the I2C Master
err_reset = i2cReset( i_target,
- args );
+ args,
+ l_reset_level);
if( err_reset )
{
@@ -992,6 +1015,7 @@ errlHndl_t i2cCheckForErrors ( TARGETING::Target * i_target,
errlHndl_t err = NULL;
bool errorFound = false;
bool nackFound = false;
+ bool busArbiLostFound = false;
uint64_t intRegVal = 0x0;
TRACDCOMP( g_trac_i2c,
@@ -1033,7 +1057,7 @@ errlHndl_t i2cCheckForErrors ( TARGETING::Target * i_target,
if( 1 == i_statusVal.arbitration_lost_error )
{
- errorFound = true;
+ busArbiLostFound = true;
TRACFCOMP( g_trac_i2c,
ERR_MRK"I2C Arbitration Lost! - status reg: %016llx",
i_statusVal.value );
@@ -1132,10 +1156,11 @@ errlHndl_t i2cCheckForErrors ( TARGETING::Target * i_target,
* @moduleid I2C_CHECK_FOR_ERRORS
* @userdata1 Status Register Value
* @userdata2 Interrupt Register Value
- * @devdesc Error was found in I2C status register. Check
- * userdata1 to determine what the error was.
+ * @devdesc a NACK Error was found in the I2C status
+ * register.
* @custdesc A problem occurred during the IPL of the system:
- * An error was found in the I2C status register.
+ * A NACK error was found in the I2C Status
+ * register.
*/
err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
I2C_CHECK_FOR_ERRORS,
@@ -1160,6 +1185,47 @@ errlHndl_t i2cCheckForErrors ( TARGETING::Target * i_target,
break;
}
+ else if( busArbiLostFound )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cCheckForErrors() - Bus Arbitration Lost (only error)");
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_ARBITRATION_LOST_ONLY_FOUND
+ * @severity ERRL_SEV_UNRECOVERABLE
+ * @moduleid I2C_CHECK_FOR_ERRORS
+ * @userdata1 Status Register Value
+ * @userdata2 Interrupt Register Value
+ * @devdesc Bus Arbitration Lost Error was found in
+ * the I2C status register.
+ * @custdesc A problem occurred during the IPL of the system:
+ * A Bus Arbitration Lost error was found
+ * in the I2C status register.
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_CHECK_FOR_ERRORS,
+ I2C_ARBITRATION_LOST_ONLY_FOUND,
+ i_statusVal.value,
+ intRegVal );
+
+ // For now limited in what we can call out:
+ // Could be an issue with Processor or its bus
+ // -- both on the same FRU
+ // @todo RTC 94872 - update this callout
+ err->addHwCallout( i_target,
+ HWAS::SRCI_PRIORITY_HIGH,
+ HWAS::NO_DECONFIG,
+ HWAS::GARD_NULL );
+
+ // Or HB code failed to do the procedure correctly
+ err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE,
+ HWAS::SRCI_PRIORITY_LOW);
+
+ err->collectTrace( "I2C" );
+
+ break;
+ }
} while( 0 );
@@ -1276,12 +1342,239 @@ errlHndl_t i2cWaitForFifoSpace ( TARGETING::Target * i_target,
return err;
} // end i2cWaitForFifoSpace
+// ------------------------------------------------------------------
+// i2cSendStopSignal
+// ------------------------------------------------------------------
+errlHndl_t i2cSendStopSignal(TARGETING::Target * i_target,
+ misc_args_t & i_args)
+{
+
+ errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cSendStopSignal" );
+
+ do
+ {
+ residual_length_reg_t clkdataline;
+ clkdataline.value = 0x0;
+
+ //manually send stop signal
+ // set clock low: write 0 to immediate reset scl register
+
+ TRACUCOMP(g_trac_i2c,"i2cSendStopSignal"
+ "clock line 0x%016llx",
+ clkdataline.value );
+
+ err = deviceWrite( i_target,
+ &clkdataline.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].reset_scl ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C reset SCL register Failed!!" );
+ break;
+ }
+
+ //set data low: write 0 to immediate reset sda register
+ err = deviceWrite( i_target,
+ &clkdataline.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].reset_sda ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C reset SDA register Failed!!" );
+ break;
+ }
+
+ // set clock high: write 0 to immediate set scl register
+ err = deviceWrite( i_target,
+ &clkdataline.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].set_scl ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C set SCL register Failed!!" );
+ break;
+ }
+
+ //set data high: write 0 to immediate set sda register
+ err = deviceWrite( i_target,
+ &clkdataline.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].set_sda ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C set SDA register Failed!!" );
+ break;
+ }
+ }while(0);
+
+ return err;
+}//end i2cSendStopSignal
+
+
+// ------------------------------------------------------------------
+// i2cToggleClockLine
+// ------------------------------------------------------------------
+errlHndl_t i2cToggleClockLine(TARGETING::Target * i_target,
+ misc_args_t & i_args)
+{
+
+ errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cToggleClockLine()" );
+
+ do
+ {
+ residual_length_reg_t clkline;
+ clkline.value = 0x0;
+
+ //toggle clock line
+ // set clock low: write 0 to immediate reset scl register
+
+ TRACUCOMP(g_trac_i2c,"i2cToggleClockLine()"
+ "clock line 0x%016llx",
+ clkline.value );
+
+ err = deviceWrite( i_target,
+ &clkline.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].reset_scl ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C reset SCL register Failed!!" );
+ break;
+ }
+
+ // set clock high: write 0 to immediate set scl register
+ err = deviceWrite( i_target,
+ &clkline.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].set_scl ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C set SCL register Failed!!" );
+ break;
+ }
+
+ }while(0);
+
+ return err;
+}//end i2cToggleClockLine
+
+
+// ------------------------------------------------------------------
+// i2cForceResetAndUnlock
+// ------------------------------------------------------------------
+errlHndl_t i2cForceResetAndUnlock( TARGETING::Target * i_target,
+ misc_args_t & i_args)
+{
+
+ errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cForceResetAndUnlock()" );
+
+
+ do
+ {
+
+ TRACUCOMP(g_trac_i2c,"i2cForceResetAndUnlock()"
+ "reset[0x%lx]",
+ masterAddrs[i_args.engine].reset );
+
+
+ // enable diagnostic mode
+ // set bit in mode register
+ mode_reg_t diagnostic;
+
+ diagnostic.diag_mode = 0x1;
+
+ err = deviceWrite( i_target,
+ &diagnostic.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].mode ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C Enable Diagnostic mode Failed!!" );
+ break;
+ }
+
+
+
+ //toggle clock line
+ err = i2cToggleClockLine( i_target,
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ //manually send stop signal
+ err = i2cSendStopSignal( i_target,
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ //disable diagnostic mode
+ //set bit in mode register
+ diagnostic.diag_mode = 0x0;
+
+ err = deviceWrite( i_target,
+ &diagnostic.value,
+ size,
+ DEVICE_SCOM_ADDRESS(
+ masterAddrs[i_args.engine].mode ) );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"I2C disable Diagnostic mode Failed!!" );
+ break;
+ }
+
+
+ }while(0);
+
+ return err;
+}// end i2cForceResetAndUnlock
// ------------------------------------------------------------------
// i2cReset
// ------------------------------------------------------------------
errlHndl_t i2cReset ( TARGETING::Target * i_target,
- misc_args_t & i_args)
+ misc_args_t & i_args,
+ i2c_reset_level i_reset_level)
{
errlHndl_t err = NULL;
size_t size = sizeof(uint64_t);
@@ -1315,6 +1608,21 @@ errlHndl_t i2cReset ( TARGETING::Target * i_target,
break;
}
+ //if the reset is a force unlock then we need to enable
+ //diagnostic mode and toggle the clock and data lines
+ //to manually reset the bus
+ if( i_reset_level == FORCE_UNLOCK_RESET )
+ {
+ err = i2cForceResetAndUnlock( i_target,
+ i_args );
+
+ if( err )
+ {
+ //error trying to force a reset break
+ break;
+ }
+ }
+
// Part of doing the I2C Master reset is also sending a stop
// command to the slave device.
err = i2cSendSlaveStop( i_target,
diff --git a/src/usr/i2c/i2c.H b/src/usr/i2c/i2c.H
index 05b66d12c..7834cbd0c 100755
--- a/src/usr/i2c/i2c.H
+++ b/src/usr/i2c/i2c.H
@@ -5,7 +5,9 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* COPYRIGHT International Business Machines Corp. 2011,2014 */
+/* Contributors Listed Below - COPYRIGHT 2011,2014 */
+/* [+] 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. */
@@ -40,6 +42,16 @@ namespace I2C
{
/**
+ * @brief Different Reset level to perform an i2c reset
+ */
+enum i2c_reset_level
+{
+ BASIC_RESET,
+ FORCE_UNLOCK_RESET,
+};
+
+
+/**
* @brief FIFO size (width) in bytes. This dictates how many bytes
* we can read/write in one FIFO access.
*/
@@ -162,6 +174,11 @@ struct i2c_addrs_t
uint64_t interrupt;
uint64_t status;
uint64_t reset;
+ uint64_t set_scl;
+ uint32_t reset_scl;
+ uint32_t set_sda;
+ uint32_t reset_sda;
+
};
/**
@@ -620,11 +637,14 @@ errlHndl_t i2cWaitForFifoSpace ( TARGETING::Target * i_target,
* @param[in] i_args - Structure containing arguments needed for a command
* transaction.
*
+ * @param[in] i_reset_level - level of reset to use.
+ *
* @return errHndl_t - NULL if successful, otherwise a pointer to
* the error log.
*/
errlHndl_t i2cReset ( TARGETING::Target * i_target,
- misc_args_t & i_args );
+ misc_args_t & i_args,
+ i2c_reset_level i_reset_level = BASIC_RESET );
/**
* @brief This function will send the Stop command to the slave device
diff --git a/src/usr/i2c/test/i2ctest.H b/src/usr/i2c/test/i2ctest.H
index f19edde02..f33ec1824 100755
--- a/src/usr/i2c/test/i2ctest.H
+++ b/src/usr/i2c/test/i2ctest.H
@@ -912,6 +912,92 @@ class I2CTest: public CxxTest::TestSuite
fails, num_ops );
}
+ void testI2cForceReset(void)
+ {
+
+
+ errlHndl_t err = NULL;
+
+ do
+ {
+ I2C::misc_args_t io_args;
+
+ uint64_t data = 0x0;
+
+ // Get the Proc Target
+ TARGETING::TargetService& tS = TARGETING::targetService();
+ TARGETING::Target* procTarget = NULL;
+ tS.masterProcChipTargetHandle( procTarget );
+ assert( procTarget != NULL );
+
+ // reset the target and then try a read
+ if(procTarget->getAttr<TARGETING::ATTR_HWAS_STATE>().functional)
+ {
+ TRACFCOMP( g_trac_i2c,
+ " testI2cForceReset- functional good path" );
+ io_args.engine = 0x0;
+ io_args.port = 0x0;
+ //get engine lock attr for engine 0
+ //prevents sbe update test fails
+
+ mutex_t * engineLock = NULL;
+ engineLock =
+ procTarget->getHbMutexAttr<TARGETING::ATTR_I2C_ENGINE_MUTEX_0>();
+
+ (void)mutex_lock( engineLock );
+
+ //force reset
+ err = I2C::i2cReset(procTarget,
+ io_args,
+ I2C::FORCE_UNLOCK_RESET);
+
+ (void) mutex_unlock( engineLock );
+
+ //if there was an error for a reason commit it
+ if(err)
+ {
+ errlCommit( err,I2C_COMP_ID );
+ delete err; err = NULL;
+ }
+
+ nanosleep( 0, 100);
+
+ //read
+ size_t data_size = 8;
+ err = deviceOp( DeviceFW::READ,
+ procTarget,
+ &data,
+ data_size,
+ DEVICE_I2C_ADDRESS( 0,
+ 0,
+ 0xAC ) );
+
+ }
+
+
+
+
+ if(err)
+ {
+ TS_FAIL("testI2cForceReset failed err detected");
+ errlCommit( err,I2C_COMP_ID );
+ delete err; err = NULL;
+ break;
+ }
+
+ if(data == 0)
+ {
+ //there should be some data
+ TS_FAIL("testI2cForceReset failed data read is 0");
+ break;
+ }
+
+
+ }while(0);
+
+
+ }
+
};
#endif
OpenPOWER on IntegriCloud