diff options
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/fsi/fsidd.C | 743 | ||||
-rw-r--r-- | src/usr/fsi/fsidd.H | 315 | ||||
-rw-r--r-- | src/usr/fsi/makefile | 30 | ||||
-rw-r--r-- | src/usr/fsi/test/fsiddtest.H | 195 | ||||
-rw-r--r-- | src/usr/fsi/test/makefile | 29 | ||||
-rw-r--r-- | src/usr/makefile | 4 | ||||
-rw-r--r-- | src/usr/xscom/xscom.C | 13 |
7 files changed, 1322 insertions, 7 deletions
diff --git a/src/usr/fsi/fsidd.C b/src/usr/fsi/fsidd.C new file mode 100644 index 000000000..d909f85b7 --- /dev/null +++ b/src/usr/fsi/fsidd.C @@ -0,0 +1,743 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/fsi/fsidd.C $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2011 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +/** + * @file fsidd.C + * + * @brief Implementation of the FSI Device Driver + */ + +/*****************************************************************************/ +// I n c l u d e s +/*****************************************************************************/ +#include <string.h> +#include <devicefw/driverif.H> +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <targeting/targetservice.H> +#include <errl/errlmanager.H> +#include <fsi/fsi_reasoncodes.H> +#include "fsidd.H" +#include <kernel/console.H> +#include <sys/time.h> +#include <algorithm> + + +// Trace definition +trace_desc_t* g_trac_fsi = NULL; +TRAC_INIT(&g_trac_fsi, "FSI", 4096); //4K + +// Easy macro replace for unit testing +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + + +//@todo - These should come from the target/attribute code somewhere +uint64_t target_to_uint64(TARGETING::Target* i_target) +{ + uint64_t id = 0; + if( i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ) + { + id = 0xFFFFFFFFFFFFFFFF; + } + else + { + // class|type|model|number : 1 byte each + id = (uint64_t)(i_target->getAttr<TARGETING::ATTR_CLASS>() & 0xFF) << 56; + id |= (uint64_t)(i_target->getAttr<TARGETING::ATTR_TYPE>() & 0xFF) << 48; + id |= (uint64_t)(i_target->getAttr<TARGETING::ATTR_MODEL>() & 0xFF) << 40; + id |= 0ull << 32; //@todo-need a unit num + } + return id; +} + +// Initialize static variables +mutex_t FsiDD::cv_mux = MUTEX_INITIALIZER; +std::vector<FsiDD*> FsiDD::cv_instances; + +/** + * @brief Select the instance for the given target + */ +FsiDD* FsiDD::getInstance( TARGETING::Target* i_target ) +{ + FsiDD* inst = NULL; + TRACUCOMP( g_trac_fsi, "FsiDD::getInstance> Looking for target : %llX", target_to_uint64(i_target) ); + + mutex_lock(&cv_mux); + + for( std::vector<FsiDD*>::iterator dd = FsiDD::cv_instances.begin(); + dd != FsiDD::cv_instances.end(); + ++dd ) + { + if( (*dd)->iv_target == i_target ) + { + inst = *dd; + break; + } + } + + // need to instantiate an object + if( (inst == NULL) + && (TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL != i_target) ) + { + // only support processor targets + //@todo - switch to PredicateCTM + if( i_target->getAttr<TARGETING::ATTR_TYPE>() == TARGETING::TYPE_PROC ) + { + TRACFCOMP( g_trac_fsi, "FsiDD::getInstance> Creating new instance for target : %llX", target_to_uint64(i_target) ); + inst = new FsiDD(i_target); + FsiDD::cv_instances.push_back(inst); + } + } + mutex_unlock(&cv_mux); + + return inst; +} + + +namespace FSI +{ + +/** + * @brief Performs a FSI Read Operation + * This function performs a FSI Read operation. It follows a pre-defined + * prototype functions in order to be registered with the device-driver + * framework. + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target FSI target + * @param[in/out] io_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] io_buflen Input: size of io_buffer (in bytes) + * Output: + * Read: Size of output data + * Write: Size of data written + * @param[in] i_accessType DeviceFW::AccessType enum (usrif.H) + * @param[in] i_args This is an argument list for DD framework. + * In this function, there's only one argument, + * containing the FSI address + * @return errlHndl_t + */ +errlHndl_t ddRead(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args) +{ + errlHndl_t l_err = NULL; + uint64_t i_addr = va_arg(i_args,uint64_t); + TRACUCOMP( g_trac_fsi, "FSI::ddRead> i_addr=%llX, target=%llX", i_addr, target_to_uint64(i_target) ); + + do{ + FsiDD* driver = FsiDD::getInstance(i_target); + if( driver ) + { + // prefix the appropriate MFSI/cMFSI slave port + uint64_t l_addr = driver->genFullFsiAddr( i_target, i_addr ); + + // do the read + l_err = driver->read(l_addr, + io_buflen, + (uint32_t*)io_buffer); + if(l_err) + { + break; + } + } + else + { + TRACFCOMP( g_trac_fsi, "FSI::ddRead> Invalid target : %llX", target_to_uint64(i_target) ); + /*@ + * @errortype + * @moduleid FSI::MOD_FSIDD_DDREAD + * @reasoncode FSI::RC_INVALID_TARGET + * @userdata1 Target Id + * @userdata2 FSI Address + * @devdesc FSI::ddRead> Invalid target + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + FSI::MOD_FSIDD_DDREAD, + FSI::RC_INVALID_TARGET, + target_to_uint64(i_target), + i_addr); + break; + + } + + }while(0); + + return l_err; +} + +/** + * @brief Performs a FSI Write Operation + * This function performs a FSI Write operation. It follows a pre-defined + * prototype functions in order to be registered with the device-driver + * framework. + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target FSI target + * @param[in/out] io_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] io_buflen Input: size of io_buffer (in bytes) + * Output: + * Read: Size of output data + * Write: Size of data written + * @param[in] i_accessType DeviceFW::AccessType enum (usrif.H) + * @param[in] i_args This is an argument list for DD framework. + * In this function, there's only one argument, + * containing the FSI address + * @return errlHndl_t + */ +errlHndl_t ddWrite(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args) +{ + errlHndl_t l_err = NULL; + uint64_t i_addr = va_arg(i_args,uint64_t); + TRACUCOMP( g_trac_fsi, "FSI::ddWrite> i_addr=%llX, target=%llX", i_addr, target_to_uint64(i_target) ); + + do{ + FsiDD* driver = FsiDD::getInstance(i_target); + if( driver ) + { + // prefix the appropriate MFSI/cMFSI slave port + uint64_t l_addr = driver->genFullFsiAddr( i_target, i_addr ); + + // do the write + l_err = driver->write(l_addr, + io_buflen, + (uint32_t*)io_buffer); + if(l_err) + { + break; + } + } + else + { + TRACFCOMP( g_trac_fsi, "FSI::ddWrite> Invalid target : %llX", target_to_uint64(i_target) ); + /*@ + * @errortype + * @moduleid FSI::MOD_FSIDD_DDWRITE + * @reasoncode FSI::RC_INVALID_TARGET + * @userdata1 Target Id + * @userdata2 FSI Address + * @devdesc FSI::ddWrite> Invalid target + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + FSI::MOD_FSIDD_DDWRITE, + FSI::RC_INVALID_TARGET, + target_to_uint64(i_target), + i_addr); + break; + + } + + }while(0); + + return l_err; +} + +// Register fsidd access functions to DD framework +DEVICE_REGISTER_ROUTE(DeviceFW::READ, + DeviceFW::FSI, + TARGETING::TYPE_PROC, + ddRead); + +// Register fsidd access functions to DD framework +DEVICE_REGISTER_ROUTE(DeviceFW::WRITE, + DeviceFW::FSI, + TARGETING::TYPE_PROC, + ddWrite); + + +}; //end FSI namespace + + + + + +/** + * @brief Read FSI Register + */ +errlHndl_t FsiDD::read(uint64_t i_address, + size_t& io_buflen, + uint32_t* o_buffer) +{ + TRACDCOMP(g_trac_fsi, "FsiDD::read(i_address=0x%llx)> ", i_address); + errlHndl_t l_err = NULL; + bool need_unlock = false; + + do { + // make sure we got a valid FSI address + l_err = verifyAddressRange( i_address, io_buflen ); + if(l_err) + { + break; + } + + // setup the OPB command register + uint64_t fsicmd = i_address | 0x60000000; // 011=Read Full Word + fsicmd <<= 32; // Command is in the upper word + + // generate the proper OPB SCOM address + uint64_t opbaddr = genOpbScomAddr(OPB_REG_CMD); + + // atomic section >> + mutex_lock(&iv_fsiMutex); + need_unlock = true; + + // always read/write 64 bits to SCOM + size_t scom_size = sizeof(uint64_t); + + // write the OPB command register to trigger the read + TRACUCOMP(g_trac_fsi, "FsiDD::read> ScomWRITE : opbaddr=%.16llX, data=%.16llX", opbaddr, fsicmd ); + l_err = deviceOp( DeviceFW::WRITE, + iv_target, + &fsicmd, + scom_size, + DEVICE_SCOM_ADDRESS(opbaddr) ); + if( l_err ) + { + TRACFCOMP(g_trac_fsi, "FsiDD::read> Error from device 1 : RC=%X", l_err->reasonCode() ); + break; + } + + // poll for complete and get the data back + l_err = pollForComplete( i_address, o_buffer ); + if( l_err ) + { + break; + } + + io_buflen = 4; + + // atomic section << + need_unlock = false; + mutex_unlock(&iv_fsiMutex); + + } while(0); + + if( need_unlock ) + { + mutex_unlock(&iv_fsiMutex); + } + + if( l_err ) + { + io_buflen = 0; + } + + return l_err; +} + +/** + * @brief Write FSI Register + */ +errlHndl_t FsiDD::write(uint64_t i_address, + size_t& io_buflen, + uint32_t* i_buffer) +{ + TRACDCOMP(g_trac_fsi, "FsiDD::write(i_address=0x%llx)> ", i_address); + errlHndl_t l_err = NULL; + bool need_unlock = false; + + do { + // make sure we got an FSI address + l_err = verifyAddressRange( i_address, io_buflen ); + if(l_err) + { + break; + } + + // pull out the data to write (length has been verified) + uint32_t fsidata = *i_buffer; + + // setup the OPB command register + uint64_t fsicmd = i_address | 0xE0000000; // 111=Write Full Word + fsicmd <<= 32; // Command is in the upper 32-bits + fsicmd |= fsidata; // Data is in the bottom 32-bits + size_t scom_size = sizeof(uint64_t); + + // generate the proper OPB SCOM address + uint64_t opbaddr = genOpbScomAddr(OPB_REG_CMD); + + // atomic section >> + mutex_lock(&iv_fsiMutex); + need_unlock = true; + + // write the OPB command register + TRACUCOMP(g_trac_fsi, "FsiDD::write> ScomWRITE : opbaddr=%.16llX, data=%.16llX", opbaddr, fsicmd ); + l_err = deviceOp( DeviceFW::WRITE, + iv_target, + &fsicmd, + scom_size, + DEVICE_SCOM_ADDRESS(opbaddr) ); + if( l_err ) + { + TRACFCOMP(g_trac_fsi, "FsiDD::write> Error from device : RC=%X", l_err->reasonCode() ); + break; + } + + // poll for complete (no return data) + l_err = pollForComplete( i_address, NULL ); + if( l_err ) + { + break; + } + + io_buflen = 4; + + // atomic section << + need_unlock = false; + mutex_unlock(&iv_fsiMutex); + + } while(0); + + if( need_unlock ) + { + mutex_unlock(&iv_fsiMutex); + } + + if( l_err ) + { + io_buflen = 0; + } + + TRACDCOMP(g_trac_fsi, "< FsiDD::write() ", i_address); + + return l_err; +} + + +/** + * @brief Constructor + */ +FsiDD::FsiDD( TARGETING::Target* i_target ) +:iv_target(i_target) +{ + TRACFCOMP(g_trac_fsi, "FsiDD::FsiDD()> target=%llX", target_to_uint64(i_target) ); + + mutex_init(&iv_fsiMutex); +} + +/** + * @brief Destructor + */ +FsiDD::~FsiDD() +{ + // remove this instance from the global list + FsiDD::cv_instances.erase( + std::find(cv_instances.begin(), + cv_instances.end(), + this) + ); +} + +/** + * @brief Verify Request is in appropriate address range + */ +errlHndl_t FsiDD::verifyAddressRange(uint64_t i_address, + size_t i_length) +{ + errlHndl_t l_err = NULL; + + do{ + //@todo - add more address checks + + // no port specified + if( (i_address & (MFSI_PORT_MASK|CMFSI_PORT_MASK|CONTROL_REG_MASK)) == 0 ) + { + TRACFCOMP( g_trac_fsi, "FsiDD::verifyAddressRange> Invalid address : i_address=0x%X", i_address ); + /*@ + * @errortype + * @moduleid FSI::MOD_FSIDD_VERIFYADDRESSRANGE + * @reasoncode FSI::RC_INVALID_ADDRESS + * @userdata1 FSI Address + * @userdata2 Data Length + * @devdesc FsiDD::verifyAddressRange> Invalid address + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + FSI::MOD_FSIDD_VERIFYADDRESSRANGE, + FSI::RC_INVALID_ADDRESS, + i_address, + TO_UINT64(i_length)); + break; + } + + if( i_length != 4 ) + { + TRACFCOMP( g_trac_fsi, "FsiDD::verifyAddressRange> Invalid data length : i_length=%d", i_length ); + /*@ + * @errortype + * @moduleid FSI::MOD_FSIDD_VERIFYADDRESSRANGE + * @reasoncode FSI::RC_INVALID_LENGTH + * @userdata1 FSI Address + * @userdata2 Data Length + * @devdesc FsiDD::verifyAddressRange> Invalid data length + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + FSI::MOD_FSIDD_VERIFYADDRESSRANGE, + FSI::RC_INVALID_LENGTH, + i_address, + TO_UINT64(i_length)); + break; + } + + + }while(0); + + return l_err; +} + + +/** + * @brief Analyze error bits and recover hardware as needed + */ +errlHndl_t FsiDD::handleOpbErrors(TARGETING::Target* i_target, + uint64_t i_address, + uint32_t i_opbStatReg) +{ + errlHndl_t l_err = NULL; + + if( (i_opbStatReg & OPB_STAT_ERR_ANY) + || (i_opbStatReg & OPB_STAT_BUSY) ) + { + TRACFCOMP( g_trac_fsi, "FsiDD::handleOpbErrors> Error during FSI access : i_address=0x%X, OPB Status=0x%.8X", i_address, i_opbStatReg ); + + + /*@ + * @errortype + * @moduleid FSI::MOD_FSIDD_HANDLEOPBERRORS + * @reasoncode FSI::RC_OPB_ERROR + * @userdata1 FSI Address + * @userdata2 OPB Status Register + * @devdesc FsiDD::handleOpbErrors> Error during FSI access + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + FSI::MOD_FSIDD_HANDLEOPBERRORS, + FSI::RC_OPB_ERROR, + i_address, + TWO_UINT32_TO_UINT64(i_opbStatReg,0)); + //@todo - figure out best data to log + + //@todo - implement recovery and callout code + + } + + return l_err; +} + +/** + * @brief Poll for completion of a FSI operation, return data on read + */ +errlHndl_t FsiDD::pollForComplete(uint64_t i_address, + uint32_t* o_readData) +{ + errlHndl_t l_err = NULL; + + do { + // poll for complete + uint32_t read_data[2]; + size_t scom_size = sizeof(uint64_t); + uint64_t opbaddr = genOpbScomAddr(OPB_REG_STAT); + int attempts = 0; + do + { + TRACUCOMP(g_trac_fsi, "FsiDD::pollForComplete> ScomREAD : opbaddr=%.16llX", opbaddr ); + l_err = deviceOp( DeviceFW::READ, + iv_target, + read_data, + scom_size, + DEVICE_SCOM_ADDRESS(opbaddr) ); + if( l_err ) + { + TRACFCOMP(g_trac_fsi, "FsiDD::pollForComplete> Error from device 2 : RC=%X", l_err->reasonCode() ); + break; + } + + //@fixme - Simics model is broken on writes, just assume completion + if( !o_readData ) + { + read_data[0] &= ~OPB_STAT_BUSY; + } + + // check for completion or error + TRACUCOMP(g_trac_fsi, "FsiDD::pollForComplete> ScomREAD : read_data[0]=%.8llX", read_data[0] ); + if( ((read_data[0] & OPB_STAT_BUSY) == 0) //not busy + || (read_data[0] & OPB_STAT_ERR_ANY) ) //error bits + { + break; + } + + attempts++; + } while( attempts < MAX_OPB_ATTEMPTS ); + if( l_err ) { break; } + //@todo - hw has a 1ms watchdog, do I need to have a limit or just + // loop until we hit an error + + // check if we got an error + l_err = handleOpbErrors( iv_target, i_address, read_data[0] ); + if( l_err ) + { + break; + } + + //@todo - is this possible? Ask hw team + // read valid isn't on + if( o_readData ) // only check if we're doing a read + { + if( !(read_data[0] & OPB_STAT_READ_VALID) ) + { + TRACFCOMP( g_trac_fsi, "FsiDD::read> Read valid never came on : i_address=0x%X, OPB Status=0x%.8X", i_address, read_data[0] ); + //@todo - create error log + break; + } + + *o_readData = read_data[1]; + } + + } while(0); + + return l_err; +} + +/** + * @brief Generate a complete FSI address based on the target and the + * FSI offset within that target + */ +uint64_t FsiDD::genFullFsiAddr(TARGETING::Target* i_target, + uint64_t i_address) +{ + //@todo - fill this in, for now just return the address as-is + return i_address; + + //target matches my target so the address is correct as-is + if( i_target == iv_target ) + { + return i_address; + } + //target is another proc so need to use the appropriate MFSI port + else if( i_target->getAttr<TARGETING::ATTR_TYPE>() == TARGETING::TYPE_PROC ) + { + //@todo - use attribute to figure out which MFSI_PORT_x to use + TRACFCOMP( g_trac_fsi, "FsiDD::genFullFsiAddr> MFSI targets aren't supported yet : i_target=%llX", target_to_uint64(i_target) ); + assert(0); + } + //target is a centaur so need to use the appropriate cMFSI port + else if( i_target->getAttr<TARGETING::ATTR_TYPE>() == TARGETING::TYPE_MEMBUF ) + { + //@todo - use attribute to figure out which CMFSI_PORT_x to use + TRACFCOMP( g_trac_fsi, "FsiDD::genFullFsiAddr> CMFSI targets aren't supported yet : i_target=%llX", target_to_uint64(i_target) ); + assert(0); + } + else + { + TRACFCOMP( g_trac_fsi, "FsiDD::genFullFsiAddr> Unknown target type : i_target=%llX", target_to_uint64(i_target) ); + assert(0); + } +} + +/** + * @brief Generate a valid SCOM address to access the OPB, this will + * choosing the correct master + */ +uint64_t FsiDD::genOpbScomAddr(uint64_t i_opbOffset) +{ + //@todo: handle redundant FSI ports, always using zero for now + uint64_t opbaddr = FSI2OPB_OFFSET_0 | i_opbOffset; + return opbaddr; +} + +//@todo - IGNORE THIS FOR NOW +/** + * @brief Initializes the FSI bus for the given slave + */ +errlHndl_t FsiDD::initSlave(TARGETING::Target* i_target) +{ + errlHndl_t l_err = NULL; + size_t regsize = sizeof(uint32_t); + + do { + uint64_t masteroffset = MFSI_CONTROL_REG; //call some function to get this + + + //figure out which port this target is on + uint64_t port = 0; //getAttr<TYPE_FSI_SLAVE_PORT>? + uint32_t portbit = 0x80000000 >> port; + + //see if the slave is present + uint32_t readbuf = 0; + l_err = read( masteroffset|0x018, regsize, &readbuf ); + if( l_err ) { break; } + + if( !(readbuf & portbit) ) + { + TRACFCOMP( g_trac_fsi, "FsiDD::initSlave> Target %llX is not present : Reg 0x018=%lX", target_to_uint64(i_target), readbuf ); + //@todo - create error log for non-present slave? + } + + //choose clock ratio,delay selection + l_err = write( masteroffset|0x008, regsize, &portbit ); + if( l_err ) { break; } + + //enable the port + l_err = write( masteroffset|0x018, regsize, &portbit ); + if( l_err ) { break; } + + //reset the port + l_err = write( masteroffset|0x008, regsize, &portbit ); + if( l_err ) { break; } + + } while(0); + + return l_err; +} + +//@todo - IGNORE THIS FOR NOW +/** + * @brief Initializes the FSI master control registers + * + * @return errlHndl_t NULL on success + */ +errlHndl_t FsiDD::initMaster() +{ + errlHndl_t l_err = NULL; + + do { + //@todo - loop to hit MFSI_CONTROL_REG and CMFSI_CONTROL_REG range? + + // Mode Register + // 1: Enable hardware error recovery + //uint32_t clock_ratio0 = 100; //iv_target->getAttr<TARGETING::>() + //uint32_t mode_reg = 0; + + } while(0); + + return l_err; +} diff --git a/src/usr/fsi/fsidd.H b/src/usr/fsi/fsidd.H new file mode 100644 index 000000000..f577f6571 --- /dev/null +++ b/src/usr/fsi/fsidd.H @@ -0,0 +1,315 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/fsi/fsidd.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2011 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +#ifndef __FSI_FSIDD_H +#define __FSI_FSIDD_H + +#include <sys/sync.h> +#include <util/locked/list.H> +#include <list> + +/** @file fsidd.H + * @brief Provides the interfaces to the FSI Device Driver + */ + +class FsiDD; + +// Predeclare these so we can make them friends +namespace FSI { +errlHndl_t ddRead(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args); +errlHndl_t ddWrite(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args); +} + +/** + * Class to handle the FSI Master operations + * there will be one instance per master + */ +class FsiDD +{ + public: + /** + * @brief Performs an FSI Read Operation + * + * @param[in] i_address Address to read (relative to FSI Master chip) + * @param[in] io_buflen Number of bytes to access, returns + * number of bytes actually read + * @param[out] o_buffer Destination buffer for data + * + * @return errlHndl_t NULL on success + */ + errlHndl_t read(uint64_t i_address, + size_t& io_buflen, + uint32_t* o_buffer); + + /** + * @brief Performs an FSI Write Operation + * + * @param[in] i_address Address to read (relative to FSI Master chip) + * @param[in] io_buflen Number of bytes to access, returns + * number of bytes actually written + * @param[out] o_buffer Source buffer for data + * + * @return errlHndl_t NULL on success + */ + errlHndl_t write(uint64_t i_address, + size_t& io_buflen, + uint32_t* i_buffer); + + /** + * @brief Initializes the FSI bus for the given slave + * + * @param[in] Chip target of FSI-slave + * + * @return errlHndl_t NULL on success + */ + errlHndl_t initSlave(TARGETING::Target* i_target); + + protected: + /** + * @brief Constructor + * + * @param[in] XSCOM target associated with this FSI Master instance + */ + FsiDD( TARGETING::Target* i_target ); + + + /** + * @brief Destructor + */ + ~FsiDD(); + + /** + * @brief Verify Request is in appropriate address range + * + * @param[in] i_address Starting address (relative to FSI Device) + * @param[in] i_length Number of bytes being accessed + * + * @return errlHndl_t NULL on success + */ + errlHndl_t verifyAddressRange(uint64_t i_address, + size_t i_length); + + /** + * @brief Analyze error bits and recover hardware as needed + * + * @param[in] i_target Target of SCOM operation + * @param[in] i_address Address of FSI register being accessed + * @param[in] i_opbStatReg OPB Status bits (OPB_REG_STAT[0:31]) + * + * @return errlHndl_t NULL on success + */ + errlHndl_t handleOpbErrors(TARGETING::Target* i_target, + uint64_t i_address, + uint32_t i_opbStatReg); + + /** + * @brief Poll for completion of a FSI operation, return data on read + * + * @param[in] i_address Address of FSI register being accessed + * @param[out] o_readData buffer to copy read data into, set to NULL + * for write operations + * + * @return errlHndl_t NULL on success + */ + errlHndl_t pollForComplete(uint64_t i_address, + uint32_t* o_readData); + + /** + * @brief Generate a complete FSI address based on the target and the + * FSI offset within that target + * + * @param[in] i_target Target of FSI-slave, or master for control regs + * @param[in] i_address Address of FSI register relative to slave space + * + * @return uint64_t Fully qualified FSI address + */ + uint64_t genFullFsiAddr(TARGETING::Target* i_target, + uint64_t i_address); + + /** + * @brief Generate a valid SCOM address to access the OPB, this will + * choosing the correct master + * + * @param[in] i_address Address of OPB register relative to OPB space, + * e.g. OPB_REG_CMD + * + * @return uint64_t Fully qualified OPB SCOM address + */ + uint64_t genOpbScomAddr(uint64_t i_opbOffset); + + /** + * @brief Initializes the FSI master control registers + * + * @return errlHndl_t NULL on success + */ + errlHndl_t initMaster(); + + /** + * FSI Address Space + */ + enum { + // Local FSI Space + CFAM_CONFIG_TABLE = 0x000000, /**< Configuration Table of CFAM */ + PEEK_TABLE = 0x000400, /**< Peek Table */ + FSI_SLAVE_REG = 0x000800, /**< FSI Slave Register */ + FSI_SHIFT_ENGINE = 0x000C00, /**< FSI Shift Engine (SCAN) */ + FSI2PIB_ENGINE = 0x001000, /**< FSI2PIB Engine (SCOM) */ + FSI_SCRATCHPAD = 0x001400, /**< FSI Scratchpad */ + FSI_I2C_MASTER = 0x001800, /**< FSI I2C-Master */ + FSI_GEMINI_MBOX = 0x002800, /**< FSI Gemini Mailbox with FSI GPx Registers */ + + // Master control registers + CMFSI_CONTROL_REG = 0x003000, /**< cMFSI Control Register */ + MFSI_CONTROL_REG = 0x003400, /**< MFSI Control Register */ + CONTROL_REG_MASK = 0x003400, /**< Mask to look for a valid control register */ + + // cMFSI Ports (32KB each) + CMFSI_PORT_0 = 0x040000, /**< cMFSI port 0 */ + CMFSI_PORT_1 = 0x048000, /**< cMFSI port 1 */ + CMFSI_PORT_2 = 0x050000, /**< cMFSI port 2 */ + CMFSI_PORT_3 = 0x058000, /**< cMFSI port 3 */ + CMFSI_PORT_4 = 0x060000, /**< cMFSI port 4 */ + CMFSI_PORT_5 = 0x068000, /**< cMFSI port 5 */ + CMFSI_PORT_6 = 0x070000, /**< cMFSI port 6 */ + CMFSI_PORT_7 = 0x078000, /**< cMFSI port 7 */ + CMFSI_PORT_MASK = 0x078000, /**< Mask to look for a valid cMFSI port */ + + // Offsets to cascaded slaves within a cMFSI port + CMFSI_SLAVE_0 = 0x000000, /**< cMFSI - Slave 0 */ + CMFSI_SLAVE_1 = 0x002000, /**< cMFSI - Slave 1 */ + CMFSI_SLAVE_2 = 0x004000, /**< cMFSI - Slave 2 */ + CMFSI_SLAVE_3 = 0x006000, /**< cMFSI - Slave 3 */ + + // MFSI Ports (512KB each) + MFSI_PORT_0 = 0x080000, /**< MFSI port 0 */ + MFSI_PORT_1 = 0x100000, /**< MFSI port 1 */ + MFSI_PORT_2 = 0x180000, /**< MFSI port 2 */ + MFSI_PORT_3 = 0x200000, /**< MFSI port 3 */ + MFSI_PORT_4 = 0x280000, /**< MFSI port 4 */ + MFSI_PORT_5 = 0x300000, /**< MFSI port 5 */ + MFSI_PORT_6 = 0x380000, /**< MFSI port 6 */ + MFSI_PORT_7 = 0x400000, /**< MFSI port 7 */ + MFSI_PORT_MASK = 0x780000, /**< Mask to look for a valid MFSI port */ + }; + + + /** + * PIB2OPB Registers + */ + enum { + OPB_REG_CMD = 0x0000, /**< Command Register */ + OPB_REG_STAT = 0x0001, /**< Status Register */ + OPB_REG_LSTAT = 0x0002, /**< Locked Status */ + // no reg for 0x0003 + OPB_REG_RES = 0x0004, /**< Reset */ + OPB_REG_CRSIC = 0x0005, /**< cMFSI Remote Slave Interrupt Condition */ + OPB_REG_CRSIM = 0x0006, /**< cMFSI Remote Slave Interrupt Mask */ + OPB_REG_CRSIS = 0x0007, /**< cMFSI Remote Slave Interrupt Status */ + OPB_REG_RSIC = 0x0008, /**< MFSI Remote Slave Interrupt Condition */ + OPB_REG_RSIM = 0x0009, /**< MFSI Remote Slave Interrupt Mask */ + OPB_REG_RSIS = 0x000A, /**< MFSI Remote Slave Interrupt Status */ + + // Offsets for cMFSI + FSI2OPB_OFFSET_0 = 0x00020010, /**< cMFSI 0 and MFSI */ + FSI2OPB_OFFSET_1 = 0x00030000, /**< cMFSI 1 */ + + // Bit masks + OPB_STAT_BUSY = 0x00010000, /**< Bit 15 is the Busy bit */ + OPB_STAT_READ_VALID = 0x00020000, /**< Bit 15 is the Busy bit */ + OPB_STAT_ERR_OPB = 0x09F00000, /**< 4,7-11 are OPB errors */ + OPB_STAT_ERR_CMFSI = 0x0000FC00, /**< 16-21 are cMFSI errors */ + OPB_STAT_ERR_MFSI = 0x000000FC, /**< 24-29 are MFSI errors */ + OPB_STAT_ERR_ANY = OPB_STAT_ERR_OPB|OPB_STAT_ERR_CMFSI|OPB_STAT_ERR_MFSI, + + MAX_OPB_ATTEMPTS = 10, /**< Maximum number of attempts for OPB reg ops */ + }; + + + /** + * Global mutex @todo - make this per target for efficiency + */ + mutex_t iv_fsiMutex; + + /** + * Associated target of FSI Master, i.e. XSCOM target + */ + TARGETING::Target* iv_target; + + private: + + // let my testcase poke around + friend class FsiDDTest; + + + friend errlHndl_t FSI::ddRead(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args); + friend errlHndl_t FSI::ddWrite(DeviceFW::OperationType i_opType, + TARGETING::Target* i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args); + + /********** + * STATIC + **********/ + + /** + * List of available instances + */ + static std::vector<FsiDD*> cv_instances; + + /** + * Global mutex to handle updates to gv_instances + */ + static mutex_t cv_mux; + + + public: + /** + * @brief Select the instance for the given target + * + * @param[in] i_target TARGETING::Target* i_target + * + * @return FsiDD* Pointer to instance that matches the target + */ + static FsiDD* getInstance( TARGETING::Target* i_target ); + + +}; + + +#endif diff --git a/src/usr/fsi/makefile b/src/usr/fsi/makefile new file mode 100644 index 000000000..94c02b826 --- /dev/null +++ b/src/usr/fsi/makefile @@ -0,0 +1,30 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/fsi/makefile $ +# +# IBM CONFIDENTIAL +# +# COPYRIGHT International Business Machines Corp. 2011 +# +# p1 +# +# Object Code Only (OCO) source materials +# Licensed Internal Code Source Materials +# IBM HostBoot Licensed Internal Code +# +# The source code for this program is not published or other- +# wise divested of its trade secrets, irrespective of what has +# been deposited with the U.S. Copyright Office. +# +# Origin: 30 +# +# IBM_PROLOG_END +ROOTPATH = ../../.. +MODULE = fsi + +OBJS = fsidd.o + +SUBDIRS = test.d + +include ${ROOTPATH}/config.mk diff --git a/src/usr/fsi/test/fsiddtest.H b/src/usr/fsi/test/fsiddtest.H new file mode 100644 index 000000000..2b87c1dd0 --- /dev/null +++ b/src/usr/fsi/test/fsiddtest.H @@ -0,0 +1,195 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/fsi/test/fsiddtest.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2011 +// +// p1 +// +// Object Code Only (OCO) source materials +// Licensed Internal Code Source Materials +// IBM HostBoot Licensed Internal Code +// +// The source code for this program is not published or other- +// wise divested of its trade secrets, irrespective of what has +// been deposited with the U.S. Copyright Office. +// +// Origin: 30 +// +// IBM_PROLOG_END +#ifndef __FSIDDTEST_H +#define __FSIDDTEST_H + +/** + * @file fsiddtest.H + * + * @brief Test cases for FSI Device Driver +*/ + +#include <cxxtest/TestSuite.H> +#include <errl/errlmanager.H> +#include <errl/errlentry.H> +#include <errl/errltypes.H> +#include <limits.h> +#include <devicefw/driverif.H> +#include "../fsidd.H" + +extern trace_desc_t* g_trac_fsi; + + +class FsiDDTest : public CxxTest::TestSuite +{ + public: + + /** + * @brief FSI DD test - Read/Write + * Perform basic read/write operations + */ + void test_readWrite(void) + { + return; //@fixme - enable when simics fixes are integrated + TRACFCOMP( g_trac_fsi, "FsiDDTest::test_readWrite> Start" ); + uint64_t fails = 0; + uint64_t total = 0; + errlHndl_t l_err = NULL; + + // scratch data to use + struct { + uint64_t addr; + uint32_t data; + bool writeable; + } test_data[] = { + { FsiDD::MFSI_CONTROL_REG | 0x000000, 0x11111100, true }, //Mode Reg + //{ 0x000010, 0x22222222 }, + //{ 0x000020, 0x33333333 }, + { FsiDD::CMFSI_CONTROL_REG | 0x000074, 0x91010800, false }, //version + { FsiDD::MFSI_CONTROL_REG | 0x000074, 0x91010800, false }, //version + //{ FsiDD::MFSI_CONTROL_REG | 0x000018, 0x44444444, true }, + + { FsiDD::MFSI_PORT_0 | 0x000000, 0x91010800, false }, //Slave Config Table + }; + const uint64_t NUM_ADDRS = sizeof(test_data)/sizeof(test_data[0]); + + // allocate some space to play with + uint32_t read_data[NUM_ADDRS]; + size_t op_size = sizeof(uint32_t); + + // target the master processor + TARGETING::TargetService& targetService = TARGETING::targetService(); + TARGETING::Target* fsi_target = NULL; + targetService.masterProcChipTargetHandle( fsi_target ); + + // read address X,Y,Z + for( uint64_t x = 0; x < NUM_ADDRS; x++ ) + { + total++; + TRACFCOMP( g_trac_fsi, "FsiDDTest::test_readWrite> Reading %llX", test_data[x].addr ); + l_err = DeviceFW::deviceOp( DeviceFW::READ, + fsi_target, + &(read_data[x]), + op_size, + DEVICE_FSI_ADDRESS(test_data[x].addr) ); + if( l_err ) + { + TRACFCOMP(g_trac_fsi, "FsiDDTest::test_readWrite> Error from device : addr=0x%X, RC=%X", test_data[x].addr, l_err->reasonCode() ); + TS_FAIL( "FsiDDTest::test_readWrite> ERROR : Unexpected error log from read1" ); + fails++; + errlCommit(l_err); + delete l_err; + } + + TRACFCOMP( g_trac_fsi, "0x%X = 0x%X", test_data[x].addr, read_data[x] ); + + //@todo - check op_size + } + + // write X=A, Y=B, Z=C + for( uint64_t x = 0; x < NUM_ADDRS; x++ ) + { + total++; + l_err = DeviceFW::deviceOp( DeviceFW::WRITE, + fsi_target, + &(test_data[x].data), + op_size, + DEVICE_FSI_ADDRESS(test_data[x].addr) ); + if( l_err ) + { + TRACFCOMP(g_trac_fsi, "FsiDDTest::test_readWrite> Error from device : addr=0x%X, RC=%X", test_data[x].addr, l_err->reasonCode() ); + TS_FAIL( "FsiDDTest::test_readWrite> ERROR : Unexpected error log from write1" ); + fails++; + errlCommit(l_err); + delete l_err; + } + + //@todo - check op_size + } + + // read X,Y,Z + for( uint64_t x = 0; x < NUM_ADDRS; x++ ) + { + total++; + l_err = DeviceFW::deviceOp( DeviceFW::READ, + fsi_target, + &(read_data[x]), + op_size, + DEVICE_FSI_ADDRESS(test_data[x].addr) ); + if( l_err ) + { + TRACFCOMP(g_trac_fsi, "FsiDDTest::test_readWrite> Error from device : addr=0x%X, RC=%X", test_data[x].addr, l_err->reasonCode() ); + TS_FAIL( "FsiDDTest::test_readWrite> ERROR : Unexpected error log from read2" ); + fails++; + errlCommit(l_err); + delete l_err; + } + + //@todo - check op_size + } + + // verify X==A, Y==B, Z==C + for( uint64_t x = 0; x < NUM_ADDRS; x++ ) + { + total++; + if( read_data[x] != test_data[x].data ) + { + TRACFCOMP(g_trac_fsi, "FsiDDTest::test_readWrite> Data mismatch : addr=0x%X, exp=0x%X, act=0x%X", test_data[x].addr, test_data[x].data, read_data[x] ); + TS_FAIL( "FsiDDTest::test_readWrite> ERROR : Data mismatch" ); + fails++; + } + } + + //@todo - repeat for each address space + + TRACFCOMP( g_trac_fsi, "FsiDDTest::test_readWrite> %d/%d fails", fails, total ); + }; + + /** + * @brief FSI DD test - verifyAddressRange + * Test output of verifyAddressRange + */ + void test_verifyAddressRange(void) + { + TRACFCOMP( g_trac_fsi, "FsiDDTest::verifyAddressRange> Start" ); + uint64_t fails = 0; + uint64_t total = 0; + + //@todo + // values to try: + // -address 0 + // -valid address at the beginning (if not 0) + // -valid address and size in the middle + // -valid address and size that hit the end of the range + // -valid address and size that exceeds range + // -address beyond range + // -address before beginning (if not 0) + + + TRACFCOMP( g_trac_fsi, "FsiDDTest::verifyAddressRange> %d/%d fails", fails, total ); + }; + +}; + + +#endif diff --git a/src/usr/fsi/test/makefile b/src/usr/fsi/test/makefile new file mode 100644 index 000000000..4c3fd7e8d --- /dev/null +++ b/src/usr/fsi/test/makefile @@ -0,0 +1,29 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/fsi/test/makefile $ +# +# IBM CONFIDENTIAL +# +# COPYRIGHT International Business Machines Corp. 2011 +# +# p1 +# +# Object Code Only (OCO) source materials +# Licensed Internal Code Source Materials +# IBM HostBoot Licensed Internal Code +# +# The source code for this program is not published or other- +# wise divested of its trade secrets, irrespective of what has +# been deposited with the U.S. Copyright Office. +# +# Origin: 30 +# +# IBM_PROLOG_END +ROOTPATH = ../../../.. + +MODULE = testfsi +TESTS = *.H + + +include ${ROOTPATH}/config.mk diff --git a/src/usr/makefile b/src/usr/makefile index c1ba0e5e6..55dcb704c 100644 --- a/src/usr/makefile +++ b/src/usr/makefile @@ -25,7 +25,7 @@ ROOTPATH = ../.. OBJS = module_init.o SUBDIRS = example.d trace.d cxxtest.d testcore.d errl.d devicefw.d \ - scom.d xscom.d targeting.d initservice.d hwpf.d \ - ecmddatabuffer.d isteps.d pnor.d vfs.d i2c.d + scom.d xscom.d targeting.d initservice.d hwpf.d \ + ecmddatabuffer.d isteps.d pnor.d i2c.d vfs.d fsi.d include ${ROOTPATH}/config.mk diff --git a/src/usr/xscom/xscom.C b/src/usr/xscom/xscom.C index 179e8dc31..c893c5c09 100644 --- a/src/usr/xscom/xscom.C +++ b/src/usr/xscom/xscom.C @@ -426,6 +426,9 @@ errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, mutex_t* l_XSComMutex; uint64_t l_addr = va_arg(i_args,uint64_t); + //@todo - Override the target to be the master sentinel + i_target = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + // Retry loop bool l_retry = false; uint8_t l_retryCtr = 0; @@ -542,28 +545,28 @@ errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, // Done, un-pin task_affinity_unpin(); - TRACFCOMP(g_trac_xscom, "xscomPerformOp: OpType 0x%llX, Address 0x%llX, MMIO Address 0x%llX", + TRACFCOMP(g_trac_xscom, "xscomPerformOp: OpType 0x%.16llX, Address 0x%llX, MMIO Address 0x%llX", static_cast<uint64_t>(i_opType), l_addr, static_cast<uint64_t>(l_mmioAddr)); - TRACFCOMP(g_trac_xscom, "xscomPerformOp: l_offset 0x%llX; VirtAddr %p; l_virtAddr+l_offset %p", + TRACFCOMP(g_trac_xscom, "xscomPerformOp: l_offset 0x%.16llX; VirtAddr %p; l_virtAddr+l_offset %p", l_offset, l_virtAddr, l_virtAddr + l_offset); if (i_opType == DeviceFW::READ) { - TRACFCOMP(g_trac_xscom, "xscomPerformOp: Read data: %llx", l_data); } + TRACFCOMP(g_trac_xscom, "xscomPerformOp: Read data: %.16llx", l_data); } else { - TRACFCOMP(g_trac_xscom, "xscomPerformOp: Write data: %llx", l_data); + TRACFCOMP(g_trac_xscom, "xscomPerformOp: Write data: %.16llx", l_data); } // Handle error if (l_hmer.mXSComStatus != HMER::XSCOM_GOOD) { uint64_t l_hmerVal = l_hmer; - TRACFCOMP(g_trac_xscom, ERR_MRK "XSCOM status error HMER: %llx, XSComStatus %llx", + TRACFCOMP(g_trac_xscom, ERR_MRK "XSCOM status error HMER: %.16llx, XSComStatus %llx", l_hmerVal, l_hmer.mXSComStatus); /*@ * @errortype |