summaryrefslogtreecommitdiffstats
path: root/src/usr/i2c
diff options
context:
space:
mode:
authoraalugore <aalugore@us.ibm.com>2016-01-22 13:36:14 -0600
committerStephen Cprek <smcprek@us.ibm.com>2016-04-21 13:51:43 -0500
commit4f4f097d65e919bcc8bd5706f50ea0f413b8bfef (patch)
tree90030301032f3a87e952e5c161763bab0ad812be /src/usr/i2c
parent18e7af4cee8e4a9b88dee257edffb528b969ecd3 (diff)
downloadtalos-hostboot-4f4f097d65e919bcc8bd5706f50ea0f413b8bfef.tar.gz
talos-hostboot-4f4f097d65e919bcc8bd5706f50ea0f413b8bfef.zip
DDR4 - Allow SPD writes
-DDR4 has 512-byte EEPROM with 2 256-byte pages. This commit contains the necessary page switching logic to support this. Change-Id: Iaa8e3e344def98b71d6a9e9387c5e0d9137a0397 RTC:137707 ForwardPort: yes Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/797 Tested-by: Jenkins Server Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com> Tested-by: Jenkins OP Build CI Reviewed-by: William G. Hoffa <wghoffa@us.ibm.com> Tested-by: Jenkins OP HW Tested-by: FSP CI Jenkins Reviewed-by: Matthew A. Ploetz <maploetz@us.ibm.com> Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/871
Diffstat (limited to 'src/usr/i2c')
-rwxr-xr-xsrc/usr/i2c/eepromdd.C895
-rwxr-xr-xsrc/usr/i2c/eepromdd.H133
-rwxr-xr-xsrc/usr/i2c/i2c.C570
-rwxr-xr-xsrc/usr/i2c/i2c.H103
4 files changed, 1370 insertions, 331 deletions
diff --git a/src/usr/i2c/eepromdd.C b/src/usr/i2c/eepromdd.C
index 55b136cc1..dc3eaa9cc 100755
--- a/src/usr/i2c/eepromdd.C
+++ b/src/usr/i2c/eepromdd.C
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2011,2015 */
+/* Contributors Listed Below - COPYRIGHT 2011,2016 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -114,7 +114,7 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
va_list i_args )
{
errlHndl_t err = NULL;
- TARGETING::Target * theTarget = NULL;
+ TARGETING::Target * i2cMasterTarget = NULL;
eeprom_addr_t i2cInfo;
i2cInfo.chip = va_arg( i_args, uint64_t );
@@ -124,7 +124,7 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
ENTER_MRK"eepromPerformOp()" );
TRACUCOMP (g_trac_eeprom, ENTER_MRK"eepromPerformOp(): "
- "i_opType=%d, chip=%d, offset=%d, len=%d",
+ "i_opType=%d, chip=%d, offset=%x, len=%d",
(uint64_t) i_opType, i2cInfo.chip, i2cInfo.offset, io_buflen);
#ifdef __HOSTBOOT_RUNTIME
@@ -149,7 +149,7 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
// the I2C Master
err = eepromGetI2CMasterTarget( i_target,
i2cInfo,
- theTarget );
+ i2cMasterTarget );
if( err )
{
@@ -196,10 +196,10 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
#ifdef __HOSTBOOT_RUNTIME
// Disable Sensor Cache if the I2C master target is MEMBUF
- if( theTarget->getAttr<TARGETING::ATTR_TYPE>() ==
+ if( i2cMasterTarget->getAttr<TARGETING::ATTR_TYPE>() ==
TARGETING::TYPE_MEMBUF )
{
- err = I2C::i2cDisableSensorCache(theTarget,scacDisabled);
+ err = I2C::i2cDisableSensorCache(i2cMasterTarget,scacDisabled);
if ( err )
{
break;
@@ -210,7 +210,7 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
// Do the read or write
if( i_opType == DeviceFW::READ )
{
- err = eepromRead( theTarget,
+ err = eepromRead( i2cMasterTarget,
io_buffer,
io_buflen,
i2cInfo );
@@ -223,7 +223,7 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
}
else if( i_opType == DeviceFW::WRITE )
{
- err = eepromWrite( theTarget,
+ err = eepromWrite( i2cMasterTarget,
io_buffer,
io_buflen,
i2cInfo );
@@ -265,18 +265,18 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
// Re-enable sensor cache if it was disabled before the eeprom op and
// the I2C master target is MEMBUF
if( scacDisabled &&
- (theTarget->getAttr<TARGETING::ATTR_TYPE>() == TARGETING::TYPE_MEMBUF) )
+ (i2cMasterTarget->getAttr<TARGETING::ATTR_TYPE>() == TARGETING::TYPE_MEMBUF) )
{
errlHndl_t tmp_err = NULL;
- tmp_err = I2C::i2cEnableSensorCache(theTarget);
+ tmp_err = I2C::i2cEnableSensorCache(i2cMasterTarget);
if( err && tmp_err)
{
delete tmp_err;
TRACFCOMP(g_trac_eeprom,
ERR_MRK" Enable Sensor Cache failed for HUID=0x%.8X",
- TARGETING::get_huid(theTarget));
+ TARGETING::get_huid(i2cMasterTarget));
}
else if(tmp_err)
{
@@ -308,12 +308,11 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
//-------------------------------------------------------------------
bool eepromPresence ( TARGETING::Target * i_target )
{
-
- TRACDCOMP(g_trac_eeprom, ENTER_MRK"eepromPresence()");
+ TRACUCOMP(g_trac_eeprom, ENTER_MRK"eepromPresence()");
errlHndl_t err = NULL;
bool l_present = false;
- TARGETING::Target * theTarget = NULL;
+ TARGETING::Target * i2cMasterTarget = NULL;
eeprom_addr_t i2cInfo;
@@ -337,7 +336,7 @@ bool eepromPresence ( TARGETING::Target * i_target )
// the I2C Master
err = eepromGetI2CMasterTarget( i_target,
i2cInfo,
- theTarget );
+ i2cMasterTarget );
if( err )
{
@@ -347,7 +346,7 @@ bool eepromPresence ( TARGETING::Target * i_target )
}
//Check for the target at the I2C level
- l_present = I2C::i2cPresence(theTarget,
+ l_present = I2C::i2cPresence(i2cMasterTarget,
i2cInfo.port,
i2cInfo.engine,
i2cInfo.devAddr );
@@ -367,6 +366,126 @@ bool eepromPresence ( TARGETING::Target * i_target )
#endif
+
+// ------------------------------------------------------------------
+// eepromPageOp
+// ------------------------------------------------------------------
+errlHndl_t eepromPageOp( TARGETING::Target * i_target,
+ bool i_switchPage,
+ bool i_lockMutex,
+ bool & io_pageLocked,
+ uint8_t i_desiredPage,
+ eeprom_addr_t i_i2cInfo )
+{
+ TRACUCOMP(g_trac_eeprom,
+ ENTER_MRK"eepromPageOp()");
+
+ errlHndl_t l_err = NULL;
+ size_t l_placeHolderZero = 0;
+
+ do
+ {
+ // DDR4 requires EEPROM page to be selected before read/write operation.
+ // The following operation locks the EEPROM_PAGE attribute behind a
+ // mutex and switches all DIMMs on the I2C bus to the appropriate
+ // page.
+ if( i_i2cInfo.addrSize == ONE_BYTE_ADDR_PAGESELECT )
+ {
+
+ bool l_lockPage;
+ if( i_switchPage )
+ {
+ // we want to switch to the desired page
+ l_lockPage = true;
+ l_err = deviceOp( DeviceFW::WRITE,
+ i_target,
+ NULL,
+ l_placeHolderZero,
+ DEVICE_I2C_CONTROL_PAGE_OP(
+ i_i2cInfo.port,
+ i_i2cInfo.engine,
+ l_lockPage,
+ i_desiredPage,
+ i_lockMutex ));
+
+ if( l_err )
+ {
+ TRACFCOMP(g_trac_eeprom,
+ "eepromPageOp::Failed locking EEPROM page");
+ break;
+ }
+ // if we make it this far, we successfully locked the page mutex
+ io_pageLocked = true;
+ }
+ else
+ {
+ // we only want to unlock the page
+ l_lockPage = false;
+ l_err = deviceOp( DeviceFW::WRITE,
+ i_target,
+ NULL,
+ l_placeHolderZero,
+ DEVICE_I2C_CONTROL_PAGE_OP(
+ i_i2cInfo.port,
+ i_i2cInfo.engine,
+ l_lockPage,
+ l_placeHolderZero,
+ i_lockMutex ));
+
+ if( l_err )
+ {
+ TRACFCOMP( g_trac_eeprom,
+ "eepromPageOp()::failed unlocking EEPROM page");
+ break;
+ }
+ // if we make it this far, we successfully unlocked the page
+ io_pageLocked = false;
+ }
+ }
+ }while(0);
+ TRACUCOMP(g_trac_eeprom,
+ EXIT_MRK"eepromPageOp()");
+ return l_err;
+}
+
+
+// ------------------------------------------------------------------
+// crossesEepromPageBoundary
+// ------------------------------------------------------------------
+bool crossesEepromPageBoundary( uint64_t i_originalOffset,
+ size_t i_originalLen,
+ size_t & io_newLen,
+ size_t & o_pageTwoBuflen,
+ eeprom_addr_t i_i2cInfo )
+{
+ bool l_boundaryCrossed = false;
+ size_t l_higherBound = i_originalOffset + i_originalLen;
+
+ if( ( i_i2cInfo.addrSize == ONE_BYTE_ADDR_PAGESELECT ) &&
+ ( ( i_originalOffset < EEPROM_PAGE_SIZE ) &&
+ ( l_higherBound > EEPROM_PAGE_SIZE) ) )
+ {
+ // The read/write request crosses the boundary
+ l_boundaryCrossed = true;
+
+ // Calculate the new length of the page 0 buffer and the
+ // length of the page 1 buffer
+ o_pageTwoBuflen = l_higherBound - EEPROM_PAGE_SIZE;
+ io_newLen = i_originalLen - o_pageTwoBuflen;
+ }
+ else
+ {
+ // The read/write request does not cross the boundary.
+ // Update new length to be used by subsequent operations
+ io_newLen = i_originalLen;
+ o_pageTwoBuflen = 0;
+ }
+
+ return l_boundaryCrossed;
+}
+
+
+
// ------------------------------------------------------------------
// eepromRead
// ------------------------------------------------------------------
@@ -376,33 +495,194 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
eeprom_addr_t i_i2cInfo )
{
errlHndl_t err = NULL;
- errlHndl_t err_NACK = NULL;
uint8_t byteAddr[MAX_BYTE_ADDR];
size_t byteAddrSize = 0;
- bool unlock = false;
+ bool l_pageLocked = false;
+ uint8_t l_desiredPage = 0;
+ bool l_boundaryCrossed = false;
+ size_t l_readBuflen = 0;
+ size_t l_pageTwoBuflen = 0;
- TRACDCOMP( g_trac_eeprom,
+ TRACUCOMP( g_trac_eeprom,
ENTER_MRK"eepromRead()" );
do
{
- TRACSCOMP( g_trac_eepromr,
+ TRACUCOMP( g_trac_eepromr,
"EEPROM READ START : Chip: %02d : Offset %.2X : Len %d",
i_i2cInfo.chip, i_i2cInfo.offset, i_buflen );
- err = eepromPrepareAddress( &byteAddr,
+
+ // Check to see if the Read operation straddles the EEPROM page
+ //boundary
+ l_boundaryCrossed = crossesEepromPageBoundary( i_i2cInfo.offset,
+ i_buflen,
+ l_readBuflen,
+ l_pageTwoBuflen,
+ i_i2cInfo );
+
+ // Set addressing parameters
+ err = eepromPrepareAddress( i_target,
+ &byteAddr,
byteAddrSize,
- i_i2cInfo );
+ l_desiredPage,
+ i_i2cInfo);
if( err )
{
+ TRACFCOMP(g_trac_eeprom,
+ ERR_MRK"eepromRead()::eepromPrepareAddress()");
+ break;
+ }
+
+
+ // Attempt to lock page mutex
+ bool l_switchPage = true;
+ bool l_lockMutex = true;
+ err = eepromPageOp( i_target,
+ l_switchPage,
+ l_lockMutex,
+ l_pageLocked,
+ l_desiredPage,
+ i_i2cInfo );
+
+ if( err )
+ {
+ TRACFCOMP(g_trac_eeprom,
+ "eepromRead()::eepromPageOp()::failed locking page");
break;
}
// Lock to sequence operations
mutex_lock( &g_eepromMutex );
- unlock = true;
+ // First Read. If Second read is necessary, this call will read
+ // everything from the original offset up to the 256th byte
+ err = eepromReadData( i_target,
+ o_buffer,
+ l_readBuflen,
+ &byteAddr,
+ byteAddrSize,
+ i_i2cInfo );
+ if( err )
+ {
+ TRACFCOMP(g_trac_eeprom,
+ "Failed reading data: original read");
+ break;
+ }
+
+
+ // Perform the second Read if necessary. Read starts at
+ // begining of EEPROM page 1 (offset=0x100) and reads the
+ // rest of the required data.
+ if( l_boundaryCrossed )
+ {
+ //Prepare the address to read at the start of EEPROM page one
+ i_i2cInfo.offset = EEPROM_PAGE_SIZE; // 0x100
+ err = eepromPrepareAddress( i_target,
+ &byteAddr,
+ byteAddrSize,
+ l_desiredPage,
+ i_i2cInfo );
+ if( err )
+ {
+ TRACFCOMP(g_trac_eeprom,
+ "Error preparing address: second eeprom read");
+ break;
+ }
+
+ // Switch to the second EEPROM page
+ l_switchPage = true;
+ l_lockMutex = false;
+ err = eepromPageOp( i_target,
+ l_switchPage,
+ l_lockMutex,
+ l_pageLocked,
+ l_desiredPage,
+ i_i2cInfo );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_eeprom,
+ "Failed switching to EEPROM page 1 for second read op");
+ break;
+ }
+
+ // Perform the second read operation
+ err = eepromReadData(
+ i_target,
+ &(reinterpret_cast<uint8_t*>(o_buffer)[l_readBuflen]),
+ l_pageTwoBuflen,
+ &byteAddr,
+ byteAddrSize,
+ i_i2cInfo );
+
+ if( err )
+ {
+ TRACFCOMP( g_trac_eeprom,
+ "Failed reading data: second read");
+ break;
+ }
+ }
+
+
+
+
+ TRACUCOMP( g_trac_eepromr,
+ "EEPROM READ END : Chip: %02d : Offset %.2X : Len %d : %016llx",
+ i_i2cInfo.chip, l_originalOffset, i_buflen,
+ *((uint64_t*)o_buffer) );
+
+ } while( 0 );
+
+ // Unlock eeprom mutex no matter what
+ mutex_unlock( & g_eepromMutex );
+
+ // Whether we failed in the main routine or not, unlock page iff the page is locked
+ if( l_pageLocked )
+ {
+ errlHndl_t l_pageOpErrl = NULL;
+ bool l_switchPage = false;
+ bool l_lockMutex = false;
+ l_pageOpErrl = eepromPageOp( i_target,
+ l_switchPage,
+ l_lockMutex,
+ l_pageLocked,
+ l_desiredPage,
+ i_i2cInfo );
+ if( l_pageOpErrl )
+ {
+ TRACFCOMP(g_trac_eeprom,
+ "eepromRead()::Failed unlocking page");
+ errlCommit(l_pageOpErrl, I2C_COMP_ID);
+ }
+
+ }
+
+ TRACUCOMP( g_trac_eeprom,
+ EXIT_MRK"eepromRead()" );
+
+ return err;
+} // end eepromRead
+
+
+// ------------------------------------------------------------------
+// eepromReadData
+// ------------------------------------------------------------------
+errlHndl_t eepromReadData( TARGETING::Target * i_target,
+ void * o_buffer,
+ size_t i_buflen,
+ void * i_byteAddress,
+ size_t i_byteAddressSize,
+ eeprom_addr_t i_i2cInfo )
+{
+ errlHndl_t l_err = NULL;
+ errlHndl_t err_NACK = NULL;
+
+ TRACUCOMP(g_trac_eeprom,
+ ENTER_MRK"eepromReadData()");
+ do
+ {
/***********************************************************/
/* Attempt read multiple times ONLY on NACK fails */
/***********************************************************/
@@ -412,10 +692,10 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
{
// Only write the byte address if we have data to write
- if( 0 != byteAddrSize )
+ if( 0 != i_byteAddressSize )
{
// Use the I2C OFFSET Interface for the READ
- err = deviceOp( DeviceFW::READ,
+ l_err = deviceOp( DeviceFW::READ,
i_target,
o_buffer,
i_buflen,
@@ -423,18 +703,18 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
i_i2cInfo.port,
i_i2cInfo.engine,
i_i2cInfo.devAddr,
- byteAddrSize,
- reinterpret_cast<uint8_t*>(&byteAddr)));
+ i_byteAddressSize,
+ reinterpret_cast<uint8_t*>(i_byteAddress)));
- if( err )
+ if( l_err )
{
TRACFCOMP(g_trac_eeprom,
- ERR_MRK"eepromRead(): I2C Read-Offset failed on "
+ ERR_MRK"eepromReadData(): I2C Read-Offset failed on "
"%d/%d/0x%X aS=%d",
i_i2cInfo.port, i_i2cInfo.engine,
- i_i2cInfo.devAddr, byteAddrSize);
- TRACFBIN(g_trac_eeprom, "byteAddr[]",
- &byteAddr, byteAddrSize);
+ i_i2cInfo.devAddr, i_byteAddressSize);
+ TRACFBIN(g_trac_eeprom, "i_byteAddress[]",
+ i_byteAddress, i_byteAddressSize);
// Don't break here -- error handled below
}
@@ -442,7 +722,7 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
else
{
// Do the actual read via I2C
- err = deviceOp( DeviceFW::READ,
+ l_err = deviceOp( DeviceFW::READ,
i_target,
o_buffer,
i_buflen,
@@ -450,10 +730,10 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
i_i2cInfo.engine,
i_i2cInfo.devAddr ) );
- if( err )
+ if( l_err )
{
TRACFCOMP(g_trac_eeprom,
- ERR_MRK"eepromRead(): I2C Read failed on "
+ ERR_MRK"eepromReadData(): I2C Read failed on "
"%d/%d/0x%0X", i_i2cInfo.port, i_i2cInfo.engine,
i_i2cInfo.devAddr);
@@ -461,21 +741,21 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
}
}
- if ( err == NULL )
+ if ( l_err == NULL )
{
// Operation completed successfully
// break from retry loop
break;
}
- else if ( err->reasonCode() != I2C::I2C_NACK_ONLY_FOUND )
+ else if ( l_err->reasonCode() != I2C::I2C_NACK_ONLY_FOUND )
{
// Only retry on NACK failures: break from retry loop
- TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromRead(): Non-Nack "
+ TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromReadData(): Non-Nack "
"Error: rc=0x%X, tgt=0x%X, No Retry (retry=%d)",
- err->reasonCode(),
+ l_err->reasonCode(),
TARGETING::get_huid(i_target), retry);
- err->collectTrace(EEPROM_COMP_NAME);
+ l_err->collectTrace(EEPROM_COMP_NAME);
// break from retry loop
break;
@@ -489,9 +769,9 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
if ( err_NACK == NULL )
{
// Save original NACK error
- err_NACK = err;
+ err_NACK = l_err;
- TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromRead(): "
+ TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromReadData(): "
"NACK Error rc=0x%X, eid=0x%X, tgt=0x%X, "
"retry/MAX=%d/%d. Save error and retry",
err_NACK->reasonCode(),
@@ -504,11 +784,11 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
else
{
// Add data to original NACK error
- TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromRead(): "
+ TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromReadData(): "
"Another NACK Error rc=0x%X, eid=0x%X "
"plid=0x%X, tgt=0x%X, retry/MAX=%d/%d. "
"Delete error and retry",
- err->reasonCode(), err->eid(), err->plid(),
+ l_err->reasonCode(), l_err->eid(), l_err->plid(),
TARGETING::get_huid(i_target),
retry, EEPROM_MAX_NACK_RETRIES);
@@ -517,8 +797,8 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
.addToLog(err_NACK);
// Delete this new NACK error
- delete err;
- err = NULL;
+ delete l_err;
+ l_err = NULL;
}
// continue to retry
@@ -526,14 +806,14 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
}
else // no more retries: trace and break
{
- TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromRead(): "
+ TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromReadData(): "
"Error rc=0x%X, eid=%d, tgt=0x%X. No More "
"Retries (retry/MAX=%d/%d). Returning Error",
- err->reasonCode(), err->eid(),
+ l_err->reasonCode(), l_err->eid(),
TARGETING::get_huid(i_target),
retry, EEPROM_MAX_NACK_RETRIES);
- err->collectTrace(EEPROM_COMP_NAME);
+ l_err->collectTrace(EEPROM_COMP_NAME);
// break from retry loop
break;
@@ -545,12 +825,12 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
// Handle saved NACK error, if any
if (err_NACK)
{
- if (err)
+ if (l_err)
{
// commit original NACK error with new err PLID
- err_NACK->plid(err->plid());
- TRACFCOMP(g_trac_eeprom, "eepromRead(): Committing saved NACK "
- "err eid=0x%X with plid of returned err: 0x%X",
+ err_NACK->plid(l_err->plid());
+ TRACFCOMP(g_trac_eeprom, "eepromReadData(): Committing saved NACK "
+ "l_err eid=0x%X with plid of returned err: 0x%X",
err_NACK->eid(), err_NACK->plid());
ERRORLOG::ErrlUserDetailsTarget(i_target)
@@ -561,7 +841,7 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
else
{
// Since we eventually succeeded, delete original NACK error
- TRACFCOMP(g_trac_eeprom, "eepromRead(): Op successful, "
+ TRACFCOMP(g_trac_eeprom, "eepromReadData(): Op successful, "
"deleting saved NACK err eid=0x%X, plid=0x%X",
err_NACK->eid(), err_NACK->plid());
@@ -570,28 +850,13 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
}
}
+ }while( 0 );
- mutex_unlock( &g_eepromMutex );
- unlock = false;
-
- TRACSCOMP( g_trac_eepromr,
- "EEPROM READ END : Chip: %02d : Offset %.2X : Len %d : %016llx",
- i_i2cInfo.chip, i_i2cInfo.offset, i_buflen,
- *((uint64_t*)o_buffer) );
-
- } while( 0 );
+ TRACUCOMP(g_trac_eeprom,
+ EXIT_MRK"eepromReadData");
+ return l_err;
+}
- // Catch it if we break out early.
- if( unlock )
- {
- mutex_unlock( & g_eepromMutex );
- }
-
- TRACDCOMP( g_trac_eeprom,
- EXIT_MRK"eepromRead()" );
-
- return err;
-} // end eepromRead
// ------------------------------------------------------------------
@@ -603,31 +868,62 @@ errlHndl_t eepromWrite ( TARGETING::Target * i_target,
eeprom_addr_t i_i2cInfo )
{
errlHndl_t err = NULL;
- errlHndl_t err_NACK = NULL;
+ uint8_t l_desiredPage = 0;
+ uint8_t l_originalPage = 0;
uint8_t byteAddr[MAX_BYTE_ADDR];
size_t byteAddrSize = 0;
uint8_t * newBuffer = NULL;
bool needFree = false;
bool unlock = false;
+ bool l_pageLocked = false;
uint32_t data_left = 0;
uint32_t diff_wps = 0;
+ size_t l_writeBuflen = 0;
+ size_t l_bytesIntoSecondPage = 0;
TRACDCOMP( g_trac_eeprom,
ENTER_MRK"eepromWrite()" );
do
{
- TRACSCOMP( g_trac_eepromr,
+ TRACUCOMP( g_trac_eeprom,
"EEPROM WRITE START : Chip: %02d : Offset %.2X : Len %d : %016llx",
i_i2cInfo.chip, i_i2cInfo.offset, io_buflen,
*((uint64_t*)io_buffer) );
- err = eepromPrepareAddress( &byteAddr,
+
+ // Prepare address parameters
+ err = eepromPrepareAddress( i_target,
+ &byteAddr,
byteAddrSize,
- i_i2cInfo );
+ l_desiredPage,
+ i_i2cInfo);
if( err )
{
+ TRACFCOMP(g_trac_eeprom,
+ ERR_MRK"eepromWrite()::eepromPrepareAddress()");
+ break;
+ }
+
+ // Save original Page
+ l_originalPage = l_desiredPage;
+ // Attempt to lock page mutex
+ bool l_switchPage = true; // true: Lock and switch page
+ // false: Just unlock page
+ bool l_lockMutex = true; // true: Lock mutex
+ // false: Skip locking mutex step
+ err = eepromPageOp( i_target,
+ l_switchPage,
+ l_lockMutex,
+ l_pageLocked,
+ l_desiredPage,
+ i_i2cInfo );
+
+ if( err )
+ {
+ TRACFCOMP(g_trac_eeprom,
+ "eepromWrite()::Failed locking EEPROM page");
break;
}
@@ -677,10 +973,7 @@ errlHndl_t eepromWrite ( TARGETING::Target * i_target,
size_t loop_data_length = 0;
size_t total_bytes_written = 0;
-
- for ( uint64_t i = 0 ;
- total_bytes_written < io_buflen ;
- i++ )
+ while( total_bytes_written < io_buflen )
{
// Determine how much data can be written in this loop
// Can't go over a writePageSize boundary
@@ -701,187 +994,91 @@ errlHndl_t eepromWrite ( TARGETING::Target * i_target,
loop_data_length );
+
+ // Check if loop_data_length crosses the EEPROM page boundary
+ crossesEepromPageBoundary( i_i2cInfo.offset,
+ loop_data_length,
+ l_writeBuflen,
+ l_bytesIntoSecondPage,
+ i_i2cInfo );
+
// Setup offset/address parms
- err = eepromPrepareAddress( &byteAddr,
- byteAddrSize,
- i_i2cInfo );
+ err = eepromPrepareAddress( i_target,
+ &byteAddr,
+ byteAddrSize,
+ l_desiredPage,
+ i_i2cInfo );
+
if( err )
{
+ TRACFCOMP(g_trac_eeprom,
+ ERR_MRK"eepromWrite::eepromPrepareAddress()::loop version");
break;
}
- TRACUCOMP(g_trac_eeprom,"eepromWrite() Loop: %d/%d/0x%X "
- "loop=%d, l_d_l=%d, offset=0x%X, bAS=%d, diffs=%d/%d",
- i_i2cInfo.port, i_i2cInfo.engine, i_i2cInfo.devAddr,
- i, loop_data_length, i_i2cInfo.offset, byteAddrSize,
- data_left, diff_wps);
-
- /***********************************************************/
- /* Attempt write multiple times ONLY on NACK fails */
- /***********************************************************/
- for (uint8_t retry = 0;
- retry <= EEPROM_MAX_NACK_RETRIES;
- retry++)
- {
- // Do the actual data write
- err = deviceOp( DeviceFW::WRITE,
- i_target,
- newBuffer,
- loop_data_length,
- DEVICE_I2C_ADDRESS_OFFSET(
- i_i2cInfo.port,
- i_i2cInfo.engine,
- i_i2cInfo.devAddr,
- byteAddrSize,
- reinterpret_cast<uint8_t*>(
- &byteAddr)));
- if ( err == NULL )
- {
- // Operation completed successfully
- // break from retry loop
- break;
- }
- else if ( err->reasonCode() != I2C::I2C_NACK_ONLY_FOUND )
+ // if desired page has changed mid-request, switch to correct page
+ if( l_desiredPage != l_originalPage )
+ {
+ l_switchPage = true;
+ l_lockMutex = false;
+ err = eepromPageOp( i_target,
+ l_switchPage,
+ l_lockMutex,
+ l_pageLocked,
+ l_desiredPage,
+ i_i2cInfo );
+ if( err )
{
- // Only retry on NACK failures: break from retry loop
- TRACFCOMP(g_trac_eeprom, ERR_MRK"eepromWrite(): I2C "
- "Write Non-NACK fail %d/%d/0x%X loop=%d, "
- "ldl=%d, offset=0x%X, aS=%d, retry=%d",
- i_i2cInfo.port, i_i2cInfo.engine,
- i_i2cInfo.devAddr, i, loop_data_length,
- i_i2cInfo.offset, i_i2cInfo.addrSize, retry);
-
- err->collectTrace(EEPROM_COMP_NAME);
-
- // break from retry loop
+ TRACFCOMP( g_trac_eeprom,
+ "Failed switching to new EEPROM page!");
break;
}
- else // Handle NACK error
- {
- TRACFCOMP(g_trac_eeprom, ERR_MRK"eepromWrite(): I2C "
- "Write NACK fail %d/%d/0x%X loop=%d, "
- "ldl=%d, offset=0x%X, aS=%d",
- i_i2cInfo.port, i_i2cInfo.engine,
- i_i2cInfo.devAddr, i, loop_data_length,
- i_i2cInfo.offset, i_i2cInfo.addrSize);
-
- // If op will be attempted again: save error and continue
- if ( retry < EEPROM_MAX_NACK_RETRIES )
- {
- // Only save original NACK error
- if ( err_NACK == NULL )
- {
- // Save original NACK error
- err_NACK = err;
-
- TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromWrite(): "
- "Error rc=0x%X, eid=0x%X plid=0x%X, "
- "tgt=0x%X, retry/MAX=%d/%d. Save error "
- "and retry",
- err_NACK->reasonCode(),
- err_NACK->eid(),
- err_NACK->plid(),
- TARGETING::get_huid(i_target),
- retry, EEPROM_MAX_NACK_RETRIES);
-
- err_NACK->collectTrace(EEPROM_COMP_NAME);
- }
- else
- {
- // Add data to original NACK error
- TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromWrite(): "
- "Another NACK Error rc=0x%X, eid=0x%X "
- "plid=0x%X, tgt=0x%X, retry/MAX=%d/%d. "
- "Delete error and retry",
- err->reasonCode(), err->eid(),
- err->plid(),
- TARGETING::get_huid(i_target),
- retry, EEPROM_MAX_NACK_RETRIES);
-
- ERRORLOG::ErrlUserDetailsString(
- "Another NACK ERROR found")
- .addToLog(err_NACK);
-
- // Delete this new NACK error
- delete err;
- err = NULL;
- }
-
- // continue to retry
- continue;
- }
- else // no more retries: trace and break
- {
- TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromWrite(): "
- "Error rc=0x%X, tgt=0x%X. No More Retries "
- "(retry/MAX=%d/%d). Returning Error",
- err->reasonCode(),
- TARGETING::get_huid(i_target),
- retry, EEPROM_MAX_NACK_RETRIES);
-
- err->collectTrace(EEPROM_COMP_NAME);
-
- // break from retry loop
- break;
- }
- }
+ l_originalPage = l_desiredPage;
+ }
- } // end of retry loop
- /***********************************************************/
- // Handle saved NACK errors, if any
- if (err_NACK)
- {
- if (err)
- {
- // commit original NACK error with new err PLID
- err_NACK->plid(err->plid());
- TRACFCOMP(g_trac_eeprom, "eepromWrite(): Committing saved "
- "NACK err eid=0x%X with plid of returned err: "
- "0x%X",
- err_NACK->eid(), err_NACK->plid());
- ERRORLOG::ErrlUserDetailsTarget(i_target)
- .addToLog(err_NACK);
+ TRACUCOMP(g_trac_eeprom,"eepromWrite() Loop: %d/%d/0x%X "
+ "loop=%d, writeBuflen=%d, offset=0x%X, bAS=%d, diffs=%d/%d",
+ i_i2cInfo.port, i_i2cInfo.engine, i_i2cInfo.devAddr,
+ i, l_writeBuflen, i_i2cInfo.offset, byteAddrSize,
+ data_left, diff_wps);
- errlCommit(err_NACK, EEPROM_COMP_ID);
- }
- else
- {
- // Since we eventually succeeded, delete original NACK error
- TRACFCOMP(g_trac_eeprom, "eepromWrite(): Op successful, "
- "deleting saved NACK err eid=0x%X, plid=0x%X",
- err_NACK->eid(), err_NACK->plid());
- delete err_NACK;
- err_NACK = NULL;
- }
- }
+ // Perform the requested write operation
+ err = eepromWriteData( i_target,
+ newBuffer,
+ l_writeBuflen,
+ &byteAddr,
+ byteAddrSize,
+ i_i2cInfo );
if ( err )
{
// Can't assume that anything was written if
// there was an error, so no update to total_bytes_written
// for this loop
+ TRACFCOMP(g_trac_eeprom,
+ "Failed writing data: original eeprom write");
break;
}
// Wait for EEPROM to write data to its internal memory
// i_i2cInfo.writeCycleTime value in milliseconds
- nanosleep(0, i_i2cInfo.writeCycleTime * NS_PER_MSEC);
+ nanosleep( 0, i_i2cInfo.writeCycleTime * NS_PER_MSEC );
// Update how much data was written
- total_bytes_written += loop_data_length;
+ total_bytes_written += l_writeBuflen;
// Update offset
- i_i2cInfo.offset += loop_data_length;
+ i_i2cInfo.offset += l_writeBuflen;
TRACUCOMP(g_trac_eeprom,"eepromWrite() Loop %d End: "
- "l_d_l=%d, offset=0x%X, t_b_w=%d, io_buflen=%d",
- i, loop_data_length, i_i2cInfo.offset,
+ "writeBuflen=%d, offset=0x%X, t_b_w=%d, io_buflen=%d",
+ i, l_writeBuflen, i_i2cInfo.offset,
total_bytes_written, io_buflen);
} // end of write for-loop
@@ -890,14 +1087,10 @@ errlHndl_t eepromWrite ( TARGETING::Target * i_target,
mutex_unlock( &g_eepromMutex );
unlock = false;
+
// Set how much data was actually written
io_buflen = total_bytes_written;
- if( err )
- {
- // Leave do-while loop
- break;
- }
TRACSCOMP( g_trac_eepromr,
"EEPROM WRITE END : Chip: %02d : Offset %.2X : Len %d",
@@ -916,6 +1109,30 @@ errlHndl_t eepromWrite ( TARGETING::Target * i_target,
mutex_unlock( & g_eepromMutex );
}
+
+ // Whether we failed in the main routine or not, unlock the page iff it is already
+ // locked
+ if( l_pageLocked )
+ {
+ errlHndl_t l_pageOpErrl = NULL;
+
+ bool l_switchPage = false;
+ bool l_lockMutex = false;
+ l_pageOpErrl = eepromPageOp( i_target,
+ l_switchPage,
+ l_lockMutex,
+ l_pageLocked,
+ l_desiredPage,
+ i_i2cInfo );
+ if( l_pageOpErrl )
+ {
+ TRACFCOMP(g_trac_eeprom,
+ "eepromWrite()::Failed unlocking page");
+ errlCommit(l_pageOpErrl, I2C_COMP_ID);
+ }
+
+ }
+
TRACDCOMP( g_trac_eeprom,
EXIT_MRK"eepromWrite()" );
@@ -924,15 +1141,185 @@ errlHndl_t eepromWrite ( TARGETING::Target * i_target,
} // end eepromWrite
+
+// ------------------------------------------------------------------
+// eepromWriteData
+// ------------------------------------------------------------------
+errlHndl_t eepromWriteData( TARGETING::Target * i_target,
+ void * i_dataToWrite,
+ size_t i_dataLen,
+ void * i_byteAddress,
+ size_t i_byteAddressSize,
+ eeprom_addr_t i_i2cInfo )
+{
+ TRACDCOMP( g_trac_eeprom,
+ ENTER_MRK"eepromWriteData()");
+ errlHndl_t err = NULL;
+ errlHndl_t err_NACK = NULL;
+ do
+ {
+ /***********************************************************/
+ /* Attempt write multiple times ONLY on NACK fails */
+ /***********************************************************/
+ for (uint8_t retry = 0;
+ retry <= EEPROM_MAX_NACK_RETRIES;
+ retry++)
+ {
+ // Do the actual data write
+ err = deviceOp( DeviceFW::WRITE,
+ i_target,
+ i_dataToWrite,
+ i_dataLen,
+ DEVICE_I2C_ADDRESS_OFFSET(
+ i_i2cInfo.port,
+ i_i2cInfo.engine,
+ i_i2cInfo.devAddr,
+ i_byteAddressSize,
+ reinterpret_cast<uint8_t*>(
+ i_byteAddress)));
+
+
+ if ( err == NULL )
+ {
+ // Operation completed successfully
+ // break from retry loop
+ break;
+ }
+ else if ( err->reasonCode() != I2C::I2C_NACK_ONLY_FOUND )
+ {
+ // Only retry on NACK failures: break from retry loop
+ TRACFCOMP(g_trac_eeprom, ERR_MRK"eepromWriteData(): I2C "
+ "Write Non-NACK fail %d/%d/0x%X, "
+ "ldl=%d, offset=0x%X, aS=%d, retry=%d",
+ i_i2cInfo.port, i_i2cInfo.engine,
+ i_i2cInfo.devAddr, i_dataLen,
+ i_i2cInfo.offset, i_i2cInfo.addrSize, retry);
+
+ err->collectTrace(EEPROM_COMP_NAME);
+
+ // break from retry loop
+ break;
+ }
+ else // Handle NACK error
+ {
+ TRACFCOMP(g_trac_eeprom, ERR_MRK"eepromWriteData(): I2C "
+ "Write NACK fail %d/%d/0x%X, "
+ "ldl=%d, offset=0x%X, aS=%d, writePageSize = %x",
+ i_i2cInfo.port, i_i2cInfo.engine,
+ i_i2cInfo.devAddr, i_dataLen,
+ i_i2cInfo.offset, i_i2cInfo.addrSize,
+ i_i2cInfo.writePageSize);
+
+ // If op will be attempted again: save error and continue
+ if ( retry < EEPROM_MAX_NACK_RETRIES )
+ {
+ // Only save original NACK error
+ if ( err_NACK == NULL )
+ {
+ // Save original NACK error
+ err_NACK = err;
+
+ TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromWriteData(): "
+ "Error rc=0x%X, eid=0x%X plid=0x%X, "
+ "tgt=0x%X, retry/MAX=%d/%d. Save error "
+ "and retry",
+ err_NACK->reasonCode(),
+ err_NACK->eid(),
+ err_NACK->plid(),
+ TARGETING::get_huid(i_target),
+ retry, EEPROM_MAX_NACK_RETRIES);
+
+ err_NACK->collectTrace(EEPROM_COMP_NAME);
+ }
+ else
+ {
+ // Add data to original NACK error
+ TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromWriteData(): "
+ "Another NACK Error rc=0x%X, eid=0x%X "
+ "plid=0x%X, tgt=0x%X, retry/MAX=%d/%d. "
+ "Delete error and retry",
+ err->reasonCode(), err->eid(),
+ err->plid(),
+ TARGETING::get_huid(i_target),
+ retry, EEPROM_MAX_NACK_RETRIES);
+
+ ERRORLOG::ErrlUserDetailsString(
+ "Another NACK ERROR found")
+ .addToLog(err_NACK);
+
+ // Delete this new NACK error
+ delete err;
+ err = NULL;
+ }
+
+ // continue to retry
+ continue;
+ }
+ else // no more retries: trace and break
+ {
+ TRACFCOMP( g_trac_eeprom, ERR_MRK"eepromWriteData(): "
+ "Error rc=0x%X, tgt=0x%X. No More Retries "
+ "(retry/MAX=%d/%d). Returning Error",
+ err->reasonCode(),
+ TARGETING::get_huid(i_target),
+ retry, EEPROM_MAX_NACK_RETRIES);
+
+ err->collectTrace(EEPROM_COMP_NAME);
+
+ // break from retry loop
+ break;
+ }
+ }
+
+ } // end of retry loop
+ /***********************************************************/
+
+ // Handle saved NACK errors, if any
+ if (err_NACK)
+ {
+ if (err)
+ {
+ // commit original NACK error with new err PLID
+ err_NACK->plid(err->plid());
+ TRACFCOMP(g_trac_eeprom, "eepromWriteData(): Committing saved "
+ "NACK err eid=0x%X with plid of returned err: "
+ "0x%X",
+ err_NACK->eid(), err_NACK->plid());
+
+ ERRORLOG::ErrlUserDetailsTarget(i_target)
+ .addToLog(err_NACK);
+
+ errlCommit(err_NACK, EEPROM_COMP_ID);
+ }
+ else
+ {
+ // Since we eventually succeeded, delete original NACK error
+ TRACFCOMP(g_trac_eeprom, "eepromWriteData(): Op successful, "
+ "deleting saved NACK err eid=0x%X, plid=0x%X",
+ err_NACK->eid(), err_NACK->plid());
+
+ delete err_NACK;
+ err_NACK = NULL;
+ }
+ }
+ }while( 0 );
+ TRACDCOMP( g_trac_eeprom,
+ EXIT_MRK"eepromWriteData()");
+ return err;
+}
+
+
+
// ------------------------------------------------------------------
// eepromPrepareAddress
// ------------------------------------------------------------------
-errlHndl_t eepromPrepareAddress ( void * io_buffer,
+errlHndl_t eepromPrepareAddress ( TARGETING::Target * i_target,
+ void * io_buffer,
size_t & o_bufSize,
+ uint8_t & o_desiredPage,
eeprom_addr_t i_i2cInfo )
{
errlHndl_t err = NULL;
-
o_bufSize = 0;
TRACDCOMP( g_trac_eeprom,
@@ -954,6 +1341,21 @@ errlHndl_t eepromPrepareAddress ( void * io_buffer,
*((uint8_t*)io_buffer+1) = (i_i2cInfo.offset & 0x00FFull);
break;
+ case ONE_BYTE_ADDR_PAGESELECT:
+ // If the offset is less than 256 bytes, report page zero, else page 1
+ if( i_i2cInfo.offset >= EEPROM_PAGE_SIZE )
+ {
+ o_desiredPage = 1;
+ }
+ else
+ {
+ o_desiredPage = 0;
+ }
+ o_bufSize = 1;
+ memset( io_buffer, 0x0, o_bufSize );
+ *((uint8_t*)io_buffer) = (i_i2cInfo.offset & 0xFFull);
+ break;
+
case ONE_BYTE_ADDR:
o_bufSize = 1;
memset( io_buffer, 0x0, o_bufSize );
@@ -1058,7 +1460,6 @@ errlHndl_t eepromReadAttributes ( TARGETING::Target * i_target,
break;
case SBE_BACKUP:
-
if( (!i_target->
tryGetAttr<TARGETING::ATTR_EEPROM_SBE_BACKUP_INFO>
( reinterpret_cast<
@@ -1132,22 +1533,26 @@ errlHndl_t eepromReadAttributes ( TARGETING::Target * i_target,
}
// Successful reading of Attribute, so extract the data
- o_i2cInfo.port = eepromData.port;
- o_i2cInfo.devAddr = eepromData.devAddr;
- o_i2cInfo.engine = eepromData.engine;
- o_i2cInfo.i2cMasterPath = eepromData.i2cMasterPath;
- o_i2cInfo.writePageSize = eepromData.writePageSize;
- o_i2cInfo.devSize_KB = eepromData.maxMemorySizeKB;
+ o_i2cInfo.port = eepromData.port;
+ o_i2cInfo.devAddr = eepromData.devAddr;
+ o_i2cInfo.engine = eepromData.engine;
+ o_i2cInfo.i2cMasterPath = eepromData.i2cMasterPath;
+ o_i2cInfo.writePageSize = eepromData.writePageSize;
+ o_i2cInfo.devSize_KB = eepromData.maxMemorySizeKB;
o_i2cInfo.writeCycleTime = eepromData.writeCycleTime;
// Convert attribute info to eeprom_addr_size_t enum
- if ( eepromData.byteAddrOffset == 0x2 )
+ if ( eepromData.byteAddrOffset == 0x3 )
+ {
+ o_i2cInfo.addrSize = ONE_BYTE_ADDR;
+ }
+ else if ( eepromData.byteAddrOffset == 0x2 )
{
o_i2cInfo.addrSize = TWO_BYTE_ADDR;
}
else if ( eepromData.byteAddrOffset == 0x1 )
{
- o_i2cInfo.addrSize = ONE_BYTE_ADDR;
+ o_i2cInfo.addrSize = ONE_BYTE_ADDR_PAGESELECT;
}
else if ( eepromData.byteAddrOffset == 0x0 )
{
diff --git a/src/usr/i2c/eepromdd.H b/src/usr/i2c/eepromdd.H
index 234be5eb1..19eb0a7e3 100755
--- a/src/usr/i2c/eepromdd.H
+++ b/src/usr/i2c/eepromdd.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2011,2015 */
+/* Contributors Listed Below - COPYRIGHT 2011,2016 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -46,9 +46,10 @@ namespace EEPROM
*/
typedef enum
{
- ZERO_BYTE_ADDR = 0,
- ONE_BYTE_ADDR = 1,
- TWO_BYTE_ADDR = 2,
+ ZERO_BYTE_ADDR = 0,
+ ONE_BYTE_ADDR_PAGESELECT = 1, // page select
+ TWO_BYTE_ADDR = 2,
+ ONE_BYTE_ADDR = 3,
LAST_DEVICE_TYPE
} eeprom_addr_size_t;
@@ -70,6 +71,14 @@ typedef struct
uint64_t writeCycleTime; // in milliseconds
} eeprom_addr_t;
+/*
+ * @brief Miscellaneous enums for EEPROM
+ */
+enum
+{
+ EEPROM_PAGE_SIZE = 0x100
+};
+
/**
*
* @brief Perform an EEPROM access operation.
@@ -109,6 +118,60 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType,
va_list i_args );
/**
+ *
+ * @brief Perform operations to lock and unlock the page mutex
+ *
+ * @param[in] i_target - the target to lock the page for
+ * @param[in] i_switchPage - bool to tell the function whether we are
+ * trying to switch pages/locking vs unlocking the page
+ * @param[in] i_lockMutex - bool to inform call chain whether we want
+ * to actually lock the page mutex or not. When false,
+ * the function assumes the appropriate mutex has already
+ * been locked and skips the call to mutex_lock
+ * @param[in/out] i_pageLocked - bool to tell the caller if the page
+ * was successfully locked.
+ * @param[in] i_desiredPage - The page we want to switch to
+ * @param[in] i_args - This is an argument list for the device driver
+ * framework. This argument list consists of the chip number of
+ * the EEPROM to access from the given I2C Master target and the
+ * internal offset to use on the slave I2C device.
+ *
+ * @return errlHndl_t - NULL if successful, otherwise a pointer to the
+ * error log.
+ */
+errlHndl_t eepromPageOp( TARGETING::Target * i_target,
+ bool i_switchPage,
+ bool i_lockMutex,
+ bool & io_pageLocked,
+ uint8_t i_desiredPage,
+ eeprom_addr_t i_i2cInfo );
+
+
+/*
+ * @brief This utility function determines if a read/write straddles
+ * the boundary between EEPROM pages and if so, returns the
+ * parameters to perform 2 operations, one for each page.
+ *
+ * @param[in] i_originalOffset - The requested read/write offset into
+ * the EEPROM.
+ * @param[in] i_originalLen - The requested length of the data.
+ * @param[in/out] io_newLen - The length of the data to retrieve
+ * @param[out] o_pageTwoBuflen - The length of data requested that
+ * crossed over into the second EEPROM page.
+ * @param[in] i_i2cInfo - Structure of I2C parameters needed to determine
+ * if we run the body of this function.
+
+ * @return bool - True if the requested data straddles the EEPROM page
+ * boundary, False otherwise. If False, io_newLen == i_originalLen.
+ */
+bool crossesEepromPageBoundary( uint64_t i_originalOffset,
+ size_t i_originalLen,
+ size_t & io_newLen,
+ size_t & o_pageTwoBuflen,
+ eeprom_addr_t i_i2cInfo );
+
+
+/**
* @brief This function peforms the sequencing to do a read of the
* EEPROM that is identified.
*
@@ -130,6 +193,37 @@ errlHndl_t eepromRead ( TARGETING::Target * i_target,
size_t i_buflen,
eeprom_addr_t i_i2cInfo );
+
+/**
+ * @brief This function actually performs the i2c operations to read data from
+ * the EEPROM
+ *
+ * @param[in] i_target - Target device
+ *
+ * @param[out] o_buffer - the buffer that will return the data read from
+ * the eeprom device
+ *
+ * @param[in] i_buflen - Number of bytes read from the EEPROM device
+ *
+ * @param[in] i_byteAddress - the offset into the EEPROM device
+ *
+ * @param[in] i_byteAddressSize - the size of the byte address (1 or 2 bytes)
+ *
+ * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute the
+ * command to the I2C device driver
+ *
+ * @return errlHndl_t - NULL if successful, otherwise a pointer to the
+ * error log.
+ */
+errlHndl_t eepromReadData( TARGETING::Target * i_target,
+ void * o_buffer,
+ size_t i_buflen,
+ void * i_byteAddress,
+ size_t i_byteAddressSize,
+ eeprom_addr_t i_i2cInfo );
+
+
+
/**
* @brief This function peforms the sequencing to do a write of the
* EEPROM that is identified.
@@ -155,25 +249,52 @@ errlHndl_t eepromWrite ( TARGETING::Target * i_target,
size_t & io_buflen,
eeprom_addr_t i_i2cInfo );
+
+
+/**
+ * @brief This function actually writes data into the devices EEPROM
+ *
+ * @param[in] i_target - Target device.
+ * @param[in] i_dataToWrite - The data to be written into the device.
+ * @param[in] i_dataLen - The length of the data to be written.
+ * @param[in] i_byteAddress - the offset into the devices EEPROM.
+ * @param[in] i_byteAddressSize - the size of byte address varable.
+ * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute
+ * the command to the I2C device driver.
+ */
+errlHndl_t eepromWriteData( TARGETING::Target * i_target,
+ void * i_dataToWrite,
+ size_t i_dataLen,
+ void * i_byteAddress,
+ size_t i_byteAddressSize,
+ eeprom_addr_t i_i2cInfo );
+
+
/**
* @brief This function prepares the I2C byte address for adding to the
* existing buffer (for Writes), or as a separate write operation
* (for Reads).
*
+ * @param[in] i_target - the target to prepare the addressing for.
+ *
* @param[in/out] io_buffer - The buffer to be written as a byte address to
* the EEPROM device. Must be pre-allocated to MAX_BYTE_ADDR size.
*
* @param[out] o_bufSize - The size of the buffer to be written.
*
+ * @param[out] o_desiredPage - The page we want to switch to.
+ *
* @param[in] i_i2cInfo - Structure of I2C parameters needed to execute
* the command to the I2C device driver.
*
* @return errlHndl_t - NULL if successful, otherwise a pointer to the
* error log.
*/
-errlHndl_t eepromPrepareAddress ( void * io_buffer,
+errlHndl_t eepromPrepareAddress ( TARGETING::Target * i_target,
+ void * io_buffer,
size_t & o_bufSize,
- eeprom_addr_t i_i2cInfo );
+ uint8_t & o_desiredPage,
+ eeprom_addr_t i_i2cInfo);
/**
* @brief this function will read all of the associated attributes needed
diff --git a/src/usr/i2c/i2c.C b/src/usr/i2c/i2c.C
index 62a8f15d0..5b9415eb5 100755
--- a/src/usr/i2c/i2c.C
+++ b/src/usr/i2c/i2c.C
@@ -73,9 +73,11 @@ TRAC_INIT( & g_trac_i2cr, "I2CR", KILOBYTE );
// Defines
// ----------------------------------------------
#define I2C_RESET_DELAY_NS (5 * NS_PER_MSEC) // Sleep for 5 ms after reset
-#define P8_MASTER_ENGINES 2 // Number of Engines used in P8
-#define P8_MASTER_PORTS 3 // Number of Ports used in P8
-#define CENTAUR_MASTER_ENGINES 1 // Number of Engines in a Centaur
+#define P8_MASTER_ENGINES 2 // Number of Engines used in P8
+#define P8_MASTER_PORTS 3 // Number of Ports used in P8
+#define CENTAUR_MASTER_ENGINES 1 // Number of Engines in a Centaur
+#define MAX_NACK_RETRIES 3
+#define PAGE_OPERATION 0xffffffff // Special value use to determine type of op
// Derived from ATTR_I2C_BUS_SPEED_ARRAY[engine][port] attribute
const TARGETING::ATTR_I2C_BUS_SPEED_ARRAY_type g_var = {{NULL}};
@@ -123,13 +125,6 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType,
// These are additional parms in the case an offset is passed in
// via va_list, as well
- args.offset_length = va_arg( i_args, uint64_t);
-
- if ( args.offset_length != 0 )
- {
- args.offset_buffer = reinterpret_cast<uint8_t*>
- (va_arg(i_args, uint64_t));
- }
// Set both Host and FSI switches to 0 so that they get set later by
// attribute in i2cCommonOp()
@@ -137,16 +132,90 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType,
args.switches.useFsiI2C = 0;
- // Call common function
- err = i2cCommonOp( i_opType,
- i_target,
- io_buffer,
- io_buflen,
- i_accessType,
- args );
+ // Decide if page select was requested (denoted with special device address)
+ if( args.devAddr == PAGE_OPERATION )
+ {
+ // since this was a page operation, next arg will be whether we want to
+ // lock the page, or unlock
+ bool l_lockOp = static_cast<bool>(va_arg(i_args, int));
+ if(l_lockOp)
+ {
+ //If page select requested, desired page would be passed in va_list
+ uint8_t l_desiredPage = static_cast<uint8_t>(va_arg(i_args, int ));
- TRACDCOMP( g_trac_i2c,
+ bool l_lockMutex = static_cast<bool>(va_arg(i_args, int));
+ err = i2cPageSwitchOp( i_opType,
+ i_target,
+ i_accessType,
+ l_desiredPage,
+ l_lockMutex,
+ args );
+
+
+ if( err )
+ {
+ TRACFCOMP(g_trac_i2c, "Locking the page FAILED");
+ bool l_pageUnlockSuccess = false;
+ l_pageUnlockSuccess = i2cPageUnlockOp( i_target,
+ args );
+ if( !l_pageUnlockSuccess )
+ {
+ TRACFCOMP(g_trac_i2c,
+ "An Error occurred when unlocking page after"
+ " failure to lock the page!");
+ }
+ }
+ }
+ else
+ {
+ bool l_pageUnlockSuccess;
+ l_pageUnlockSuccess = i2cPageUnlockOp( i_target,
+ args );
+ if( !l_pageUnlockSuccess )
+ {
+ TRACFCOMP(g_trac_i2c,"i2cPerformOp::i2cPageUnlockOp - Failure unlocking the page");
+ /*@
+ * @errortype
+ * @reasoncode I2C_FAILURE_UNLOCKING_EEPROM_PAGE
+ * @severity ERRORLOG_SEV_UNRECOVERABLE
+ * @moduleid I2C_PERFORM_OP
+ * @userdata1 Target Huid
+ * @userdata2 <UNUSED>
+ * @devdesc I2C master encountered an error while
+ * trying to unlock the eepromPage
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_PERFORM_OP,
+ I2C_FAILURE_UNLOCKING_EEPROM_PAGE,
+ TARGETING::get_huid(i_target),
+ 0x0,
+ true /*Add HB SW Callout*/ );
+ // TODO RTC 148705: Callout downstream DIMMs
+ err->collectTrace(I2C_COMP_NAME, 256 );
+ }
+ }
+
+ }
+ else
+ {
+ args.offset_length = va_arg( i_args, uint64_t);
+ if ( args.offset_length != 0 )
+ {
+ args.offset_buffer = reinterpret_cast<uint8_t*>
+ (va_arg(i_args, uint64_t));
+ }
+ // Else, call the normal common function
+ err = i2cCommonOp( i_opType,
+ i_target,
+ io_buffer,
+ io_buflen,
+ i_accessType,
+ args );
+ }
+
+
+ TRACUCOMP( g_trac_i2c,
EXIT_MRK"i2cPerformOp() - %s",
((NULL == err) ? "No Error" : "With Error") );
@@ -283,6 +352,365 @@ errlHndl_t fsi_i2cPerformOp( DeviceFW::OperationType i_opType,
return err;
} // end fsi_i2cPerformOp
+
+// ------------------------------------------------------------------
+// i2cHandleError
+// ------------------------------------------------------------------
+void i2cHandleError( TARGETING::Target * i_target,
+ errlHndl_t & i_err,
+ misc_args_t & i_args )
+{
+ errlHndl_t err_reset = NULL;
+ TRACUCOMP(g_trac_i2c, ENTER_MRK"i2cHandlError()");
+ if( i_err )
+ {
+ // if it was a bus arbitration lost error set the
+ // the reset level so a force unlock reset can be performed
+ i2c_reset_level l_reset_level = BASIC_RESET;
+
+ if ( i_err->reasonCode() == I2C_ARBITRATION_LOST_ONLY_FOUND )
+ {
+
+ l_reset_level = FORCE_UNLOCK_RESET;
+ }
+
+ // Reset the I2C Master
+ err_reset = i2cReset( i_target,
+ i_args,
+ l_reset_level);
+
+ if( err_reset )
+ {
+ // 2 error logs, so commit the reset log here
+ TRACFCOMP( g_trac_i2c, ERR_MRK"i2cCommonOp() - "
+ "Previous error (rc=0x%X, eid=0x%X) before "
+ "i2cReset() failed. Committing reset error "
+ "(rc=0x%X, eid=0x%X) and returning original error",
+ i_err->reasonCode(), i_err->eid(),
+ err_reset->reasonCode(), err_reset->eid() );
+
+ errlCommit( err_reset, I2C_COMP_ID );
+
+ }
+
+ // Sleep to allow devices to recover from reset
+ nanosleep( 0, I2C_RESET_DELAY_NS );
+
+ }
+ TRACUCOMP(g_trac_i2c, EXIT_MRK"i2cHandlError()");
+}
+
+// ------------------------------------------------------------------
+// i2cPageSwitchOp
+// ------------------------------------------------------------------
+errlHndl_t i2cPageSwitchOp( DeviceFW::OperationType i_opType,
+ TARGETING::Target * i_target,
+ int64_t i_accessType,
+ uint8_t i_desiredPage,
+ bool i_lockMutex,
+ misc_args_t & i_args )
+{
+ TRACUCOMP(g_trac_i2c, ENTER_MRK"i2cPageSwitchOp");
+
+ errlHndl_t l_err = NULL;
+ errlHndl_t l_err_NACK = NULL;
+ bool l_mutexSuccess = false;
+ bool l_pageSwitchNeeded = false;
+ bool l_mutex_needs_unlock = false;
+
+ bool l_error = false;
+ mutex_t * l_pageLock = NULL;
+
+ do
+ {
+
+ // Check for Master Sentinel chip
+ if( TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL == i_target )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cPageSwitchOp() - Cannot target Master Sentinel "
+ "Chip for an I2C Operation!" );
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_MASTER_SENTINEL_TARGET
+ * @severity ERRORLOG_SEV_UNRECOVERABLE
+ * @moduleid I2C_PAGE_LOCK_OP
+ * @userdata1 Operation Type requested
+ * @userdata2 <UNUSED>
+ * @devdesc Master Sentinel chip was used as a target for an
+ * I2C operation. This is not permitted.
+ */
+ l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_PERFORM_OP,
+ I2C_MASTER_SENTINEL_TARGET,
+ i_opType,
+ 0x0,
+ true /*Add HB SW Callout*/ );
+
+ l_err->collectTrace( I2C_COMP_NAME, 256);
+
+ break;
+ }
+
+ //Set Host vs Fsi switches if not done already
+ i2cSetSwitches(i_target, i_args);
+
+
+
+ if(i_lockMutex)
+ {
+ //get page mutex
+ l_mutexSuccess = i2cGetPageMutex(i_target,
+ i_args,
+ l_pageLock );
+ if(!l_mutexSuccess)
+ {
+ TRACUCOMP(g_trac_i2c,
+ ERR_MRK"Error in i2cPageSwitchOp::i2cGetPageMutex()");
+ /*@
+ * @errortype
+ * @reasoncode I2C_INVALID_EEPROM_PAGE_MUTEX
+ * @severity ERRORLOG_SEV_UNRECOVERABLE
+ * @moduleid I2C_PAGE_LOCK_OP
+ * @userdata1 Target Huid
+ * @userdata2 <UNUSED>
+ * @devdesc There was an error retrieving the EEPROM page
+ * mutex for this i2c master engine
+ */
+ l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_PERFORM_OP,
+ I2C_INVALID_EEPROM_PAGE_MUTEX,
+ TARGETING::get_huid(i_target),
+ 0x0,
+ true /*Add HB SW Callout*/ );
+
+ l_err->collectTrace( I2C_COMP_NAME, 256 );
+ break;
+ }
+
+ (void)mutex_lock( l_pageLock );
+ }
+ // Calculate variables related to I2C Bus speed in 'args' struct
+ l_err = i2cSetBusVariables( i_target, I2C_BUS_SPEED_FROM_MRW, i_args);
+
+ if( l_err )
+ {
+ TRACFCOMP(g_trac_i2c,
+ "Error in i2cPageSwitchOp::i2cSetBusVariables()");
+
+ // Error means we need to unlock the page mutex prematurely
+ l_mutex_needs_unlock = true;
+ // Skip performing actual I2C Operation
+ break;
+ }
+
+
+ // Set device address to switch to appropriate page
+ if( i_desiredPage == PAGE_ONE )
+ {
+ i_args.devAddr = PAGE_ONE_ADDR;
+ }
+ else if( i_desiredPage == PAGE_ZERO )
+ {
+ i_args.devAddr = PAGE_ZERO_ADDR;
+ }
+ l_pageSwitchNeeded = true;
+
+ //TODO: RTC 147385
+ //optimize to remember current page of i2c master device
+
+ if( l_pageSwitchNeeded )
+ {
+ // Perform the actual write operation to switch pages.
+ i_args.read_not_write = false;
+ i_args.with_stop = true;
+ i_args.skip_mode_setup = false;
+
+
+ //going to write 2 bytes of zeros to special device address
+ size_t l_zeroBuflen = 2;
+ uint8_t * l_zeroBuffer = static_cast<uint8_t*>(malloc(l_zeroBuflen));
+ memset(l_zeroBuffer, 0, l_zeroBuflen);
+
+
+ TRACUCOMP(g_trac_i2c,"i2cPageSwitchOp args! \n"
+ "misc_args_t: port:%d / engine: %d: devAddr: %x: skip_mode_step(%d):\n"
+ "with_stop(%d): read_not_write(%d): bus_speed: %d: bit_rate_divisor: %d:\n"
+ "polling_interval_ns: %d: timeout_count: %d: offset_length: %d",
+ i_args.port, i_args.engine, i_args.devAddr, i_args.skip_mode_setup,
+ i_args.with_stop, i_args.read_not_write, i_args.bus_speed,
+ i_args.bit_rate_divisor, i_args.polling_interval_ns, i_args.timeout_count,
+ i_args.offset_length );
+
+
+ // Retry MAX_NACK_RETRIES so we can bypass nacks caused by a busy bus.
+ // Other Nack errors are expected and caused by the empty write
+ // associated with the page switch operation.
+ for(uint8_t retry = 0;
+ retry <= MAX_NACK_RETRIES;
+ retry++)
+ {
+ l_err = i2cWrite(i_target,
+ l_zeroBuffer,
+ l_zeroBuflen,
+ i_args);
+
+ if(l_err == NULL)
+ {
+ // Operation completed successfully
+ // set attribute, free memory and break from retry loop
+ // TODO Set EEPROM_PAGE attribute to save page for
+ // optimization
+ TRACUCOMP(g_trac_i2c,"Set EEPROM_PAGE to %d", i_desiredPage);
+ // i_target->setAttr<TARGETING::ATTR_EEPROM_PAGE>(l_newPage);
+ free(l_zeroBuffer);
+ break;
+ }
+ else if( l_err->reasonCode() != I2C_NACK_ONLY_FOUND)
+ {
+ // Only retry on NACK failures. Break form retry loop
+ TRACFCOMP(g_trac_i2c,
+ ERR_MRK"i2cPageSwitchOp(): I2C Write "
+ "Non-NACK fail %x", i_args.devAddr );
+ l_err->collectTrace(I2C_COMP_NAME);
+ l_mutex_needs_unlock = true;
+ l_error = true;
+ break;
+ }
+ else // Handle NACK error
+ {
+ TRACFCOMP(g_trac_i2c,
+ "i2cPageSwitchOp::Expected Nack error. Retrying in case "
+ "this nack was caused by bus being busy "
+ "loop = %d", retry);
+
+ nanosleep( 0, i_args.polling_interval_ns );
+
+ // Retry on NACKs just in case the cause was a busy i2c bus.
+ if( retry < MAX_NACK_RETRIES )
+ {
+ if(l_err_NACK == NULL)
+ {
+ l_err_NACK = l_err;
+ // TRACFCOMP(g_trac_i2c,
+ // "Saving first Nack error and retry");
+ nanosleep(0, i_args.polling_interval_ns);
+ l_err_NACK->collectTrace(I2C_COMP_NAME);
+
+ }
+ else
+ {
+ // Delete this new NACK error
+ delete l_err;
+ nanosleep(0 ,i_args.polling_interval_ns);
+ l_err = NULL;
+ }
+ // continue to retry
+ continue;
+ }
+ else // no more retries: trace and break;
+ {
+ TRACFCOMP(g_trac_i2c,
+ "Exiting Nack retry loop");
+ break;
+ }
+ }
+
+ } // end of retry loop
+
+ if(l_err_NACK)
+ {
+ if( l_err )
+ {
+ i2cHandleError( i_target,
+ l_err,
+ i_args );
+ delete l_err;
+ l_err = NULL;
+
+ }
+ delete l_err_NACK;
+ l_err_NACK = NULL;
+ }
+ }
+
+ if( l_error )
+ {
+ //call i2cHandleError
+ i2cHandleError( i_target,
+ l_err,
+ i_args );
+ break;
+ }
+ }while( 0 );
+ if( l_mutex_needs_unlock )
+ {
+ TRACFCOMP(g_trac_i2c,
+ "Prematurely unlocking page mutex");
+ (void)mutex_unlock(l_pageLock);
+ }
+
+ TRACUCOMP(g_trac_i2c, EXIT_MRK"i2cPageSwitchOp()");
+
+ return l_err;
+}
+
+
+
+// ------------------------------------------------------------------
+// i2cPageUnlockOp
+// ------------------------------------------------------------------
+bool i2cPageUnlockOp( TARGETING::Target * i_target,
+ misc_args_t & i_args )
+{
+ TRACUCOMP(g_trac_i2c, ENTER_MRK"i2cPageUnlockOp()");
+ bool l_mutexSuccess = false;
+ mutex_t * l_pageLock = NULL;
+ errlHndl_t l_err = NULL;
+
+ do
+ {
+ // Get the mutex for this target
+ l_mutexSuccess = i2cGetPageMutex( i_target,
+ i_args,
+ l_pageLock );
+
+ if( !l_mutexSuccess )
+ {
+ TRACUCOMP( g_trac_i2c,
+ ERR_MRK"Error in i2cPageUnlockOp::i2cGetPageMutex()");
+ /*@
+ * @errortype
+ * @reasoncode I2C_INVALID_EEPROM_PAGE_MUTEX
+ * @severity ERRORLOG_SEV_UNRECOVERABLE
+ * @moduleid I2C_PAGE_UNLOCK_OP
+ * @userdata1 Target Huid
+ * @userdata2 <UNUSED>
+ * @devdesc There was an error retrieving the EEPROM page
+ * mutex for this i2c master engine
+ */
+ l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_PERFORM_OP,
+ I2C_INVALID_EEPROM_PAGE_MUTEX,
+ TARGETING::get_huid(i_target),
+ 0x0,
+ true /*Add HB SW Callout*/ );
+
+ l_err->collectTrace( I2C_COMP_NAME, 256 );
+ errlCommit(l_err, I2C_COMP_ID);
+ break;
+ }
+ //Unlock the page mutex
+ (void)mutex_unlock(l_pageLock);
+ }while( 0 );
+
+ TRACUCOMP(g_trac_i2c, EXIT_MRK"i2cPageUnlockOp()");
+ return l_mutexSuccess;
+}
+
+
+
// ------------------------------------------------------------------
// i2cSetSwitches
// ------------------------------------------------------------------
@@ -316,22 +744,14 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType,
misc_args_t & i_args )
{
errlHndl_t err = NULL;
- errlHndl_t err_reset = NULL;
bool mutex_success = false;
mutex_t * engineLock = NULL;
bool mutex_needs_unlock = false;
- TRACDCOMP( g_trac_i2c,
- ENTER_MRK"i2cCommonOp(): i_opType=%d, aType=%d, "
- "p/e/devAddr= %d/%d/0x%X, len=%d, offset=%d/%p",
- (uint64_t) i_opType, i_accessType, i_args.port, i_args.engine,
- i_args.devAddr, io_buflen, i_args.offset_length,
- i_args.offset_buffer);
-
TRACUCOMP( g_trac_i2c,
ENTER_MRK"i2cCommonOp(): i_opType=%d, aType=%d, "
- "p/e/devAddr= %d/%d/0x%x, len=%d, offset=%d/%p",
+ "p/e/devAddr= %d/%d/0x%x, len=%d, offset=%x/%p",
(uint64_t) i_opType, i_accessType, i_args.port, i_args.engine,
i_args.devAddr, io_buflen, i_args.offset_length,
i_args.offset_buffer);
@@ -370,6 +790,7 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType,
//Set Host vs Fsi switches if not done already
i2cSetSwitches(i_target, i_args);
+
// Get the mutex for the requested engine
mutex_success = i2cGetEngineMutex( i_target,
i_args,
@@ -383,14 +804,8 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType,
}
// Lock on this engine
- TRACUCOMP( g_trac_i2c,
- INFO_MRK"Obtaining lock for engine: %d",
- i_args.engine );
(void)mutex_lock( engineLock );
mutex_needs_unlock = true;
- TRACUCOMP( g_trac_i2c,
- INFO_MRK"Locked on engine: %d",
- i_args.engine );
// Calculate variables related to I2C Bus Speed in 'args' struct
@@ -413,7 +828,6 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType,
if( i_opType == DeviceFW::READ &&
i_args.offset_length != 0 )
{
-
// First WRITE offset to device without a stop
i_args.read_not_write = false;
i_args.with_stop = false;
@@ -447,7 +861,6 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType,
else if( i_opType == DeviceFW::WRITE &&
i_args.offset_length != 0 )
{
-
// Add the Offset Information to the start of the data and
// then send as a single write operation
@@ -552,37 +965,9 @@ errlHndl_t i2cCommonOp( 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,
- i_args,
- l_reset_level);
-
- if( err_reset )
- {
- // 2 error logs, so commit the reset log here
- TRACFCOMP( g_trac_i2c, ERR_MRK"i2cCommonOp() - "
- "Previous error (rc=0x%X, eid=0x%X) before "
- "i2cReset() failed. Committing reset error "
- "(rc=0x%X, eid=0x%X) and returning original error",
- err->reasonCode(), err->eid(),
- err_reset->reasonCode(), err_reset->eid() );
-
- errlCommit( err_reset, I2C_COMP_ID );
- }
-
- // Sleep to allow devices to recover from reset
- nanosleep( 0, I2C_RESET_DELAY_NS );
-
+ i2cHandleError( i_target,
+ err,
+ i_args );
break;
}
@@ -1066,7 +1451,7 @@ errlHndl_t i2cWrite ( TARGETING::Target * i_target,
// Define regs we'll be using
fifo_reg_t fifo;
- TRACDCOMP( g_trac_i2c,
+ TRACUCOMP( g_trac_i2c,
ENTER_MRK"i2cWrite()" );
TRACSCOMP( g_trac_i2cr,
@@ -1113,7 +1498,7 @@ errlHndl_t i2cWrite ( TARGETING::Target * i_target,
break;
}
- TRACUCOMP( g_trac_i2cr,
+ TRACSCOMP( g_trac_i2cr,
"I2C WRITE DATA : engine %.2X : port %.2X : "
"devAddr %.2X : byte %d : %.2X (0x%lx)",
i_args.engine, i_args.port, i_args.devAddr,
@@ -1142,7 +1527,7 @@ errlHndl_t i2cWrite ( TARGETING::Target * i_target,
"I2C WRITE END : engine %.2X: port %.2X : devAddr %.2X : len %d",
i_args.engine, i_args.port, i_args.devAddr, io_buflen );
- TRACDCOMP( g_trac_i2c,
+ TRACUCOMP( g_trac_i2c,
EXIT_MRK"i2cWrite()" );
return err;
@@ -1276,7 +1661,7 @@ bool i2cGetEngineMutex( TARGETING::Target * i_target,
default:
TRACFCOMP( g_trac_i2c,
- ERR_MRK"Invalid engine for getting Mutex! "
+ ERR_MRK"i2cGetEngineMutex: Invalid engine for getting Mutex! "
"i_args.engine=%d", i_args.engine );
success = false;
assert(false, "i2c.C: Invalid engine for getting Mutex!"
@@ -1290,6 +1675,47 @@ bool i2cGetEngineMutex( TARGETING::Target * i_target,
return success;
}
+// ------------------------------------------------------------------
+// i2cGetPageMutex
+// ------------------------------------------------------------------
+bool i2cGetPageMutex( TARGETING::Target * i_target,
+ misc_args_t & i_args,
+ mutex_t *& i_pageLock )
+{
+ bool success = true;
+ do
+ {
+ switch( i_args.engine )
+ {
+ case 0:
+ i_pageLock = i_target->
+ getHbMutexAttr<TARGETING::ATTR_I2C_PAGE_MUTEX_0>();
+ break;
+
+ case 1:
+ i_pageLock = i_target->
+ getHbMutexAttr<TARGETING::ATTR_I2C_PAGE_MUTEX_1>();
+ break;
+
+ case 2:
+ i_pageLock = i_target->
+ getHbMutexAttr<TARGETING::ATTR_I2C_PAGE_MUTEX_2>();
+ break;
+
+ default:
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cGetPageMutex: Invalid engine for getting mutex!");
+ success = false;
+ assert(false, "i2c.C: Invalid engine for getting Mutex!"
+ "i_args.engine=%d", i_args.engine );
+ break;
+
+ };
+
+ }while( 0 );
+ return success;
+}
+
// ------------------------------------------------------------------
// i2cWaitForCmdComp
@@ -1326,6 +1752,7 @@ errlHndl_t i2cWaitForCmdComp ( TARGETING::Target * i_target,
if( err )
{
+ TRACFCOMP(g_trac_i2c, "Errored at i2cWaitForCmdComplete::i2cReadStatusReg");
break;
}
@@ -1720,6 +2147,9 @@ errlHndl_t i2cWaitForFifoSpace ( TARGETING::Target * i_target,
if( err )
{
+ TRACFCOMP( g_trac_i2c,
+ "Errored out at i2cReadStatusReg: statusreg = %016llx",
+ status.value);
break;
}
diff --git a/src/usr/i2c/i2c.H b/src/usr/i2c/i2c.H
index f4be4b8fb..05f11d551 100755
--- a/src/usr/i2c/i2c.H
+++ b/src/usr/i2c/i2c.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2011,2015 */
+/* Contributors Listed Below - COPYRIGHT 2011,2016 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -51,6 +51,18 @@ enum i2c_reset_level
FORCE_UNLOCK_RESET,
};
+/**
+ * @brief Miscellaneous enums for I2C
+ */
+enum
+{
+ PAGE_ZERO = 0x0,
+ PAGE_ONE = 0x1,
+ PAGE_UNKNOWN = 0x2,
+ PAGE_ZERO_ADDR = 0x6C,
+ PAGE_ONE_ADDR = 0x6E,
+};
+
/**
* @brief FIFO size (width) in bytes. This dictates how many bytes
@@ -520,8 +532,61 @@ errlHndl_t fsi_i2cPerformOp( DeviceFW::OperationType i_opType,
int64_t i_accessType,
va_list i_args );
+
+/**
+ * @brief Analyzes an error handle object and
+ * performs an i2c reset if necessary
+ *
+ * @param[in] i_target - The target
+ * @param[in] i_err - There error to analyze
+ * @param[in] i_args - miscellaneous arguments
+ *
+ * @return void
+ */
+void i2cHandleError( TARGETING::Target * i_target,
+ errlHndl_t & i_err,
+ misc_args_t & i_args );
+
+
+
+/**
+ * @brief Performs the necessary operations and comparisons
+ * needed to decide what EEPROM page we are on and locks the appropriate
+ * page control mutexes
+ * @param[in] i_opType - Operation Type - See DeviceFW::OperationType in
+ * driverif.H
+ * @param[in] i_target - The target to lock the page for.
+ * @param[in] i_accessType - Access Type - See DeviceFW::AccessType in
+ * userif.H
+ * @param[in] i_desiredPage - The EEPROM page that will be switched to.
+ * @param[in] i_lockMutex - True if we want to actually lock the mutex,
+ * False if we do not want to lock it.
+ * @param[in] i_args - miscellaneous arguments
+ *
+ * @return errlHndl_t - NULL if successful, otherwise a pointer to an error log
+ *
+ */
+errlHndl_t i2cPageSwitchOp( DeviceFW::OperationType i_opType,
+ TARGETING::Target * i_target,
+ int64_t i_accessType,
+ uint8_t i_desiredPage,
+ bool i_lockMutex,
+ misc_args_t & i_args );
+/**
+ * @brief Unlocks the page mutex for targets given page attribute
+ *
+ * @param[in] i_target - target to unlock the page for.
+ * @param[in] i_args - miscellaneous arguments
+ *
+ * @return - True on success, False on failure.
+ */
+bool i2cPageUnlockOp( TARGETING::Target * i_target,
+ misc_args_t & i_args );
+
+
+
/**
- * @brief This function sets the Host vs FSI switches if the user has not
+ * @brief Sets the Host vs FSI switches if the user has not
* already.
*
* param[in] i_target - The target device
@@ -536,7 +601,7 @@ void i2cSetSwitches( TARGETING::Target * i_target,
/**
*
* @brief Performs the actual I2C operation.
-* NOTE: This function handles the MUTEX used to avoid deadlocks.
+* NOTE: Handles the MUTEX used to avoid deadlocks.
*
* @param[in] i_opType - Operation Type - See DeviceFW::OperationType in
* driverif.H
@@ -572,8 +637,7 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType,
/**
- * @brief This function will do the real work of reading from the I2C
- * device.
+ * @brief Does the real work of reading from the I2C device.
*
* @param[in] i_target - The I2C master to source the read to the slave.
*
@@ -594,7 +658,7 @@ errlHndl_t i2cRead ( TARGETING::Target * i_target,
misc_args_t & i_args);
/**
- * @brief This function will do the real work of writinging to the I2C
+ * @brief Does the real work of writing to the I2C
* device.
*
* @param[in] i_target - The I2C master to source the write to the slave.
@@ -618,7 +682,7 @@ errlHndl_t i2cWrite ( TARGETING::Target * i_target,
misc_args_t & i_args);
/**
- * @brief This function will do the I2C setup of the Address/Command registers
+ * @brief does the I2C setup of the Address/Command registers
* before issuing the 'go' on the I2C bus.
*
* @param[in] i_target - The I2C master.
@@ -637,14 +701,15 @@ errlHndl_t i2cSetup ( TARGETING::Target * i_target,
/**
- * @brief This function gets the appropriate engine mutex for a given target.
+ * @brief Gets the appropriate engine
+ * mutex for a given target.
*
* @param[in] i_target - The target to get the mutex for.
*
* @param[in] i_args - Structure containing arguments needed to determine
* the correct engine mutex.
*
- * @param[in/out] i_engineLock - The mutex.
+ * @param[in/out] i_engineLock - The engine mutex.
*
* @return bool - True if valid mutex is found, False otherwise.
*/
@@ -652,8 +717,26 @@ bool i2cGetEngineMutex( TARGETING::Target * i_target,
misc_args_t & i_args,
mutex_t *& i_engineLock );
+
+/**
+ * @brief Gets the appropriate page mutex for the given i2c engine
+ *
+ * @param[in] i_target - The target to get the mutex for.
+ * @param[in] i_args - Structure containing arguments needed to determine
+ * the correct page mutex.
+ * @param[in/out] i_pageLock - The page mutex.
+ *
+ * @return bool - True if valid mutex is found, False otherwise.
+ */
+bool i2cGetPageMutex( TARGETING::Target * i_target,
+ misc_args_t & i_args,
+ mutex_t *& i_pageLock );
+
+
+
+
/**
- * @brief This function will wait for the command to be complete or
+ * @brief Wait for the command to be complete or
* timeout waiting before returning.
*
* @param[in] i_target - The I2C master target.
OpenPOWER on IntegriCloud