diff options
| author | Tsung Yeung <tyeung@us.ibm.com> | 2018-06-04 14:12:42 -0500 |
|---|---|---|
| committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2018-09-18 15:48:56 -0500 |
| commit | 1f6ed77b32b629f5cd316f2f3e1d8a432f037881 (patch) | |
| tree | 24b56ee17587dc2311cbb63936e405590c62d282 /src | |
| parent | 2968366be4705757b8766c8c422f608f491f0e0b (diff) | |
| download | blackbird-hostboot-1f6ed77b32b629f5cd316f2f3e1d8a432f037881.tar.gz blackbird-hostboot-1f6ed77b32b629f5cd316f2f3e1d8a432f037881.zip | |
Targeting support for NVDIMM-N P9 on ZZ
(Redo from previous change https://ralgit01.raleigh.ibm.com/gerrit1/#/c/56096/)
-Added interfaces for accessing NVDIMM via I2C
-Added attributes to support NV controller on NVDIMM
-Moved all attributes to common file
Change-Id: Ief68d9d5e0aaadfa017b4fe117bdb3e29739e772
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/59995
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Dean Sanner <dsanner@us.ibm.com>
Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/include/usr/devicefw/userif.H | 8 | ||||
| -rw-r--r-- | src/include/usr/hbotcompid.H | 10 | ||||
| -rw-r--r-- | src/include/usr/i2c/nvdimmddreasoncodes.H | 89 | ||||
| -rw-r--r-- | src/include/usr/i2c/nvdimmif.H | 56 | ||||
| -rw-r--r-- | src/usr/i2c/HBconfig | 5 | ||||
| -rwxr-xr-x | src/usr/i2c/eepromdd.C | 8 | ||||
| -rw-r--r-- | src/usr/i2c/errlud_i2c.C | 119 | ||||
| -rw-r--r-- | src/usr/i2c/errlud_i2c.H | 38 | ||||
| -rwxr-xr-x | src/usr/i2c/i2c.C | 49 | ||||
| -rw-r--r-- | src/usr/i2c/i2c.mk | 1 | ||||
| -rw-r--r-- | src/usr/i2c/makefile | 2 | ||||
| -rwxr-xr-x | src/usr/i2c/nvdimmdd.C | 1436 | ||||
| -rwxr-xr-x | src/usr/i2c/nvdimmdd.H | 302 | ||||
| -rwxr-xr-x | src/usr/targeting/common/genHwsvMrwXml.pl | 25 | ||||
| -rwxr-xr-x | src/usr/targeting/common/xmltohb/attribute_types.xml | 138 | ||||
| -rw-r--r-- | src/usr/targeting/common/xmltohb/target_types.xml | 9 |
16 files changed, 2288 insertions, 7 deletions
diff --git a/src/include/usr/devicefw/userif.H b/src/include/usr/devicefw/userif.H index 68f923f95..5bdd3b238 100644 --- a/src/include/usr/devicefw/userif.H +++ b/src/include/usr/devicefw/userif.H @@ -69,6 +69,7 @@ namespace DeviceFW DVPD, // Direct access memory VPD DEPRECATED_MEMD_VPD, NODECOMM, + NVDIMM, LAST_ACCESS_TYPE, }; @@ -365,7 +366,12 @@ namespace DeviceFW static_cast<uint64_t>(( i_mode )),\ static_cast<uint64_t>(( i_link_id )),\ static_cast<uint64_t>(( i_mailbox_id )) - + /** + * Construct the device addressing parameters for the NVDIMM device ops. + * @param[i] i_address - NVDIMM address to internal register + */ + #define DEVICE_NVDIMM_ADDRESS(i_address)\ + DeviceFW::NVDIMM, static_cast<uint64_t>((i_address)) /** * @brief Perform a hardware read operation. diff --git a/src/include/usr/hbotcompid.H b/src/include/usr/hbotcompid.H index 6f1c90675..53ba8cf74 100644 --- a/src/include/usr/hbotcompid.H +++ b/src/include/usr/hbotcompid.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2017 */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -421,6 +421,14 @@ const compId_t HBBL_COMP_ID = 0x3400; const char HBBL_COMP_NAME[] = "hbbl"; //@} +/** @name NVDIMM + * NVDIMM NV Controller Interface + */ +//@{ +const compId_t NVDIMM_COMP_ID = 0x3500; +const char NVDIMM_COMP_NAME[] = "nvdimm"; +//@} + /** @name HDAT * HDAT component * @Note HDAT_COMP_ID=0x9000 matches with what diff --git a/src/include/usr/i2c/nvdimmddreasoncodes.H b/src/include/usr/i2c/nvdimmddreasoncodes.H new file mode 100644 index 000000000..d01755bc0 --- /dev/null +++ b/src/include/usr/i2c/nvdimmddreasoncodes.H @@ -0,0 +1,89 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/usr/i2c/nvdimmddreasoncodes.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +/** + * @file nvdimmddreasoncodes.H + * + * @brief Reason codes and module ids for the NVDIMM device driver + * + */ +#ifndef __NVDIMMDDREASONCODES_H +#define __NVDIMMDDREASONCODES_H +// ----------------------------------------------- +// Includes +// ----------------------------------------------- +#include <hbotcompid.H> + +namespace NVDIMM +{ + +/** +* @enum nvdimmModuleid +* +* @brief Module Ids used in created errorlogs. Indicates which +* functions an error log was created in. +* +*/ +enum nvdimmModuleId +{ + NVDIMM_INVALID_MODULE = 0x00, // Invalid Module Id + NVDIMM_PERFORM_OP = 0x01, + NVDIMM_READ = 0x02, + NVDIMM_WRITE = 0x03, + NVDIMM_PREPAREADDRESS = 0x04, + NVDIMM_READATTRIBUTES = 0x05, + NVDIMM_GETI2CMASTERTARGET = 0x06, + NVDIMM_CROSSESNVDIMMPAGEBOUNDARY = 0x07, +}; + +/** + * @enum nvdimmReasonCode + * + * @brief Reasoncodes used to describe what errors are being indicated. + * + */ +enum nvdimmReasonCode +{ + NVDIMM_INVALID_REASONCODE = NVDIMM_COMP_ID | 0x00, // Invalid Reasoncode + NVDIMM_INVALID_OPERATION = NVDIMM_COMP_ID | 0x01, + NVDIMM_INVALID_DEVICE_TYPE = NVDIMM_COMP_ID | 0x02, + NVDIMM_ATTR_INFO_NOT_FOUND = NVDIMM_COMP_ID | 0x03, + NVDIMM_INVALID_CHIP = NVDIMM_COMP_ID | 0x04, + NVDIMM_I2C_MASTER_PATH_ERROR = NVDIMM_COMP_ID | 0x05, + NVDIMM_TARGET_NULL = NVDIMM_COMP_ID | 0x06, + NVDIMM_INVALID_ADDR_OFFSET_SIZE = NVDIMM_COMP_ID | 0x07, + NVDIMM_OVERFLOW_ERROR = NVDIMM_COMP_ID | 0x08, + NVDIMM_I2C_WRITE_PAGE_SIZE_ZERO = NVDIMM_COMP_ID | 0x09, + NVDIMM_INVALID_OFFSET = NVDIMM_COMP_ID | 0x0A, +}; + +enum UserDetailsTypes +{ + NVDIMM_UDT_NO_FORMAT = 0x0, + NVDIMM_UDT_PARAMETERS = 0x1, +}; + +}; // end NVDIMM + +#endif diff --git a/src/include/usr/i2c/nvdimmif.H b/src/include/usr/i2c/nvdimmif.H new file mode 100644 index 000000000..8c332e1c5 --- /dev/null +++ b/src/include/usr/i2c/nvdimmif.H @@ -0,0 +1,56 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/include/usr/i2c/nvdimmif.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2013,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __NVDIMMIF_H +#define __NVDIMMIF_H + +#include <list> +#include "eepromif.H" + +namespace NVDIMM +{ +/** + * @brief Determine if the given dimm target is an NVDIMM + * + * @param[in] i_target : dimm target to check + * + * @return bool - True if the given target is an NVDIMM + */ +bool isNVDIMM( TARGETING::Target * i_target ); + +/** + * @brief Return a set of information related to every unique + * NVDIMM in the system + * + * @param[out] o_info - list of NVDIMM Information + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +void getNVDIMMs( std::list<EEPROM::EepromInfo_t>& o_info ); + +}; // end namespace NVDIMM + +#endif // end __NVDIMMIF_H + diff --git a/src/usr/i2c/HBconfig b/src/usr/i2c/HBconfig index dc68e5cfb..91579f80d 100644 --- a/src/usr/i2c/HBconfig +++ b/src/usr/i2c/HBconfig @@ -8,3 +8,8 @@ config TPM_NUVOTON default y help Enable Nuvoton TPM I2C driver + +config NVDIMM + default n + help + Enable NVDIMM I2C support diff --git a/src/usr/i2c/eepromdd.C b/src/usr/i2c/eepromdd.C index 7c1fb69fc..79a78323d 100755 --- a/src/usr/i2c/eepromdd.C +++ b/src/usr/i2c/eepromdd.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2017 */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -50,6 +50,7 @@ #include <i2c/i2cif.H> #include "eepromdd.H" #include "errlud_i2c.H" +#include <i2c/nvdimmif.H> // ---------------------------------------------- // Globals @@ -2059,6 +2060,11 @@ void getEEPROMs( std::list<EepromInfo_t>& o_info ) &l_dimmFilter ); for( ; dimm_itr; ++dimm_itr ) { + #ifdef CONFIG_NVDIMM + // Skip if this is an NVDIMM as this will get added later + if (NVDIMM::isNVDIMM( *dimm_itr )) + continue; + #endif add_to_list( o_info, *dimm_itr ); } diff --git a/src/usr/i2c/errlud_i2c.C b/src/usr/i2c/errlud_i2c.C index 0cdf97808..6442e635b 100644 --- a/src/usr/i2c/errlud_i2c.C +++ b/src/usr/i2c/errlud_i2c.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2014,2016 */ +/* Contributors Listed Below - COPYRIGHT 2014,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -30,9 +30,11 @@ #include "errlud_i2c.H" #include <i2c/i2creasoncodes.H> #include <i2c/eepromddreasoncodes.H> +#include <i2c/nvdimmddreasoncodes.H> #include <devicefw/driverif.H> #include "eepromdd.H" #include "i2c.H" +#include "nvdimmdd.H" namespace I2C { @@ -285,3 +287,118 @@ UdEepromParms::~UdEepromParms() } } // end EEPROM namespace + +namespace NVDIMM +{ + +//------------------------------------------------------------------------------ +// NVDIMM User Details +//------------------------------------------------------------------------------ +UdNvdimmParms::UdNvdimmParms( uint8_t i_opType, + TARGETING::Target * i_target, + uint64_t i_buflen, + nvdimm_addr_t &i_i2cInfo ) +{ + // Set up Ud instance variables + iv_CompId = NVDIMM_COMP_ID; + iv_Version = 2; + iv_SubSection = NVDIMM_UDT_PARAMETERS; + + //***** Memory Layout ***** + // 1 byte : Op Type Description + // 1 byte : Op Type (DeviceFW::OperationType) + // 4 bytes : Target HUID + // 8 bytes : Length of In/Out Buffer + // 8 bytes : Offset + // 8 bytes : Port + // 8 bytes : Engine + // 8 bytes : Device Address + // 1 byte : Address Size + // 8 bytes : Write Page Size + // 8 bytes : Device Size (in KB) + // 8 bytes : Chip Count + // 8 bytes : Write Cycle Time + + char * l_pBuf = reinterpret_cast<char *>( + reallocUsrBuf(sizeof(uint8_t)*2 + +sizeof(uint32_t) + +sizeof(uint64_t)*6 + +sizeof(uint8_t) + +sizeof(uint64_t)*3 )); + + uint64_t tmp64 = 0; + uint32_t tmp32 = 0; + uint8_t tmp8 = 0; + + if( i_opType == DeviceFW::READ ) + { + tmp8 = 0; + } + else if( i_opType == DeviceFW::WRITE ) + { + tmp8 = 1; + } + else + { + tmp8 = 2; + } + memcpy(l_pBuf, &tmp8, sizeof(tmp8)); + l_pBuf += sizeof(tmp8); + + tmp8 = i_opType; + memcpy(l_pBuf, &tmp8, sizeof(tmp8)); + l_pBuf += sizeof(tmp8); + + tmp32 = TARGETING::get_huid(i_target); + memcpy(l_pBuf, &tmp32, sizeof(tmp32)); + l_pBuf += sizeof(tmp32); + + tmp64 = i_buflen; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp64 = i_i2cInfo.offset; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp64 = i_i2cInfo.port; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp64 = i_i2cInfo.engine; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp64 = i_i2cInfo.devAddr; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp8 = static_cast<uint8_t>(i_i2cInfo.addrSize); + memcpy(l_pBuf, &tmp8, sizeof(tmp8)); + l_pBuf += sizeof(tmp8); + + tmp64 = i_i2cInfo.writePageSize; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp64 = i_i2cInfo.devSize_KB; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp64 = i_i2cInfo.chipCount; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + + tmp64 = i_i2cInfo.writeCycleTime; + memcpy(l_pBuf, &tmp64, sizeof(tmp64)); + l_pBuf += sizeof(tmp64); + +} + +//------------------------------------------------------------------------------ +UdNvdimmParms::~UdNvdimmParms() +{ + +} + +} // end NVDIMM namespace diff --git a/src/usr/i2c/errlud_i2c.H b/src/usr/i2c/errlud_i2c.H index 7cea03b6e..bd11a1be2 100644 --- a/src/usr/i2c/errlud_i2c.H +++ b/src/usr/i2c/errlud_i2c.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2014,2015 */ +/* Contributors Listed Below - COPYRIGHT 2014,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -35,6 +35,7 @@ #include <devicefw/driverif.H> #include "i2c_common.H" #include "eepromdd.H" +#include "nvdimmdd.H" namespace I2C { @@ -111,5 +112,40 @@ class UdEepromParms : public ERRORLOG::ErrlUserDetails } // end EEPROM namespace +namespace NVDIMM +{ +/** + * @class UdNvdimmParms + * + * Adds NVDIMM information to an error log as user detail data + */ +class UdNvdimmParms : public ERRORLOG::ErrlUserDetails +{ + public: + /** + * @brief Constructor + * + * @param i_opType Operation Type + * @param i_target Chip being detected/acted upon + * @param i_buflen Length of In/Out Buffer + * @param i_i2cInfo Miscellaneous Parameters + */ + UdNvdimmParms( uint8_t i_opType, + TARGETING::Target * i_target, + uint64_t i_buflen, + nvdimm_addr_t &i_i2cInfo ); + + /** + * @brief Destructor + */ + virtual ~UdNvdimmParms(); + + private: + // Disabled + UdNvdimmParms(UdNvdimmParms &); + UdNvdimmParms & operator=(UdNvdimmParms &); +}; + +} // end NVDIMM namespace #endif diff --git a/src/usr/i2c/i2c.C b/src/usr/i2c/i2c.C index 4045ab26c..05c3e40ea 100755 --- a/src/usr/i2c/i2c.C +++ b/src/usr/i2c/i2c.C @@ -55,6 +55,7 @@ #include <secureboot/service.H> #include <i2c/eepromif.H> #include <i2c/tpmddif.H> +#include <i2c/nvdimmif.H> // TODO RTC 94872 Remove the following include in a future commit #include <initservice/initserviceif.H> @@ -4112,6 +4113,17 @@ void getDeviceInfo( TARGETING::Target* i_i2cMaster, TRUSTEDBOOT::getTPMs(tpmList); #endif + //Find all NVDIMMs + #ifdef CONFIG_NVDIMM + TARGETING::ATTR_MODEL_type l_chipModel = + pProc->getAttr<TARGETING::ATTR_MODEL>(); + std::list<EEPROM::EepromInfo_t> l_nvdimmInfo; + if (l_chipModel != TARGETING::MODEL_CUMULUS) + { + NVDIMM::getNVDIMMs( l_nvdimmInfo ); + } + #endif + for(auto const& i2cm : l_i2cInfo) { TRACDCOMP(g_trac_i2c,"i2c loop - eng=%.8X", TARGETING::get_huid(pChipTarget)); @@ -4224,6 +4236,43 @@ void getDeviceInfo( TARGETING::Target* i_i2cMaster, } //end of tpm iter #endif + #ifdef CONFIG_NVDIMM + for (auto& l_nv : l_nvdimmInfo) + { + TRACDCOMP(g_trac_i2c,"nvdimm loop - eng=%.8X, port=%.8X", TARGETING::get_huid(l_nv.i2cMaster), l_nv.engine ); + DeviceInfo_t l_currentDI; + + //ignore the devices that aren't on the current target + if( l_nv.i2cMaster != pChipTarget ) + { + TRACDCOMP(g_trac_i2c,"skipping unmatched i2cmaster"); + continue; + } + + //skip the devices that are on a different engine + else if( l_nv.engine != i2cm.engine) + { + TRACDCOMP(g_trac_i2c,"skipping umatched engine"); + continue; + } + + l_currentDI.assocNode = assocNode; + l_currentDI.assocProc = assocProc; + l_currentDI.masterChip = l_nv.i2cMaster; + l_currentDI.engine = l_nv.engine; + l_currentDI.masterPort = l_nv.port; + l_currentDI.addr = l_nv.devAddr; + l_currentDI.slavePort = 0xFF; + l_currentDI.busFreqKhz = (l_nv.busFreq) + / FREQ_CONVERSION::HZ_PER_KHZ; + strcpy(l_currentDI.deviceLabel, + "?unknown,unknown,nvdimm,nvdimm"); + + TRACDCOMP(g_trac_i2c,"Adding addr=%X", l_nv.devAddr); + o_deviceInfo.push_back(l_currentDI); + } //end of nvdimm iter + #endif + } //end of i2cm #if CONFIG_INCLUDE_XML_OPENPOWER diff --git a/src/usr/i2c/i2c.mk b/src/usr/i2c/i2c.mk index 33d2accda..822f1d948 100644 --- a/src/usr/i2c/i2c.mk +++ b/src/usr/i2c/i2c.mk @@ -25,3 +25,4 @@ # common objects with runtime OBJS += eepromdd.o OBJS += errlud_i2c.o +OBJS += $(if $(CONFIG_NVDIMM),nvdimmdd.o,) diff --git a/src/usr/i2c/makefile b/src/usr/i2c/makefile index a226d6754..a0a7e0f26 100644 --- a/src/usr/i2c/makefile +++ b/src/usr/i2c/makefile @@ -5,7 +5,7 @@ # # OpenPOWER HostBoot Project # -# Contributors Listed Below - COPYRIGHT 2011,2015 +# Contributors Listed Below - COPYRIGHT 2011,2018 # [+] International Business Machines Corp. # # diff --git a/src/usr/i2c/nvdimmdd.C b/src/usr/i2c/nvdimmdd.C new file mode 100755 index 000000000..b0f1f6aca --- /dev/null +++ b/src/usr/i2c/nvdimmdd.C @@ -0,0 +1,1436 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/i2c/nvdimmdd.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +/** + * @file nvdimmdd.C + * + * @brief Implementation of the NVDIMM device driver, + * which will access various NVDIMMs within the + * system via the I2C device driver + * + */ + +// ---------------------------------------------- +// Includes +// ---------------------------------------------- +#include <string.h> +#include <sys/time.h> +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <errl/errlmanager.H> +#include <errl/errludtarget.H> +#include <errl/errludstring.H> +#include <targeting/common/targetservice.H> +#include <targeting/common/utilFilter.H> +#include <devicefw/driverif.H> +#include <i2c/nvdimmddreasoncodes.H> +#include <i2c/nvdimmif.H> +#include <i2c/eepromif.H> +#include <i2c/i2creasoncodes.H> +#include <i2c/i2cif.H> +#include "nvdimmdd.H" +#include "errlud_i2c.H" + +// ---------------------------------------------- +// Globals +// ---------------------------------------------- +mutex_t g_nvdimmMutex = MUTEX_INITIALIZER; + +// ---------------------------------------------- +// Trace definitions +// ---------------------------------------------- +trace_desc_t* g_trac_nvdimm = nullptr; +TRAC_INIT( & g_trac_nvdimm, NVDIMM_COMP_NAME, KILOBYTE ); + +trace_desc_t* g_trac_nvdimmr = nullptr; +TRAC_INIT( & g_trac_nvdimmr, "NVDIMMR", KILOBYTE ); + +// Easy macro replace for unit testing +#define TRACUCOMP(args...) TRACFCOMP(args) +//#define TRACUCOMP(args...) + +// ---------------------------------------------- +// Defines +// ---------------------------------------------- +#define MAX_BYTE_ADDR 2 +#define NVDIMM_MAX_RETRIES 2 +#define MCA_PER_MCS 2 +#define DIMM_PER_MCA 2 +// ---------------------------------------------- + + +namespace +{ + +// ------------------------------------------------------------------ +// errorIsRetryable +// ------------------------------------------------------------------ +static bool errorIsRetryable(uint16_t reasonCode) +{ + return reasonCode == I2C::I2C_NACK_ONLY_FOUND || + reasonCode == I2C::I2C_ARBITRATION_LOST_ONLY_FOUND; +} + +} + +namespace NVDIMM +{ + +// Register the perform Op with the routing code for DIMMs. +DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD, + DeviceFW::NVDIMM, + TARGETING::TYPE_DIMM, + nvdimmPerformOp ); + +// ------------------------------------------------------------------ +// nvdimmPerformOp +// ------------------------------------------------------------------ +errlHndl_t nvdimmPerformOp( 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 = nullptr; + TARGETING::Target * i2cMasterTarget = nullptr; + nvdimm_addr_t i2cInfo; + + i2cInfo.offset = va_arg( i_args, uint64_t ); + + TRACDCOMP( g_trac_nvdimm, + ENTER_MRK"nvdimmPerformOp()" ); + + TRACUCOMP (g_trac_nvdimm, ENTER_MRK"nvdimmPerformOp(): " + "i_opType=%d, offset=%x, len=%d", + (uint64_t) i_opType, i2cInfo.offset, io_buflen); + + void * l_pBuffer = io_buffer; + size_t l_currentOpLen = io_buflen; + size_t l_remainingOpLen = io_buflen; + + do + { + // Read Attributes needed to complete the operation + err = nvdimmReadAttributes( i_target, + i2cInfo ); + + if( err ) + { + break; + } + + size_t l_snglChipSize = (i2cInfo.devSize_KB * KILOBYTE) + / i2cInfo.chipCount; + + // Check to see if we need to find a new target for + // the I2C Master + err = nvdimmGetI2CMasterTarget( i_target, + i2cInfo, + i2cMasterTarget ); + + if( err ) + { + break; + } + + // Check that the offset + data length is less than device max size + if ( ( i2cInfo.offset + io_buflen ) > + ( i2cInfo.devSize_KB * KILOBYTE ) ) + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmPerformOp(): Device Overflow! " + "p/e/dA=%d/%d/0x%X, offset=0x%X, len=0x%X " + "devSizeKB=0x%X", i2cInfo.port, i2cInfo.engine, + i2cInfo.devAddr, i2cInfo.offset, io_buflen, + i2cInfo.devSize_KB); + + + /*@ + * @errortype + * @reasoncode NVDIMM_OVERFLOW_ERROR + * @severity ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_PERFORM_OP + * @userdata1[0:31] Offset + * @userdata1[32:63] Buffer Length + * @userdata2 Device Max Size (in KB) + * @devdesc I2C Buffer Length + Offset > Max Size + * @custdesc A problem occurred during the IPL of the + * system: I2C buffer offset is too large. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_PERFORM_OP, + NVDIMM_OVERFLOW_ERROR, + TWO_UINT32_TO_UINT64( + i2cInfo.offset, + io_buflen ), + i2cInfo.devSize_KB, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + err->collectTrace( NVDIMM_COMP_NAME ); + + break; + } + + // Keep first op length within a chip + if( ( i2cInfo.offset + io_buflen ) > l_snglChipSize ) + { + l_currentOpLen = l_snglChipSize - i2cInfo.offset; + } + + TRACFCOMP( g_trac_nvdimm, + "nvdimmPerformOp(): i_opType=%d " + "p/e/dA=%d/%d/0x%X, offset=0x%X, len=0x%X, " + "snglChipKB=0x%X, chipCount=0x%X, devSizeKB=0x%X", i_opType, + i2cInfo.port, i2cInfo.engine, i2cInfo.devAddr, + i2cInfo.offset, io_buflen, l_snglChipSize, + i2cInfo.chipCount, i2cInfo.devSize_KB); + + // Do the read or write + while(l_remainingOpLen > 0) + { + if( i_opType == DeviceFW::READ ) + { + err = nvdimmRead( i2cMasterTarget, + l_pBuffer, + l_currentOpLen, + i2cInfo ); + } + else if( i_opType == DeviceFW::WRITE ) + { + err = nvdimmWrite( i2cMasterTarget, + l_pBuffer, + l_currentOpLen, + i2cInfo ); + } + else + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmPerformOp(): " + "Invalid NVDIMM Operation!"); + + /*@ + * @errortype + * @reasoncode NVDIMM_INVALID_OPERATION + * @severity ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_PERFORM_OP + * @userdata1 Operation Type + * @userdata2 Chip to Access + * @devdesc Invalid operation type. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_PERFORM_OP, + NVDIMM_INVALID_OPERATION, + i_opType, + 0, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + err->collectTrace( NVDIMM_COMP_NAME ); + } + + if ( err ) + { + break; + } + + // Adjust the buffer pointer and remaining op length + l_pBuffer = (void *)(reinterpret_cast<uint64_t>(l_pBuffer) + + l_currentOpLen); + l_remainingOpLen -= l_currentOpLen; + + if( l_remainingOpLen > l_snglChipSize ) + { + // Keep next op length within a chip + l_currentOpLen = l_snglChipSize; + } + else if( l_remainingOpLen > 0 ) + { + // Set next op length to what is left to do + l_currentOpLen = l_remainingOpLen; + } + else + { + // Break if there is nothing left to do + break; + } + + // Prepare the address at the start of next NVDIMM + i2cInfo.offset = 0; + i2cInfo.devAddr += NVDIMM_DEVADDR_INC; + } // Do the read or write + } while( 0 ); + + // If there is an error, add parameter info to log + if ( err != nullptr ) + { + NVDIMM::UdNvdimmParms( i_opType, + i_target, + io_buflen, + i2cInfo ) + .addToLog(err); + } + + TRACDCOMP( g_trac_nvdimm, + EXIT_MRK"nvdimmPerformOp() - %s", + ((nullptr == err) ? "No Error" : "With Error") ); + + return err; +} // end nvdimmPerformOp + +// ------------------------------------------------------------------ +// crossesNvdimmPageBoundary +// ------------------------------------------------------------------ +errlHndl_t crossesNvdimmPageBoundary( uint64_t i_offset, + size_t i_buflen ) +{ + + TRACUCOMP(g_trac_nvdimm, + ENTER_MRK"crossesNvdimmPageBoundary()"); + + errlHndl_t err = nullptr; + + if(i_offset >= NVDIMM_PAGE_SIZE || (i_offset+i_buflen) >= NVDIMM_PAGE_SIZE) + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"crossesNvdimmPageBoundary() - offset 0x%X, buflen 0x%X" + "crossed the page size boundary 0x%X", + i_offset, i_buflen, NVDIMM_PAGE_SIZE ); + + /*@ + * @errortype + * @reasoncode NVDIMM_INVALID_OFFSET + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_CROSSESNVDIMMPAGEBOUNDARY + * @userdata1 Offset attempting to access + * @userdata2 Requested buffer length + * @devdesc NVDIMM register offset out of bound + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_CROSSESNVDIMMPAGEBOUNDARY, + NVDIMM_INVALID_OFFSET, + static_cast<uint64_t>(i_offset), + static_cast<uint64_t>(i_buflen)); + + // Could be FSP or HB code's fault + err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_MED); + + err->collectTrace( NVDIMM_COMP_NAME ); + + return err; + + } + + TRACUCOMP(g_trac_nvdimm, + EXIT_MRK"crossesNvdimmPageBoundary()"); + + return err; +} + +// ------------------------------------------------------------------ +// nvdimmRead +// ------------------------------------------------------------------ +errlHndl_t nvdimmRead ( TARGETING::Target * i_target, + void * o_buffer, + size_t i_buflen, + nvdimm_addr_t i_i2cInfo ) +{ + errlHndl_t err = nullptr; + uint8_t byteAddr[MAX_BYTE_ADDR]; + size_t byteAddrSize = 0; + bool unlock = false; + + TRACUCOMP( g_trac_nvdimm, + ENTER_MRK"nvdimmRead()" ); + + do + { + TRACUCOMP( g_trac_nvdimmr, + "NVDIMM READ START : Offset %.2X : Len %d", + i_i2cInfo.offset, i_buflen ); + + + // Check to see if the Read operation straddles the NVDIMM page + // boundary + err = crossesNvdimmPageBoundary( i_i2cInfo.offset, i_buflen ); + if( err ) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimmRead()::crossesNvdimmPageBoundary()"); + break; + } + + // Set addressing parameters + err = nvdimmPrepareAddress( i_target, + &byteAddr, + byteAddrSize, + i_i2cInfo); + + if( err ) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimmRead()::nvdimmPrepareAddress()"); + break; + } + + // Lock to sequence operations + mutex_lock( &g_nvdimmMutex ); + unlock = true; + + // Read data from NV controller + err = nvdimmReadData( i_target, + o_buffer, + i_buflen, + &byteAddr, + byteAddrSize, + i_i2cInfo ); + if( err ) + { + TRACFCOMP(g_trac_nvdimm, + "Failed reading data: original read"); + break; + } + + TRACUCOMP( g_trac_nvdimmr, + "NVDIMM READ END : Offset %.2X : Len %d : %016llx", + i_i2cInfo.offset, i_buflen, *((uint64_t*)o_buffer) ); + + } while( 0 ); + + if (unlock) + mutex_unlock( & g_nvdimmMutex ); + + TRACUCOMP( g_trac_nvdimm, + EXIT_MRK"nvdimmRead()" ); + + return err; +} // end nvdimmRead + + +// ------------------------------------------------------------------ +// nvdimmReadData +// ------------------------------------------------------------------ +errlHndl_t nvdimmReadData( TARGETING::Target * i_target, + void * o_buffer, + size_t i_buflen, + void * i_byteAddress, + size_t i_byteAddressSize, + nvdimm_addr_t i_i2cInfo ) +{ + errlHndl_t l_err = nullptr; + errlHndl_t err_retryable = nullptr; + + TRACUCOMP(g_trac_nvdimm, + ENTER_MRK"nvdimmReadData()"); + do + { + /************************************************************/ + /* Attempt read multiple times ONLY on retryable fails */ + /************************************************************/ + for (uint8_t retry = 0; + retry <= NVDIMM_MAX_RETRIES; + retry++) + { + + // Only write the byte address if we have data to write + if( 0 != i_byteAddressSize ) + { + // Use the I2C OFFSET Interface for the READ + l_err = deviceOp( DeviceFW::READ, + i_target, + o_buffer, + i_buflen, + DEVICE_I2C_ADDRESS_OFFSET( + i_i2cInfo.port, + i_i2cInfo.engine, + i_i2cInfo.devAddr, + i_byteAddressSize, + reinterpret_cast<uint8_t*>(i_byteAddress))); + + if( l_err ) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimmReadData(): I2C Read-Offset failed on " + "%d/%d/0x%X aS=%d", + i_i2cInfo.port, i_i2cInfo.engine, + i_i2cInfo.devAddr, i_byteAddressSize); + TRACFBIN(g_trac_nvdimm, "i_byteAddress[]", + i_byteAddress, i_byteAddressSize); + + // Don't break here -- error handled below + } + } + else + { + // Do the actual read via I2C + l_err = deviceOp( DeviceFW::READ, + i_target, + o_buffer, + i_buflen, + DEVICE_I2C_ADDRESS( i_i2cInfo.port, + i_i2cInfo.engine, + i_i2cInfo.devAddr ) ); + + if( l_err ) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimmReadData(): I2C Read failed on " + "%d/%d/0x%0X", i_i2cInfo.port, i_i2cInfo.engine, + i_i2cInfo.devAddr); + + // Don't break here -- error handled below + } + } + + if ( l_err == nullptr ) + { + // Operation completed successfully + // break from retry loop + break; + } + else if ( !errorIsRetryable( l_err->reasonCode() ) ) + { + // Only retry on errorIsRetryable() failures: break from retry loop + TRACFCOMP( g_trac_nvdimm, ERR_MRK"nvdimmReadData(): Non-Nack " + "Error: rc=0x%X, tgt=0x%X, No Retry (retry=%d)", + l_err->reasonCode(), + TARGETING::get_huid(i_target), retry); + + l_err->collectTrace(NVDIMM_COMP_NAME); + + // break from retry loop + break; + } + else // Handle retryable error + { + // If op will be attempted again: save log and continue + if ( retry < NVDIMM_MAX_RETRIES ) + { + // Only save original retryable error + if ( err_retryable == nullptr ) + { + // Save original retryable error + err_retryable = l_err; + + TRACFCOMP( g_trac_nvdimm, ERR_MRK"nvdimmReadData(): " + "Retryable Error rc=0x%X, eid=0x%X, tgt=0x%X, " + "retry/MAX=%d/%d. Save error and retry", + err_retryable->reasonCode(), + err_retryable->eid(), + TARGETING::get_huid(i_target), + retry, NVDIMM_MAX_RETRIES); + + err_retryable->collectTrace(NVDIMM_COMP_NAME); + } + else + { + // Add data to original retryable error + TRACFCOMP( g_trac_nvdimm, ERR_MRK"nvdimmReadData(): " + "Another Retryable Error rc=0x%X, eid=0x%X " + "plid=0x%X, tgt=0x%X, retry/MAX=%d/%d. " + "Delete error and retry", + l_err->reasonCode(), l_err->eid(), l_err->plid(), + TARGETING::get_huid(i_target), + retry, NVDIMM_MAX_RETRIES); + + ERRORLOG::ErrlUserDetailsString( + "Another Retryable ERROR found") + .addToLog(err_retryable); + + // Delete this new retryable error + delete l_err; + l_err = nullptr; + } + + // continue to retry + continue; + } + else // no more retries: trace and break + { + TRACFCOMP( g_trac_nvdimm, ERR_MRK"nvdimmReadData(): " + "Error rc=0x%X, eid=%d, tgt=0x%X. No More " + "Retries (retry/MAX=%d/%d). Returning Error", + l_err->reasonCode(), l_err->eid(), + TARGETING::get_huid(i_target), + retry, NVDIMM_MAX_RETRIES); + + l_err->collectTrace(NVDIMM_COMP_NAME); + + // break from retry loop + break; + } + } + + } // end of retry loop + + // Handle saved retryable error, if any + if (err_retryable) + { + if (l_err) + { + // commit original retryable error with new err PLID + err_retryable->plid(l_err->plid()); + TRACFCOMP(g_trac_nvdimm, "nvdimmReadData(): Committing saved retryable " + "l_err eid=0x%X with plid of returned err: 0x%X", + err_retryable->eid(), err_retryable->plid()); + + ERRORLOG::ErrlUserDetailsTarget(i_target) + .addToLog(err_retryable); + + errlCommit(err_retryable, NVDIMM_COMP_ID); + } + else + { + // Since we eventually succeeded, delete original retryable error + TRACFCOMP(g_trac_nvdimm, "nvdimmReadData(): Op successful, " + "deleting saved retryable err eid=0x%X, plid=0x%X", + err_retryable->eid(), err_retryable->plid()); + + delete err_retryable; + err_retryable = nullptr; + } + } + + }while( 0 ); + + TRACUCOMP(g_trac_nvdimm, + EXIT_MRK"nvdimmReadData"); + return l_err; +} + + + +// ------------------------------------------------------------------ +// nvdimmWrite +// ------------------------------------------------------------------ +errlHndl_t nvdimmWrite ( TARGETING::Target * i_target, + void * io_buffer, + size_t & io_buflen, + nvdimm_addr_t i_i2cInfo ) +{ + errlHndl_t err = nullptr; + uint8_t byteAddr[MAX_BYTE_ADDR]; + size_t byteAddrSize = 0; + uint8_t * newBuffer = nullptr; + bool needFree = false; + uint32_t data_left = 0; + uint32_t diff_wps = 0; + + TRACDCOMP( g_trac_nvdimm, + ENTER_MRK"nvdimmWrite()" ); + + do + { + TRACUCOMP( g_trac_nvdimm, + "NVDIMM WRITE START : Offset %.2X : Len %d : %016llx", + i_i2cInfo.offset, io_buflen, *((uint64_t*)io_buffer) ); + + + // Prepare address parameters + err = nvdimmPrepareAddress( i_target, + &byteAddr, + byteAddrSize, + i_i2cInfo); + + if( err ) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimmWrite()::nvdimmPrepareAddress()"); + break; + } + + // Check for writePageSize of zero + if ( i_i2cInfo.writePageSize == 0 ) + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmWrite(): writePageSize is 0!"); + + /*@ + * @errortype + * @reasoncode NVDIMM_I2C_WRITE_PAGE_SIZE_ZERO + * @severity ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_WRITE + * @userdata1 HUID of target + * @devdesc I2C write page size is zero. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_WRITE, + NVDIMM_I2C_WRITE_PAGE_SIZE_ZERO, + TARGETING::get_huid(i_target), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + err->collectTrace( NVDIMM_COMP_NAME ); + + break; + } + + // Make sure offset and buffer length are within NVDIMM page boundary + err = crossesNvdimmPageBoundary( i_i2cInfo.offset, + io_buflen ); + if( err ) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimmWrite()::crossesNvdimmPageBoundary()"); + break; + } + + // NVDIMM devices have write page boundaries, so when necessary + // need to split up command into multiple write operations + + // Setup a max-size buffer of writePageSize + size_t newBufLen = i_i2cInfo.writePageSize; + newBuffer = static_cast<uint8_t*>(malloc( newBufLen )); + needFree = true; + + // Point a uint8_t ptr at io_buffer for array addressing below + uint8_t * l_data_ptr = reinterpret_cast<uint8_t*>(io_buffer); + + // Lock for operation sequencing + mutex_lock( &g_nvdimmMutex ); + + // variables to store different amount of data length + size_t total_bytes_written = 0; + + while( total_bytes_written < io_buflen ) + { + // Determine how much data can be written in this loop + // Can't go over a writePageSize boundary + + // Total data left to write + data_left = io_buflen - total_bytes_written; + + // Difference to next writePageSize boundary + diff_wps = i_i2cInfo.writePageSize - + (i_i2cInfo.offset % i_i2cInfo.writePageSize); + + // Add the data the user wanted to write + memcpy( newBuffer, + &l_data_ptr[total_bytes_written], + newBufLen ); + + // Setup offset/address parms + err = nvdimmPrepareAddress( i_target, + &byteAddr, + byteAddrSize, + i_i2cInfo ); + + if( err ) + { + TRACFCOMP(g_trac_nvdimm, + ERR_MRK"nvdimmWrite::nvdimmPrepareAddress()::loop version"); + break; + } + + TRACUCOMP(g_trac_nvdimm,"nvdimmWrite() Loop: %d/%d/0x%X " + "writeBuflen=%d, offset=0x%X, bAS=%d, diffs=%d/%d", + i_i2cInfo.port, i_i2cInfo.engine, i_i2cInfo.devAddr, + newBufLen, i_i2cInfo.offset, byteAddrSize, + data_left, diff_wps); + + // Perform the requested write operation + err = nvdimmWriteData( i_target, + newBuffer, + newBufLen, + &byteAddr, + byteAddrSize, + i_i2cInfo ); + + if ( err ) + { + // Can't assume that anything was written if + // there was an error, so no update to total_bytes_written + // for this loop + TRACFCOMP(g_trac_nvdimm, + "Failed writing data: original nvdimm write"); + break; + } + + // Wait for NVDIMM to write data to its internal memory + // i_i2cInfo.writeCycleTime value in milliseconds + nanosleep( 0, i_i2cInfo.writeCycleTime * NS_PER_MSEC ); + + // Update how much data was written + total_bytes_written += newBufLen; + + // Update offset + i_i2cInfo.offset += newBufLen; + + TRACUCOMP(g_trac_nvdimm,"nvdimmWrite() Loop End: " + "writeBuflen=%d, offset=0x%X, t_b_w=%d, io_buflen=%d", + newBufLen, i_i2cInfo.offset, + total_bytes_written, io_buflen); + } // end of write for-loop + + // Release mutex lock + mutex_unlock( &g_nvdimmMutex ); + + // Set how much data was actually written + io_buflen = total_bytes_written; + + + TRACSCOMP( g_trac_nvdimmr, + "NVDIMM WRITE END : Offset %.2X : Len %d", + i_i2cInfo.offset, io_buflen ); + } while( 0 ); + + // Free memory + if( needFree ) + { + free( newBuffer ); + } + + TRACDCOMP( g_trac_nvdimm, + EXIT_MRK"nvdimmWrite()" ); + + return err; + +} // end nvdimmWrite + +// ------------------------------------------------------------------ +// nvdimmWriteData +// ------------------------------------------------------------------ +errlHndl_t nvdimmWriteData( TARGETING::Target * i_target, + void * i_dataToWrite, + size_t i_dataLen, + void * i_byteAddress, + size_t i_byteAddressSize, + nvdimm_addr_t i_i2cInfo ) +{ + TRACDCOMP( g_trac_nvdimm, + ENTER_MRK"nvdimmWriteData()"); + errlHndl_t err = nullptr; + errlHndl_t err_retryable = nullptr; + do + { + /***********************************************************/ + /* Attempt write multiple times ONLY on retryable fails */ + /***********************************************************/ + for (uint8_t retry = 0; + retry <= NVDIMM_MAX_RETRIES; + retry++) + { + // Do the actual data write + err = deviceOp( DeviceFW::WRITE, + i_target, + i_dataToWrite, + i_dataLen, + DEVICE_I2C_ADDRESS_OFFSET( + i_i2cInfo.port, + i_i2cInfo.engine, + i_i2cInfo.devAddr, + i_byteAddressSize, + reinterpret_cast<uint8_t*>( + i_byteAddress))); + + + if ( err == nullptr ) + { + // Operation completed successfully + // break from retry loop + break; + } + else if ( !errorIsRetryable( err->reasonCode() ) ) + { + // Only retry on errorIsRetryable() failures: break from retry loop + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmWriteData(): I2C " + "Write Non-Retryable fail %d/%d/0x%X, " + "ldl=%d, offset=0x%X, aS=%d, retry=%d", + i_i2cInfo.port, i_i2cInfo.engine, + i_i2cInfo.devAddr, i_dataLen, + i_i2cInfo.offset, i_i2cInfo.addrSize, retry); + + err->collectTrace(NVDIMM_COMP_NAME); + + // break from retry loop + break; + } + else // Handle retryable error + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmWriteData(): I2C " + "Write retryable fail %d/%d/0x%X, " + "ldl=%d, offset=0x%X, aS=%d, writePageSize = %x", + i_i2cInfo.port, i_i2cInfo.engine, + i_i2cInfo.devAddr, i_dataLen, + i_i2cInfo.offset, i_i2cInfo.addrSize, + i_i2cInfo.writePageSize); + + // If op will be attempted again: save error and continue + if ( retry < NVDIMM_MAX_RETRIES ) + { + // Only save original retryable error + if ( err_retryable == nullptr ) + { + // Save original retryable error + err_retryable = err; + + TRACFCOMP( g_trac_nvdimm, ERR_MRK"nvdimmWriteData(): " + "Error rc=0x%X, eid=0x%X plid=0x%X, " + "tgt=0x%X, retry/MAX=%d/%d. Save error " + "and retry", + err_retryable->reasonCode(), + err_retryable->eid(), + err_retryable->plid(), + TARGETING::get_huid(i_target), + retry, NVDIMM_MAX_RETRIES); + + err_retryable->collectTrace(NVDIMM_COMP_NAME); + } + else + { + // Add data to original retryable error + TRACFCOMP( g_trac_nvdimm, ERR_MRK"nvdimmWriteData(): " + "Another Retryable Error rc=0x%X, eid=0x%X " + "plid=0x%X, tgt=0x%X, retry/MAX=%d/%d. " + "Delete error and retry", + err->reasonCode(), err->eid(), + err->plid(), + TARGETING::get_huid(i_target), + retry, NVDIMM_MAX_RETRIES); + + ERRORLOG::ErrlUserDetailsString( + "Another retryable ERROR found") + .addToLog(err_retryable); + + // Delete this new retryable error + delete err; + err = nullptr; + } + + // continue to retry + continue; + } + else // no more retries: trace and break + { + TRACFCOMP( g_trac_nvdimm, ERR_MRK"nvdimmWriteData(): " + "Error rc=0x%X, tgt=0x%X. No More Retries " + "(retry/MAX=%d/%d). Returning Error", + err->reasonCode(), + TARGETING::get_huid(i_target), + retry, NVDIMM_MAX_RETRIES); + + err->collectTrace(NVDIMM_COMP_NAME); + + // break from retry loop + break; + } + } + + } // end of retry loop + /***********************************************************/ + + // Handle saved retryable errors, if any + if (err_retryable) + { + if (err) + { + // commit original retryable error with new err PLID + err_retryable->plid(err->plid()); + TRACFCOMP(g_trac_nvdimm, "nvdimmWriteData(): Committing saved " + "retryable err eid=0x%X with plid of returned err: " + "0x%X", + err_retryable->eid(), err_retryable->plid()); + + ERRORLOG::ErrlUserDetailsTarget(i_target) + .addToLog(err_retryable); + + errlCommit(err_retryable, NVDIMM_COMP_ID); + } + else + { + // Since we eventually succeeded, delete original retryable error + TRACFCOMP(g_trac_nvdimm, "nvdimmWriteData(): Op successful, " + "deleting saved retryable err eid=0x%X, plid=0x%X", + err_retryable->eid(), err_retryable->plid()); + + delete err_retryable; + err_retryable = nullptr; + } + } + }while( 0 ); + TRACDCOMP( g_trac_nvdimm, + EXIT_MRK"nvdimmWriteData()"); + return err; +} + + + +// ------------------------------------------------------------------ +// nvdimmPrepareAddress +// ------------------------------------------------------------------ +errlHndl_t nvdimmPrepareAddress ( TARGETING::Target * i_target, + void * io_buffer, + size_t & o_bufSize, + nvdimm_addr_t i_i2cInfo ) +{ + errlHndl_t err = nullptr; + o_bufSize = 0; + + TRACDCOMP( g_trac_nvdimm, + ENTER_MRK"nvdimmPrepareAddress()" ); + + // NV Controller only supports ONE_BYTE_ADDR + // Throw an error if it's something else + if ( i_i2cInfo.addrSize == ONE_BYTE_ADDR) + { + o_bufSize = 1; + memset( io_buffer, 0x0, o_bufSize ); + *((uint8_t*)io_buffer) = (i_i2cInfo.offset & 0xFFull); + } + else + { + + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmPrepareAddress() - Invalid Device " + "Address Size: 0x%08x", i_i2cInfo.addrSize); + + /*@ + * @errortype + * @reasoncode NVDIMM_INVALID_DEVICE_TYPE + * @severity ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_PREPAREADDRESS + * @userdata1 Address Size (aka Device Type) + * @devdesc The Device type not supported (addrSize) + * @custdesc A problem was detected during the IPL of + * the system: Device type not supported. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_PREPAREADDRESS, + NVDIMM_INVALID_DEVICE_TYPE, + i_i2cInfo.addrSize, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + err->collectTrace( NVDIMM_COMP_NAME ); + } + + TRACDCOMP( g_trac_nvdimm, + EXIT_MRK"nvdimmPrepareAddress()" ); + + return err; +} // end nvdimmPrepareAddress + + +// ------------------------------------------------------------------ +// nvdimmReadAttributes +// ------------------------------------------------------------------ +errlHndl_t nvdimmReadAttributes ( TARGETING::Target * i_target, + nvdimm_addr_t & o_i2cInfo ) +{ + errlHndl_t err = nullptr; + + TRACDCOMP( g_trac_nvdimm, + ENTER_MRK"nvdimmReadAttributes()" ); + + // These variables will be used to hold the NVDIMM attribute data + // 'EepromNvInfo' struct is kept the same as nvdimm_addr_t via the + // attributes. + TARGETING::EepromNvInfo nvdimmData; + + if(!(i_target->tryGetAttr<TARGETING::ATTR_EEPROM_NV_INFO> + ( reinterpret_cast<TARGETING::ATTR_EEPROM_NV_INFO_type&>( nvdimmData) ))) + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmReadAttributes() - ERROR reading " + "attributes for tgt=0x%X", + TARGETING::get_huid(i_target) ); + + /*@ + * @errortype + * @reasoncode NVDIMM_ATTR_INFO_NOT_FOUND + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_READATTRIBUTES + * @userdata1 HUID of target + * @devdesc NVDIMM attribute was not found + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_READATTRIBUTES, + NVDIMM_ATTR_INFO_NOT_FOUND, + TARGETING::get_huid(i_target)); + + // Could be FSP or HB code's fault + err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, + HWAS::SRCI_PRIORITY_MED); + err->addProcedureCallout(HWAS::EPUB_PRC_SP_CODE, + HWAS::SRCI_PRIORITY_MED); + + err->collectTrace( NVDIMM_COMP_NAME ); + + return err; + + } + + // Successful reading of Attribute, so extract the data + o_i2cInfo.port = nvdimmData.port; + o_i2cInfo.devAddr = nvdimmData.devAddr; + o_i2cInfo.engine = nvdimmData.engine; + o_i2cInfo.i2cMasterPath = nvdimmData.i2cMasterPath; + o_i2cInfo.writePageSize = nvdimmData.writePageSize; + o_i2cInfo.devSize_KB = nvdimmData.maxMemorySizeKB; + o_i2cInfo.chipCount = nvdimmData.chipCount; + o_i2cInfo.writeCycleTime = nvdimmData.writeCycleTime; + + // Convert attribute info to nvdimm_addr_size_t enum + if ( nvdimmData.byteAddrOffset == 0x3 ) + { + o_i2cInfo.addrSize = ONE_BYTE_ADDR; + } + else if ( nvdimmData.byteAddrOffset == 0x2 ) + { + o_i2cInfo.addrSize = TWO_BYTE_ADDR; + } + else if ( nvdimmData.byteAddrOffset == 0x1 ) + { + o_i2cInfo.addrSize = ONE_BYTE_ADDR_PAGESELECT; + } + else if ( nvdimmData.byteAddrOffset == 0x0 ) + { + o_i2cInfo.addrSize = ZERO_BYTE_ADDR; + } + else + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmReadAttributes() - INVALID ADDRESS " + "OFFSET SIZE %d!", + o_i2cInfo.addrSize ); + + /*@ + * @errortype + * @reasoncode NVDIMM_INVALID_ADDR_OFFSET_SIZE + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_READATTRIBUTES + * @userdata1 HUID of target + * @userdata2 Address Offset Size + * @devdesc Invalid address offset size + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_READATTRIBUTES, + NVDIMM_INVALID_ADDR_OFFSET_SIZE, + TARGETING::get_huid(i_target), + o_i2cInfo.addrSize, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + err->collectTrace( NVDIMM_COMP_NAME ); + + return err; + + } + + TRACUCOMP(g_trac_nvdimm,"nvdimmReadAttributes() tgt=0x%X, %d/%d/0x%X " + "wpw=0x%X, dsKb=0x%X, chpCnt=%d, aS=%d (%d), wct=%d", + TARGETING::get_huid(i_target), + o_i2cInfo.port, o_i2cInfo.engine, o_i2cInfo.devAddr, + o_i2cInfo.writePageSize, o_i2cInfo.devSize_KB, + o_i2cInfo.chipCount, o_i2cInfo.addrSize, + nvdimmData.byteAddrOffset, o_i2cInfo.writeCycleTime); + + + TRACDCOMP( g_trac_nvdimm, + EXIT_MRK"nvdimmReadAttributes()" ); + + return err; +} // end nvdimmReadAttributes + + +// ------------------------------------------------------------------ +// nvdimmGetI2CMasterTarget +// ------------------------------------------------------------------ +errlHndl_t nvdimmGetI2CMasterTarget ( TARGETING::Target * i_target, + nvdimm_addr_t i_i2cInfo, + TARGETING::Target * &o_target ) +{ + errlHndl_t err = nullptr; + o_target = nullptr; + + TRACDCOMP( g_trac_nvdimm, + ENTER_MRK"nvdimmGetI2CMasterTarget()" ); + + do + { + TARGETING::TargetService& tS = TARGETING::targetService(); + + // The path from i_target to its I2C Master was read from the + // attribute via nvdimmReadAttributes() and passed to this function + // in i_i2cInfo.i2cMasterPath + + // check that the path exists + bool exists = false; + tS.exists( i_i2cInfo.i2cMasterPath, + exists ); + + if( !exists ) + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmGetI2CMasterTarget() - " + "i2cMasterPath attribute path doesn't exist!" ); + + // Compress the entity path + uint64_t l_epCompressed = 0; + for( uint32_t i = 0; i < i_i2cInfo.i2cMasterPath.size(); i++ ) + { + // Path element: type:8 instance:8 + l_epCompressed |= + i_i2cInfo.i2cMasterPath[i].type << (16*(3-i)); + l_epCompressed |= + i_i2cInfo.i2cMasterPath[i].instance << ((16*(3-i))-8); + + // Can only fit 4 path elements into 64 bits + if ( i == 3 ) + { + break; + } + } + + /*@ + * @errortype + * @reasoncode NVDIMM_I2C_MASTER_PATH_ERROR + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_GETI2CMASTERTARGET + * @userdata1 HUID of target + * @userdata2 Compressed Entity Path + * @devdesc I2C master entity path doesn't exist. + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_GETI2CMASTERTARGET, + NVDIMM_I2C_MASTER_PATH_ERROR, + TARGETING::get_huid(i_target), + l_epCompressed, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + err->collectTrace( NVDIMM_COMP_NAME ); + + ERRORLOG::ErrlUserDetailsString( + i_i2cInfo.i2cMasterPath.toString()).addToLog(err); + + break; + } + + // Since it exists, convert to a target + o_target = tS.toTarget( i_i2cInfo.i2cMasterPath ); + + if( nullptr == o_target ) + { + TRACFCOMP( g_trac_nvdimm, + ERR_MRK"nvdimmGetI2CMasterTarget() - I2C Master " + "Path target was nullptr!" ); + + // Compress the entity path + uint64_t l_epCompressed = 0; + for( uint32_t i = 0; i < i_i2cInfo.i2cMasterPath.size(); i++ ) + { + // Path element: type:8 instance:8 + l_epCompressed |= + i_i2cInfo.i2cMasterPath[i].type << (16*(3-i)); + l_epCompressed |= + i_i2cInfo.i2cMasterPath[i].instance << ((16*(3-i))-8); + + // Can only fit 4 path elements into 64 bits + if ( i == 3 ) + { + break; + } + } + + /*@ + * @errortype + * @reasoncode NVDIMM_TARGET_NULL + * @severity ERRORLOG::ERRL_SEV_UNRECOVERABLE + * @moduleid NVDIMM_GETI2CMASTERTARGET + * @userdata1 HUID of target + * @userdata2 Compressed Entity Path + * @devdesc I2C master path target is null. + */ + err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_GETI2CMASTERTARGET, + NVDIMM_TARGET_NULL, + TARGETING::get_huid(i_target), + l_epCompressed, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT ); + + err->collectTrace( NVDIMM_COMP_NAME ); + + ERRORLOG::ErrlUserDetailsString( + i_i2cInfo.i2cMasterPath.toString()).addToLog(err); + + break; + } + + } while( 0 ); + + TRACDCOMP( g_trac_nvdimm, + EXIT_MRK"nvdimmGetI2CMasterTarget()" ); + + return err; +} // end nvdimmGetI2CMasterTarget + +/** + * @brief Add any new NVDIMMs associated with this target + * to the list + * @param[in] i_list : list of previously discovered NVDIMMs + * @param[out] i_targ : owner of NVDIMMs to add + */ +void add_to_list( std::list<EEPROM::EepromInfo_t>& i_list, + TARGETING::Target* i_targ ) +{ + TRACFCOMP(g_trac_nvdimm,"Targ %.8X",TARGETING::get_huid(i_targ)); + + TARGETING::EepromNvInfo nvdimmData; + + if( i_targ->tryGetAttr<TARGETING::ATTR_EEPROM_NV_INFO> + ( reinterpret_cast<TARGETING::ATTR_EEPROM_NV_INFO_type&>( nvdimmData) ) ) + { + // check that the path exists + bool exists = false; + TARGETING::targetService().exists( nvdimmData.i2cMasterPath, + exists ); + if( !exists ) + { + TRACDCOMP(g_trac_nvdimm,"no master path"); + return; + } + + // Since it exists, convert to a target + TARGETING::Target* i2cm = TARGETING::targetService() + .toTarget( nvdimmData.i2cMasterPath ); + if( nullptr == i2cm ) + { + //not sure how this could happen, but just skip it + TRACDCOMP(g_trac_nvdimm,"no target"); + return; + } + + // ignore anything with junk data + TARGETING::Target * sys = nullptr; + TARGETING::targetService().getTopLevelTarget( sys ); + if( i2cm == sys ) + { + TRACDCOMP(g_trac_nvdimm,"sys target"); + return; + } + + // copy all the data out + EEPROM::EepromInfo_t nv_info; + nv_info.i2cMaster = i2cm; + nv_info.engine = nvdimmData.engine; + nv_info.port = nvdimmData.port; + nv_info.devAddr = nvdimmData.devAddr; + nv_info.assocTarg = i_targ; + nv_info.chipCount = nvdimmData.chipCount; + nv_info.sizeKB = nvdimmData.maxMemorySizeKB; + nv_info.addrBytes = nvdimmData.byteAddrOffset; + //one more lookup for the speed + TARGETING::ATTR_I2C_BUS_SPEED_ARRAY_type speeds; + if( i2cm->tryGetAttr<TARGETING::ATTR_I2C_BUS_SPEED_ARRAY> + (speeds) ) + { + if( (nv_info.engine > I2C_BUS_MAX_ENGINE(speeds)) + || (nv_info.port > I2C_BUS_MAX_PORT(speeds)) ) + { + TRACDCOMP(g_trac_nvdimm,"bad engine/port"); + return; + } + nv_info.busFreq = speeds[nv_info.engine][nv_info.port]; + nv_info.busFreq *= 1000; //convert KHz->Hz + } + else + { + TRACDCOMP(g_trac_nvdimm,"bus speed not found"); + return; + } + + i_list.push_back(nv_info); + TRACFCOMP(g_trac_nvdimm,"--Adding i2cm=%.8X, eng=%d, port=%d, addr=%.2X for %.8X", TARGETING::get_huid(i2cm),nvdimmData.engine,nvdimmData.port, nv_info.devAddr, TARGETING::get_huid(nv_info.assocTarg)); + } +} + +/** + * @brief Determine if the given dimm target is an NVDIMM + * + * @param[in] i_target : dimm target to check + * + * @return bool - True if the given target is an NVDIMM + */ +bool isNVDIMM( TARGETING::Target * i_target ) +{ + // Not the most elegant way of doing it but the hybrid attributes + // are at the MCS level. Need to find my way up to MCS and check + // if the dimm is hybrid + + TARGETING::TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, i_target, TARGETING::CLASS_UNIT, TARGETING::TYPE_MCA); + + if (l_mcaList.size()) + { + TARGETING::TargetHandleList l_mcsList; + getParentAffinityTargets(l_mcsList, l_mcaList[0], TARGETING::CLASS_UNIT, TARGETING::TYPE_MCS); + + if(l_mcsList.size()) + { + // 2-D array. [MCA][DIMM] + TARGETING::ATTR_EFF_HYBRID_type l_hybrid; + TARGETING::ATTR_EFF_HYBRID_MEMORY_TYPE_type l_hybrid_type; + + if( l_mcsList[0]->tryGetAttr<TARGETING::ATTR_EFF_HYBRID>(l_hybrid) && + l_mcsList[0]->tryGetAttr<TARGETING::ATTR_EFF_HYBRID_MEMORY_TYPE>(l_hybrid_type) ) + { + //Using huid to lookup the hybrid attribute for the current dimm + const auto l_dimm_huid = TARGETING::get_huid(i_target); + const auto l_mca_huid = TARGETING::get_huid(l_mcaList[0]); + + return (l_hybrid[l_mca_huid%MCA_PER_MCS][l_dimm_huid%DIMM_PER_MCA] == TARGETING::EFF_HYBRID_IS_HYBRID && + l_hybrid_type[l_mca_huid%MCA_PER_MCS][l_dimm_huid%DIMM_PER_MCA] == TARGETING::EFF_HYBRID_MEMORY_TYPE_NVDIMM); + } + } + } + + return false; +} + +/** + * @brief Return a set of information related to every unique + * NVDIMM in the system + */ +void getNVDIMMs( std::list<EEPROM::EepromInfo_t>& o_info ) +{ + TRACUCOMP(g_trac_nvdimm,ENTER_MRK "getNVDIMMs()"); + + TARGETING::TargetHandleList l_dimmList; + + TARGETING::getAllLogicalCards( + l_dimmList, + TARGETING::TYPE_DIMM); + + for (auto const l_dimm : l_dimmList) + { + if (isNVDIMM(l_dimm)) + add_to_list( o_info, l_dimm ); + } + + TRACUCOMP(g_trac_nvdimm,EXIT_MRK "getNVDIMMs(): Found %d NVDIMMs", + o_info.size()); +} + +} // end namespace NVDIMM diff --git a/src/usr/i2c/nvdimmdd.H b/src/usr/i2c/nvdimmdd.H new file mode 100755 index 000000000..e7885d3d4 --- /dev/null +++ b/src/usr/i2c/nvdimmdd.H @@ -0,0 +1,302 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/i2c/nvdimmdd.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2018 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __NVDIMM_H +#define __NVDIMM_H + +/** + * @file nvdimmdd.H + * + * @brief Provides the interfaces for accessing NVDIMMs within + * the system via the I2C device driver. + */ + +// ---------------------------------------------- +// Includes +// ---------------------------------------------- +#include <errl/errlentry.H> + +namespace NVDIMM +{ + +/** +* @brief Enumerations to describe the type of devices to be accessed. +*/ +typedef enum +{ + ZERO_BYTE_ADDR = 0, + ONE_BYTE_ADDR_PAGESELECT = 1, // page select + TWO_BYTE_ADDR = 2, + ONE_BYTE_ADDR = 3, + LAST_DEVICE_TYPE +} nvdimm_addr_size_t; + +/** + * @brief Structure of common parameters needed by different parts of + * the code. + */ +typedef struct +{ + uint64_t port; + uint64_t engine; + uint64_t devAddr; + uint64_t offset; + nvdimm_addr_size_t addrSize; + TARGETING::EntityPath i2cMasterPath; + uint64_t writePageSize; // in bytes + uint64_t devSize_KB; // in kilobytes + uint64_t chipCount; // number of chips making up nvdimm device + uint64_t writeCycleTime; // in milliseconds +} nvdimm_addr_t; + +/* + * @brief Miscellaneous enums for NVDIMM + */ +enum +{ + NVDIMM_PAGE_SIZE = 0x100, + NVDIMM_DEVADDR_INC = 2 +}; + +/** +* +* @brief Perform an NVDIMM access operation. +* +* @param[in] i_opType - Operation Type - See DeviceFW::OperationType in +* driververif.H +* +* @param[in] i_target - Target device. +* +* @param[in/out] io_buffer +* INPUT: Pointer to the data that will be written to the target +* device. +* OUTPUT: Pointer to the data that was read from the target device. +* +* @param[in/out] io_buflen +* INPUT: Length of the buffer to be written to target device. +* OUTPUT: Length of buffer that was written, or length of buffer +* to be read from target device. +* +* @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. This argument list consists of the internal offset +* to use on the slave I2C device. +* +* @return errlHndl_t - NULL if successful, otherwise a pointer to the +* error log. +* +*/ +errlHndl_t nvdimmPerformOp( DeviceFW::OperationType i_opType, + TARGETING::Target * i_target, + void * io_buffer, + size_t & io_buflen, + int64_t i_accessType, + va_list i_args ); + +/* + * @brief On the NV Controller, the page is selected by writing to offset + * 0x00 with the page you would like to switch too. e.g. to activate + * page 1, write 0x01 to offset 0x00. The user will need to select + * the page as appropriate. + * Each page contains 256 offsets (0xFF). This function checks if + * the offset is outside of 0xFF boundary. + * + * @param[in] i_offset - The requested read/write offset to the NVDIMM. + * crossed over into the second NVDIMM page. + * @param[in] i_buflen - The requested length of the data. + * + * @return bool - True if the requested data straddles the NVDIMM page + * boundary, False otherwise. If False, io_newLen == i_originalLen. + */ +errlHndl_t crossesNvdimmPageBoundary( uint64_t i_offset, + size_t i_buflen ); + + +/** + * @brief This function peforms the sequencing to do a read of the + * NVDIMM that is identified. + * + * @param[in] i_target - Target device. + * + * @param[out] o_buffer - The buffer that will return the data read + * from the NVDIMM device. + * + * @param[in] i_buflen - Number of bytes to read from the NVDIMM device. + * + * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute + * the command to the I2C device driver. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t nvdimmRead ( TARGETING::Target * i_target, + void * o_buffer, + size_t i_buflen, + nvdimm_addr_t i_i2cInfo ); + + +/** + * @brief This function actually performs the i2c operations to read data from + * the NVDIMM + * + * @param[in] i_target - Target device + * + * @param[out] o_buffer - the buffer that will return the data read from + * the nvdimm device + * + * @param[in] i_buflen - Number of bytes read from the NVDIMM device + * + * @param[in] i_byteAddress - the offset into the NVDIMM device + * + * @param[in] i_byteAddressSize - the size of the byte address (1 or 2 bytes) + * + * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute the + * command to the I2C device driver + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t nvdimmReadData( TARGETING::Target * i_target, + void * o_buffer, + size_t i_buflen, + void * i_byteAddress, + size_t i_byteAddressSize, + nvdimm_addr_t i_i2cInfo ); + + + +/** + * @brief This function peforms the sequencing to do a write of the + * NVDIMM that is identified. + * + * @param[in] i_target - Target device. + * + * @param[in] io_buffer - The buffer that contains the data to be written + * to the NVDIMM device. + * + * @param[in/out] io_buflen +* INPUT: Length of the buffer to be written to target device. +* OUTPUT: Length of buffer that was written, or length of buffer +* to be read from target device. + * + * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute + * the command to the I2C device driver. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t nvdimmWrite ( TARGETING::Target * i_target, + void * io_buffer, + size_t & io_buflen, + nvdimm_addr_t i_i2cInfo ); + + + +/** + * @brief This function actually writes data into the devices NVDIMM + * + * @param[in] i_target - Target device. + * @param[in] i_dataToWrite - The data to be written into the device. + * @param[in] i_dataLen - The length of the data to be written. + * @param[in] i_byteAddress - the offset into the devices NVDIMM. + * @param[in] i_byteAddressSize - the size of byte address varable. + * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute + * the command to the I2C device driver. + */ +errlHndl_t nvdimmWriteData( TARGETING::Target * i_target, + void * i_dataToWrite, + size_t i_dataLen, + void * i_byteAddress, + size_t i_byteAddressSize, + nvdimm_addr_t i_i2cInfo ); + + +/** + * @brief This function prepares the I2C byte address for adding to the + * existing buffer (for Writes), or as a separate write operation + * (for Reads). + * + * @param[in] i_target - the target to prepare the addressing for. + * + * @param[in/out] io_buffer - The buffer to be written as a byte address to + * the NVDIMM device. Must be pre-allocated to MAX_BYTE_ADDR size. + * + * @param[out] o_bufSize - The size of the buffer to be written. + * + * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute + * the command to the I2C device driver. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t nvdimmPrepareAddress ( TARGETING::Target * i_target, + void * io_buffer, + size_t & o_bufSize, + nvdimm_addr_t i_i2cInfo); + +/** + * @brief this function will read all of the associated attributes needed + * to access the intended NVDIMM. These attributes will be used to + * determine the type of I2C device as well as how to address it via + * the I2C device driver. + * + * @param[in] i_target - Target device. + * + * @param[out] o_i2cInfo - The structure that will contain the attribute data + * read from the target device. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t nvdimmReadAttributes ( TARGETING::Target * i_target, + nvdimm_addr_t & o_i2cInfo ); + +/** + * @brief This function decides whether or not the target passed into the + * NVDIMM device driver actually contains the I2C Master engines. If + * not, it will then read the attribute of the target to get the path + * of the target which does contain the I2C Master engine. + * + * @param[in] i_target - The current Target. + * + * @param[in] i_i2cInfo - Structure of I2C parameters needed to execute + * the command to the I2C device driver. + * + * @param[out] o_target - The "new" target that will be used for all operations + * from this point on. It may be == to i_target, or a completely different + * target. BUT, this target will contain the I2C Master engine that will + * allow operations to the target NVDIMM. + * + * @return errlHndl_t - NULL if successful, otherwise a pointer to the + * error log. + */ +errlHndl_t nvdimmGetI2CMasterTarget ( TARGETING::Target * i_target, + nvdimm_addr_t i_i2cInfo, + TARGETING::Target * &o_target ); + +}; // end NVDIMM namespace + +#endif // __NVDIMM_H diff --git a/src/usr/targeting/common/genHwsvMrwXml.pl b/src/usr/targeting/common/genHwsvMrwXml.pl index e0cf8f6f5..843c23d2b 100755 --- a/src/usr/targeting/common/genHwsvMrwXml.pl +++ b/src/usr/targeting/common/genHwsvMrwXml.pl @@ -6167,6 +6167,7 @@ sub generate_is_dimm # Add TEMP_SENSOR_I2C_CONFIG if (exists $dimmI2C[$node][$pos]) { + print " <attribute> <id>TEMP_SENSOR_I2C_CONFIG</id> @@ -6178,6 +6179,26 @@ sub generate_is_dimm </default> </attribute> "; + + # Add EEPROM_NV_INFO i2c config + # Base address for the NV controller is 0x80 + # piggybacking the last nibble of devAddr from $dimmI2C to build the device address + print " + <attribute> + <id>EEPROM_NV_INFO</id> + <default> + <field><id>i2cMasterPath</id><value>physical:sys-$sys/node-$node/proc-$proc</value></field> + <field><id>port</id><value>". $dimmI2C[$node][$pos]->{port} ."</value></field> + <field><id>devAddr</id><value>0x8". substr($dimmI2C[$node][$pos]->{devAddr},-1) ."</value></field> + <field><id>engine</id><value>". $dimmI2C[$node][$pos]->{engine} ."</value></field> + <field><id>byteAddrOffset</id><value>0x03</value></field> + <field><id>maxMemorySizeKB</id><value>0x01</value></field> + <field><id>chipCount</id><value>0x01</value></field> + <field><id>writePageSize</id><value>0x01</value></field> + <field><id>writeCycleTime</id><value>0x05</value></field> + </default> + </attribute> + "; } #RID number hack, get it from location code @@ -7050,10 +7071,12 @@ sub addI2cBusSpeedArray my $tmp_ct eq ""; # bus_speed_array[engine][port] is 4x13 array + # Hardcoding speed_array[3][1] for nvdimm as NVDIMM is current + # not in mrw for ZZ (last gen of serverwiz1) my @speed_array = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); # Loop through all i2c devices for my $i ( 0 .. $#I2Cdevices ) { diff --git a/src/usr/targeting/common/xmltohb/attribute_types.xml b/src/usr/targeting/common/xmltohb/attribute_types.xml index 38c3a5b09..4995ee0e7 100755 --- a/src/usr/targeting/common/xmltohb/attribute_types.xml +++ b/src/usr/targeting/common/xmltohb/attribute_types.xml @@ -1187,6 +1187,144 @@ </attribute> <attribute> + <id>EEPROM_NV_INFO</id> + <description>Information needed to address the NV controller on the NVDIMM</description> + <complexType> + <description>Structure to define the addressing for NV controller.</description> + <field> + <name>i2cMasterPath</name> + <description>Entity path to the chip that contains the I2C + master. + </description> + <type>EntityPath</type> + <default>physical:sys-0</default> + </field> + <field> + <name>port</name> + <description>Port from the I2C Master device. This is a 6-bit + value, but then shifted 2 bits left. + </description> + <type>uint8_t</type> + <default>0x80</default> + </field> + <field> + <name>devAddr</name> + <description>Device address on the I2C bus. This is a 7-bit value, + but then shifted 1 bit left. + </description> + <type>uint8_t</type> + <default>0x80</default> + </field> + <field> + <name>engine</name> + <description>I2C master engine. This is a 2-bit value, + but then shifted 6 bits left. + </description> + <type>uint8_t</type> + <default>0x80</default> + </field> + <field> + <name>byteAddrOffset</name> + <description> + The number of bytes a device requires to set its + internal address/offset. For NV controller it's only + one byte addressing with no page select (3) + 0 = Zero Byte Addressing + 1 = One Byte Addressing with page select + 2 = Two Byte Addressing + 3 = OneByte Addressing with no page select + </description> + <type>uint8_t</type> + <default>0x03</default> + </field> + <field> + <name>maxMemorySizeKB</name> + <description>The number of kilobytes a device can hold. 'Zero' + value possible for some devices. + </description> + <type>uint64_t</type> + <default>0x01</default> + </field> + <field> + <name>chipCount</name> + <description>The number of chips making up an eeprom device. + </description> + <type>uint8_t</type> + <default>0x01</default> + </field> + <field> + <name>writePageSize</name> + <description>The maximum number of bytes that can be written to + a device at one time. 'Zero' value means no maximum value is + expected or checked. + </description> + <type>uint64_t</type> + <default>0x01</default> + </field> + <field> + <name>writeCycleTime</name> + <description>The amount of time in milliseconds a device requires + on the completion of a write command to update its internal memory. + </description> + <type>uint64_t</type> + <default>0x05</default> + </field> + </complexType> + <persistency>non-volatile</persistency> + <readable></readable> + </attribute> + + <attribute> + <id>NV_OPS_TIMEOUT_MSEC</id> + <description> + NVDIMM timeout value for 6 main operations + 0 - CSAVE + 1 - Page Switch + 2 - Restore + 3 - ERASE + 4 - ARM + 5 - CHARGE + + This attribute is set to volatile because the timeout values vary + depending on the vendor and capacity. The timeout values will be + determined during init by reading the i2c registers on the NV + controller. + The indices are defined in src/usr/isteps/nvdimm.H and the attribute + is consumed in nvdimm.C + </description> + <simpleType> + <array>6</array> + <uint32_t> + <default>0,0,0,0,0,0</default> + </uint32_t> + </simpleType> + <persistency>volatile-zeroed</persistency> + <readable/> + <writeable/> + </attribute> + + <attribute> + <id>NV_STATUS_FLAG</id> + <description> + NVDIMM status flag. This is used to record the status and + later report to OPAL. Possible values: + + 0x0080 - memory valid, contents not preserved (genesis) + 0x0040 - memory contents preserved + 0x0020 - memory failed to preserve contents + 0x0010 - memory unable to preserve future contents + </description> + <simpleType> + <uint16_t> + <default>0x0000</default> + </uint16_t> + </simpleType> + <persistency>volatile-zeroed</persistency> + <readable/> + <writeable/> + </attribute> + + <attribute> <description> Holds the effective EC of the system. Effective EC is the lowest EC among all the functional procs in the system. Some cards may "downbin" diff --git a/src/usr/targeting/common/xmltohb/target_types.xml b/src/usr/targeting/common/xmltohb/target_types.xml index c5245785d..7a2f9b6d9 100644 --- a/src/usr/targeting/common/xmltohb/target_types.xml +++ b/src/usr/targeting/common/xmltohb/target_types.xml @@ -959,6 +959,15 @@ <attribute> <id>VPD_REC_NUM</id> </attribute> + <attribute> + <id>EEPROM_NV_INFO</id> + </attribute> + <attribute> + <id>NV_OPS_TIMEOUT_MSEC</id> + </attribute> + <attribute> + <id>NV_STATUS_FLAG</id> + </attribute> </targetType> <targetType> |

