summaryrefslogtreecommitdiffstats
path: root/src/usr/i2c
diff options
context:
space:
mode:
authorTerry J. Opie <opiet@us.ibm.com>2011-08-17 14:12:26 -0500
committerTerry J. Opie <opiet@us.ibm.com>2011-10-21 08:17:43 -0500
commit21185b30cd99a00f01e15edba28402cdc00de1d1 (patch)
treeabfa4543ba35c680414f89bf8b15d8dddbd61258 /src/usr/i2c
parent591bf9abfe58c662a8e81820fdc96b90b85a9b55 (diff)
downloadtalos-hostboot-21185b30cd99a00f01e15edba28402cdc00de1d1.tar.gz
talos-hostboot-21185b30cd99a00f01e15edba28402cdc00de1d1.zip
I2C Device Driver Good Machine Path
Change-Id: I88bf5ce464cdeceb3e151bde72fb51295ede07c0 Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/428 Tested-by: Jenkins Server Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com> Reviewed-by: Terry J. Opie <opiet@us.ibm.com>
Diffstat (limited to 'src/usr/i2c')
-rwxr-xr-xsrc/usr/i2c/i2c.C859
-rwxr-xr-xsrc/usr/i2c/i2c.H290
-rwxr-xr-xsrc/usr/i2c/test/i2ctest.H152
3 files changed, 1065 insertions, 236 deletions
diff --git a/src/usr/i2c/i2c.C b/src/usr/i2c/i2c.C
index e172f9e63..8b2239b63 100755
--- a/src/usr/i2c/i2c.C
+++ b/src/usr/i2c/i2c.C
@@ -31,6 +31,8 @@
// Includes
// ----------------------------------------------
#include <string.h>
+#include <sys/time.h>
+
#include <trace/interface.H>
#include <errl/errlentry.H>
#include <errl/errlmanager.H>
@@ -40,10 +42,13 @@
#include "i2c.H"
// ----------------------------------------------
-// Trace definition
+// Trace definitions
trace_desc_t* g_trac_i2c = NULL;
TRAC_INIT( & g_trac_i2c, "I2C", 4096 );
+trace_desc_t* g_trac_i2cr = NULL;
+TRAC_INIT( & g_trac_i2cr, "I2CR", 4096 );
+
namespace I2C
{
@@ -53,14 +58,11 @@ DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD,
TARGETING::TYPE_PROC,
i2cPerformOp );
-// TODO - This will be added back in once the bug in the registration
-// code has been fixed. Right now it will not compile with 2 registrations
-// to the same function.
// Register the perform Op with the routing code for Memory Buffers.
-//DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD,
-// DeviceFW::I2C,
-// TARGETING::TYPE_MEMBUF,
-// i2cPerformOp );
+DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD,
+ DeviceFW::I2C,
+ TARGETING::TYPE_MEMBUF,
+ i2cPerformOp );
// ------------------------------------------------------------------
// i2cPerformOp
@@ -73,55 +75,99 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType,
va_list i_args )
{
errlHndl_t err = NULL;
- uint64_t addr = va_arg( i_args, uint64_t );
- TRACFCOMP( g_trac_i2c,
+ // Get the input args our of the va_list
+ // Address, Port, Engine, Device Addr.
+ input_args_t args;
+ args.addr = va_arg( i_args, uint64_t );
+ args.port = va_arg( i_args, uint64_t );
+ args.engine = va_arg( i_args, uint64_t );
+ args.devAddr = va_arg( i_args, uint64_t );
+
+ TRACDCOMP( g_trac_i2c,
ENTER_MRK"i2cPerformOp()" );
- // --------------------------------------------------------------
- // NOTE: There are attributes that can be read for targetted
- // devices (slaves). But since we don't have any of those
- // targets yet, there isn't a way to test reading those
- // attributes yet. I assume those will be added as full device
- // driver testing is started on Simics.
- // --------------------------------------------------------------
- if( i_opType == DeviceFW::READ )
- {
- err = i2cRead( i_target,
- addr,
- io_buffer,
- io_buflen );
- }
- else if( i_opType == DeviceFW::WRITE )
- {
- err = i2cWrite( i_target,
- addr,
- io_buffer,
- io_buflen );
- }
- else
+ do
{
- TRACFCOMP( g_trac_i2c,
- ERR_MRK"i2cPerformOp() - Unknown Operation Type!" );
-
- /*@
- * @errortype
- * @reasoncode I2C_INVALID_OP_TYPE
- * @severity ERRL_SEV_UNRECOVERABLE
- * @moduleid I2C_PERFORM_OP
- * @userdata1 i_opType
- * @userdata2 addr
- * @devdesc Invalid Operation type.
- */
- err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
- I2C_PERFORM_OP,
- I2C_INVALID_OP_TYPE,
- i_opType,
- addr );
-
- }
-
- TRACFCOMP( g_trac_i2c,
+ // Check for Master Sentinel chip
+ if( TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL == i_target )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cPerformOp() - Cannot target Master Sentinel Chip "
+ "for an I2C Operation!" );
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_MASTER_SENTINEL_TARGET
+ * @severity ERRORLOG_SEV_UNRECOVERABLE
+ * @moduleid I2C_PERFORM_OP
+ * @userdata1 Operation Type requested
+ * @userdata2 <UNUSED>
+ * @devdesc Master Sentinel chip was used as a target for an
+ * I2C operation. This is NOT permitted.
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_PERFORM_OP,
+ I2C_MASTER_SENTINEL_TARGET,
+ i_opType,
+ 0x0 );
+
+ break;
+ }
+
+ // TODO - Locking needs to be implemented for each engine on each
+ // possible chip. The details of this still need to be worked out.
+ // This will be implemented with the bad machine path story (3629).
+
+ if( i_opType == DeviceFW::READ )
+ {
+ err = i2cRead( i_target,
+ io_buffer,
+ io_buflen,
+ args );
+
+ if( err )
+ {
+ break;
+ }
+ }
+ else if( i_opType == DeviceFW::WRITE )
+ {
+ err = i2cWrite( i_target,
+ io_buffer,
+ io_buflen,
+ args );
+
+ if( err )
+ {
+ break;
+ }
+ }
+ else
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cPerformOp() - Unknown Operation Type!" );
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_INVALID_OP_TYPE
+ * @severity ERRL_SEV_UNRECOVERABLE
+ * @moduleid I2C_PERFORM_OP
+ * @userdata1 i_opType
+ * @userdata2 addr
+ * @devdesc Invalid Operation type.
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_PERFORM_OP,
+ I2C_INVALID_OP_TYPE,
+ i_opType,
+ args.addr );
+
+ break;
+ }
+ } while( 0 );
+
+ TRACDCOMP( g_trac_i2c,
EXIT_MRK"i2cPerformOp() - %s",
((NULL == err) ? "No Error" : "With Error") );
@@ -132,26 +178,188 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType,
// i2cRead
// ------------------------------------------------------------------
errlHndl_t i2cRead ( TARGETING::Target * i_target,
- uint64_t i_addr,
void * o_buffer,
- size_t & i_size )
+ size_t & i_buflen,
+ input_args_t i_args )
{
errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
+ uint64_t bytesRead = 0x0;
+
+ uint64_t addr = i_args.addr;
+ uint64_t engine = i_args.engine;
+ uint64_t devAddr = i_args.devAddr;
+
+ // TODO - hardcoded to 400KHz for now
+ uint64_t interval = I2C_TIMEOUT_INTERVAL( I2C_CLOCK_DIVISOR_400KHZ );
+ uint64_t timeoutCount = I2C_TIMEOUT_COUNT( interval );
- TRACFCOMP( g_trac_i2c,
+ // Define the regs we'll be using
+ cmdreg cmd;
+ statusreg status;
+ fiforeg fifo;
+
+ TRACDCOMP( g_trac_i2c,
ENTER_MRK"i2cRead()" );
+ TRACSCOMP( g_trac_i2cr,
+ "I2C READ START : engine %.2X : devAddr %.2X : addr %.4X : len %d",
+ engine, devAddr, addr, i_buflen );
+
do
{
- // TODO - Functionality still needs to be added.
+ // Do Command/Mode reg setups.
+ size_t tmpSize = 0;
err = i2cSetup( i_target,
- i_addr,
- i_size,
- true,
- true );
+ tmpSize, // First length is always 0 for reads (byte addr)
+ false, // RnW, false to do initial setup of byte addr
+ false,
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Write the 2byte address to the FIFO
+ err = i2cWriteByteAddr( i_target,
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Wait for cmd complete before continuing
+ err = i2cWaitForCmdComp( i_target,
+ engine );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Setup the Command register to start the read operation
+ cmd.value = 0x0ull;
+ cmd.with_start = 1;
+ cmd.with_stop = 1;
+ cmd.with_addr = 1;
+ cmd.device_addr = devAddr;
+ cmd.read_not_write = 1; // Now doing a read
+ cmd.length_b = i_buflen;
+
+ err = deviceWrite( i_target,
+ &cmd.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[engine].command ) );
+
+ if( err )
+ {
+ break;
+ }
+
+ for( bytesRead = 0; bytesRead < i_buflen; bytesRead++ )
+ {
+ TRACDCOMP( g_trac_i2c,
+ INFO_MRK"Reading byte (%d) out of (%d)",
+ (bytesRead+1), i_buflen );
+
+ // Read the status reg to see if there is data in the FIFO
+ status.value = 0x0ull;
+ err = i2cReadStatusReg( i_target,
+ engine,
+ status );
+
+ if( err )
+ {
+ break;
+ }
+
+ while( 0 == status.fifo_entry_count )
+ {
+ nanosleep( 0, (interval * 1000) );
+
+ status.value = 0x0ull;
+ err = i2cReadStatusReg( i_target,
+ engine,
+ status );
+
+ if( err )
+ {
+ break;
+ }
+
+ if( 0 == timeoutCount-- )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cRead() - Timed out waiting for data in FIFO!" );
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_FIFO_TIMEOUT
+ * @severity ERRL_SEV_UNRECOVERABLE
+ * @moduleid I2C_READ
+ * @userdata1 Status Register Value
+ * @userdata2 Byte Address of write
+ * @devdesc Timed out waiting for data in FIFO to read
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_READ,
+ I2C_FIFO_TIMEOUT,
+ status.value,
+ addr );
+
+ break;
+ }
+ }
+
+ if( err )
+ {
+ break;
+ }
+
+ // Read the data from the fifo
+ fifo.value = 0x0ull;
+ err = deviceRead( i_target,
+ &fifo.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[engine].fifo ) );
+
+ if( err )
+ {
+ break;
+ }
+
+ *((uint8_t*)o_buffer + bytesRead) = fifo.byte_0;
+
+ TRACSCOMP( g_trac_i2cr,
+ "I2C READ DATA : engine %.2X : devAddr %.2X : addr %.4X : "
+ // TODO - when trace parameter limit is lifted, add byte count back in
+// "byte %d : %.2X",
+ "%.2X",
+ engine, devAddr, addr, /*bytesRead,*/ fifo.byte_0 );
+ }
+
+ if( err )
+ {
+ break;
+ }
+
+ // Poll for Command Complete
+ err = i2cWaitForCmdComp( i_target,
+ engine );
+
+ if( err )
+ {
+ break;
+ }
} while( 0 );
- TRACFCOMP( g_trac_i2c,
+ TRACSCOMP( g_trac_i2cr,
+ "I2C READ END : engine %.2X : devAddr %.2X : addr %.4X : len %d",
+ engine, devAddr, addr, i_buflen );
+
+ TRACDCOMP( g_trac_i2c,
EXIT_MRK"i2cRead()" );
return err;
@@ -161,26 +369,107 @@ errlHndl_t i2cRead ( TARGETING::Target * i_target,
// i2cWrite
// ------------------------------------------------------------------
errlHndl_t i2cWrite ( TARGETING::Target * i_target,
- uint64_t i_addr,
void * i_buffer,
- size_t & io_size )
+ size_t & io_buflen,
+ input_args_t i_args )
{
errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
+ uint64_t bytesWritten = 0x0;
+
+ uint64_t addr = i_args.addr;
+ uint64_t engine = i_args.engine;
+ uint64_t devAddr = i_args.devAddr;
- TRACFCOMP( g_trac_i2c,
+ // Define regs we'll be using
+ fiforeg fifo;
+
+ TRACDCOMP( g_trac_i2c,
ENTER_MRK"i2cWrite()" );
+ TRACSCOMP( g_trac_i2cr,
+ "I2C WRITE START : engine %.2X : devAddr %.2X : addr %.4X : len %d",
+ engine, devAddr, addr, io_buflen );
+
do
{
- // TODO - Functionality still needs to be added.
+ // Do Command/Mode reg setups
err = i2cSetup( i_target,
- i_addr,
- io_size,
+ io_buflen,
+ false,
true,
- false );
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Write the 2 byte address to the FIFO
+ err = i2cWriteByteAddr( i_target,
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ for( bytesWritten = 0x0; bytesWritten < io_buflen; bytesWritten++ )
+ {
+ // Wait for FIFO space to be available for the write
+ err = i2cWaitForFifoSpace( i_target,
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Write data to FIFO
+ fifo.value = 0x0ull;
+ fifo.byte_0 = *((uint8_t*)i_buffer + bytesWritten);
+
+ err = deviceWrite( i_target,
+ &fifo.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[engine].fifo ) );
+
+ if( err )
+ {
+ break;
+ }
+
+ TRACSCOMP( g_trac_i2cr,
+ "I2C WRITE DATA : engine %.2X : devAddr %.2X : addr %.4X : "
+ // TODO - Once trace paramenter limit is lifted add byte count in
+ "%.2X",
+// "byte %d : %.2X",
+ engine, devAddr, addr, /*bytesWritten,*/ fifo.byte_0 );
+ }
+
+ if( err )
+ {
+ break;
+ }
+
+ // Check for Command complete, and make sure no errors
+ err = i2cWaitForCmdComp( i_target,
+ engine );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Make sure we send back how many bytes were written
+ io_buflen = bytesWritten;
} while( 0 );
- TRACFCOMP( g_trac_i2c,
+ TRACSCOMP( g_trac_i2cr,
+ "I2C WRITE END : engine %.2X : devAddr %.2X : addr %.4X : len %d",
+ engine, devAddr, addr, io_buflen );
+
+ TRACDCOMP( g_trac_i2c,
EXIT_MRK"i2cWrite()" );
return err;
@@ -190,25 +479,443 @@ errlHndl_t i2cWrite ( TARGETING::Target * i_target,
// i2cSetup
// ------------------------------------------------------------------
errlHndl_t i2cSetup ( TARGETING::Target * i_target,
- uint64_t i_addr,
- size_t & i_size,
+ size_t & i_buflen,
+ bool i_readNotWrite,
bool i_withStop,
- bool i_readNotWrite )
+ input_args_t i_args )
{
errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
- TRACFCOMP( g_trac_i2c,
+ uint64_t port = i_args.port;
+ uint64_t engine = i_args.engine;
+ uint64_t devAddr = i_args.devAddr;
+
+ TRACDCOMP( g_trac_i2c,
ENTER_MRK"i2cSetup()" );
+ // Define the registers that we'll use
+ statusreg status;
+ modereg mode;
+ cmdreg cmd;
+
do
{
- // TODO - Functionality still needs to be added.
+ // TODO - Validate some of the arg values passed in
+
+ // Wait for Command complete before we start
+ status.value = 0x0ull;
+ err = i2cWaitForCmdComp( i_target,
+ engine );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Write Mode Register:
+ // - bit rate divisor
+ // - port number
+ mode.value = 0x0ull;
+
+ // Hard code to 400KHz until we get attributes in place to get this from
+ // the target.
+ mode.bit_rate_div = I2C_CLOCK_DIVISOR_400KHZ;
+ mode.port_num = port;
+
+ err = deviceWrite( i_target,
+ &mode.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[engine].mode ) );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Write Command Register:
+ // - with start
+ // - with stop
+ // - RnW
+ // - length
+ cmd.value = 0x0ull;
+ cmd.with_start = 1;
+ cmd.with_stop = (i_withStop ? 1 : 0);
+ cmd.with_addr = 1;
+ cmd.device_addr = devAddr;
+ cmd.read_not_write = (i_readNotWrite ? 1 : 0);
+
+ // Need to accomodate the byte addr length when writing
+ // to the FIFO, so add 2 bytes to the length.
+ cmd.length_b = i_buflen + 2;
+
+ err = deviceWrite( i_target,
+ &cmd.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[engine].command ) );
+
+ if( err )
+ {
+ break;
+ }
} while( 0 );
- TRACFCOMP( g_trac_i2c,
+ TRACDCOMP( g_trac_i2c,
EXIT_MRK"i2cSetup()" );
return err;
} // end i2cSetup
+// ------------------------------------------------------------------
+// i2cWaitForCmdComp
+// ------------------------------------------------------------------
+errlHndl_t i2cWaitForCmdComp ( TARGETING::Target * i_target,
+ uint64_t i_engine )
+{
+ errlHndl_t err = NULL;
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cWaitForCmdComp()" );
+
+ // Define the registers that we'll use
+ statusreg status;
+
+ // TODO - hardcoded to 400KHz for now.
+ uint64_t interval = I2C_TIMEOUT_INTERVAL( I2C_CLOCK_DIVISOR_400KHZ );
+ uint64_t timeoutCount = I2C_TIMEOUT_COUNT( interval );
+
+ do
+ {
+ // Check the Command Complete bit
+ do
+ {
+ nanosleep( 0, (interval * 1000) );
+ status.value = 0x0ull;
+ err = i2cReadStatusReg( i_target,
+ i_engine,
+ status );
+
+ if( err )
+ {
+ break;
+ }
+
+ if( 0 == timeoutCount-- )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cWaitForCmdComp() - Timed out waiting for Command Complete!" );
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_CMD_COMP_TIMEOUT
+ * @severity ERRL_SEV_UNRECOVERABLE
+ * @moduleid I2C_WAIT_FOR_CMD_COMP
+ * @userdata1 Status Register Value
+ * @userdata2 Master Engine
+ * @devdesc Timed out waiting for command complete.
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_WAIT_FOR_CMD_COMP,
+ I2C_CMD_COMP_TIMEOUT,
+ status.value,
+ i_engine );
+
+ break;
+ }
+ } while( 0 == status.command_complete ); /* Command Complete */
+
+ if( err )
+ {
+ break;
+ }
+ } while( 0 );
+
+ TRACDCOMP( g_trac_i2c,
+ EXIT_MRK"i2cWaitForCmdComp()" );
+
+ return err;
+} // end i2cWaitForBus
+
+// ------------------------------------------------------------------
+// i2cReadStatusReg
+// ------------------------------------------------------------------
+errlHndl_t i2cReadStatusReg ( TARGETING::Target * i_target,
+ uint64_t i_engine,
+ statusreg & o_statusReg )
+{
+ errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cReadStatusReg()" );
+
+ do
+ {
+ // Read the status Reg
+ err = deviceRead( i_target,
+ &o_statusReg.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[i_engine].status ) );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Check for Errors
+ // Per the specification it is a requirement to check for errors each time
+ // that the status register is read.
+ err = i2cCheckForErrors( i_target,
+ o_statusReg );
+
+ if( err )
+ {
+ break;
+ }
+ } while( 0 );
+
+ TRACDCOMP( g_trac_i2c,
+ EXIT_MRK"i2cReadStatusReg()" );
+
+ return err;
+} // end i2cReadStatusReg
+
+// ------------------------------------------------------------------
+// i2cCheckForErrors
+// ------------------------------------------------------------------
+errlHndl_t i2cCheckForErrors ( TARGETING::Target * i_target,
+ statusreg i_statusVal )
+{
+ errlHndl_t err = NULL;
+ i2cReasonCode reasonCode = I2C_INVALID_REASONCODE;
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cCheckForErrors()" );
+
+ do
+ {
+ if( 1 == i_statusVal.invalid_cmd )
+ {
+ reasonCode = I2C_INVALID_COMMAND;
+ }
+ else if( 1 == i_statusVal.lbus_parity_error )
+ {
+ reasonCode = I2C_LBUS_PARITY_ERROR;
+ }
+ else if( 1 == i_statusVal.backend_overrun_error )
+ {
+ reasonCode = I2C_BACKEND_OVERRUN_ERROR;
+ }
+ else if( 1 == i_statusVal.backend_access_error )
+ {
+ reasonCode = I2C_BACKEND_ACCESS_ERROR;
+ }
+ else if( 1 == i_statusVal.arbitration_lost_error )
+ {
+ reasonCode = I2C_ARBITRATION_LOST_ERROR;
+ }
+ else if( 1 == i_statusVal.nack_received )
+ {
+ reasonCode = I2C_NACK_RECEIVED;
+ }
+ else if( 1 == i_statusVal.data_request )
+ {
+ reasonCode = I2C_DATA_REQUEST;
+ }
+ else if( 1 == i_statusVal.stop_error )
+ {
+ reasonCode = I2C_STOP_ERROR;
+ }
+ else if( 1 == i_statusVal.any_i2c_interrupt )
+ {
+ // TODO - This will be expanded during bad machine path to specify
+ // which interrupts have fired.
+ reasonCode = I2C_INTERRUPT;
+ }
+
+ if( I2C_INVALID_REASONCODE != reasonCode )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cCheckForErrors() - Error found after command complete!" );
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_HW_ERROR_FOUND
+ * @severity ERRL_SEV_UNRECOVERABLE
+ * @moduleid I2C_CHECK_FOR_ERRORS
+ * @userdata1 Reasoncode
+ * @userdata2 <UNUSED>
+ * @devdesc Error was found in I2C status register. Check userdata1
+ * to determine what the error was.
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_CHECK_FOR_ERRORS,
+ I2C_HW_ERROR_FOUND,
+ reasonCode,
+ 0x0 );
+
+ // TODO - RTC entry to be created to allow for adding a target to an errorlog.
+ // Once that is implemented, the target will be used here to add to the log.
+
+ break;
+ }
+ } while( 0 );
+
+ TRACDCOMP( g_trac_i2c,
+ EXIT_MRK"i2cCheckForErrors()" );
+
+ return err;
+} // end i2cCheckForErrors
+
+// ------------------------------------------------------------------
+// i2cWriteByteAddr
+// ------------------------------------------------------------------
+errlHndl_t i2cWriteByteAddr ( TARGETING::Target * i_target,
+ input_args_t i_args )
+{
+ errlHndl_t err = NULL;
+ size_t size = sizeof(uint64_t);
+
+ uint64_t engine = i_args.engine;
+ uint64_t addr = i_args.addr;
+
+ // Define the reg(s) we'll be accessing
+ fiforeg fifo;
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cWriteByteAddr( %04x )",
+ addr );
+
+ do
+ {
+ // Make sure there is space in the FIFO
+ err = i2cWaitForFifoSpace( i_target,
+ i_args );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Write first byte of address to the FIFO
+ fifo.value = 0x0ull;
+ fifo.byte_0 = ((addr & 0xFF00) >> 8);
+
+ err = deviceWrite( i_target,
+ &fifo.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[engine].fifo ) );
+
+ if( err )
+ {
+ break;
+ }
+
+ // Write 2nd byte of address to the FIFO
+ fifo.value = 0x0ull;
+ fifo.byte_0 = (addr & 0xFF);
+
+ err = deviceWrite( i_target,
+ &fifo.value,
+ size,
+ DEVICE_SCOM_ADDRESS( masterAddrs[engine].fifo ) );
+
+ if( err )
+ {
+ break;
+ }
+ } while( 0 );
+
+ TRACDCOMP( g_trac_i2c,
+ EXIT_MRK"i2cWriteByteAddr( %04x )",
+ addr );
+
+ return err;
+} // end i2cWriteByteAddr
+
+
+// ------------------------------------------------------------------
+// i2cWaitForFifoSpace
+// ------------------------------------------------------------------
+errlHndl_t i2cWaitForFifoSpace ( TARGETING::Target * i_target,
+ input_args_t i_args )
+{
+ errlHndl_t err = NULL;
+ uint64_t engine = i_args.engine;
+ uint64_t addr = i_args.addr;
+
+ // TODO - hardcoded to 400KHz for now
+ uint64_t interval = I2C_TIMEOUT_INTERVAL( I2C_CLOCK_DIVISOR_400KHZ );
+ uint64_t timeoutCount = I2C_TIMEOUT_COUNT( interval );
+
+ // Define regs we'll be using
+ statusreg status;
+
+ TRACDCOMP( g_trac_i2c,
+ ENTER_MRK"i2cWaitForFifoSpace()" );
+
+ do
+ {
+ // Read Status reg to get available FIFO bytes
+ status.value = 0x0ull;
+ err = i2cReadStatusReg( i_target,
+ engine,
+ status );
+
+ if( err )
+ {
+ break;
+ }
+
+ while( I2C_MAX_FIFO_CAPACITY <= status.fifo_entry_count )
+ {
+ // FIFO is full, wait before writing any data
+ nanosleep( 0, (interval * 1000) );
+
+ status.value = 0x0ull;
+ err = i2cReadStatusReg( i_target,
+ engine,
+ status );
+
+ if( err )
+ {
+ break;
+ }
+
+ if( 0 == timeoutCount-- )
+ {
+ TRACFCOMP( g_trac_i2c,
+ ERR_MRK"i2cWrite() - Timed out waiting for space in FIFO!" );
+
+ /*@
+ * @errortype
+ * @reasoncode I2C_FIFO_TIMEOUT
+ * @severity ERRL_SEV_UNRECOVERABLE
+ * @moduleid I2C_WRITE
+ * @userdata1 Status Register Value
+ * @userdata2 Requested Byte Address
+ * @devdesc Timed out waiting for space to write into FIFO.
+ */
+ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE,
+ I2C_WRITE,
+ I2C_FIFO_TIMEOUT,
+ status.value,
+ addr );
+
+ break;
+ }
+ }
+
+ if( err )
+ {
+ break;
+ }
+ } while( 0 );
+
+ TRACDCOMP( g_trac_i2c,
+ EXIT_MRK"i2cWaitForFifoSpace()" );
+
+ return err;
+} // end i2cWaitForFifoSpace
+
} // end namespace I2C
diff --git a/src/usr/i2c/i2c.H b/src/usr/i2c/i2c.H
index 10031167a..86500dfa6 100755
--- a/src/usr/i2c/i2c.H
+++ b/src/usr/i2c/i2c.H
@@ -62,36 +62,105 @@ namespace I2C
#define I2C_CLOCK_DIVISOR_400KHZ 374 // 0x176
/**
+ * @brief I2C Clock delay polling values
+ *
+ * The first macro returns an interval to sleep, based on the clock speed.
+ * The macro and values were determined by examples provided from the prism
+ * hardware team.
+ *
+ * Poll interval = clock divisor / 37
+ * @100KHz = 40uS
+ * @400KHz = 10uS
+ *
+ * Poll count = 5000 / Poll interval
+ * @100KHz = 125
+ * @400KHz = 500
+ */
+#define I2C_TIMEOUT_INTERVAL( i_clockSpeed ) ( i_clockSpeed / 37 )
+#define I2C_TIMEOUT_COUNT( i_interval ) ( 5000 / i_interval )
+
+/**
* @brief I2C Master Base Addresses
+ *
+ * These addresses will not be needed once there is some solution in
+ * the attribute code that can be queried to get the chip base
+ * addresses.
+ */
+#define I2C_MASTER_BASE_ADDR 0xA0000
+
+/**
+ * @brief I2C Master Offset Addresses
+ */
+#define I2C_MASTER0_OFFSET 0x00
+#define I2C_MASTER1_OFFSET 0x20
+#define I2C_MASTER2_OFFSET 0x40
+
+/**
+ * @brief I2C Master Addresses
*/
-#define I2C_MASTER0_BASE 0x00
-#define I2C_MASTER1_BASE 0x20
-#define I2C_MASTER2_BASE 0x40
+#define I2C_MASTER0_ADDR (I2C_MASTER_BASE_ADDR | I2C_MASTER0_OFFSET)
+#define I2C_MASTER1_ADDR (I2C_MASTER_BASE_ADDR | I2C_MASTER1_OFFSET)
+#define I2C_MASTER2_ADDR (I2C_MASTER_BASE_ADDR | I2C_MASTER2_OFFSET)
+
+/**
+ * @brief I2C Master register structure and address definition
+ */
+typedef struct
+{
+ uint64_t fifo;
+ uint64_t command;
+ uint64_t mode;
+ // TODO - More to add for Interrupts when Bad Machine path is started
+ uint64_t status;
+} i2c_addrs_t;
+
+// Addresses for each of the registers in each engine.
+static i2c_addrs_t masterAddrs[] =
+{
+ { /* Master 0 */
+ I2C_MASTER0_ADDR | 0x4, // FIFO
+ I2C_MASTER0_ADDR | 0x5, // Command Register
+ I2C_MASTER0_ADDR | 0x6, // Mode Register
+ I2C_MASTER0_ADDR | 0xB, // Status Register
+ },
+ { /* Master 1 */
+ I2C_MASTER1_ADDR | 0x4, // FIFO
+ I2C_MASTER1_ADDR | 0x5, // Command Register
+ I2C_MASTER1_ADDR | 0x6, // Mode Register
+ I2C_MASTER1_ADDR | 0xB, // Status Register
+ },
+ { /* Master 2 */
+ I2C_MASTER2_ADDR | 0x4, // FIFO
+ I2C_MASTER2_ADDR | 0x5, // Command Register
+ I2C_MASTER2_ADDR | 0x6, // Mode Register
+ I2C_MASTER2_ADDR | 0xB, // Status Register
+ }
+};
+
+
+// -----------------------------------------------------------------------
+// NOTE: Addressing listed below is from the PIB I2C Master Addressing
+// scheme from the I2C Master specification. Only the Legacy
+// registers are being implemented.
+// -----------------------------------------------------------------------
/**
* @brief I2C FIFO register definition
-* Address 0x00
+* Address 0x04
*/
union fiforeg
{
uint64_t value;
- uint8_t bytes[8];
struct
{
uint64_t byte_0 : 8;
- uint64_t byte_1 : 8;
- uint64_t byte_2 : 8;
- uint64_t byte_3 : 8;
- uint64_t byte_4 : 8;
- uint64_t byte_5 : 8;
- uint64_t byte_6 : 8;
- uint64_t byte_7 : 8;
+ uint64_t padding : 56;
} PACKED;
} fifo_reg_t;
/**
- * @brief I2C Command register definition.
- * Address 0x01
+ * @brief I2C Command register definition
+ * Address 0x05
*/
union cmdreg
{
@@ -112,7 +181,7 @@ union cmdreg
/**
* @brief I2C Mode register definition
- * Address 0x02
+ * Address 0x06
*/
union modereg
{
@@ -132,7 +201,7 @@ union modereg
/**
* @brief Watermark register definition
- * Address 0x03
+ * Address 0x07
*/
union watermarkreg
{
@@ -150,7 +219,7 @@ union watermarkreg
/**
* @brief Interrupt Mask register definition
- * Address 0x04
+ * Address 0x08
*/
union intmaskreg
{
@@ -180,7 +249,7 @@ union intmaskreg
/**
* @brief Interrupt Condition register definition
- * Address 0x05
+ * Address 0x09
*/
union intcondreg
{
@@ -210,7 +279,7 @@ union intcondreg
/**
* @brief Interrupt register definition
- * Address 0x06
+ * Address 0x0A
*/
union interruptreg
{
@@ -240,7 +309,7 @@ union interruptreg
/**
* @brief Status register definition
- * Address 0x07
+ * Address 0x0B
*/
union statusreg
{
@@ -252,12 +321,12 @@ union statusreg
uint64_t backend_overrun_error : 1;
uint64_t backend_access_error : 1;
uint64_t arbitration_lost_error : 1;
- uint64_t nack_received_error : 1;
+ uint64_t nack_received : 1;
uint64_t data_request : 1;
uint64_t command_complete : 1;
uint64_t stop_error : 1;
uint64_t upper_threshold : 7;
- uint64_t any_i2_interrupt : 1;
+ uint64_t any_i2c_interrupt : 1;
uint64_t reserved0 : 2;
uint64_t i2c_port_history_busy : 1;
uint64_t scl_input_level : 1;
@@ -271,7 +340,7 @@ union statusreg
/**
* @brief Extended Status register definition
- * Address 0x08
+ * Address 0x0C
*/
union extstatusreg
{
@@ -299,7 +368,7 @@ union extstatusreg
/**
* @brief Residual Front/Back end length register definition
- * Address 0x09
+ * Address 0x0D
*/
union residuallengthreg
{
@@ -312,50 +381,13 @@ union residuallengthreg
} PACKED;
} residual_length_reg_t;
-/**
- * @brief Port Busy register definition
- * Address 0x0A
- */
-union portbusyreg
+typedef struct
{
- uint64_t value;
- struct
- {
- uint64_t port0_busy : 1;
- uint64_t port1_busy : 1;
- uint64_t port2_busy : 1;
- uint64_t port3_busy : 1;
- uint64_t port4_busy : 1;
- uint64_t port5_busy : 1;
- uint64_t port6_busy : 1;
- uint64_t port7_busy : 1;
- uint64_t port8_busy : 1;
- uint64_t port9_busy : 1;
- uint64_t port10_busy : 1;
- uint64_t port11_busy : 1;
- uint64_t port12_busy : 1;
- uint64_t port13_busy : 1;
- uint64_t port14_busy : 1;
- uint64_t port15_busy : 1;
- uint64_t port16_busy : 1;
- uint64_t port17_busy : 1;
- uint64_t port18_busy : 1;
- uint64_t port19_busy : 1;
- uint64_t port20_busy : 1;
- uint64_t port21_busy : 1;
- uint64_t port22_busy : 1;
- uint64_t port23_busy : 1;
- uint64_t port24_busy : 1;
- uint64_t port25_busy : 1;
- uint64_t port26_busy : 1;
- uint64_t port27_busy : 1;
- uint64_t port28_busy : 1;
- uint64_t port29_busy : 1;
- uint64_t port30_busy : 1;
- uint64_t port31_busy : 1;
- uint64_t padding : 32;
- } PACKED;
-} port_busy_reg_t;
+ uint64_t addr;
+ uint64_t port;
+ uint64_t engine;
+ uint64_t devAddr;
+} input_args_t;
/**
@@ -367,7 +399,7 @@ union portbusyreg
* @param[in] i_opType - Operation Type - See DeviceFW::OperationType in
* driververif.H
*
-* @param[in] i_target - I2C Target device
+* @param[in] i_target - I2C Master Target device
*
* @param [in/out] io_buffer
* INPUT: Pointer to the data that will be written to the target
@@ -402,70 +434,148 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType,
* @brief This function will do the real work of reading from the I2C
* device.
*
- * @param[in] i_target - The device target to read from.
- *
- * @param[in] i_addr - The I2C address to use for the read.
+ * @param[in] i_target - The I2C master to source the read to the slave.
*
* @param[out] o_buffer - The buffer to place the retrieved data.
*
- * @param[in] i_size - The size of the data to read and place in the
+ * @param[in] i_buflen - The size of the data to read and place in the
* buffer.
*
+ * @param[in] i_args - Structure containing arguments needed for a command
+ * transaction.
+ *
* @return errlHndl_t - NULL if successful, otherwise a pointer to
* the error log.
*/
errlHndl_t i2cRead ( TARGETING::Target * i_target,
- uint64_t i_addr,
void * o_buffer,
- size_t & i_size );
+ size_t & i_buflen,
+ input_args_t i_args );
/**
* @brief This function will do the real work of writinging to the I2C
* device.
*
- * @param[in] i_target - The device target to write to.
- *
- * @param[in] i_addr - The I2C address to use for the write.
+ * @param[in] i_target - The I2C master to source the write to the slave.
*
* @param[in] i_buffer - The buffer containing the data to be written
* to the target device.
*
- * @param[in/out] i_size - INPUT: The size of the data to write to the
+ * @param[in/out] io_buflen - INPUT: The size of the data to write to the
* target device. OUTPUT: The size of the data buffer written.
*
+ * @param[in] i_args - Structure containing arguments needed for a command
+ * transaction.
+ *
* @return errlHndl_t - NULL if successful, otherwise a pointer to
* the error log.
*/
errlHndl_t i2cWrite ( TARGETING::Target * i_target,
- uint64_t i_addr,
void * i_buffer,
- size_t & io_size );
+ size_t & io_buflen,
+ input_args_t i_args );
/**
* @brief This function will do the I2C setup of the Address/Command registers
* before issuing the 'go' on the I2C bus.
*
- * @param[in] i_target - The target device.
- *
- * @param[in] i_addr - The I2C address to use for the operation.
- *
- * @param[in] i_size - The size of the data that will be read/written.
+ * @param[in] i_target - The I2C master.
*
- * @param[in] i_withStop - Whether or not to set the with_stop bit in the
- * I2C Command register.
+ * @param[in] i_buflen - The size of the data that will be read/written.
*
* @param[in] i_readNotWrite - true if doing a read operation, false if
* doing a write operation.
*
+ * @param[in] i_withStop - true if with_stop bit is to be set, otherwise
+ * with_stop will be set to zero.
+ *
+ * @param[in] i_args - Structure containing arguments needed for a command
+ * transaction.
+ *
* @return errlHndl_t - NULL if successful, otherwise a pointer to
* the error log.
*/
errlHndl_t i2cSetup ( TARGETING::Target * i_target,
- uint64_t i_addr,
- size_t & i_size,
+ size_t & i_buflen,
+ bool i_readNotWrite,
bool i_withStop,
- bool i_readNotWrite );
+ input_args_t i_args );
+
+/**
+ * @brief This function will wait for the command to be complete or
+ * timeout waiting before returning.
+ *
+ * @param[in] i_target - The I2C master target.
+ *
+ * @return errlHndl_t - NULL if successful, otherwise a pointer to
+ * the error log.
+ */
+errlHndl_t i2cWaitForCmdComp ( TARGETING::Target * i_target,
+ uint64_t i_engine );
+
+/**
+ * @brief This function will read the I2C Master engine status register
+ * and perform all required steps after reading it.
+ *
+ * @param[in] i_target - The I2C master target.
+ *
+ * @param[in] i_engine - The I2C Master engine that is to be read.
+ *
+ * @param[out] o_statusReg - The value of the status register read.
+ *
+ * @return errlHndl_t - NULL if successful, otherwise a pointer to
+ * the error log.
+ */
+errlHndl_t i2cReadStatusReg ( TARGETING::Target * i_target,
+ uint64_t i_engine,
+ statusreg & o_statusReg );
+
+/**
+ * @brief This function will check for errors in the status register
+ * value that is read out.
+ *
+ * @param[in] i_target - The I2C master target.
+ *
+ * @param[in] i_statusVal - The value of the Status Register.
+ *
+ * @return errlHndl_t - NULL if successful, otherwise a pointer to
+ * the error log.
+ */
+errlHndl_t i2cCheckForErrors ( TARGETING::Target * i_target,
+ statusreg i_statusVal );
+
+/**
+ * @brief This function will take the 2 byte address to be accessed
+ * on the slave and write it to the FIFO.
+ *
+ * @param[in] i_target - The I2C master target.
+ *
+ * @param[in] i_args - Structure containing arguments needed for a command
+ * transaction.
+ *
+ * @return errlHndl_t - NULL if successful, otherwise a pointer to
+ * the error log.
+ */
+errlHndl_t i2cWriteByteAddr ( TARGETING::Target * i_target,
+ input_args_t i_args );
+
+/**
+ * @brief This function will read the status register and not return
+ * until there is room in the FIFO for data to be written. An
+ * error will be returned if it times out waiting for space in
+ * the FIFO.
+ *
+ * @param[in] i_target - The I2C master target.
+ *
+ * @param[in] i_args - Structure containing arguments needed for a command
+ * transaction.
+ *
+ * @return errHndl_t - NULL if successful, otherwise a pointer to
+ * the error log.
+ */
+errlHndl_t i2cWaitForFifoSpace ( TARGETING::Target * i_target,
+ input_args_t i_args );
}; // end I2C namespace
diff --git a/src/usr/i2c/test/i2ctest.H b/src/usr/i2c/test/i2ctest.H
index 9387f79e3..314f8c523 100755
--- a/src/usr/i2c/test/i2ctest.H
+++ b/src/usr/i2c/test/i2ctest.H
@@ -27,7 +27,8 @@
* @file i2ctest.H
*
* @brief Test case for I2C code
-*/
+ */
+#include <sys/time.h>
#include <cxxtest/TestSuite.H>
#include <errl/errlmanager.H>
@@ -41,95 +42,106 @@ extern trace_desc_t* g_trac_i2c;
using namespace TARGETING;
// Address and data to read/write
-struct testI2CAddrData
+struct testI2CParms
{
- uint32_t addr;
+ uint64_t port;
+ uint64_t engine;
+ uint64_t addr;
+ uint64_t devAddr;
uint64_t data;
+ size_t size;
};
// Test table values
-const testI2CAddrData g_i2cAddrTable[] =
+const testI2CParms g_i2cWriteCmdTable[] =
{
- // Write data to be ORed with read value
- {0x13030007, 0x0000040000000000},
- {0x13010002, 0x00000C0000000000},
+ { 0x00, 0x00, 0x1234, 0x50, 0xFEDCBA9876543210, 8 },
+ { 0x00, 0x00, 0x1234, 0x50, 0xFEDCBA9876543210, 8 },
};
-const uint32_t g_i2cmAddrTableSz =
- sizeof(g_i2cAddrTable)/sizeof(testI2CAddrData);
+const uint32_t g_i2cWriteCmdTableSz =
+sizeof(g_i2cWriteCmdTable)/sizeof(testI2CParms);
class I2CTest: public CxxTest::TestSuite
{
-public:
-
- /**
- * @brief I2C test #1
- * Write value and read back to verify
- */
- void testI2C1(void)
- {
- errlHndl_t l_err = NULL;
- TS_TRACE( "I2C Test 1: its running!" );
-
- do
+ public:
+
+ /**
+ * @brief I2C test #1
+ * Write value and read back to verify
+ * Currently only 1 operation
+ */
+ void testI2C1(void)
{
- TARGETING::TargetService& targetService = TARGETING::targetService();
- TARGETING::Target* l_testTarget = NULL;
- targetService.masterProcChipTargetHandle( l_testTarget );
+ errlHndl_t err = NULL;
- if( NULL == l_testTarget )
+ TS_TRACE( "I2C Test 1: its running!" );
+
+ do
+ {
+ TARGETING::TargetService& l_targetService = TARGETING::targetService();
+ TARGETING::Target* testTarget = NULL;
+ l_targetService.masterProcChipTargetHandle( testTarget );
+ assert(testTarget != NULL);
+
+ testI2CParms testEntry = g_i2cWriteCmdTable[0];
+
+ // Perform I2C write
+ uint64_t data = testEntry.data;
+ TS_TRACE( "I2C - calling from Write" );
+ err = deviceOp( DeviceFW::WRITE,
+ testTarget,
+ &data,
+ testEntry.size,
+ DEVICE_I2C_ADDRESS( testEntry.addr,
+ testEntry.port,
+ testEntry.engine,
+ testEntry.devAddr ) );
+
+ TS_TRACE( "I2C - returned from Write" );
+ if( err )
+ {
+ break;
+ }
+
+ // Perform I2C read
+ err = deviceOp( DeviceFW::READ,
+ testTarget,
+ &data,
+ testEntry.size,
+ DEVICE_I2C_ADDRESS( testEntry.addr,
+ testEntry.port,
+ testEntry.engine,
+ testEntry.devAddr ) );
+
+ if( err )
+ {
+ break;
+ }
+
+ // check the data read
+ if( testEntry.data != data )
+ {
+ TS_FAIL( "testI2C1 failed! - Data read does not match what was written!" );
+ TS_TRACE( "testI2C1 - Data Written: %016llx, Data Read: %016llx",
+ testEntry.data, data );
+
+ }
+ } while( 0 );
+
+ if ( err )
{
- TS_TRACE( "I2C Test - PROC test target is NULL!" );
- break;
+ TS_FAIL( "testI2C1 failed! Error committed." );
+ errlCommit( err );
}
-
- size_t l_size = sizeof(uint64_t);
- testI2CAddrData l_testEntry = g_i2cAddrTable[0];
-
- // --------------------------------------------
- // NOTE: The following doesn't actually read
- // or write any data, it is strictly there to
- // prove that the interfaces are working. The
- // real tests will be added at a later time.
- // --------------------------------------------
-
- // Perform I2C write
- uint64_t l_data = 0;
-
- l_err = deviceOp( DeviceFW::WRITE,
- l_testTarget,
- &l_data,
- l_size,
- DEVICE_I2C_ADDRESS(l_testEntry.addr) );
-
- if( l_err )
+ else
{
- break;
+ TS_TRACE( "testI2C1 runs successfully!" );
}
- // Perform I2C read
- l_err = deviceOp( DeviceFW::READ,
- l_testTarget,
- &l_data,
- l_size,
- DEVICE_I2C_ADDRESS(l_testEntry.addr) );
-
-
- } while( 0 );
-
- if (l_err)
- {
- TS_FAIL("testI2C1 failed! Error committed.");
- errlCommit(l_err);
+ return;
}
- else
- {
- TS_TRACE("testI2C1 runs successfully!");
- }
-
- return;
- }
};
#endif
OpenPOWER on IntegriCloud