From cdaabfdbec148d9ce85f422507c596a8ae6ced08 Mon Sep 17 00:00:00 2001 From: Thi Tran Date: Sat, 11 Jun 2011 15:49:38 -0500 Subject: Initial XSCOM delivery Change-Id: I80278bf2e03b4e6403d9a36b8782b225dba29fe3 Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/144 Tested-by: Jenkins Server Reviewed-by: MIKE J. JONES Reviewed-by: A. Patrick Williams III --- src/usr/xscom/makefile | 8 ++ src/usr/xscom/test/makefile | 6 + src/usr/xscom/test/xscomtest.H | 113 +++++++++++++++ src/usr/xscom/xscom.C | 309 +++++++++++++++++++++++++++++++++++++++++ src/usr/xscom/xscom.H | 233 +++++++++++++++++++++++++++++++ 5 files changed, 669 insertions(+) create mode 100644 src/usr/xscom/makefile create mode 100644 src/usr/xscom/test/makefile create mode 100644 src/usr/xscom/test/xscomtest.H create mode 100644 src/usr/xscom/xscom.C create mode 100644 src/usr/xscom/xscom.H (limited to 'src/usr/xscom') diff --git a/src/usr/xscom/makefile b/src/usr/xscom/makefile new file mode 100644 index 000000000..557920fdc --- /dev/null +++ b/src/usr/xscom/makefile @@ -0,0 +1,8 @@ +ROOTPATH = ../../.. +MODULE = xscom + +OBJS = xscom.o + +SUBDIRS = test.d + +include ${ROOTPATH}/config.mk diff --git a/src/usr/xscom/test/makefile b/src/usr/xscom/test/makefile new file mode 100644 index 000000000..57e9ca832 --- /dev/null +++ b/src/usr/xscom/test/makefile @@ -0,0 +1,6 @@ +ROOTPATH = ../../../.. + +MODULE = testxscom +TESTS = *.H + +include ${ROOTPATH}/config.mk diff --git a/src/usr/xscom/test/xscomtest.H b/src/usr/xscom/test/xscomtest.H new file mode 100644 index 000000000..4351487e9 --- /dev/null +++ b/src/usr/xscom/test/xscomtest.H @@ -0,0 +1,113 @@ +#ifndef __XCOMTEST_H +#define __XCOMTEST_H + +/** + * @file xscomtest.H + * + * @brief Test case for XSCOM code +*/ + +#include +#include +#include +#include +#include + +// Address and data to read/write +struct testXscomAddrData +{ + uint32_t addr; + uint64_t data; +}; + +// Test table values +//@todo - Select scratch register so chip doesn't get messed-up +const testXscomAddrData g_xscomAddrTable[] = +{ + {0x08030007, 0x8000000000000001}, + {0x08010587, 0x9000000000000003}, + +}; +const uint32_t g_xscomAddrTableSz = + sizeof(g_xscomAddrTable)/sizeof(testXscomAddrData); + + +class XscomTest: public CxxTest::TestSuite +{ +public: + + /** + * @brief XSCOM test #1 + * Write value and read back to verify + */ + void testXscom1(void) + { + + //@todo - Replace printk with traces + + DeviceFW::TargetHandle_t l_testTarget = 0; //@todo - Fix this + size_t l_size = sizeof(uint64_t); + + // Loop thru table + for( uint32_t l_num=0; l_num < g_xscomAddrTableSz; l_num++) + { + testXscomAddrData l_testEntry = g_xscomAddrTable[l_num]; + + // Perform XSComOM read + uint64_t l_data = 0; + errlHndl_t l_err = deviceRead(l_testTarget, + &l_data, + l_size, + DEVICE_SCOM_ADDRESS(l_testEntry.addr)); + if (l_err) + { + printk("\ntestXscom1: XSCom read: deviceRead() fails (1) !\n"); + } + else + { + printk("\ntestXscom1: XSCom read (1) Address 0x%.8X, Data %llx\n\n\n", + l_testEntry.addr, + (long long unsigned)l_data); + } + + // Perform an XSCom write + l_err = deviceWrite(l_testTarget, + &l_testEntry.data, + l_size, + DeviceFW::SCOM, + l_testEntry.addr); + + if (l_err) + { + printk("\ntestXscom1: XSCom write: deviceWrite() fails!\n"); + break; + } + else + { + printk("\ntestXscom1: XSCom write Address 0x%.8X, Data %llx\n", + l_testEntry.addr, + (long long unsigned)l_testEntry.data); + } + + // Read back + l_data = 0; + l_err = deviceRead(l_testTarget, + &l_data, + l_size, + DEVICE_SCOM_ADDRESS(l_testEntry.addr)); + if (l_err) + { + printk("\ntestXscom1: XSCom read: deviceRead() fails (2) !\n"); + } + else + { + printk("\ntestXscom1: XSCom read (2) Address 0x%.8X, Data %llx\n\n\n", + l_testEntry.addr, + (long long unsigned)l_data); + } + } + return; + } +}; + +#endif diff --git a/src/usr/xscom/xscom.C b/src/usr/xscom/xscom.C new file mode 100644 index 000000000..b07eb5747 --- /dev/null +++ b/src/usr/xscom/xscom.C @@ -0,0 +1,309 @@ +/** + * @file xscom.C + * + * @brief Implementation of SCOM operations + */ + +/*****************************************************************************/ +// I n c l u d e s +/*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xscom.H" + +// Trace definition +trace_desc_t* g_trac_xscom = NULL; +TRAC_INIT(&g_trac_xscom, "XSCOM", 4096); + +namespace XSCOM +{ + +// Register XSCcom access functions to DD framework +DEVICE_REGISTER_ROUTE(DeviceFW::WILDCARD, + DeviceFW::XSCOM, + DeviceFW::PROCESSOR, + xscomPerformOp); + +/** + * @brief Internal routine that reset XSCOM status bits + * of HMER register before an XSCOM operation + * + * @return None + */ +void resetHMERStatus() +{ + + // mtspr on the HMER is an AND write. + // This is not set the bits value to 1s, it's clearing + // the xscom status bits while leaving the rest of the bits + // in the register alone. + HMER hmer(-1); + + hmer.mXSComDone = 0; + hmer.mXSComFail = 0; + hmer.mXSComStatus = 0; + // This call always returns 0, no checking needed + mmio_hmer_write(hmer); + return; +} + +/** + * @brief Internal routine that monitor XSCOM Fail and XSCOM Done + * status bits of HMER register + * + * @return None + */ +HMER waitForHMERStatus() +{ + HMER hmer; + + do + { + hmer = mmio_hmer_read(); + } + while(!hmer.mXSComFail && !hmer.mXSComDone); + return hmer; +} + + +/** + * @brief Internal routine that checks to see if retry is + * possible on an XSCOM error + * + * @return true if retry is possible; false otherwise. + */ +bool XSComRetry(const HMER i_hmer) +{ + bool l_retry = false; + switch (i_hmer.mXSComStatus) + { + // Should retry if parity or timeout error. + case HMER::XSCOM_PARITY_ERROR: + case HMER::XSCOM_TIMEOUT: + l_retry = true; + break; + default: + break; + } + return l_retry; +} + +/** + * @brief Internal routine that verifies the validity of input parameters + * for an XSCOM access. + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target XSCom target + * @param[in/out] i_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] i_buflen Input: size of io_buffer (in bytes) + * Output: + * Read: Size of output data + * Write: Size of data written + * @param[in] i_args This is an argument list for DD framework. + * In this function, there's only one argument, + * which is the MMIO XSCom address + * @return errlHndl_t + */ +errlHndl_t xscomOpSanityCheck(const DeviceFW::OperationType i_opType, + const DeviceFW::TargetHandle_t i_target, + const void* i_buffer, + const size_t& i_buflen, + const va_list i_args) +{ + errlHndl_t l_err = NULL; + + do + { + // Verify data buffer + if ( (i_buflen < XSCOM_BUFFER_SIZE) || + (i_buffer == NULL) ) + { + /*@ + * @errortype + * @moduleid XSCOM_SANITY_CHECK + * @reasoncode XSCOM_INVALID_DATA_BUFFER + * @userdata1 Buffer size + * @userdata2 XSCom address + * @devdesc XSCOM buffer size < 8 bytes or NULL data buffer + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + XSCOM_SANITY_CHECK, + XSCOM_INVALID_DATA_BUFFER, + i_buflen, + va_arg(i_args,uint64_t)); + break; + } + + // Verify OP type + if ( (i_opType != DeviceFW::READ) && + (i_opType != DeviceFW::WRITE) ) + { + /*@ + * @errortype + * @moduleid XSCOM_SANITY_CHECK + * @reasoncode XSCOM_INVALID_OP_TYPE + * @userdata1 Operation type + * @userdata2 XSCom address + * @devdesc XSCOM invalid operation type + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + XSCOM_SANITY_CHECK, + XSCOM_INVALID_OP_TYPE, + i_opType, + va_arg(i_args,uint64_t)); + break; + } + + + } while(0); + + return l_err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, + DeviceFW::TargetHandle_t i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args) +{ + errlHndl_t l_err = NULL; + HMER l_hmer; + mutex_t* l_XSComMutex; + + // Retry loop + bool l_retry = false; + uint8_t l_retryCtr = 0; + do + { + // XSCOM operation sanity check + l_err = xscomOpSanityCheck(i_opType, i_target, io_buffer, + io_buflen, i_args); + if (l_err) + { + break; + } + + // Set to buffer len to 0 until successfully access + io_buflen = 0; + + // Setup the address + // @todo: Hard code chipId and Base address for now + XSComChipId_t l_chipId = 0; + XSComBase_t l_XSComBaseAddr = 0x300000000000; // Node0,chip0, Simics map + XSComP8Address l_mmioAddr(va_arg(i_args,uint64_t), l_chipId, + l_XSComBaseAddr); + + // Re-init l_retry for loop + l_retry = false; + + // Pin this thread to current CPU + task_affinity_pin(); + + // Lock other XSCom in this same thread from running + l_XSComMutex = mmio_xscom_mutex(); + mutex_lock(l_XSComMutex); + + // Single XSCom read + // Keep MMIO reading until XSCOM successfully done or error + do + { + // Reset status + resetHMERStatus(); + + // Calculate MMIO addr + uint64_t l_page = l_mmioAddr.page(); + uint64_t l_offset_64 = (l_mmioAddr.offset()/sizeof(uint64_t)); + uint64_t* l_virtAddr = static_cast + (mmio_map(reinterpret_cast(l_page), 1)); + + // The dereferencing should handle Cache inhibited internally + // Use local variable and memcpy to avoid unaligned memory access + uint64_t l_data = 0; + if (i_opType == DeviceFW::READ) + { + l_data = *(l_virtAddr + l_offset_64); + memcpy(io_buffer, &l_data, sizeof(l_data)); + } + else + { + memcpy(&l_data, io_buffer, sizeof(l_data)); + *(l_virtAddr + l_offset_64) = l_data; + } + + // @todo - Need to un-map for now + mmio_unmap(l_virtAddr, 1); + + TRACFCOMP(g_trac_xscom, "xscomPerformOp: OpType 0x%.8X, Address %llx, Page %llx; Offset %llx; VirtAddr %llx; l_virtAddr+l_offset %llx", + i_opType, static_cast(l_mmioAddr), l_page, + l_offset_64, l_virtAddr, l_virtAddr + l_offset_64); + + // Check for error or done + l_hmer = waitForHMERStatus(); + + } while (l_hmer.mXSComStatus == HMER::XSCOM_BLOCKED); // Single read + + // Unlock + mutex_unlock(l_XSComMutex); + + // Done, un-pin + task_affinity_unpin(); + + // Handle error + if (l_hmer.mXSComStatus != HMER::XSCOM_GOOD) + { + /*@ + * @errortype + * @moduleid XSCOM_PERFORM_OP + * @reasoncode XSCOM_STATUS_ERR + * @userdata1 HMER value + * @userdata2 XSCom address + * @devdesc XSCom access error + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + XSCOM_PERFORM_OP, + XSCOM_STATUS_ERR, + l_hmer, + l_mmioAddr); + + // @todo - Collect more FFDC: HMER value, target ID, other registers? + + // Retry + if (l_retryCtr <= MAX_XSCOM_RETRY) + { + l_retryCtr++; + // If retry is possible, commit error as informational. + l_retry = XSComRetry(l_hmer); + if (l_retry == true) + { + l_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); + // Commit/delete error + errlCommit(l_err); + } + } + } + else + { + // No error, set output buffer size. + // Always 8 bytes for XSCOM, but we want to make it consistent + // with all other device drivers + io_buflen = XSCOM_BUFFER_SIZE; + } + + } while (l_retry == true); // End retry loop + + return l_err; +} + +} // end namespace diff --git a/src/usr/xscom/xscom.H b/src/usr/xscom/xscom.H new file mode 100644 index 000000000..613ab2d01 --- /dev/null +++ b/src/usr/xscom/xscom.H @@ -0,0 +1,233 @@ +#ifndef __XSCOM_H +#define __XSCOM_H + +/** @file xscom.H + * @brief Provides the interfaces to perform XSCom + */ + +#include +#include + +/** + * @brief Max number of retries if XSCOM fails with certain errors + */ +#define MAX_XSCOM_RETRY 1 + +/** + * @brief Type definition for XSCom address and Chip ID + */ +typedef uint32_t XSComAddress_t; +typedef uint16_t XSComChipId_t; +typedef uint64_t XSComBase_t; + +namespace XSCOM +{ + +#define XSCOM_BUFFER_SIZE 8 // 8 bytes + +/** + * @brief Performs an XSCom access operation + * This function performs an XSCom access 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 XSCom 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, + * which is the MMIO XSCom address + * @return errlHndl_t + */ +errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, + DeviceFW::TargetHandle_t i_target, + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args); + +/** + * @brief Abstracts HMER register of a P8/Salerno chip + */ +class HMER +{ + +public: + + /** + * @brief Constructor + */ + ALWAYS_INLINE inline HMER() + { + mRegister = 0; + } + + /** + * @brief Constructor that takes in a value + */ + explicit ALWAYS_INLINE inline HMER(uint64_t val) + { + mRegister = val; + } + + /** + * @brief Conversion operator + */ + ALWAYS_INLINE inline operator uint64_t() const + { + return mRegister; + } + + /** + * @brief Operator= + */ + ALWAYS_INLINE inline uint64_t operator = (uint64_t val) + { + return mRegister = val; + } + + /** + * @brief XSCom status values + */ + enum XSComStatus + { + XSCOM_GOOD = 0, + XSCOM_BLOCKED = 1, + XSCOM_OFFLINE = 2, + XSCOM_PARTIAL = 3, + XSCOM_BAD_ADDRESS = 4, + XSCOM_CLOCK_ERROR = 5, + XSCOM_PARITY_ERROR = 6, + XSCOM_TIMEOUT= 7, + }; + + // Layout of HMER register parts + union + { + uint64_t mRegister; + struct + { + uint64_t :8; + uint64_t mXSComFail:1; + uint64_t mXSComDone:1; + uint64_t :11; + uint64_t mXSComStatus:3; + uint64_t :40; + }; + }; +}; + +/** + * @brief This class contains necessary information to build an XSCOM + * address for a P8/Salerno chip. + */ +class XSComP8Address +{ + public: + /** + * @brief Constructor of XSComP8Address class + * + * @param[in] i_addr PCB address of the register being accessed + * + * @param[in] i_chipId Chip number of the processor chip that + * holds the register being accessed. + * + * @param[in] i_mXSComBase Base address of XSCom address range. + * + * @return None + */ + ALWAYS_INLINE inline XSComP8Address(const XSComAddress_t i_addr, + const XSComChipId_t i_chipId, + const XSComBase_t i_mXSComBase); + + /** + * @brief Conversion operator + */ + ALWAYS_INLINE inline operator uint64_t() const; + + /** + * @brief Return the page-aligned value of the address + * + * @return uint64_t + */ + ALWAYS_INLINE inline uint64_t page() const; + + /** + * @brief Return the address' byte offset from the aligned page + * + * @return uint64_t + */ + ALWAYS_INLINE inline uint64_t offset() const; + + private: + /** + * @brief Disabled copy constructor and assignment operator + */ + XSComP8Address(const XSComP8Address& i_right); + XSComP8Address& operator=(const XSComP8Address& right); + + // Layout of XSCOM address parts + union + { + uint64_t mMmioAddress; // mMmio address + struct + { + uint64_t mReserved1:18; // Not currently used (0:17) + uint64_t mBaseAddress:5; // Base address (18:22) + uint64_t mChipId:6; // Targeted chip ID (23:28) + uint64_t mSComAddrHi:27; // PCB Address High (29:55) + uint64_t mCacheLine:1; // Cached line (56) + uint64_t mSComAddrLo:4; // PCB Address low (57:60) + uint64_t mAlign:3; // Align (61:63) + } mAddressParts; + }; + +}; // End XSComP8Address class + +//----------------------------------------------------------------------- +// In-line functions +//----------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +XSComP8Address::XSComP8Address(const XSComAddress_t i_addr, + const XSComChipId_t i_chipId, + const XSComBase_t i_mXSComBase) +:mMmioAddress(i_mXSComBase) +{ + mAddressParts.mChipId = i_chipId; + mAddressParts.mSComAddrHi = i_addr >> 4; // Get 27-bits + mAddressParts.mSComAddrLo = i_addr; // Get 4 bits +} + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +XSComP8Address::operator uint64_t() const +{ + return mMmioAddress; +} + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +uint64_t XSComP8Address::page() const +{ + return ((mMmioAddress/PAGE_SIZE) * PAGE_SIZE); +} + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +uint64_t XSComP8Address::offset() const +{ + return (mMmioAddress % PAGE_SIZE); +} + +}; + +#endif -- cgit v1.2.1