summaryrefslogtreecommitdiffstats
path: root/src/usr/xscom
diff options
context:
space:
mode:
authorThi Tran <thi@us.ibm.com>2011-06-11 15:49:38 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2011-06-17 13:57:06 -0500
commitcdaabfdbec148d9ce85f422507c596a8ae6ced08 (patch)
treebc8edbc339263a4c4b9c71f3298ff6043af1decb /src/usr/xscom
parentb301c77be3ab2b9c097f2e3df47e96a907221ed9 (diff)
downloadtalos-hostboot-cdaabfdbec148d9ce85f422507c596a8ae6ced08.tar.gz
talos-hostboot-cdaabfdbec148d9ce85f422507c596a8ae6ced08.zip
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 <mjjones@us.ibm.com> Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr/xscom')
-rw-r--r--src/usr/xscom/makefile8
-rw-r--r--src/usr/xscom/test/makefile6
-rw-r--r--src/usr/xscom/test/xscomtest.H113
-rw-r--r--src/usr/xscom/xscom.C309
-rw-r--r--src/usr/xscom/xscom.H233
5 files changed, 669 insertions, 0 deletions
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 <cxxtest/TestSuite.H>
+#include <errl/errlmanager.H>
+#include <errl/errlentry.H>
+#include <errl/errltypes.H>
+#include <devicefw/userif.H>
+
+// 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 <sys/mmio.h>
+#include <sys/task.h>
+#include <sys/sync.h>
+#include <string.h>
+#include <devicefw/driverif.H>
+#include <trace/interface.H>
+#include <errl/errlentry.H>
+#include <errl/errlmanager.H>
+#include <xscom/xscomreasoncodes.H>
+#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<uint64_t*>
+ (mmio_map(reinterpret_cast<void*>(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<uint64_t>(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 <stdint.h>
+#include <limits.h>
+
+/**
+ * @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
OpenPOWER on IntegriCloud