diff options
Diffstat (limited to 'src/usr/i2c')
-rw-r--r-- | src/usr/i2c/eepromCache.C | 812 | ||||
-rw-r--r-- | src/usr/i2c/eepromCache.H | 157 | ||||
-rw-r--r-- | src/usr/i2c/eepromCache_common.C | 325 | ||||
-rw-r--r-- | src/usr/i2c/eeprom_utils.C | 14 | ||||
-rwxr-xr-x | src/usr/i2c/eepromdd.C | 73 | ||||
-rw-r--r-- | src/usr/i2c/eepromdd_hardware.C | 16 | ||||
-rw-r--r-- | src/usr/i2c/fapi_i2c_dd.C | 74 | ||||
-rwxr-xr-x | src/usr/i2c/i2c.C | 138 | ||||
-rwxr-xr-x | src/usr/i2c/i2c.H | 4 | ||||
-rw-r--r-- | src/usr/i2c/i2c.mk | 4 | ||||
-rw-r--r-- | src/usr/i2c/i2cTargetPres.C | 127 | ||||
-rw-r--r-- | src/usr/i2c/makefile | 1 | ||||
-rw-r--r-- | src/usr/i2c/runtime/makefile | 6 | ||||
-rw-r--r-- | src/usr/i2c/runtime/rt_eepromCache.C | 285 | ||||
-rwxr-xr-x | src/usr/i2c/runtime/rt_i2c.C | 8 | ||||
-rw-r--r-- | src/usr/i2c/test/eecachetest.H | 121 | ||||
-rw-r--r-- | src/usr/i2c/test/makefile | 3 | ||||
-rwxr-xr-x | src/usr/i2c/test/tpmddtest.H | 36 | ||||
-rwxr-xr-x | src/usr/i2c/tpmdd.C | 3 |
19 files changed, 1757 insertions, 450 deletions
diff --git a/src/usr/i2c/eepromCache.C b/src/usr/i2c/eepromCache.C index ef906302c..bbdddf334 100644 --- a/src/usr/i2c/eepromCache.C +++ b/src/usr/i2c/eepromCache.C @@ -30,17 +30,18 @@ #include <devicefw/driverif.H> #include <errl/errlmanager.H> #include <fsi/fsiif.H> +#include <hwas/hwasPlat.H> #include "i2c.H" #include "eepromCache.H" #include <i2c/i2cif.H> -#include <i2c/eepromif.H> + #include <i2c/eepromddreasoncodes.H> #include <initservice/initserviceif.H> #include <initservice/initsvcreasoncodes.H> #include <pnor/pnorif.H> #include <vpd/vpd_if.H> + #include <errl/errludtarget.H> -#include <config.h> #ifdef CONFIG_CONSOLE #include <console/consoleif.H> #endif @@ -53,6 +54,10 @@ extern trace_desc_t* g_trac_eeprom; namespace EEPROM { +// Any time we access either any of the global variables defined below we want +// to wrap the call in this mutex to avoid multi-threading issues +mutex_t g_eecacheMutex = MUTEX_INITIALIZER; + // Global variable that will keep track of the virtual address which // points to the start of the EECACHE section, and the size of this section. // It is handy to keep these around so we do not need to look them up in the @@ -63,166 +68,10 @@ uint64_t g_eecachePnorSize = 0; // Global map which is used as a way to quickly look up the virtual address // of a given eeprom's cached data in EECACHE section // Key = eepromRecordHeader with unique info filled out -// Value = virtual address pointing to the cached eeprom data in pnor -std::map<eepromRecordHeader, uint64_t> g_cachedEeproms; - -// Any time we access either any of the global variables defined above we want -// to wrap the call in this mutex to avoid multi-threading issues -mutex_t g_eecacheMutex = MUTEX_INITIALIZER; - -uint64_t lookupEepromAddr(const eepromRecordHeader& i_eepromRecordHeader) -{ - uint64_t l_vaddr = 0; - std::map<eepromRecordHeader, uint64_t>::iterator l_it; - - // Wrap lookup in mutex because reads are not thread safe - mutex_lock(&g_eecacheMutex); - l_it = g_cachedEeproms.find(i_eepromRecordHeader); - mutex_unlock(&g_eecacheMutex); - - if(l_it != g_cachedEeproms.end()) - { - l_vaddr = l_it->second; - } - - if(l_vaddr == 0) - { - TRACSSCOMP( g_trac_eeprom, "lookupEepromAddr() failed to find I2CM Huid: 0x%.08X, Port: 0x%.02X, Engine: 0x%.02X, Dev Addr: 0x%.02X, Mux Select: 0x%.02X, Size: 0x%.08X in g_cachedEeproms", - i_eepromRecordHeader.completeRecord.i2c_master_huid, - i_eepromRecordHeader.completeRecord.port, - i_eepromRecordHeader.completeRecord.engine, - i_eepromRecordHeader.completeRecord.devAddr, - i_eepromRecordHeader.completeRecord.mux_select, - i_eepromRecordHeader.completeRecord.cache_copy_size); - } - return l_vaddr; -} - -errlHndl_t buildEepromRecordHeader(TARGETING::Target * i_target, - eeprom_addr_t & io_eepromInfo, - eepromRecordHeader & o_eepromRecordHeader) -{ - - TARGETING::Target * l_muxTarget = nullptr; - TARGETING::Target * l_i2cMasterTarget = nullptr; - TARGETING::TargetService& l_targetService = TARGETING::targetService(); - errlHndl_t l_errl = nullptr; - - do{ - - l_errl = eepromReadAttributes(i_target, io_eepromInfo); - if(l_errl) - { - TRACFCOMP( g_trac_eeprom, - "buildEepromRecordHeader() error occured reading eeprom attributes for eepromType %d, target 0x%.08X, returning!!", - io_eepromInfo.eepromRole, - TARGETING::get_huid(i_target)); - l_errl->collectTrace(EEPROM_COMP_NAME); - break; - } - - // Grab the I2C mux target so we can read the HUID, if the target is NULL we will not be able - // to lookup attribute to uniquely ID this eeprom so we will not cache it - l_muxTarget = l_targetService.toTarget( io_eepromInfo.i2cMuxPath); - if(l_muxTarget == nullptr) - { - TRACFCOMP( g_trac_eeprom, - "buildEepromRecordHeader() Mux target associated with target 0x%.08X resolved to a nullptr , check attribute for eepromType %d. Skipping Cache", - TARGETING::get_huid(i_target), - io_eepromInfo.eepromRole); - /*@ - * @errortype - * @moduleid EEPROM_CACHE_EEPROM - * @reasoncode EEPROM_I2C_MUX_PATH_ERROR - * @userdata1 HUID of target we want to cache - * @userdata2 Type of EEPROM we are caching - * @devdesc buildEepromRecordHeader invalid mux target - */ - l_errl = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_UNRECOVERABLE, - EEPROM_CACHE_EEPROM, - EEPROM_I2C_MUX_PATH_ERROR, - TARGETING::get_huid(i_target), - io_eepromInfo.eepromRole, - ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); - l_errl->collectTrace(EEPROM_COMP_NAME); - break; - } - - // Grab the I2C master target so we can read the HUID, if the target is NULL we will not be able - // to lookup attribute to uniquely ID this eeprom so we will not cache it - l_i2cMasterTarget = l_targetService.toTarget( io_eepromInfo.i2cMasterPath ); - if(l_i2cMasterTarget == nullptr) - { - TRACFCOMP( g_trac_eeprom, - "buildEepromRecordHeader() I2C Master target associated with target 0x%.08X resolved to a nullptr , check attribute for eepromType %d. Skipping Cache ", - TARGETING::get_huid(i_target), - io_eepromInfo.eepromRole); - /*@ - * @errortype - * @moduleid EEPROM_CACHE_EEPROM - * @reasoncode EEPROM_I2C_MASTER_PATH_ERROR - * @userdata1 HUID of target we want to cache - * @userdata2 Type of EEPROM we are caching - * @devdesc buildEepromRecordHeader invalid master target - */ - l_errl = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_UNRECOVERABLE, - EEPROM_CACHE_EEPROM, - EEPROM_I2C_MASTER_PATH_ERROR, - TARGETING::get_huid(i_target), - io_eepromInfo.eepromRole, - ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); - l_errl->collectTrace(EEPROM_COMP_NAME); - break; - } - - // This is what we will compare w/ when we are going through the existing - // caches in the eeprom to see if we have already cached something - // Or if no matches are found we will copy this into the header - o_eepromRecordHeader.completeRecord.i2c_master_huid = l_i2cMasterTarget->getAttr<TARGETING::ATTR_HUID>(); - o_eepromRecordHeader.completeRecord.port = static_cast<uint8_t>(io_eepromInfo.port); - o_eepromRecordHeader.completeRecord.engine = static_cast<uint8_t>(io_eepromInfo.engine); - o_eepromRecordHeader.completeRecord.devAddr = static_cast<uint8_t>(io_eepromInfo.devAddr); - o_eepromRecordHeader.completeRecord.mux_select = static_cast<uint8_t>(io_eepromInfo.i2cMuxBusSelector); - o_eepromRecordHeader.completeRecord.cache_copy_size = static_cast<uint32_t>(io_eepromInfo.devSize_KB); - - // Do not set valid bit nor internal offset here as we do not have - // enough information availible to determine - - }while(0); - - return l_errl; -} - -// Do NOT allow adding/removing eeproms to cache during RT -#ifndef __HOSTBOOT_RUNTIME - -bool addEepromToCachedList(const eepromRecordHeader & i_eepromRecordHeader) -{ - bool l_matchFound = true; - std::map<eepromRecordHeader, uint64_t>::iterator it; - - // Map accesses are not thread safe, make sure this is always wrapped in mutex - mutex_lock(&g_eecacheMutex); - - if(g_cachedEeproms.find(i_eepromRecordHeader) == g_cachedEeproms.end()) - { - g_cachedEeproms[i_eepromRecordHeader] = g_eecachePnorVaddr + i_eepromRecordHeader.completeRecord.internal_offset; - TRACSSCOMP( g_trac_eeprom, "addEepromToCachedList() Adding I2CM Huid: 0x%.08X, Port: 0x%.02X, Engine: 0x%.02X, Dev Addr: 0x%.02X, Mux Select: 0x%.02X, Size: 0x%.08X to g_cachedEeproms", - i_eepromRecordHeader.completeRecord.i2c_master_huid, - i_eepromRecordHeader.completeRecord.port, - i_eepromRecordHeader.completeRecord.engine, - i_eepromRecordHeader.completeRecord.devAddr, - i_eepromRecordHeader.completeRecord.mux_select, - i_eepromRecordHeader.completeRecord.cache_copy_size); - l_matchFound = false; - } - - mutex_unlock(&g_eecacheMutex); - - return l_matchFound; -} +// Value = A struct of 2 uint64_t virtual addresses ,one points to header address +// and other points to the location of the cache, and a byte indicating +// if this eeprom's hardware has changed this IPL +std::map<eepromRecordHeader, EeepromEntryMetaData_t> g_cachedEeproms; /** * @brief Lookup I2C information for given eeprom, check if eeprom exists in cache. @@ -252,13 +101,19 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, bool l_updateHeader = true; bool l_updateContents = true; + // Initially assume this is a new eeprom cache entry + bool l_newEntryDetected = true; + do{ // eepromReadAttributes keys off the eepromRole value // to determine what attribute to lookup to get eeprom info l_eepromInfo.eepromRole = i_eepromType; // if the target is present, then this record is valid - l_eepromRecordHeader.completeRecord.cached_copy_valid = i_present; + if(i_present) + { + l_eepromRecordHeader.completeRecord.cached_copy_valid = 1; + } // buildEepromRecordHeader will call eepromReadAttributes to fill in l_eepromInfo // with info looked up in attributes and also fill in l_eepromRecordHeader @@ -271,7 +126,7 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, if(l_errl) { // buildEepromRecordHeader should have traced any relavent information if - // is was needed, just break out and pass the error along + // it was needed, just break out and pass the error along break; } @@ -310,7 +165,7 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, // if nothing has been cached before then version should // be set to be the latest version of the struct available l_eecacheSectionHeaderPtr->version = EECACHE_VERSION_LATEST; - TRACFCOMP( g_trac_eeprom, + TRACDCOMP( g_trac_eeprom, "cacheEeprom() Found Empty Cache, set version of cache structure to be 0x%.02x", EECACHE_VERSION_1); } @@ -322,7 +177,7 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, // This means the start of first eeprom's cached data will be immediately // following the end of the EECACHE header. l_eecacheSectionHeaderPtr->end_of_cache = sizeof(eecacheSectionHeader); - TRACFCOMP( g_trac_eeprom, + TRACDCOMP( g_trac_eeprom, "cacheEeprom() Found Empty Cache, set end of cache to be 0x%.04x (End of ToC)", sizeof(eecacheSectionHeader)); } @@ -356,6 +211,9 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, // to be the current "end of cache" offset in the toc. l_eepromRecordHeader.completeRecord.internal_offset = l_eecacheSectionHeaderPtr->end_of_cache; l_eecacheSectionHeaderPtr->end_of_cache += l_eepromLen; + + // Set cached_copy_valid to 0 until the cache contents actually gets loaded + l_recordHeaderToUpdate->completeRecord.cached_copy_valid = 0; l_updateContents = i_present; break; } @@ -366,6 +224,9 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, if( memcmp(l_recordHeaderToUpdate, &l_eepromRecordHeader, NUM_BYTE_UNIQUE_ID ) == 0 ) { l_recordHeaderToUpdateIndex = i; + // We have matched with existing eeprom in the PNOR's EECACHE + // section. So we know this is not a new entry. + l_newEntryDetected = false; if( l_recordHeaderToUpdate->completeRecord.cache_copy_size != l_eepromRecordHeader.completeRecord.cache_copy_size) { @@ -410,81 +271,192 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, #ifdef CONFIG_CONSOLE CONSOLE::displayf(EEPROM_COMP_NAME, - "New EEPROM size detected for an existing part, clearing EEPROM cache and performing reconfig loop"); + "New EEPROM size detected for an existing part," + "clearing EEPROM cache and performing reconfig loop"); #endif INITSERVICE::doShutdown(INITSERVICE::SHUTDOWN_DO_RECONFIG_LOOP); } - // - // At this point we have found a match in the PNOR but we need - // to decide what all needs an update - // - - // Stash the internal_offset of the section we found in so we can add - // this record to g_cachedEeproms for later use + // Stash the internal_offset of the section we found in so we + // can add this record to g_cachedEeproms for later use l_eepromRecordHeader.completeRecord.internal_offset = - l_recordHeaderToUpdate->completeRecord.internal_offset; + l_recordHeaderToUpdate->completeRecord.internal_offset; + TRACSSCOMP(g_trac_eeprom, + "cacheEeprom() already found copy for eeprom role %d " + "for target w/ HUID 0x.%08X in EECACHE table of contents", + i_eepromType , TARGETING::get_huid(i_target)); + break; + } + } - if(l_recordHeaderToUpdate->completeRecord.cached_copy_valid) - { - // If the existing eeprom record is valid, then only update the - // contents if the SN/PN for current HW do not match the eeprom - // record. (target must be present to cache) + // pass the record we have been building up (l_eepromRecordHeader) + // and the virtual address of this eeprom's record entry in the + // EECACHE table of contents as a uint64. + if(!addEepromToCachedList(l_eepromRecordHeader, + reinterpret_cast<uint64_t>(l_recordHeaderToUpdate))) + { + TRACSSCOMP( g_trac_eeprom, + "cacheEeprom() Eeprom w/ Role %d, HUID 0x.%08X added to the global map of cached eeproms", + i_eepromType , TARGETING::get_huid(i_target)); + } + else + { + // If this target's eeprom has already been cached in PNOR and our global map + // indicates the cache entry was updated this boot, then we must also + // mark this target associated with the cached eeprom as changed for hwas + if( hasEeepromChanged( l_eepromRecordHeader ) ) + { + HWAS::markTargetChanged(i_target); + } + TRACSSCOMP( g_trac_eeprom, + "cacheEeprom() Eeprom w/ Role %d, HUID 0x.%08X already in global map of cached eeproms", + i_eepromType , TARGETING::get_huid(i_target)); - // TODO RTC:203788 add lookup for PN and SN matches - //if( !i_present || PNandSNMatch ) - { - l_updateContents = false; - } + // Cache entry has already been updated via another target, just break out + break; + } - // If target is present there is nothing in the - // header to update - if( i_present ) - { - l_updateHeader = false; - } + // Only check if the cache is in sync with HARDWARE if there is an + // existing EECACHE section. Otherwise, the code after this logic will + // take care of adding a new eeprom cache section for the target. + if (l_recordHeaderToUpdate->completeRecord.cached_copy_valid) + { + // At this point we have found a match in the PNOR but we need + // to decide what all needs an update. + + // Create namespace alias for targeting to reduce number of + // new lines required to be within line character limit. + namespace T = TARGETING; + + // If the existing eeprom record is valid, then only update + // the contents if the SN/PN for current HW do not match the + // eeprom record. (target must be present to cache) + T::EEPROM_CONTENT_TYPE l_eepromContentType = + T::EEPROM_CONTENT_TYPE_RAW; + + if (i_eepromType == EEPROM::VPD_PRIMARY) + { + auto l_eepromVpd = + i_target->getAttr<T::ATTR_EEPROM_VPD_PRIMARY_INFO>(); + + l_eepromContentType = + static_cast<T::EEPROM_CONTENT_TYPE>( + l_eepromVpd.eepromContentType); + } + else + { + auto l_eepromVpd = + i_target->getAttr<T::ATTR_EEPROM_VPD_BACKUP_INFO>(); + + l_eepromContentType = + static_cast<T::EEPROM_CONTENT_TYPE>( + l_eepromVpd.eepromContentType); + } + + + bool l_isInSync = false; + + if (i_present) + { + l_errl = VPD::ensureEepromCacheIsInSync(i_target, + l_eepromContentType, + l_isInSync); + + if (l_errl != nullptr) + { + break; } - else if(!i_present) + + if(l_isInSync) { - // If the target is not present, then do not update contents or header l_updateContents = false; - l_updateHeader = false; } - TRACSSCOMP( g_trac_eeprom, "cacheEeprom() already found copy for eeprom role %d for target w/ HUID 0x.%08X", - i_eepromType , TARGETING::get_huid(i_target)); - break; } - } + else + { + // Clear out the contents of the cache for this eeprom if we have detected that it + // was once valid, indicating it was present at one time, and is now showing + // up as not present. We want to clear the contents of cache so we can achieve + // the replug behavior where a tester can remove the part, boot, then plug in the + // same part and boot again fresh. + void * l_internalSectionAddr = + reinterpret_cast<uint8_t *>(l_eecacheSectionHeaderPtr) + + l_eepromRecordHeader.completeRecord.internal_offset; + + memset( l_internalSectionAddr, 0xFF , + (l_recordHeaderToUpdate->completeRecord.cache_copy_size * KILOBYTE)); + + l_updateContents = false; + + setIsValidCacheEntry(l_eepromRecordHeader, false); + + TRACFCOMP( g_trac_eeprom, "Detected Master 0x%.08X" + " Engine 0x%.02X Port 0x%.02X" + " MuxSelect 0x%.02X DevAddr 0x%.02X" + " no longer present, clearing cache and marking cache as invalid", + l_recordHeaderToUpdate->completeRecord.i2c_master_huid, + l_recordHeaderToUpdate->completeRecord.engine, + l_recordHeaderToUpdate->completeRecord.port, + l_recordHeaderToUpdate->completeRecord.mux_select, + l_recordHeaderToUpdate->completeRecord.devAddr); + + setEeepromChanged(l_eepromRecordHeader); + // We have cleared the cache entry, this indicates we have found a part has been removed. + // Mark that the target is changed in hwas. + HWAS::markTargetChanged(i_target); + } - if(!addEepromToCachedList(l_eepromRecordHeader)) + // If target is present there is nothing in the + // header to update + if( i_present ) + { + l_updateHeader = false; + } + } + else if(!i_present) { - TRACSSCOMP( g_trac_eeprom, "cacheEeprom() Eeprom w/ Role %d, HUID 0x.%08X added to cached list", - i_eepromType , TARGETING::get_huid(i_target)); + // If the target is not present, then do not update contents + l_updateContents = false; + // Only update header if this is a new entry + l_updateHeader = l_newEntryDetected; } - else + // The check below makes sure that is isnt a new entry + // If there is a matching header entry in PNOR marked 'invalid' + // but we now see the target as present, this indicates a replacement + // part has been added where a part was removed + else if(!l_newEntryDetected) { - TRACSSCOMP( g_trac_eeprom, "cacheEeprom() Eeprom w/ Role %d, HUID 0x.%08X already in cached list", - i_eepromType , TARGETING::get_huid(i_target)); + TRACFCOMP(g_trac_eeprom, "cacheEeprom() Detected replacement of a part" + " Master 0x%.08X Engine 0x%.02X" + " Port 0x%.02X MuxSelect 0x%.02X DevAddr 0x%.02X" + " that was previously removed, we will update the cache with new part's eeproms contents", + l_recordHeaderToUpdate->completeRecord.i2c_master_huid, + l_recordHeaderToUpdate->completeRecord.engine, + l_recordHeaderToUpdate->completeRecord.port, + l_recordHeaderToUpdate->completeRecord.mux_select, + l_recordHeaderToUpdate->completeRecord.devAddr); } - // Above we have determined whether the contents of the eeprom at // hand need to have their contents updated. Only do the following // steps that update the eeprom's cached data if we were told to do so. - if(l_updateContents ) + if( l_updateContents ) { assert(l_recordHeaderToUpdateIndex != INVALID_EEPROM_INDEX, "More than MAX_EEPROMS_VERSION_1 in system XML"); + TRACFCOMP( g_trac_eeprom, "cacheEeprom() updating cache entry"); + void * l_tmpBuffer; l_tmpBuffer = malloc(l_eepromLen); void * l_internalSectionAddr = - reinterpret_cast<uint8_t *>(l_eecacheSectionHeaderPtr) + l_eepromRecordHeader.completeRecord.internal_offset; + reinterpret_cast<uint8_t *>(l_eecacheSectionHeaderPtr) + + l_eepromRecordHeader.completeRecord.internal_offset; TRACSSCOMP( g_trac_eeprom, "cacheEeprom() passing the following into deviceOp eeprom address : huid 0x%.08X length 0x%.08X vaddr %p" , get_huid(i_target), l_eepromLen, l_internalSectionAddr); @@ -543,6 +515,18 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, break; } + // Set mark_target_changed and cached_copy_valid and update set updateHeader + // Since we have copied stuff in the cache is valid, and been updated. + // Even if this is a replacement ( cached_copy_valid was already 1) we + // must set mark_target_changed so just always update the header + setEeepromChanged(l_eepromRecordHeader); + // We will update header in PNOR below so no need to call + // setIsValidCacheEntry right here + l_eepromRecordHeader.completeRecord.cached_copy_valid = 1; + l_updateHeader = true; + // We have updated the cache entry, this indicates we have found a "new" part. + // Mark that the target is changed in hwas. + HWAS::markTargetChanged(i_target); } // Above we have determined whether the header entry for the eeprom at @@ -550,6 +534,7 @@ errlHndl_t cacheEeprom(TARGETING::Target* i_target, // the eeprom's header entry if we were told to do so. if(l_updateHeader) { + TRACFCOMP( g_trac_eeprom, "cacheEeprom() updating header entry"); TRACDBIN( g_trac_eeprom, "cacheEeprom: l_eecacheSectionHeaderPtr currently ", l_eecacheSectionHeaderPtr, sizeof(eecacheSectionHeader)); @@ -658,175 +643,318 @@ DEVICE_REGISTER_ROUTE( DeviceFW::READ, TARGETING::TYPE_DIMM, genericI2CEepromCache ); -#endif +DEVICE_REGISTER_ROUTE( DeviceFW::READ, + DeviceFW::EEPROM_CACHE, + TARGETING::TYPE_NODE, + genericI2CEepromCache ); -errlHndl_t eepromPerformOpCache(DeviceFW::OperationType i_opType, - TARGETING::Target * i_target, - void * io_buffer, - size_t& io_buflen, - eeprom_addr_t &i_eepromInfo) +errlHndl_t setIsValidCacheEntry(const TARGETING::Target * i_target, + const EEPROM_ROLE &i_eepromRole, + bool i_isValid) { errlHndl_t l_errl = nullptr; eepromRecordHeader l_eepromRecordHeader; + eeprom_addr_t l_eepromInfo; do{ - TRACSSCOMP( g_trac_eeprom, ENTER_MRK"eepromPerformOpCache() " - "Target HUID 0x%.08X Enter", TARGETING::get_huid(i_target)); + TRACDCOMP( g_trac_eeprom, ENTER_MRK"setIsValidCacheEntry() " + "Target HUID 0x%.08X Eeprom Role = %d Enter", + TARGETING::get_huid(i_target), l_eepromInfo.eepromRole); - l_errl = buildEepromRecordHeader(i_target, i_eepromInfo, l_eepromRecordHeader); + l_eepromInfo.eepromRole = i_eepromRole; + l_errl = buildEepromRecordHeader(const_cast<TARGETING::Target *>(i_target), l_eepromInfo, l_eepromRecordHeader); if(l_errl) { - // buildEepromRecordHeader should have traced any relavent information if - // it was needed, just break out and pass the error along break; } - uint64_t l_eepromCacheVaddr = lookupEepromAddr(l_eepromRecordHeader); + l_errl = setIsValidCacheEntry(l_eepromRecordHeader, i_isValid); - // Ensure that a copy of the eeprom exists in our map of cached eeproms - if(l_eepromCacheVaddr) - { - // First check if io_buffer is a nullptr, if so then assume user is - // requesting size back in io_bufferlen - if(io_buffer == nullptr) - { - io_buflen = l_eepromRecordHeader.completeRecord.cache_copy_size * KILOBYTE; - TRACSSCOMP( g_trac_eeprom, "eepromPerformOpCache() " - "io_buffer == nullptr , returning io_buflen as 0x%lx", - io_buflen); - break; - } + }while(0); - TRACSSCOMP( g_trac_eeprom, "eepromPerformOpCache() " - "Performing %s on target 0x%.08X offset 0x%lx length 0x%x vaddr 0x%lx", - (i_opType == DeviceFW::READ) ? "READ" : "WRITE", - TARGETING::get_huid(i_target), - i_eepromInfo.offset, io_buflen, l_eepromCacheVaddr); + return l_errl; +} - // Make sure that offset + buflen are less than the total size of the eeprom - if(i_eepromInfo.offset + io_buflen > (l_eepromRecordHeader.completeRecord.cache_copy_size * KILOBYTE)) - { - TRACFCOMP(g_trac_eeprom, - ERR_MRK"eepromPerformOpCache: i_eepromInfo.offset + i_offset is greater than size of eeprom (0x%x KB)", - l_eepromRecordHeader.completeRecord.cache_copy_size); - /*@ - * @errortype - * @moduleid EEPROM_CACHE_PERFORM_OP - * @reasoncode EEPROM_OVERFLOW_ERROR - * @userdata1 Length of Operation - * @userdata2 Offset we are attempting to read/write - * @custdesc Soft error in Firmware - * @devdesc cacheEeprom invalid op type - */ - l_errl = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_UNRECOVERABLE, - EEPROM_CACHE_PERFORM_OP, - EEPROM_OVERFLOW_ERROR, - TO_UINT64(io_buflen), - TO_UINT64(i_eepromInfo.offset), - ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); - ERRORLOG::ErrlUserDetailsTarget(i_target).addToLog(l_errl); - l_errl->collectTrace( EEPROM_COMP_NAME ); +errlHndl_t setIsValidCacheEntry(const eepromRecordHeader& i_eepromRecordHeader, bool i_isValid) +{ + errlHndl_t l_errl = nullptr; + eepromRecordHeader * l_eepromRecordHeaderToUpdate; + std::map<eepromRecordHeader, EeepromEntryMetaData_t>::iterator l_headerMapIterator; - break; - } + do{ - if(i_opType == DeviceFW::READ) - { - memcpy(io_buffer, reinterpret_cast<void *>(l_eepromCacheVaddr + i_eepromInfo.offset), io_buflen); - } - else if(i_opType == DeviceFW::WRITE) - { - memcpy(reinterpret_cast<void *>(l_eepromCacheVaddr + i_eepromInfo.offset), io_buffer, io_buflen); + TRACDCOMP( g_trac_eeprom, ENTER_MRK"setIsValidCacheEntry() "); - #ifndef __HOSTBOOT_RUNTIME - // Perform flush to ensure pnor is updated - int rc = mm_remove_pages( FLUSH, - reinterpret_cast<void *>(l_eepromCacheVaddr + i_eepromInfo.offset), - io_buflen ); - if( rc ) - { - TRACFCOMP(g_trac_eeprom,ERR_MRK"eepromPerformOpCache: Error from mm_remove_pages trying for flush contents write to pnor! rc=%d",rc); - /*@ - * @errortype - * @moduleid EEPROM_CACHE_PERFORM_OP - * @reasoncode EEPROM_FAILED_TO_FLUSH_CONTENTS - * @userdata1 Requested Address - * @userdata2 rc from mm_remove_pages - * @devdesc cacheEeprom mm_remove_pages FLUSH failed - */ - l_errl = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_UNRECOVERABLE, - EEPROM_CACHE_PERFORM_OP, - EEPROM_FAILED_TO_FLUSH_CONTENTS, - (l_eepromCacheVaddr + i_eepromInfo.offset), - TO_UINT64(rc), - ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); - l_errl->collectTrace( EEPROM_COMP_NAME ); - } - #endif //__HOSTBOOT_RUNTIME - } - else - { - TRACFCOMP(g_trac_eeprom,ERR_MRK"eepromPerformOpCache: Invalid OP_TYPE passed to function, i_opType=%d", i_opType); - /*@ - * @errortype - * @moduleid EEPROM_CACHE_PERFORM_OP - * @reasoncode EEPROM_INVALID_OPERATION - * @userdata1[0:31] Op Type that was invalid - * @userdata1[32:63] Eeprom Role - * @userdata2 Offset we are attempting to perfrom op on - * @custdesc Soft error in Firmware - * @devdesc cacheEeprom invalid op type - */ - l_errl = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_UNRECOVERABLE, - EEPROM_CACHE_PERFORM_OP, - EEPROM_INVALID_OPERATION, - TWO_UINT32_TO_UINT64(i_opType, - i_eepromInfo.eepromRole), - TO_UINT64(i_eepromInfo.offset), - ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); - ERRORLOG::ErrlUserDetailsTarget(i_target).addToLog(l_errl); - l_errl->collectTrace( EEPROM_COMP_NAME ); - } + // Find the address of the header entry in the table of contents of the EECACHE pnor section + l_eepromRecordHeaderToUpdate = + reinterpret_cast<eepromRecordHeader *>(lookupEepromHeaderAddr(i_eepromRecordHeader)); + + if(l_eepromRecordHeaderToUpdate == 0) + { + TRACFCOMP(g_trac_eeprom, + ERR_MRK"setIsValidCacheEntry: Attempting to invalidate cache for an " + "eeprom but we could not find in global eecache map"); + /*@ + * @errortype + * @moduleid EEPROM_INVALIDATE_CACHE + * @reasoncode EEPROM_CACHE_NOT_FOUND_IN_MAP + * @userdata1[0:7] i2c_master_huid + * @userdata1[8:9] port on i2c master eeprom slave is on + * @userdata1[10:11] engine on i2c master eeprom slave is on + * @userdata1[12:13] devAddr of eeprom slave + * @userdata1[14:15] muxSelect of eeprom slave (0xFF is not valid) + * @userdata2[0:7] size of eeprom + * @devdesc invalidateCache failed to find cache in map + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_INVALIDATE_CACHE, + EEPROM_CACHE_NOT_FOUND_IN_MAP, + TWO_UINT32_TO_UINT64( + i_eepromRecordHeader.completeRecord.i2c_master_huid, + TWO_UINT16_TO_UINT32( + TWO_UINT8_TO_UINT16( + i_eepromRecordHeader.completeRecord.port, + i_eepromRecordHeader.completeRecord.engine), + TWO_UINT8_TO_UINT16( + i_eepromRecordHeader.completeRecord.devAddr, + i_eepromRecordHeader.completeRecord.mux_select))), + i_eepromRecordHeader.completeRecord.cache_copy_size, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + break; } - else + + // Ensure that information at the address we just looked up matches the record we built up + if( memcmp(&l_eepromRecordHeaderToUpdate->uniqueRecord.uniqueID, + &i_eepromRecordHeader.uniqueRecord.uniqueID, + NUM_BYTE_UNIQUE_ID ) != 0 ) + { + TRACFCOMP(g_trac_eeprom,ERR_MRK"setIsValidCacheEntry: Attempting to invalidate cache for an" + "eeprom but we could not find the entry in table of contents of EECACHE section of pnor"); + /*@ + * @errortype + * @moduleid EEPROM_INVALIDATE_CACHE + * @reasoncode EEPROM_CACHE_NOT_FOUND_IN_PNOR + * @userdata1[0:7] i2c_master_huid + * @userdata1[8:9] port on i2c master eeprom slave is on + * @userdata1[10:11] engine on i2c master eeprom slave is on + * @userdata1[12:13] devAddr of eeprom slave + * @userdata1[14:15] muxSelect of eeprom slave (0xFF is not valid) + * @userdata2[0:7] size of eeprom + * @devdesc invalidateCache failed to find cache in pnor + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_INVALIDATE_CACHE, + EEPROM_CACHE_NOT_FOUND_IN_PNOR, + TWO_UINT32_TO_UINT64( + i_eepromRecordHeader.completeRecord.i2c_master_huid, + TWO_UINT16_TO_UINT32( + TWO_UINT8_TO_UINT16( + i_eepromRecordHeader.completeRecord.port, + i_eepromRecordHeader.completeRecord.engine), + TWO_UINT8_TO_UINT16( + i_eepromRecordHeader.completeRecord.devAddr, + i_eepromRecordHeader.completeRecord.mux_select))), + i_eepromRecordHeader.completeRecord.cache_copy_size, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + break; + } + + // Update the header so that it state the entry is invalid + l_eepromRecordHeaderToUpdate->completeRecord.cached_copy_valid = i_isValid; + + // Flush the page to make sure it gets to the PNOR + int rc = mm_remove_pages( FLUSH, + l_eepromRecordHeaderToUpdate, + sizeof(eepromRecordHeader) ); + if( rc ) { - TRACFCOMP( g_trac_eeprom,"eepromPerformOpCache: Failed to find entry in cache for 0x%.08X, %s failed", - TARGETING::get_huid(i_target), - (i_opType == DeviceFW::READ) ? "READ" : "WRITE"); + TRACFCOMP(g_trac_eeprom, + ERR_MRK"setIsValidCacheEntry: Error from mm_remove_pages trying for flush header write to pnor, rc=%d",rc); /*@ * @errortype - * @moduleid EEPROM_CACHE_PERFORM_OP - * @reasoncode EEPROM_NOT_IN_CACHE - * @userdata1[0:31] Op Type - * @userdata1[32:63] Eeprom Role - * @userdata2 Offset we are attempting to read/write - * @custdesc Soft error in Firmware - * @devdesc Tried to lookup eeprom not in cache + * @moduleid EEPROM_INVALIDATE_CACHE + * @reasoncode EEPROM_FAILED_TO_FLUSH_HEADER + * @userdata1 Requested Address + * @userdata2 rc from mm_remove_pages + * @devdesc invalidateCache mm_remove_pages FLUSH failed */ l_errl = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, - EEPROM_CACHE_PERFORM_OP, - EEPROM_NOT_IN_CACHE, - TWO_UINT32_TO_UINT64(i_opType, - i_eepromInfo.eepromRole), - TO_UINT64(i_eepromInfo.offset), + EEPROM_INVALIDATE_CACHE, + EEPROM_FAILED_TO_FLUSH_HEADER, + (uint64_t)l_eepromRecordHeaderToUpdate, + TO_UINT64(rc), ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); - ERRORLOG::ErrlUserDetailsTarget(i_target).addToLog(l_errl); - l_errl->collectTrace( EEPROM_COMP_NAME ); + break; } - TRACSSCOMP( g_trac_eeprom, EXIT_MRK"eepromPerformOpCache() " - "Target HUID 0x%.08X Exit", TARGETING::get_huid(i_target)); - }while(0); return l_errl; } +bool addEepromToCachedList(const eepromRecordHeader & i_eepromRecordHeader, + const uint64_t i_recordHeaderVaddr) +{ + bool l_matchFound = true; + + // Map accesses are not thread safe, make sure this is always wrapped in mutex + mutex_lock(&g_eecacheMutex); + + if(g_cachedEeproms.find(i_eepromRecordHeader) == g_cachedEeproms.end()) + { + g_cachedEeproms[i_eepromRecordHeader].cache_entry_address = + g_eecachePnorVaddr + i_eepromRecordHeader.completeRecord.internal_offset; + + g_cachedEeproms[i_eepromRecordHeader].header_entry_address = + i_recordHeaderVaddr; + + TRACSSCOMP( g_trac_eeprom, + "addEepromToCachedList() Adding I2CM Huid: 0x%.08X, Port: 0x%.02X," + " Engine: 0x%.02X, Dev Addr: 0x%.02X, Mux Select: 0x%.02X," + " Size: 0x%.08X to g_cachedEeproms", + i_eepromRecordHeader.completeRecord.i2c_master_huid, + i_eepromRecordHeader.completeRecord.port, + i_eepromRecordHeader.completeRecord.engine, + i_eepromRecordHeader.completeRecord.devAddr, + i_eepromRecordHeader.completeRecord.mux_select, + i_eepromRecordHeader.completeRecord.cache_copy_size); + + l_matchFound = false; + } + + mutex_unlock(&g_eecacheMutex); + + return l_matchFound; +} + +void printTableOfContents(void) +{ + eecacheSectionHeader * l_eecacheSectionHeaderPtr = + reinterpret_cast<eecacheSectionHeader*>(g_eecachePnorVaddr); + + TRACFCOMP( g_trac_eeprom, + "printTableOfContents(): Version = 0x%.02X" + " End of Cache = 0x.08X", + l_eecacheSectionHeaderPtr->version, + l_eecacheSectionHeaderPtr->end_of_cache); + + for(uint8_t i = 0; i < MAX_EEPROMS_VERSION_1; i++) + { + eepromRecordHeader l_currentRecordHeader = + l_eecacheSectionHeaderPtr->recordHeaders[i]; + + if( l_currentRecordHeader.completeRecord.internal_offset != + UNSET_INTERNAL_OFFSET_VALUE) + { + TRACFCOMP( g_trac_eeprom, + "printTableOfContents(): I2CM Huid: 0x%.08X, Port: 0x%.02X," + " Engine: 0x%.02X, Dev Addr: 0x%.02X," + " Mux Select: 0x%.02X, Size: 0x%.08X", + l_currentRecordHeader.completeRecord.i2c_master_huid, + l_currentRecordHeader.completeRecord.port, + l_currentRecordHeader.completeRecord.engine, + l_currentRecordHeader.completeRecord.devAddr, + l_currentRecordHeader.completeRecord.mux_select, + l_currentRecordHeader.completeRecord.cache_copy_size); + + TRACFCOMP( g_trac_eeprom, + " " + "Internal Offset: 0x%.08X, Cache Valid: 0x%.02X", + l_currentRecordHeader.completeRecord.internal_offset, + l_currentRecordHeader.completeRecord.cached_copy_valid); + } + } + } + +bool hasEeepromChanged(const eepromRecordHeader & i_eepromRecordHeader) +{ + bool l_eepromHasChanged = false; + + // Map accesses are not thread safe, make sure this is always wrapped in mutex + mutex_lock(&g_eecacheMutex); + + if(g_cachedEeproms.find(i_eepromRecordHeader) != g_cachedEeproms.end()) + { + l_eepromHasChanged = g_cachedEeproms[i_eepromRecordHeader].mark_target_changed; + } + + mutex_unlock(&g_eecacheMutex); + + return l_eepromHasChanged; +} + +void setEeepromChanged(const eepromRecordHeader & i_eepromRecordHeader) +{ + + // Map accesses are not thread safe, make sure this is always wrapped in mutex + mutex_lock(&g_eecacheMutex); + + if(g_cachedEeproms.find(i_eepromRecordHeader) != g_cachedEeproms.end()) + { + g_cachedEeproms[i_eepromRecordHeader].mark_target_changed = true; + } + + mutex_unlock(&g_eecacheMutex); + +} + +uint64_t lookupEepromCacheAddr(const eepromRecordHeader& i_eepromRecordHeader) +{ + uint64_t l_vaddr = 0; + std::map<eepromRecordHeader, EeepromEntryMetaData_t>::iterator l_it; + + // Wrap lookup in mutex because reads are not thread safe + mutex_lock(&g_eecacheMutex); + l_it = g_cachedEeproms.find(i_eepromRecordHeader); + + if(l_it != g_cachedEeproms.end()) + { + l_vaddr = l_it->second.cache_entry_address; + } + + mutex_unlock(&g_eecacheMutex); + + return l_vaddr; +} + +uint64_t lookupEepromHeaderAddr(const eepromRecordHeader& i_eepromRecordHeader) +{ + uint64_t l_vaddr = 0; + std::map<eepromRecordHeader, EeepromEntryMetaData_t>::iterator l_it; + + // Wrap lookup in mutex because reads are not thread safe + mutex_lock(&g_eecacheMutex); + l_it = g_cachedEeproms.find(i_eepromRecordHeader); + + if(l_it != g_cachedEeproms.end()) + { + l_vaddr = l_it->second.header_entry_address; + } + mutex_unlock(&g_eecacheMutex); + + if(l_vaddr == 0) + { + TRACFCOMP( g_trac_eeprom, + "lookupEepromHeaderAddr() failed to find" + " I2CM Huid: 0x%.08X, Port: 0x%.02X," + " Engine: 0x%.02X, Dev Addr: 0x%.02X," + " Mux Select: 0x%.02X, Size: 0x%.08X" + "in g_cachedEeproms", + i_eepromRecordHeader.completeRecord.i2c_master_huid, + i_eepromRecordHeader.completeRecord.port, + i_eepromRecordHeader.completeRecord.engine, + i_eepromRecordHeader.completeRecord.devAddr, + i_eepromRecordHeader.completeRecord.mux_select, + i_eepromRecordHeader.completeRecord.cache_copy_size); + } + return l_vaddr; +} + +}
\ No newline at end of file diff --git a/src/usr/i2c/eepromCache.H b/src/usr/i2c/eepromCache.H index 5cad475ba..253367b07 100644 --- a/src/usr/i2c/eepromCache.H +++ b/src/usr/i2c/eepromCache.H @@ -97,10 +97,44 @@ errlHndl_t buildEepromRecordHeader(TARGETING::Target * i_target, eeprom_addr_t & io_eepromInfo, eepromRecordHeader & o_eepromRecordHeader); + +#ifndef __HOSTBOOT_RUNTIME + +/** +* +* @brief Check if entry already exists in g_cachedEeproms, if a match is +* found then return true. If there is no match, add it to the list +* and return false; +* +* @param[in] i_eepromRecordHeader Header for record we want to add to map +* +* @param[in] i_recordHeaderVaddr Virtual address to PNOR copy of header information +* +* @return TRUE if entry is already in map FALSE if this is a new entry +* +*/ +bool addEepromToCachedList(const eepromRecordHeader & i_eepromRecordHeader, + const uint64_t i_recordHeaderVaddr); + +/** +* +* @brief Perform a lookup on the global map g_cachedEeproms to get a +* virtual address for a given EEPROM entry in the EECACHE table of contents +* +* @param[in] i_eepromRecordHeader +* +* @pre It is expected that i_eepromRecordHeader has valid information for +* the uniqueID (i2cm_huid, port, engine, devAddr, mux_select) +* +* @return uint64_t virtual address pointing to the cached eeprom data in pnor +* +*/ +uint64_t lookupEepromHeaderAddr(const eepromRecordHeader& i_eepromRecordHeader); + /** * * @brief Perform a lookup on the global map g_cachedEeproms to get a -* virtual address for a given EEPROM +* virtual address for a given EEPROM cache entry * * @param[in] i_eepromRecordHeader * @@ -110,8 +144,127 @@ errlHndl_t buildEepromRecordHeader(TARGETING::Target * i_target, * @return uint64_t virtual address pointing to the cached eeprom data in pnor * */ -uint64_t lookupEepromAddr(const eepromRecordHeader & i_eepromRecordHeader); +uint64_t lookupEepromCacheAddr(const eepromRecordHeader& i_eepromRecordHeader); + +/** +* +* @brief Print the info found in the Table of Contents of the EECACHE +* section of pnor to trace buffer +* +* @return void +* +*/ +void printTableOfContents(void); + + +/** +* +* @brief Update the record entry in the Table of Contents of the EECACHE +* section of pnor to either mark the contents of the cache to be +* valid or invalid +* +* @param[in] i_target Target associated with EEPROM +* +* @param[in] i_eepromRole Role of EEPROM associated with target (VPD_PRIMARY etc) +* +* @param[in] i_isValid Mark eeprom cache valid or invalid ? +* +* @return errlHndl_t - nullptr if successful, otherwise a pointer to the +* error log. +* +*/ +errlHndl_t setIsValidCacheEntry(const TARGETING::Target * i_target, + const EEPROM_ROLE &i_eepromRole, + bool i_isValid); + +/** +* +* @brief Update the record entry in the Table of Contents of the EECACHE +* section of pnor to either mark the contents of the cache to be +* valid or invalid +* +* @param[in] i_eepromRecordHeader eepromRecord oject already filled in (including eepromRole) +* +* @param[in] i_isValid Mark eeprom cache valid or invalid ? +* +* @return errlHndl_t - nullptr if successful, otherwise a pointer to the +* error log. +* +*/ +errlHndl_t setIsValidCacheEntry(const eepromRecordHeader& i_eepromRecordHeader, bool i_isValid); + +/** +* +* @brief Lookup a given i_eepromRecordHeader in the global map of eeprom +* caches and check if the eeprom has changed this IPL or not +* +* @param[in] i_eepromRecordHeader we want to look up +* +* @return bool Return TRUE if eeprom is found in map AND mark_target_changed + was set to true for the eeprom entry. Return FALSE otherwise. +* +*/ +bool hasEeepromChanged(const eepromRecordHeader & i_eepromRecordHeader); + +/** +* +* @brief Lookup a given i_eepromRecordHeader in the global map of eeprom +* caches and mark that it has changed this IPL +* +* @param[in] i_eepromRecordHeader we want to mark as changed +* +* @return void +*/ +void setEeepromChanged(const eepromRecordHeader & i_eepromRecordHeader); +#else +/** +* +* @brief Check if entry already exists in g_cachedEeproms, if a match is +* found then return true. If there is no match, add it to the list +* and return false; +* +* @param[in] i_eepromRecordHeader Header for record we want to add to map +* +* @param[in] i_recordHeaderVaddr Virtual address to PNOR copy of header information +* +* @param[in] i_instance Node ID that this eeprom is on +* +* @return TRUE if entry is already in map FALSE if this is a new entry +* +*/ +bool addEepromToCachedList(const eepromRecordHeader & i_eepromRecordHeader, + const uint64_t i_recordHeaderVaddr, + const uint8_t i_instance); + +/** +* +* @brief Perform a lookup on the global map g_cachedEeproms to get a +* virtual address for a given EEPROM cache entry +* +* @param[in] i_eepromRecordHeader Header for record we want to add to lookup address for +* +* @param[in] i_isntance Node ID that this eeprom is on +* +* @pre It is expected that i_eepromRecordHeader has valid information for +* the uniqueID (i2cm_huid, port, engine, devAddr, mux_select) +* +* @return uint64_t virtual address pointing to the cached eeprom data in pnor +* +*/ +uint64_t lookupEepromCacheAddr(const eepromRecordHeader& i_eepromRecordHeader, + const uint8_t i_instance); + +/** +* +* @brief Walk through g_cachedEeproms map and print information about +* the cached eeproms found +* +* @return void +* +*/ +void printCurrentCachedEepromMap(void); +#endif // __HOSTBOOT_RUNTIME } #endif
\ No newline at end of file diff --git a/src/usr/i2c/eepromCache_common.C b/src/usr/i2c/eepromCache_common.C new file mode 100644 index 000000000..72c982056 --- /dev/null +++ b/src/usr/i2c/eepromCache_common.C @@ -0,0 +1,325 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/i2c/eepromCache_common.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] 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 "eepromCache.H" +#include <errl/errlmanager.H> +#include <i2c/eepromif.H> +#include <i2c/eepromddreasoncodes.H> +#include <errl/errludtarget.H> + +#ifdef __HOSTBOOT_RUNTIME +#include <targeting/attrrp.H> +#else +#include <sys/mm.h> +#endif + +extern trace_desc_t* g_trac_eeprom; + +//#define TRACSSCOMP(args...) TRACFCOMP(args) +#define TRACSSCOMP(args...) + +namespace EEPROM +{ + +errlHndl_t buildEepromRecordHeader(TARGETING::Target * i_target, + eeprom_addr_t & io_eepromInfo, + eepromRecordHeader & o_eepromRecordHeader) +{ + + TARGETING::Target * l_muxTarget = nullptr; + TARGETING::Target * l_i2cMasterTarget = nullptr; + TARGETING::TargetService& l_targetService = TARGETING::targetService(); + errlHndl_t l_errl = nullptr; + + do{ + + l_errl = eepromReadAttributes(i_target, io_eepromInfo); + if(l_errl) + { + TRACFCOMP( g_trac_eeprom, + "buildEepromRecordHeader() error occurred reading eeprom attributes for eepromType %d, target 0x%.08X, returning!!", + io_eepromInfo.eepromRole, + TARGETING::get_huid(i_target)); + l_errl->collectTrace(EEPROM_COMP_NAME); + break; + } + + // Grab the I2C mux target so we can read the HUID, if the target is NULL we will not be able + // to lookup attribute to uniquely ID this eeprom so we will not cache it + l_muxTarget = l_targetService.toTarget( io_eepromInfo.i2cMuxPath); + if(l_muxTarget == nullptr) + { + TRACFCOMP( g_trac_eeprom, + "buildEepromRecordHeader() Mux target associated with target 0x%.08X resolved to a nullptr , check attribute for eepromType %d. Skipping Cache", + TARGETING::get_huid(i_target), + io_eepromInfo.eepromRole); + /*@ + * @errortype + * @moduleid EEPROM_CACHE_EEPROM + * @reasoncode EEPROM_I2C_MUX_PATH_ERROR + * @userdata1 HUID of target we want to cache + * @userdata2 Type of EEPROM we are caching + * @devdesc buildEepromRecordHeader invalid mux target + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_CACHE_EEPROM, + EEPROM_I2C_MUX_PATH_ERROR, + TARGETING::get_huid(i_target), + io_eepromInfo.eepromRole, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + l_errl->collectTrace(EEPROM_COMP_NAME); + break; + } + + // Grab the I2C master target so we can read the HUID, if the target is NULL we will not be able + // to lookup attribute to uniquely ID this eeprom so we will not cache it + l_i2cMasterTarget = l_targetService.toTarget( io_eepromInfo.i2cMasterPath ); + if(l_i2cMasterTarget == nullptr) + { + TRACFCOMP( g_trac_eeprom, + "buildEepromRecordHeader() I2C Master target associated with target 0x%.08X resolved to a nullptr , check attribute for eepromType %d. Skipping Cache ", + TARGETING::get_huid(i_target), + io_eepromInfo.eepromRole); + /*@ + * @errortype + * @moduleid EEPROM_CACHE_EEPROM + * @reasoncode EEPROM_I2C_MASTER_PATH_ERROR + * @userdata1 HUID of target we want to cache + * @userdata2 Type of EEPROM we are caching + * @devdesc buildEepromRecordHeader invalid master target + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_CACHE_EEPROM, + EEPROM_I2C_MASTER_PATH_ERROR, + TARGETING::get_huid(i_target), + io_eepromInfo.eepromRole, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + l_errl->collectTrace(EEPROM_COMP_NAME); + break; + } + + // This is what we will compare w/ when we are going through the existing + // caches in the eeprom to see if we have already cached something + // Or if no matches are found we will copy this into the header + o_eepromRecordHeader.completeRecord.i2c_master_huid = l_i2cMasterTarget->getAttr<TARGETING::ATTR_HUID>(); + o_eepromRecordHeader.completeRecord.port = static_cast<uint8_t>(io_eepromInfo.port); + o_eepromRecordHeader.completeRecord.engine = static_cast<uint8_t>(io_eepromInfo.engine); + o_eepromRecordHeader.completeRecord.devAddr = static_cast<uint8_t>(io_eepromInfo.devAddr); + o_eepromRecordHeader.completeRecord.mux_select = static_cast<uint8_t>(io_eepromInfo.i2cMuxBusSelector); + o_eepromRecordHeader.completeRecord.cache_copy_size = static_cast<uint32_t>(io_eepromInfo.devSize_KB); + + // Do not set valid bit nor internal offset here as we do not have + // enough information availible to determine + + }while(0); + + return l_errl; +} + +errlHndl_t eepromPerformOpCache(DeviceFW::OperationType i_opType, + TARGETING::Target * i_target, + void * io_buffer, + size_t& io_buflen, + eeprom_addr_t &i_eepromInfo) +{ + errlHndl_t l_errl = nullptr; + eepromRecordHeader l_eepromRecordHeader; + + do{ + + TRACSSCOMP( g_trac_eeprom, ENTER_MRK"eepromPerformOpCache() " + "Target HUID 0x%.08X Enter", TARGETING::get_huid(i_target)); + + l_errl = buildEepromRecordHeader(i_target, + i_eepromInfo, + l_eepromRecordHeader); + + if(l_errl) + { + // buildEepromRecordHeader should have traced any relavent information if + // it was needed, just break out and pass the error along + break; + } + +#ifndef __HOSTBOOT_RUNTIME + uint64_t l_eepromCacheVaddr = lookupEepromCacheAddr(l_eepromRecordHeader); +#else + uint8_t l_instance = TARGETING::AttrRP::getNodeId(i_target); + uint64_t l_eepromCacheVaddr = lookupEepromCacheAddr(l_eepromRecordHeader, l_instance); +#endif + + // Ensure that a copy of the eeprom exists in our map of cached eeproms + if(l_eepromCacheVaddr) + { + // First check if io_buffer is a nullptr, if so then assume user is + // requesting size back in io_bufferlen + if(io_buffer == nullptr) + { + io_buflen = l_eepromRecordHeader.completeRecord.cache_copy_size * KILOBYTE; + TRACSSCOMP( g_trac_eeprom, "eepromPerformOpCache() " + "io_buffer == nullptr , returning io_buflen as 0x%lx", + io_buflen); + break; + } + + TRACSSCOMP( g_trac_eeprom, "eepromPerformOpCache() " + "Performing %s on target 0x%.08X offset 0x%lx length 0x%x vaddr 0x%lx", + (i_opType == DeviceFW::READ) ? "READ" : "WRITE", + TARGETING::get_huid(i_target), + i_eepromInfo.offset, io_buflen, l_eepromCacheVaddr); + + // Make sure that offset + buflen are less than the total size of the eeprom + if(i_eepromInfo.offset + io_buflen > + (l_eepromRecordHeader.completeRecord.cache_copy_size * KILOBYTE)) + { + TRACFCOMP(g_trac_eeprom, + ERR_MRK"eepromPerformOpCache: i_eepromInfo.offset + i_offset is greater than size of eeprom (0x%x KB)", + l_eepromRecordHeader.completeRecord.cache_copy_size); + /*@ + * @errortype + * @moduleid EEPROM_CACHE_PERFORM_OP + * @reasoncode EEPROM_OVERFLOW_ERROR + * @userdata1 Length of Operation + * @userdata2 Offset we are attempting to read/write + * @custdesc Soft error in Firmware + * @devdesc cacheEeprom invalid op type + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_CACHE_PERFORM_OP, + EEPROM_OVERFLOW_ERROR, + TO_UINT64(io_buflen), + TO_UINT64(i_eepromInfo.offset), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + ERRORLOG::ErrlUserDetailsTarget(i_target).addToLog(l_errl); + l_errl->collectTrace( EEPROM_COMP_NAME ); + + break; + } + + if(i_opType == DeviceFW::READ) + { + memcpy(io_buffer, + reinterpret_cast<void *>(l_eepromCacheVaddr + i_eepromInfo.offset), + io_buflen); + } + else if(i_opType == DeviceFW::WRITE) + { + memcpy(reinterpret_cast<void *>(l_eepromCacheVaddr + i_eepromInfo.offset), + io_buffer, + io_buflen); + +#ifndef __HOSTBOOT_RUNTIME + // Perform flush to ensure pnor is updated + int rc = mm_remove_pages( FLUSH, + reinterpret_cast<void *>(l_eepromCacheVaddr + i_eepromInfo.offset), + io_buflen ); + if( rc ) + { + TRACFCOMP(g_trac_eeprom, + ERR_MRK"eepromPerformOpCache: Error from mm_remove_pages trying for flush contents write to pnor! rc=%d", + rc); + /*@ + * @errortype + * @moduleid EEPROM_CACHE_PERFORM_OP + * @reasoncode EEPROM_FAILED_TO_FLUSH_CONTENTS + * @userdata1 Requested Address + * @userdata2 rc from mm_remove_pages + * @devdesc cacheEeprom mm_remove_pages FLUSH failed + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_CACHE_PERFORM_OP, + EEPROM_FAILED_TO_FLUSH_CONTENTS, + (l_eepromCacheVaddr + i_eepromInfo.offset), + TO_UINT64(rc), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + l_errl->collectTrace( EEPROM_COMP_NAME ); + } +#endif + } + else + { + TRACFCOMP(g_trac_eeprom, + ERR_MRK"eepromPerformOpCache: Invalid OP_TYPE passed to function, i_opType=%d", + i_opType); + /*@ + * @errortype + * @moduleid EEPROM_CACHE_PERFORM_OP + * @reasoncode EEPROM_INVALID_OPERATION + * @userdata1[0:31] Op Type that was invalid + * @userdata1[32:63] Eeprom Role + * @userdata2 Offset we are attempting to perfrom op on + * @custdesc Soft error in Firmware + * @devdesc cacheEeprom invalid op type + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_CACHE_PERFORM_OP, + EEPROM_INVALID_OPERATION, + TWO_UINT32_TO_UINT64(i_opType, + i_eepromInfo.eepromRole), + TO_UINT64(i_eepromInfo.offset), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + ERRORLOG::ErrlUserDetailsTarget(i_target).addToLog(l_errl); + l_errl->collectTrace( EEPROM_COMP_NAME ); + } + } + else + { + TRACFCOMP( g_trac_eeprom,"eepromPerformOpCache: Failed to find entry in cache for 0x%.08X, %s failed", + TARGETING::get_huid(i_target), + (i_opType == DeviceFW::READ) ? "READ" : "WRITE"); + /*@ + * @errortype + * @moduleid EEPROM_CACHE_PERFORM_OP + * @reasoncode EEPROM_NOT_IN_CACHE + * @userdata1[0:31] Op Type + * @userdata1[32:63] Eeprom Role + * @userdata2 Offset we are attempting to read/write + * @custdesc Soft error in Firmware + * @devdesc Tried to lookup eeprom not in cache + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_CACHE_PERFORM_OP, + EEPROM_NOT_IN_CACHE, + TWO_UINT32_TO_UINT64(i_opType, + i_eepromInfo.eepromRole), + TO_UINT64(i_eepromInfo.offset), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + ERRORLOG::ErrlUserDetailsTarget(i_target).addToLog(l_errl); + l_errl->collectTrace( EEPROM_COMP_NAME ); + } + + TRACSSCOMP( g_trac_eeprom, EXIT_MRK"eepromPerformOpCache() " + "Target HUID 0x%.08X Exit", TARGETING::get_huid(i_target)); + + }while(0); + + return l_errl; +} +}
\ No newline at end of file diff --git a/src/usr/i2c/eeprom_utils.C b/src/usr/i2c/eeprom_utils.C index 806a0cf31..8333ebf49 100644 --- a/src/usr/i2c/eeprom_utils.C +++ b/src/usr/i2c/eeprom_utils.C @@ -87,6 +87,18 @@ bool eepromPresence ( TARGETING::Target * i_target ) break; } + // If the target has dynamic device address attribute, then use that instead of the + // read-only address found in ATTR_EEPROM_XX_INFO attrs. We use the dynamic address + // attribute because ATTR_EEPROM_XX_INFO attrs are not writable and its difficult + // to override complex attributes. + if(i_target->tryGetAttr<TARGETING::ATTR_DYNAMIC_I2C_DEVICE_ADDRESS>(i2cInfo.devAddr)) + { + TRACDCOMP(g_trac_eeprom, + "Using DYNAMIC_I2C_DEVICE_ADDRESS %.2x for HUID %.8x", + i2cInfo.devAddr, + TARGETING::get_huid(i_target)); + } + //Check for the target at the I2C level l_present = I2C::i2cPresence(i2cMasterTarget, i2cInfo.port, @@ -373,7 +385,7 @@ errlHndl_t eepromReadAttributes ( TARGETING::Target * i_target, // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = o_i2cInfo.i2cMuxPath.toString(); - TRACFCOMP(g_trac_eeprom, "eepromReadAttributes(): " + TRACUCOMP(g_trac_eeprom, "eepromReadAttributes(): " "muxSelector=0x%X, muxPath=%s", o_i2cInfo.i2cMuxBusSelector, l_muxPath); diff --git a/src/usr/i2c/eepromdd.C b/src/usr/i2c/eepromdd.C index 74b6d3692..2243e9b38 100755 --- a/src/usr/i2c/eepromdd.C +++ b/src/usr/i2c/eepromdd.C @@ -41,6 +41,11 @@ // va_list #include "eepromCache.H" #include "eepromdd_hardware.H" +#include <i2c/eepromddreasoncodes.H> +#ifdef __HOSTBOOT_RUNTIME +// Need to be able to convert HB target id's to runtime target ids +#include <targeting/attrrp.H> +#endif extern trace_desc_t* g_trac_eeprom; @@ -87,19 +92,59 @@ errlHndl_t resolveSource(TARGETING::Target * i_target, err = buildEepromRecordHeader(i_target, io_i2cInfo, l_eepromRecordHeader); +#ifndef __HOSTBOOT_RUNTIME // if lookupEepromAddr returns non-zero address // then we know it exists in cache somewhere - if(lookupEepromAddr(l_eepromRecordHeader)) + if(lookupEepromCacheAddr(l_eepromRecordHeader)) { - TRACFCOMP(g_trac_eeprom,"Eeprom found in cache, looking at eecache"); + TRACDCOMP(g_trac_eeprom,"Eeprom found in cache, looking at eecache"); o_source = EEPROM::CACHE; } else { - TRACFCOMP(g_trac_eeprom,"Eeprom not found in cache, looking at hardware"); + TRACDCOMP(g_trac_eeprom,"Eeprom not found in cache, looking at hardware"); o_source = EEPROM::HARDWARE; } - +#else + uint8_t l_instance = TARGETING::AttrRP::getNodeId(i_target); + // if lookupEepromAddr returns non-zero address + // then we know it exists in cache somewhere + if(lookupEepromCacheAddr(l_eepromRecordHeader, l_instance)) + { + TRACDCOMP(g_trac_eeprom,"Eeprom found in cache, looking at eecache"); + o_source = EEPROM::CACHE; + } + else + { + /*@ + * @errortype + * @moduleid EEPROM_RESOLVE_SOURCE + * @reasoncode EEPROM_CACHE_NOT_FOUND_IN_MAP + * @userdata1[0:7] i2c_master_huid + * @userdata1[8:9] port on i2c master eeprom slave is on + * @userdata1[10:11] engine on i2c master eeprom slave is on + * @userdata1[12:13] devAddr of eeprom slave + * @userdata1[14:15] muxSelect of eeprom slave (0xFF is not valid) + * @userdata2[0:7] size of eeprom + * @devdesc resolveSource failed to find cache in map during runtime + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM_RESOLVE_SOURCE, + EEPROM_CACHE_NOT_FOUND_IN_MAP, + TWO_UINT32_TO_UINT64( + l_eepromRecordHeader.completeRecord.i2c_master_huid, + TWO_UINT16_TO_UINT32( + TWO_UINT8_TO_UINT16( + l_eepromRecordHeader.completeRecord.port, + l_eepromRecordHeader.completeRecord.engine), + TWO_UINT8_TO_UINT16( + l_eepromRecordHeader.completeRecord.devAddr, + l_eepromRecordHeader.completeRecord.mux_select))), + l_eepromRecordHeader.completeRecord.cache_copy_size, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + } +#endif return err; } @@ -178,29 +223,35 @@ errlHndl_t eepromPerformOp( DeviceFW::OperationType i_opType, if(l_source == EEPROM::CACHE ) { // Read the copy of the EEPROM data we have cached in PNOR - err = eepromPerformOpCache(i_opType, i_target, io_buffer, io_buflen, i2cInfo); + err = eepromPerformOpCache(i_opType, i_target, + io_buffer, io_buflen, i2cInfo); if(err) { break; } - - // If the operation is a write we also need to "write through" to HW after - // we write cache + // TODO RTC:212469 Complete Work needed for Axone i2c runtime support + #ifndef __HOSTBOOT_RUNTIME if(i_opType == DeviceFW::WRITE) { - err = eepromPerformOpHW(i_opType, i_target, io_buffer, io_buflen, i2cInfo); + // If the operation is a write we also need to + // "write through" to HW after we write cache + err = eepromPerformOpHW(i_opType, i_target, + io_buffer, io_buflen, i2cInfo); } + #endif } else if(l_source == EEPROM::HARDWARE) { // Read from the actual physical EEPROM device - err = eepromPerformOpHW(i_opType, i_target, io_buffer, io_buflen, i2cInfo); + err = eepromPerformOpHW(i_opType, i_target, io_buffer, + io_buflen, i2cInfo); } #else // Read from the actual physical EEPROM device - err = eepromPerformOpHW(i_opType, i_target, io_buffer, io_buflen, i2cInfo); + err = eepromPerformOpHW(i_opType, i_target, io_buffer, + io_buflen, i2cInfo); #endif // CONFIG_SUPPORT_EEPROM_CACHING diff --git a/src/usr/i2c/eepromdd_hardware.C b/src/usr/i2c/eepromdd_hardware.C index 351ca549a..b48c3889e 100644 --- a/src/usr/i2c/eepromdd_hardware.C +++ b/src/usr/i2c/eepromdd_hardware.C @@ -109,7 +109,7 @@ errlHndl_t eepromPerformOpHW(DeviceFW::OperationType i_opType, ( io_i2cInfo.devSize_KB * KILOBYTE ) ) { TRACFCOMP( g_trac_eeprom, - ERR_MRK"eepromPerformOp(): Device Overflow! " + ERR_MRK"eepromPerformOpHW(): Device Overflow! " "C-e/p/dA=%d-%d/%d/0x%X, offset=0x%X, len=0x%X " "devSizeKB=0x%X", io_i2cInfo.eepromRole, io_i2cInfo.engine, io_i2cInfo.port, io_i2cInfo.devAddr, io_i2cInfo.offset, @@ -155,19 +155,19 @@ errlHndl_t eepromPerformOpHW(DeviceFW::OperationType i_opType, l_currentOpLen = l_snglChipSize - io_i2cInfo.offset; } - TRACFCOMP( g_trac_eeprom, - "eepromPerformOp(): i_opType=%d " + TRACDCOMP( g_trac_eeprom, + "eepromPerformOpHW(): i_opType=%d " "C-e/p/dA=%d-%d/%d/0x%X, offset=0x%X, len=0x%X, ", i_opType, io_i2cInfo.eepromRole, io_i2cInfo.engine, - io_i2cInfo.port, io_i2cInfo.devAddr, io_i2cInfo.offset, io_buflen) + io_i2cInfo.port, io_i2cInfo.devAddr, io_i2cInfo.offset, io_buflen); - TRACFCOMP (g_trac_eeprom, - "eepromPerformOp(): snglChipKB=0x%X, chipCount=0x%X, devSizeKB=0x%X", + TRACDCOMP (g_trac_eeprom, + "eepromPerformOpHW(): snglChipKB=0x%X, chipCount=0x%X, devSizeKB=0x%X", l_snglChipSize, io_i2cInfo.chipCount, io_i2cInfo.devSize_KB); // Printing mux info separately, if combined, nothing is displayed char* l_muxPath = io_i2cInfo.i2cMuxPath.toString(); - TRACFCOMP(g_trac_eeprom, "eepromPerformOp(): " + TRACDCOMP(g_trac_eeprom, "eepromPerformOpHW(): " "muxSelector=0x%X, muxPath=%s", io_i2cInfo.i2cMuxBusSelector, l_muxPath); @@ -207,7 +207,7 @@ errlHndl_t eepromPerformOpHW(DeviceFW::OperationType i_opType, else { TRACFCOMP( g_trac_eeprom, - ERR_MRK"eepromPerformOp(): " + ERR_MRK"eepromPerformOpHW(): " "Invalid EEPROM Operation!"); /*@ diff --git a/src/usr/i2c/fapi_i2c_dd.C b/src/usr/i2c/fapi_i2c_dd.C index bf1771865..35a838998 100644 --- a/src/usr/i2c/fapi_i2c_dd.C +++ b/src/usr/i2c/fapi_i2c_dd.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018 */ +/* Contributors Listed Below - COPYRIGHT 2018,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -41,6 +41,8 @@ #include <devicefw/driverif.H> #include <i2c/i2creasoncodes.H> #include "fapi_i2c_dd.H" +#include <time.h> +#include <stdio.h> extern trace_desc_t* g_trac_i2c; @@ -69,6 +71,11 @@ DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD, TARGETING::TYPE_OCMB_CHIP, fapiI2cPerformOp ); +DEVICE_REGISTER_ROUTE( DeviceFW::WILDCARD, + DeviceFW::FAPI_I2C, + TARGETING::TYPE_PMIC, + fapiI2cPerformOp ); + errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, TARGETING::Target * i_target, void * io_buffer, @@ -78,6 +85,7 @@ errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, { errlHndl_t l_err = nullptr; errlHndl_t l_err_retryable = nullptr; + bool l_non_retryable_err_hit = false; const uint8_t FAPI_I2C_MAX_RETRIES = 2; TARGETING::ATTR_FAPI_I2C_CONTROL_INFO_type l_i2cInfo; @@ -89,6 +97,10 @@ errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, l_cfgData = va_arg( i_args, uint8_t* ); } + timespec_t l_startTime; + timespec_t l_endTime; + clock_gettime(CLOCK_MONOTONIC, &l_startTime); + TRACUCOMP(g_trac_i2c, ENTER_MRK"fapiI2cPerformOp(): " "%s operation on target %.8X", (i_opType==DeviceFW::READ)?"READ":"WRITE", @@ -103,6 +115,18 @@ errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, break; } + // If the target has dynamic device address attribute, then use that instead of the + // read-only address found in ATTR_FAPI_I2C_CONTROL_INFO. We use the dynamic address + // attribute because ATTR_FAPI_I2C_CONTROL_INFO is not writable and its difficult + // to override complex attributes. + if(i_target->tryGetAttr<TARGETING::ATTR_DYNAMIC_I2C_DEVICE_ADDRESS>(l_i2cInfo.devAddr)) + { + TRACDCOMP(g_trac_i2c, + "Using DYNAMIC_I2C_DEVICE_ADDRESS %.2x for HUID %.8x", + l_i2cInfo.devAddr, + TARGETING::get_huid(i_target)); + } + // grab target pointer to master TARGETING::TargetService& ts = TARGETING::targetService(); TARGETING::Target * i2cm = ts.toTarget(l_i2cInfo.i2cMasterPath); @@ -136,8 +160,6 @@ errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, TARGETING::get_huid(i_target), 0, true /*Add HB SW Callout*/ ); - - l_err->collectTrace( I2C_COMP_NAME ); break; } @@ -164,8 +186,6 @@ errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, i_opType, TARGETING::get_huid(i_target), true /*Add HB SW Callout*/ ); - - l_err->collectTrace( I2C_COMP_NAME ); break; } @@ -204,8 +224,7 @@ errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, "Error: rc=0x%X, tgt=0x%X, No Retry (retry=%d)", l_err->reasonCode(), TARGETING::get_huid(i_target), retry); - l_err->collectTrace(I2C_COMP_NAME); - + l_non_retryable_err_hit = true; // break from retry loop break; } @@ -267,35 +286,54 @@ errlHndl_t fapiI2cPerformOp(DeviceFW::OperationType i_opType, } // end of retryable error loop + clock_gettime(CLOCK_MONOTONIC, &l_endTime); + char l_time_str[128]; + snprintf(l_time_str, sizeof(l_time_str), + "Start Time: %lu sec %lu ns End Time: %lu sec %lu ns", + l_startTime.tv_sec, l_startTime.tv_nsec, + l_endTime.tv_sec, l_endTime.tv_nsec); + // Handle saved retryable error, if any if (l_err_retryable) { - if (l_err) + // This is the case where we had 1 or more retryable errors and + // eventually hit a non retryable error. + if (l_err && l_non_retryable_err_hit) { // commit original retryable error with new err PLID l_err_retryable->plid(l_err->plid()); TRACFCOMP(g_trac_i2c, "fapiI2cPerformOp(): Committing saved " "retryable error eid=0x%X with plid of returned err 0x%X", l_err_retryable->eid(), l_err_retryable->plid()); - + ERRORLOG::ErrlUserDetailsString(l_time_str) + .addToLog(l_err_retryable); ERRORLOG::ErrlUserDetailsTarget(i_target) .addToLog(l_err_retryable); l_err_retryable->collectTrace(I2C_COMP_NAME); + l_err_retryable->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); errlCommit(l_err_retryable, I2C_COMP_ID); } else { - // Since we eventually succeeded, - // delete original retryable error - TRACUCOMP(g_trac_i2c, "fapiI2cPerformOp(): Op successful, " - "deleting saved retryable err eid=0x%X, plid=0x%X", + // In this case we have either hit the max retryable errors and + // failed, or hit one or more retryable errors and eventually + // passed. Either way we do not need this l_err_retryable anymore. + TRACUCOMP(g_trac_i2c, "fapiI2cPerformOp(): Op successful, or we hit max Retries and" + " the caller is polling on this i2c op. Deleting saved retryable err eid=0x%X," + " plid=0x%X.", l_err_retryable->eid(), l_err_retryable->plid()); - delete l_err_retryable; l_err_retryable = nullptr; } } + if(l_err) + { + ERRORLOG::ErrlUserDetailsString(l_time_str) + .addToLog(l_err); + l_err->collectTrace(I2C_COMP_NAME); + } + } while (0); return l_err; @@ -376,10 +414,10 @@ errlHndl_t i2cWrite( TARGETING::Target * i_target, i_buffer, io_buffer_size, DEVICE_I2C_ADDRESS(i_i2cInfo->port, - i_i2cInfo->engine, - i_i2cInfo->devAddr, - i_i2cInfo->i2cMuxBusSelector, - &(i_i2cInfo->i2cMuxPath) ) ); + i_i2cInfo->engine, + i_i2cInfo->devAddr, + i_i2cInfo->i2cMuxBusSelector, + &(i_i2cInfo->i2cMuxPath) ) ); if( l_err ) { diff --git a/src/usr/i2c/i2c.C b/src/usr/i2c/i2c.C index 63b579202..2439f1eff 100755 --- a/src/usr/i2c/i2c.C +++ b/src/usr/i2c/i2c.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2019 */ +/* Contributors Listed Below - COPYRIGHT 2011,2020 */ /* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ @@ -94,6 +94,7 @@ TRAC_INIT( & g_trac_i2cr, "I2CR", KILOBYTE ); #define MAX_NACK_RETRIES 3 #define PAGE_OPERATION 0xffffffff // Special value use to determine type of op #define P9_ENGINE_SCOM_OFFSET 0x1000 +constexpr uint64_t FSI_BUS_SPEED_MHZ = 133; //FSI runs at 133MHz // Derived from ATTR_I2C_BUS_SPEED_ARRAY[engine][port] attribute const TARGETING::ATTR_I2C_BUS_SPEED_ARRAY_type g_var = {{NULL}}; @@ -447,14 +448,16 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType, // Else this is not a page operation, call the normal common function else { - if( (subop==DeviceFW::I2C_SMBUS_BLOCK) - || (subop==DeviceFW::I2C_SMBUS_BYTE) - || (subop==DeviceFW::I2C_SMBUS_WORD)) + if( (subop == DeviceFW::I2C_SMBUS_BLOCK) + || (subop == DeviceFW::I2C_SMBUS_BYTE) + || (subop == DeviceFW::I2C_SMBUS_WORD) + || (subop == DeviceFW::I2C_SMBUS_WORD_NO_PEC) + || (subop == DeviceFW::I2C_SMBUS_BLOCK_NO_BYTE_COUNT) ) { args.smbus.commandCode = static_cast<decltype(args.smbus.commandCode)>( va_arg(i_args,uint64_t)); - args.smbus.usePec = true; // All implementations use PEC + args.smbus.usePec = true; // Most implementations use PEC args.i2cMuxBusSelector = va_arg(i_args,uint64_t); args.i2cMuxPath = reinterpret_cast<const TARGETING::EntityPath*>( va_arg(i_args, uint64_t)); @@ -477,11 +480,23 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType, if ( args.offset_length != 0 ) { args.offset_buffer = temp; - } } - args.subop=subop; + if (subop == DeviceFW::I2C_SMBUS_WORD_NO_PEC) + { + args.smbus.usePec = false; + args.subop = DeviceFW::I2C_SMBUS_WORD; + } + else if (subop == DeviceFW::I2C_SMBUS_BLOCK_NO_BYTE_COUNT) + { + args.smbus.usePec = false; + args.subop=subop; + } + else + { + args.subop=subop; + } err = i2cCommonOp( i_opType, i_target, @@ -1284,11 +1299,22 @@ errlHndl_t i2cAccessMux( TARGETING::Target* i_masterTarget, if (! (l_i2cMuxTarget->tryGetAttr<TARGETING::ATTR_FAPI_I2C_CONTROL_INFO>(l_muxData)) ) { TRACFCOMP(g_trac_i2c, - "i2cAccessMux(): get attributes failed"); + "i2cAccessMux(): getting ATTR_FAPI_I2C_CONTROL_INFO failed"); break; } - uint8_t l_muxSelector = i_i2cMuxBusSelector; + TARGETING::ATTR_MODEL_type l_muxModel; + if (! (l_i2cMuxTarget->tryGetAttr<TARGETING::ATTR_MODEL>(l_muxModel)) ) + { + TRACFCOMP(g_trac_i2c, + "i2cAccessMux(): getting ATTR_MODEL failed"); + break; + } + + assert(l_muxModel == TARGETING::MODEL_PCA9847, "Invalid model of mux detected"); + const uint8_t PCA9847_ENABLE_BIT = 8; + + uint8_t l_muxSelector = i_i2cMuxBusSelector | PCA9847_ENABLE_BIT; uint8_t *l_ptrMuxSelector = &l_muxSelector; size_t l_muxSelectorSize = sizeof(l_muxSelector); @@ -1430,7 +1456,8 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType, /*******************************************************/ /* Perform the I2C Operation */ /*******************************************************/ - + TRACUCOMP( g_trac_i2c, INFO_MRK "i2cCommonOp() -- opType: %d, subOp: %d", + static_cast<uint64_t>(i_opType), i_args.subop); /***********************************************/ /* I2C SMBUS Send Byte */ /***********************************************/ @@ -1598,14 +1625,16 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType, /***********************************************/ /* I2C SMBUS Block Write */ /***********************************************/ - else if( (i_opType == DeviceFW::WRITE ) - && (i_args.subop == DeviceFW::I2C_SMBUS_BLOCK)) + else if( (i_opType == DeviceFW::WRITE ) && + ((i_args.subop == DeviceFW::I2C_SMBUS_BLOCK) || + (i_args.subop == DeviceFW::I2C_SMBUS_BLOCK_NO_BYTE_COUNT)) ) { TRACUCOMP(g_trac_i2c, INFO_MRK - "I2C SMBUS Block Write: Command code = 0x%02X, " - "Use PEC = %d. io_buflen = %lu", - i_args.smbus.commandCode, - i_args.smbus.usePec, io_buflen); + "I2C SMBUS Block Write: Command code = 0x%02X, SubCmd = 0x%02X " + "Use PEC = %d. io_buflen = %lu, io_buffer byte0: 0x%02X", + i_args.smbus.commandCode, i_args.subop, + i_args.smbus.usePec, io_buflen, + *reinterpret_cast<uint8_t*>(io_buffer)); // If requested length is for < 1 byte or > 255 bytes for a block // write transaction, throw an error. @@ -1649,12 +1678,29 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType, io_buflen, io_buffer, i_args.smbus.usePec); + void * writeStart = &blockWrite.commandCode; + + // byteCount might be altered into commandCode, so store its value + size_t dataByteCount = blockWrite.byteCount; + if (i_args.subop == DeviceFW::I2C_SMBUS_BLOCK_NO_BYTE_COUNT) + { + // Moving commandCode down a byte so it is followed + // immediately by dataBytes[] instead of byteCount + // (this removes byteCount from the block write) + writeStart = &blockWrite.byteCount; + blockWrite.byteCount = blockWrite.commandCode; + blockWrite.messageSize -= sizeof(blockWrite.commandCode); + TRACUCOMP(g_trac_i2c, INFO_MRK + "I2C SMBUS Block Write no-byte-count: removing byteCount," + " msgSize = %d", blockWrite.messageSize); + } + do { size_t writeSize = blockWrite.messageSize; const auto writeSizeExp = writeSize; err = i2cWrite(i_target, - &blockWrite.commandCode, + writeStart, writeSize, i_args); if(err) @@ -1666,7 +1712,8 @@ errlHndl_t i2cCommonOp( DeviceFW::OperationType i_opType, "Write size mismatch; expected %d but got %d", writeSizeExp,writeSize); - io_buflen = blockWrite.byteCount; + + io_buflen = dataByteCount; } while(0); @@ -4308,13 +4355,23 @@ errlHndl_t i2cSetBusVariables ( TARGETING::Target * i_target, if ( io_args.switches.useFsiI2C == 1 ) { - // @todo RTC 117560 - verify correct frequency - local_bus_MHZ = g_I2C_NEST_FREQ_MHZ; + // For FSI I2C we should use the FSI clock + local_bus_MHZ = FSI_BUS_SPEED_MHZ; } else { - // For Host I2C use Nest Frequency - local_bus_MHZ = g_I2C_NEST_FREQ_MHZ; + // For Host I2C use Nest Frequency as base + + // PIB_CLK = NEST_FREQ / 4 + uint64_t pib_clk = g_I2C_NEST_FREQ_MHZ / 4; + +#ifdef CONFIG_AXONE + // Axone has a by-2 internal divider + local_bus_MHZ = pib_clk / 2; +#else + // Nimbus/Cumulus have a by-4 internal divider + local_bus_MHZ = pib_clk / 4; +#endif } io_args.bit_rate_divisor = i2cGetBitRateDivisor(io_args.bus_speed, @@ -5148,8 +5205,13 @@ void getMasterInfo( const TARGETING::Target* i_chip, info.engine = engine; info.freq = i2cGetNestFreq()*FREQ_CONVERSION::HZ_PER_MHZ; // PIB_CLK = NEST_FREQ /4 - // Local Bus = PIB_CLK / 4 +#ifdef CONFIG_AXONE + // Local Bus = PIB_CLK / 2 [Axone] + info.freq = info.freq/8; //convert nest to local bus +#else + // Local Bus = PIB_CLK / 4 [Nimbus/Cumulus] info.freq = info.freq/16; //convert nest to local bus +#endif TRACFCOMP(g_trac_i2c,"getMasterInfo(%.8X): pushing back engine=%d, scomAddr=0x%X",TARGETING::get_huid(i_chip), engine, info.scomAddr); @@ -5447,6 +5509,7 @@ void getDeviceInfo( TARGETING::Target* i_i2cMaster, // Lookup i2c info for the TPM l_err = TPMDD::tpmReadAttributes(pTpm, tpmInfo, locality); + if( NULL != l_err ) { // Unable to get info, so we skip @@ -5474,11 +5537,34 @@ void getDeviceInfo( TARGETING::Target* i_i2cMaster, l_currentDI.slavePort = 0xFF; l_currentDI.busFreqKhz = (tpmInfo.busFreq) / FREQ_CONVERSION::HZ_PER_KHZ; - l_currentDI.deviceType = - TARGETING::HDAT_I2C_DEVICE_TYPE_NUVOTON_TPM; l_currentDI.devicePurpose = TARGETING::HDAT_I2C_DEVICE_PURPOSE_TPM; - strcpy(l_currentDI.deviceLabel,"?nuvoton,npct601,tpm,host"); + + // Read TPM Model attribute to determine some values + if (tpmInfo.model == TPMDD::TPM_MODEL_65x) + { + strcpy(l_currentDI.deviceLabel,"?nuvoton,npct601,tpm,host"); + l_currentDI.deviceType = + TARGETING::HDAT_I2C_DEVICE_TYPE_NUVOTON_TPM; + } + else if (tpmInfo.model == TPMDD::TPM_MODEL_75x) + { + strcpy(l_currentDI.deviceLabel,"?tcg,tpm_i2c_ptp,tpm,host"); + l_currentDI.deviceType = + TARGETING::HDAT_I2C_DEVICE_TYPE_TCG_I2C_TPM; + } + else + { + // Should never get here as tpmReadAttributes will fail if + // unknown TPM Model, but just in case do this: + strcpy(l_currentDI.deviceLabel, + "?unknwon,unknown,tpm,host"); + } + + TRACUCOMP(g_trac_i2c,"TPM 0x%X is Model %d using label %s", + TARGETING::get_huid(pTpm), + tpmInfo.model, + l_currentDI.deviceLabel); o_deviceInfo.push_back(l_currentDI); } //end of tpm iter diff --git a/src/usr/i2c/i2c.H b/src/usr/i2c/i2c.H index eca944479..6f230ac58 100755 --- a/src/usr/i2c/i2c.H +++ b/src/usr/i2c/i2c.H @@ -112,10 +112,10 @@ static uint64_t g_I2C_NEST_FREQ_MHZ = i2cGetNestFreq(); ALWAYS_INLINE inline uint16_t i2cGetBitRateDivisor(uint64_t i_bus_speed_khz, uint64_t i_local_bus_MHZ) { - // BRD = ( ( ( LocalBus_MHZ / 16 ) / i_bus_speed_khz ) - 1 ) / 4 + // BRD = ( ( LocalBus_MHZ) / i_bus_speed_khz ) - 1 ) / 4 // Use tmp variable to convert everything to KHZ safely - uint64_t tmp = ( i_local_bus_MHZ / 16 ) * 1000; + uint64_t tmp = i_local_bus_MHZ * 1000; return ( ( ( tmp / i_bus_speed_khz ) - 1 ) / 4 ); } diff --git a/src/usr/i2c/i2c.mk b/src/usr/i2c/i2c.mk index 6d4ef8935..0300d2c46 100644 --- a/src/usr/i2c/i2c.mk +++ b/src/usr/i2c/i2c.mk @@ -5,7 +5,7 @@ # # OpenPOWER HostBoot Project # -# Contributors Listed Below - COPYRIGHT 2015 +# Contributors Listed Below - COPYRIGHT 2015,2019 # [+] International Business Machines Corp. # # @@ -27,4 +27,4 @@ OBJS += eepromdd.o OBJS += eepromdd_hardware.o OBJS += eeprom_utils.o OBJS += errlud_i2c.o -OBJS += $(if $(CONFIG_SUPPORT_EEPROM_CACHING),eepromCache.o) +OBJS += $(if $(CONFIG_SUPPORT_EEPROM_CACHING),eepromCache_common.o) diff --git a/src/usr/i2c/i2cTargetPres.C b/src/usr/i2c/i2cTargetPres.C index c3809f099..c7c9421a3 100644 --- a/src/usr/i2c/i2cTargetPres.C +++ b/src/usr/i2c/i2cTargetPres.C @@ -32,7 +32,7 @@ #include <initservice/initserviceif.H> #include <errl/errlmanager.H> #include "i2c_common.H" - +#include <fapiwrap/fapiWrapif.H> extern trace_desc_t* g_trac_i2c; @@ -182,6 +182,18 @@ errlHndl_t genericI2CTargetPresenceDetect(TARGETING::Target* i_target, //* If we make it through all of the checks then we have verified master is present * //*********************************************************************************** + // If the target has dynamic device address attribute, then use that instead of the + // read-only address found in ATTR_FAPI_I2C_CONTROL_INFO. We use the dynamic address + // attribute because ATTR_FAPI_I2C_CONTROL_INFO is not writable and its difficult + // to override complex attributes. + if(i_target->tryGetAttr<TARGETING::ATTR_DYNAMIC_I2C_DEVICE_ADDRESS>(l_i2cInfo.devAddr)) + { + TRACDCOMP(g_trac_i2c, + "Using DYNAMIC_I2C_DEVICE_ADDRESS %.2x for HUID %.8x", + l_i2cInfo.devAddr, + TARGETING::get_huid(i_target)); + } + //Check for the target at the I2C level l_target_present = I2C::i2cPresence(l_i2cMasterTarget, l_i2cInfo.port, @@ -223,10 +235,6 @@ errlHndl_t ocmbI2CPresencePerformOp(DeviceFW::OperationType i_opType, { errlHndl_t l_invalidateErrl = nullptr; - // @TODO RTC 208696: Gemini vs Explorer Presence Detection via SPD - // This function will be updated to differentiate between Explorer and - // Gemini OCMB chips. For now, presense of an OCMB chip is inferred by - // the presense of the eeprom. bool l_ocmbPresent = EEPROM::eepromPresence(i_target); memcpy(io_buffer, &l_ocmbPresent, sizeof(l_ocmbPresent)); @@ -236,10 +244,97 @@ errlHndl_t ocmbI2CPresencePerformOp(DeviceFW::OperationType i_opType, } /** - * @brief Performs a presence detect operation on a Target that has the + * @brief Performs a presence detect operation on a PMIC Target + * + * @param[in] i_opType Operation type, see DeviceFW::OperationType + * in driverif.H + * @param[in] i_target Presence detect target + * @param[in/out] io_buffer Read: Pointer to output data storage + * Write: Pointer to input data storage + * @param[in/out] io_buflen Input: size of io_buffer (in bytes, always 1) + * Output: Success = 1, Failure = 0 + * @param[in] i_accessType DeviceFW::AccessType enum (userif.H) + * @param[in] i_args This is an argument list for DD framework. + * In this function, there are no arguments. + * @return errlHndl_t + */ +errlHndl_t pmicI2CPresencePerformOp(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 l_errl = nullptr; + bool l_pmicPresent = 0; + uint8_t l_devAddr; + TARGETING::Target* l_parentOcmb = TARGETING::getImmediateParentByAffinity(i_target); + auto l_parentHwasState = l_parentOcmb->getAttr<TARGETING::ATTR_HWAS_STATE>(); + + do{ + + if(! l_parentHwasState.present) + { + // If the parent chip is not present, then neither is the pmic + // so just break out and return not present + break; + } + + TARGETING::ATTR_REL_POS_type l_relPos = i_target->getAttr<TARGETING::ATTR_REL_POS>(); + + // PMICs will have a different device address depending on the vendor. + // Prior to doing present detection on a pmic we must first query the + // device address from the parent OCMB's SPD + l_errl = FAPIWRAP::get_pmic_dev_addr(l_parentOcmb, + l_relPos, + l_devAddr); + if (l_errl) + { + TRACFCOMP( g_trac_i2c, ERR_MRK"pmicI2CPresencePerformOp() " + "Error attempting to read pmic device address on OCMB 0x%.08X", + TARGETING::get_huid(l_parentOcmb)); + break; + } + + assert(l_devAddr != 0, + "Found devAddr for PMIC 0x%.08x to be 0, this cannot be. Check SPD and REL_POS on target", + TARGETING::get_huid(i_target)); + + if(l_devAddr == FAPIWRAP::NO_PMIC_DEV_ADDR) + { + // There is no pmic device address for this rel position on this ocmb so + // break and return not present. + break; + } + + i_target->setAttr<TARGETING::ATTR_DYNAMIC_I2C_DEVICE_ADDRESS>(l_devAddr); + + l_errl = genericI2CTargetPresenceDetect(i_target, + io_buflen, + l_pmicPresent); + + if (l_errl) + { + TRACFCOMP( g_trac_i2c, ERR_MRK"pmicI2CPresencePerformOp() " + "Error detecting target 0x%.08X, io_buffer will not be set", + TARGETING::get_huid(i_target)); + break; + } + + // Copy variable describing if target is present or not to i/o buffer param + memcpy(io_buffer, &l_pmicPresent, sizeof(l_pmicPresent)); + io_buflen = sizeof(l_pmicPresent); + + }while(0); + + return l_errl; +} + +/** + * @brief Performs a presence detect operation on a Mux Target that has the * ATTR_FAPI_I2C_CONTROL_INFO and can be detected via that device * - * Currently used to detect I2C_MUTEX targets * * @param[in] i_opType Operation type, see DeviceFW::OperationType * in driverif.H @@ -253,7 +348,7 @@ errlHndl_t ocmbI2CPresencePerformOp(DeviceFW::OperationType i_opType, * In this function, there are no arguments. * @return errlHndl_t */ -errlHndl_t basicI2CPresencePerformOp(DeviceFW::OperationType i_opType, +errlHndl_t muxI2CPresencePerformOp(DeviceFW::OperationType i_opType, TARGETING::Target* i_target, void* io_buffer, size_t& io_buflen, @@ -261,13 +356,13 @@ errlHndl_t basicI2CPresencePerformOp(DeviceFW::OperationType i_opType, va_list i_args) { bool l_muxPresent = 0; - errlHndl_t l_returnedError = nullptr; + errlHndl_t l_errl = nullptr; - l_returnedError = genericI2CTargetPresenceDetect(i_target, - io_buflen, - l_muxPresent); + l_errl = genericI2CTargetPresenceDetect(i_target, + io_buflen, + l_muxPresent); - if (l_returnedError) + if (l_errl) { TRACFCOMP( g_trac_i2c, ERR_MRK"basicI2CTargetPresenceDetect() " "Error detecting target 0x%.08X, io_buffer will not be set", @@ -280,7 +375,7 @@ errlHndl_t basicI2CPresencePerformOp(DeviceFW::OperationType i_opType, io_buflen = sizeof(l_muxPresent); } - return l_returnedError; + return l_errl; } // Register the ocmb presence detect function with the device framework @@ -293,12 +388,12 @@ DEVICE_REGISTER_ROUTE(DeviceFW::READ, DEVICE_REGISTER_ROUTE( DeviceFW::READ, DeviceFW::PRESENT, TARGETING::TYPE_I2C_MUX, - basicI2CPresencePerformOp ); + muxI2CPresencePerformOp ); // Register the pmic vrm presence detect function with the device framework DEVICE_REGISTER_ROUTE( DeviceFW::READ, DeviceFW::PRESENT, TARGETING::TYPE_PMIC, - basicI2CPresencePerformOp ); + pmicI2CPresencePerformOp ); } diff --git a/src/usr/i2c/makefile b/src/usr/i2c/makefile index 535484752..10e55c9de 100644 --- a/src/usr/i2c/makefile +++ b/src/usr/i2c/makefile @@ -33,6 +33,7 @@ OBJS += i2c.o OBJS += $(if $(CONFIG_TPMDD),tpmdd.o,) OBJS += fapi_i2c_dd.o OBJS += i2cTargetPres.o +OBJS += $(if $(CONFIG_SUPPORT_EEPROM_CACHING),eepromCache.o) SUBDIRS += test.d SUBDIRS += runtime.d diff --git a/src/usr/i2c/runtime/makefile b/src/usr/i2c/runtime/makefile index e6bf9330b..73d3d464b 100644 --- a/src/usr/i2c/runtime/makefile +++ b/src/usr/i2c/runtime/makefile @@ -1,11 +1,11 @@ # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # -# $Source: src/usr/vpd/runtime/makefile $ +# $Source: src/usr/i2c/runtime/makefile $ # # OpenPOWER HostBoot Project # -# Contributors Listed Below - COPYRIGHT 2013,2015 +# Contributors Listed Below - COPYRIGHT 2013,2019 # [+] International Business Machines Corp. # # @@ -32,7 +32,7 @@ include ../i2c.mk #add unique object modules OBJS += rt_i2c.o - +OBJS += $(if $(CONFIG_SUPPORT_EEPROM_CACHING),rt_eepromCache.o) VPATH += .. include $(ROOTPATH)/config.mk diff --git a/src/usr/i2c/runtime/rt_eepromCache.C b/src/usr/i2c/runtime/rt_eepromCache.C new file mode 100644 index 000000000..2b775ddb5 --- /dev/null +++ b/src/usr/i2c/runtime/rt_eepromCache.C @@ -0,0 +1,285 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/i2c/runtime/rt_eepromCache.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2019 */ +/* [+] 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 rt_eepromCache.C + * + * @brief Runtime functionality of the eeprom cache driver + * + */ + +// ---------------------------------------------- +// Includes +// ---------------------------------------------- + +#include <errl/errlentry.H> +#include <devicefw/driverif.H> +#include <i2c/eepromddreasoncodes.H> +#include <runtime/interface.h> +#include <targeting/runtime/rt_targeting.H> +#include <targeting/common/utilFilter.H> +#include <targeting/attrrp.H> +#include <trace/interface.H> +#include <util/runtime/util_rt.H> +#include <sys/internode.h> +#include <errl/errlentry.H> +#include <errl/errlmanager.H> + +#include "../eepromCache.H" + +// ---------------------------------------------- +// Trace definitions +// ---------------------------------------------- +extern trace_desc_t* g_trac_eeprom; + +//#define TRACSSCOMP(args...) TRACFCOMP(args) +#define TRACSSCOMP(args...) + +namespace EEPROM +{ + +// Any time we access either any of the global variables defined above we want +// to wrap the call in this mutex to avoid multi-threading issues +mutex_t g_eecacheMutex = MUTEX_INITIALIZER; + +uint64_t g_eecachePnorVaddr[MAX_NODES_PER_SYS] = {0,0,0,0,0,0,0,0}; +std::map<eepromRecordHeader, EeepromEntryMetaData_t> g_cachedEeproms[MAX_NODES_PER_SYS]; + +// ------------------------------------------------------------------ +// rtEecacheInit +// ------------------------------------------------------------------ +struct rtEecacheInit +{ + rtEecacheInit() + { + errlHndl_t l_errl = nullptr; + + // Add cache status for the node + TARGETING::TargetHandleList l_nodeList; + getEncResources(l_nodeList, + TARGETING::TYPE_NODE, + TARGETING::UTIL_FILTER_ALL); + // Find all the targets with VPD switches + for (auto & l_node : l_nodeList) + { + uint8_t l_instance = TARGETING::AttrRP::getNodeId(l_node); + uint64_t vpd_size = 0; + eecacheSectionHeader* l_sectionHeader = + reinterpret_cast<eecacheSectionHeader*>( + hb_get_rt_rsvd_mem(Util::HBRT_MEM_LABEL_VPD, + l_instance, vpd_size)); + + g_eecachePnorVaddr[l_instance] = reinterpret_cast<uint64_t>(l_sectionHeader); + + // Check if reserved memory does not exist for this instance + if ((NULL == l_sectionHeader) || (0 == vpd_size)) + { + TRACFCOMP(g_trac_eeprom, + "rtEecacheInit(): ERROR Could not find VPD section of reserved memory for Node %d, ", + l_instance); + /*@ + * @errortype ERRL_SEV_UNRECOVERABLE + * @moduleid EEPROM_CACHE_INIT_RT + * @reasoncode EEPROM_CACHE_NO_VPD_IN_RSV_MEM + * @userdata1 Node Id + * @userdata2 Unused + * + * @devdesc Attempted to lookup VPD in reserved memory + * and failed + * @custdesc A problem occurred during the IPL of the + * system. + */ + l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM::EEPROM_CACHE_INIT_RT, + EEPROM::EEPROM_CACHE_NO_VPD_IN_RSV_MEM, + l_instance, + 0, + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + + errlCommit (l_errl, EEPROM_COMP_ID); + break; + + } + + eepromRecordHeader * l_recordHeaderToCopy = nullptr; + uint8_t l_eepromCount = 0; + for(int8_t i = 0; i < MAX_EEPROMS_VERSION_1; i++) + { + // Keep track of current record so we can use outside for loop + l_recordHeaderToCopy = &l_sectionHeader->recordHeaders[i]; + + // If internal_offset is UNSET_INTERNAL_OFFSET_VALUE then we will assume this address not been filled + if(l_recordHeaderToCopy->completeRecord.internal_offset != UNSET_INTERNAL_OFFSET_VALUE) + { + l_eepromCount++; + // Will return true if already found an entry in the list + if(addEepromToCachedList(*l_recordHeaderToCopy, + reinterpret_cast<uint64_t>(l_recordHeaderToCopy), + l_instance)) + { + + TRACFCOMP(g_trac_eeprom, + "rtEecacheInit(): ERROR Duplicate cache entries found in VPD reserved memory section"); + /*@ + * @errortype ERRL_SEV_UNRECOVERABLE + * @moduleid EEPROM_CACHE_INIT_RT + * @reasoncode EEPROM_DUPLICATE_CACHE_ENTRY + * @userdata1[0:31] i2c_master_huid + * @userdata1[32:39] port on i2c master eeprom slave is on + * @userdata1[40:47] engine on i2c master eeprom slave is on + * @userdata1[48:55] devAddr of eeprom slave + * @userdata1[56:63] muxSelect of eeprom slave (0xFF is not valid) + * @userdata2[0:31] size of eeprom + * @userdata2[32:63] Node Id + * @devdesc Attempted to lookup VPD in reserved memory + * and failed + * @custdesc A problem occurred during the IPL of the + * system. + */ + l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + EEPROM::EEPROM_CACHE_INIT_RT, + EEPROM::EEPROM_DUPLICATE_CACHE_ENTRY, + TWO_UINT32_TO_UINT64( + l_recordHeaderToCopy->completeRecord.i2c_master_huid, + TWO_UINT16_TO_UINT32( + TWO_UINT8_TO_UINT16( + l_recordHeaderToCopy->completeRecord.port, + l_recordHeaderToCopy->completeRecord.engine), + TWO_UINT8_TO_UINT16( + l_recordHeaderToCopy->completeRecord.devAddr, + l_recordHeaderToCopy->completeRecord.mux_select))), + TWO_UINT32_TO_UINT64(l_recordHeaderToCopy->completeRecord.cache_copy_size, + TO_UINT32(l_instance)), + ERRORLOG::ErrlEntry::ADD_SW_CALLOUT); + + errlCommit (l_errl, EEPROM_COMP_ID); + // Something is wrong so we have committed the unrecoverable + // but keep processing the eeproms because we want a full picture + // of what we understand the eeprom cache contents to be + continue; + } + } + } + + TRACFCOMP(g_trac_eeprom, "Found %d cached eeproms in reserved memory for node %d", + l_eepromCount, l_instance); + + printCurrentCachedEepromMap(); + + } + } + +}; +rtEecacheInit g_rtEecacheInit; + +void printCurrentCachedEepromMap(void) +{ + TRACFCOMP( g_trac_eeprom, + "printCurrentCachedEepromMap():"); + + mutex_lock(&g_eecacheMutex); + for(int8_t i = 0; i < MAX_NODES_PER_SYS; i++) + { + for(std::map<eepromRecordHeader, EeepromEntryMetaData_t>::iterator iter = g_cachedEeproms[i].begin(); + iter != g_cachedEeproms[i].end(); + ++iter) + { + TRACSSCOMP( g_trac_eeprom, + "printTableOfContents(): I2CM Huid: 0x%.08X, Port: 0x%.02X," + " Engine: 0x%.02X, Dev Addr: 0x%.02X," + " Mux Select: 0x%.02X, Size: 0x%.08X", + iter->first.completeRecord.i2c_master_huid, + iter->first.completeRecord.port, + iter->first.completeRecord.engine, + iter->first.completeRecord.devAddr, + iter->first.completeRecord.mux_select, + iter->first.completeRecord.cache_copy_size); + + TRACSSCOMP( g_trac_eeprom, + " " + "Internal Offset: 0x%.08X, Cache Valid: 0x%.02X", + iter->first.completeRecord.internal_offset, + iter->first.completeRecord.cached_copy_valid); + } + } + mutex_unlock(&g_eecacheMutex); + +} + +uint64_t lookupEepromCacheAddr(const eepromRecordHeader& i_eepromRecordHeader, + const uint8_t i_instance) +{ + uint64_t l_vaddr = 0; + std::map<eepromRecordHeader, EeepromEntryMetaData_t>::iterator l_it; + + // Wrap lookup in mutex because reads are not thread safe + mutex_lock(&g_eecacheMutex); + l_it = g_cachedEeproms[i_instance].find(i_eepromRecordHeader); + + if(l_it != g_cachedEeproms[i_instance].end()) + { + l_vaddr = l_it->second.cache_entry_address; + } + mutex_unlock(&g_eecacheMutex); + return l_vaddr; +} + +bool addEepromToCachedList(const eepromRecordHeader & i_eepromRecordHeader, + const uint64_t i_recordHeaderVaddr, + const uint8_t i_instance) +{ + bool l_matchFound = true; + + // Map accesses are not thread safe, make sure this is always wrapped in mutex + mutex_lock(&g_eecacheMutex); + + if(g_cachedEeproms[i_instance].find(i_eepromRecordHeader) == + g_cachedEeproms[i_instance].end()) + { + g_cachedEeproms[i_instance][i_eepromRecordHeader].cache_entry_address = + g_eecachePnorVaddr[i_instance] + i_eepromRecordHeader.completeRecord.internal_offset; + + g_cachedEeproms[i_instance][i_eepromRecordHeader].header_entry_address = + i_recordHeaderVaddr; + + TRACSSCOMP( g_trac_eeprom, + "addEepromToCachedList() Adding I2CM Huid: 0x%.08X, Port: 0x%.02X," + " Engine: 0x%.02X, Dev Addr: 0x%.02X, Mux Select: 0x%.02X," + " Size: 0x%.08X to g_cachedEeproms", + i_eepromRecordHeader.completeRecord.i2c_master_huid, + i_eepromRecordHeader.completeRecord.port, + i_eepromRecordHeader.completeRecord.engine, + i_eepromRecordHeader.completeRecord.devAddr, + i_eepromRecordHeader.completeRecord.mux_select, + i_eepromRecordHeader.completeRecord.cache_copy_size); + + l_matchFound = false; + } + + mutex_unlock(&g_eecacheMutex); + + return l_matchFound; +} + +} // end namespace EEPROM diff --git a/src/usr/i2c/runtime/rt_i2c.C b/src/usr/i2c/runtime/rt_i2c.C index 16f62c76e..d3f5537c6 100755 --- a/src/usr/i2c/runtime/rt_i2c.C +++ b/src/usr/i2c/runtime/rt_i2c.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2019 */ +/* Contributors Listed Below - COPYRIGHT 2015,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -41,7 +41,7 @@ #include <devicefw/driverif.H> #include <i2c/i2creasoncodes.H> #include <runtime/interface.h> -#include <runtime/rt_targeting.H> +#include <targeting/runtime/rt_targeting.H> #include "../errlud_i2c.H" // ---------------------------------------------- @@ -146,10 +146,10 @@ errlHndl_t i2cPerformOp( DeviceFW::OperationType i_opType, int rc = 0; bool l_host_if_enabled = true; - RT_TARG::rtChipId_t proc_id = 0; + TARGETING::rtChipId_t proc_id = 0; // Convert target to proc id - err = RT_TARG::getRtTarget( i_target, + err = TARGETING::getRtTarget( i_target, proc_id); if(err) { diff --git a/src/usr/i2c/test/eecachetest.H b/src/usr/i2c/test/eecachetest.H new file mode 100644 index 000000000..cc5233bf6 --- /dev/null +++ b/src/usr/i2c/test/eecachetest.H @@ -0,0 +1,121 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/i2c/test/eecachetest.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2019 */ +/* [+] 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 __EECACHETEST_H +#define __EECACHETEST_H + +/** + * @file eepromtest.H + * + * @brief Test cases for the eeprom cache code + */ + +#include <cxxtest/TestSuite.H> +#include "../eepromCache.H" + +extern trace_desc_t* g_trac_eeprom; + +using namespace TARGETING; +using namespace EEPROM; + +class EECACHETest: public CxxTest::TestSuite +{ + public: + + /** + * @brief Verify we can mark a cache as invalid then mark it valid again + */ + void test_invalidateCache( void ) + { + uint8_t l_numTests = 0; + uint8_t l_numFails = 0; + + TRACFCOMP( g_trac_eeprom, ENTER_MRK"test_invalidateCache" ); + + do{ + // Uncomment to verify manually + //printTableOfContents(); + + // Get a processor Target + TARGETING::TargetService& tS = TARGETING::targetService(); + TARGETING::Target* testTarget = NULL; + tS.masterProcChipTargetHandle( testTarget ); + assert(testTarget != NULL); + + // Create dummy eeprom info w/ VPD_PRIMARY set + const EEPROM_ROLE l_eepromRole = EEPROM::VPD_PRIMARY; + + eeprom_addr_t l_primaryVpdEeprom; + l_primaryVpdEeprom.eepromRole = l_eepromRole; + + eepromRecordHeader l_eepromRecordHeader_forLookup; + eepromRecordHeader * l_eepromRecordHeader_realPnor; + + buildEepromRecordHeader( testTarget, + l_primaryVpdEeprom, + l_eepromRecordHeader_forLookup); + + l_eepromRecordHeader_realPnor = reinterpret_cast<eepromRecordHeader *>(lookupEepromHeaderAddr(l_eepromRecordHeader_forLookup)); + + l_numTests++; + if(l_eepromRecordHeader_realPnor->completeRecord.cached_copy_valid != 1) + { + TS_FAIL("test_invalidateCache Master Proc VPD EECACHE is expected to be valid at start of test!"); + l_numFails++; + break; + } + + // Invalidate the cache entry + setIsValidCacheEntry(testTarget, l_eepromRole, 0); + + l_numTests++; + if(l_eepromRecordHeader_realPnor->completeRecord.cached_copy_valid != 0) + { + TS_FAIL("test_invalidateCache Master Proc VPD EECACHE is expected to be invalid after setIsValidCacheEntry(invalid) is called!"); + l_numFails++; + break; + } + + // Re-validate the cache entry + setIsValidCacheEntry(testTarget, l_eepromRole, 1); + + l_numTests++; + if(l_eepromRecordHeader_realPnor->completeRecord.cached_copy_valid != 1) + { + TS_FAIL("test_invalidateCache Master Proc VPD EECACHE is expected to be invalid after setIsValidCacheEntry(valid) is called!"); + l_numFails++; + break; + } + + // Uncomment to verify manually + // printTableOfContents(); + + }while(0); + + TRACFCOMP( g_trac_eeprom, EXIT_MRK"test_getEEPROMs numTests = %d / num fails = %d", l_numTests, l_numFails ); + } + +}; + +#endif
\ No newline at end of file diff --git a/src/usr/i2c/test/makefile b/src/usr/i2c/test/makefile index ef774e6e0..fa9cf31c0 100644 --- a/src/usr/i2c/test/makefile +++ b/src/usr/i2c/test/makefile @@ -5,7 +5,7 @@ # # OpenPOWER HostBoot Project # -# Contributors Listed Below - COPYRIGHT 2011,2015 +# Contributors Listed Below - COPYRIGHT 2011,2019 # [+] International Business Machines Corp. # # @@ -26,6 +26,7 @@ ROOTPATH = ../../../.. MODULE = testi2c TESTS = eepromddtest.H +TESTS += $(if $(CONFIG_SUPPORT_EEPROM_CACHING), eecachetest.H, ) TESTS += i2ctest.H TESTS += $(if $(CONFIG_TPMDD),tpmddtest.H,) diff --git a/src/usr/i2c/test/tpmddtest.H b/src/usr/i2c/test/tpmddtest.H index 8cd1fbc2c..0ea987949 100755 --- a/src/usr/i2c/test/tpmddtest.H +++ b/src/usr/i2c/test/tpmddtest.H @@ -111,21 +111,33 @@ class TPMDDTest: public CxxTest::TestSuite uint32_t data = 0x0; size_t dataSize = sizeof(data); + // default to most common ID + uint32_t expected_vendorID = TPMDD::TPM_VENDORID_65x; + uint8_t tpmModel = TPM_MODEL_UNDETERMINED; + TRACFCOMP( g_trac_tpmdd, "testTPMReadVendorID - Start" ); do { -#ifdef CONFIG_AXONE_BRING_UP - TRACFCOMP( g_trac_tpmdd,"Skipping test on Axone" ); - break; -#endif - // Get a TPM Target TARGETING::Target* testTarget = getTestTarget(); if (NULL == testTarget) { - continue; + break; + } + + if ( !(testTarget->tryGetAttr<ATTR_TPM_MODEL>(tpmModel)) ) + { + TS_FAIL("Unable to read ATTR_TPM_MODEL for %.8X target", + get_huid(testTarget)); + break; + } + + // This should match Axone and later systems + if (TPM_MODEL_75x == tpmModel) + { + expected_vendorID = TPMDD::TPM_VENDORID_75x; } num_ops++; @@ -144,16 +156,16 @@ class TPMDDTest: public CxxTest::TestSuite TPMDD_COMP_ID ); delete err; err = NULL; - continue; + break; } else if ((data & TPMDD::TPM_VENDORID_MASK) - // Only 65x supported in simics for now: - != TPMDD::TPM_VENDORID_65x) + != expected_vendorID) { fails++; TS_FAIL( "testTPMReadVendorID - Failed to read " - "correct vendor id ID=0x%X", data); - continue; + "correct vendor id ID=0x%X, expected 0x%X", data, + expected_vendorID); + break; } else @@ -161,7 +173,7 @@ class TPMDDTest: public CxxTest::TestSuite TRACUCOMP(g_trac_tpmdd, "testTPMReadVendorID - " "VendorID returned as expected. ID=0x%X", data); - continue; + break; } } while( 0 ); TRACFCOMP( g_trac_tpmdd, diff --git a/src/usr/i2c/tpmdd.C b/src/usr/i2c/tpmdd.C index a6f96514b..93aac12b9 100755 --- a/src/usr/i2c/tpmdd.C +++ b/src/usr/i2c/tpmdd.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2011,2019 */ +/* Contributors Listed Below - COPYRIGHT 2011,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -51,7 +51,6 @@ #include <i2c/i2cif.H> #include <secureboot/service.H> #include <secureboot/trustedbootif.H> -#include <scom/centaurScomCache.H> // for TRACE_ERR_FMT, TRACE_ERR_ARGS #include "tpmdd.H" #include "errlud_i2c.H" |