diff options
author | Terry J. Opie <opiet@us.ibm.com> | 2012-01-03 15:06:17 -0600 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2012-01-31 13:06:56 -0600 |
commit | e5908dd4c110595e7c11d46009b07aa068dc19cd (patch) | |
tree | e0b7647123fbc87f4f459f909bb4f2be0da8f0dd /src | |
parent | 39b01da0087097401dea4d9a9f4e483734e751d8 (diff) | |
download | talos-hostboot-e5908dd4c110595e7c11d46009b07aa068dc19cd.tar.gz talos-hostboot-e5908dd4c110595e7c11d46009b07aa068dc19cd.zip |
SPD Device Driver
- JEDEC format for DDR3
- Testcases
Change-Id: I8a9bf87335914d5cb824adb92f11546c37e5b423
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/610
Tested-by: Jenkins Server
Reviewed-by: Van H. Lee <vanlee@us.ibm.com>
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/include/usr/devicefw/userif.H | 27 | ||||
-rw-r--r-- | src/include/usr/hbotcompid.H | 8 | ||||
-rw-r--r-- | src/include/usr/initservice/initsvcreasoncodes.H | 1 | ||||
-rw-r--r-- | src/include/usr/spd/spdenums.H | 114 | ||||
-rw-r--r-- | src/include/usr/spd/spdreasoncodes.H | 78 | ||||
-rw-r--r-- | src/makefile | 7 | ||||
-rw-r--r-- | src/usr/initservice/extinitsvc/extinitsvctasks.H | 13 | ||||
-rw-r--r-- | src/usr/makefile | 2 | ||||
-rw-r--r-- | src/usr/spd/dimmspd.dat | bin | 0 -> 256 bytes | |||
-rw-r--r-- | src/usr/spd/makefile | 36 | ||||
-rwxr-xr-x | src/usr/spd/spd.C | 901 | ||||
-rwxr-xr-x | src/usr/spd/spd.H | 261 | ||||
-rwxr-xr-x | src/usr/spd/spdDDR3.H | 125 | ||||
-rw-r--r-- | src/usr/spd/test/makefile | 28 | ||||
-rwxr-xr-x | src/usr/spd/test/spdtest.H | 413 |
15 files changed, 2001 insertions, 13 deletions
diff --git a/src/include/usr/devicefw/userif.H b/src/include/usr/devicefw/userif.H index 836e468ca..0fbe63149 100644 --- a/src/include/usr/devicefw/userif.H +++ b/src/include/usr/devicefw/userif.H @@ -21,8 +21,8 @@ // // IBM_PROLOG_END /** @file userif.H - * @brief Provides the user application interfaces for performing device - * access. + * @brief Provides the user application interfaces for performing device + * access. * * @note These interfaces should not be used directly by device drivers. * Use driverif.H instead. @@ -47,16 +47,17 @@ namespace DeviceFW MAILBOX, PRESENT, FSI, + SPD, LAST_ACCESS_TYPE, }; - - /** Construct the device addressing parameters for SCOM device ops. + + /** Construct the device addressing parameters for SCOM device ops. * @param[in] i_address - Scom address to operate on. */ #define DEVICE_SCOM_ADDRESS(i_address) \ DeviceFW::SCOM, static_cast<uint64_t>((i_address)) - + /** Construct the device addressing parameters for the PRESENT device ops. */ #define DEVICE_PRESENT_ADDRESS() \ @@ -78,15 +79,23 @@ namespace DeviceFW */ #define DEVICE_PNOR_ADDRESS( i_chip, i_addr ) \ DeviceFW::PNOR, ((static_cast<uint64_t>(i_chip)<<32)|static_cast<uint64_t>(i_addr)) - + /** Construct the device addressing parameters for FSI device ops. * @param[in] i_address - FSI address to operate on. */ #define DEVICE_FSI_ADDRESS(i_address) \ DeviceFW::FSI, static_cast<uint64_t>((i_address)) + /** + * Construct the device addressing parameters for the SPD device ops. + * @param[in] i_keyword - The keyword enumeration value to be accessed + * by the device driver. + */ + #define DEVICE_SPD_ADDRESS( i_keyword )\ + DeviceFW::SPD, static_cast<uint64_t>(( i_keyword )) + - /** + /** * @brief Perform a hardware read operation. * * @param[in] i_target Device target to operate on. @@ -112,11 +121,11 @@ namespace DeviceFW * </PRE> * */ - errlHndl_t deviceRead(TARGETING::Target* i_target, + errlHndl_t deviceRead(TARGETING::Target* i_target, void* o_buffer, size_t& io_buflen, AccessType i_accessType, ...); - /** + /** * @brief Perform a hardware write operation. * * @param[in] i_target Device target to operate on. diff --git a/src/include/usr/hbotcompid.H b/src/include/usr/hbotcompid.H index a4102001c..711670aa2 100644 --- a/src/include/usr/hbotcompid.H +++ b/src/include/usr/hbotcompid.H @@ -180,6 +180,14 @@ const compId_t INTR_COMP_ID = 0x0F00; const char INTR_COMP_NAME[] = "intr"; //@} +/** @name SPD + * EEPROM device driver component + */ +//@{ +const compId_t SPD_COMP_ID = 0x1000; +const char SPD_COMP_NAME[] = "spd"; +//@} + // ---------------------------------------------------------- // CXXTEST Unit Test, reserve compid near the end... /** @name CXXTEST diff --git a/src/include/usr/initservice/initsvcreasoncodes.H b/src/include/usr/initservice/initsvcreasoncodes.H index 6cbaa81c7..971c8c29f 100644 --- a/src/include/usr/initservice/initsvcreasoncodes.H +++ b/src/include/usr/initservice/initsvcreasoncodes.H @@ -64,6 +64,7 @@ enum InitServiceModuleID START_TARGETING_ERRL_ID = 0x18, START_I2C_ERRL_ID = 0x19, START_INTR_ERRL_ID = 0x1A, + START_SPD_ERRL_ID = 0x1B, // Internal InitService codes diff --git a/src/include/usr/spd/spdenums.H b/src/include/usr/spd/spdenums.H new file mode 100644 index 000000000..8ca1cd39f --- /dev/null +++ b/src/include/usr/spd/spdenums.H @@ -0,0 +1,114 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/include/usr/spd/spdenums.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// 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 spdenums.H + * + * @brief Enums for the SPD fields to be requested. + * + */ +#ifndef __SPDENUMS_H +#define __SPDENUMS_H + +namespace SPD +{ + +/** +* @brief Enumerations for fields that can be accessed in the SPD +*/ +enum +{ + SPD_FIRST_KEYWORD = 0x00, + CRC_EXCLUDE = SPD_FIRST_KEYWORD, + SPD_BYTES_TOTAL = 0x01, + SPD_BYTES_USED = 0x02, + SPD_MAJOR_REVISION = 0x03, + SPD_MINOR_REVISION = 0x04, + BASIC_MEMORY_TYPE = 0x05, + MODULE_TYPE = 0x06, + BANK_ADDRESS_BITS = 0x07, + DENSITY = 0x08, + ROW_ADDRESS = 0x09, + COL_ADDRESS = 0x0a, + MODULE_NOMINAL_VOLTAGE = 0x0b, + MODULE_RANKS = 0x0c, + MODULE_DRAM_WIDTH = 0x0d, + ECC_BITS = 0x0e, + MODULE_MEMORY_BUS_WIDTH = 0x0f, + FTB_DIVIDEND = 0x10, + FTB_DIVISOR = 0x11, + MTB_DIVIDEND = 0x12, + MTB_DIVISOR = 0x13, + TCK_MIN = 0x14, + CAS_LATENCIES_SUPPORTED = 0x15, + MIN_CAS_LATENCY = 0x16, + TWR_MIN = 0x17, + TRCD_MIN = 0x18, + TRRD_MIN = 0x19, + TRP_MIN = 0x1a, + TRC_MIN = 0x1b, + TRAS_MIN = 0x1c, + TRFC_MIN = 0x1d, + TWTR_MIN = 0x1e, + TRTP_MIN = 0x1f, + TFAW_MIN = 0x20, + DLL_OFF = 0x21, + RZQ_7 = 0x22, + RZQ_6 = 0x23, + PASR = 0x24, + ODTS = 0x25, + ASR = 0x26, + ETR_1X = 0x27, + ETR = 0x28, + THERMAL_SENSOR_PRESENT = 0x29, + THERMAL_SENSOR_ACCURACY = 0x2a, + SDRAM_DEVICE_TYPE_NONSTD = 0x2b, + SDRAM_DEVICE_TYPE = 0x2c, + MODULE_TYPE_SPECIFIC_SECTION = 0x2d, + MODULE_MANUFACTURER_ID = 0x2e, + MODULE_MANUFACTURING_LOCATION = 0x2f, + MODULE_MANUFACTURING_DATE = 0x30, + MODULE_SERIAL_NUMBER = 0x31, + MODULE_CRC = 0x32, + MODULE_PART_NUMBER = 0x33, + MODULE_REVISION_CODE = 0x34, + DRAM_MANUFACTURER_ID = 0x35, + MANUFACTURER_SPECIFIC_DATA = 0x36, + TCKMIN_FINE_OFFSET = 0x37, + TAAMIN_FINE_OFFSET = 0x38, + TRCDMIN_FINE_OFFSET = 0x39, + TRPMIN_FINE_OFFSET = 0x3a, + TRPCMIN_FINE_OFFSET = 0x3b, + MODULE_THERMAL_SENSOR = 0x3c, + SDRAM_OPTIONAL_FEATURES = 0x3d, + SDRAM_THERMAL_REFRESH_OPTIONS = 0x3e, + + // This keyword should be last in the list + SPD_LAST_KEYWORD, + + // Invalid Keyword + INVALID_SPD_KEYWORD = 0xFFFF, +}; + +}; // end SPD + +#endif diff --git a/src/include/usr/spd/spdreasoncodes.H b/src/include/usr/spd/spdreasoncodes.H new file mode 100644 index 000000000..d1c128100 --- /dev/null +++ b/src/include/usr/spd/spdreasoncodes.H @@ -0,0 +1,78 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/include/usr/spd/spdreasoncodes.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// 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 spdreasoncodes.H + * + * @brief Reason codes and module ids for the SPD device driver + * + */ +#ifndef __SPDREASONCODES_H +#define __SPDREASONCODES_H +// ----------------------------------------------- +// Includes +// ----------------------------------------------- +#include <hbotcompid.H> + +namespace SPD +{ + +/** +* @enum spdModuleid +* +* @brief Module Ids used in created errorlogs. Indicates which +* functions an error log was created in. +* +*/ +enum spdModuleId +{ + SPD_INVALID_MODULE = 0x00, + SPD_GET_KEYWORD_VALUE = 0x01, + SPD_GET_VALUE = 0x02, + SPD_CHECK_SIZE = 0x03, + SPD_READ_BINARY_FILE = 0x04, + SPD_WRITE_KEYWORD_VALUE = 0x05, + SPD_SPECIAL_CASES = 0x06, +}; + +/** + * @enum spdReasonCode + * + * @brief Reasoncodes used to describe what errors are being indicated. + * + */ +enum spdReasonCode +{ + SPD_INVALID_REASONCODE = SPD_COMP_ID | 0x00, // Invalid Reasoncode + SPD_INVALID_BASIC_MEMORY_TYPE = SPD_COMP_ID | 0x01, + SPD_BASIC_MEMORY_TYPE = SPD_COMP_ID | 0x02, + SPD_INVALID_SPD_KEYWORD = SPD_COMP_ID | 0x03, + SPD_MEMTYPE_NOT_SUPPORTED = SPD_COMP_ID | 0x04, + SPD_INSUFFICIENT_BUFFER_SIZE = SPD_COMP_ID | 0x05, + SPD_INSUFFICIENT_FILE_SIZE = SPD_COMP_ID | 0x06, + SPD_NOT_SUPPORTED = SPD_COMP_ID | 0x07, + SPD_KEYWORD_NOT_FOUND = SPD_COMP_ID | 0x08, +}; + +}; // end SPD + +#endif diff --git a/src/makefile b/src/makefile index 019a8ac17..f325ad502 100644 --- a/src/makefile +++ b/src/makefile @@ -48,21 +48,22 @@ BASE_MODULES = trace errl devicefw scom xscom initservice taskargs \ pnor vfs EXTENDED_MODULES = targeting ecmddatabuffer fapi hwp plat \ - extinitsvc istepdisp hwas fsi fsiscom i2c intr + extinitsvc istepdisp hwas fsi fsiscom i2c intr \ + spd DIRECT_BOOT_MODULES = example RUNTIME_MODULES = TESTCASE_MODULES = cxxtest testerrl testdevicefw testsyslib \ testscom testxscom testtargeting testinitservice testkernel \ testhwpf testecmddatabuffer initsvctasktest2 testcxxtest \ - testpnor testi2c testfsi testvfs testhwas testintr + testpnor testi2c testfsi testvfs testhwas testintr testspd RELOCATABLE_IMAGE_LDFLAGS = -pie --export-dynamic hbicore_OBJECTS = ${BASE_OBJECTS} ${DIRECT_BOOT_OBJECTS} ${STUB_TESTCASE_OBJECT} hbicore_MODULES = ${BASE_MODULES} ${DIRECT_BOOT_MODULES} hbicore_EXTENDED_MODULES = ${EXTENDED_MODULES} -hbicore_DATA_MODULES = sample.if +hbicore_DATA_MODULES = sample.if dimmspd.dat hbicore_LIDNUMBER = 80f00100 diff --git a/src/usr/initservice/extinitsvc/extinitsvctasks.H b/src/usr/initservice/extinitsvc/extinitsvctasks.H index 03bc232c8..c21f218ef 100644 --- a/src/usr/initservice/extinitsvc/extinitsvctasks.H +++ b/src/usr/initservice/extinitsvc/extinitsvctasks.H @@ -56,6 +56,19 @@ const TaskInfo g_exttaskinfolist[] = { }, /** + * @brief spd task + */ + { + "libspd.so", // taskname + NULL, // no pointer to fn + { + INIT_TASK, // task type + EXT_IMAGE, // Extended module + START_SPD_ERRL_ID // module id + } + }, + + /** * @brief fapi task, */ { diff --git a/src/usr/makefile b/src/usr/makefile index d9089f4d6..71365d0c1 100644 --- a/src/usr/makefile +++ b/src/usr/makefile @@ -27,6 +27,6 @@ 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 pnor.d i2c.d vfs.d fsi.d hwas.d fsiscom.d \ - intr.d + intr.d spd.d include ${ROOTPATH}/config.mk diff --git a/src/usr/spd/dimmspd.dat b/src/usr/spd/dimmspd.dat Binary files differnew file mode 100644 index 000000000..8abf344bd --- /dev/null +++ b/src/usr/spd/dimmspd.dat diff --git a/src/usr/spd/makefile b/src/usr/spd/makefile new file mode 100644 index 000000000..dc71eb1c8 --- /dev/null +++ b/src/usr/spd/makefile @@ -0,0 +1,36 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/spd/makefile $ +# +# IBM CONFIDENTIAL +# +# COPYRIGHT International Business Machines Corp. 2012 +# +# 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 = spd + +OBJS = spd.o + +SUBDIRS = test.d + +DIMM_SPD_DATA = dimmspd.dat +EXTRA_PARTS = $(addprefix $(IMGDIR)/, $(DIMM_SPD_DATA)) + +include ${ROOTPATH}/config.mk + +${EXTRA_PARTS}: ${IMGDIR}/% : ./% + cp -f $^ $@ diff --git a/src/usr/spd/spd.C b/src/usr/spd/spd.C new file mode 100755 index 000000000..0a1dc5bae --- /dev/null +++ b/src/usr/spd/spd.C @@ -0,0 +1,901 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/spd/spd.C $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// 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 spd.C + * + * @brief Implementation of the SPD device driver + * + */ + +// ---------------------------------------------- +// Includes +// ---------------------------------------------- +//#include <string.h> +//#include <sys/time.h> + +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <errl/errlmanager.H> +#include <targeting/targetservice.H> +#include <devicefw/driverif.H> +#include <vfs/vfs.H> +#include <spd/spdreasoncodes.H> +#include <spd/spdenums.H> + +#include "spd.H" +#include "spdDDR3.H" + +// ---------------------------------------------- +// Globals +// ---------------------------------------------- +bool g_loadModule = true; +mutex_t g_spdMutex = MUTEX_INITIALIZER; + +// ---------------------------------------------- +// Trace definitions +// ---------------------------------------------- +trace_desc_t* g_trac_spd = NULL; +TRAC_INIT( & g_trac_spd, "SPD", 4096 ); + +// ------------------------ +// Macros for unit testing +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) +//#define TRACSSCOMP(args...) TRACFCOMP(args) +#define TRACSSCOMP(args...) + + +// ---------------------------------------------- +// Defines +// ---------------------------------------------- + +namespace SPD +{ + +// Register the perform Op with the routing code for DIMMs. +DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD, + DeviceFW::SPD, + TARGETING::TYPE_DIMM, + spdAccess ); + +// ------------------------------------------------------------------ +// spdRead +// ------------------------------------------------------------------ +errlHndl_t spdAccess( 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 err = NULL; + uint64_t keyword = va_arg( i_args, uint64_t ); + + if( DeviceFW::READ == i_opType ) + { + // Read the SPD keyword + err = spdGetKeywordValue( keyword, + io_buffer, + io_buflen, + i_target ); + } + else + { + // Write the SPD keyword + err = spdWriteKeywordValue( keyword, + io_buffer, + io_buflen, + i_target ); + } + + return err; +} // end spdRead + + +// ------------------------------------------------------------------ +// spdGetKeywordValue +// ------------------------------------------------------------------ +errlHndl_t spdGetKeywordValue ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target ) +{ + errlHndl_t err = NULL; + + TRACSSCOMP( g_trac_spd, + ENTER_MRK"spdGetKeywordValue(), io_buflen: %d, keyword: 0x%04x", + io_buflen, i_keyword ); + + do + { + // Read the Basic Memory Type + uint8_t memType = 0x0; + err = spdFetchData( MEM_TYPE_ADDR, + MEM_TYPE_ADDR_SZ, + &memType, + i_target ); + + if( err ) + { + break; + } + + TRACDCOMP( g_trac_spd, + INFO_MRK"Mem Type: %04x", + memType ); + + // If the user wanted the Basic Memory Type, return this now + if( BASIC_MEMORY_TYPE == i_keyword ) + { + io_buflen = MEM_TYPE_ADDR_SZ; + memcpy( io_buffer, &memType, io_buflen ); + break; + } + + // Check the Basic Memory Type to be sure its valid before + // continuing. + if( SPD_DDR3 == memType ) + { + // Read the keyword value + err = spdGetValue( i_keyword, + io_buffer, + io_buflen, + i_target, + memType ); + + if( err ) + { + break; + } + } + else + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Invalid Basic Memory Type (0x%04x)", + memType ); + + /*@ + * @errortype + * @reasoncode SPD_INVALID_BASIC_MEMORY_TYPE + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_GET_KEYWORD_VALUE + * @userdata1 Basic Memory Type (Byte 2) + * @userdata2 Keyword Requested + * @devdesc Invalid Basic Memory Type + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_GET_KEYWORD_VALUE, + SPD_INVALID_BASIC_MEMORY_TYPE, + memType, + i_keyword ); + } + } while( 0 ); + + TRACSSCOMP( g_trac_spd, + EXIT_MRK"spdGetKeywordValue()" ); + + return err; +} + + +// ------------------------------------------------------------------ +// spdWriteKeywordValue +// ------------------------------------------------------------------ +errlHndl_t spdWriteKeywordValue ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target ) +{ + errlHndl_t err = NULL; + + TRACSSCOMP( g_trac_spd, + ENTER_MRK"spdWriteKeywordValue()" ); + + do + { + // TODO - This will be implemented with story 4659 + TRACFCOMP( g_trac_spd, + ERR_MRK"SPD writes are not supported yet!" ); + + /*@ + * @errortype + * @reasoncode SPD_NOT_SUPPORTED + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_WRITE_KEYWORD_VALUE + * @userdata1 i_keyword + * @userdata2 <UNUSED> + * @devdesc SPD Writes are not supported yet. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_WRITE_KEYWORD_VALUE, + SPD_NOT_SUPPORTED, + i_keyword, + 0x0 ); + break; + } while( 0 ); + + TRACSSCOMP( g_trac_spd, + EXIT_MRK"spdWriteKeywordValue()" ); + + return err; +} + + +// ------------------------------------------------------------------ +// spdFetchData +// ------------------------------------------------------------------ +errlHndl_t spdFetchData ( uint64_t i_byteAddr, + size_t i_numBytes, + void * o_data, + TARGETING::Target * i_target ) +{ + errlHndl_t err = NULL; + + TRACSSCOMP( g_trac_spd, + ENTER_MRK"spdFetchData()" ); + + do + { + // --------------------------------------------------------------- + // TODO - For now, we will use a generic name of dimmspd.dat, and + // access the file via vfs for all SPD content for ALL DIMMs. + // + // Unfortunately Fsp will not be able to write into our file + // space in PNOR because the files/names will need to be signed. + // This means that eventually there will be block of data in PNOR + // where each DIMMs, and potential DIMM, information will be at + // a given offset. + // --------------------------------------------------------------- + + err = spdReadBinaryFile( i_byteAddr, + i_numBytes, + o_data ); + + if( err ) + { + break; + } + } while( 0 ); + + TRACSSCOMP( g_trac_spd, + EXIT_MRK"spdFetchData()" ); + + return err; +} + + +// ------------------------------------------------------------------ +// spdGetValue +// ------------------------------------------------------------------ +errlHndl_t spdGetValue ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target, + uint64_t i_DDRRev ) +{ + errlHndl_t err = NULL; + uint8_t * tmpBuffer = static_cast<uint8_t *>(io_buffer); + KeywordData * kwdData; + uint32_t arraySize = 0x0; + + TRACSSCOMP( g_trac_spd, + ENTER_MRK"spdGetValue()" ); + + do + { + if( SPD_DDR3 == i_DDRRev ) + { + kwdData = ddr3Data; + arraySize = (sizeof(ddr3Data)/sizeof(ddr3Data[0])); + + } + else + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Unsupported DDRx Revision (0x%04x)", + i_DDRRev ); + + /*@ + * @errortype + * @reasoncode SPD_INVALID_BASIC_MEMORY_TYPE + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_GET_VALUE + * @userdata1 SPD Keyword + * @userdata2 The DDR Revision + * @devdesc Invalid DDR Revision + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_GET_VALUE, + SPD_INVALID_BASIC_MEMORY_TYPE, + i_keyword, + i_DDRRev ); + + break; + } + + // TODO - A binary_search algorithm will be implemented with + // Story 4709. + // Loop through the lookup table. + bool keywordFound = false; + for( uint32_t i = 0; i < arraySize; i++ ) + { + if( kwdData[i].keyword == i_keyword ) + { + keywordFound = true; + + if( kwdData[i].isSpecialCase ) + { + // Handle special cases where data isn't sequential + // or is in reverse order from what would be read. + err = spdSpecialCases( i_keyword, + io_buffer, + io_buflen, + i_target, + i, + i_DDRRev ); + + break; + } + + // Check io_buflen versus size in table + err = spdCheckSize( io_buflen, + kwdData[i].length, + i_keyword ); + + if( err ) + { + break; + } + + // Read length requested + err = spdFetchData( kwdData[i].offset, + kwdData[i].length, + tmpBuffer, + i_target ); + + if( err ) + { + break; + } + + // if useBitmask set, mask and then shift data + if( kwdData[i].useBitMask ) + { + // Any bit mask/shifting will always be on a <1 Byte value + // thus, we touch only byte 0. + tmpBuffer[0] = tmpBuffer[0] & kwdData[i].bitMask; + tmpBuffer[0] = tmpBuffer[0] >> kwdData[i].shift; + } + + // Set length read + io_buflen = kwdData[i].length; + break; + } + } + + if( err ) + { + break; + } + + if( !keywordFound ) + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Could not find keyword (0x%04x) in lookup table!", + i_keyword ); + + /*@ + * @errortype + * @reasoncode SPD_KEYWORD_NOT_FOUND + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_GET_VALUE + * @userdata1 SPD Keyword + * @userdata2 <UNUSED> + * @devdesc Invalid SPD Keyword + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_GET_VALUE, + SPD_KEYWORD_NOT_FOUND, + i_keyword, + 0x0 ); + break; + } + } while( 0 ); + + if( err ) + { + // Signal the caller that there was an error getting + // data and that there is no valid data. + io_buflen = 0; + } + + TRACSSCOMP( g_trac_spd, + EXIT_MRK"spdGetValue()" ); + + return err; +} + + +// ------------------------------------------------------------------ +// spdSpecialCases +// ------------------------------------------------------------------ +errlHndl_t spdSpecialCases ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target, + uint64_t i_entry, + uint64_t i_DDRRev ) +{ + errlHndl_t err = NULL; + uint8_t * tmpBuffer = static_cast<uint8_t *>(io_buffer); + + TRACSSCOMP( g_trac_spd, + ENTER_MRK"spdSpecialCases()" ); + + do + { + // Handle each of the special cases here + if( SPD_DDR3 == i_DDRRev ) + { + switch( i_keyword ) + { + case CAS_LATENCIES_SUPPORTED: + // Length 2 bytes + // Byte 0x0e [7:0] + // Byte 0x0f [6:0] - MSB + + // Check Size of buffer + err = spdCheckSize( io_buflen, + 2, + i_keyword ); + + if( err ) break; + + // Get MSB + err = spdFetchData( ddr3Data[i_entry].offset, + 1, /* Read 1 byte at a time */ + &tmpBuffer[0], + i_target ); + + if( err ) break; + + // Mask and shift if needed + if( ddr3Data[i_entry].useBitMask ) + { + tmpBuffer[0] = tmpBuffer[0] & ddr3Data[i_entry].bitMask; + tmpBuffer[0] = tmpBuffer[0] >> ddr3Data[i_entry].shift; + } + + // Get LSB + err = spdFetchData( 0x0e, + 1, /* Read 1 byte at a time */ + &tmpBuffer[1], + i_target ); + + if( err ) break; + + // Set number of bytes read + io_buflen = 2; + break; + + case TRC_MIN: + // Length 2 bytes + // Byte 0x15 [7:4] - MSB + // Byte 0x17 [7:0] + + // Check Size of buffer + err = spdCheckSize( io_buflen, + 2, + i_keyword ); + + if( err ) break; + + // Get MSB + err = spdFetchData( ddr3Data[i_entry].offset, + 1, /* Read 1 byte at a time */ + &tmpBuffer[0], + i_target ); + + if( err ) break; + + // Mask and shift if needed + if( ddr3Data[i_entry].useBitMask ) + { + tmpBuffer[0] = tmpBuffer[0] & ddr3Data[i_entry].bitMask; + tmpBuffer[0] = tmpBuffer[0] >> ddr3Data[i_entry].shift; + } + + // Get LSB + err = spdFetchData( 0x17, + 1, /* Read 1 byte at a time */ + &tmpBuffer[1], + i_target ); + + if( err ) break; + + // Set number of bytes read + io_buflen = 2; + break; + + case TRAS_MIN: + // Length 2 bytes + // Byte 0x15 [3:0] - MSB + // Byte 0x16 [7:0] + + // Check size of buffer + err = spdCheckSize( io_buflen, + 2, + i_keyword ); + + if( err ) break; + + // Get MSB + err = spdFetchData( ddr3Data[i_entry].offset, + 1, /* Read 1 byte at a time */ + &tmpBuffer[0], + i_target ); + + if( err ) break; + + // Mask and shift if needed + if( ddr3Data[i_entry].useBitMask ) + { + tmpBuffer[0] = tmpBuffer[0] & ddr3Data[i_entry].bitMask; + tmpBuffer[0] = tmpBuffer[0] >> ddr3Data[i_entry].shift; + } + + // Get LSB + err = spdFetchData( 0x16, + 1, /* Read 1 byte at a time */ + &tmpBuffer[1], + i_target ); + + if( err ) break; + + // Set number of bytes read + io_buflen = 2; + break; + + case TRFC_MIN: + // Length 2 bytes + // Byte 0x18 [7:0] + // Byte 0x19 [7:0] - MSB + + // Check size of buffer + err = spdCheckSize( io_buflen, + 2, + i_keyword ); + + if( err ) break; + + // Get MSB + err = spdFetchData( ddr3Data[i_entry].offset, + 1, /* Read 1 byte at a time */ + &tmpBuffer[0], + i_target ); + + if( err ) break; + + // Mask and shift if needed + if( ddr3Data[i_entry].useBitMask ) + { + tmpBuffer[0] = tmpBuffer[0] & ddr3Data[i_entry].bitMask; + tmpBuffer[0] = tmpBuffer[0] >> ddr3Data[i_entry].shift; + } + + // Get LSB + err = spdFetchData( 0x18, + 1, /* Read 1 byte at a time */ + &tmpBuffer[1], + i_target ); + + if( err ) break; + + // Set number of bytes read + io_buflen = 2; + break; + + case TFAW_MIN: + // Length 2 bytes + // Byte 0x1c [3:0] - MSB + // byte 0x1d [7:0] + + // Check size of buffer + err = spdCheckSize( io_buflen, + 2, + i_keyword ); + + if( err ) break; + + // Get MSB + err = spdFetchData( ddr3Data[i_entry].offset, + 1, /* Read 1 byte at a time */ + &tmpBuffer[0], + i_target ); + + if( err ) break; + + // Mask and shift if needed + if( ddr3Data[i_entry].useBitMask ) + { + tmpBuffer[0] = tmpBuffer[0] & ddr3Data[i_entry].bitMask; + tmpBuffer[0] = tmpBuffer[0] >> ddr3Data[i_entry].shift; + } + + // Get LSB + err = spdFetchData( 0x1d, + 1, /* Read 1 byte at a time */ + &tmpBuffer[1], + i_target ); + + if( err ) break; + + // Set number of bytes read + io_buflen = 2; + break; + + case MODULE_MANUFACTURER_ID: + // Length 2 bytes + // Byte 0x75 [7:0] + // Byte 0x76 [7:0] - MSB + + + // Check size of buffer + err = spdCheckSize( io_buflen, + 2, + i_keyword ); + + if( err ) break; + + // Get MSB + err = spdFetchData( ddr3Data[i_entry].offset, + 1, /* Read 1 byte at a time */ + &tmpBuffer[0], + i_target ); + + if( err ) break; + + // Mask and shift if needed + if( ddr3Data[i_entry].useBitMask ) + { + tmpBuffer[0] = tmpBuffer[0] & ddr3Data[i_entry].bitMask; + tmpBuffer[0] = tmpBuffer[0] >> ddr3Data[i_entry].shift; + } + + // Get LSB + err = spdFetchData( 0x75, + 1, /* Read 1 byte at a time */ + &tmpBuffer[1], + i_target ); + + if( err ) break; + + // Set number of bytes read + io_buflen = 2; + break; + + default: + TRACFCOMP( g_trac_spd, + ERR_MRK"Unknown keyword (0x%04x) for DDR3 special cases!", + i_keyword ); + + /*@ + * @errortype + * @reasoncode SPD_INVALID_SPD_KEYWORD + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_SPECIAL_CASES + * @userdata1 SPD Keyword + * @userdata2 Table Entry + * @devdesc Keyword is not a special case keyword. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_SPECIAL_CASES, + SPD_INVALID_SPD_KEYWORD, + i_keyword, + i_entry ); + break; + }; + } + else + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Unsupported DDRx Revision (0x%04x)", + i_DDRRev ); + + /*@ + * @errortype + * @reasoncode SPD_INVALID_BASIC_MEMORY_TYPE + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_SPECIAL_CASES + * @userdata1 SPD Keyword + * @userdata2[0:31] SPD Table entry + * @userdata2[32:63] DIMM DDR Revision + * @devdesc Invalid DDR Revision + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_SPECIAL_CASES, + SPD_INVALID_BASIC_MEMORY_TYPE, + i_keyword, + TWO_UINT32_TO_UINT64( i_entry, i_DDRRev) ); + + break; + } + } while( 0 ); + + TRACSSCOMP( g_trac_spd, + EXIT_MRK"spdSpecialCases()" ); + + return err; +} + + +// ------------------------------------------------------------------ +// spdCheckSize +// ------------------------------------------------------------------ +errlHndl_t spdCheckSize ( size_t i_bufferSz, + size_t i_expBufferSz, + uint64_t i_keyword ) +{ + errlHndl_t err = NULL; + + // Check that the buffer is greater than or equal to the size + // we need to get all the keyword data requested. + if( i_bufferSz < i_expBufferSz ) + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Buffer Size (%d) for keyword (0x%04x) wasn't greater than " + "or equal to expected size (%d)", + i_bufferSz, i_keyword, i_expBufferSz ); + + /*@ + * @errortype + * @reasoncode SPD_INSUFFICIENT_BUFFER_SIZE + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_CHECK_SIZE + * @userdata1 Keyword + * @userdata2[0:31] Needed Buffer Size + * @userdata2[32:63] Expected Buffer Size + * @devdesc Buffer Size provided was not big enough for + * the keyword requested. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_CHECK_SIZE, + SPD_INSUFFICIENT_BUFFER_SIZE, + i_keyword, + TWO_UINT32_TO_UINT64( i_bufferSz, i_expBufferSz ) ); + } + + return err; +} + + +// ------------------------------------------------------------------ +// spdReadBinaryFile +// ------------------------------------------------------------------ +errlHndl_t spdReadBinaryFile ( uint64_t i_byteAddr, + size_t i_numBytes, + void * o_data ) +{ + errlHndl_t err = NULL; + const char * fileName = "dimmspd.dat"; + const char * startAddr = NULL; + size_t fileSize; + + TRACSSCOMP( g_trac_spd, + ENTER_MRK"spdReadBinaryFile()" ); + + do + { + if( g_loadModule ) + { + mutex_lock( &g_spdMutex ); + + if( g_loadModule ) + { + // Load the file + TRACUCOMP( g_trac_spd, + "Load file" ); + err = VFS::module_load( fileName ); + + if( err ) + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Error opening binary SPD file: %s", + fileName ); + mutex_unlock( &g_spdMutex ); + + break; + } + + g_loadModule = false; + } + mutex_unlock( &g_spdMutex ); + } + + // Get the starting address of the file/module + TRACUCOMP( g_trac_spd, + "Get starting address/size" ); + err = VFS::module_address( fileName, + startAddr, + fileSize ); + + if( err ) + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Error getting starting address of binary SPD file: %s", + fileName ); + + break; + } + + // Check that we can read the amount of data we need to from the + // file we just loaded + TRACUCOMP( g_trac_spd, + "Check Size vs file size" ); + if( (i_byteAddr + i_numBytes) > fileSize ) + { + TRACFCOMP( g_trac_spd, + ERR_MRK"Unable to read %d bytes from %s at offset 0x%08x " + "because file size is only %d bytes!", + i_numBytes, + fileName, + i_byteAddr, + fileSize ); + uint64_t tmpData = (i_byteAddr + i_numBytes); + tmpData = tmpData << 16; + tmpData = tmpData & (fileSize & 0xFFFF); + + /*@ + * @errortype + * @reasoncode SPD_INSUFFICIENT_FILE_SIZE + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid SPD_READ_BINARY_FILE + * @userdata1 File Size + * @userdata2[0:48] Starting offset into file + * @userdata2[49:63] Number of bytes to read + * @devdesc File is not sufficiently large to read number of + * bytes at offset given without overrunning file. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + SPD_READ_BINARY_FILE, + SPD_INSUFFICIENT_FILE_SIZE, + fileSize, + tmpData ); + + break; + } + + // Retrieve the data requested + TRACUCOMP( g_trac_spd, + "Copy data out of file" ); + memcpy( o_data, (startAddr + i_byteAddr), i_numBytes ); + } while( 0 ); + + TRACSSCOMP( g_trac_spd, + EXIT_MRK"spdReadBinaryFile()" ); + + return err; +} + + +} // end namespace SPD diff --git a/src/usr/spd/spd.H b/src/usr/spd/spd.H new file mode 100755 index 000000000..65f85119a --- /dev/null +++ b/src/usr/spd/spd.H @@ -0,0 +1,261 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/spd/spd.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// 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 __SPD_H +#define __SPD_H + +/** + * @file spd.H + * + * @brief Provides the interfaces for the SPD device driver + * + */ + +// ---------------------------------------------- +// Includes +// ---------------------------------------------- +#include <errl/errltypes.H> + +namespace SPD +{ + +/** +* @brief Miscellaneous enumerations for SPD +*/ +enum +{ + MEM_TYPE_ADDR = 0x2, // DIMM Basic Memory Type address + MEM_TYPE_ADDR_SZ = 0x1, // DIMM Basic Memory Type address size + + // Basic Memory Type Enumerations + SPD_DDR3 = 0xB, + SPD_DDR4 = 0xC, // TODO - Proposed value from draft Spec. +}; + +/** +* @brief Structure to define the lookup table for SPD keywords +* for DIMMs. +*/ +struct KeywordData +{ + uint16_t keyword; // SPD keyword this data corresponds to + uint8_t offset; // Byte offset in the SPD data + uint8_t length; // Number of bytes to retrieve + bool useBitMask; // Use the bitmask to mask off bits, if true, length must be + // l byte, unless it is a "special" case + uint8_t bitMask; // Bit mask + uint8_t shift; // Used for fields < 1 byte to right justify all values. + bool isSpecialCase; // Whether or not this entry is a special case. +}; + + +/** +* +* @brief Perform an SPD operation. It follows a pre-defined +* prototype function in order to be registered with the device +* driver framework. +* +* @param[in] i_opType - Operation Type - See DeviceFW::OperationType in +* driververif.H +* +* @param[in] i_target - DIMM Target device +* +* @param [in/out] io_buffer - Pointer to the data that was read from +* the target device. It will also be used to contain data to +* be written to the device. +* +* @param [in/out] io_buflen - Length of the buffer to be read or written +* to/from the target. This value should indicate the size of the +* io_buffer parameter that has been allocated. Being returned it will +* indicate the number of valid bytes in the buffer being returned. +* +* @param [in] i_accessType - Access Type - See DeviceFW::AccessType in +* usrif.H +* +* @param [in] i_args - This is an argument list for the device driver +* framework. +* +* @return errlHndl_t - NULL if successful, otherwise a pointer to the +* error log. +* +*/ +errlHndl_t spdAccess ( DeviceFW::OperationType i_opType, + TARGETING::Target * i_target, + void * io_buffer, + size_t & io_buflen, + int64_t i_accessType, + va_list i_args ); + +/** + * @brief This function is used to read SPD keywords from collected + * SPD data for the given target + * + * @param[in] i_keyword - The SPD keyword to access. + * + * @param[in/out] io_buffer - The buffer that will contain the data + * read from the SPD data. + * + * @param[in/out] io_buflen - The requested number of bytes to read. + * The actual number of bytes read will be returned. + * + * @param[in] i_target - The target DIMM to access the data for. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t spdGetKeywordValue ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target ); + +/** + * @brief This function is used to write SPD keyword values. + * + * @param[in] i_keyword - The SPD keyword to access. + * + * @param[in/out] io_buffer - The buffer that will contain the data + * read from the SPD data. + * + * @param[in/out] io_buflen - The requested number of bytes to read. + * The actual number of bytes read will be returned. + * + * @param[in] i_target - The target DIMM to access the data for. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t spdWriteKeywordValue ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target ); + +/** + * @param This function is a wrapper for reading the correct keyword. + * It will route the read to whatever function has the latest + * supported access code. + * + * @param[in] i_byteAddress - The offset into the JEDEC SPD layout. + * + * @param[in] i_numbytes - Number of bytes to read. + * + * @param[out] o_data - The data buffer that will return the data read. + * + * @param[in] i_target - The target DIMM to access. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t spdFetchData ( uint64_t i_byteAddr, + size_t i_numBytes, + void * o_data, + TARGETING::Target * i_target ); + +/** + * @param This function will read the SPD keyword from the appropriate + * table. + * + * @param[in] i_keyword - The SPD keyword to access. + * + * @param[in/out] io_buffer - The buffer that will contain the data + * read from the SPD data. + * + * @param[in/out] io_buflen - The requested number of bytes to read. + * The actual number of bytes read will be returned. + * + * @param[in] i_target - The target DIMM to access the data for. + * + * @param[in] i_DDRRev - The DIMM DDR Revision. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t spdGetValue ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target, + uint64_t i_DDRRev ); + +/** + * @param This function handles the special case keywords where + * the data isn't sequential, or is broken up between two different + * offsets within the layout. + * + * @param[in] i_keyword - The SPD keyword to access. + * + * @param[in/out] io_buffer - The buffer that will contain the data + * read from the SPD data. + * + * @param[in/out] io_buflen - The requested number of bytes to read. + * The actual number of bytes read will be returned. + * + * @param[in] i_target - The target DIMM to access the data for. + * + * @param[in] i_entry - The table entry for the keyword to read. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t spdSpecialCases ( uint64_t i_keyword, + void * io_buffer, + size_t & io_buflen, + TARGETING::Target * i_target, + uint64_t i_entry, + uint64_t i_DDRRev ); + +/** + * @brief This function checks to make sure that the buffer allocated + * is large enough to hold the data that needs to be returned. + * + * @param[in] i_bufferSz - The size of the buffer passed in by the caller. + * + * @param[in] i_expBufferSz - The expected buffer size for the keyword + * requested. + * + * @param[in] i_keyword - The SPD Keyword requested. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t spdCheckSize ( size_t i_bufferSz, + size_t i_expBufferSz, + uint64_t i_keyword ); + +/** + * @brief This function will read a binary file from PNOR. This is + * not the long term solution, but mainly for initial testing. + * + * @param[in] i_byteAddr - The byte offset into the SPD layout. + * + * @param[in] i_bytes - Number of bytes to access. + * + * @param[out] o_data - The data buffer containing the data read. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t spdReadBinaryFile ( uint64_t i_byteAddr, + size_t i_numBytes, + void * o_data ); + +}; // end SPD namespace + +#endif // __SPD_H diff --git a/src/usr/spd/spdDDR3.H b/src/usr/spd/spdDDR3.H new file mode 100755 index 000000000..4b36b40a9 --- /dev/null +++ b/src/usr/spd/spdDDR3.H @@ -0,0 +1,125 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/spd/spdDDR3.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// 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 __SPDDDR3_H +#define __SPDDDR3_H + +/** + * @file spdDDR3.H + * + * @brief Provides the enumerations for the DDR 3 fields to read. + * + */ + +// ---------------------------------------------- +// Includes +// ---------------------------------------------- +#include "spd.H" + +namespace SPD +{ + +/** + * @brief Pre-defined lookup table for DDR3 keywords and the + * information needed to read that data from the SPD data. + */ +KeywordData ddr3Data[] = +{ + // ---------------------------------------------------------------------------------- + // Bit order for each byte is [7:0] as defined by the JEDEC spec (little endian) + // + // Special cases listed below will be handled out of the normal table lookup + // handler code. + // ---------------------------------------------------------------------------------- + // Keyword offset size Use Bitmsk Shift + // Bitmsk Number + // ---------------------------------------------------------------------------------- + { CRC_EXCLUDE, 0x00, 0x01, true, 0x80, 0x07, false }, + { SPD_BYTES_TOTAL, 0x00, 0x01, true, 0x70, 0x04, false }, + { SPD_BYTES_USED, 0x00, 0x01, true, 0x0F, 0x00, false }, + { SPD_MAJOR_REVISION, 0x01, 0x01, true, 0xF0, 0x04, false }, + { SPD_MINOR_REVISION, 0x01, 0x01, true, 0x0F, 0x00, false }, + { BASIC_MEMORY_TYPE, 0x02, 0x01, false, 0x00, 0x00, false }, + { MODULE_TYPE, 0x03, 0x01, true, 0x0F, 0x00, false }, + { BANK_ADDRESS_BITS, 0x04, 0x01, true, 0x70, 0x04, false }, + { DENSITY, 0x04, 0x01, true, 0x0F, 0x00, false }, + { ROW_ADDRESS, 0x05, 0x01, true, 0x38, 0x03, false }, + { COL_ADDRESS, 0x05, 0x01, true, 0x07, 0x00, false }, + { MODULE_NOMINAL_VOLTAGE, 0x06, 0x01, true, 0x07, 0x00, false }, + { MODULE_RANKS, 0x07, 0x01, true, 0x38, 0x03, false }, + { MODULE_DRAM_WIDTH, 0x07, 0x01, true, 0x07, 0x00, false }, + { ECC_BITS, 0x08, 0x01, true, 0x18, 0x03, false }, + { MODULE_MEMORY_BUS_WIDTH, 0x08, 0x01, true, 0x07, 0x00, false }, + { FTB_DIVIDEND, 0x09, 0x01, true, 0xF0, 0x04, false }, + { FTB_DIVISOR, 0x09, 0x01, true, 0x0F, 0x00, false }, + { MTB_DIVIDEND, 0x0a, 0x01, false, 0x00, 0x00, false }, + { MTB_DIVISOR, 0x0b, 0x01, false, 0x00, 0x00, false }, + { TCK_MIN, 0x0c, 0x01, false, 0x00, 0x00, false }, + { CAS_LATENCIES_SUPPORTED, 0x0f, 0x02, true, 0x7F, 0x00, true }, + { MIN_CAS_LATENCY, 0x10, 0x01, false, 0x00, 0x00, false }, + { TWR_MIN, 0x11, 0x01, false, 0x00, 0x00, false }, + { TRCD_MIN, 0x12, 0x01, false, 0x00, 0x00, false }, + { TRRD_MIN, 0x13, 0x01, false, 0x00, 0x00, false }, + { TRP_MIN, 0x14, 0x01, false, 0x00, 0x00, false }, + { TRC_MIN, 0x15, 0x02, true, 0xF0, 0x04, true }, + { TRAS_MIN, 0x15, 0x02, true, 0x0F, 0x00, true }, + { TRFC_MIN, 0x19, 0x02, false, 0x00, 0x00, true }, + { TWTR_MIN, 0x1a, 0x01, false, 0x00, 0x00, false }, + { TRTP_MIN, 0x1b, 0x01, false, 0x00, 0x00, false }, + { TFAW_MIN, 0x1c, 0x02, true, 0x0F, 0x00, true }, + { DLL_OFF, 0x1e, 0x01, true, 0x80, 0x07, false }, + { RZQ_7, 0x1e, 0x01, true, 0x02, 0x01, false }, + { RZQ_6, 0x1e, 0x01, true, 0x01, 0x00, false }, + { SDRAM_OPTIONAL_FEATURES, 0x1e, 0x01, false, 0x00, 0x00, false }, + { PASR, 0x1f, 0x01, true, 0x80, 0x07, false }, + { ODTS, 0x1f, 0x01, true, 0x08, 0x03, false }, + { ASR, 0x1f, 0x01, true, 0x04, 0x02, false }, + { ETR_1X, 0x1f, 0x01, true, 0x02, 0x01, false }, + { ETR, 0x1f, 0x01, true, 0x01, 0x00, false }, + { SDRAM_THERMAL_REFRESH_OPTIONS, 0x1f, 0x01, false, 0x00, 0x00, false }, + { THERMAL_SENSOR_PRESENT, 0x20, 0x01, true, 0x80, 0x07, false }, + { THERMAL_SENSOR_ACCURACY, 0x20, 0x01, true, 0x7F, 0x00, false }, + { MODULE_THERMAL_SENSOR, 0x20, 0x01, false, 0x00, 0x00, false }, + { SDRAM_DEVICE_TYPE_NONSTD, 0x21, 0x01, true, 0x80, 0x07, false }, + { SDRAM_DEVICE_TYPE, 0x21, 0x01, true, 0x7F, 0x00, false }, + { TCKMIN_FINE_OFFSET, 0x22, 0x01, false, 0x00, 0x00, false }, + { TAAMIN_FINE_OFFSET, 0x23, 0x01, false, 0x00, 0x00, false }, + { TRCDMIN_FINE_OFFSET, 0x24, 0x01, false, 0x00, 0x00, false }, + { TRPMIN_FINE_OFFSET, 0x25, 0x01, false, 0x00, 0x00, false }, + { TRPCMIN_FINE_OFFSET, 0x26, 0x01, false, 0x00, 0x00, false }, + { MODULE_TYPE_SPECIFIC_SECTION, 0x3c, 0x39, false, 0x00, 0x00, false }, + { MODULE_MANUFACTURER_ID, 0x76, 0x02, false, 0x00, 0x00, true }, + { MODULE_MANUFACTURING_LOCATION, 0x77, 0x01, false, 0x00, 0x00, false }, + { MODULE_MANUFACTURING_DATE, 0x78, 0x02, false, 0x00, 0x00, false }, + { MODULE_SERIAL_NUMBER, 0x7a, 0x04, false, 0x00, 0x00, false }, + { MODULE_CRC, 0x7e, 0x02, false, 0x00, 0x00, false }, + { MODULE_PART_NUMBER, 0x80, 0x12, false, 0x00, 0x00, false }, + { MODULE_REVISION_CODE, 0x92, 0x02, false, 0x00, 0x00, false }, + { DRAM_MANUFACTURER_ID, 0x94, 0x02, false, 0x00, 0x00, false }, + { MANUFACTURER_SPECIFIC_DATA, 0x96, 0x1a, false, 0x00, 0x00, false }, + // ---------------------------------------------------------------------------------- +}; + + +}; // end SPD namespace + +#endif // __SPDDDR3_H diff --git a/src/usr/spd/test/makefile b/src/usr/spd/test/makefile new file mode 100644 index 000000000..58e8fd562 --- /dev/null +++ b/src/usr/spd/test/makefile @@ -0,0 +1,28 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/spd/test/makefile $ +# +# IBM CONFIDENTIAL +# +# COPYRIGHT International Business Machines Corp. 2012 +# +# 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 = testspd +TESTS = *.H + +include ${ROOTPATH}/config.mk diff --git a/src/usr/spd/test/spdtest.H b/src/usr/spd/test/spdtest.H new file mode 100755 index 000000000..3efa690cc --- /dev/null +++ b/src/usr/spd/test/spdtest.H @@ -0,0 +1,413 @@ +// IBM_PROLOG_BEGIN_TAG +// This is an automatically generated prolog. +// +// $Source: src/usr/spd/test/spdtest.H $ +// +// IBM CONFIDENTIAL +// +// COPYRIGHT International Business Machines Corp. 2012 +// +// 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 __SPDTEST_H +#define __SPDTEST_H + +/** + * @file spdtest.H + * + * @brief Test cases for SPD code + */ +#include <sys/time.h> + +#include <cxxtest/TestSuite.H> +#include <errl/errlmanager.H> +#include <errl/errlentry.H> +#include <errl/errltypes.H> +#include <devicefw/driverif.H> +#include <targeting/predicates/predicatectm.H> + +#include <spd/spdreasoncodes.H> +#include <spd/spdenums.H> +#include "../spdDDR3.H" +#include "../spd.H" + +extern trace_desc_t* g_trac_spd; + +using namespace TARGETING; +using namespace SPD; + +void getDIMMTargets ( TargetHandleList & o_dimmList ) +{ + // Get top level system target + TARGETING::TargetService& tS = TARGETING::targetService(); + TARGETING::Target * sysTarget = NULL; + tS.getTopLevelTarget( sysTarget ); + assert( sysTarget != NULL ); + + // Get a DIMM Target + TARGETING::PredicateCTM predDimm( TARGETING::CLASS_CARD, + TARGETING::TYPE_DIMM ); + tS.getAssociated( o_dimmList, + sysTarget, + TARGETING::TargetService::CHILD, + TARGETING::TargetService::ALL, + &predDimm ); + TRACFCOMP( g_trac_spd, + "getDIMMTargets() - found %d DIMMs", + o_dimmList.size() ); + return; +} + +class SPDTest: public CxxTest::TestSuite +{ + public: + + /** + * @brief This test reads all of the keywords for all of the + * DIMMs found. + */ + void testSpdRead ( void ) + { + errlHndl_t err = NULL; + uint64_t cmds = 0x0; + uint64_t fails = 0x0; + + TRACFCOMP( g_trac_spd, + ENTER_MRK"testSpdRead()" ); + + do + { + TARGETING::Target * theTarget = NULL; + + // Get DIMM Targets + TargetHandleList dimmList; + getDIMMTargets( dimmList ); + + if( ( 0 == dimmList.size() ) || + ( NULL == dimmList[0] ) ) + { + TRACFCOMP( g_trac_spd, + "testSpdRead - No DIMMs found!" ); + break; + } + + for( uint32_t dimm = 0; dimm < dimmList.size(); dimm++ ) + { + theTarget = dimmList[dimm]; + uint8_t * theData = NULL; + size_t theSize = 0; + uint32_t entry = 0x0; + + // Get the DDR revision + uint8_t memType = 0x0; + size_t memTypeSize = 0x1; + err = deviceRead( theTarget, + &memType, + memTypeSize, + DEVICE_SPD_ADDRESS( SPD::BASIC_MEMORY_TYPE ) ); + + if( err ) + { + fails++; + TS_FAIL( "testSpdRead- Failure reading Basic memory type!" ); + errlCommit( err, + SPD_COMP_ID ); + continue; + } + + for( uint64_t keyword = SPD::SPD_FIRST_KEYWORD; + keyword < SPD::SPD_LAST_KEYWORD; keyword++ ) + { + cmds++; + if( NULL != theData ) + { + free( theData ); + theData = NULL; + } + + // Get the required size of the buffer + theSize = 0; + if( SPD_DDR3 == memType ) + { + for( entry = 0; + entry < (sizeof(ddr3Data)/sizeof(ddr3Data[0])); + entry++ ) + { + if( keyword == ddr3Data[entry].keyword ) + { + theSize = ddr3Data[entry].length; + break; + } + } + } + else + { + // Nothing else supported yet! + // Not really a fail, just not supported + cmds--; + continue; + } + + if( 0x0 == theSize ) + { + fails++; + TS_FAIL( "testSpdRead - Keyword (0x%04x) size = 0x0", + entry ); + continue; + } + + // Allocate the buffer + theData = static_cast<uint8_t*>(malloc( theSize )); + + err = deviceRead( theTarget, + theData, + theSize, + DEVICE_SPD_ADDRESS( keyword ) ); + + if( err ) + { + fails++; + TS_FAIL( "testSpdRead - Failure on keyword: %04x", + keyword ); + errlCommit( err, + SPD_COMP_ID ); + continue; + } + + // Read was successful, print out first 2 bytes of data read + TRACFCOMP( g_trac_spd, + "testSpdRead - kwd: 0x%04x, val: %02x%02x, size: %d", + keyword, theData[0], theData[1], theSize ); + + if( NULL != theData ) + { + free( theData ); + theData = NULL; + } + } + + if( err ) + { + break; + } + } + + if( err ) + { + break; + } + } while( 0 ); + + TRACFCOMP( g_trac_spd, + "testSpdRead - %d/%d fails", + fails, cmds ); + } + + /** + * @brief Test a SPD Write + */ + void testSpdWrite ( void ) + { + errlHndl_t err = NULL; + uint64_t cmds = 0x0; + uint64_t fails = 0x0; + + TRACFCOMP( g_trac_spd, + ENTER_MRK"testSpdWrite()" ); + + do + { + TARGETING::Target * theTarget = NULL; + + // Get DIMM Targets + TargetHandleList dimmList; + getDIMMTargets( dimmList ); + + if( ( 0 == dimmList.size() ) || + ( NULL == dimmList[0] ) ) + { + TRACFCOMP( g_trac_spd, + "testSpdWrite - No DIMMs found!" ); + break; + } + + for( uint32_t dimm = 0; dimm < dimmList.size(); dimm++ ) + { + theTarget = dimmList[dimm]; + uint8_t * theData = NULL; + size_t theSize = 0; + + cmds++; + err = deviceWrite( theTarget, + theData, + theSize, + DEVICE_SPD_ADDRESS( SPD_FIRST_KEYWORD ) ); + + if( NULL == err ) + { + // No error returned, failure + fails++; + TS_FAIL( "testSpdWrite - No error returned from deviceWrite()" ); + continue; + } + else + { + delete err; + err = NULL; + } + } + + if( err ) + { + break; + } + } while( 0 ); + + TRACFCOMP( g_trac_spd, + "testSpdWrite - %d/%d fails", + fails, cmds ); + } + + /** + * @brief Test an invalid Keyword to a DIMM target. + */ + void testSpdInvalidKeyword ( void ) + { + errlHndl_t err = NULL; + uint64_t cmds = 0x0; + uint64_t fails = 0x0; + + TRACFCOMP( g_trac_spd, + ENTER_MRK"testSpdInvalidKeyword()" ); + + do + { + TARGETING::Target * theTarget = NULL; + + // Get DIMM Targets + TargetHandleList dimmList; + getDIMMTargets( dimmList ); + + if( ( 0 == dimmList.size() ) || + ( NULL == dimmList[0] ) ) + { + TRACFCOMP( g_trac_spd, + "testSpdInvalidKeyword - No DIMMs found!" ); + break; + } + + for( uint32_t dimm = 0; dimm < dimmList.size(); dimm++ ) + { + theTarget = dimmList[dimm]; + uint8_t * theData = NULL; + size_t theSize = 0x0; + + cmds++; + err = deviceRead( theTarget, + theData, + theSize, + DEVICE_SPD_ADDRESS( SPD::SPD_LAST_KEYWORD ) ); + + if( NULL == err ) + { + fails++; + TS_FAIL( "testSpdInvalidKeyword - No error returned!" ); + continue; + } + else + { + delete err; + err = NULL; + } + } + + if( err ) + { + break; + } + } while( 0 ); + + TRACFCOMP( g_trac_spd, + "testSpdInvalidKeyword - %d/%d fails", + fails, cmds ); + } + + /** + * @brief Test an invalid size for an SPD read. + */ + void testSpdInvalidSize ( void ) + { + errlHndl_t err = NULL; + uint64_t cmds = 0x0; + uint64_t fails = 0x0; + + TRACFCOMP( g_trac_spd, + ENTER_MRK"testSpdInvalidSize()" ); + + do + { + TARGETING::Target * theTarget = NULL; + + // Get DIMM Targets + TargetHandleList dimmList; + getDIMMTargets( dimmList ); + + if( ( 0 == dimmList.size() ) || + ( NULL == dimmList[0] ) ) + { + TRACFCOMP( g_trac_spd, + "testSpdInvalidSize - No DIMMs found!" ); + break; + } + + for( uint32_t dimm = 0; dimm < dimmList.size(); dimm++ ) + { + theTarget = dimmList[dimm]; + uint8_t * theData = NULL; + size_t theSize = 0x0; // Invalid size of 0x0 + + cmds++; + err = deviceRead( theTarget, + theData, + theSize, + DEVICE_SPD_ADDRESS( SPD::SPD_FIRST_KEYWORD ) ); + + if( NULL == err ) + { + fails++; + TS_FAIL( "testSpdInvalidSize - No error for invalid size!" ); + continue; + } + else + { + delete err; + err = NULL; + } + } + + if( err ) + { + break; + } + } while( 0 ); + + TRACFCOMP( g_trac_spd, + "testSpdInvalidSize - %d/%d fails", + fails, cmds ); + } + + +}; + +#endif |