diff options
author | Terry J. Opie <opiet@us.ibm.com> | 2011-08-17 14:12:26 -0500 |
---|---|---|
committer | Terry J. Opie <opiet@us.ibm.com> | 2011-10-21 08:17:43 -0500 |
commit | 21185b30cd99a00f01e15edba28402cdc00de1d1 (patch) | |
tree | abfa4543ba35c680414f89bf8b15d8dddbd61258 /src/usr/i2c | |
parent | 591bf9abfe58c662a8e81820fdc96b90b85a9b55 (diff) | |
download | talos-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-x | src/usr/i2c/i2c.C | 859 | ||||
-rwxr-xr-x | src/usr/i2c/i2c.H | 290 | ||||
-rwxr-xr-x | src/usr/i2c/test/i2ctest.H | 152 |
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 |