diff options
author | Tsung Yeung <tyeung@us.ibm.com> | 2018-08-06 08:51:22 -0500 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2018-10-30 16:50:11 -0500 |
commit | 2c1c99f370191e4099870a0699cd6f01510222e7 (patch) | |
tree | 52081d13a60adf0cfeea62189e45d48b9cb9203a /src/usr/isteps | |
parent | d9711869369308c99eea851c45022b66f68b12df (diff) | |
download | talos-hostboot-2c1c99f370191e4099870a0699cd6f01510222e7.tar.gz talos-hostboot-2c1c99f370191e4099870a0699cd6f01510222e7.zip |
Adds NVDIMM IPL Support on ZZ
-New NVDIMM functions to support restore and trigger setup
-Includes NVDIMM step in call_mss_power_cleanup after mss_power_cleanup HWP
-Fixes attribute NV_STATUS_FLAG to match HDAT spec
Change-Id: I2d68123ceb0b8e7a33b54f9acad0968670a67ea9
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/63948
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr/isteps')
-rw-r--r-- | src/usr/isteps/HBconfig | 2 | ||||
-rw-r--r-- | src/usr/isteps/istep14/call_mss_power_cleanup.C | 67 | ||||
-rw-r--r-- | src/usr/isteps/istep14/makefile | 2 | ||||
-rw-r--r-- | src/usr/isteps/makefile | 1 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/makefile | 53 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm.C | 1435 | ||||
-rw-r--r-- | src/usr/isteps/nvdimm/nvdimm.H | 151 |
7 files changed, 1709 insertions, 2 deletions
diff --git a/src/usr/isteps/HBconfig b/src/usr/isteps/HBconfig index ce6573a2e..231464b7e 100644 --- a/src/usr/isteps/HBconfig +++ b/src/usr/isteps/HBconfig @@ -32,6 +32,6 @@ config PCA95X_16BIT Set the PCA95X support to a 16 bit chip. config PRINT_SYSTEM_INFO - defaults n + default n help Enables code that prints out the HWAS information for each target found during discovery diff --git a/src/usr/isteps/istep14/call_mss_power_cleanup.C b/src/usr/isteps/istep14/call_mss_power_cleanup.C index ae6a4f42a..861647e09 100644 --- a/src/usr/isteps/istep14/call_mss_power_cleanup.C +++ b/src/usr/isteps/istep14/call_mss_power_cleanup.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2017 */ +/* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -40,6 +40,11 @@ #include <p9_mss_power_cleanup.H> #include <p9c_mss_power_cleanup.H> +#ifdef CONFIG_NVDIMM +// NVDIMM support +#include <isteps/nvdimm/nvdimm.H> +#endif + using namespace ISTEP; using namespace ISTEP_ERROR; using namespace ERRORLOG; @@ -95,6 +100,66 @@ void* call_mss_power_cleanup (void *io_pArgs) } } +#ifdef CONFIG_NVDIMM + TARGETING::TargetHandleList l_procList; + getAllChips(l_procList, TARGETING::TYPE_PROC, false); + TARGETING::ATTR_MODEL_type l_chipModel = + l_procList[0]->getAttr<TARGETING::ATTR_MODEL>(); + + if(l_chipModel == TARGETING::MODEL_NIMBUS) + { + // Check for any NVDIMMs after the mss_power_cleanup + TARGETING::TargetHandleList l_dimmTargetList; + TARGETING::TargetHandleList l_nvdimmTargetList; + getAllLogicalCards(l_dimmTargetList, TYPE_DIMM); + + // Walk the dimm list and collect all the nvdimm targets + for (auto const l_dimm : l_dimmTargetList) + { + //@TODO replace this with isNVDIMM() + // 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, l_dimm, 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(l_dimm); + const auto l_mca_huid = TARGETING::get_huid(l_mcaList[0]); + const uint8_t MCA_PER_MCS = 2; + const uint8_t DIMM_PER_MCA = 2; + + if (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 ) + { + l_nvdimmTargetList.push_back(l_dimm); + } + } + } + } + } + + // Run the nvdimm management function if the list is not empty + if (!l_nvdimmTargetList.empty()){ + NVDIMM::nvdimm_restore(l_nvdimmTargetList); + } + } +#endif + // -- Cumulus only // Get a list of all present Centaurs TargetHandleList l_presCentaurs; diff --git a/src/usr/isteps/istep14/makefile b/src/usr/isteps/istep14/makefile index 21e1383c0..68156f5b1 100644 --- a/src/usr/isteps/istep14/makefile +++ b/src/usr/isteps/istep14/makefile @@ -46,6 +46,7 @@ EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ EXTRAINCDIR += ${CEN_PROC_PATH}/hwp/memory EXTRAINCDIR += ${CEN_PROC_PATH}/hwp/memory/lib/shared/ EXTRAINCDIR += ${ROOTPATH}/src/import/chips/centaur/common/include/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ddr4/ OBJS += call_mss_memdiag.o OBJS += call_mss_thermal_init.o @@ -99,3 +100,4 @@ VPATH += ${PROCEDURE_PATH}/hwp/memory/lib/utils/ VPATH += ${PROCEDURE_PATH}/hwp/memory/lib/mcbist/ VPATH += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ VPATH += ${CEN_PROC_PATH}/hwp/memory/ +VPATH += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ddr4/ diff --git a/src/usr/isteps/makefile b/src/usr/isteps/makefile index b64adc283..920eae593 100644 --- a/src/usr/isteps/makefile +++ b/src/usr/isteps/makefile @@ -50,6 +50,7 @@ SUBDIRS+=tod.d SUBDIRS+=fab_iovalid.d SUBDIRS+=nest.d SUBDIRS+=io.d +SUBDIRS+=nvdimm.d #TODO: RTC 176018 EXTRAINCDIR += ${ROOTPATH}/src/import/ diff --git a/src/usr/isteps/nvdimm/makefile b/src/usr/isteps/nvdimm/makefile new file mode 100644 index 000000000..a39be6f0e --- /dev/null +++ b/src/usr/isteps/nvdimm/makefile @@ -0,0 +1,53 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/usr/isteps/nvdimm/makefile $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2012,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 +ROOTPATH = ../../../.. + +PROCEDURE_PATH = ${ROOTPATH}/src/import/chips/p9/procedures + +#Add all the extra include paths + +EXTRAINCDIR += ${ROOTPATH}/obj/genfiles/ +EXTRAINCDIR += ${ROOTPATH}/src/import/hwpf/fapi2/include +EXTRAINCDIR += ${ROOTPATH}/src/include/usr/fapi2 +EXTRAINCDIR += ${ROOTPATH}/src/import/chips/common/utils/imageProcs/ +EXTRAINCDIR += ${ROOTPATH}/src/import/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory +EXTRAINCDIR += ${ROOTPATH}/src/import/chips/p9/common/include/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/lib/eff_config/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/lib/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/lib/mcbist/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ddr4/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/memory/ +EXTRAINCDIR += ${PROCEDURE_PATH}/hwp/ffdc/ + +MODULE = nvdimm + +OBJS += nvdimm.o + +include ${ROOTPATH}/config.mk + +VPATH += ${PROCEDURE_PATH}/hwp/memory/ +VPATH += ${PROCEDURE_PATH}/hwp/memory/lib/dimm/ddr4/ diff --git a/src/usr/isteps/nvdimm/nvdimm.C b/src/usr/isteps/nvdimm/nvdimm.C new file mode 100644 index 000000000..d98397edb --- /dev/null +++ b/src/usr/isteps/nvdimm/nvdimm.C @@ -0,0 +1,1435 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/isteps/nvdimm/nvdimm.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014,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 */ + +#include "nvdimm.H" +#include <trace/interface.H> +#include <errl/errlentry.H> +#include <errl/errlmanager.H> +#include <errl/errludtarget.H> +#include <i2c/nvdimmddreasoncodes.H> +#include <targeting/common/commontargeting.H> +#include <targeting/common/util.H> +#include <targeting/common/utilFilter.H> +#include <kernel/timemgr.H> +#include <sys/time.h> +#include <usr/devicefw/userif.H> +#include <fapi2.H> +#include <fapi2/plat_hwp_invoker.H> +#include <lib/dimm/ddr4/nvdimm_utils.H> +#include <isteps/nvdimm/nvdimm.H> + +using namespace TARGETING; +using namespace DeviceFW; +using namespace EEPROM; + +trace_desc_t* g_trac_nvdimm = NULL; +TRAC_INIT(&g_trac_nvdimm, "NVDIMM", 2*KILOBYTE); + +// Easy macro replace for unit testing +// #define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + + +namespace NVDIMM +{ +#define NUM_OFFSET 2 +#define NVDIMM_SET_USER_DATA_1(left_32_ops_id, right_32_huid) \ + TWO_UINT32_TO_UINT64(left_32_ops_id, right_32_huid) + +#define NVDIMM_SET_USER_DATA_2_TIMEOUT(left_32_polled, right_32_timeout) \ + NVDIMM_SET_USER_DATA_1(left_32_polled, right_32_timeout) + +enum +{ + NSTD_VAL_NOPRSV = 0x08, // memory valid, contents not preserved (genesis) + NSTD_VAL_NOPRSV_MASK = 0xF7, + NSTD_VAL_PRSV = 0x04, // memory contents preserved + NSTD_VAL_PRSV_MASK = 0xFB, + NSTD_ERR_NOPRSV = 0x02, // memory failed to preserve contents + NSTD_ERR_NOPRSV_MASK = 0xFD, + NSTD_ERR_NOBKUP = 0x01, // memory unable to preserve future content + NSTD_ERR_NOBKUP_MASK = 0xFE, + NSTD_ERR = 0x03, // NSTD_ERR_NOPRSV+NSTD_ERR_NOBKUP +}; + +typedef struct ops_timeoutInfo{ + const char * desc; + uint8_t page; + uint8_t offset[2]; + uint8_t idx; + uint8_t status_reg_offset; + uint8_t status_progress; +} ops_timeoutInfo_t; + +static bool init = true; + +// Table containing register info on the timeout registers for different ops +constexpr ops_timeoutInfo_t timeoutInfoTable[] = +{ + {"SAVE", 0, {0x19, 0x18}, SAVE , NVDIMM_CMD_STATUS0, SAVE_IN_PROGRESS}, + {"RESTORE", 0, {0x1D, 0x1C}, RESTORE , NVDIMM_CMD_STATUS0, RSTR_IN_PROGRESS}, + {"ERASE", 0, {0x1F, 0x1E}, ERASE , NVDIMM_CMD_STATUS0, ERASE_IN_PROGRESS}, + {"ARM", 0, {0x21, 0x20}, ARM , NVDIMM_CMD_STATUS0, ARM_IN_PROGRESS}, + {"PAGE_SWITCH", 0, {0x1B, 0x1A}, PAGE_SWITCH, 0xff, 0xff}, + {"CHARGE", 1, {0x11, 0x10}, CHARGE , MODULE_HEALTH_STATUS1, CHARGE_IN_PROGRESS}, +}; + +/** + * @brief Wrapper to call deviceOp to read the NV controller via I2C + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[in] i_addr - address/offset for the register to be read + * + * @param[out] o_data - returned data from read + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmReadReg(Target* i_nvdimm, + uint8_t i_addr, + uint8_t & o_data ) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"NVDIMM Read HUID %X, addr 0x%X", + TARGETING::get_huid(i_nvdimm), i_addr); + + errlHndl_t l_err = nullptr; + size_t l_numBytes = 1; + + l_err = DeviceFW::deviceOp( DeviceFW::READ, + i_nvdimm, + &o_data, + l_numBytes, + DEVICE_NVDIMM_ADDRESS(i_addr)); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NVDIMM Read HUID %X, addr 0x%X = %X", + TARGETING::get_huid(i_nvdimm), i_addr, o_data); + + return l_err; +} + +/** + * @brief Wrapper to call deviceOp to write the NV controller via I2C + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[in] i_addr - address/offset for the register to be written + * + * @param[in] i_data - data to be written to register @ i_addr + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmWriteReg(Target* i_nvdimm, + uint8_t i_addr, + uint8_t i_data ) +{ + errlHndl_t l_err = nullptr; + size_t l_numBytes = 1; + + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"NVDIMM Write HUID %X, addr 0x%X = %X", + TARGETING::get_huid(i_nvdimm), i_addr, i_data); + + // Need to write directly from target's EEPROM. + l_err = DeviceFW::deviceOp( DeviceFW::WRITE, + i_nvdimm, + &i_data, + l_numBytes, + DEVICE_NVDIMM_ADDRESS(i_addr)); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NVDIMM Write HUID %X, addr 0x%X = %X", + TARGETING::get_huid(i_nvdimm), i_addr, i_data); + + return l_err; +} + +/** + * @brief Set the status flag + * + * @param[in] i_nvdimm - nvdimm target + * + * @param[in] i_status_flag - status flag to set for each nvdimm + * + */ +void nvdimmSetStatusFlag(Target *i_nvdimm, const uint8_t i_status_flag) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmSetStatusFlag() HUID[%X], i_status_flag[%X]" + ,TARGETING::get_huid(i_nvdimm), i_status_flag); + + auto l_statusFlag = i_nvdimm->getAttr<TARGETING::ATTR_NV_STATUS_FLAG>(); + + switch(i_status_flag) + { + // Make sure NSTD_VAL_PRSV (content preserved) is unset before setting NSTD_VAL_NOPRSV + // (data not preserved) or NSTD_ERR_NOPRSV (error preserving data) + case NSTD_ERR: + case NSTD_VAL_NOPRSV: + case NSTD_ERR_NOPRSV: + l_statusFlag &= NSTD_VAL_PRSV_MASK; + l_statusFlag |= i_status_flag; + break; + + // If the content preserved(restore sucessfully), make sure + // NSTD_VAL_NOPRSV (not preserved) and NSTD_ERR_NOPRSV (error preserving) + // are unset before setting this flag. + case NSTD_VAL_PRSV: + l_statusFlag &= (NSTD_VAL_NOPRSV_MASK & NSTD_ERR_NOPRSV_MASK); + l_statusFlag |= i_status_flag; + break; + + case NSTD_ERR_NOBKUP: + l_statusFlag |= i_status_flag; + break; + + default: + assert(0, "nvdimmSetStatusFlag() HUID[%X], i_status_flag[%X] invalid flag!", + TARGETING::get_huid(i_nvdimm), i_status_flag); + break; + } + + i_nvdimm->setAttr<TARGETING::ATTR_NV_STATUS_FLAG>(l_statusFlag); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmSetStatusFlag() HUID[%X], i_status_flag[%X]" + ,TARGETING::get_huid(i_nvdimm), i_status_flag); +} + +/** + * @brief Check nvdimm error state + * + * @param[in] i_nvdimm - nvdimm target + * + * @return bool - true if nvdimm is in any error state, false otherwise + */ +bool nvdimmInErrorState(Target *i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmInErrorState() HUID[%X]",TARGETING::get_huid(i_nvdimm)); + + uint8_t l_statusFlag = i_nvdimm->getAttr<TARGETING::ATTR_NV_STATUS_FLAG>(); + bool l_ret = true; + + if ((l_statusFlag & NSTD_ERR) == 0) + l_ret = false; + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmInErrorState() HUID[%X]",TARGETING::get_huid(i_nvdimm)); + return l_ret; +} + +/** + * @brief Check NV controller ready state + * + * @param[in] i_nvdimm - nvdimm target + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmReady(Target *i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmReady() HUID[%X]",TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + uint8_t l_data = 0x0; + + l_err = nvdimmReadReg(i_nvdimm, NVDIMM_READY, l_data); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmReady() nvdimm[%X] - error getting ready status[%d]", + TARGETING::get_huid(i_nvdimm), l_data); + } + else if (l_data != NV_READY) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmReady() nvdimm[%X] - nvdimm not ready[%d]", + TARGETING::get_huid(i_nvdimm), l_data); + /*@ + *@errortype + *@reasoncode NVDIMM_NOT_READY + *@severity ERRORLOG_SEV_UNRECOVERABLE + *@moduleid NVDIMM_CHECK_READY + *@userdata1[0:31] Ret value from ready register + *@userdata1[32:63] Target Huid + *@userdata2 <UNUSED> + *@devdesc Failed to read ready status or NVDIMM not ready + * for host access. (userdata1 != 0xA5) + *@custdesc NVDIMM not ready + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_POLL_STATUS, + NVDIMM_STATUS_TIMEOUT, + NVDIMM_SET_USER_DATA_1(l_data, TARGETING::get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + //@TODO RTC 199645 - add HW callout on dimm target. + //if nvdimm is not ready for access by now, this is + //a failing indication on the NV controller + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmReady() HUID[%X] ready[%X]", + TARGETING::get_huid(i_nvdimm), l_data); + + return l_err; +} + +/** + * @brief This function polls the status register for the given ops_id + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[in] i_ops_id - id assigned to each operation in nvdimm.H + * + * @param[out] o_poll - total polled time in ms + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmPollStatus ( Target *i_nvdimm, + ops_id i_ops_id, + uint32_t &o_poll) +{ + errlHndl_t l_err = nullptr; + uint8_t l_data = 0x0; + uint32_t l_target_timeout_values[6]; + bool l_done = false; + + // Get the timeout value for ops_id + assert(i_nvdimm->tryGetAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(l_target_timeout_values), + "nvdimmPollStatus() HUID[%X], failed reading ATTR_NV_OPS_TIMEOUT_MSEC!", TARGETING::get_huid(i_nvdimm)); + uint32_t l_timeout = l_target_timeout_values[i_ops_id]; + + do + { + l_err = nvdimmReadReg( i_nvdimm, + timeoutInfoTable[i_ops_id].status_reg_offset, + l_data ); + if(l_err) + { + break; + } + + if((l_data & timeoutInfoTable[i_ops_id].status_progress) != + timeoutInfoTable[i_ops_id].status_progress) // Done + { + l_done = true; + break; + } + + nanosleep( 0, OPS_POLL_TIME_MS*NS_PER_MSEC ); //sleep for POLL ms + o_poll += OPS_POLL_TIME_MS; + + } while (o_poll < l_timeout); + + if (!l_done && !l_err) + { + + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmPollStatus() nvdimm[%X] - Status timed out ops_id[%d]", + TARGETING::get_huid(i_nvdimm), i_ops_id); + /*@ + *@errortype + *@reasoncode NVDIMM_STATUS_TIMEOUT + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_POLL_STATUS + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2[0:31] Polled value + *@userdata2[32:63] Timeout value + *@devdesc Encountered timeout while performing operation on NVDIMM + * Refer to userdata1 for which operation it timed out. + *@custdesc NVDIMM timed out + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_POLL_STATUS, + NVDIMM_STATUS_TIMEOUT, + NVDIMM_SET_USER_DATA_1(i_ops_id, TARGETING::get_huid(i_nvdimm)), + NVDIMM_SET_USER_DATA_2_TIMEOUT(o_poll, l_timeout), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + //@TODO RTC 199645 - add HW callout on dimm target. + //may have to move the error handling to the caller + //as different op could have different error severity + } + + return l_err; +} + +/** + * @brief This function polls the command status register for backup/CSAVE + * completion (does not indicate success or fail) + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_poll - total polled time in ms + * + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmPollBackupDone(Target* i_nvdimm, + uint32_t &o_poll) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollBackupDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmPollStatus ( i_nvdimm, SAVE, o_poll); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollBackupDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief This function polls the command status register for restore + * completion (does not indicate success or fail) + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_poll - total polled time in ms + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmPollRestoreDone(Target* i_nvdimm, + uint32_t &o_poll) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollRestoreDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmPollStatus ( i_nvdimm, RESTORE, o_poll ); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollRestoreDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief This function polls the command status register for erase + * completion (does not indicate success or fail) + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_poll - total polled time in ms + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmPollEraseDone(Target* i_nvdimm, + uint32_t &o_poll) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollEraseDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmPollStatus ( i_nvdimm, ERASE, o_poll); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollEraseDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief This function polls the command status register for arm completion + * (does not indicate success or fail) + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_poll - total polled time in ms + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmPollArmDone(Target* i_nvdimm, + uint32_t &o_poll) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollArmDone() nvdimm[%X]", TARGETING::get_huid(i_nvdimm) ); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmPollStatus ( i_nvdimm, ARM, o_poll); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollArmDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief This function polls the command status register for backup power + * charge completion (does not indicate success or fail) + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_poll - total polled time in ms + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmPollESChargeStatus(Target* i_nvdimm, + uint32_t &o_poll) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmPollESChargeDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmPollStatus ( i_nvdimm, CHARGE, o_poll ); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmPollESChargeDone() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief This function retrieve the restore status + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_rstrValid - returned data from the restore status register + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmGetRestoreValid(Target* i_nvdimm, uint8_t & o_rstrValid) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmGetRestoreValid() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmReadReg(i_nvdimm, RESTORE_STATUS, o_rstrValid); + + if (l_err){ + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X], Error getting restore status!", + TARGETING::get_huid(i_nvdimm)); + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmGetRestoreValid() nvdimm[%X], restore_status[%x],", + TARGETING::get_huid(i_nvdimm), o_rstrValid); + + return l_err; +} + +/** + * @brief This function sets the energy supply policy to device-managed + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmSetESPolicy(Target* i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmSetESPolicy() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + uint8_t l_data; + + do + { + + l_err = nvdimmWriteReg(i_nvdimm, SET_ES_POLICY_CMD, ES_DEV_MANAGE); + + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_NOBKUP); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmSetESPolicy() nvdimm[%X]" + "failed to write ES register!",TARGETING::get_huid(i_nvdimm)); + break; + } + + // Give it a bit of time (100ms) for the status reg to reflect the change + nanosleep( 0, 100*NS_PER_MSEC ); + + // Make sure the set was a success + l_err = nvdimmReadReg(i_nvdimm, SET_ES_POLICY_STATUS, l_data); + + if (l_err) + { + nvdimmSetStatusFlag(i_nvdimm, NSTD_ERR_NOBKUP); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmSetESPolicy() nvdimm[%X]" + "failed to read ES register!",TARGETING::get_huid(i_nvdimm)); + break; + } + + if ((l_data & ES_SUCCESS) != ES_SUCCESS) + { + TRACFCOMP(g_trac_nvdimm, EXIT_MRK"NDVIMM HUID[%X], nvdimmSetESPolicy() " + "failed!",TARGETING::get_huid(i_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_SET_ES_ERROR + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_SET_ES + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2 <UNUSED> + *@devdesc Encountered error setting the energy source policy + * Make sure the connection between energy source and + * NVDIMM is intact + *@custdesc NVDIMM encountered error setting the energy source policy + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SET_ES, + NVDIMM_SET_ES_ERROR, + NVDIMM_SET_USER_DATA_1(CHARGE, TARGETING::get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + //@TODO RTC 199645 - add procedure callout on backup power source. + //Failure setting the energy source policy could mean error on the + //battery or even the cabling + } + }while(0); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"NDVIMM HUID[%X], nvdimmSetESPolicy()," + ,TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + + +/** + * @brief This function checks the arm status register to make sure + * the trigger has been armed to ddr_reset_n + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmCheckArmSuccess(Target *i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmCheckArmSuccess() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + uint8_t l_data = 0; + + l_err = nvdimmReadReg(i_nvdimm, ARM_STATUS, l_data); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckArmSuccess() nvdimm[%X]" + "failed to read arm status reg!",TARGETING::get_huid(i_nvdimm)); + } + else if ((l_data & ARM_SUCCESS) != ARM_SUCCESS) + { + + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckArmSuccess() nvdimm[%X]" + "failed to arm!",TARGETING::get_huid(i_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_ARM_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_SET_ARM + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2 <UNUSED> + *@devdesc Encountered error arming the catastrophic save + * trigger on NVDIMM. Make sure an energy source + * is connected to the NVDIMM and the ES policy + * is set properly + *@custdesc NVDIMM encountered error arming save trigger + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_SET_ARM, + NVDIMM_ARM_FAILED, + NVDIMM_SET_USER_DATA_1(ARM, TARGETING::get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + //@TODO RTC 199645 - add HW callout on dimm target + //failure to arm could mean internal NV controller error or + //even error on the battery pack. NVDIMM will lose persistency + //if failed to arm trigger + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmCheckArmSuccess() nvdimm[%X] ret[%X]", + TARGETING::get_huid(i_nvdimm), l_data); + + return l_err; +} + +/** + * @brief This function arms the trigger to enable backup in the event + * of power loss (DDR Reset_n goes low) + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmArmResetN(Target *i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmArmResetN() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + + l_err = nvdimmWriteReg(i_nvdimm, ARM_CMD, ARM_DIMM); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmArmResetN() nvdimm[%X] error arming nvdimm!!", + TARGETING::get_huid(i_nvdimm)); + } + else + { + // Arm happens one module at a time. No need to set any offset on the counter + uint32_t l_poll = 0; + l_err = nvdimmPollArmDone(i_nvdimm, l_poll); + if (!l_err) + { + l_err = nvdimmCheckArmSuccess(i_nvdimm); + } + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmArmResetN() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + return l_err; +} + +/** + * @brief This function checks for valid image on the given target + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[out] o_imgValid - return true if the target has a valid image + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmValidImage(Target *i_nvdimm, bool &o_imgValid) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmValidImage(): nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + uint8_t l_data = 0x0; + o_imgValid = false; + + l_err = nvdimmReadReg(i_nvdimm, CSAVE_INFO, l_data); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmValidImage() nvdimm[%X]" + "failed to for image!",TARGETING::get_huid(i_nvdimm) ); + } + else if(l_data & VALID_IMAGE) + { + o_imgValid = true; + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmValidImage(): nvdimm[%X] ret[%X]", + TARGETING::get_huid(i_nvdimm), l_data); + + return l_err; +} + +/** + * @brief This function handles all the restore related operations. + * SRE -> restore -> SRX/RCD/MRS + * + * @param[in] i_nvdimmList - list of nvdimms + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmRestore(TargetHandleList i_nvdimmList) +{ + errlHndl_t l_err = nullptr; + bool l_imgValid; + uint8_t l_rstrValid; + uint32_t l_poll = 0; + + do + { + // Put NVDIMM into self-refresh + for (TargetHandleList::iterator it = i_nvdimmList.begin(); + it != i_nvdimmList.end();) + { + l_err = nvdimmValidImage(*it, l_imgValid); + + // No reason to run if we can't figure out + // if there is an image or not + if (l_err) + { + nvdimmSetStatusFlag(*it, NSTD_ERR_NOPRSV); + break; + } + + if (!l_imgValid) + { + nvdimmSetStatusFlag(*it, NSTD_VAL_NOPRSV); + i_nvdimmList.erase(it); + continue; + } + + TARGETING::TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, *it, TARGETING::CLASS_UNIT, TARGETING::TYPE_MCA); + assert(l_mcaList.size(), "nvdimmRestore() failed to find parent MCA."); + + fapi2::Target<fapi2::TARGET_TYPE_MCA> l_fapi_mca(l_mcaList[0]); + + // Self-refresh is done at the port level + FAPI_INVOKE_HWP(l_err, mss::nvdimm::self_refresh_entry, l_fapi_mca); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore() HUID[%X] self_refresh_entry failed!", + TARGETING::get_huid(*it)); + + nvdimmSetStatusFlag(*it, NSTD_ERR_NOPRSV); + //@TODO RTC 199645 - add HW callout on dimm target + //Without SRE the data could be not reliably restored + break; + } + it++; + } + + if (l_err) + { + break; + } + + // Nothing to do. Move on. + if (i_nvdimmList.empty()) + { + break; + } + + // Kick off the restore on each nvdimm in the nvdimm list + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimmWriteReg(l_nvdimm, NVDIMM_FUNC_CMD, RESTORE_IMAGE); + if (l_err) + { + //@TODO RTC 199645 - add HW callout on dimm target + //failing here is likely an NV controller/i2c problem + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X], error initiating restore!!", + TARGETING::get_huid(l_nvdimm)); + break; + } + } + + if (l_err) + { + break; + } + + // Make sure the restore completed + for (const auto & l_nvdimm : i_nvdimmList) + { + // Since we kicked off the restore on all the modules at once, the restore + // should complete on all of the modules in one restore window. Use the + // polled time from the previous nvdimm as the offset for the next one. + l_err = nvdimmPollRestoreDone(l_nvdimm, l_poll); + if (l_err) + { + //@TODO RTC 199645 - add HW callout on dimm target + //Restore is taking longer than the allotted time here. + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); + errlCommit(l_err, NVDIMM_COMP_ID); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X], error restoring!", + TARGETING::get_huid(l_nvdimm)); + break; + } + } + + if (l_err) + { + break; + } + + // Make sure the restore is valid + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimmGetRestoreValid(l_nvdimm, l_rstrValid); + if (l_err) + { + //@TODO RTC 199645 - add HW callout on dimm target + //failing here is likely an NV controller/i2c problem + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore Target[%X] error validating restore status!", + TARGETING::get_huid(l_nvdimm)); + break; + } + + if ((l_rstrValid & RSTR_SUCCESS) != RSTR_SUCCESS){ + + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X] restoreValid[%d], restore failed!", + TARGETING::get_huid(l_nvdimm), l_rstrValid); + /*@ + *@errortype + *@reasoncode NVDIMM_RESTORE_FAILED + *@severity ERRORLOG_SEV_UNRECOVERABLE + *@moduleid NVDIMM_RESTORE + *@userdata1 Target Huid + *@userdata2 <UNUSED> + *@devdesc NVDIMM failed to restore data. This is likely + * due to failure entering self-refresh and/or + * restore timeout (Controller error) + *@custdesc NVDIMM failed to restore data + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, + NVDIMM_RESTORE, + NVDIMM_RESTORE_FAILED, + TARGETING::get_huid(l_nvdimm), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); + //@TODO RTC 199645 - add HW callout on dimm target + //Invalid restore could be due to dram not in self-refresh + //or controller issue. Data should not be trusted at this point + break; + } + } + + if (l_err) + { + break; + } + + // Exit self-refresh + for (const auto & l_nvdimm : i_nvdimmList) + { + + TARGETING::TargetHandleList l_mcaList; + getParentAffinityTargets(l_mcaList, l_nvdimm, TARGETING::CLASS_UNIT, TARGETING::TYPE_MCA); + assert(l_mcaList.size(), "nvdimmRestore() failed to find parent MCA."); + + fapi2::Target<fapi2::TARGET_TYPE_MCA> l_fapi_mca(l_mcaList[0]); + + // This is done again at the port level + // Post restore consists of exiting self-refresh, restoring MRS/RCD, and running ZQCAL + FAPI_INVOKE_HWP(l_err, mss::nvdimm::post_restore_transition, l_fapi_mca); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmRestore() HUID[%X] post_restore_transition failed!", + TARGETING::get_huid(l_nvdimm)); + + // Commit the error from the HWP + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOPRSV); + break; + } + else + { + // Restore success! + nvdimmSetStatusFlag(l_nvdimm, NSTD_VAL_PRSV); + } + } + + }while(0); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmRestore() restore completed!!"); + + return l_err; +} + +/** + * @brief This function checks the erase status register to make sure + * the last erase completed witout error + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmCheckEraseSuccess(Target *i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmCheckEraseSuccess() : nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + uint8_t l_data = 0; + errlHndl_t l_err = nullptr; + + l_err = nvdimmReadReg(i_nvdimm, ERASE_STATUS, l_data); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckEraseSuccess() nvdimm[%X]" + "failed to read erase status reg!",TARGETING::get_huid(i_nvdimm)); + } + else if ((l_data & ERASE_SUCCESS) != ERASE_SUCCESS) + { + + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmCheckEraseSuccess() nvdimm[%X]" + "failed to erase!",TARGETING::get_huid(i_nvdimm)); + /*@ + *@errortype + *@reasoncode NVDIMM_ERASE_FAILED + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_CHECK_ERASE + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2 <UNUSED> + *@devdesc Encountered error erasing previously stored data image + * on NVDIMM. Likely due to timeout and/or controller error + *@custdesc NVDIMM error erasing data image + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_CHECK_ERASE, + NVDIMM_ERASE_FAILED, + NVDIMM_SET_USER_DATA_1(ERASE, TARGETING::get_huid(i_nvdimm)), + 0x0, + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + errlCommit( l_err, NVDIMM_COMP_ID ); + //@TODO RTC 199645 - add HW callout on dimm target. + //failure to erase could mean internal NV controller error and/or + //HW error on nand flash. NVDIMM will lose persistency if failed to + //erase nand flash + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmCheckEraseSuccess(): nvdimm[%X] ret[%X]", + TARGETING::get_huid(i_nvdimm), l_data); + + return l_err; +} + +/** + * @brief This function erases image on the nvdimm target + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmEraseNF(Target *i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmEraseNF() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + + do + { + l_err = nvdimmWriteReg(i_nvdimm, NVDIMM_FUNC_CMD, ERASE_IMAGE); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"NDVIMM HUID[%X] error initiating erase!!", + TARGETING::get_huid(i_nvdimm)); + break; + } + + // Erase happens one module at a time. No need to set any offset on the counter + uint32_t l_poll = 0; + l_err = nvdimmPollEraseDone(i_nvdimm, l_poll); + if (!l_err) + { + l_err = nvdimmCheckEraseSuccess(i_nvdimm); + } + + }while(0); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmEraseNF() nvdimm[%X]", + TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief This functions opens the NV controller to the specified page + * Refer to the BAEBI to see what each page does + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[in] i_page - page number to open to + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log + */ +errlHndl_t nvdimmOpenPage(Target *i_nvdimm, + uint8_t i_page) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmOpenPage nvdimm[%X]", TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + bool l_success = false; + uint8_t l_data; + uint32_t l_poll = 0; + + uint32_t l_target_timeout_values[6]; + assert(i_nvdimm->tryGetAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(l_target_timeout_values), + "nvdimmOpenPage() HUID[%X], failed reading ATTR_NV_OPS_TIMEOUT_MSEC!", TARGETING::get_huid(i_nvdimm)); + + uint32_t l_timeout = l_target_timeout_values[PAGE_SWITCH]; + + do + { + + l_err = nvdimmWriteReg(i_nvdimm, OPEN_PAGE, i_page); + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmOpenPage nvdimm[%X]" + "error writing to page change reg", TARGETING::get_huid(i_nvdimm)); + break; + } + + // This should not take long, but putting a loop here anyway + // to make sure it finished within time + // Not using the nvdimmPollStatus since this is polled differently + do + { + l_err = nvdimmReadReg(i_nvdimm, OPEN_PAGE, l_data); + if (l_err){ + break; + } + + if (l_data == i_page){ + l_success = true; + break; + } + + nanosleep(0, PAGE_SWITCH_POLL_TIME_NS); + l_poll += PAGE_SWITCH_POLL_TIME_NS; + + }while (l_poll < l_timeout*NS_PER_MSEC); + + if (l_err) + { + break; + } + + //timed out + if (!l_success && !l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmOpenPage nvdimm[%X] openpage_success[%d]," + "failure to open page!", TARGETING::get_huid(i_nvdimm), static_cast<uint8_t>(l_success)); + + /*@ + *@errortype + *@reasoncode NVDIMM_OPEN_PAGE_TIMEOUT + *@severity ERRORLOG_SEV_PREDICTIVE + *@moduleid NVDIMM_OPEN_PAGE + *@userdata1[0:31] Related ops (0xff = NA) + *@userdata1[32:63] Target Huid + *@userdata2[0:31] Polled value + *@userdata2[32:63] Timeout value + *@devdesc NVDIMM OpenPage timed out, likely due to controller error + *@custdesc Encountered error performing internal operaiton + * on NVDIMM + */ + l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_PREDICTIVE, + NVDIMM_POLL_STATUS, + NVDIMM_STATUS_TIMEOUT, + NVDIMM_SET_USER_DATA_1(PAGE_SWITCH, TARGETING::get_huid(i_nvdimm)), + NVDIMM_SET_USER_DATA_2_TIMEOUT(l_poll, l_timeout), + ERRORLOG::ErrlEntry::NO_SW_CALLOUT ); + + l_err->collectTrace(NVDIMM_COMP_NAME, 256 ); + //@TODO RTC 199645 - add HW callout on dimm target. + //failure to open page most likely means problem with + //the NV controller. + } + }while(0); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmOpenPage nvdimm[%X] nvdimmOpenPage.success[%d]," + ,TARGETING::get_huid(i_nvdimm), static_cast<uint8_t>(l_success)); + + return l_err; +} + +/** + * @brief This function gets the timeout values and fill out + * ATTR_NV_OPS_TIMEOUT_MSEC for nvdimm + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmGetTimeoutVal(Target* i_nvdimm) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmGetTimeoutVal() HUID[%X]" + ,TARGETING::get_huid(i_nvdimm)); + + errlHndl_t l_err = nullptr; + uint8_t l_data = 0; + uint32_t timeout_map[6]; + i_nvdimm->tryGetAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(timeout_map); + + //Get the 6 main timeout values + for (uint8_t i = SAVE; i <= CHARGE; i++){ + + // Some timeout value maybe in different page + l_err = nvdimmOpenPage(i_nvdimm, timeoutInfoTable[i].page); + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ENTER_MRK"nvdimmGetTimeoutVal() HUID[%X] " + "failing on nvdimmOpenPage()",TARGETING::get_huid(i_nvdimm)); + break; + } + + // Need to loop thru both offsets to get the full timeout value + // The first offset contains the MSByte of the timeout value + // with the MSB indicating ms or sec + // The second offset contains the LSByte of the value + for (uint8_t j = 0; j < NUM_OFFSET; j++){ + + timeout_map[i] = timeout_map[i] << 8; + + l_err = nvdimmReadReg(i_nvdimm, timeoutInfoTable[i].offset[j], l_data); + + if (l_err) + { + break; + } + + timeout_map[i] = timeout_map[i] | static_cast<uint32_t>(l_data); + + } + + if (l_err) + { + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimmGetTimeoutVal() HUID[%X] " + "error reading timeout value for op[%d]!", TARGETING::get_huid(i_nvdimm), i); + break; + } + + //Converting to msec depending on bit 15. 1 = sec, 0 = msec + //except for charge. Charge is only in seconds so convert anyway + if (timeout_map[i] >= 0x8000 || i == CHARGE){ + timeout_map[i] = timeout_map[i] & 0x7FFF; + timeout_map[i] = timeout_map[i] * MS_PER_SEC; + } + + TRACFCOMP(g_trac_nvdimm, "nvdimmGetTimeoutVal() HUID[%X], timeout_idx[%d], timeout_ms[%d]" + ,TARGETING::get_huid(i_nvdimm), timeoutInfoTable[i].idx, timeout_map[i]); + } + + if (!l_err) + { + i_nvdimm->setAttr<TARGETING::ATTR_NV_OPS_TIMEOUT_MSEC>(timeout_map); + } + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimmGetTimeoutVal() HUID[%X]" + ,TARGETING::get_huid(i_nvdimm)); + + return l_err; +} + +/** + * @brief Entry function to NVDIMM management + * - Restore image from NVDIMM NAND flash to DRAM + * - Arms the backup trigger to ddr_reset_n once the restore + * is completed + * - Erase image + * + * @param[in] i_nvdimmList - list of nvdimm targets + * + */ +void nvdimm_restore(TargetHandleList &i_nvdimmList) +{ + TRACUCOMP(g_trac_nvdimm, ENTER_MRK"nvdimm_restore()"); + errlHndl_t l_err = nullptr; + + do + { + // Typically during init the NV controller defaults the page + // to 0, but it doesn't in warmboot because the controller + // doesn't get power cycled. So, let's change it to page 0 + // anyway right at the beginning. + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimmOpenPage(l_nvdimm, ZERO); + if (l_err) + { + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR); + break; + } + + } + + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + + // @TODO-RTC:200275-Make logic generic for all system configs + // Because of interleaving, if one is garded the other one + // in the pair should be garded by association. So, let's + // move on since there is nothing else to do. + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing open page"); + break; + } + + // Before proceeding, make sure the NV controller + // is in ready state. + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimmReady(l_nvdimm); + if (l_err) + { + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR); + break; + } + } + + if (l_err) + { + // If this failing right off the bat, + // something isn't quite right with + // the module + errlCommit(l_err, NVDIMM_COMP_ID); + + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing nvdimmReady()"); + break; + } + + // Set the energy policy to device-managed + // Don't think this is needed for the supercaps to start charging + // but on some devices this is needed to get the charge timeout value + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimmSetESPolicy(l_nvdimm); + if (l_err) + { + // Failing this is an indication of power pack issue. + // This will prevent future backup, but let's continue + // since we can still restore the data if there is any + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOBKUP); + errlCommit( l_err, NVDIMM_COMP_ID ); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing nvdimmSetESPolicy()"); + } + } + + // Get the timeout values for the major ops at init + if (init){ + for (const auto & l_nvdimm : i_nvdimmList){ + l_err = nvdimmGetTimeoutVal(l_nvdimm); + if (l_err) + { + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR); + break; + } + } + init = false; + } + + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing nvdimmGetTimeoutVal()"); + break; + } + + // Change back to page 0 just in case, as all of the remaining + // operations will be using offsets in page 0 + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimmOpenPage(l_nvdimm, ZERO); + if (l_err) + { + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR); + break; + } + + } + + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing open page"); + break; + } + + // Start the restore + l_err = nvdimmRestore(i_nvdimmList); + + if (l_err) + { + errlCommit( l_err, NVDIMM_COMP_ID ); + TRACFCOMP(g_trac_nvdimm, ERR_MRK"nvdimm_restore() - Failing nvdimmRestore()"); + break; + } + + // Make sure the energy source is fully charged before erasing the images + // Doing this on all the nvdimms since the ones w/o image will need + // to be fully charged before arming the trigger + uint32_t l_poll = 0; + for (const auto & l_nvdimm : i_nvdimmList) + { + l_err = nvdimmPollESChargeStatus(l_nvdimm, l_poll); + + if (l_err){ + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOBKUP); + errlCommit( l_err, NVDIMM_COMP_ID ); + } + } + + // @TODO move arm and erase to pm_common.C, after occ has started + + // From this point forward, we only want to set up the trigger on the + // nvdimm that is not in error state. Any nvdimm in error state will + // get to preserve the data previously saved and recover if needed. + + // Arm the nvdimm to trigger save on RESET_n + // and erase the image + for (const auto & l_nvdimm : i_nvdimmList) + { + // skip if the nvdimm is in error state + if (nvdimmInErrorState(l_nvdimm)) + { + continue; + } + + l_err = nvdimmArmResetN(l_nvdimm); + if (l_err) + { + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOBKUP); + errlCommit( l_err, NVDIMM_COMP_ID ); + continue; + } + + l_err = nvdimmEraseNF(l_nvdimm); + if (l_err){ + errlCommit( l_err, NVDIMM_COMP_ID ); + nvdimmSetStatusFlag(l_nvdimm, NSTD_ERR_NOBKUP); + } + } + }while(0); + + TRACUCOMP(g_trac_nvdimm, EXIT_MRK"nvdimm_restore()"); +} + +} // end NVDIMM namespace diff --git a/src/usr/isteps/nvdimm/nvdimm.H b/src/usr/isteps/nvdimm/nvdimm.H new file mode 100644 index 000000000..12a2ee2aa --- /dev/null +++ b/src/usr/isteps/nvdimm/nvdimm.H @@ -0,0 +1,151 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/isteps/nvdimm/nvdimm.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014,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__ + +#include <usr/errl/errlentry.H> +#include <targeting/common/commontargeting.H> +#include <targeting/common/util.H> +#include <targeting/common/utilFilter.H> +#include <i2c/eepromif.H> +#include <map> + +using namespace EEPROM; + +namespace NVDIMM +{ + +//Defining necessary offsets for page0 +//Refer to BAEBI spec for details +//https://www.jedec.org/standards-documents/docs/jesd245a +enum i2cReg : uint8_t +{ + ARM_CMD = 0x45, + ARM_STATUS = 0x6A, + NVDIMM_FUNC_CMD = 0x43, + NVDIMM_CMD_STATUS0 = 0x61, + CSAVE_INFO = 0x80, + CSAVE_STATUS = 0x64, + RESTORE_STATUS = 0x66, + SET_ES_POLICY_CMD = 0x49, + SET_ES_POLICY_STATUS = 0x70, + NVDIMM_MGT_CMD0 = 0x40, + ERASE_STATUS = 0x68, + MODULE_HEALTH = 0xA0, + MODULE_HEALTH_STATUS0 = 0xA1, + MODULE_HEALTH_STATUS1 = 0xA2, + OPEN_PAGE = 0x00, + NVDIMM_READY = 0x60, +}; + +// Up to 10 pages per BAEBI Spec, +// but use page 0 mostly +enum page : uint8_t +{ + ZERO = 0x00, + ONE = 0x01, + TWO = 0x02, + THREE = 0x03, + FOUR = 0x04, +}; + +// Enums for inputs/expected output to/from the i2c registers +enum i2c_in_values : uint8_t +{ + ARM_DIMM = 0x04, //0x04 to trigger on RESET_n + ES_DEV_MANAGE = 0x01, //0x01 for device manage + ERASE_IMAGE = 0x08, + RESTORE_IMAGE = 0x04, + RESET_CTRLR = 0x01, + VALID_IMAGE = 0x01, + PAGE_SWITCH_POLL_TIME_NS = 100, +}; + +enum i2c_out_values : uint8_t +{ + SAVE_IN_PROGRESS = 0x05, + RSTR_IN_PROGRESS = 0x09, + ERASE_IN_PROGRESS = 0x11, + ARM_IN_PROGRESS = 0x41, + CHARGE_IN_PROGRESS = 0x41, + SAVE_SUCCESS = 0x01, + RSTR_SUCCESS = 0X01, + ARM_SUCCESS = 0X09, + ERASE_SUCCESS = 0X01, + ES_SUCCESS = 0x05, + CHARGE_SUCCESS = 0x00, + NV_READY = 0xA5, +}; + +// Timeout-related enum +enum timeout : uint32_t +{ + OPS_POLL_TIME_MS = 5000, +}; + +// Assign an id to each of the 6 major ops +enum ops_id : uint8_t +{ + SAVE = 0, + RESTORE, + ERASE, + ARM, + PAGE_SWITCH, + CHARGE, +}; + +/** + * @brief Wrapper to call deviceOp to read the NV controller via I2C + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[in] i_addr - address/offset for the register to be read + * + * @param[out] o_data - returned data from read + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmReadReg(TARGETING::Target* i_nvdimm, uint8_t i_addr, uint8_t & o_data ); + +/** + * @brief Wrapper to call deviceOp to write the NV controller via I2C + * + * @param[in] i_nvdimm - nvdimm target with NV controller + * + * @param[in] i_addr - address/offset for the register to be written + * + * @param[in] i_data - data to register + * + * @return errlHndl_t - Null if successful, otherwise a pointer to + * the error log. + */ +errlHndl_t nvdimmWriteReg(TARGETING::Target* i_nvdimm, uint8_t i_addr, uint8_t i_data ); + +} //End NVDIMM namespace + + +#endif // NVDIMM_H__ |