summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTsung Yeung <tyeung@us.ibm.com>2018-06-04 14:12:42 -0500
committerDaniel M. Crowell <dcrowell@us.ibm.com>2018-09-18 15:48:56 -0500
commit1f6ed77b32b629f5cd316f2f3e1d8a432f037881 (patch)
tree24b56ee17587dc2311cbb63936e405590c62d282 /src
parent2968366be4705757b8766c8c422f608f491f0e0b (diff)
downloadblackbird-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.H8
-rw-r--r--src/include/usr/hbotcompid.H10
-rw-r--r--src/include/usr/i2c/nvdimmddreasoncodes.H89
-rw-r--r--src/include/usr/i2c/nvdimmif.H56
-rw-r--r--src/usr/i2c/HBconfig5
-rwxr-xr-xsrc/usr/i2c/eepromdd.C8
-rw-r--r--src/usr/i2c/errlud_i2c.C119
-rw-r--r--src/usr/i2c/errlud_i2c.H38
-rwxr-xr-xsrc/usr/i2c/i2c.C49
-rw-r--r--src/usr/i2c/i2c.mk1
-rw-r--r--src/usr/i2c/makefile2
-rwxr-xr-xsrc/usr/i2c/nvdimmdd.C1436
-rwxr-xr-xsrc/usr/i2c/nvdimmdd.H302
-rwxr-xr-xsrc/usr/targeting/common/genHwsvMrwXml.pl25
-rwxr-xr-xsrc/usr/targeting/common/xmltohb/attribute_types.xml138
-rw-r--r--src/usr/targeting/common/xmltohb/target_types.xml9
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>
OpenPOWER on IntegriCloud