From f058c4b71a182364fd664f5a4d93cd1fe3e07d19 Mon Sep 17 00:00:00 2001 From: Dan Crowell Date: Tue, 17 Jun 2014 10:27:07 -0500 Subject: Split out SFC logic and add support for AST2400 Refactored the PNOR device driver to pull all SFC-specific code into a new set of classes. Any time a new type of serial flash controller (SFC) is introduced, a new subclass should be created to support it. Also added the full support for the AST2400 BMC that is being used on Palmetto. Change-Id: I9cdbf9b48bbf94615a39804920e170a3142ec386 Origin: Google Shared Technology RTC: 97493 Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/13229 Tested-by: Jenkins Server Reviewed-by: A. Patrick Williams III --- src/usr/errl/plugins/errludlogregister.H | 13 +- src/usr/lpc/lpcdd.C | 83 +- src/usr/lpc/lpcdd.H | 3 +- src/usr/pnor/HBconfig | 22 +- src/usr/pnor/makefile | 11 + src/usr/pnor/nor_micron.C | 198 +++ src/usr/pnor/norflash.H | 164 ++ src/usr/pnor/pnordd.C | 2409 +++++------------------------- src/usr/pnor/pnordd.H | 634 +------- src/usr/pnor/pnorrp.C | 5 +- src/usr/pnor/pnorrp.H | 3 + src/usr/pnor/pnorvalid.C | 2 +- src/usr/pnor/sfc_ast2400.C | 856 +++++++++++ src/usr/pnor/sfc_ast2400.H | 295 ++++ src/usr/pnor/sfc_fake.C | 295 ++++ src/usr/pnor/sfc_fake.H | 151 ++ src/usr/pnor/sfc_ibm.C | 1290 ++++++++++++++++ src/usr/pnor/sfc_ibm.H | 502 +++++++ src/usr/pnor/sfcdd.C | 120 ++ src/usr/pnor/sfcdd.H | 231 +++ src/usr/pnor/test/makefile | 12 +- src/usr/pnor/test/pnorddtest.H | 662 +------- src/usr/pnor/test/pnorrptest.H | 13 +- src/usr/pnor/test/sfc_ast2400test.H | 305 ++++ src/usr/pnor/test/sfc_ibmtest.H | 174 +++ 25 files changed, 5091 insertions(+), 3362 deletions(-) create mode 100644 src/usr/pnor/nor_micron.C create mode 100644 src/usr/pnor/norflash.H create mode 100644 src/usr/pnor/sfc_ast2400.C create mode 100644 src/usr/pnor/sfc_ast2400.H create mode 100644 src/usr/pnor/sfc_fake.C create mode 100644 src/usr/pnor/sfc_fake.H create mode 100644 src/usr/pnor/sfc_ibm.C create mode 100644 src/usr/pnor/sfc_ibm.H create mode 100644 src/usr/pnor/sfcdd.C create mode 100644 src/usr/pnor/sfcdd.H create mode 100644 src/usr/pnor/test/sfc_ast2400test.H create mode 100644 src/usr/pnor/test/sfc_ibmtest.H (limited to 'src/usr') diff --git a/src/usr/errl/plugins/errludlogregister.H b/src/usr/errl/plugins/errludlogregister.H index 87450fad6..f795a9643 100644 --- a/src/usr/errl/plugins/errludlogregister.H +++ b/src/usr/errl/plugins/errludlogregister.H @@ -5,7 +5,10 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2012,2014 */ +/* Contributors Listed Below - COPYRIGHT 2013,2014 */ +/* [+] Google Inc. */ +/* [+] 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. */ @@ -142,6 +145,14 @@ public: addrParams.push_back(" I2C slave device address"); addrParams.push_back(" EEPROM chip number"); break; + case DeviceFW::LPC: // driverif.H + i_parser.PrintString("AccessType", "DeviceFW::LPC"); + i_parser.PrintString("Ranges", + "0=IO,1=MEM,2=FW,3=REG,4=ABS"); + numArgs = 2; + addrParams.push_back(" Range"); + addrParams.push_back(" Addr"); + break; // three parameters case DeviceFW::I2C: // driverif.H i_parser.PrintString("AccessType", "DeviceFW::I2C"); diff --git a/src/usr/lpc/lpcdd.C b/src/usr/lpc/lpcdd.C index 9124f0803..db217bda4 100644 --- a/src/usr/lpc/lpcdd.C +++ b/src/usr/lpc/lpcdd.C @@ -92,9 +92,10 @@ errlHndl_t lpcRead(DeviceFW::OperationType i_opType, uint64_t l_addr = va_arg(i_args,uint64_t); errlHndl_t l_err = NULL; - assert( io_buflen == sizeof(uint8_t) || - io_buflen == sizeof(uint16_t) || - io_buflen == sizeof(uint32_t) ); + // Only able to do 1,2,4 byte LPC operations + assert( (io_buflen == sizeof(uint8_t)) || + (io_buflen == sizeof(uint16_t)) || + (io_buflen == sizeof(uint32_t)) ); // if the request is for something besides the master sentinel // then we have to use our special side copy of the driver @@ -180,9 +181,10 @@ errlHndl_t lpcWrite(DeviceFW::OperationType i_opType, uint64_t l_addr = va_arg(i_args,uint64_t); errlHndl_t l_err = NULL; - assert( io_buflen == sizeof(uint8_t) || - io_buflen == sizeof(uint16_t) || - io_buflen == sizeof(uint32_t) ); + // Only able to do 1,2,4 byte LPC operations + assert( (io_buflen == sizeof(uint8_t)) || + (io_buflen == sizeof(uint16_t)) || + (io_buflen == sizeof(uint32_t)) ); // if the request is for something besides the master sentinel // then we have to use our special side copy of the driver @@ -236,7 +238,6 @@ errlHndl_t lpcWrite(DeviceFW::OperationType i_opType, } return l_err; - } // Register LPC access functions to DD framework @@ -442,6 +443,22 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) break; } + case RESET_INIT: + { + // Set OPB LPCM FIR Mask + // hostboot will monitor these FIR bits + size_t scom_size = sizeof(uint64_t); + uint64_t fir_mask_data = OPB_LPCM_FIR_ERROR_MASK; + // Write FIR Register + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &(fir_mask_data), + scom_size, + DEVICE_SCOM_ADDRESS( + OPB_LPCM_FIR_MASK_WO_OR_REG)); + if( l_err ) { break; } + } + case RESET_ECCB: { // Write Reset Register to reset FW Logic registers @@ -558,7 +575,6 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) 0, true /*SW error*/); - l_err->collectTrace(PNOR_COMP_NAME); l_err->collectTrace(LPC_COMP_NAME); break; @@ -567,7 +583,7 @@ errlHndl_t LpcDD::hwReset( ResetLevels i_resetLevel ) if ( l_err ) { - // Indicate that we weren't successful in resetting LPC + // Indicate that we weren't successful in resetting iv_errorRecoveryFailed = true; TRACFCOMP( g_trac_lpc,ERR_MRK"LpcDD::hwReset> Fail doing LPC reset at level 0x%X (recovery count=%d): eid=0x%X", i_resetLevel, iv_errorHandledCount, l_err->eid()); } @@ -621,19 +637,9 @@ errlHndl_t LpcDD::checkAddr(LPC::TransType i_type, *o_addr = i_addr + LPCHC_MEM_SPACE; break; case LPC::TRANS_FW: - if( i_addr < LPCHC_FW_SPACE ) - { - invalid_address = true; - break; - } - *o_addr = i_addr; + *o_addr = i_addr + LPCHC_FW_SPACE; break; case LPC::TRANS_REG: - if( i_addr >= 0x100 ) - { - invalid_address = true; - break; - } *o_addr = i_addr + LPCHC_REG_SPACE; break; case LPC::TRANS_ABS: @@ -646,6 +652,7 @@ errlHndl_t LpcDD::checkAddr(LPC::TransType i_type, if( invalid_address ) { + TRACFCOMP( g_trac_lpc, "LpcDD::checkAddr() Invalid address : i_type=%d, i_addr=%X", i_type, i_addr ); /*@ * @errortype * @moduleid LPC::MOD_LPCDD_CHECKADDR @@ -777,9 +784,6 @@ errlHndl_t LpcDD::_writeLPC(LPC::TransType i_type, break; } - LPC_TRACFCOMP(g_trac_lpc, "writeLPC> %08X[%d] = %08X", l_addr, io_buflen, - eccb_data >> (32 + 8 * (4 - io_buflen))); - // Write data out size_t scom_size = sizeof(uint64_t); l_err = deviceOp( DeviceFW::WRITE, @@ -913,7 +917,6 @@ errlHndl_t LpcDD::pollComplete(const ControlReg_t &i_ctrl, addFFDC(l_err); l_err->collectTrace(LPC_COMP_NAME); - l_err->collectTrace(PNOR_COMP_NAME); l_err->collectTrace(XSCOM_COMP_NAME); // Reset ECCB - handled below @@ -932,18 +935,26 @@ errlHndl_t LpcDD::pollComplete(const ControlReg_t &i_ctrl, if ( l_err && ( l_resetLevel != RESET_CLEAR ) ) { errlHndl_t tmp_err = hwReset(l_resetLevel); - if ( tmp_err ) { // Commit reset error since we have original error l_err TRACFCOMP(g_trac_lpc, "LpcDD::pollComplete> Error from reset() after previous error eid=0x%X. Committing reset() error log eid=0x%X.", l_err->eid(), tmp_err->eid()); tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); - tmp_err->collectTrace(PNOR_COMP_NAME); tmp_err->collectTrace(LPC_COMP_NAME); tmp_err->plid(l_err->plid()); errlCommit(tmp_err, LPC_COMP_ID); } + + // Limited in callout: no LPC sub-target, so calling out processor + l_err->addHwCallout( iv_proc, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + addFFDC(l_err); + l_err->collectTrace(LPC_COMP_NAME); + l_err->collectTrace(XSCOM_COMP_NAME); } return l_err; @@ -964,19 +975,15 @@ void LpcDD::addFFDC(errlHndl_t & io_errl) ERRORLOG::ErrlUserDetailsLogRegister l_eud(iv_proc); - do { - // Add ECCB Status Register - l_eud.addData(DEVICE_SCOM_ADDRESS(ECCB_STAT_REG)); + // Add ECCB Status Register + l_eud.addData(DEVICE_SCOM_ADDRESS(ECCB_STAT_REG)); - // Add OPB LPC Master FIR - l_eud.addData(DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG)); - - //@todo - add more LPC regs RTC:37744 - //LPCIRQ_STATUS = 0x38 - //SYS_ERR_ADDR = 0x40 - - } while(0); + // Add OPB LPC Master FIR + l_eud.addData(DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG)); + //@todo - add more LPC regs RTC:37744 + //LPCIRQ_STATUS = 0x38 + //SYS_ERR_ADDR = 0x40 l_eud.addToLog(io_errl); @@ -1117,7 +1124,6 @@ errlHndl_t LpcDD::checkForOpbErrors( ResetLevels &o_resetLevel ) fir_reg.data64, o_resetLevel ); - // Limited in callout: no PNOR target, so calling out processor l_err->addHwCallout( iv_proc, HWAS::SRCI_PRIORITY_HIGH, HWAS::NO_DECONFIG, @@ -1133,7 +1139,6 @@ errlHndl_t LpcDD::checkForOpbErrors( ResetLevels &o_resetLevel ) l_eud.addToLog(l_err); addFFDC(l_err); - l_err->collectTrace(PNOR_COMP_NAME); l_err->collectTrace(LPC_COMP_NAME); } diff --git a/src/usr/lpc/lpcdd.H b/src/usr/lpc/lpcdd.H index 074c5da98..3fa8388f2 100644 --- a/src/usr/lpc/lpcdd.H +++ b/src/usr/lpc/lpcdd.H @@ -85,8 +85,7 @@ class LpcDD RESET_ECCB = 0x00000001, /**< ECCB FW Logic */ RESET_OPB_LPCHC_SOFT = 0x00000002, /**< OPB LPCHC Clear Errors */ RESET_OPB_LPCHC_HARD = 0x00000004, /**< OPB LPCHC Reset Logic */ - - RESET_INIT = RESET_CLEAR, // Nothing to do for initial boot yet + RESET_INIT = 0x00000008, /**< Initial HW setup */ }; diff --git a/src/usr/pnor/HBconfig b/src/usr/pnor/HBconfig index e25089cef..bc1ebc9dd 100644 --- a/src/usr/pnor/HBconfig +++ b/src/usr/pnor/HBconfig @@ -1,16 +1,20 @@ config SFC_IS_IBM_DPSS - default y if !SFC_IS_AST2400 - depends on !SFC_IS_AST2400 + default y + depends on !SFC_IS_AST2400 && !SFC_IS_FAKE help The Serial Flash Controller is the IBM DPSS FPGA. config SFC_IS_AST2400 default n - depends on !SFC_IS_IBM_DPSS && BMC_DOES_SFC_INIT + depends on !SFC_IS_IBM_DPSS && !SFC_IS_FAKE help The Serial Flash Controller is the AST2400 BMC. - +config SFC_IS_FAKE + default n + depends on !SFC_IS_IBM_DPSS && !SFC_IS_AST2400 + help + The Serial Flash Controller is emulated using memory. config BMC_DOES_SFC_INIT default y @@ -19,3 +23,13 @@ config BMC_DOES_SFC_INIT SFC before Hostboot is started. The BMC is also responsible for doing any repairs or recovery for the SFC. +config ALLOW_MICRON_PNOR + default y + help + Include support for Micron PNOR chips + +config ALLOW_MACRONIX_PNOR + default y + help + Include support for Macronix PNOR chips + diff --git a/src/usr/pnor/makefile b/src/usr/pnor/makefile index c9a37cc3e..e6585c123 100644 --- a/src/usr/pnor/makefile +++ b/src/usr/pnor/makefile @@ -6,6 +6,7 @@ # OpenPOWER HostBoot Project # # Contributors Listed Below - COPYRIGHT 2011,2014 +# [+] Google Inc. # [+] International Business Machines Corp. # # @@ -29,6 +30,16 @@ OBJS += pnorrp.o OBJS += pnordd.o OBJS += pnorvalid.o OBJS += ecc.o +OBJS += sfcdd.o + +#SFC Implementations +OBJS += $(if $(CONFIG_SFC_IS_IBM_DPSS),sfc_ibm.o) +OBJS += $(if $(CONFIG_SFC_IS_AST2400),sfc_ast2400.o) +OBJS += $(if $(CONFIG_SFC_IS_FAKE),sfc_fake.o) + +#NOR Implementations +OBJS += $(if $(CONFIG_ALLOW_MICRON_PNOR),nor_micron.o) + SUBDIRS += test.d diff --git a/src/usr/pnor/nor_micron.C b/src/usr/pnor/nor_micron.C new file mode 100644 index 000000000..817b1d3fd --- /dev/null +++ b/src/usr/pnor/nor_micron.C @@ -0,0 +1,198 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/nor_micron.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 "norflash.H" +#include "sfcdd.H" +#include +#include +#include +#include + +// Initialized in pnorrp.C +extern trace_desc_t* g_trac_pnor; + + +namespace PNOR { + +/** + * @brief Check the version of the part to see if it has any + * known errors that require a workaround. + */ +errlHndl_t micronCheckForWorkarounds( SfcDD* i_sfc, + uint32_t& o_workarounds ) +{ + errlHndl_t l_err = NULL; + + do { + // Assume all Micron chips have this bug + o_workarounds |= HWWK_MICRON_EXT_READ; + + uint32_t outdata[4]; + + //Read back full 6 bytes of chipid + l_err = i_sfc->sendSpiCmd( PNOR::SPI_JEDEC_CHIPID, + SfcDD::NO_ADDRESS, + 0, NULL, + 6, reinterpret_cast(outdata) ); + if(l_err) { break; } + + //If bit 1 (indicates 45nm new part) is set in 2nd word of cmd buffer + // data, then we do not need the workaround. + //Ex: CCCCCCLL 40000000 + // CCCCCC -> Industry Standard Chip ID + // LL -> Length of Micron extended data + // 4 -> Bit to indicate we do not need the erase/write workaround + TRACFCOMP( g_trac_pnor, "micronCheckForWorkarounds> ExtId = %.8X %.8X", outdata[0], outdata[1] ); + if((outdata[1] & 0x40000000) == 0x00000000) + { + TRACFCOMP( g_trac_pnor, "micronCheckForWorkarounds> Setting Micron workaround flag" ); + //Set Micron workaround flag + o_workarounds |= HWWK_MICRON_WRT_ERASE; + } + + //Prove this works + l_err = micronFlagStatus( i_sfc ); + if(l_err) { delete l_err; } + + } while(0); + + return l_err; +} + +/** + * @brief Check flag status bit on Micron NOR chips + * Some versions of Micron parts require the Flag + * Status register be read after a write or erase operation, + * otherwise all future operations won't work.. + */ +errlHndl_t micronFlagStatus( SfcDD* i_sfc ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "micronFlagStatus>" ); + + do { + //Read Micron 'flag status' register + uint8_t flagstat = 0; + l_err = i_sfc->sendSpiCmd( SPI_MICRON_FLAG_STAT, + SfcDD::NO_ADDRESS, + 0, NULL, + sizeof(flagstat), &flagstat ); + if(l_err) { break; } + + TRACDCOMP(g_trac_pnor, + "micronFlagStatus> (0x%.2X)", + flagstat); + + // check for ready and no errors + // bit 0 = ready, bit 2=erase fail, bit 3=Program (Write) failure + if( (flagstat & 0xB0) != 0x80) + { + TRACFCOMP(g_trac_pnor, "micronFlagStatus> Error or timeout from Micron Flag Status Register (0x%.2X)", flagstat); + + //Read back full 6 bytes of chipid + uint32_t outdata[2]; + l_err = i_sfc->sendSpiCmd( PNOR::SPI_JEDEC_CHIPID, + SfcDD::NO_ADDRESS, + 0, NULL, + 6, reinterpret_cast(outdata) ); + if( l_err ) { delete l_err; } + + /*@ + * @errortype + * @moduleid PNOR::MOD_NORMICRON_MICRONFLAGSTATUS + * @reasoncode PNOR::RC_MICRON_INCOMPLETE + * @userdata1[0:31] Micron Flag status register + * @userdata2 NOR Flash Chip ID + * @devdesc micronFlagStatus> Error or timeout from + * Micron Flag Status Register + * @custdesc Hardware error accessing flash during IPL + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_NORMICRON_MICRONFLAGSTATUS, + PNOR::RC_MICRON_INCOMPLETE, + TWO_UINT32_TO_UINT64(flagstat,0), + TWO_UINT32_TO_UINT64(outdata[0], + outdata[1]) ); + + // Limited in callout: no PNOR target, so calling out processor + l_err->addHwCallout( + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + i_sfc->addFFDC(l_err); + + //Read out SFDP + uint8_t sfdp[16]; + l_err = i_sfc->sendSpiCmd( PNOR::SPI_JEDEC_READ_SFDP, + 0, + 0, NULL, + 16, sfdp ); + if( l_err ) + { + delete l_err; + } + else + { + //@fixme-RTC:115212 - Create userdetails class + l_err->addFFDC( PNOR_COMP_ID, + sfdp, + sizeof(sfdp), + 0, // Version + ERRORLOG::ERRL_UDT_NOFORMAT, + false ); // merge + } + + //Erase & Program error bits are sticky, + // so they need to be cleared. + uint8_t flagstat = 0; + errlHndl_t tmp_err = i_sfc->sendSpiCmd( SPI_MICRON_CLRFLAG_STAT, + SfcDD::NO_ADDRESS, + sizeof(flagstat), + &flagstat, + 0, + NULL ); + if(tmp_err) + { + //commit this error and return the original + tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); + tmp_err->plid(l_err->plid()); + ERRORLOG::errlCommit(tmp_err,PNOR_COMP_ID); + } + + l_err->collectTrace(PNOR_COMP_NAME); + + break; + } + + + }while(0); + + return l_err; + +} + + +};//namespace PNOR diff --git a/src/usr/pnor/norflash.H b/src/usr/pnor/norflash.H new file mode 100644 index 000000000..4ab2628fe --- /dev/null +++ b/src/usr/pnor/norflash.H @@ -0,0 +1,164 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/norflash.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 __PNOR_NORFLASH_H +#define __PNOR_NORFLASH_H +#include +#include + +class SfcDD; + +/** @file norflash.H + * @brief Contains constants related to specific types of + * of NOR flash chips + */ + +namespace PNOR { + +/** + * @brief Supported NOR Chip IDs + */ +enum NorChipIDs +{ + UNKNOWN_NOR_ID = 0x12345600, /**< Initial value before read */ + + MICRON_MFG_ID = 0x20000000, /**< Micron Mfg ID */ + MICRON_NOR_ID = 0x20ba2000, /**< Micron NOR */ + + MACRONIX_MFG_ID = 0xC2000000, /**< Macronix Mfg ID */ + MACRONIX32_NOR_ID = 0xC2201A00, /**< Macronix NOR MXxxL51235F */ + MACRONIX64_NOR_ID = 0xC2201900, /**< Macronix NOR MXxxL25635F */ + + /* Note: Simics currently models Micron NOR */ + VPO_NOR_ID = 0x20201800, /**< VPO NOR chip ID */ + FAKE_NOR_ID = 0xBADBAD00, /**< ID used during fake pnor */ + + ID_MASK = 0xFFFFFF00, /**< Only look at 3 bytes */ + MFGID_MASK = 0xFF000000, /**< Manufacturer ID is the first byte */ +}; + +/** + * @brief SPI Config Info + * OP Codes and other MISC info for configuring SFC + */ +enum SpiConfigInfo +{ + SPI_NO_OPCODE = 0x00, /**< Undefined value */ + + /* + * Micron Flash Commands + */ + SPI_MICRON_FLAG_STAT = 0x70, /**< Check write/erase complete */ + SPI_MICRON_CLRFLAG_STAT = 0x50, /**< Clear write/erase Status reg */ + + /* + * Macronix Flash Commands + */ + SPI_MACRONIX_EN4B = 0xB7, /**< Enable Macronix 4-Byte addressing */ + + /* SPI protocol commands */ + SPI_JEDEC_WRITE_STATUS = 0x01, //WRSR + SPI_JEDEC_PAGE_PROGRAM = 0x02, //PP + SPI_JEDEC_READ = 0x03, //READ + SPI_JEDEC_WRITE_DISABLE = 0x04, //WRDI + SPI_JEDEC_READ_STATUS = 0x05, //RDSR + SPI_JEDEC_WRITE_ENABLE = 0x06, //WREN + SPI_JEDEC_FAST_READ = 0x0B, //FAST_READ + SPI_JEDEC_SECTOR_ERASE = 0x20, //SE + SPI_JEDEC_READ_SFDP = 0x5A, //RDSFDP + SPI_JEDEC_CHIPID = 0x9F, //RDID + SPI_JEDEC_BLOCK_ERASE = 0xD8, //BE + +}; + +/** + * Common format of Status Register + */ +union NorStatusReg_t +{ + uint8_t data8; + struct + { + uint8_t writeProtect : 1; //0 + uint8_t rsvd : 5; //1:5 + uint8_t writeEnable : 1; //6 + uint8_t writeInProgress : 1; //7 + }; + NorStatusReg_t() : data8(0x00) { }; +}; + +/** + * Flags used to trigger Hardware workarounds + */ +enum VendorWorkarounds +{ + // No workarounds present + HWWK_NO_WORKAROUNDS = 0x00000000, + + // Must perform 'read flag status' commands after + // any write or erase + HWWK_MICRON_WRT_ERASE = 0x00000001, + + // Must do a read of a low flash address before issuing read + // commands that return more than 1 word of data + HWWK_MICRON_EXT_READ = 0x00000002, +}; + + +/* + * Vendor-specific interfaces + */ +#ifdef CONFIG_ALLOW_MICRON_PNOR + +/** + * @brief Check the version of the part to see if it has any + * known errors that require a workaround. + * + * @parm i_sfc SFC driver to operate on + * @parm o_workarounds Function will OR in any flags it discovers + * + * @return error from operation + */ +errlHndl_t micronCheckForWorkarounds( SfcDD* i_sfc, + uint32_t& o_workarounds ); + +/** + * @brief Check flag status bit on Micron NOR chips + * The current version of Micron parts require the Flag + * Status register be read after a read or erase operation, + * otherwise all future operations won't work.. + * + * @parm i_sfc SFC driver to operate on + * + * @return Error from operation + */ +errlHndl_t micronFlagStatus( SfcDD* i_sfc ); + +#endif + + +};//namespace PNOR + +#endif diff --git a/src/usr/pnor/pnordd.C b/src/usr/pnor/pnordd.C index 2d9bc1854..e5b8bd7f3 100644 --- a/src/usr/pnor/pnordd.C +++ b/src/usr/pnor/pnordd.C @@ -53,7 +53,7 @@ #include #include #include - +#include "sfcdd.H" /*****************************************************************************/ // D e f i n e s @@ -66,11 +66,6 @@ extern trace_desc_t* g_trac_pnor; namespace PNOR { - enum { - VPO_MODE_MEMCPY = 0xFAC0FAC0FAC0FAC0, - VPO_MODE_MMIO = 0xDAB0DAB0DAB0DAB0, - }; - /** * @brief Performs an PNOR Read Operation * This function performs a PNOR Read operation. It follows a pre-defined @@ -150,10 +145,10 @@ errlHndl_t ddRead(DeviceFW::OperationType i_opType, */ errlHndl_t ddWrite(DeviceFW::OperationType i_opType, TARGETING::Target* i_target, - void* io_buffer, - size_t& io_buflen, - int64_t i_accessType, - va_list i_args) + void* io_buffer, + size_t& io_buflen, + int64_t i_accessType, + va_list i_args) { errlHndl_t l_err = NULL; uint64_t l_addr = va_arg(i_args,uint64_t); @@ -223,28 +218,14 @@ errlHndl_t PnorDD::readFlash(void* o_buffer, uint64_t i_address) { //TRACDCOMP(g_trac_pnor, "PnorDD::readFlash(i_address=0x%llx)> ", i_address); - errlHndl_t l_err = NULL; - - do{ - //mask off chip select for now, will probably break up fake PNOR into - //multiple fake chips eventually - uint64_t l_address = i_address & 0x00000000FFFFFFFF; - - // skip everything in MEMCPY mode - if( MODEL_MEMCPY == iv_mode ) - { - read_fake_pnor( l_address, o_buffer, io_buflen ); - break; - } - //If we get here we're doing either MODEL_LPC_MEM, MODEL_REAL_CMD, or MODEL_REAL_MMIO - mutex_lock(iv_mutex_ptr); - l_err = bufferedSfcRead(i_address, io_buflen, o_buffer); - mutex_unlock(iv_mutex_ptr); + //mask off chip select for now, will probably break up fake PNOR into + //multiple fake chips eventually + uint64_t l_address = i_address & 0x00000000FFFFFFFF; - if(l_err) { break;} - - }while(0); + mutex_lock(iv_mutex_ptr); + errlHndl_t l_err = _readFlash( l_address, io_buflen, o_buffer ); + mutex_unlock(iv_mutex_ptr); return l_err; } @@ -266,36 +247,14 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer, //multiple fake chips eventually uint64_t l_address = i_address & 0x00000000FFFFFFFF; - // skip everything in MEMCPY mode - if( MODEL_MEMCPY == iv_mode ) - { - write_fake_pnor( l_address, i_buffer, io_buflen ); - break; - } - - //If we get here we're doing either MODEL_LPC_MEM, MODEL_REAL_CMD, or MODEL_REAL_MMIO - - //If we're in VPO, just do the write directly (no erases) - if(0 != iv_vpoMode) - { - mutex_lock(iv_mutex_ptr); - l_err = bufferedSfcWrite(static_cast(l_address), - 8, - i_buffer); - mutex_unlock(iv_mutex_ptr); - break; - } - - - // LPC is accessed 32-bits at a time, but SFC has a 256byte buffer - // but we also need to be smart about handling erases. In NOR - // flash we can clear bits without an erase but we cannot set them. - // When we erase we have to erase an entire block of data at a time. + // In NOR flash we can clear bits without an erase but we + // cannot set them. When we erase we have to erase an entire + // block of data at a time. uint32_t cur_writeStart_addr = static_cast(l_address); uint32_t cur_blkStart_addr = findEraseBlock(cur_writeStart_addr); - uint32_t cur_blkEnd_addr = cur_blkStart_addr + iv_erasesize_bytes; - uint32_t write_bytes = iv_erasesize_bytes; + uint32_t cur_blkEnd_addr = cur_blkStart_addr + iv_eraseSizeBytes; + uint32_t write_bytes = iv_eraseSizeBytes; uint64_t num_blocks = getNumAffectedBlocks(cur_writeStart_addr,io_buflen); uint64_t bytes_left = io_buflen; @@ -310,9 +269,9 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer, // writing at a block boundary, just write the whole thing if( cur_writeStart_addr == cur_blkStart_addr ) { - if( bytes_left > iv_erasesize_bytes ) + if( bytes_left > iv_eraseSizeBytes ) { - write_bytes = iv_erasesize_bytes; + write_bytes = iv_eraseSizeBytes; } else { @@ -347,8 +306,10 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer, if( l_err ) { break; } - cur_blkStart_addr = cur_blkEnd_addr; //move start to end of current erase block - cur_blkEnd_addr += iv_erasesize_bytes;; //increment end by erase block size. + //move start to end of current erase block + cur_blkStart_addr = cur_blkEnd_addr; + //increment end by erase block size. + cur_blkEnd_addr += iv_eraseSizeBytes; cur_writeStart_addr += write_bytes; bytes_left -= write_bytes; @@ -375,37 +336,20 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer, ********************/ mutex_t PnorDD::cv_mutex = MUTEX_INITIALIZER; -// -// @note fake pnor no longer needs to allow from for SLW -// image so it is now 4MB. -// -#define FAKE_PNOR_START (4*MEGABYTE) -#define FAKE_PNOR_END (8*MEGABYTE) -#define FAKE_PNOR_SIZE (FAKE_PNOR_END-FAKE_PNOR_START) - /** * @brief Constructor */ -PnorDD::PnorDD( PnorMode_t i_mode, - uint64_t i_fakeStart, - uint64_t i_fakeSize, - TARGETING::Target* i_target ) -: iv_mode(i_mode) -, iv_vpoMode(0) -, iv_nor_chipid(0) -, iv_hw_workaround(0) -, iv_sfcInitDone(false) -, iv_ffdc_active(false) -, iv_error_handled_count(0x0) -, iv_error_recovery_failed(false) -, iv_reset_active(false) +PnorDD::PnorDD( TARGETING::Target* i_target ) +: iv_eraseSizeBytes(ERASESIZE_BYTES_DEFAULT) +, iv_norChipId(0) +, iv_sfc(NULL) { - iv_erasesize_bytes = ERASESIZE_BYTES_DEFAULT; + TRACFCOMP(g_trac_pnor, ENTER_MRK "PnorDD::PnorDD()" ); + errlHndl_t l_err = NULL; //Zero out erase counter memset(iv_erases, 0xff, sizeof(iv_erases)); - // Use i_target if all of these apply // 1) not NULL // 2) not MASTER_PROCESSOR_CHIP_TARGET_SENTINEL @@ -438,54 +382,37 @@ PnorDD::PnorDD( PnorMode_t i_mode, iv_mutex_ptr = &(cv_mutex); } - //Use real PNOR for everything except VPO - if(0 == iv_vpoMode) - { - iv_vpoMode = mmio_scratch_read(MMIO_SCRATCH_PNOR_MODE); - } - //@fixme-RTC:95130 - //Force to not be in VPO mode - iv_vpoMode = 0; + do { + //Instantiate the appropriate SFC object + l_err = PNOR::create_SfcDD( iv_sfc, + iv_target ); + if( l_err ) { break; } - //In the normal case we will choose the mode for the caller - if( MODEL_UNKNOWN == iv_mode ) - { - if(iv_vpoMode == PNOR::VPO_MODE_MEMCPY) - { - //VPO override set -- use fastest method -- memcpy - TRACFCOMP(g_trac_pnor,"PNORDD: Running in MEMCPY mode for VPO"); - iv_mode = MODEL_MEMCPY; - } - else if(iv_vpoMode == PNOR::VPO_MODE_MMIO) - { - //VPO override set -- use MMIO mode - TRACFCOMP(g_trac_pnor,"PNORDD: Running in VPO Mode, writes will not trigger erase attempts"); - iv_mode = MODEL_REAL_MMIO; - } - else - { - //Normal mode - iv_mode = MODEL_REAL_MMIO; - } - } + //Initialize the SFC hardware if needed +#ifndef BMC_DOES_SFC_INIT + l_err = iv_sfc->hwInit(); + if( l_err ) { break; } +#endif - if( (MODEL_MEMCPY == iv_mode) || - (MODEL_LPC_MEM == iv_mode) ) - { - //Only use input fake values if they are != zero - iv_fakeStart = (i_fakeStart != 0) ? i_fakeStart : FAKE_PNOR_START; - iv_fakeSize = (i_fakeSize != 0) ? i_fakeSize : FAKE_PNOR_SIZE; - } + //Figure out what kind of flash chip we have + l_err = iv_sfc->getNORChipId(iv_norChipId); + if( l_err ) { break; } - if( (MODEL_REAL_CMD == iv_mode) || - (MODEL_REAL_MMIO == iv_mode) ) - { + //Keep track of the size of the erase block + iv_eraseSizeBytes = iv_sfc->eraseSizeBytes(); + //We only support 4K erase blocks for now + assert(iv_eraseSizeBytes == ERASESIZE_BYTES_DEFAULT); + } while(0); - // @todo RTC 97493 - make sure sfcInit only gets called once per target - sfcInit( ); + if( l_err ) + { + TRACFCOMP( g_trac_pnor, "Failure to initialize the PNOR logic, shutting down :: RC=%.4X", ERRL_GETRC_SAFE(l_err) ); + l_err->collectTrace(PNOR_COMP_NAME); + ERRORLOG::errlCommit(l_err,PNOR_COMP_ID); + INITSERVICE::doShutdown( PNOR::RC_PNOR_INIT_FAILURE ); } - TRACFCOMP(g_trac_pnor, "PnorDD::PnorDD()> Using mode %d, vpo=%d", iv_mode, iv_vpoMode); + TRACFCOMP(g_trac_pnor, EXIT_MRK "PnorDD::PnorDD()" ); } /** @@ -493,1680 +420,260 @@ PnorDD::PnorDD( PnorMode_t i_mode, */ PnorDD::~PnorDD() { - + if( iv_sfc ) + { + delete iv_sfc; + } } - /** - * STATIC - * @brief Static Initializer + * @brief Informs caller if PNORDD is using + * L3 Cache for fake PNOR or not. */ -void PnorDD::sfcInit( ) +bool PnorDD::usingL3Cache( void ) { - TRACFCOMP(g_trac_pnor, "PnorDD::sfcInit> iv_mode=0x%.8x", iv_mode ); - errlHndl_t l_err = NULL; + return iv_sfc->usingL3Cache(); +} - mutex_lock(iv_mutex_ptr); +/** + * @brief Compare the existing data in 1 erase block of the flash with + * the incoming data and write or erase as needed + */ +errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart, + uint32_t i_writeStart, + size_t i_bytesToWrite, + void* i_data) +{ + TRACDCOMP(g_trac_pnor,">>compareAndWriteBlock(0x%.8X,0x%.8X,0x%.8X)", i_blockStart, i_writeStart, i_bytesToWrite); + errlHndl_t l_err = NULL; + uint8_t* read_data = NULL; do { -#ifdef CONFIG_SFC_IS_AST2400 - TRACFCOMP( g_trac_pnor, "PnorDD::sfcInit> Nothing to do yet for AST2400" ); - break; - //@todo RTC:106881 - Fix up to support erase/write later -#endif //CONFIG_SFC_IS_AST2400 - - if(!iv_sfcInitDone) - { -#ifdef CONFIG_BMC_DOES_SFC_INIT - - // Set OPB LPCM FIR Mask - hostboot will monitor these FIR bits - size_t scom_size = sizeof(uint64_t); - uint64_t fir_mask_data = OPB_LPCM_FIR_ERROR_MASK; + // remember any data we read so we don't have to reread it later + read_data = new uint8_t[iv_eraseSizeBytes]; - l_err = deviceOp( DeviceFW::WRITE, - iv_target, - &(fir_mask_data), - scom_size, - DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_MASK_WO_OR_REG)); - if( l_err ) { break; } + // remember if we need to erase the block or not + bool need_erase = false; + bool need_write = false; - //Determine NOR Flash type - triggers vendor specific workarounds - //We also use the chipID in some FFDC situations. - l_err = getNORChipId(iv_nor_chipid); - if(l_err) { break; } - TRACFCOMP(g_trac_pnor, - "PnorDD::sfcInit: iv_nor_chipid=0x%.8x> ", - iv_nor_chipid ); + //STEP 1: Read data in PNOR for compares (only read section we + // want to write) + //read_start needs to be uint32* for bitwise word compares later + uint32_t* read_start = (uint32_t*)(read_data + + i_writeStart-i_blockStart); + l_err = _readFlash( i_writeStart, + i_bytesToWrite, + (void*) read_start ); + if( l_err ) { break; } - // Re-initialize internal erase size cached value. - l_err = readRegSfc(SFC_CMD_SPACE, - SFC_REG_ERASMS, - iv_erasesize_bytes); - if(l_err) { break; } - TRACFCOMP(g_trac_pnor,"PnorDD::sfcInit: iv_erasesize_bytes=%X",iv_erasesize_bytes); + //STEP 2: walk through the write data to see if we need to do an erase + const uint32_t wordsToWrite = i_bytesToWrite/4; + uint32_t* i_dataWord = (uint32_t*) i_data; -#else //==!CONFIG_BMC_DOES_SFC_INIT + for(uint32_t cword = 0; cword < wordsToWrite; cword++) + { + // look for any bits being changed (using XOR) + if(read_start[cword] ^ i_dataWord[cword] ) + { + need_write = true; - TRACFCOMP( g_trac_pnor, INFO_MRK "Initializing SFC registers -- unsupported!!!" ); - //@todo RTC:97493 - Add SFC initialization from Host - INITSERVICE::doShutdown( PNOR::RC_UNSUPPORTED_MODE); + //Can only write zeros to NOR, see if any bits changed from 0->1 + if( (~(read_start[cword])) & (i_dataWord[cword]) ) + { + need_erase = true; -#endif //CONFIG_BMC_DOES_SFC_INIT + // skip comparing the rest of the block, + // just start writing it + break; + } + } + } - iv_sfcInitDone = true; + if(need_write == false) + { + //No write actually needed, break out here + TRACFCOMP(g_trac_pnor,"compareAndWriteBlock> NO Write Needed! Exiting Function"); + break; } - }while(0); + //STEP 3: If the need to erase was detected, read out the + // rest of the Erase block + if(need_erase) + { + TRACFCOMP(g_trac_pnor,"compareAndWriteBlock> Need to perform Erase"); + //Get data before write section + if(i_writeStart > i_blockStart) + { + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Reading beginning data i_blockStart=0x%.8x, readLen=0x%.8x", + i_blockStart, i_writeStart-i_blockStart); + l_err = _readFlash( i_blockStart, + i_writeStart-i_blockStart, + read_data ); + if( l_err ) { break; } + } - mutex_unlock(iv_mutex_ptr); + //Get data after write section + if( (i_writeStart+i_bytesToWrite) + < (i_blockStart + iv_eraseSizeBytes) ) + { + uint32_t tail_length = + i_blockStart + + iv_eraseSizeBytes + - (i_writeStart+i_bytesToWrite); + uint8_t* tail_buffer = + read_data + + i_writeStart-i_blockStart + + i_bytesToWrite; - if( l_err ) - { - TRACFCOMP( g_trac_pnor, ERR_MRK - "PnorDD::sfcInit> Committing error log"); - errlCommit(l_err,PNOR_COMP_ID); - } + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Reading tail data. addr=0x%.8x, tail_length=0x%.8x", + i_writeStart+i_bytesToWrite, tail_length); + l_err = _readFlash( i_writeStart+i_bytesToWrite, + tail_length, + tail_buffer ); + if( l_err ) { break; } + } - TRACFCOMP(g_trac_pnor, "< PnorDD::sfcInit" ); -} + // erase the flash + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Calling eraseFlash:. i_blockStart=0x%.8x", i_blockStart); + l_err = _eraseFlash( i_blockStart ); + if( l_err ) { break; } -bool PnorDD::usingL3Cache( ) -{ - TRACDCOMP(g_trac_pnor, - "PnorDD::usingL3Cache> iv_mode=0x%.8x", iv_mode ); + //STEP 4: Write the data back out - need to write everything + // since we erased the block - //If we are in one of the fake PNOR modes, - //than we are using L3 CAche - if( (MODEL_MEMCPY == iv_mode) || - (MODEL_LPC_MEM == iv_mode) ) - { - return true; - } - else - { - return false; - } -} + //re-write data before new data to write + if(i_writeStart > i_blockStart) + { + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Writing beginning data i_blockStart=0x%.8x, readLen=0x%.8x", + i_blockStart, i_writeStart-i_blockStart); + l_err = _writeFlash(i_blockStart, + i_writeStart-i_blockStart, + read_data); + if( l_err ) { break; } + } -/** - * @brief Write a SFC Register - */ -errlHndl_t PnorDD::writeRegSfc(SfcRange i_range, - uint32_t i_addr, - uint32_t i_data) -{ - errlHndl_t l_err = NULL; - uint32_t lpc_addr = 0; + //Write data after new data to write + if( (i_writeStart+i_bytesToWrite) + < (i_blockStart + iv_eraseSizeBytes) ) + { + uint32_t tail_length = + i_blockStart + + iv_eraseSizeBytes + - (i_writeStart+i_bytesToWrite); + uint8_t* tail_buffer = + read_data + + i_writeStart-i_blockStart + + i_bytesToWrite; + + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Writing tail data. addr=0x%.8x, tail_length=0x%.8x", i_writeStart+i_bytesToWrite, tail_length); + l_err = _writeFlash(i_writeStart+i_bytesToWrite, + tail_length, + tail_buffer); + if( l_err ) { break; } + } - switch(i_range) - { - case SFC_MMIO_SPACE: - { - lpc_addr = LPC_SFC_MMIO_OFFSET | i_addr; - break; - } - case SFC_CMD_SPACE: - { - lpc_addr = LPC_SFC_CMDREG_OFFSET | i_addr; - break; - } - case SFC_CMDBUF_SPACE: - { - lpc_addr = LPC_SFC_CMDBUF_OFFSET | i_addr; - break; - } - case SFC_LPC_SPACE: - { - lpc_addr = LPCHC_FW_SPACE | i_addr; - break; + //Write the new data - always do this + l_err = _writeFlash(i_writeStart, + i_bytesToWrite, + i_data); + if( l_err ) { break; } } - default: + else // { - TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::writeRegSfc> Unsupported SFC Address Range: i_range=0x%.16X, i_addr=0x%.8X. Calling doShutdown(PNOR::RC_UNSUPPORTED_SFCRANGE)", - i_range, i_addr); - - //Can't function without PNOR, initiate shutdown. - INITSERVICE::doShutdown( PNOR::RC_UNSUPPORTED_SFCRANGE); - break; - } - } //end switch + //STEP 4 ALT: No erase needed, only write the parts that changed. + TRACFCOMP(g_trac_pnor,"compareAndWriteBlock> No erase, just writing"); - TRACDCOMP( g_trac_pnor, "PnorDD::writeRegSfc> lpc_addr=0x%.8x, i_data=0x%.8x", - lpc_addr, i_data ); - l_err = writeLPC(lpc_addr, i_data); + for(uint32_t cword = 0; cword < wordsToWrite; cword++) + { + // look for any bits being changed (using XOR) + if(read_start[cword] ^ i_dataWord[cword] ) + { + //Write the new data - always do this + l_err = _writeFlash(i_writeStart + (cword*4), + 4, + &i_dataWord[cword]); + if( l_err ) { break; } + } + if( l_err ) { break; } - return l_err; -} + } + } -/** - * @brief Read a SFC Register - */ -errlHndl_t PnorDD::readRegSfc(SfcRange i_range, - uint32_t i_addr, - uint32_t& o_data) -{ - errlHndl_t l_err = NULL; - uint32_t lpc_addr = 0; + } while(0); - switch(i_range) + if( read_data ) { - case SFC_MMIO_SPACE: - { - lpc_addr = LPC_SFC_MMIO_OFFSET | i_addr; - break; - } - case SFC_CMD_SPACE: - { - lpc_addr = LPC_SFC_CMDREG_OFFSET | i_addr; - break; - } - case SFC_CMDBUF_SPACE: - { - lpc_addr = LPC_SFC_CMDBUF_OFFSET | i_addr; - break; - } - case SFC_LPC_SPACE: - { - lpc_addr = LPCHC_FW_SPACE | i_addr; - break; - } - default: - { - TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::readRegSfc> Unsupported SFC Address Range: i_range=0x%.16X, i_addr=0x%.8X. Calling doShutdown(PNOR::RC_UNSUPPORTED_SFCRANGE)", - i_range, i_addr); + delete[] read_data; + } - //Can't function without PNOR, initiate shutdown. - INITSERVICE::doShutdown( PNOR::RC_UNSUPPORTED_SFCRANGE); - break; - } - } //end switch + TRACDCOMP(g_trac_pnor,"< lpc_addr=0x%.8x, o_data=0x%.8x", - lpc_addr, o_data ); return l_err; } /** - * @brief Poll for SFC Op Complete + * @brief Erase a block of flash */ -errlHndl_t PnorDD::pollSfcOpComplete(uint64_t i_pollTime) +errlHndl_t PnorDD::eraseFlash(uint32_t i_address) { errlHndl_t l_err = NULL; - ResetLevels pnorResetLevel = RESET_CLEAR; - - TRACDCOMP( g_trac_pnor, "PnorDD::pollSfcOpComplete> i_pollTime=0x%.8x", - i_pollTime ); + TRACFCOMP(g_trac_pnor, ">>PnorDD::eraseFlash> Block 0x%.8X", i_address ); do { - //Poll for complete status - SfcStatReg_t sfc_stat; - uint64_t poll_time = 0; - uint64_t loop = 0; - while( poll_time < i_pollTime ) - { - l_err = readRegSfc(SFC_CMD_SPACE, - SFC_REG_STATUS, - sfc_stat.data32); - if(l_err) { break; } - - if( ( sfc_stat.done == 1 ) || - ( sfc_stat.timeout == 1 ) || - ( sfc_stat.illegal == 1 ) ) - { - break; - } - - // want to start out incrementing by small numbers then get bigger - // to avoid a really tight loop in an error case so we'll increase - // the wait each time through - ++loop; - nanosleep( 0, SFC_POLL_INCR_NS*loop ); - poll_time += SFC_POLL_INCR_NS*loop; - } - if( l_err ) { break; } - - l_err = checkForSfcErrors( pnorResetLevel ); - if( l_err ) { break; } - - // If no errors AND done bit not set, call out undefined error - if( (sfc_stat.done == 0) ) + if( findEraseBlock(i_address) != i_address ) { - TRACFCOMP(g_trac_pnor, - "PnorDD::pollSfcOpComplete> Error or timeout from SFC Status Register" - ); - /*@ * @errortype - * @moduleid PNOR::MOD_PNORDD_POLLSFCOPCOMPLETE - * @reasoncode PNOR::RC_SFC_ERROR - * @userdata1[0:31] NOR Flash Chip ID - * @userdata1[32:63] Total poll time (ns) - * @userdata2[0:31] ECCB Status Register - * @devdesc PnorDD::pollSfcOpComplete> Error or timeout from - * SFC Status Register - * @custdesc Hardware error accessing flash during IPL + * @moduleid PNOR::MOD_PNORDD_ERASEFLASH + * @reasoncode PNOR::RC_INVALID_ADDRESS + * @userdata1 Flash Address + * @userdata2 Nearest Erase Boundary + * @devdesc PnorDD::eraseFlash> Address not on erase boundary + * @custdesc Firmware error accessing flash during IPL */ l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_POLLSFCOPCOMPLETE, - PNOR::RC_SFC_ERROR, - TWO_UINT32_TO_UINT64(iv_nor_chipid, - poll_time), - TWO_UINT32_TO_UINT64(sfc_stat.data32,0)); - - // Limited in callout: no PNOR target, so calling out processor - l_err->addHwCallout( - iv_target, - HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, - HWAS::GARD_NULL ); - - addFFDCRegisters(l_err); + PNOR::MOD_PNORDD_ERASEFLASH, + PNOR::RC_INVALID_ADDRESS, + TWO_UINT32_TO_UINT64(0,i_address), + findEraseBlock(i_address), + true /*Add HB SW Callout*/ ); l_err->collectTrace(PNOR_COMP_NAME); - l_err->collectTrace(XSCOM_COMP_NAME); - - // Reset LPC Slave since it appears to be hung - handled below - pnorResetLevel = RESET_LPC_SLAVE; - break; } - TRACDCOMP(g_trac_pnor,"pollSfcOpComplete> command took %d ns", poll_time); - }while(0); - - // If we have an error that requires a reset, do that here - if ( l_err && ( pnorResetLevel != RESET_CLEAR ) ) - { - errlHndl_t tmp_err = NULL; - tmp_err = resetPnor(pnorResetLevel); - - if ( tmp_err ) + for(uint32_t idx = 0; idx < ERASE_COUNT_MAX; idx++ ) { - // Commit reset error as informational since we have - // original error l_err - TRACFCOMP(g_trac_pnor, "PnorDD::pollSfcOpComplete> Error from resetPnor() after previous error eid=0x%X. Committing resetPnor() error log eid=0x%X.", - l_err->eid(), tmp_err->eid()); - tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); - tmp_err->collectTrace(PNOR_COMP_NAME); - tmp_err->plid(l_err->plid()); - errlCommit(tmp_err, PNOR_COMP_ID); + if(iv_erases[idx].addr == i_address) + { + iv_erases[idx].count++; + TRACFCOMP(g_trac_pnor, + "PnorDD::eraseFlash> Block 0x%.8X has %d erasures", + i_address, iv_erases[idx].count ); + break; + + } + //iv_erases is init to all 0xff, + // so can use ~0 to check for an unused position + else if(iv_erases[idx].addr == ~0u) + { + iv_erases[idx].addr = i_address; + iv_erases[idx].count = 1; + TRACFCOMP(g_trac_pnor, + "PnorDD::eraseFlash> Block 0x%.8X has %d erasures", + i_address, iv_erases[idx].count ); + break; + } + else if( idx == (ERASE_COUNT_MAX - 1)) + { + TRACFCOMP(g_trac_pnor, "PnorDD::eraseFlash> Erase counter full! Block 0x%.8X Erased", i_address ); + break; + } } - } - return l_err; - -} - -/** - * @brief Check For Errors in SFC Status Registers - */ -errlHndl_t PnorDD::checkForSfcErrors( ResetLevels &o_pnorResetLevel ) -{ - errlHndl_t l_err = NULL; - bool errorFound = false; - - // Used to set Reset Levels, if necessary - o_pnorResetLevel = RESET_CLEAR; - - // Default status values in case we fail in reading the registers - LpcSlaveStatReg_t lpc_slave_stat; - lpc_slave_stat.data32 = 0xDEADBEEF; - SfcStatReg_t sfc_stat; - sfc_stat.data32 = 0xDEADBEEF; - - do { - - // First Read LPC Slave Status Register - l_err = readRegSfc(SFC_LPC_SPACE, - LPC_SLAVE_REG_STATUS, - lpc_slave_stat.data32); - - // If we can't read status register, exit out - if( l_err ) { break; } - - TRACDCOMP( g_trac_pnor, INFO_MRK"PnorDD::checkForSfcErrors> LPC Slave status reg: 0x%08llx", - lpc_slave_stat.data32); - - // Start with lighter reset level - if( 1 == lpc_slave_stat.lbusparityerror ) - { - errorFound = true; - o_pnorResetLevel = RESET_LPC_SLAVE_ERRS; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LPC Slave Local Bus Parity Error: status reg: 0x%08llx, ResetLevel=%d", - lpc_slave_stat.data32, o_pnorResetLevel); - } - - // Check for more stronger reset level - if( 0 != lpc_slave_stat.lbus2opberr ) - { - errorFound = true; - // All of these errors require the SFC Local Bus Reset - o_pnorResetLevel = RESET_SFC_LOCAL_BUS; - - if ( LBUS2OPB_ADDR_PARITY_ERR == lpc_slave_stat.lbus2opberr ) - { - // This error also requires LPC Slave Errors to be Cleared - o_pnorResetLevel = RESET_SFCBUS_LPCSLAVE_ERRS; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Address Parity Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", - lpc_slave_stat.data32, o_pnorResetLevel); - - } - - else if ( LBUS2OPB_INVALID_SELECT_ERR == lpc_slave_stat.lbus2opberr) - { - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Invalid Select Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", - lpc_slave_stat.data32, o_pnorResetLevel); - - } - else if ( LBUS2OPB_DATA_PARITY_ERR == lpc_slave_stat.lbus2opberr ) - { - // This error also requires LPC Slave Errors to be Cleared - o_pnorResetLevel = RESET_SFCBUS_LPCSLAVE_ERRS; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Data Parity Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", - lpc_slave_stat.data32, o_pnorResetLevel); - - } - else if ( LBUS2OPB_MONITOR_ERR == lpc_slave_stat.lbus2opberr ) - { - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Monitor Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", - lpc_slave_stat.data32, o_pnorResetLevel); - - } - - else if ( LBUS2OPB_TIMEOUT_ERR == lpc_slave_stat.lbus2opberr ) - { - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB Timeout Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", - lpc_slave_stat.data32, o_pnorResetLevel); - - } - else - { - // Just in case, clear LPC Slave Errors - o_pnorResetLevel = RESET_LPC_SLAVE_ERRS; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> LBUS2OPB UNKNOWN Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", - lpc_slave_stat.data32, o_pnorResetLevel); - } - - } - - // Second Read SFC and check for error bits - l_err = readRegSfc(SFC_CMD_SPACE, - SFC_REG_STATUS, - sfc_stat.data32); - - // If we can't read status register, exit out - if( l_err ) { break; } - - TRACDCOMP( g_trac_pnor, INFO_MRK"PnorDD::checkForSfcErrors> SFC status reg(0x%X): 0x%08llx", - SFC_CMD_SPACE|SFC_REG_STATUS,sfc_stat.data32); - - // No resets needed for these errors - if( 1 == sfc_stat.eccerrcntr ) - { - errorFound = true; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Threshold of SRAM ECC Errors Reached: SFC status reg: 0x%08llx, ResetLevel=%d", - sfc_stat.data32, o_pnorResetLevel); - } - - if( 1 == sfc_stat.eccues ) - { - errorFound = true; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> SRAM Command Uncorrectable ECC Error: SFC status reg: 0x%08llx, ResetLevel=%d", - sfc_stat.data32, o_pnorResetLevel); - } - - if( 1 == sfc_stat.illegal ) - { - errorFound = true; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Previous Operation was Illegal: SFC status reg: 0x%08llx, ResetLevel=%d", - sfc_stat.data32, o_pnorResetLevel); - } - - if( 1 == sfc_stat.eccerrcntn ) - { - errorFound = true; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Threshold for Flash ECC Errors Reached: SFC status reg: 0x%08llx, ResetLevel=%d", - - sfc_stat.data32, o_pnorResetLevel); - } - - if( 1 == sfc_stat.eccuen ) - { - errorFound = true; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Flash Command Uncorrectable ECC Error: SFC status reg: 0x%08llx, ResetLevel=%d", - sfc_stat.data32, o_pnorResetLevel); - } - - if( 1 == sfc_stat.timeout ) - { - errorFound = true; - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Timeout: SFC status reg: 0x%08llx, ResetLevel=%d", - sfc_stat.data32, o_pnorResetLevel); - } - - }while(0); - - - // If there is any error create an error log - if ( errorFound ) - { - // If we failed on a register read above, but still found an error, - // delete register read error log and create an original error log - // for the found error - if ( l_err ) - { - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::checkForSfcErrors> Deleting register read error. Returning error created for the found error"); - delete l_err; - } - - - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_CHECKFORSFCERRORS - * @reasoncode PNOR::RC_ERROR_IN_STATUS_REG - * @userdata1[0:31] SFC Status Register - * @userdata1[32:63] LPC Slave Status Register - * @userdata2 Reset Level - * @devdesc PnorDD::checkForSfcErrors> Error(s) found in SFC - * and/or LPC Slave Status Registers - * @custdesc Hardware error accessing flash during IPL - */ - l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_CHECKFORSFCERRORS, - PNOR::RC_ERROR_IN_STATUS_REG, - TWO_UINT32_TO_UINT64( - sfc_stat.data32, - lpc_slave_stat.data32), - o_pnorResetLevel ); - - // Limited in callout: no PNOR target, so calling out processor - l_err->addHwCallout( - iv_target, - HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, - HWAS::GARD_NULL ); - - addFFDCRegisters(l_err); - l_err->collectTrace(PNOR_COMP_NAME); - } - - return l_err; - -} - -/** - * @brief Add Error Registers to an existing Error Log - */ -void PnorDD::addFFDCRegisters(errlHndl_t & io_errl) -{ - - errlHndl_t tmp_err = NULL; - uint32_t data32 = 0; - uint64_t data64 = 0; - size_t size64 = sizeof(data64); - size_t size32 = sizeof(data32); - - // check iv_ffdc_active to avoid infinite loops - if ( iv_ffdc_active == false ) - { - iv_ffdc_active = true; - - TRACFCOMP( g_trac_pnor, ENTER_MRK"PnorDD::addFFDCRegisters> adding FFDC to Error Log EID=0x%X, PLID=0x%X", - io_errl->eid(), io_errl->plid() ); - - ERRORLOG::ErrlUserDetailsLogRegister - l_eud(iv_target); - - do { - //@fixme - RTC:107788 Seems like this might not belong here, - // instead only in the LPC layer... - // Add ECCB Status Register - tmp_err = deviceOp( DeviceFW::READ, - iv_target, - &(data64), - size64, - DEVICE_SCOM_ADDRESS(ECCB_STAT_REG) ); - - if( tmp_err ) - { - delete tmp_err; - TRACFCOMP( g_trac_pnor, "PnorDD::addFFDCRegisters> Fail reading ECCB_STAT_REG"); - } - else - { - l_eud.addDataBuffer(&data64, size64, - DEVICE_SCOM_ADDRESS(ECCB_STAT_REG)); - } - - // Add OPB Fir Register - tmp_err = deviceOp( DeviceFW::READ, - iv_target, - &(data64), - size64, - DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG) ); - - if( tmp_err ) - { - delete tmp_err; - TRACFCOMP( g_trac_pnor, "PnorDD::addFFDCRegisters> Fail reading OPB_LPCM_FIR_REG"); - } - else - { - l_eud.addDataBuffer(&data64, size64, - DEVICE_SCOM_ADDRESS(OPB_LPCM_FIR_REG)); - } - - // Add LPC Slave Status Register - LpcSlaveStatReg_t lpc_slave_stat; - - tmp_err = readRegSfc(SFC_LPC_SPACE, - LPC_SLAVE_REG_STATUS, - lpc_slave_stat.data32); - - if ( tmp_err ) - { - delete tmp_err; - TRACFCOMP( g_trac_pnor, "PnorDD::addFFDCRegisters> Fail reading LPC Slave Status Register"); - } - else - { - l_eud.addDataBuffer(&lpc_slave_stat.data32, size32, - DEVICE_SCOM_ADDRESS( LPCHC_FW_SPACE | - LPC_SLAVE_REG_STATUS)); - } - - // Add SFC Registers - uint32_t sfc_regs[] = { - SFC_REG_STATUS, - SFC_REG_CONF, - SFC_REG_CMD, - SFC_REG_ADR, - SFC_REG_ERASMS, - SFC_REG_ERASLGS, - SFC_REG_CONF4, - SFC_REG_CONF5, - SFC_REG_ADRCBF, - SFC_REG_ADRCMF, - SFC_REG_OADRNB, - SFC_REG_OADRNS, - SFC_REG_CHIPIDCONF, - SFC_REG_ERRCONF, - SFC_REG_ERRTAG, - SFC_REG_ERROFF, - SFC_REG_ERRSYN, - SFC_REG_ERRDATH, - SFC_REG_ERRDATL, - SFC_REG_ERRCNT, - SFC_REG_CLRCNT, - SFC_REG_ERRINJ, - SFC_REG_PROTA, - SFC_REG_PROTM, - SFC_REG_ECCADDR, - SFC_REG_ECCRNG, - SFC_REG_ERRORS, - SFC_REG_INTMSK, - SFC_REG_INTENM, - SFC_REG_CONF2, - SFC_REG_CONF3 - }; - - - for( size_t x=0; x<(sizeof(sfc_regs)/sizeof(sfc_regs[0])); x++ ) - { - tmp_err = readRegSfc( SFC_CMD_SPACE, - sfc_regs[x], - data32 ); - - if( tmp_err ) - { - delete tmp_err; - } - else - { - l_eud.addDataBuffer(&data32, size32, - DEVICE_SCOM_ADDRESS(LPC_SFC_CMDREG_OFFSET - | sfc_regs[x])); - } - } - - }while(0); - - l_eud.addToLog(io_errl); - - TRACFCOMP( g_trac_pnor, EXIT_MRK"PnorDD::addFFDCRegisters> Information added to error log"); - - // reset FFDC active flag - iv_ffdc_active = false; - } - - return; -} - - -/** - * @brief Check flag status bit on Micron NOR chips - * The current version of Micron parts require the Flag - * Status register be read after a write or erase operation, - * otherwise all future operations won't work.. - */ -errlHndl_t PnorDD::micronFlagStatus(uint64_t i_pollTime) -{ - errlHndl_t l_err = NULL; - TRACDCOMP( g_trac_pnor, "PnorDD::micronFlagStatus> i_pollTime=0x%.8x", - i_pollTime ); - - do { - - //Configure Get "Chip ID" command in SFC to check special - //Micron 'flag status' register - SfcCustomReg_t readflag_cmd; - readflag_cmd.data32 = 0; - readflag_cmd.opcode = SPI_MICRON_FLAG_STAT; - readflag_cmd.read = 1; - readflag_cmd.length = 1; - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CHIPIDCONF, - readflag_cmd.data32); - if(l_err) { break; } - - - //Check flag status bit. - uint32_t opStatus = 0; - uint64_t poll_time = 0; - uint64_t loop = 0; - while( poll_time < i_pollTime ) - { - //Issue Get Chip ID command (reading flag status) - SfcCmdReg_t sfc_cmd; - sfc_cmd.opcode = SFC_OP_CHIPID; - sfc_cmd.length = 0; - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CMD, - sfc_cmd.data32); - if(l_err) { break; } - - //Poll for complete status - l_err = pollSfcOpComplete(); - if(l_err) { break; } - - //Read the Status from the Command Buffer - l_err = readRegSfc(SFC_CMDBUF_SPACE, - 0, //Offset into CMD BUFF space in bytes - opStatus); - if(l_err) { break; } - - //check for complete or error - // bit 0 = ready, bit 2=erase fail, bit 3=Program (Write) failure - if( (opStatus & 0xB0000000)) - { - break; - } - - // want to start out incrementing by small numbers then get bigger - // to avoid a really tight loop in an error case so we'll increase - // the wait each time through - //TODO tmp remove for VPO, need better polling strategy -- RTC43738 - //nanosleep( 0, SFC_POLL_INCR_NS*(++loop) ); - ++loop; - poll_time += SFC_POLL_INCR_NS*loop; - } - if( l_err ) { break; } - - TRACDCOMP(g_trac_pnor, - "PnorDD::micronFlagStatus> (0x%.8X)", - opStatus); - - // check for ready and no errors - // bit 0 = ready, bit 2=erase fail, bit 3=Program (Write) failure - if( (opStatus & 0xB0000000) != 0x80000000) - { - TRACFCOMP(g_trac_pnor, - "PnorDD::micronFlagStatus> Error or timeout from Micron Flag Status Register (0x%.8X)", - opStatus); - - //Read SFDP - uint32_t outdata[4]; - SfcCustomReg_t new_cmd; - new_cmd.data32 = 0; - new_cmd.opcode = SPI_MICRON_READ_SFDP; - new_cmd.read = 1; - new_cmd.needaddr = 1; - new_cmd.clocks = 8; - new_cmd.length = 16; - l_err = readRegFlash( new_cmd, - outdata, - 0 ); - if(l_err) { break; } - - //Loop around and grab all 16 bytes - for( size_t x=0; x<4; x++ ) - { - TRACFCOMP( g_trac_pnor, "SFDP[%d]=%.8X", x, outdata[x] ); - } - - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_MICRONFLAGSTATUS - * @reasoncode PNOR::RC_MICRON_INCOMPLETE - * @userdata1[0:31] NOR Flash Chip ID - * @userdata1[32:63] Total poll time (ns) - * @userdata2[0:31] Micron Flag status register - * @devdesc PnorDD::micronFlagStatus> Error or timeout from - * Micron Flag Status Register - * @custdesc Hardware error accessing flash during IPL - */ - l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_MICRONFLAGSTATUS, - PNOR::RC_MICRON_INCOMPLETE, - TWO_UINT32_TO_UINT64(iv_nor_chipid, - poll_time), - TWO_UINT32_TO_UINT64(opStatus,0)); - - // Limited in callout: no PNOR target, so calling out processor - l_err->addHwCallout( - iv_target, - HWAS::SRCI_PRIORITY_HIGH, - HWAS::NO_DECONFIG, - HWAS::GARD_NULL ); - - addFFDCRegisters(l_err); - - //Erase & Program error bits are sticky, so they need to be cleared. - - //Configure Get "Chip ID" command in SFC to clear special - //Micron 'flag status' register. remaining bits are all zero - // since we just need to issue the SPI command. - uint32_t confData = SPI_MICRON_CLRFLAG_STAT << 24; - TRACDCOMP( g_trac_pnor, "PnorDD::micronFlagStatus> confData=0x%.8x", - confData ); - errlHndl_t tmp_err = NULL; - tmp_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CHIPIDCONF, - confData); - if(tmp_err) - { - //commit this error and return the original - tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); - tmp_err->plid(l_err->plid()); - ERRORLOG::errlCommit(tmp_err,PNOR_COMP_ID); - } - - //Issue Get Chip ID command (clearing flag status) - SfcCmdReg_t sfc_cmd; - sfc_cmd.opcode = SFC_OP_CHIPID; - sfc_cmd.length = 0; - tmp_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CMD, - sfc_cmd.data32); - if(tmp_err) - { - //commit this error and return the original - tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); - tmp_err->plid(l_err->plid()); - ERRORLOG::errlCommit(tmp_err,PNOR_COMP_ID); - } - - //Poll for complete status - tmp_err = pollSfcOpComplete(); - if(tmp_err) - { - delete tmp_err; - } - - l_err->collectTrace(PNOR_COMP_NAME); - l_err->collectTrace(XSCOM_COMP_NAME); - - break; - } - - - }while(0); - - return l_err; - -} - -/** - * @brief Read the NOR FLash ChipID - */ -errlHndl_t PnorDD::getNORChipId(uint32_t& o_chipId, - uint32_t i_spiOpcode) -{ - errlHndl_t l_err = NULL; - errlHndl_t original_err = NULL; - uint8_t retry = 0; - - TRACFCOMP( g_trac_pnor, "PnorDD::getNORChipId> i_spiOpcode=0x%.8x", - i_spiOpcode ); - - - // reset class variable since we're starting a new operation - iv_error_recovery_failed = false; - - /***********************************************************/ - /* This do-while loop supports retries */ - /***********************************************************/ - do { - - // group this block together with do-while - do{ - //Configure Get Chip ID opcode - uint32_t confData = i_spiOpcode << 24; - confData |= 0x00800003; // 8-> read, 3->3 bytes - TRACDCOMP( g_trac_pnor, "PnorDD::getNORChipId> confData=0x%.8x", - confData ); - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CHIPIDCONF, - confData); - if(l_err) { break; } - - //Issue Get Chip ID command - SfcCmdReg_t sfc_cmd; - sfc_cmd.opcode = SFC_OP_CHIPID; - sfc_cmd.length = 0; - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CMD, - sfc_cmd.data32); - if(l_err) { break; } - - //Poll for complete status - l_err = pollSfcOpComplete(); - if(l_err) { break; } - - //Read the ChipID from the Command Buffer - l_err = readRegSfc(SFC_CMDBUF_SPACE, - 0, //Offset into CMD BUFF space in bytes - o_chipId); - if(l_err) { break; } - - // Only look at a portion of the data that is returned - o_chipId &= ID_MASK; - - //Some micron chips require a special workaround required - //so need to set a flag for later use - //We can't read all 6 bytes above because not all MFG - //support that operation. - if( o_chipId == MICRON_NOR_ID ) - { - // Assume all Micron chips have this bug - iv_hw_workaround |= HWWK_MICRON_EXT_READ; - - uint32_t outdata[4]; - - //Change ChipID command to read back 6 bytes. - SfcCustomReg_t new_cmd; - new_cmd.opcode = SPI_GET_CHIPID_OP; - new_cmd.read = 1; - new_cmd.length = 6; - l_err = readRegFlash( new_cmd, - outdata ); - if(l_err) { break; } - - //If bit 1 is set in 2nd word of cmd buffer data, then - //We must do the workaround. - //Ex: CCCCCCLL 40000000 - // CCCCCC -> Industry Standard Chip ID - // LL -> Length of Micron extended data - // 4 -> Bit to indicate we must do erase/write workaround - TRACFCOMP( g_trac_pnor, "PnorDD::getNORChipId> ExtId = %.8X %.8X", outdata[0], outdata[1] ); - if((outdata[1] & 0x40000000) == 0x00000000) - { - TRACFCOMP( g_trac_pnor,"PnorDD::getNORChipId> Setting Micron workaround flag"); - //Set Micron workaround flag - iv_hw_workaround |= HWWK_MICRON_WRT_ERASE; - } - - - //Read SFDP for FFDC - new_cmd.data32 = 0; - new_cmd.opcode = SPI_MICRON_READ_SFDP; - new_cmd.read = 1; - new_cmd.needaddr = 1; - new_cmd.clocks = 8; - new_cmd.length = 16; - l_err = readRegFlash( new_cmd, - outdata, - 0 ); - if(l_err) { break; } - - //Loop around and grab all 16 bytes - for( size_t x=0; x<4; x++ ) - { - TRACFCOMP( g_trac_pnor, "SFDP[%d]=%.8X", x, outdata[x] ); - } - - //Prove this works - l_err = micronFlagStatus(); - if(l_err) { delete l_err; } - } - - }while(0); // group of commands - - // end of operation - check for retry - } while ( shouldRetry(RETRY_getNORChipId, l_err, original_err, retry) ); - - return l_err; -} - - -/** - * @brief Load SFC command buffer with data from PNOR - */ -errlHndl_t PnorDD::loadSfcBuf(uint32_t i_addr, - size_t i_size) -{ - errlHndl_t l_err = NULL; - TRACDCOMP( g_trac_pnor, "PnorDD::loadSfcBuf> i_addr=0x%.8x, i_size=0x%.8x", - i_addr, i_size ); - - do { - //Write flash address to ADR reg - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_ADR, - i_addr); - if(l_err) { break; } - - //Issue ReadRaw command with size to read - SfcCmdReg_t sfc_cmd; - sfc_cmd.opcode = SFC_OP_READRAW; - sfc_cmd.length = i_size; - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CMD, - sfc_cmd.data32); - if(l_err) { break; } - - //Poll for complete status - l_err = pollSfcOpComplete(); - if(l_err) { break; } - - }while(0); - - return l_err; - -} - -/** - * @brief Flush SFC command buffer data to PNOR Flash - */ -errlHndl_t PnorDD::flushSfcBuf(uint32_t i_addr, - size_t i_size) -{ - errlHndl_t l_err = NULL; - TRACDCOMP( g_trac_pnor, - "PnorDD::flushSfcBuf> i_addr=0x%.8x, i_size=0x%.8x", - i_addr, i_size ); - - do { - //Write flash address to ADR reg - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_ADR, - i_addr); - if(l_err) { break; } - - //Issue WriteRaw command + size to write - SfcCmdReg_t sfc_cmd; - sfc_cmd.opcode = SFC_OP_WRITERAW; - sfc_cmd.length = i_size; - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CMD, - sfc_cmd.data32); - if(l_err) { break; } - - //Poll for complete status - l_err = pollSfcOpComplete(); - if(l_err) { break; } - - //check for special Micron Flag Status reg - if(iv_hw_workaround & HWWK_MICRON_WRT_ERASE) - { - l_err = micronFlagStatus(); - if(l_err) { break; } - } - - }while(0); - - return l_err; - -} - -/** - * @brief Perform command based read of PNOR, maximizing use of - * SFC Command buffer.. - */ -errlHndl_t PnorDD::bufferedSfcRead(uint32_t i_addr, - size_t i_size, - void* o_data) -{ - TRACDCOMP( g_trac_pnor, "PnorDD::bufferedSfcRead> i_addr=0x%.8x, i_size=0x%.8x", - i_addr, i_size ); - - errlHndl_t l_err = NULL; - errlHndl_t original_err = NULL; - uint8_t retry = 0; - - // reset class variable since we're starting a new operation - iv_error_recovery_failed = false; - - /***********************************************************/ - /* This do-while loop supports retries */ - /***********************************************************/ - do { - - switch(iv_mode) - { - case MODEL_REAL_MMIO: - { - //Read directly from MMIO space - uint32_t* word_ptr = static_cast(o_data); - uint32_t word_size = i_size/4; - for( uint32_t words_read = 0; - words_read < word_size; - words_read ++ ) - { - l_err = readRegSfc( - SFC_MMIO_SPACE, - i_addr+words_read*4, //MMIO Address offset - word_ptr[words_read]); - if( l_err ) { break; } - } - - break; - } - case MODEL_REAL_CMD: - { - - // Command based reads are buffered 256 bytes at a time. - uint32_t chunk_size = 0; - uint64_t addr = i_addr; - uint64_t end_addr = i_addr + i_size; - - while(addr < end_addr) - { - chunk_size = SFC_CMDBUF_SIZE; - if( (addr + SFC_CMDBUF_SIZE) > end_addr) - { - chunk_size = end_addr - addr; - } - - //Read data via SFC CMD Buffer - l_err = loadSfcBuf(addr, chunk_size); - if(l_err) { break;} - - //read SFC CMD Buffer via MMIO - l_err = readSfcBuffer( - chunk_size, - (void*)((uint64_t)o_data + (addr-i_addr))); - if(l_err) { break;} - - addr += chunk_size; - } - break; - } - case MODEL_LPC_MEM: - { - read_fake_pnor( i_addr, - o_data, - i_size ); - break; - } - default: - { - TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::bufferedSfcRead> Unsupported mode: iv_mode=0x%.16X, i_addr=0x%.8X. Calling doShutdown(PNOR::RC_UNSUPPORTED_MODE)", - iv_mode, i_addr); - - //Can't function without PNOR, initiate shutdown. - INITSERVICE::doShutdown( PNOR::RC_UNSUPPORTED_MODE); - break; - } - } //end switch - - // end of operation - check for retry - } while ( shouldRetry(RETRY_bufferedSfcRead, l_err, original_err, retry) ); - - return l_err; - -} - - -/** - * @brief Perform command based write of PNOR, maximizing use of - * SFC Command buffer.. - */ -errlHndl_t PnorDD::bufferedSfcWrite(uint32_t i_addr, - size_t i_size, - void* i_data) -{ - TRACDCOMP( g_trac_pnor, "PnorDD::bufferedSfcWrite> i_addr=0x%.8x, i_size=0x%.8x", - i_addr, i_size ); - - errlHndl_t l_err = NULL; - errlHndl_t original_err = NULL; - uint8_t retry = 0; - - // reset class variable since we're starting a new operation - iv_error_recovery_failed = false; - - /***********************************************************/ - /* This do-while loop supports retries */ - /***********************************************************/ - do { - switch(iv_mode) - { - case MODEL_REAL_CMD: - case MODEL_REAL_MMIO: - { - // Note: MODEL_REAL_CMD or MODEL_REAL_MMIO both used command - // based writes, thus the common code path here - - // Command based reads are buffered 256 bytes at a time. - uint32_t chunk_size = 0; - uint64_t addr = i_addr; - uint64_t end_addr = i_addr + i_size; - - while(addr < end_addr) - { - chunk_size = SFC_CMDBUF_SIZE; - if( (addr + SFC_CMDBUF_SIZE) > end_addr) - { - chunk_size = end_addr - addr; - } - - //write data to SFC CMD Buffer via MMIO - l_err = writeSfcBuffer(chunk_size, - (void*)((uint64_t)i_data + (addr-i_addr))); - if(l_err) { break;} - - //Fetch bits into SFC CMD Buffer - l_err = flushSfcBuf(addr, chunk_size); - if(l_err) { break;} - - addr += chunk_size; - } - break; - } - case MODEL_LPC_MEM: - { - write_fake_pnor( i_addr, - i_data, - i_size ); - break; - } - default: - { - TRACFCOMP(g_trac_pnor, ERR_MRK"PnorDD::bufferedSfcWrite> Unsupported mode: iv_mode=0x%.16X, i_addr=0x%.8X. Calling doShutdown(PNOR::RC_UNSUPPORTED_MODE)", - iv_mode, i_addr); - - //Can't function without PNOR, initiate shutdown. - INITSERVICE::doShutdown( PNOR::RC_UNSUPPORTED_MODE); - } - } // end of switch statement - - // end of operation - check for retry - } while ( shouldRetry(RETRY_bufferedSfcWrite, l_err, original_err, retry) ); - - return l_err; -} - - -/** - * @brief Read data in SFC Command buffer and put into buffer - */ -errlHndl_t PnorDD::readSfcBuffer(size_t i_size, - void* o_data) -{ - errlHndl_t l_err = NULL; - TRACDCOMP( g_trac_pnor, "PnorDD::readSfcBuffer> i_size=0x%.8x", - i_size ); - - // SFC Command Buffer is accessed 32-bits at a time - uint32_t* word_ptr = static_cast(o_data); - uint32_t word_size = (ALIGN_4(i_size))/4; - for( uint32_t words_read = 0; - words_read < word_size; - words_read ++ ) - { - l_err = readRegSfc(SFC_CMDBUF_SPACE, - words_read*4, //Offset into CMD BUFF space in bytes - word_ptr[words_read]); - TRACDCOMP( g_trac_pnor, "PnorDD::readSfcBuffer: Read offset=0x%.8x, data_read=0x%.8x", - words_read*4, word_ptr[words_read] ); - - if( l_err ) { break; } - } - - return l_err; -} - -/** - * @brief Write data to SFC Command buffer - */ -errlHndl_t PnorDD::writeSfcBuffer(size_t i_size, - void* i_data) -{ - errlHndl_t l_err = NULL; - TRACDCOMP( g_trac_pnor, "PnorDD::writeSfcBuffer> i_size=0x%.8x", - i_size ); - - // SFC Command Buffer is accessed 32-bits at a time - uint32_t* word_ptr = static_cast(i_data); - uint32_t word_size = i_size/4; - for( uint32_t words_read = 0; - words_read < word_size; - words_read ++ ) - { - l_err = writeRegSfc(SFC_CMDBUF_SPACE, - words_read*4, //Offset into CMD BUFF space in bytes - word_ptr[words_read]); - if( l_err ) { break; } - } - - return l_err; -} - -/** - * @brief Read an address from LPC space - */ -errlHndl_t PnorDD::readLPC(uint32_t i_addr, - uint32_t& o_data) -{ - size_t reg_size = sizeof(uint32_t); - errlHndl_t l_err = deviceOp( DeviceFW::READ, - iv_target, - &o_data, - reg_size, - DEVICE_LPC_ADDRESS(LPC::TRANS_ABS,i_addr) ); - return l_err; -} - -/** - * @brief Write an address from LPC space - */ -errlHndl_t PnorDD::writeLPC(uint32_t i_addr, - uint32_t i_data) -{ - size_t reg_size = sizeof(uint32_t); - errlHndl_t l_err = deviceOp( DeviceFW::WRITE, - iv_target, - &i_data, - reg_size, - DEVICE_LPC_ADDRESS(LPC::TRANS_ABS,i_addr) ); - TRACDCOMP(g_trac_pnor, "writeLPC> %.8X = %.8X", i_addr, i_data ); - - return l_err; -} - -/** - * @brief Compare the existing data in 1 erase block of the flash with - * the incoming data and write or erase as needed - */ -errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_blockStart, - uint32_t i_writeStart, - size_t i_bytesToWrite, - void* i_data) -{ - TRACDCOMP(g_trac_pnor,">>compareAndWriteBlock(0x%.8X,0x%.8X,0x%.8X)", i_blockStart, i_writeStart, i_bytesToWrite); - errlHndl_t l_err = NULL; - uint8_t* read_data = NULL; - - do { - - // remember any data we read so we don't have to reread it later - read_data = new uint8_t[iv_erasesize_bytes]; - - // remember if we need to erase the block or not - bool need_erase = false; - bool need_write = false; - - //STEP 1: Read data in PNOR for compares (only read section we want to write) - //read_start needs to be uint32* for bitwise word compares later - uint32_t* read_start = (uint32_t*)(read_data + i_writeStart-i_blockStart); - l_err = bufferedSfcRead(i_writeStart, - i_bytesToWrite, - (void*) read_start); - if( l_err ) { break; } - - //STEP 2: walk through the write data to see if we need to do an erase - const uint32_t wordsToWrite = i_bytesToWrite/4; - uint32_t* i_dataWord = (uint32_t*) i_data; - - for(uint32_t cword = 0; cword < wordsToWrite; cword++) - { - // look for any bits being changed (using XOR) - if(read_start[cword] ^ i_dataWord[cword] ) - { - need_write = true; - - //Can only write zeros to NOR, see if any bits changed from 0->1 - if( (~(read_start[cword])) & (i_dataWord[cword]) ) - { - need_erase = true; - - // skip comparing the rest of the block, - // just start writing it - break; - } - } - } - - if(need_write == false) - { - //No write actually needed, break out here - TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> NO Write Needed! Exiting Function"); - break; - } - - - //STEP 3: If the need to erase was detected, read out the rest of the Erase block - if(need_erase) - { - TRACFCOMP(g_trac_pnor,"compareAndWriteBlock> Need to perform Erase"); - //Get data before write section - if(i_writeStart > i_blockStart) - { - TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Reading beginning data i_blockStart=0x%.8x, readLen=0x%.8x", - i_blockStart, i_writeStart-i_blockStart); - l_err = bufferedSfcRead(i_blockStart, - i_writeStart-i_blockStart, - read_data); - if( l_err ) { break; } - } - - //Get data after write section - if((i_writeStart+i_bytesToWrite) < (i_blockStart + iv_erasesize_bytes)) - { - uint32_t tail_length = i_blockStart + iv_erasesize_bytes - (i_writeStart+i_bytesToWrite); - uint8_t* tail_buffer = read_data + i_writeStart-i_blockStart + i_bytesToWrite; - - TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Reading tail data. addr=0x%.8x, tail_length=0x%.8x", - i_writeStart+i_bytesToWrite, tail_length); - l_err = bufferedSfcRead(i_writeStart+i_bytesToWrite, - tail_length, - tail_buffer); - if( l_err ) { break; } - } - - // erase the flash - TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Calling eraseFlash:. i_blockStart=0x%.8x", i_blockStart); - l_err = eraseFlash( i_blockStart ); - if( l_err ) { break; } - - - //STEP 4: Write the data back out - - // need to write everything since we erased the block - // re-write data before new data to write - if(i_writeStart > i_blockStart) - { - TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Writing beginning data i_blockStart=0x%.8x, readLen=0x%.8x", - i_blockStart, i_writeStart-i_blockStart); - l_err = bufferedSfcWrite(i_blockStart, - i_writeStart-i_blockStart, - read_data); - if( l_err ) { break; } - } - - //Write data after new data to write - if((i_writeStart+i_bytesToWrite) < (i_blockStart + iv_erasesize_bytes)) - { - uint32_t tail_length = i_blockStart + iv_erasesize_bytes - (i_writeStart+i_bytesToWrite); - uint8_t* tail_buffer = read_data + i_writeStart-i_blockStart + i_bytesToWrite; - - TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Writing tail data. addr=0x%.8x, tail_length=0x%.8x", - i_writeStart+i_bytesToWrite, tail_length); - l_err = bufferedSfcWrite(i_writeStart+i_bytesToWrite, - tail_length, - tail_buffer); - if( l_err ) { break; } - } - - //Write the new data - always do this - l_err = bufferedSfcWrite(i_writeStart, - i_bytesToWrite, - i_data); - if( l_err ) { break; } - } - else // - { - //STEP 4 ALT: No erase needed, only write the parts that changed. - TRACFCOMP(g_trac_pnor,"compareAndWriteBlock> No erase, just writing"); - - for(uint32_t cword = 0; cword < wordsToWrite; cword++) - { - // look for any bits being changed (using XOR) - if(read_start[cword] ^ i_dataWord[cword] ) - { - //Write the new data - always do this - l_err = bufferedSfcWrite(i_writeStart + (cword*4), - 4, - &i_dataWord[cword]); - if( l_err ) { break; } - } - if( l_err ) { break; } - - } - } - - } while(0); - - if( read_data ) - { - delete[] read_data; - } - - TRACDCOMP(g_trac_pnor,"<>PnorDD::eraseFlash> Block 0x%.8X", i_address ); - - errlHndl_t l_err = NULL; - errlHndl_t original_err = NULL; - uint8_t retry = 0; - - do { - if( findEraseBlock(i_address) != i_address ) - { - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_ERASEFLASH - * @reasoncode PNOR::RC_INVALID_ADDRESS - * @userdata1 LPC Address - * @userdata2 Nearest Erase Boundary - * @devdesc PnorDD::eraseFlash> Address not on erase boundary - * @custdesc Firmware error accessing flash during IPL - */ - l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_ERASEFLASH, - PNOR::RC_INVALID_ADDRESS, - TWO_UINT32_TO_UINT64(0,i_address), - findEraseBlock(i_address), - true /*Add HB SW Callout*/ ); - l_err->collectTrace(PNOR_COMP_NAME); - break; - } - - /***********************************************************/ - /* Attempt erase multiple times */ - /***********************************************************/ - // reset class variable since we're starting a new operation - iv_error_recovery_failed = false; - - // This do-while loop supports retries - do { - - for(uint32_t idx = 0; idx < ERASE_COUNT_MAX; idx++ ) - { - if(iv_erases[idx].addr == i_address) - { - iv_erases[idx].count++; - TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Block 0x%.8X has %d erasures", - i_address, iv_erases[idx].count ); - break; - - } - //iv_erases is init to all 0xff, - // so can use ~0 to check for an unused position - else if(iv_erases[idx].addr == ~0u) - { - iv_erases[idx].addr = i_address; - iv_erases[idx].count = 1; - TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Block 0x%.8X has %d erasures", - i_address, iv_erases[idx].count ); - break; - } - else if( idx == (ERASE_COUNT_MAX - 1)) - { - TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Erase counter full! Block 0x%.8X Erased", - i_address ); - break; - } - } - - if( (MODEL_MEMCPY == iv_mode) || (MODEL_LPC_MEM == iv_mode)) - { - erase_fake_pnor( i_address, iv_erasesize_bytes ); - break; //all done - } - - else if(iv_nor_chipid != 0) - { - - // group this block together with do-while - do{ - TRACDCOMP(g_trac_pnor, "PnorDD::eraseFlash> Erasing flash for iv_nor_chipid=0x%.8x, iv_mode=0x%.8x", - iv_nor_chipid, iv_mode); - - //Write erase address to ADR reg - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_ADR, - i_address); - - if(l_err) { break; } - - //Issue Erase command - SfcCmdReg_t sfc_cmd; - sfc_cmd.opcode = SFC_OP_ERASM; - sfc_cmd.length = 0; //Not used for erase - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CMD, - sfc_cmd.data32); - if(l_err) { break; } - - //Poll for complete status - l_err = pollSfcOpComplete(); - if(l_err) { break; } - - //check for special Micron Flag Status reg - if(iv_hw_workaround & HWWK_MICRON_WRT_ERASE) - { - l_err = micronFlagStatus(); - if(l_err) { break; } - } - - }while(0); // group of commands - - } - else - { - TRACFCOMP(g_trac_pnor,"PnorDD::eraseFlash> Erase not supported for iv_nor_chipid=%d", - iv_nor_chipid ); - - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_ERASEFLASH - * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION - * @userdata1 NOR Chip ID - * @userdata2 LPC Address to erase - * @devdesc PnorDD::eraseFlash> No support for MODEL_REAL - */ - l_err = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_ERASEFLASH, - PNOR::RC_UNSUPPORTED_OPERATION, - static_cast(iv_nor_chipid), - i_address, - true /*Add HB SW Callout*/ ); - l_err->collectTrace(PNOR_COMP_NAME); - - // this error can't be fixed with a retry, so just break - if(l_err) { break; } - - } - - // end of operation - check for retry - } while ( shouldRetry(RETRY_eraseFlash, l_err, original_err, retry) ); - - if(l_err) { break;} - - } while(0); - - return l_err; -} - -/** - * @brief Read a user-defined Flash Register - */ -errlHndl_t PnorDD::readRegFlash( SfcCustomReg_t i_cmd, - uint32_t* o_data, - uint32_t i_addr ) -{ - errlHndl_t l_err = NULL; - - do - { - //Do a read of flash address zero to workaround - // a micron bug with extended reads - if( (HWWK_MICRON_EXT_READ & iv_hw_workaround) - && (i_cmd.length > 4) ) - { - l_err = loadSfcBuf( 0, 1 ); - if(l_err) { break; } - } - - //Change ChipID command - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CHIPIDCONF, - i_cmd.data32); - if(l_err) { break; } - - //Setup the address (ADR) if needed - if( i_cmd.needaddr ) - { - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_ADR, - i_addr); - if(l_err) { break; } - } - - //Issue (new) Get Chip ID command - SfcCmdReg_t sfc_cmd; - sfc_cmd.opcode = SFC_OP_CHIPID; - sfc_cmd.length = i_cmd.length; - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CMD, - sfc_cmd.data32); - if(l_err) { break; } - - //Poll for complete status - l_err = pollSfcOpComplete(); - if(l_err) { break; } - - //Go get the data - l_err = readSfcBuffer( i_cmd.length, - o_data ); + // actually do the erase + l_err = _eraseFlash(i_address); if(l_err) { break; } } while(0); @@ -2175,336 +682,11 @@ errlHndl_t PnorDD::readRegFlash( SfcCustomReg_t i_cmd, } -/* - This code is used in the MODEL_MEMCPY and MODEL_LPC_MEM modes -*/ -/** - * @brief Write to fake PNOR - */ -void PnorDD::write_fake_pnor( uint64_t i_pnorAddr, - void* i_buffer, - size_t i_size ) -{ - //create a pointer to the offset start. - char * destPtr = (char *)(iv_fakeStart+i_pnorAddr); - - if( (i_pnorAddr+i_size) > iv_fakeSize ) - { - TRACFCOMP(g_trac_pnor, - "PnorDD write_fake_pnor> Write goes past end of fake-PNOR, skipping write. i_pnorAddr=0x%X, i_size=0x%X", - i_pnorAddr, i_size ); - } - else - { - //copy data from memory into the buffer. - memcpy(destPtr, i_buffer, i_size); - } -} - -/** - * @brief Read from fake PNOR - */ -void PnorDD::read_fake_pnor( uint64_t i_pnorAddr, - void* o_buffer, - size_t i_size ) -{ - //create a pointer to the offset start. - char * srcPtr = (char *)(iv_fakeStart+i_pnorAddr); - - if( (i_pnorAddr+i_size) > iv_fakeSize ) - { - TRACFCOMP(g_trac_pnor, - "PnorDD read_fake_pnor> Read goes past end of fake-PNOR, skipping read. i_pnorAddr=0x%X, i_size=0x%X", - i_pnorAddr, i_size ); - } - else - { - //copy data from memory into the buffer. - memcpy(o_buffer, srcPtr, i_size); - } - -} /** - * @brief Erase chunk of fake PNOR + * @brief Returns if an operation should be retried and handles + * the error logs */ -void PnorDD::erase_fake_pnor( uint64_t i_pnorAddr, - size_t i_size ) -{ - //create a pointer to the offset start. - char * srcPtr = (char *)(iv_fakeStart+i_pnorAddr); - - if( (i_pnorAddr+i_size) > iv_fakeSize ) - { - TRACFCOMP(g_trac_pnor, - "PnorDD erase_fake_pnor> Erase goes past end of fake-PNOR, skipping erase. i_pnorAddr=0x%X, i_size=0x%X", - i_pnorAddr, i_size ); - } - else - { - //Zero out memory - memset( srcPtr, 0, i_size ); - } -} - - - -errlHndl_t PnorDD::resetPnor( ResetLevels i_pnorResetLevel ) -{ - errlHndl_t l_err = NULL; - - // check iv_reset_active to avoid infinite loops - // and don't reset if in the middle of FFDC collection - if ( ( iv_reset_active == false ) && - ( iv_ffdc_active == false ) ) - { - iv_reset_active = true; - - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> i_pnorResetLevel=0x%.8X", i_pnorResetLevel); - - do { -#if 0 - // 32 bits for address and data for LPC operations - uint32_t lpc_addr=0; - uint32_t lpc_data=0; -#endif - - // To Avoid Infinite Loop - skip recovery if it already failed - if ( iv_error_recovery_failed == true ) - { - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Recovery Previously Failed (%d). Skipping reset to avoid infinite loop", iv_error_recovery_failed); - break; - } - - /***************************************/ - /* Handle the different reset levels */ - /***************************************/ - switch(i_pnorResetLevel) - { - case RESET_CLEAR: - {// Nothing to do here, so just break - break; - } - - //@fixme RTC:109859 -- All SFC recovery should move into SFC classes - case RESET_LPC_SLAVE: - { - // @todo RTC 109999 - Skipping because SFC resets can - // cause problems on subsequent reads and writes - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_LPC_SLAVE"); - -#if 0 - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bit0 of LPC_SLAVE_REG_RESET to reset LPC Slave Logic"); - lpc_addr = LPC_SLAVE_REG_RESET; - lpc_data = 0x80000000; - - l_err = writeLPC(lpc_addr, lpc_data); -#endif - break; - } - - //@fixme RTC:109859 -- All SFC recovery should move into SFC classes - case RESET_LPC_SLAVE_ERRS: - { - // @todo RTC 109999 - Skipping because SFC resets can - // cause problems on subsequent reads and writes - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_LPC_SLAVE_ERRS"); -#if 0 - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bit1 of LPC_SLAVE_REG_RESET to reset LPC Slave Errors"); - lpc_addr = LPC_SLAVE_REG_RESET; - lpc_data = 0x40000000; - - l_err = writeLPC(lpc_addr, lpc_data); -#endif - break; - } - - - //@fixme RTC:109859 -- All SFC recovery should move into SFC classes - case RESET_SFC_LOCAL_BUS: - { - // @todo RTC 109999 - Skipping because SFC resets can - // cause problems on subsequent reads and writes - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_SFC_LPC_LOCAL_BUS"); -#if 0 - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bit2 of LPC_SLAVE_REG_RESET to reset Local SFC Bus. Requires PNOR reinitialization"); - lpc_addr = LPC_SLAVE_REG_RESET; - lpc_data = 0x20000000; - - l_err = writeLPC(lpc_addr, lpc_data); - - if (l_err) { break; } - - l_err = PnorDD::reinitializeSfc(); -#endif - break; - } - - //@fixme RTC:109859 -- All SFC recovery should move into SFC classes - case RESET_SFCBUS_LPCSLAVE_ERRS: - { - // @todo RTC 109999 - Skipping because SFC resets can - // cause problems on subsequent reads and writes - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Skipping RESET_SFCBUS_LPCSLAVE_ERRS"); -#if 0 - // Must handle both errors - TRACFCOMP(g_trac_pnor, "PnorDD::resetPnor> Writing bits1,2 of LPC_SLAVE_REG_RESET to reset LPC Slave Errors and Local SFC Bus. Requires PNOR reinitialization"); - lpc_addr = LPC_SLAVE_REG_RESET; - lpc_data = 0x60000000; - - l_err = writeLPC(lpc_addr, lpc_data); - - if (l_err) { break; } - - l_err = PnorDD::reinitializeSfc(); -#endif - break; - } - - // else - unsupported reset level - default: - { - - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::resetPnor> Unsupported Reset Level Passed In: 0x%X", i_pnorResetLevel); - - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_RESETPNOR - * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION - * @userdata1 Unsupported Reset Level Parameter - * @userdata2 - * @devdesc PnorDD::resetPnor> Unsupported Reset Level - * requested - */ - l_err = new ERRORLOG::ErrlEntry( - ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_RESETPNOR, - PNOR::RC_UNSUPPORTED_OPERATION, - i_pnorResetLevel, - 0, - true /*SW error*/); - - l_err->collectTrace(PNOR_COMP_NAME); - break; - } - }// end switch - - if ( l_err ) - { - // Indicate that we weren't successful in resetting PNOR - iv_error_recovery_failed = true; - TRACFCOMP( g_trac_pnor,ERR_MRK"PnorDD::resetPnor>> Fail doing PNOR reset at level 0x%X (recovery count=%d): eid=0x%X", i_pnorResetLevel, iv_error_handled_count, l_err->eid()); - } - else - { - // Successful, so increment recovery count - iv_error_handled_count++; - - TRACFCOMP( g_trac_pnor,INFO_MRK"PnorDD::resetPnor>> Successful PNOR reset at level 0x%X (recovery count=%d)", i_pnorResetLevel, iv_error_handled_count); - } - - - } while(0); - - // reset RESET active flag - iv_reset_active = false; - } - - return l_err; -} - - - -//@fixme RTC:109859 -- All SFC recovery should move into SFC classes -errlHndl_t PnorDD::reinitializeSfc( void ) -{ - TRACFCOMP(g_trac_pnor, "PnorDD::reinitializeSfc>"); - - errlHndl_t l_err = NULL; - - //Initial configuration settings for SFC: - #define oadrnb_init 0x0C000000 //Set MMIO/Direct window to start at 64MB - #define oadrns_init 0x0000000F //Set the MMIO/Direct window size to 64MB - #define adrcbf_init 0x00000000 //Set the flash index to 0 - #define adrcmf_init 0x0000000F //Set the flash size to 64MB - #define conf_init 0x00000002 //Disable Direct Access Cache - - do { - -#ifdef CONFIG_BMC_DOES_SFC_INIT - - TRACFCOMP( g_trac_pnor, ERR_MRK"PnorDD::reinitializeSfc> Need to Re-Initialize SFC. Calling doShutdown(PNOR::RC_REINITIALIZE_SFC)"); - - l_err = NULL; // to avoid compiler warnings - - //Shutdown if SFC needs to be re-initialized - INITSERVICE::doShutdown( PNOR::RC_REINITIALIZE_SFC); - -#else - - - // Clear member variable in case we don't successfully - // reinitialize PNOR - iv_sfcInitDone = false; - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_OADRNB, - oadrnb_init); - if(l_err) { break; } - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_OADRNS, - oadrns_init); - if(l_err) { break; } - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_ADRCBF, - adrcbf_init); - if(l_err) { break; } - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_ADRCMF, - adrcmf_init); - if(l_err) { break; } - - l_err = writeRegSfc(SFC_CMD_SPACE, - SFC_REG_CONF, - conf_init); - if(l_err) { break; } - - - //Determine NOR Flash type - triggers vendor specific workarounds - //We also use the chipID in some FFDC situations. - l_err = getNORChipId(iv_nor_chipid); - if(l_err) { break; } - - TRACFCOMP(g_trac_pnor, - "PnorDD::reinitializeSfc> iv_nor_chipid=0x%.8x> ", - iv_nor_chipid ); - - l_err = readRegSfc(SFC_CMD_SPACE, - SFC_REG_ERASMS, - iv_erasesize_bytes); - if(l_err) { break; } - - // Good path if you made it here: reset class variable - iv_sfcInitDone = true; - -#endif - - } while(0); - - if (l_err) - { - TRACFCOMP( g_trac_pnor,INFO_MRK"PnorDD::reinitializeSfc> SFC reinitialization FAILED - l_err rc=0x%X, eid=%d", l_err->reasonCode(), l_err->eid()); - iv_error_recovery_failed = true; - - } - - return l_err; -} - - bool PnorDD::shouldRetry( RetryOp i_op, errlHndl_t& io_err, errlHndl_t& io_original_err, @@ -2514,10 +696,9 @@ bool PnorDD::shouldRetry( RetryOp i_op, bool should_retry = false; - if ( ( io_err == NULL ) || ( iv_error_recovery_failed == true ) ) + if ( io_err == NULL ) { - // Either Operation was successful OR error recovery failed - - // either way don't retry + // Operation was successful so don't retry should_retry = false; // Error logs handled below @@ -2604,3 +785,83 @@ bool PnorDD::shouldRetry( RetryOp i_op, return should_retry; } + +/** + * @brief Calls the SFC to perform a PNOR Write Operation + */ +errlHndl_t PnorDD::_writeFlash( uint32_t i_addr, + size_t i_size, + void* i_data ) +{ + TRACDCOMP(g_trac_pnor, ENTER_MRK"PnorDD::_writeFlash(i_addr=0x%.8X)> ", i_addr); + errlHndl_t l_err = NULL; + errlHndl_t original_err = NULL; + uint8_t retry = 0; + + do + { + // Call over to the SFC code to do the actual write + l_err = iv_sfc->writeFlash( i_addr, i_size, i_data ); + + // end of operation - check for retry + } while( shouldRetry(RETRY_writeFlash, l_err, original_err, retry) ); + + if( l_err ) + { + l_err->collectTrace(PNOR_COMP_NAME); + } + + return l_err; +} + +/** + * @brief Calls the SFC to perform a PNOR Read Operation + */ +errlHndl_t PnorDD::_readFlash( uint32_t i_addr, + size_t i_size, + void* o_data ) +{ + //TRACDCOMP(g_trac_pnor, "PnorDD::_readFlash(i_address=0x%.8X)> ", i_addr); + errlHndl_t l_err = NULL; + errlHndl_t original_err = NULL; + uint8_t retry = 0; + + do{ + //Send command over to the flash controller to do the work + l_err = iv_sfc->readFlash(i_addr, i_size, o_data); + + // end of operation - check for retry + } while( shouldRetry(RETRY_readFlash, l_err, original_err, retry) ); + + if( l_err ) + { + l_err->collectTrace(PNOR_COMP_NAME); + } + + return l_err; +} + +/** + * @brief Calls the SFC to perform a PNOR Read Operation + */ +errlHndl_t PnorDD::_eraseFlash( uint32_t i_addr ) +{ + TRACDCOMP(g_trac_pnor, "PnorDD::_eraseFlash(i_address=0x%.8X)> ", i_addr); + errlHndl_t l_err = NULL; + errlHndl_t original_err = NULL; + uint8_t retry = 0; + + do{ + //Send command over to the flash controller to do the work + l_err = iv_sfc->eraseFlash(i_addr); + + // end of operation - check for retry + } while( shouldRetry(RETRY_eraseFlash, l_err, original_err, retry) ); + + if( l_err ) + { + l_err->collectTrace(PNOR_COMP_NAME); + } + + return l_err; +} diff --git a/src/usr/pnor/pnordd.H b/src/usr/pnor/pnordd.H index 31b3d8d7d..4b086e36b 100644 --- a/src/usr/pnor/pnordd.H +++ b/src/usr/pnor/pnordd.H @@ -29,6 +29,7 @@ #include #include namespace PNOR { class UdPnorDDParms; } +class SfcDD; /** @file pnordd.H * @brief Provides the interfaces to the PNOR Device Driver @@ -42,13 +43,6 @@ class PnorDD { public: - /** - * @brief Initializer called by PnorRP::init() to init the SFC - * - * @return void - */ - void sfcInit( ); - /** * @brief Performs a PNOR Read Operation * @@ -100,16 +94,10 @@ class PnorDD /** * @brief Constructor * - * @parm i_mode o_buffer Buffer to read data into - * @parm i_fakeStart Input: Number of bytes to read, - * @parm i_fakeSize Output: Number of bytes actually read * @parm i_target Processor Target connected to PNOR - * NOTE: i_target can only be used when targetting is loaded + * NOTE: i_target can only be used after targeting is loaded */ - PnorDD( PnorMode_t i_mode = MODEL_UNKNOWN, - uint64_t i_fakeStart = 0, - uint64_t i_fakeSize = 0, - TARGETING::Target* i_target = NULL ); + PnorDD( TARGETING::Target* i_target = NULL ); /** @@ -125,431 +113,19 @@ class PnorDD uint32_t count; /**< Num Erases of block */ }; - /** - * @brief SPI Config Info - * OP Codes and other MISC info for configuring SFC - */ - enum SpiConfigInfo { - SPI_GET_CHIPID_OP = 0x9F, /**< Default Op code for getting NOR ChipID */ - SPI_START4BA = 0x37, /**< Enable Macronix 4-Byte addressing */ - - /* - * Micron Flash Commands - */ - SPI_MICRON_FLAG_STAT = 0x70, /**< Check write/erase complete */ - SPI_MICRON_CLRFLAG_STAT = 0x50, /**< Clear write/erase Status reg */ - SPI_MICRON_READ_SFDP = 0x5A, /**< Read Serial Flash Disc Parms */ - SPI_MICRON_CHIPID = 0x9F, /**< Read ChipID */ - - /* SPI protocol command bytes */ - SPI_JEDEC_FAST_READ = 0x0B, - SPI_JEDEC_SECTOR_ERASE = 0x20, - SPI_JEDEC_BLOCK_ERASE = 0xD8, - - SPI_SIM_SM_ERASE_OP = 0x00000020, /**< Simics Op Code for Small Erase */ - SPI_SIM_SM_ERASE_SZ = 0x1000, /**< Simics Small Erase Size */ - }; - - /** - * @brief Supported NOR Chip IDs - * - */ - enum NorChipIDs - { - /* Note: Simics currently models Micron NOR */ - UNKNOWN_NOR_ID = 0, /**< Unknown NOR chip ID */ - MICRON_NOR_ID = 0x20ba2000, /**< Micron NOR chip ID */ - VPO_NOR_ID = 0x20201800, /**< VPO NOR chip ID */ - MACRONIX_NOR_ID = 0xC2201A00, /**< Macronix NOR chip ID */ - - ID_MASK = 0xFFFFFF00, /**< Only look at 3 bytes */ - }; - - - enum SfcRange { - SFC_CMD_SPACE, /**< Indicate accessing command reg */ - SFC_CMDBUF_SPACE, /**< Indicate accessing command buffer space */ - SFC_MMIO_SPACE, /**< Indicate accessing MMIO based Direct Reads */ - SFC_LPC_SPACE, /**< Indicate LPC Slave Space */ - }; - - /** - * @brief SFC Registers - * These are offsets within the SFC Register Space - */ - enum SfcRegAddr { - SFC_REG_CONF = 0x10, /**< CONF: Direct Access Configuration */ - SFC_REG_STATUS = 0x0C, /**< STATUS : Status Reg */ - SFC_REG_SPICLK = 0x3C, /**< SPICLK: SPI clock rate config */ - SFC_REG_CMD = 0x40, /**< CMD : Command */ - SFC_REG_ADR = 0x44, /**< ADR : Address */ - SFC_REG_ERASMS = 0x48, /**< ERASMS : Small Erase Block Size */ - SFC_REG_ERASLGS = 0x4C, /**< ERALGS : Large Erase Block Size */ - SFC_REG_CONF4 = 0x54, /**< CONF4 : SPI Op Code for Small Erase */ - SFC_REG_CONF5 = 0x58, /**< CONF5 : Small Erase Size config reg */ - SFC_REG_CONF8 = 0x64, /**< CONF8 : Read Command */ - SFC_REG_ADRCBF = 0x80, /**< ADRCBF : First Intf NOR Addr Offset */ - SFC_REG_ADRCMF = 0x84, /**< ADRCMF : First Intf NOR Allocation */ - SFC_REG_ADRCBS = 0x88, /**< ADRCBS : Second Intf NOR Addr Offset */ - SFC_REG_ADRCMS = 0x8C, /**< ADRCMS : Second Intf NOR Allocation */ - SFC_REG_OADRNB = 0x90, /**< OADRNB : Direct Access OBP Window Base Address */ - SFC_REG_OADRNS = 0x94, /**< OADRNS : DIrect Access OPB Window Size */ - SFC_REG_CHIPIDCONF = 0x9C, /**< CHIPIDCONF : config ChipId CMD */ - SFC_REG_ERRCONF = 0x6C, /**< ERRCONF : Configures error counts that - cause interupts */ - SFC_REG_ERRTAG = 0x1C, /**< ERRTAG : Holds Control Info of Error */ - SFC_REG_ERROFF = 0x20, /**< ERROFF : Holds Address Info of Error */ - SFC_REG_ERRSYN = 0x24, /**< ERRSYN : Holds Syndrome That Caused Error*/ - SFC_REG_ERRDATH = 0x28, /**< ERRDATH : Holds Most Signifcant Word of - Double Word That Caused Error */ - SFC_REG_ERRDATL = 0x2C, /**< ERRDATL : Holds Least Signifcant Word of - Double Word That Caused Error */ - SFC_REG_ERRCNT = 0x30, /**< ERRCNT : Counts The Number Of Errors */ - SFC_REG_CLRCNT = 0x34, /**< CLRCNT : Which Bits To Clear In ERRCNT */ - SFC_REG_ERRINJ = 0x38, /**< ERRINJ : Force Errors Into Read Paths */ - SFC_REG_PROTA = 0x70, /**< PROTA : Write Protect Range Address Base */ - SFC_REG_PROTM = 0x74, /**< PROTM : Write Protect Range Size */ - SFC_REG_ECCADDR = 0x78, /**< ECCADDR : ECC Disable Range Base Address */ - SFC_REG_ECCRNG = 0x7C, /**< ECCRNG : ECC Disable Range Size */ - SFC_REG_ERRORS = 0x00, /**< ERRORS : Collection of Error Status Bits */ - SFC_REG_INTMSK = 0x04, /**< INTMSK : Record of Events That Could Lead - To Interupt */ - SFC_REG_INTENM = 0x14, /**< INTENM : Controls Which Events Lead - To Interupts */ - SFC_REG_CONF2 = 0x18, /**< CONF2 : SPI Configuration */ - SFC_REG_CONF3 = 0x50, /**< CONF3 : SPI Recovery */ - - }; - - /** - * @brief SFC Op Codes - * OP Codes for the SFC Command Register - */ - enum SfcOpCodes { - SFC_OP_READRAW = 0x03, /**< Read Raw */ - SFC_OP_WRITERAW = 0x02, /**< Write Raw */ - SFC_OP_ERASM = 0x32, /**< Erase Small */ - SFC_OP_ERALG = 0x34, /**< Erase Large */ - SFC_OP_ENWRITPROT = 0x53, /**< Enable WRite Protect */ - SFC_OP_CHIPID = 0x1F, /**< Get Chip ID */ - SFC_OP_STATUS = 0x05, /**< Get Status */ - SFC_OP_TURNOFF = 0x5E, /**< Turn Off */ - SFC_OP_TURNON = 0x50, /**< Turn On */ - SFC_OP_ABORT = 0x6F, /**< Super-Abort */ - SFC_OP_START4BA = 0x37, /**< Start 4BA */ - SFC_OP_END4BA = 0x69, /**< End 4BA */ - SFC_OP_INVALID = 0x00, /**< Invalid - used for testing */ - }; - - - enum { - SFC_CMDBUF_SIZE = 256, /**< SFC Command buffer is - 0x100/256 bytes/0x40 words */ - - //@todo-RTC:95125 Find out Max time to wait*/ - SFC_POLL_TIME_NS = 1000000000, /**< Max time to wait for SFC Op */ - SFC_POLL_INCR_NS = 10, /**< minimum increment during poll */ - - }; - - - /** - * Flags used to trigger Hardware workarounds - */ - enum { - // Must perform 'read flag status' commands after - // any write or erase - HWWK_MICRON_WRT_ERASE = 0x00000001, - - // Must do a read of a low flash address before issuing read - // commands that return more than 1 word of data - HWWK_MICRON_EXT_READ = 0x00000002, - }; - - /** - * Enums for different levels of resetting PNOR communication levels - */ - enum ResetLevels { - RESET_CLEAR = 0x00000000, /**< Clear Reset Level */ - RESET_LPC_SLAVE = 0x00000008, /**< LPC Slave Logic on SFC */ - RESET_LPC_SLAVE_ERRS = 0x00000010, /**< LPC Slave Errors on SFC */ - RESET_SFC_LOCAL_BUS = 0x00000020, /**< SFC Local Bus */ - // Known possible combination: - RESET_SFCBUS_LPCSLAVE_ERRS = 0x00000030, /**< Bus and LPC Slave Errs */ - }; - /** * Enums for different operations that might be re-tried */ - enum RetryOp { - RETRY_NOOP = 0, - RETRY_getNORChipId = 1, - RETRY_bufferedSfcRead = 2, - RETRY_bufferedSfcWrite = 3, - RETRY_eraseFlash = 4, - }; - - - /** - * @brief SFC Command Register Layout - */ - union SfcCmdReg_t - { - uint32_t data32; - struct - { - uint32_t reserved : 16; /**< 0:15 = Reserved */ - uint32_t opcode : 7; /**< 16:22 = OpCode */ - uint32_t length : 9; /**< 22:31 = Num bytes for Read/Write Raw */ - }; - SfcCmdReg_t() : data32(0) {}; - }; - - /** - * @brief SFC Status Register Layout - */ - union SfcStatReg_t - { - uint32_t data32; - struct - { - uint32_t unused : 20; /**< 0:19 = Not Currently Used */ - uint32_t eccerrcntr : 1; /**< 20 Threshold for SRAM ECC errors */ - uint32_t eccues : 1; /**< 21 SRAM cmd uncorrectable ECC error*/ - uint32_t unused_22 : 3; /**< 22:24 = Not Currently Used */ - uint32_t cmdexe : 1; /**< 25 Previous cmd is in progress */ - uint32_t cmdwait : 1; /**< 26 Previous cmd waiting to execute */ - uint32_t illegal : 1; /**< 27 Previous op illegal */ - uint32_t eccerrcntn : 1; /**< 28 Threshold for Flash ECC errors */ - uint32_t eccuen : 1; /**< 29 Flash cmd uncorrectable ECC err */ - uint32_t timeout : 1; /**< 30 Timeout */ - uint32_t done : 1; /**< 31 Done */ - }; - SfcStatReg_t() : data32(0) {}; - }; - - /** - * @brief LPC Slave Registers - * These are offsets within the LPC Slave Register Space - */ - enum LpcSlaveRegAddr { - LPC_SLAVE_REG_STATUS = 0x14, /**< STATUS: read-only */ - LPC_SLAVE_REG_RESET = 0x14, /**< RESET : write-only */ - }; - - /** - * @brief LPC Slave Status Register Layout - */ - union LpcSlaveStatReg_t - { - uint32_t data32; - struct - { - uint32_t lbusowner : 2; /**< 0:1 = Local Bus Owner */ - uint32_t lbusparityerror : 1; /**< 2 = Local Bus Parity Error */ - uint32_t lbus2opberr : 3; /**< 3:5 = Errors From LBUS2OPB */ - uint32_t unused : 26; /**< 6:21 = Not Currently Used */ - }; - LpcSlaveStatReg_t() : data32(0) {}; - }; - - /** - * @brief LPC Slave Reset Register Layout - */ - union LpcSlaveResetReg_t - { - uint32_t data32; - struct - { - uint32_t lpcslave : 1; /**< 0 Reset LPC Slave */ - uint32_t lpcslaveerrs : 1; /**< 1 Reset LPC Slave Errors */ - uint32_t localbus : 1; /**< 2 Reset Local Bus */ - uint32_t unused : 29; /**< 4:31 = Not Currently Used */ - }; - LpcSlaveResetReg_t() : data32(0) {}; - }; - - - /** - * @brief LPC Slave LBUS2OPB Errors - * Translation of LPC Slave Status Register Bits 3:5 - */ - enum LpcSlaveLbus2OpbErrors { - LBUS2OPB_ADDR_PARITY_ERR = 0b010, /**< Address Parity Error */ - LBUS2OPB_INVALID_SELECT_ERR = 0b001, /**< Invalid Select Error */ - LBUS2OPB_DATA_PARITY_ERR = 0b011, /**< Data Parity Error */ - LBUS2OPB_MONITOR_ERR = 0b100, /**< Monitor Error */ - LBUS2OPB_TIMEOUT_ERR = 0b101, /**< Timeout Error */ - }; - - /** - * @brief SFC ConfId Register Layout - */ - union SfcCustomReg_t + enum RetryOp { - uint32_t data32; - struct - { - uint32_t opcode : 8; - uint32_t read : 1; - uint32_t write : 1; - uint32_t needaddr : 1; - uint32_t clocks : 5; - uint32_t reserved : 8; - uint32_t length : 8; - }; - SfcCustomReg_t() : data32(0) {}; + RETRY_NOOP = 0, + RETRY_writeFlash = 1, + RETRY_readFlash = 2, + RETRY_eraseFlash = 3, }; - /** - * @brief Write a SFC Register - * - * @parm i_range SFC Address Range - * @parm i_addr SFC Register to write - * @parm i_data Data to write - * - * @return Error from operation - */ - errlHndl_t writeRegSfc(SfcRange i_range, - uint32_t i_addr, - uint32_t i_data); - - /** - * @brief Read a SFC Register - * - * @parm i_range SFC Address Range - * @parm i_addr SFC Register to read - * @parm o_data Data to write - * - * @return Error from operation - */ - errlHndl_t readRegSfc(SfcRange i_range, - uint32_t i_addr, - uint32_t& o_data); - - /** - * @brief Read a user-defined Flash Register - * - * @parm i_cmd Custom command - * @parm o_data Data being read - * @parm i_addr Address to use with command - * - * @return Error from operation - */ - errlHndl_t readRegFlash(SfcCustomReg_t i_cmd, - uint32_t* o_data, - uint32_t i_addr = 0); - - - /** - * @brief Poll for SFC Op Complete - * - * @parm i_pollTime Amount of time to Poll, default SFC_POLL_TIME_NS - * - * @return Error from operation - */ - errlHndl_t pollSfcOpComplete(uint64_t i_pollTime = SFC_POLL_TIME_NS); - - /** - * @brief Check flag status bit on Micron NOR chips - * The current version of Micron parts require the Flag - * Status register be read after a read or erase operation, - * otherwise all future operations won't work.. - * - * @parm i_pollTime Amount of time to Poll, default SFC_POLL_TIME_NS - * - * @return Error from operation - */ - errlHndl_t micronFlagStatus(uint64_t i_pollTime = SFC_POLL_TIME_NS); - - /** - * @brief Read the NOR FLash ChipID - * - * @parm o_chipId NOR Flash ChipID - * @parm i_spiOpcode SPI OpCode to use to get Chip ID - * - * @return Error from operation - */ - errlHndl_t getNORChipId(uint32_t& o_chipId, - uint32_t i_spiOpcode = SPI_GET_CHIPID_OP); - - /** - * @brief Load SFC command buffer with data from PNOR - * - * @parm i_addr PNOR flash Address to read - * @parm i_size Number of bytes to read.to command buffer - * - * @return Error from operation - */ - errlHndl_t loadSfcBuf(uint32_t i_addr, - size_t i_size); - - /** - * @brief Flush SFC command buffer contents to PNOR Flash - * - * @parm i_addr PNOR flash Address to write - * @parm i_size Number of bytes to write.to command buffer - * - * @return Error from operation - */ - errlHndl_t flushSfcBuf(uint32_t i_addr, - size_t i_size); - - /** - * @brief Read data in SFC Command buffer and put into buffer - * - * @parm i_size Amount of data in Cmd Buffer to read, in bytes. - * @parm o_data Buffer to read data into - * - * @return Error from operation - */ - errlHndl_t readSfcBuffer(size_t i_size, - void* o_data); - - /** - * @brief Write data to SFC Command buffer - * - * @parm i_size Amount of data in Cmd Buffer to write, in bytes. - * @parm o_data Buffer to read data from - * - * @return Error from operation - */ - errlHndl_t writeSfcBuffer(size_t i_size, - void* i_data); - - - /** - * @brief Perform command based read of PNOR, maximizing use of - * SFC Command buffer.. - * - * @parm i_addr PNOR flash Address to read - * @parm i_size Amount of data to read, in bytes. - * @parm o_data Buffer to read data into - * - * @return Error from operation - */ - errlHndl_t bufferedSfcRead(uint32_t i_addr, - size_t i_size, - void* o_data); - - /** - * @brief Perform command based write of PNOR, maximizing use of - * SFC Command buffer.. - * - * @parm i_addr PNOR flash Address to write - * @parm i_size Amount of data to write, in bytes. - * @parm i_data Buffer containing data to write - * - * @return Error from operation - */ - errlHndl_t bufferedSfcWrite(uint32_t i_addr, - size_t i_size, - void* i_data); - - /** * @brief Some general constants - * */ enum { LPCHC_FW_SPACE = 0xF0000000, /**< LPC Host Controller FW Space */ @@ -559,11 +135,6 @@ class PnorDD LPC_SFC_CMDREG_OFFSET = 0xF0000C00, /** LPC Offest to SFC Cmd Regs */ LPC_SFC_CMDBUF_OFFSET = 0xF0000D00, /** LPC Off to SFC Cmd Buf space */ -#ifdef CONFIG_SFC_IS_AST2400 - LPC_SFC_MMIO_OFFSET = 0xFE000000, /** LPC Off to SFC Direct Read space*/ -#else //default to IBM DPSS controller - LPC_SFC_MMIO_OFFSET = 0xFC000000, /** LPC Off to SFC Direct Read space*/ -#endif LPC_TOP_OF_FLASH_OFFSET = 0xFFFFFFFF, ECCB_STAT_REG = 0x000B0022, /**< ECCB Status Reg (FW) */ @@ -612,6 +183,7 @@ class PnorDD /** * @brief Erase a block of flash + * @pre Mutex should already be locked before calling * * @parm i_address Offset into flash to erase, aligned to erase block * @@ -622,6 +194,7 @@ class PnorDD /** * @brief Compare the existing data in 1 erase block of the flash with * the incoming data and write or erase as needed + * @pre Mutex should already be locked before calling * * @parm i_blockStart Start of Erase Block we're writing to * @parm i_writeStart Starting address where we want to write data. @@ -644,7 +217,7 @@ class PnorDD */ uint32_t findEraseBlock(uint32_t i_address) { - return (i_address - i_address%iv_erasesize_bytes); + return (i_address - i_address%iv_eraseSizeBytes); }; /** @@ -664,87 +237,11 @@ class PnorDD while( findEraseBlock(addr) < (i_address+i_byteSize) ) { blocks++; - addr += iv_erasesize_bytes; + addr += iv_eraseSizeBytes; } return blocks; }; - // These are used to cheat and use a chunk of our cache as a PNOR - // iv_mode == MODEL_MEMCPY,MODEL_LPC_MEM - /** - * @brief write to fake PNOR - * - * @parm i_pnorAddr Offset into fake PNOR - * @parm i_buffer Buffer of data to write - * @param i_size Amount to write. - */ - void write_fake_pnor( uint64_t i_pnorAddr, - void* i_buffer, size_t i_size ); - - /** - * @brief Read from fake PNOR - * - * @parm i_pnorAddr Offset into fake PNOR - * @parm i_buffer Buffer to return read data - * @param i_size Amount to read. - */ - void read_fake_pnor( uint64_t i_pnorAddr, - void* o_buffer, - size_t i_size ); - - /** - * @brief Erase fake PNOR - * - * @parm i_pnorAddr Offset to start erase - * @param i_size Amount to erase. - */ - void erase_fake_pnor( uint64_t i_pnorAddr, - size_t i_size ); - - /** - * @brief Check For Errors in SFC Status Registers - * - * @parm o_pnorResetLevel if error, reset level to clear error - * @return Error log if error found - */ - errlHndl_t checkForSfcErrors( ResetLevels &o_pnorResetLevel ); - - - /** - * @brief Check For Errors in OPB and LPCHC Status Registers - * - * @parm o_pnorResetLevel if error, reset level to clear error - * @return Error log if error found - */ - errlHndl_t checkForOpbErrors( ResetLevels &o_pnorResetLevel ); - - - /** - * @brief Add FFDC Error Registers to an existing Error Log - * - * @parm io_errl Error Log To Add FFDC To - * - */ - void addFFDCRegisters(errlHndl_t & io_errl); - - /** - * @brief Reset PNOR Logic At The Specified Level - * - * @parm i_pnorResetLevel Level of PNOR to Reset - * - * @return Error log if error found - */ - errlHndl_t resetPnor( ResetLevels i_pnorResetLevel ); - - - /** - * @brief Reinitialize the SFC - * - * @return Error log if error found - */ - errlHndl_t reinitializeSfc( void ); - - /** * @brief Returns if an operation should be retried and handles * the error logs @@ -803,11 +300,48 @@ class PnorDD uint8_t& io_retry_count ); + /** + * @brief Call SFC to write data to the PNOR flash, doing retries + * as needed + * @pre Mutex should already be locked before calling + * + * @parm[in] i_addr PNOR flash Address to write + * @parm[in] i_size Amount of data to write, in bytes. + * @parm[in] i_data Buffer containing data to write + * + * @return Error from operation + */ + errlHndl_t _writeFlash( uint32_t i_addr, + size_t i_size, + void* i_data ); - private: // Variables + /** + * @brief Call SFC to read data from the PNOR flash, doing retries + * as needed + * @pre Mutex should already be locked before calling + * + * @parm[in] i_addr PNOR flash Address to read + * @parm[in] i_size Amount of data to read, in bytes. + * @parm[out] o_data Buffer to read data into + * + * @return Error from operation + */ + errlHndl_t _readFlash( uint32_t i_addr, + size_t i_size, + void* o_data ); + + /** + * @brief Call SFC to erase the PNOR flash, doing retries + * as needed + * @pre Mutex should already be locked before calling + * + * @parm i_address Offset into flash to erase, aligned to erase block + * + * @return Error from operation + */ + errlHndl_t _eraseFlash( uint32_t i_address ); - // NOTE: The layout of the variables in this class must be maintained - // along with the offsets in the debug framework. + private: // Variables /** * @brief Global Mutex to prevent concurrent PNOR accesses to Master Proc @@ -833,77 +367,23 @@ class PnorDD */ EraseInfo_t iv_erases[ERASE_COUNT_MAX]; - /** - * @brief Determine how much of the PNOR logic to use, - * this is required due to different model functionality - * in the current VPO and Simics models - */ - PnorMode_t iv_mode; - - /** - * @brief Flag to drive special behavior in VPO. - */ - uint64_t iv_vpoMode; - /** * @brief describes the erase block size, set based on NOR chip type * */ - uint32_t iv_erasesize_bytes; + uint32_t iv_eraseSizeBytes; /** * @brief CHIP ID or the NOR chip attached to SFC. * */ - uint32_t iv_nor_chipid; - - /** - * @brief Hardware workarounds - * - */ - uint32_t iv_hw_workaround; - - /** - * @brief indicates if SFC initialization has been performed. - * - */ - bool iv_sfcInitDone; - - /** - * @brief Start of Fake PNOR address range.. - * - */ - uint64_t iv_fakeStart; - - /** - * @brief Size of Fake PNOR address range.. - * - */ - uint64_t iv_fakeSize; - - /** - * @brief Indicates if class is currently collecting FFDC data - * - */ - bool iv_ffdc_active; - - /** - * @brief Number of times recovered from an error - * - */ - uint32_t iv_error_handled_count; - - /** - * @brief Indicates recovery from an error has failed - * - */ - bool iv_error_recovery_failed; + uint32_t iv_norChipId; /** - * @brief Indicates if class is currently doing a RESET procedure + * @brief Associated Serial Flash Controller * */ - bool iv_reset_active; + SfcDD* iv_sfc; /** * @brief Processor Target used to access PNOR device @@ -913,6 +393,8 @@ class PnorDD // Needed for testcases friend class PnorDdTest; + friend class SfcIBMTest; + friend class SfcAST2400Test; // let the UserDetails classes see internal structures friend class PNOR::UdPnorDDParms; diff --git a/src/usr/pnor/pnorrp.C b/src/usr/pnor/pnorrp.C index 3b6790d88..aec79ba59 100644 --- a/src/usr/pnor/pnorrp.C +++ b/src/usr/pnor/pnorrp.C @@ -46,7 +46,7 @@ // Trace definition trace_desc_t* g_trac_pnor = NULL; -TRAC_INIT(&g_trac_pnor, PNOR_COMP_NAME, 2*KILOBYTE, TRACE::BUFFER_SLOW); //2K +TRAC_INIT(&g_trac_pnor, PNOR_COMP_NAME, 4*KILOBYTE, TRACE::BUFFER_SLOW); //2K // Easy macro replace for unit testing //#define TRACUCOMP(args...) TRACFCOMP(args) @@ -72,7 +72,8 @@ const char* cv_EYECATCHER[] = { "MVPD", /**< PNOR::MODULE_VPD : Module VPD */ "CVPD", /**< PNOR::CENTAUR_VPD : Centaur VPD */ "ATTROVER", /**< PNOR::ATTR_OVER : Attribute Override */ - "TEST", /**< PNOR::TEST : Test space for PNOR*/ + "NVRAM", /**< PNOR::NVRAM : OPAL Storage */ + "TEST", /**< PNOR::TEST : Test space for PNOR*/ //Not currently used // "XXX", /**< NUM_SECTIONS : Used as invalid entry */ diff --git a/src/usr/pnor/pnorrp.H b/src/usr/pnor/pnorrp.H index 38043f875..f11a2644d 100644 --- a/src/usr/pnor/pnorrp.H +++ b/src/usr/pnor/pnorrp.H @@ -6,6 +6,7 @@ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2011,2014 */ +/* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -260,6 +261,8 @@ class PnorRP // allow testcase to see inside friend class PnorRpTest; friend class PnorDdTest; + friend class SfcIBMTest; + friend class SfcAST2400Test; // allow this function to use constant(s) friend errlHndl_t PNOR::validateAltMaster( void ); diff --git a/src/usr/pnor/pnorvalid.C b/src/usr/pnor/pnorvalid.C index b690fd5fc..048078c93 100644 --- a/src/usr/pnor/pnorvalid.C +++ b/src/usr/pnor/pnorvalid.C @@ -173,7 +173,7 @@ errlHndl_t validateAltMaster( void ) continue; } - pnordd = new PnorDD(PnorDD::MODEL_REAL_MMIO, 0, 0, procList[i]); + pnordd = new PnorDD(procList[i]); // Read Flash l_err = pnordd->readFlash(tocBuffer, read_size, diff --git a/src/usr/pnor/sfc_ast2400.C b/src/usr/pnor/sfc_ast2400.C new file mode 100644 index 000000000..197cd6b1a --- /dev/null +++ b/src/usr/pnor/sfc_ast2400.C @@ -0,0 +1,856 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfc_ast2400.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 */ +/*****************************************************************************/ +// I n c l u d e s +/*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sfc_ast2400.H" +#include "norflash.H" + + +/*****************************************************************************/ +// C o n s t a n t s +/*****************************************************************************/ + + + + +/*****************************************************************************/ +// G l o b a l s +/*****************************************************************************/ + +// Initialized in pnorrp.C +extern trace_desc_t* g_trac_pnor; + +/*****************************************************************************/ +// M e t h o d s +/*****************************************************************************/ + + +namespace PNOR { + +/** + * @brief Wrapper for device driver constructor + */ +errlHndl_t create_SfcDD( SfcDD*& o_sfc, + TARGETING::Target* i_proc ) +{ + errlHndl_t l_err = NULL; + TRACFCOMP( g_trac_pnor, "Creating SfcAST2400 object" ); + o_sfc = new SfcAST2400( l_err, i_proc ); + return l_err; +} + +}; + +/** + * @brief Constructor + */ +SfcAST2400::SfcAST2400( errlHndl_t& o_err, + TARGETING::Target* i_proc ) +: SfcDD(o_err,i_proc) +{ +} + + +/** + * @brief Read data from the flash + */ +errlHndl_t SfcAST2400::readFlash( uint32_t i_addr, + size_t i_size, + void* o_data ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::readFlash> i_addr=0x%.8x, i_size=0x%.8x", i_addr, i_size ); + + do{ + uint32_t* word_ptr = static_cast(o_data); + uint32_t word_size = i_size/4; + for( uint32_t words_read = 0; + words_read < word_size; + words_read ++ ) + { + //Read directly from MMIO space + uint32_t lpc_addr = LPC_SFC_MMIO_OFFSET | (i_addr + words_read*4); + size_t reg_size = sizeof(uint32_t); + + l_err = deviceOp( DeviceFW::READ, + iv_proc, + &(word_ptr[words_read]), + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_FW, + lpc_addr) ); + if( l_err ) { break; } + } + if( l_err ) { break; } + }while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::readFlash> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + + +/** + * @brief Write data into flash + */ +errlHndl_t SfcAST2400::writeFlash( uint32_t i_addr, + size_t i_size, + void* i_data ) +{ + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::writeFlash> i_addr=0x%.8x, i_size=0x%.8x", i_addr, i_size ); + errlHndl_t l_err = NULL; + + do { + // Enable write mode + l_err = enableWriteMode(); + if( l_err ) { break; } + + // Send in the Page Program command with the data to write + uint8_t opcode = PNOR::SPI_JEDEC_PAGE_PROGRAM; + l_err = sendSpiCmd( opcode, i_addr, + i_size, reinterpret_cast(i_data), + 0, NULL ); + if( l_err ) { break; } + + // Wait for idle + l_err = pollOpComplete(); + if( l_err ) { break; } + +#ifdef CONFIG_ALLOW_MICRON_PNOR + //check for special Micron Flag Status reg + if(iv_flashWorkarounds & PNOR::HWWK_MICRON_WRT_ERASE) + { + l_err = PNOR::micronFlagStatus(this); + if(l_err) { break; } + } +#endif + + } while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::writeFlash> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + + +/** + * @brief Erase a block of flash + */ +errlHndl_t SfcAST2400::eraseFlash( uint32_t i_addr ) +{ + TRACFCOMP(g_trac_pnor, ">>SfcAST2400::eraseFlash> Block 0x%.8X", i_addr ); + errlHndl_t l_err = NULL; + + do { + // Enable write mode + l_err = enableWriteMode(); + if( l_err ) { break; } + + // Send erase command + uint8_t opcode = PNOR::SPI_JEDEC_SECTOR_ERASE; + l_err = sendSpiCmd( opcode, i_addr, 0, 0, 0, NULL ); + if( l_err ) { break; } + + // Wait for idle + l_err = pollOpComplete(); + if( l_err ) { break; } + +#ifdef CONFIG_ALLOW_MICRON_PNOR + //check for special Micron Flag Status reg + if(iv_flashWorkarounds & PNOR::HWWK_MICRON_WRT_ERASE) + { + l_err = PNOR::micronFlagStatus(this); + if(l_err) { break; } + } +#endif + + } while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::eraseFlash> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Initialize and configure the SFC hardware + */ +errlHndl_t SfcAST2400::hwInit( ) +{ + TRACFCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::hwInit>" ); + errlHndl_t l_err = NULL; + + do { + size_t reg_size = sizeof(uint8_t); + + //** Initialize the LPC2AHB logic + + // Send SuperIO password - send A5 twice + uint8_t data = 0xA5; + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO,SIO_ADDR_2E) ); + if( l_err ) { break; } + + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO,SIO_ADDR_2E) ); + if( l_err ) { break; } + + + // Select logical device D (iLPC2AHB) + l_err = writeRegSIO( 0x07, 0x0D ); + if( l_err ) { break; } + + + // Enable iLPC->AHB + l_err = writeRegSIO( 0x30, 0x01 ); + if( l_err ) { break; } + + + //** Setup the SPI Controller + + /* Enable writing to the controller */ + SpiControlReg04_t ctlreg; + l_err = readRegSPIC( CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + ctlreg.cmdMode = 0b10; //10:Normal Write (CMD + Address + Write data) + l_err = writeRegSPIC( CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + + SpiConfigReg00_t confreg; + l_err = readRegSPIC( CONFREG_00, confreg.data32 ); + if( l_err ) { break; } + confreg.inactiveX2mode = 1; //Enable CE# Inactive pulse width X2 mode + confreg.enableWrite = 1; //Enable flash memory write + l_err = writeRegSPIC( CONFREG_00, confreg.data32 ); + if( l_err ) { break; } + + + /* + * Setup control reg and for our use, switching + * to 1-bit mode, clearing user mode if set, etc... + * + * Also configure SPI clock to something safe + * like HCLK/8 (24Mhz) + */ + ctlreg.fourByteMode = 1; + ctlreg.ioMode = 0b00; //single bit or controlled by bit[3] + ctlreg.pulseWidth = 0x0; //0000: 16T (1T = 1 HCLK clock) + ctlreg.cmdData = 0x00; + ctlreg.spiClkFreq = 0x4; //HCLK/8 + ctlreg.dummyCycleRead1 = 0; //no dummy cycles + ctlreg.dummyCycleRead2 = 0b00; //no dummy cycles + ctlreg.cmdMode = 0b00; //00:Normal Read (03h + Address + Read data) + iv_ctlRegDefault = ctlreg; // Default setup is regular read mode + + // Configure for read + l_err = writeRegSPIC( CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + + // Figure out what flash chip we have + uint32_t chipid = 0; + l_err = getNORChipId( chipid ); + if( l_err ) { break; } + + // Setup flash-specific settings here, if there are any + + } while(0); + + TRACFCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::hwInit> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Send a SPI command + */ +errlHndl_t SfcAST2400::sendSpiCmd( uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ) +{ + errlHndl_t l_err = NULL; + size_t opsize = 0; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::sendSpiCmd> i_opCode=%.2X, i_address=%.8X, i_writeCnt=0x%X, i_writeData=%p, i_readCnt=0x%X, o_readData=%p", i_opCode, i_address, i_writeCnt, i_writeData, i_readCnt, o_readData ); + + do { +#ifdef CONFIG_ALLOW_MICRON_PNOR + //Do a read of flash address zero to workaround + // a micron bug with extended reads + if( (PNOR::HWWK_MICRON_EXT_READ & iv_flashWorkarounds) + && (i_readCnt > 4) ) + { + uint32_t ignored = 0; + l_err = readFlash( 0, 1, &ignored ); + if(l_err) { break; } + } +#endif + + // Put controller into command mode (instead of read mode) + l_err = commandMode( true ); + if( l_err ) { break; } + + // Write command to the beginning of the flash space + opsize = sizeof(i_opCode); + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &i_opCode, + opsize, //just send opcode + DEVICE_LPC_ADDRESS(LPC::TRANS_FW, + LPC_SFC_MMIO_OFFSET) ); + if( l_err ) { break; } + + // Send address if there is one + if( i_address != NO_ADDRESS ) + { + // Write address to the beginning of the flash space + opsize = sizeof(i_address); + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &i_address, + opsize, //only supporting 4-byte addresses + DEVICE_LPC_ADDRESS(LPC::TRANS_FW, + LPC_SFC_MMIO_OFFSET) ); + if( l_err ) { break; } + } + + // Send in the rest of the write data + if( i_writeCnt && i_writeData ) + { + size_t bytes_left = i_writeCnt; + uint8_t* curptr = const_cast(i_writeData); + while( bytes_left ) + { + // Write the last partial word if there is one + if( bytes_left < 4 ) + { + opsize = bytes_left; + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + curptr, + opsize, + DEVICE_LPC_ADDRESS(LPC::TRANS_FW, + LPC_SFC_MMIO_OFFSET) ); + break; + } + + // Write data into the beginning of the flash space, + // in command mode this doesn't write the flash + // but instead is a pass-through to the area we + // really want to write + opsize = sizeof(uint32_t); + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + curptr, + opsize, + DEVICE_LPC_ADDRESS(LPC::TRANS_FW, + LPC_SFC_MMIO_OFFSET) ); + if( l_err ) { break; } + + curptr += 4; + bytes_left -= 4; + } + if( l_err ) { break; } + } + + // Read back the results + if( i_readCnt && o_readData ) + { + size_t bytes_left = i_readCnt; + uint8_t* curptr = o_readData; + while( bytes_left ) + { + // Grab the last partial word if there is one + if( bytes_left < 4 ) + { + opsize = bytes_left; + l_err = deviceOp( DeviceFW::READ, + iv_proc, + curptr, + opsize, + DEVICE_LPC_ADDRESS(LPC::TRANS_FW, + LPC_SFC_MMIO_OFFSET) ); + break; + } + + // Read data from the beginning of the flash space, + // in command mode this doesn't read the flash + // but instead is a pass-through to the data we + // really want + opsize = sizeof(uint32_t); + l_err = deviceOp( DeviceFW::READ, + iv_proc, + curptr, + opsize, + DEVICE_LPC_ADDRESS(LPC::TRANS_FW, + LPC_SFC_MMIO_OFFSET) ); + if( l_err ) { break; } + + curptr += 4; + bytes_left -= 4; + } + if( l_err ) { break; } + } + } while(0); + + // No matter what, put the logic back into read mode + errlHndl_t tmp_err = commandMode( false ); + if( tmp_err ) + { + if( l_err ) + { + // Keep the original error, commit this one as info + tmp_err->plid(l_err->plid()); + tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); + ERRORLOG::errlCommit(tmp_err,PNOR_COMP_ID); + } + else + { + l_err = tmp_err; + } + } + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::sendSpiCmd> o_readData=%.2X, err=%.8X", o_readData == NULL ? 0 : o_readData[0], ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Enter/exit command mode + */ +errlHndl_t SfcAST2400::commandMode( bool i_enter ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::commandMode(%d)", i_enter ); + + /* + * There is only a limited addressable window within LPC space. The AST + * has its control register space at too far of a distance from the read + * space for them both to fit in a single window. Rather than moving the + * window around we will use the iLPC2AHB backdoor inside the SuperIO + * controller to do both register accesses and to write into the flash. + * + * High level flow to write into control space: + * Stop active control (SPI04 Control Reg) + * Enable command mode (SPI04 Control Reg) + * Write actual command into flash base addr (0x0E000000) + */ + + do { + SpiControlReg04_t ctlreg = iv_ctlRegDefault; + + // Switch to user mode, CE# dropped + ctlreg.stopActiveCtl = 1; + ctlreg.cmdMode = 0b11; //User Mode (Read/Write Data) + l_err = writeRegSPIC( CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + + if( i_enter ) //ast_sf_start_cmd + { + // user mode, CE# active + ctlreg.stopActiveCtl = 0; + l_err = writeRegSPIC( CTLREG_04, ctlreg.data32 ); + if( l_err ) { break; } + } + else //ast_sf_end_cmd + { + // Switch back to read mode + l_err = writeRegSPIC( CTLREG_04, iv_ctlRegDefault.data32 ); + if( l_err ) { break; } + } + } while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::commandMode> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Enable write mode + */ +errlHndl_t SfcAST2400::enableWriteMode( void ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::enableWriteMode>" ); + + /* Some flashes need it to be hammered */ + PNOR::NorStatusReg_t status; + size_t i = 0; + for( i = 0; i < 10; i++ ) + { + // Send the command to enable writes + uint8_t opcode = PNOR::SPI_JEDEC_WRITE_ENABLE; + l_err = sendSpiCmd( opcode, NO_ADDRESS, 0, NULL, 0, NULL ); + if( l_err ) { break; } + + // Check to see if it worked + opcode = PNOR::SPI_JEDEC_READ_STATUS; + l_err = sendSpiCmd( opcode, NO_ADDRESS, 0, NULL, 1, &(status.data8) ); + if( l_err ) { break; } + + if( status.writeEnable ) + { + break; + } + } + + if( !l_err && !status.writeEnable ) + { + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCAST2400_ENABLEWRITEMODE + * @reasoncode PNOR::RC_CANNOT_ENABLE_WRITES + * @userdata1[24:31] Output from RDSR + * @userdata1[32:63] NOR chip id + * @userdata2 + * @devdesc SfcAST2400::enableWriteMode> Unable to enable + * write mode on the PNOR flash + * @custdesc Firmware error accessing flash during IPL + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCAST2400_ENABLEWRITEMODE, + PNOR::RC_CANNOT_ENABLE_WRITES, + TWO_UINT32_TO_UINT64( TO_UINT32(status.data8), + iv_norChipId), + 0); + // Limited in callout: no flash sub-target, so calling out processor + l_err->addHwCallout( iv_proc, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + addFFDC(l_err); + l_err->collectTrace(PNOR_COMP_NAME); + } + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::enableWriteMode> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Write a single byte into the SIO + */ +errlHndl_t SfcAST2400::writeRegSIO( uint8_t i_regAddr, + uint8_t i_data ) +{ //lpc_sio_outb + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::writeRegSIO> i_regAddr=0x%.2X, i_data=0x%.2X", i_regAddr, i_data ); + + do { + size_t reg_size = sizeof(uint8_t); + + // AST2400 integrates a Super I/O module with + // LPC protocol (I/O cycle 0x2E/0x2F) + + // Write out the register address + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &i_regAddr, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO,SIO_ADDR_2E) ); + if( l_err ) { break; } + + // Write out the register data + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &i_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO,SIO_DATA_2F) ); + if( l_err ) { break; } + + } while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::writeRegSIO> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Read a single byte from the SIO + */ +errlHndl_t SfcAST2400::readRegSIO( uint8_t i_regAddr, + uint8_t& o_data ) +{ //lpc_sio_inb + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::readRegSIO> i_regAddr=0x%.2X", i_regAddr ); + + do { + size_t reg_size = sizeof(uint8_t); + + // AST2400 integrates a Super I/O module with + // LPC protocol (I/O cycle 0x2E/0x2F) + + // Write out the register address + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &i_regAddr, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO,SIO_ADDR_2E) ); + if( l_err ) { break; } + + // Read in the register data + l_err = deviceOp( DeviceFW::READ, + iv_proc, + &o_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_IO,SIO_DATA_2F) ); + if( l_err ) { break; } + + } while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::readRegSIO> o_data=0x%.2X, err-%.8X", o_data, ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Prepare the iLPC2AHB address regs + */ +errlHndl_t SfcAST2400::setupAddrLPC2AHB( uint32_t i_addr ) +{ //lpc_ahb_prep + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::setupAddrLPC2AHB> i_addr=0x%X", i_addr ); + + do { + // Select logical device D (iLPC2AHB) + l_err = writeRegSIO( 0x07, 0x0D ); + if( l_err ) { break; } + + // Push 4 address bytes into SIO regs 0xF0-0xF3 + for( size_t i=sizeof(i_addr); i>0; i-- ) + { + l_err = writeRegSIO( 0xF3-(i-1), //F0,F1,F2,F3 + static_cast(i_addr >> ((i-1)*8)) ); + if( l_err ) { break; } + } + if( l_err ) { break; } + + // Configure 4 byte length + l_err = writeRegSIO( 0xF8, 0x02 ); + if( l_err ) { break; } + + } while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::setupAddrLPC2AHB> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Write SPI Controller Register + */ +errlHndl_t SfcAST2400::writeRegSPIC( SpicReg_t i_reg, + uint32_t i_data ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::writeRegSPIC> i_reg=0x%.2X, i_data=0x%.8X", i_reg, i_data ); + + do { + // Compute the full LPC address + uint32_t lpc_addr = i_reg | SPIC_BASE_ADDR_AHB; + + // Setup the logic for the write + l_err = setupAddrLPC2AHB( lpc_addr ); + if( l_err ) { break; } + + // Push 4 data bytes into SIO regs 0xF4-0xF7 + uint8_t* ptr8 = reinterpret_cast(&i_data); + for( size_t i=0; i err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Read SPI Controller Register + */ +errlHndl_t SfcAST2400::readRegSPIC( SpicReg_t i_reg, + uint32_t& o_data ) +{ + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::readRegSPIC> i_reg=0x%.2X", i_reg ); + errlHndl_t l_err = NULL; + + do { + // Compute the full LPC address + uint32_t lpc_addr = i_reg | SPIC_BASE_ADDR_AHB; + + // Setup the logic for the write + l_err = setupAddrLPC2AHB( lpc_addr ); + if( l_err ) { break; } + + // Trigger the write operation by reading the magic register + uint8_t ignored = 0; + l_err = readRegSIO( 0xFE, ignored ); + if( l_err ) { break; } + + // Read 4 data bytes into SIO regs 0xF4-0xF7 + uint8_t* ptr8 = reinterpret_cast(&o_data); + for( size_t i=0; i o_data=0x%.8X, l_err=%.8X", o_data, ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Poll for completion of SPI operation + */ +errlHndl_t SfcAST2400::pollOpComplete( void ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::pollOpComplete>" ); + + do { + // Send RDSR command until write-in-progress clears + PNOR::NorStatusReg_t status; + uint64_t poll_time = 0; + uint64_t loop = 0; + while( poll_time < MAX_WRITE_TIME_NS ) + { + uint8_t opcode = PNOR::SPI_JEDEC_READ_STATUS; + l_err = sendSpiCmd( opcode, + NO_ADDRESS, + 0, NULL, + 1, &(status.data8) ); + if( l_err ) { break; } + + // check if any op is still going + if( !status.writeInProgress ) + { + break; + } + + // want to start out incrementing by small numbers then get bigger + // to avoid a really tight loop in an error case so we'll increase + // the wait each time through + ++loop; + nanosleep( 0, 100*loop ); + poll_time += 100*loop; + } + if( l_err ) { break; } + + TRACDCOMP(g_trac_pnor,"SfcAST2400::pollOpComplete> command took %d ns", poll_time); + + // No status regs to check so just look for timeout + if( status.writeInProgress ) + { + TRACFCOMP( g_trac_pnor, "SfcAST2400::pollOpComplete> Timeout during write or erase" ); + + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCAST2400_POLLOPCOMPLETE + * @reasoncode PNOR::RC_SFC_TIMEOUT + * @userdata1[0:31] NOR Flash Chip ID + * @userdata1[32:63] Total poll time (ns) + * @userdata2[56:63] Output of RDSR command + * @devdesc SfcAST2400::pollOpComplete> Timeout during + * write or erase operation + * @custdesc Hardware error accessing flash during IPL + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCAST2400_POLLOPCOMPLETE, + PNOR::RC_SFC_TIMEOUT, + TWO_UINT32_TO_UINT64(iv_norChipId, + poll_time), + status.data8); + + // Limited in callout: no PNOR target, so calling out processor + l_err->addHwCallout( + iv_proc, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + addFFDC(l_err); + l_err->collectTrace(PNOR_COMP_NAME); + break; + } + } while(0); + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::pollOpComplete> err=%.8X", ERRL_GETEID_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Add error registers to an existing Error Log + */ +void SfcAST2400::addFFDC( errlHndl_t& io_errhdl ) +{ + TRACDCOMP( g_trac_pnor, ENTER_MRK"SfcAST2400::addFFDC>" ); + //@fixme-RTC:115212 - Create userdetails that includes chipid and SFDP data + + errlHndl_t l_err = NULL; + + //Read SFDP for FFDC + uint32_t outdata[4]; + l_err = sendSpiCmd( PNOR::SPI_JEDEC_READ_SFDP, + 0, + 0, NULL, + 16, reinterpret_cast(outdata) ); + if( l_err ) + { + delete l_err; + } + else + { + //Loop around and grab all 16 bytes + for( size_t x=0; x<4; x++ ) + { + TRACFCOMP( g_trac_pnor, "SFDP[%d]=%.8X", x, outdata[x] ); + } + } + + TRACDCOMP( g_trac_pnor, EXIT_MRK"SfcAST2400::addFFDC>" ); +} diff --git a/src/usr/pnor/sfc_ast2400.H b/src/usr/pnor/sfc_ast2400.H new file mode 100644 index 000000000..7b4527c09 --- /dev/null +++ b/src/usr/pnor/sfc_ast2400.H @@ -0,0 +1,295 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfc_ast2400.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 __PNOR_SFCAST2400_H +#define __PNOR_SFCAST2400_H + +#include +#include +#include +#include "sfcdd.H" + +/** @file sfc_ast2400.H + * @brief Provides the logic to access and configure the + * AST2400 BMC in order to access the PNOR + */ + +/** + * @brief AST2400 SFC Device Driver Class + * Provides the logic to access and configure the + * AST2400 BMC in order to access the PNOR + */ +class SfcAST2400 : public SfcDD +{ + + public: //SfcDD methods + /** + * @brief Initialize the SFC Hardware + * + * @return void + */ + virtual errlHndl_t hwInit(); + + /** + * @brief Read data from the PNOR flash + * + * @parm i_addr PNOR flash Address to read + * @parm i_size Amount of data to read, in bytes. + * @parm o_data Buffer to read data into + * + * @return Error from operation + */ + virtual errlHndl_t readFlash(uint32_t i_addr, + size_t i_size, + void* o_data); + + /** + * @brief Write data to the PNOR flash + * + * @parm i_addr PNOR flash Address to write + * @parm i_size Amount of data to write, in bytes. + * @parm i_data Buffer containing data to write + * + * @return Error from operation + */ + virtual errlHndl_t writeFlash(uint32_t i_addr, + size_t i_size, + void* i_data); + + /** + * @brief Erase a block of flash + * + * @parm i_address Offset into flash to erase, aligned to erase block + * + * @return Error from operation + */ + virtual errlHndl_t eraseFlash(uint32_t i_address); + + + /** + * @brief Send a SPI command + * + * @parm[in] i_opCode: command to send into controller first + * @parm[in] i_address: address for those commands that need it + * @parm[in] i_writeCnt: number of bytes to write to device + * @parm[in] i_writeData: write data buffer + * @parm[in] i_readCnt: number of bytes to read from device + * @parm[out] o_readData: read data buffer + * + * @return Error from operation + */ + virtual errlHndl_t sendSpiCmd( uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ); + + /** + * @brief Add error registers to an existing Error Log + * + * @param[in] io_errhdl: Error log to add data to + */ + virtual void addFFDC( errlHndl_t& io_errhdl ); + + public: + /** + * @brief Constructor + * @param[out] Return any error in constructor + * @param[in] Processor target associated with the LPC master + */ + SfcAST2400( errlHndl_t& o_err, + TARGETING::Target* i_proc + = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ); + + protected: + /** + * @brief List of registers in the SPI Controller logic + */ + enum SpicReg_t + { + CONFREG_00 = 0x00, + CTLREG_04 = 0x04, + MISCCTLREG_10 = 0x10, + READTIMEREG_14 = 0x14 + }; + + /** + * @brief Write a SPI Controller register + * + * @param[in] i_reg: Register to write + * @param[in] i_data: Data to write + * + * @return Error from operation + */ + errlHndl_t writeRegSPIC( SpicReg_t i_reg, + uint32_t i_data ); + + /** + * @brief Write a SPI Controller register + * + * @param[in] i_reg: Register to write + * @param[in] o_data: Data that was read + * + * @return Error from operation + */ + errlHndl_t readRegSPIC( SpicReg_t i_reg, + uint32_t& o_data ); + + /** + * @brief Write a single byte into a SIO register + * + * @param[in] i_reg: Register to write + * @param[in] i_data: Data to write + * + * @return Error from operation + */ + errlHndl_t writeRegSIO( uint8_t i_regAddr, + uint8_t i_data ); + + /** + * @brief Read a single byte from a SIO register + * + * @param[in] i_reg: Register to read + * @param[in] o_data: Data that was read + * + * @return Error from operation + */ + errlHndl_t readRegSIO( uint8_t i_regAddr, + uint8_t& o_data ); + + /** + * @brief Enable write mode + * + * @return Error from operation + */ + errlHndl_t enableWriteMode( void ); + + /** + * @brief Enter/exit command mode + * + * @param[in] i_enter: true=enter cmd mode, false=exit cmd mode + * + * @return Error from operation + */ + errlHndl_t commandMode( bool i_enter ); + + /** + * @brief Poll for completion of SPI operation + * + * @return Error from operation + */ + errlHndl_t pollOpComplete( void ); + + /** + * @brief Prepare the iLPC2AHB address regs + * + * @param[in] i_addr: LPC address to access + * + * @return Error from operation + */ + errlHndl_t setupAddrLPC2AHB( uint32_t i_addr ); + + + /** + * @brief SPI0 Configuration Register + */ + union SpiConfigReg00_t + { + uint32_t data32; + struct + { //Little-endian bit positions + uint32_t rsvd : 30; //31:2 + uint32_t inactiveX2mode : 1; //1 + uint32_t enableWrite : 1; //0 + }; + SpiConfigReg00_t() : data32(0) {}; + }; + + /** + * @brief SPI04 Control Register + */ + union SpiControlReg04_t + { + uint32_t data32; + struct + { //Little-endian bit positions + uint32_t rsvd : 2; //31:30 + uint32_t ioMode : 2; //29:28 + uint32_t pulseWidth : 4; //27:24 + uint32_t cmdData : 8; //23:16 + uint32_t dummyCycleCmd : 1; //15 + uint32_t dummyCycleRead1 : 1; //14 + uint32_t fourByteMode : 1; //13 + uint32_t disableCmdMerge : 1; //12 + uint32_t spiClkFreq : 4; //11:8 + uint32_t dummyCycleRead2 : 2; //7:6 + uint32_t lsbFirst : 1; //5 + uint32_t useClkMode3 : 1; //4 + uint32_t dualInputMode : 1; //3 + uint32_t stopActiveCtl : 1; //2 + uint32_t cmdMode : 2; //1:0 + }; + SpiControlReg04_t() : data32(0) {}; + }; + + /** + * @brief Default value of SPI04 (saves a read) + */ + SpiControlReg04_t iv_ctlRegDefault; + + + /** @brief General Constants */ + enum + { + /**< Offset to direct read space, from FW base */ + LPC_SFC_MMIO_OFFSET = 0x0E000000, + + /**< Offset to SPI Controller Register Space */ + LPC_SFC_CTLR_BASE = 0x1E789000, + + /**< AHB address of SPI Flash controller */ + SPIC_BASE_ADDR_AHB = 0x1E630000, + + /**< AHB address of flash */ + FLASH_BASE_ADDR_AHB = 0x30000000, + + /**< AHB address of LPC registers */ + LPC_CTLR_BASE_ADDR_AHB = 0x1E789000, + + /**< Maximum time to wait for a write/erase */ + MAX_WRITE_TIME_NS = NS_PER_SEC, + + /**< SuperIO Address Cycle */ + SIO_ADDR_2E = 0x2E, + + /**< SuperIO Data Cycle */ + SIO_DATA_2F = 0x2F, + }; + + friend class SfcAST2400Test; +}; + +#endif diff --git a/src/usr/pnor/sfc_fake.C b/src/usr/pnor/sfc_fake.C new file mode 100644 index 000000000..90cce7843 --- /dev/null +++ b/src/usr/pnor/sfc_fake.C @@ -0,0 +1,295 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfc_fake.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 */ +/*****************************************************************************/ +// I n c l u d e s +/*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sfc_fake.H" +#include "norflash.H" + + +/*****************************************************************************/ +// C o n s t a n t s +/*****************************************************************************/ + +// By default we well use the top of the cache 4MB-8MB +#define FAKE_PNOR_START (4*MEGABYTE) +#define FAKE_PNOR_END (8*MEGABYTE) +#define FAKE_PNOR_SIZE (FAKE_PNOR_END-FAKE_PNOR_START) + + + + +/*****************************************************************************/ +// G l o b a l s +/*****************************************************************************/ + +// Initialized in pnorrp.C +extern trace_desc_t* g_trac_pnor; + +/*****************************************************************************/ +// M e t h o d s +/*****************************************************************************/ + +namespace PNOR { +/** + * @brief Wrapper for device driver constructor + */ +errlHndl_t create_SfcDD( SfcDD*& o_sfc, + TARGETING::Target* i_proc ) +{ + errlHndl_t l_err = NULL; + TRACFCOMP( g_trac_pnor, "Creating SfcFake object" ); + o_sfc = new SfcFake( l_err, i_proc ); + return l_err; +} + +}; + +/** + * @brief Constructor + */ +SfcFake::SfcFake( errlHndl_t& o_err, + TARGETING::Target* i_proc ) +: SfcDD(o_err,i_proc) +, iv_fakePnor(reinterpret_cast(FAKE_PNOR_START)) +, iv_sizeBytes(FAKE_PNOR_SIZE) +{ + TRACFCOMP( g_trac_pnor, "Instantiating SfcFake" ); +} + + +/** + * @brief Read data from the flash + */ +errlHndl_t SfcFake::readFlash( uint32_t i_addr, + size_t i_size, + void* o_data ) +{ + TRACDCOMP( g_trac_pnor, "SfcFake::readFlash> i_addr=0x%.8x, i_size=0x%.8x", + i_addr, i_size ); + errlHndl_t errhdl = NULL; + + //create a pointer to the offset start. + uint8_t* srcPtr = reinterpret_cast(iv_fakePnor+i_addr); + + if( (srcPtr+i_size) > (iv_fakePnor+iv_sizeBytes) ) + { + TRACFCOMP(g_trac_pnor, "SfcFake::readFlash> Read goes past end of fake-PNOR : i_addr=0x%X, i_size=0x%X", i_addr, i_size ); + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCFAKE_READFLASH + * @reasoncode PNOR::RC_INVALID_ADDRESS + * @userdata1[0:31] PNOR Address + * @userdata1[32:63] Bytes to read + * @userdata2[0:31] + * @userdata2[32:63] Size of allocated PNOR space + * @devdesc SfcFake::readFlash> Requested access exceeded the + * bounds of the allocated PNOR space + * @custdesc Firmware error accessing flash during IPL + */ + errhdl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCFAKE_READFLASH, + PNOR::RC_INVALID_ADDRESS, + TWO_UINT32_TO_UINT64(i_addr, + i_size), + TWO_UINT32_TO_UINT64(0, + iv_sizeBytes), + true /*Software error*/); + } + else + { + //Read directly from memory + memcpy( o_data, srcPtr, i_size ); + } + + return errhdl; +} + + +/** + * @brief Write data into flash + */ +errlHndl_t SfcFake::writeFlash( uint32_t i_addr, + size_t i_size, + void* i_data ) +{ + TRACDCOMP( g_trac_pnor, "SfcFake::writeFlash> i_addr=0x%.8x, i_size=0x%.8x", + i_addr, i_size ); + errlHndl_t errhdl = NULL; + + //create a pointer to the offset start. + uint8_t* destPtr = reinterpret_cast(iv_fakePnor+i_addr); + + if( (destPtr+i_size) > (iv_fakePnor+iv_sizeBytes) ) + { + TRACFCOMP(g_trac_pnor, "SfcFake::writeFlash> Write goes past end of fake-PNOR : i_addr=0x%X, i_size=0x%X", i_addr, i_size ); + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCFAKE_WRITEFLASH + * @reasoncode PNOR::RC_INVALID_ADDRESS + * @userdata1[0:31] PNOR Address + * @userdata1[32:63] Bytes to write + * @userdata2[0:31] + * @userdata2[32:63] Size of allocated PNOR space + * @devdesc SfcFake::writeFlash> Requested access exceeded the + * bounds of the allocated PNOR space + * @custdesc Firmware error accessing flash during IPL + */ + errhdl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCFAKE_WRITEFLASH, + PNOR::RC_INVALID_ADDRESS, + TWO_UINT32_TO_UINT64(i_addr, + i_size), + TWO_UINT32_TO_UINT64(0, + iv_sizeBytes), + true /*Software error*/); + } + else + { + //Write directly to memory + memcpy( destPtr, i_data, i_size ); + } + + return errhdl; +} + + +/** + * @brief Erase a block of flash + */ +errlHndl_t SfcFake::eraseFlash( uint32_t i_addr ) +{ + TRACDCOMP( g_trac_pnor, "SfcFake::eraseFlash> i_addr=0x%.8x, i_size=0x%.8x", + i_addr ); + errlHndl_t errhdl = NULL; + + //create a pointer to the offset start. + uint8_t* destPtr = reinterpret_cast(iv_fakePnor+i_addr); + + if( (destPtr+iv_eraseSizeBytes) > (iv_fakePnor+iv_sizeBytes) ) + { + TRACFCOMP(g_trac_pnor, "SfcFake::writeFlash> Write goes past end of fake-PNOR : i_addr=0x%X, i_size=0x%X", i_addr ); + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCFAKE_ERASEFLASH + * @reasoncode PNOR::RC_INVALID_ADDRESS + * @userdata1[0:31] PNOR Address + * @userdata1[32:63] + * @userdata2[0:31] Bytes in erase block + * @userdata2[32:63] Size of allocated PNOR space + * @devdesc SfcFake::writeFlash> Requested access exceeded the + * bounds of the allocated PNOR space + * @custdesc Firmware error accessing flash during IPL + */ + errhdl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCFAKE_ERASEFLASH, + PNOR::RC_INVALID_ADDRESS, + TWO_UINT32_TO_UINT64(i_addr, + 0), + TWO_UINT32_TO_UINT64(iv_eraseSizeBytes, + iv_sizeBytes), + true /*Software error*/); + } + else + { + //A real erase sets every bit so emulate that with a memset + memset( destPtr, 0xFF, iv_eraseSizeBytes ); + } + + return errhdl; +} + +/** + * @brief Initialize and configure the SFC hardware + */ +errlHndl_t SfcFake::hwInit( ) +{ + TRACFCOMP( g_trac_pnor, "SfcFake::hwInit> Nothing to do here" ); + return NULL; +} + +/** + * @brief Informs caller if PNORDD is using + * L3 Cache for fake PNOR or not. + */ +bool SfcFake::usingL3Cache( void ) +{ + return true; +} + +/** + * @brief Send a user-defined SPI command + */ +errlHndl_t SfcFake::sendSpiCmd( uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ) +{ + TRACFCOMP( g_trac_pnor, "SfcFake::sendSpiCmd> Nothing to do here : opcode=%.2X", i_opCode ); + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCFAKE_SENDSPICMD + * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION + * @userdata1[0:31] Op Code + * @userdata1[32:63] Address + * @userdata2 + * @devdesc SfcFake::sendSpiCmd> Function is not supported + * @custdesc Firmware error accessing flash during IPL + */ + return new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCFAKE_SENDSPICMD, + PNOR::RC_UNSUPPORTED_OPERATION, + TWO_UINT32_TO_UINT64(i_opCode + i_addr), + 0, + true /*Software error*/); +} + +/** + * @brief Return first 3 bytes of NOR chip id + */ +errlHndl_t SfcFake::getNORChipId( uint32_t& o_chipId ) +{ + o_chipId = PNOR::FAKE_NOR_ID; + iv_norChipId = PNOR::FAKE_NOR_ID; + return NULL; +} + diff --git a/src/usr/pnor/sfc_fake.H b/src/usr/pnor/sfc_fake.H new file mode 100644 index 000000000..dc55e5e0d --- /dev/null +++ b/src/usr/pnor/sfc_fake.H @@ -0,0 +1,151 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfc_fake.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 __PNOR_SFCFAKE_H +#define __PNOR_SFCFAKE_H + +#include +#include +#include +#include "sfcdd.H" + +/** @file sfc_fake.H + * @brief Provides the logic to access an emulated PNOR + * model using mainstore as a proxy for flash + */ + +/** + * @brief Fake SFC Device Driver Class + * Provides the logic to access an emulated PNOR + * model using mainstore as a proxy for flash + */ +class SfcFake : public SfcDD +{ + + public: //SfcDD methods + /** + * @brief Initialize the SFC Hardware + * + * @return void + */ + virtual errlHndl_t hwInit(); + + /** + * @brief Read data from the PNOR flash + * + * @parm i_addr PNOR flash Address to read + * @parm i_size Amount of data to read, in bytes. + * @parm o_data Buffer to read data into + * + * @return Error from operation + */ + virtual errlHndl_t readFlash(uint32_t i_addr, + size_t i_size, + void* o_data); + + /** + * @brief Write data to the PNOR flash + * + * @parm i_addr PNOR flash Address to write + * @parm i_size Amount of data to write, in bytes. + * @parm i_data Buffer containing data to write + * + * @return Error from operation + */ + virtual errlHndl_t writeFlash(uint32_t i_addr, + size_t i_size, + void* i_data); + + /** + * @brief Erase a block of flash + * + * @parm i_address Offset into flash to erase, aligned to erase block + * + * @return Error from operation + */ + virtual errlHndl_t eraseFlash(uint32_t i_address); + + /** + * @brief Send a user-defined SPI command + * + * @parm[in] i_opCode: command to send into controller first + * @parm[in] i_address: address for those commands that need it + * @parm[in] i_writeCnt: number of bytes to write to device + * @parm[in] i_writeData: write data buffer + * @parm[in] i_readCnt: number of bytes to read from device + * @parm[out] o_readData: read data buffer + * + * @return Error from operation + */ + virtual errlHndl_t sendSpiCmd( uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ); + + /** + * @brief Informs caller if PNORDD is using + * L3 Cache for fake PNOR or not. + * + * @return Indicate state of fake PNOR + * true = using L3 Cache for fake PNOR + * false = not using L3 Cache for fake PNOR + */ + virtual bool usingL3Cache( void ); + + /** + * @brief Return first 3 bytes of NOR chip id + * + * @parm[out] NOR chip id + * + * @return Error from operation + */ + virtual errlHndl_t getNORChipId( uint32_t& o_chipId ); + + public: + /** + * @brief Constructor + * @param[out] Return any error in constructor + * @param[in] Processor target associated with the LPC master + */ + SfcFake( errlHndl_t& o_err, + TARGETING::Target* i_proc + = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ); + + + protected: + /** + * @brief Pointer to fake memory location + */ + uint8_t* iv_fakePnor; + + /** + * @brief Size of allocated PNOR space in bytes + */ + size_t iv_sizeBytes; +}; + +#endif diff --git a/src/usr/pnor/sfc_ibm.C b/src/usr/pnor/sfc_ibm.C new file mode 100644 index 000000000..7574923b6 --- /dev/null +++ b/src/usr/pnor/sfc_ibm.C @@ -0,0 +1,1290 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfc_ibm.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 */ +/*****************************************************************************/ +// I n c l u d e s +/*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sfc_ibm.H" +#include "norflash.H" +using namespace PNOR; + +/*****************************************************************************/ +// C o n s t a n t s +/*****************************************************************************/ + + + + +/*****************************************************************************/ +// G l o b a l s +/*****************************************************************************/ + +// Initialized in pnorrp.C +extern trace_desc_t* g_trac_pnor; + +/*****************************************************************************/ +// M e t h o d s +/*****************************************************************************/ + +namespace PNOR { +/** + * @brief Wrapper for device driver constructor + */ +errlHndl_t create_SfcDD( SfcDD*& o_sfc, + TARGETING::Target* i_proc ) +{ + errlHndl_t l_err = NULL; + TRACFCOMP( g_trac_pnor, "Creating SfcIBM object" ); + o_sfc = new SfcIBM( l_err, i_proc ); + return l_err; +} + +}; + +/** + * @brief Constructor + */ +SfcIBM::SfcIBM( errlHndl_t& o_err, + TARGETING::Target* i_proc ) +: SfcDD(o_err,i_proc) +,iv_ffdcActive(false) +,iv_errorHandledCount(0) +,iv_resetActive(false) +{ +} + + +/** + * @brief Write a SFC Register + */ +errlHndl_t SfcIBM::writeReg( SfcRange i_range, + uint32_t i_addr, + uint32_t i_data ) +{ + errlHndl_t l_err = NULL; + uint32_t lpc_addr = i_addr; + LPC::TransType lpc_range = LPC::TRANS_LAST; + + // Find the appropriate LPC parms + sfc2lpc( i_range, i_addr, lpc_range, lpc_addr ); + + TRACDCOMP( g_trac_pnor, "SfcIBM::writeReg> SFC::%d-%.8X, LPC::%d-%.8X, i_data=0x%.8x", i_range, i_addr, lpc_range, lpc_addr, i_data ); + size_t reg_size = sizeof(uint32_t); + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &i_data, + reg_size, + DEVICE_LPC_ADDRESS(lpc_range,lpc_addr) ); + + return l_err; +} + + +/** + * @brief Read a SFC Register + */ +errlHndl_t SfcIBM::readReg( SfcRange i_range, + uint32_t i_addr, + uint32_t& o_data ) +{ + errlHndl_t l_err = NULL; + uint32_t lpc_addr = i_addr; + LPC::TransType lpc_range = LPC::TRANS_LAST; + + // Find the appropriate LPC parms + sfc2lpc( i_range, i_addr, lpc_range, lpc_addr ); + + size_t reg_size = sizeof(uint32_t); + l_err = deviceOp( DeviceFW::READ, + iv_proc, + &o_data, + reg_size, + DEVICE_LPC_ADDRESS(lpc_range,lpc_addr) ); + TRACDCOMP( g_trac_pnor, "SfcIBM::readReg> SFC::%d-%.8X, LPC::%d-%.8X, o_data=0x%.8x", i_range, i_addr, lpc_range, lpc_addr, o_data ); + + return l_err; +} + + +/** + * @brief Poll for SFC operation to complete and look for errors + */ +errlHndl_t SfcIBM::pollOpComplete( void ) +{ + TRACDCOMP( g_trac_pnor, "SfcIBM::pollOpComplete>" ); + errlHndl_t l_err = NULL; + ResetLevels l_resetLevel = RESET_CLEAR; + + do { + //Poll for complete status + SfcStatReg_t sfc_stat; + uint64_t poll_time = 0; + uint64_t loop = 0; + while( poll_time < SFC_POLL_TIME_NS ) + { + l_err = readReg(SFC_CMD_SPACE, + SFC_REG_STATUS, + sfc_stat.data32); + if(l_err) { break; } + + if( ( sfc_stat.done == 1 ) || + ( sfc_stat.timeout == 1 ) || + ( sfc_stat.illegal == 1 ) ) + { + break; + } + + // want to start out incrementing by small numbers then get bigger + // to avoid a really tight loop in an error case so we'll increase + // the wait each time through + ++loop; + nanosleep( 0, SFC_POLL_INCR_NS*loop ); + poll_time += SFC_POLL_INCR_NS*loop; + } + if( l_err ) { break; } + + // Look for errors, regardless of how we exited the loop + l_err = checkForErrors(l_resetLevel); + if( l_err ) { break; } + + // If no errors AND done bit not set, call out undefined error + if( sfc_stat.done == 0 ) + { + TRACFCOMP(g_trac_pnor, "SfcIBM::pollOpComplete> Error or timeout from SFC Status Register" ); + + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCIBM_POLLOPCOMPLETE + * @reasoncode PNOR::RC_SFC_TIMEOUT + * @userdata1[0:31] NOR Flash Chip ID + * @userdata1[32:63] Total poll time (ns) + * @userdata2[0:31] SFC Status Register + * @devdesc SfcIBM::pollOpComplete> Error or timeout from + * SFC Status Register + * @custdesc Hardware error accessing flash during IPL + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCIBM_POLLOPCOMPLETE, + PNOR::RC_SFC_TIMEOUT, + TWO_UINT32_TO_UINT64(iv_norChipId, + poll_time), + TWO_UINT32_TO_UINT64(sfc_stat.data32,0)); + + // Limited in callout: no PNOR target, so calling out processor + l_err->addHwCallout( + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + addFFDC(l_err); + l_err->collectTrace(PNOR_COMP_NAME); + l_err->collectTrace(XSCOM_COMP_NAME); + + // Reset LPC Slave since it appears to be hung - handled below + l_resetLevel = RESET_LPC_SLAVE; + + break; + } + TRACDCOMP(g_trac_pnor,"SfcIBM::pollOpComplete> command took %d ns", poll_time); + + }while(0); + + // If we have an error that requires a reset, do that here + if ( l_err && ( l_resetLevel != RESET_CLEAR ) ) + { + errlHndl_t tmp_err = NULL; + tmp_err = hwReset(l_resetLevel); + + if ( tmp_err ) + { + // Commit reset error as informational since we have + // original error l_err + TRACFCOMP(g_trac_pnor, "SfcIBM::pollOpComplete> Error from resetPnor() after previous error eid=0x%X. Committing resetPnor() error log eid=0x%X.", + l_err->eid(), tmp_err->eid()); + tmp_err->setSev(ERRORLOG::ERRL_SEV_INFORMATIONAL); + tmp_err->collectTrace(PNOR_COMP_NAME); + tmp_err->plid(l_err->plid()); + errlCommit(tmp_err, PNOR_COMP_ID); + } + } + + + return l_err; +} + + +/** + * @brief Load SFC command buffer with data from PNOR + */ +errlHndl_t SfcIBM::loadSfcBuf(uint32_t i_addr, + size_t i_size) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "SfcIBM::loadSfcBuf> i_addr=0x%.8x, i_size=0x%.8x", + i_addr, i_size ); + + do { + //Write flash address to ADR reg + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_ADR, + i_addr); + if(l_err) { break; } + + //Issue ReadRaw command with size to read + SfcCmdReg_t sfc_cmd; + sfc_cmd.opcode = SFC_OP_READRAW; + sfc_cmd.length = i_size; + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_CMD, + sfc_cmd.data32); + if(l_err) { break; } + + //Poll for complete status + l_err = pollOpComplete(); + if(l_err) { break; } + + }while(0); + + return l_err; + +} + + +/** + * @brief Flush SFC command buffer data out to PNOR Flash + */ +errlHndl_t SfcIBM::flushSfcBuf( uint32_t i_addr, + size_t i_size ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, + "SfcIBM::flushSfcBuf> i_addr=0x%.8x, i_size=0x%.8x", + i_addr, i_size ); + + do { + //Write flash address to ADR reg + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_ADR, + i_addr); + if(l_err) { break; } + + //Issue WriteRaw command + size to write + SfcCmdReg_t sfc_cmd; + sfc_cmd.opcode = SFC_OP_WRITERAW; + sfc_cmd.length = i_size; + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_CMD, + sfc_cmd.data32); + if(l_err) { break; } + + //Poll for complete status + l_err = pollOpComplete(); + if(l_err) { break; } + +#ifdef CONFIG_ALLOW_MICRON_PNOR + //check for special Micron Flag Status reg + if(iv_flashWorkarounds & HWWK_MICRON_WRT_ERASE) + { + l_err = PNOR::micronFlagStatus(this); + if(l_err) { break; } + } +#endif + + }while(0); + + return l_err; + +} + + +/** + * @brief Read data from the flash + */ +errlHndl_t SfcIBM::readFlash( uint32_t i_addr, + size_t i_size, + void* o_data ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "SfcIBM::readFlash> i_addr=0x%.8x, i_size=0x%.8x", + i_addr, i_size ); + + do{ + //Read directly from MMIO space + uint32_t* word_ptr = static_cast(o_data); + uint32_t word_size = i_size/4; + for( uint32_t words_read = 0; + words_read < word_size; + words_read ++ ) + { + l_err = readReg(SFC_MMIO_SPACE, + i_addr+words_read*4, //MMIO Address offset + word_ptr[words_read]); + if( l_err ) { break; } + } + if( l_err ) { break; } + }while(0); + + return l_err; +} + + +/** + * @brief Write data into flash + */ +errlHndl_t SfcIBM::writeFlash( uint32_t i_addr, + size_t i_size, + void* i_data ) +{ + TRACDCOMP( g_trac_pnor, "SfcIBM::writeFlash> i_addr=0x%.8x, i_size=0x%.8x", + i_addr, i_size ); + + errlHndl_t l_err = NULL; + + do{ + // Command based reads are buffered 256 bytes at a time. + uint32_t chunk_size = 0; + uint64_t addr = i_addr; + uint64_t end_addr = i_addr + i_size; + + while(addr < end_addr) + { + chunk_size = SFC_CMDBUF_SIZE; + if( (addr + SFC_CMDBUF_SIZE) > end_addr) + { + chunk_size = end_addr - addr; + } + + //write data to SFC CMD Buffer via MMIO + l_err = writeIntoBuffer(chunk_size, + (void*)((uint64_t)i_data + (addr-i_addr))); + if(l_err) { break;} + + //Push data from buffer out to flash + l_err = flushSfcBuf(addr, chunk_size); + if(l_err) { break;} + + addr += chunk_size; + } + if(l_err) { break;} + + } while(0); + + return l_err; +} + + +/** + * @brief Read data from SFC Command buffer + */ +errlHndl_t SfcIBM::readFromBuffer( size_t i_size, + void* o_data ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "SfcIBM::readFromBuffer> i_size=0x%.8x", + i_size ); + + // SFC Command Buffer is accessed 32-bits at a time + uint32_t* word_ptr = static_cast(o_data); + uint32_t word_size = (ALIGN_4(i_size))/4; + for( uint32_t words_read = 0; + words_read < word_size; + words_read ++ ) + { + l_err = readReg(SFC_CMDBUF_SPACE, + words_read*4, //Offset into CMD BUFF space in bytes + word_ptr[words_read]); + TRACDCOMP( g_trac_pnor, "SfcIBM::readFromBuffer: Read offset=0x%.8x, data_read=0x%.8x", words_read*4, word_ptr[words_read] ); + + if( l_err ) { break; } + } + + return l_err; +} + +/** + * @brief Write data into SFC Command buffer + */ +errlHndl_t SfcIBM::writeIntoBuffer( size_t i_size, + void* i_data ) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "SfcIBM::writeIntoBuffer> i_size=0x%.8x", + i_size ); + + // SFC Command Buffer is accessed 32-bits at a time + uint32_t* word_ptr = static_cast(i_data); + uint32_t word_size = i_size/4; + for( uint32_t words_read = 0; + words_read < word_size; + words_read ++ ) + { + l_err = writeReg(SFC_CMDBUF_SPACE, + words_read*4, //Offset into CMD BUFF space in bytes + word_ptr[words_read]); + if( l_err ) { break; } + } + + return l_err; +} + +/** + * @brief Erase a block of flash + */ +errlHndl_t SfcIBM::eraseFlash(uint32_t i_address) +{ + errlHndl_t l_err = NULL; + TRACFCOMP(g_trac_pnor, ">>SfcIBM::eraseFlash> Block 0x%.8X", i_address ); + + do { + if( i_address%iv_eraseSizeBytes != 0 ) + { + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCIBM_ERASEFLASH + * @reasoncode PNOR::RC_INVALID_ADDRESS + * @userdata1 Flash address being erased + * @userdata2 Nearest Erase Boundary + * @devdesc PnorDD::eraseFlash> Address not on erase boundary + * @custdesc Firmware error accessing flash during IPL + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCIBM_ERASEFLASH, + PNOR::RC_INVALID_ADDRESS, + TWO_UINT32_TO_UINT64(0,i_address), + i_address + - i_address%iv_eraseSizeBytes, + true /*Add HB SW Callout*/ ); + l_err->collectTrace(PNOR_COMP_NAME); + break; + } + + //Write erase address to ADR reg + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_ADR, + i_address); + if(l_err) { break; } + + //Issue Erase command + SfcCmdReg_t sfc_cmd; + sfc_cmd.opcode = SFC_OP_ERASM; + sfc_cmd.length = 0; //Not used for erase + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_CMD, + sfc_cmd.data32); + if(l_err) { break; } + + //Poll for complete status + l_err = pollOpComplete(); + if(l_err) { break; } + +#ifdef CONFIG_ALLOW_MICRON_PNOR + //check for special Micron Flag Status reg + if(iv_flashWorkarounds & HWWK_MICRON_WRT_ERASE) + { + l_err = PNOR::micronFlagStatus(this); + if(l_err) { break; } + } +#endif + } while(0); + + return l_err; +} + + +/** + * @brief Initialize and configure the SFC hardware + */ +errlHndl_t SfcIBM::hwInit( ) +{ + TRACFCOMP(g_trac_pnor, "SfcIBM::hwInit>" ); + errlHndl_t l_err = NULL; + + do { + //Determine NOR Flash type - triggers vendor specific workarounds + //We also use the chipID in some FFDC situations. + l_err = getNORChipId(iv_norChipId); + if(l_err) { break; } + TRACFCOMP(g_trac_pnor, + "SfcIBM::hwInit: iv_norChipId=0x%.8x> ", + iv_norChipId ); + + //Query the configured size of the erase block + l_err = readReg(SFC_CMD_SPACE, + SFC_REG_ERASMS, + iv_eraseSizeBytes); + if(l_err) { break; } + TRACFCOMP(g_trac_pnor,"iv_eraseSizeBytes=0x%X",iv_eraseSizeBytes); + + +#ifndef CONFIG_BMC_DOES_SFC_INIT + TRACFCOMP( g_trac_pnor, INFO_MRK "Initializing SFC registers" ); + + static struct + { + // Chip id to match or UNKNOWN_NOR_ID for all chips. + uint32_t chip_id; + // SFC register to set. + uint8_t reg; + // Value which is set in register. + uint32_t val; + } sfc_init_regs[] = { + //*** Direct access window and basic SFC settings. + //Set MMIO/Direct window to start at 64MB + { PNOR::UNKNOWN_NOR_ID, SFC_REG_OADRNB, 0x0C000000 }, + //Set the MMIO/Direct window size to 64MB + { PNOR::UNKNOWN_NOR_ID, SFC_REG_OADRNS, 0x0000000F }, + //Set the flash index to 0 + { PNOR::UNKNOWN_NOR_ID, SFC_REG_ADRCBF, 0x00000000 }, + //Set the flash size to 64MB + { PNOR::UNKNOWN_NOR_ID, SFC_REG_ADRCMF, 0x0000000F }, + //Enable Direct Access Cache + { PNOR::UNKNOWN_NOR_ID, SFC_REG_CONF, 0x00000001 }, + +#ifdef CONFIG_ALLOW_MICRON_PNOR + //*** Micron 512mb chip specific settings. + { PNOR::MICRON_NOR_ID, SFC_REG_SPICLK, + 0 << SFC_REG_SPICLK_OUTDLY_SHFT | + 0 << SFC_REG_SPICLK_INSAMPDLY_SHFT | + 1 << SFC_REG_SPICLK_CLKHI_SHFT | + 1 << SFC_REG_SPICLK_CLKLO_SHFT + }, + { PNOR::MICRON_NOR_ID, SFC_REG_CONF8, + 6 << SFC_REG_CONF8_CSINACTIVEREAD_SHFT | + 15 << SFC_REG_CONF8_DUMMY_SHFT | + SPI_JEDEC_FAST_READ << SFC_REG_CONF8_READOP_SHFT + }, + { PNOR::MICRON_NOR_ID, SFC_REG_CONF4, SPI_JEDEC_SECTOR_ERASE }, + { PNOR::MICRON_NOR_ID, SFC_REG_CONF5, 4096 }, + //*** End Micron +#endif + +#ifdef CONFIG_RHESUS + // HACK: Micron N25Q256A13 for use with EM100. + { 0x20ba1900, SFC_REG_CONF4, SPI_JEDEC_SECTOR_ERASE }, + { 0x20ba1900, SFC_REG_CONF5, 4096 }, +#endif + +#ifdef CONFIG_ALLOW_MACRONIX_PNOR + //*** Macronix 512mb chip specific settings. + { PNOR::MACRONIX_NOR_ID, SFC_REG_SPICLK, + 0 << SFC_REG_SPICLK_OUTDLY_SHFT | + 0 << SFC_REG_SPICLK_INSAMPDLY_SHFT | + 0 << SFC_REG_SPICLK_CLKHI_SHFT | + 0 << SFC_REG_SPICLK_CLKLO_SHFT + }, + { PNOR::MACRONIX_NOR_ID, SFC_REG_CONF8, + 2 << SFC_REG_CONF8_CSINACTIVEREAD_SHFT | + 8 << SFC_REG_CONF8_DUMMY_SHFT | + SPI_JEDEC_FAST_READ << SFC_REG_CONF8_READOP_SHFT + }, + { PNOR::MACRONIX_NOR_ID, SFC_REG_CONF4, SPI_JEDEC_SECTOR_ERASE }, + { PNOR::MACRONIX_NOR_ID, SFC_REG_CONF5, 4096 }, + //*** End Macronix +#endif + }; + + for ( size_t i = 0; + i < sizeof(sfc_init_regs) / sizeof(sfc_init_regs[0]); + ++i ) + { + if( (sfc_init_regs[i].chip_id == PNOR::UNKNOWN_NOR_ID) || + (sfc_init_regs[i].chip_id == iv_norChipId) ) + { + TRACDCOMP( g_trac_pnor, INFO_MRK " SFC reg %02x = %08x", + sfc_init_regs[i].reg, + sfc_init_regs[i].val ); + l_err = writeReg( SFC_CMD_SPACE, + sfc_init_regs[i].reg, + sfc_init_regs[i].val ); + if( l_err ) { break; } + } + } + if( l_err ) { break; } + +#if 0 //@fixme-RTC:109860 + // Enable 4-byte addressing. + l_err = SfcErrlFromRc( iv_sfc.set_4ba( &iv_sfc, 1 ) ); + if( l_err ) { break; } + + // Re-initialize internal erase size cached value. + l_err = SfcErrlFromRc( iv_sfc.get_erase_size( + &iv_sfc, &iv_eraseSizeBytes, NULL ) ); + if( l_err ) { break; } +#endif + +#endif //!CONFIG_BMC_DOES_SFC_INIT + + +#ifdef CONFIG_ALLOW_MICRON_PNOR + if( iv_norChipId == PNOR::MICRON_NOR_ID ) + { + l_err = PNOR::micronCheckForWorkarounds( this, + iv_flashWorkarounds ); + if(l_err) { break; } + } +#endif //CONFIG_ALLOW_MICRON_PNOR + + }while(0); + + TRACFCOMP(g_trac_pnor, "< SfcIBM::hwInit :: RC=%.4X", ERRL_GETRC_SAFE(l_err) ); + return l_err; +} + +/** + * @brief Convert a SFC address to a LPC address + */ +void SfcIBM::sfc2lpc( SfcRange i_sfcRange, + uint32_t i_sfcAddr, + LPC::TransType& o_lpcRange, + uint32_t& o_lpcAddr ) +{ + switch(i_sfcRange) + { + case SFC_MMIO_SPACE: + o_lpcRange = LPC::TRANS_FW; + o_lpcAddr = i_sfcAddr | SFC_MMIO_OFFSET; + break; + case SFC_CMD_SPACE: + o_lpcRange = LPC::TRANS_FW; + o_lpcAddr = i_sfcAddr | SFC_CMDREG_OFFSET; + break; + case SFC_CMDBUF_SPACE: + o_lpcRange = LPC::TRANS_FW; + o_lpcAddr = i_sfcAddr | SFC_CMDBUF_OFFSET; + break; + case SFC_LPC_SPACE: + o_lpcRange = LPC::TRANS_FW; + o_lpcAddr = i_sfcAddr; + break; + } //end switch +} + + +/** + * @brief Check For Errors in SFC Status Registers + */ +errlHndl_t SfcIBM::checkForErrors( ResetLevels &o_resetLevel ) +{ + errlHndl_t l_err = NULL; + bool errorFound = false; + + // Used to set Reset Levels, if necessary + o_resetLevel = RESET_CLEAR; + + // Default status values in case we fail in reading the registers + LpcSlaveStatReg_t lpc_slave_stat; + lpc_slave_stat.data32 = 0xDEADBEEF; + SfcStatReg_t sfc_stat; + sfc_stat.data32 = 0xDEADBEEF; + + do { + + // First Read LPC Slave Status Register + l_err = readReg( SFC_LPC_SPACE, + LPC_SLAVE_REG_STATUS, + lpc_slave_stat.data32 ); + + // If we can't read status register, exit out + if( l_err ) { break; } + + TRACDCOMP( g_trac_pnor, INFO_MRK"SfcIBM::checkForErrors> LPC Slave status reg: 0x%08llx", + lpc_slave_stat.data32); + + // Start with lighter reset level + if( 1 == lpc_slave_stat.lbusparityerror ) + { + errorFound = true; + o_resetLevel = RESET_LPC_SLAVE_ERRS; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> LPC Slave Local Bus Parity Error: status reg: 0x%08llx, ResetLevel=%d", + lpc_slave_stat.data32, o_resetLevel); + } + + // Check for more stronger reset level + if( 0 != lpc_slave_stat.lbus2opberr ) + { + errorFound = true; + // All of these errors require the SFC Local Bus Reset + o_resetLevel = RESET_SFC_LOCAL_BUS; + + if ( LBUS2OPB_ADDR_PARITY_ERR == lpc_slave_stat.lbus2opberr ) + { + // This error also requires LPC Slave Errors to be Cleared + o_resetLevel = RESET_SFCBUS_LPCSLAVE_ERRS; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> LBUS2OPB Address Parity Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", + lpc_slave_stat.data32, o_resetLevel); + + } + + else if ( LBUS2OPB_INVALID_SELECT_ERR == lpc_slave_stat.lbus2opberr) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> LBUS2OPB Invalid Select Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", + lpc_slave_stat.data32, o_resetLevel); + + } + else if ( LBUS2OPB_DATA_PARITY_ERR == lpc_slave_stat.lbus2opberr ) + { + // This error also requires LPC Slave Errors to be Cleared + o_resetLevel = RESET_SFCBUS_LPCSLAVE_ERRS; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> LBUS2OPB Data Parity Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", + lpc_slave_stat.data32, o_resetLevel); + + } + else if ( LBUS2OPB_MONITOR_ERR == lpc_slave_stat.lbus2opberr ) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> LBUS2OPB Monitor Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", + lpc_slave_stat.data32, o_resetLevel); + + } + + else if ( LBUS2OPB_TIMEOUT_ERR == lpc_slave_stat.lbus2opberr ) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> LBUS2OPB Timeout Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", + lpc_slave_stat.data32, o_resetLevel); + + } + else + { + // Just in case, clear LPC Slave Errors + o_resetLevel = RESET_LPC_SLAVE_ERRS; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> LBUS2OPB UNKNOWN Error: LPC Slave status reg: 0x%08llx, ResetLevel=%d", + lpc_slave_stat.data32, o_resetLevel); + } + + } + + // Second Read SFC and check for error bits + l_err = readReg(SFC_CMD_SPACE, + SFC_REG_STATUS, + sfc_stat.data32); + + // If we can't read status register, exit out + if( l_err ) { break; } + + TRACDCOMP( g_trac_pnor, INFO_MRK"SfcIBM::checkForErrors> SFC status reg(0x%X): 0x%08llx", + SFC_CMD_SPACE|SFC_REG_STATUS,sfc_stat.data32); + + // No resets needed for these errors + if( 1 == sfc_stat.eccerrcntr ) + { + errorFound = true; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> Threshold of SRAM ECC Errors Reached: SFC status reg: 0x%08llx, ResetLevel=%d", + sfc_stat.data32, o_resetLevel); + } + + if( 1 == sfc_stat.eccues ) + { + errorFound = true; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> SRAM Command Uncorrectable ECC Error: SFC status reg: 0x%08llx, ResetLevel=%d", + sfc_stat.data32, o_resetLevel); + } + + if( 1 == sfc_stat.illegal ) + { + errorFound = true; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> Previous Operation was Illegal: SFC status reg: 0x%08llx, ResetLevel=%d", + sfc_stat.data32, o_resetLevel); + } + + if( 1 == sfc_stat.eccerrcntn ) + { + errorFound = true; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> Threshold for Flash ECC Errors Reached: SFC status reg: 0x%08llx, ResetLevel=%d", + + sfc_stat.data32, o_resetLevel); + } + + if( 1 == sfc_stat.eccuen ) + { + errorFound = true; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> Flash Command Uncorrectable ECC Error: SFC status reg: 0x%08llx, ResetLevel=%d", + sfc_stat.data32, o_resetLevel); + } + + if( 1 == sfc_stat.timeout ) + { + errorFound = true; + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> Timeout: SFC status reg: 0x%08llx, ResetLevel=%d", + sfc_stat.data32, o_resetLevel); + } + + }while(0); + + + // If there is any error create an error log + if ( errorFound ) + { + // If we failed on a register read above, but still found an error, + // delete register read error log and create an original error log + // for the found error + if ( l_err ) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::checkForErrors> Deleting register read error. Returning error created for the found error"); + delete l_err; + } + + + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCIBM_CHECKFORERRORS + * @reasoncode PNOR::RC_ERROR_IN_STATUS_REG + * @userdata1[0:31] SFC Status Register + * @userdata1[32:63] LPC Slave Status Register + * @userdata2 Reset Level + * @devdesc SfcIBM::checkForErrors> Error(s) found in SFC + * and/or LPC Slave Status Registers + * @custdesc A problem occurred while accessing the boot flash. + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCIBM_CHECKFORERRORS, + PNOR::RC_ERROR_IN_STATUS_REG, + TWO_UINT32_TO_UINT64( + sfc_stat.data32, + lpc_slave_stat.data32), + o_resetLevel ); + + // Limited in callout: no PNOR target, so calling out processor + l_err->addHwCallout( + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL, + HWAS::SRCI_PRIORITY_HIGH, + HWAS::NO_DECONFIG, + HWAS::GARD_NULL ); + + addFFDC(l_err); + l_err->collectTrace(PNOR_COMP_NAME); + } + + return l_err; + +} + +/** + * @brief Add FFDC Error Registers to an existing Error Log + */ +void SfcIBM::addFFDC(errlHndl_t & io_errl) +{ + errlHndl_t tmp_err = NULL; + uint32_t data32 = 0; + size_t size32 = sizeof(data32); + + // check iv_ffdcActive to avoid infinite loops + if ( iv_ffdcActive == false ) + { + iv_ffdcActive = true; + + TRACFCOMP( g_trac_pnor, ENTER_MRK"SfcIBM::addFFDC> adding FFDC to Error Log EID=0x%X, PLID=0x%X", io_errl->eid(), io_errl->plid() ); + + ERRORLOG::ErrlUserDetailsLogRegister l_eud(iv_proc); + + // Add LPC Slave Status Register + LpcSlaveStatReg_t lpc_slave_stat; + tmp_err = readReg(SFC_LPC_SPACE, + LPC_SLAVE_REG_STATUS, + lpc_slave_stat.data32); + if ( tmp_err ) + { + delete tmp_err; + TRACFCOMP( g_trac_pnor, "SfcIBM::addFFDC> Fail reading LPC Slave Status Register"); + } + else + { + LPC::TransType lpc_range; + uint32_t lpc_addr; + sfc2lpc( SFC_LPC_SPACE, LPC_SLAVE_REG_STATUS, + lpc_range, lpc_addr ); + l_eud.addDataBuffer(&lpc_slave_stat.data32, size32, + DEVICE_LPC_ADDRESS( lpc_range, + lpc_addr ) ); + } + + // Add SFC Registers + uint32_t sfc_regs[] = { + SFC_REG_STATUS, + SFC_REG_CONF, + SFC_REG_CMD, + SFC_REG_ADR, + SFC_REG_ERASMS, + SFC_REG_ERASLGS, + SFC_REG_CONF4, + SFC_REG_CONF5, + SFC_REG_ADRCBF, + SFC_REG_ADRCMF, + SFC_REG_OADRNB, + SFC_REG_OADRNS, + SFC_REG_CHIPIDCONF, + SFC_REG_ERRCONF, + SFC_REG_ERRTAG, + SFC_REG_ERROFF, + SFC_REG_ERRSYN, + SFC_REG_ERRDATH, + SFC_REG_ERRDATL, + SFC_REG_ERRCNT, + SFC_REG_CLRCNT, + SFC_REG_ERRINJ, + SFC_REG_PROTA, + SFC_REG_PROTM, + SFC_REG_ECCADDR, + SFC_REG_ECCRNG, + SFC_REG_ERRORS, + SFC_REG_INTMSK, + SFC_REG_INTENM, + SFC_REG_CONF2, + SFC_REG_CONF3 + }; + + + for( size_t x=0; x<(sizeof(sfc_regs)/sizeof(sfc_regs[0])); x++ ) + { + tmp_err = readReg( SFC_CMD_SPACE, + sfc_regs[x], + data32 ); + + if( tmp_err ) + { + delete tmp_err; + } + else + { + LPC::TransType lpc_range; + uint32_t lpc_addr; + sfc2lpc( SFC_CMD_SPACE, sfc_regs[x], + lpc_range, lpc_addr ); + l_eud.addDataBuffer(&data32, size32, + DEVICE_LPC_ADDRESS(lpc_range,lpc_addr)); + } + } + + l_eud.addToLog(io_errl); + + TRACFCOMP( g_trac_pnor, EXIT_MRK"SfcIBM::addFFDC> Information added to error log"); + + // reset FFDC active flag + iv_ffdcActive = false; + } + + return; +} + + +/** + * @brief Send a user-defined SPI command + */ +errlHndl_t SfcIBM::sendSpiCmd( uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ) +{ + errlHndl_t errhdl = NULL; + + do { +#ifdef CONFIG_ALLOW_MICRON_PNOR + //Do a read of flash address zero to workaround + // a micron bug with extended reads + if( (PNOR::HWWK_MICRON_EXT_READ & iv_flashWorkarounds) + && (i_readCnt > 4) ) + { + uint32_t ignored = 0; + errhdl = readFlash( 0, 1, &ignored ); + if(errhdl) { break; } + } +#endif + + //Configure the custom command definition + SfcCustomReg_t confreg; + confreg.opcode = i_opCode; + confreg.length = i_writeCnt; + confreg.write = 1; + if( i_readCnt > 0 ) + { + confreg.length = i_readCnt; + confreg.read = 1; + confreg.write = 0; + } + + //Setup the address if needed + if( i_address != NO_ADDRESS ) + { + confreg.needaddr = 1; + errhdl = writeReg(SFC_CMD_SPACE, + SFC_REG_ADR, + i_address); + if( errhdl ) { break; } + } + + //@fixme-RTC:109860 - handle write data someday (no current need) + assert( i_writeCnt == 0 ); + + //Setup the custom command reg + errhdl = writeReg(SFC_CMD_SPACE, + SFC_REG_CHIPIDCONF, + confreg.data32); + if( errhdl ) { break; } + + //Issue Get Chip ID command + SfcCmdReg_t sfc_cmd; + sfc_cmd.opcode = SFC_OP_CHIPID; + sfc_cmd.length = 0; + errhdl = writeReg(SFC_CMD_SPACE, + SFC_REG_CMD, + sfc_cmd.data32); + if( errhdl ) { break; } + + //Poll for complete status + errhdl = pollOpComplete(); + if( errhdl ) { break; } + + //Return the data if this is a read + if( i_readCnt > 0 ) + { + //Read the Status from the Command Buffer + errhdl = readFromBuffer( i_readCnt, o_readData ); + if(errhdl) { break; } + } + } while(0); + + + return errhdl; +} + +/** + * @brief Return first 3 bytes of NOR chip id + * @return Error from operation + */ +errlHndl_t SfcIBM::getNORChipId( uint32_t& o_chipId ) +{ + errlHndl_t l_err = NULL; + TRACFCOMP( g_trac_pnor, "SfcIBM::getNORChipId>" ); + + do { + if( iv_norChipId != PNOR::UNKNOWN_NOR_ID ) + { + o_chipId = iv_norChipId; + break; + } + + //Configure Get Chip ID opcode + uint32_t confData = SPI_JEDEC_CHIPID << 24; + confData |= 0x00800003; // 8-> read, 3->3 bytes + TRACDCOMP( g_trac_pnor, "SfcIBM::getNORChipId> confData=0x%.8x", + confData ); + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_CHIPIDCONF, + confData); + if(l_err) { break; } + + //Issue Get Chip ID command + SfcCmdReg_t sfc_cmd; + sfc_cmd.opcode = SFC_OP_CHIPID; + sfc_cmd.length = 0; + + l_err = writeReg(SFC_CMD_SPACE, + SFC_REG_CMD, + sfc_cmd.data32); + if(l_err) { break; } + + //Poll for complete status + l_err = pollOpComplete(); + if(l_err) { break; } + + //Read the ChipID from the Command Buffer + l_err = readReg(SFC_CMDBUF_SPACE, + 0, //Offset into CMD BUFF space in bytes + o_chipId); + if(l_err) { break; } + + // Only look at a portion of the data that is returned + o_chipId &= ID_MASK; + iv_norChipId = o_chipId; + } while(0); + + return l_err; + +} + + +/** + * @brief Reset hardware to get into clean state + */ +errlHndl_t SfcIBM::hwReset( ResetLevels i_resetLevel ) +{ + errlHndl_t l_err = NULL; + + // @todo RTC 109999 - Skipping because SFC resets can + // cause problems on subsequent reads and writes + TRACFCOMP(g_trac_pnor, "SfcIBM::hwReset> Skipping reset"); +#if 0 + + // check iv_reset_active to avoid infinite loops + // and don't reset if in the middle of FFDC collection + if ( ( iv_resetActive == false ) && + ( iv_ffdcActive == false ) ) + { + iv_resetActive = true; + + TRACFCOMP(g_trac_pnor, "SfcIBM::hwReset> i_pnorResetLevel=0x%.8X", i_resetLevel); + + do { + // 32 bits for address and data for LPC operations + uint32_t lpc_data=0; + size_t reg_size = sizeof(uint32_t); + + /***************************************/ + /* Handle the different reset levels */ + /***************************************/ + switch(i_resetLevel) + { + case RESET_CLEAR: + {// Nothing to do here, so just break + break; + } + + case RESET_LPC_SLAVE: + { + TRACFCOMP(g_trac_pnor, "SfcIBM::hwReset> Writing bit0 of LPC_SLAVE_REG_RESET to reset LPC Slave Logic"); + lpc_data = 0x80000000; + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &lpc_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_REG, + LPC_SLAVE_REG_RESET) ); + break; + } + + case RESET_LPC_SLAVE_ERRS: + { + TRACFCOMP(g_trac_pnor, "SfcIBM::hwReset> Writing bit1 of LPC_SLAVE_REG_RESET to reset LPC Slave Errors"); + lpc_data = 0x40000000; + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &lpc_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_REG, + LPC_SLAVE_REG_RESET) ); + break; + } + + case RESET_SFC_LOCAL_BUS: + { + TRACFCOMP(g_trac_pnor, "SfcIBM::hwReset> Writing bit2 of LPC_SLAVE_REG_RESET to reset Local SFC Bus. Requires PNOR reinitialization"); + lpc_data = 0x20000000; + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &lpc_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_REG, + LPC_SLAVE_REG_RESET) ); + if (l_err) { break; } + + l_err = hwInit(); + break; + } + + case RESET_SFCBUS_LPCSLAVE_ERRS: + { + // Must handle both errors + TRACFCOMP(g_trac_pnor, "SfcIBM::hwReset> Writing bits1,2 of LPC_SLAVE_REG_RESET to reset LPC Slave Errors and Local SFC Bus. Requires PNOR reinitialization"); + lpc_data = 0x60000000; + l_err = deviceOp( DeviceFW::WRITE, + iv_proc, + &lpc_data, + reg_size, + DEVICE_LPC_ADDRESS(LPC::TRANS_REG, + LPC_SLAVE_REG_RESET) ); + if (l_err) { break; } + + l_err = hwInit(); + break; + } + + // else - unsupported reset level + default: + { + + TRACFCOMP( g_trac_pnor, ERR_MRK"SfcIBM::hwReset> Unsupported Reset Level Passed In: 0x%X", i_resetLevel); + + /*@ + * @errortype + * @moduleid PNOR::MOD_SFCIBM_HWRESET + * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION + * @userdata1 Unsupported Reset Level Parameter + * @userdata2 + * @devdesc SfcIBM::hwReset> Unsupported Reset + * Level requested + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_SFCIBM_HWRESET, + PNOR::RC_UNSUPPORTED_OPERATION, + i_resetLevel, + 0, + true /*SW error*/); + + l_err->collectTrace(PNOR_COMP_NAME); + break; + } + }// end switch + + if ( l_err ) + { + // Indicate that we weren't successful in resetting PNOR + TRACFCOMP( g_trac_pnor,ERR_MRK"SfcIBM::hwReset>> Fail doing PNOR reset at level 0x%X (recovery count=%d): eid=0x%X", i_resetLevel, iv_errorHandledCount, l_err->eid()); + } + else + { + // Successful, so increment recovery count + iv_errorHandledCount++; + + TRACFCOMP( g_trac_pnor,INFO_MRK"SfcIBM::hwReset>> Successful PNOR reset at level 0x%X (recovery count=%d)", i_resetLevel, iv_errorHandledCount); + } + + + } while(0); + + // reset RESET active flag + iv_resetActive = false; + } + +#endif + + return l_err; +} + diff --git a/src/usr/pnor/sfc_ibm.H b/src/usr/pnor/sfc_ibm.H new file mode 100644 index 000000000..03ce0b772 --- /dev/null +++ b/src/usr/pnor/sfc_ibm.H @@ -0,0 +1,502 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfc_ibm.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 __PNOR_SFC_IBM_H +#define __PNOR_SFC_IBM_H + +#include +#include +#include +#include +#include "sfcdd.H" + +/** @file sfc_ibm.H + * @brief Provides the logic to access and configure the + * IBM Serial Flash Controller which provides access + * to the PNOR + */ + +/** + * @brief IBM SFC Device Driver Class + * Provides the logic to access and configure the + * IBM Serial Flash Controller which provides access + * to the PNOR + */ +class SfcIBM : public SfcDD +{ + + public: //SfcDD methods + /** + * @brief Initialize the SFC Hardware + * + * @return void + */ + virtual errlHndl_t hwInit(); + + /** + * @brief Return first 3 bytes of NOR chip id + * + * @parm[out] NOR chip id + * + * @return Error from operation + */ + virtual errlHndl_t getNORChipId( uint32_t& o_chipId ); + + /** + * @brief Read data from the PNOR flash + * + * @parm i_addr PNOR flash Address to read + * @parm i_size Amount of data to read, in bytes. + * @parm o_data Buffer to read data into + * + * @return Error from operation + */ + virtual errlHndl_t readFlash(uint32_t i_addr, + size_t i_size, + void* o_data); + + /** + * @brief Write data to the PNOR flash + * + * @parm i_addr PNOR flash Address to write + * @parm i_size Amount of data to write, in bytes. + * @parm i_data Buffer containing data to write + * + * @return Error from operation + */ + virtual errlHndl_t writeFlash(uint32_t i_addr, + size_t i_size, + void* i_data); + + /** + * @brief Erase a block of flash + * + * @parm i_address Offset into flash to erase, aligned to erase block + * + * @return Error from operation + */ + virtual errlHndl_t eraseFlash(uint32_t i_address); + + /** + * @brief Send a user-defined SPI command + * + * @parm[in] i_opCode: command to send into controller first + * @parm[in] i_address: address for those commands that need it + * @parm[in] i_writeCnt: number of bytes to write to device + * @parm[in] i_writeData: write data buffer + * @parm[in] i_readCnt: number of bytes to read from device + * @parm[out] o_readData: read data buffer + * + * @return Error from operation + */ + virtual errlHndl_t sendSpiCmd( uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ); + + /** + * @brief Add error registers to an existing Error Log + * + * @param[in] io_errhdl: Error log to add data to + */ + virtual void addFFDC( errlHndl_t& io_errhdl ); + + public: //SfcIBM methods + /** + * @brief Constructor + * @param[out] Return any error in constructor + * @param[in] Processor target associated with the LPC master + */ + SfcIBM( errlHndl_t& o_err, + TARGETING::Target* i_proc + = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ); + + + protected: //SfcDD methods + + /** + * @brief Reset hardware to get into clean state + * + * @parm i_resetLevel How much SFC logic to reset + * + * @return errlHndl_t NULL on success, else error log + */ + virtual errlHndl_t hwReset( ResetLevels i_resetLevel ); + + + protected: //SfcIBM stuff + + /** @brief General constants */ + enum + { + /** @brief SFC Command buffer is 0x100/256 bytes/0x40 words */ + SFC_CMDBUF_SIZE = 256, + + //@todo-RTC:95125 Find out Max time to wait + /** @brief Max time to wait for SFC op to complete */ + SFC_POLL_TIME_NS = 1000000000, + + /** @brief Minimum increment during poll for complete */ + SFC_POLL_INCR_NS = 10, + + /** @brief Offset to SFC command regs, from FW base */ + SFC_CMDREG_OFFSET = 0x00000C00, + + /** @brief Offset to SFC buffer space, from FW base */ + SFC_CMDBUF_OFFSET = 0x00000D00, + + /** @brief Offset to SFC direct read space, from FW base */ + SFC_MMIO_OFFSET = 0x0C000000, + }; + + /** + * @brief SFC Op Codes + * OP Codes for the SFC Command Register + */ + enum SfcOpCodes { + SFC_OP_READRAW = 0x03, /**< Read Raw */ + SFC_OP_WRITERAW = 0x02, /**< Write Raw */ + SFC_OP_ERASM = 0x32, /**< Erase Small */ + SFC_OP_ERALG = 0x34, /**< Erase Large */ + SFC_OP_ENWRITPROT = 0x53, /**< Enable WRite Protect */ + SFC_OP_CHIPID = 0x1F, /**< Get Chip ID */ + SFC_OP_STATUS = 0x05, /**< Get Status */ + SFC_OP_TURNOFF = 0x5E, /**< Turn Off */ + SFC_OP_TURNON = 0x50, /**< Turn On */ + SFC_OP_ABORT = 0x6F, /**< Super-Abort */ + SFC_OP_START4BA = 0x37, /**< Start 4BA */ + SFC_OP_END4BA = 0x69, /**< End 4BA */ + SFC_OP_INVALID = 0x00, /**< Invalid - used for testing */ + }; + + //@fixme-RTC:109860 : Create structures for this data +#define SFC_REG_SPICLK_OUTDLY_SHFT 24 +#define SFC_REG_SPICLK_INSAMPDLY_SHFT 16 +#define SFC_REG_SPICLK_CLKHI_SHFT 8 +#define SFC_REG_SPICLK_CLKLO_SHFT 0 +#define SFC_REG_CONF8_CSINACTIVEREAD_SHFT 18 +#define SFC_REG_CONF8_DUMMY_SHFT 8 +#define SFC_REG_CONF8_READOP_SHFT 0 + + /** + * @brief Ranges of SFC addresses + */ + enum SfcRange { + SFC_CMD_SPACE, /**< Indicate accessing command reg */ + SFC_CMDBUF_SPACE, /**< Indicate accessing command buffer space */ + SFC_MMIO_SPACE, /**< Indicate accessing MMIO based Direct Reads */ + SFC_LPC_SPACE, /**< Indicate LPC Slave Space */ + }; + + + /** + * @brief SFC Registers + * These are offsets within the SFC Register Space + */ + enum SfcRegAddr { + SFC_REG_CONF = 0x10, /**< CONF: Direct Access Configuration */ + SFC_REG_STATUS = 0x0C, /**< STATUS : Status Reg */ + SFC_REG_SPICLK = 0x3C, /**< SPICLK : SPI clock rate config */ + SFC_REG_CMD = 0x40, /**< CMD : Command */ + SFC_REG_ADR = 0x44, /**< ADR : Address */ + SFC_REG_ERASMS = 0x48, /**< ERASMS : Small Erase Block Size */ + SFC_REG_ERASLGS = 0x4C, /**< ERALGS : Large Erase Block Size */ + SFC_REG_CONF4 = 0x54, /**< CONF4 : SPI Op Code for Small Erase */ + SFC_REG_CONF5 = 0x58, /**< CONF5 : Small Erase Size config reg */ + SFC_REG_CONF8 = 0x64, /**< CONF8 : Read Command */ + SFC_REG_ADRCBF = 0x80, /**< ADRCBF : First Intf NOR Addr Offset */ + SFC_REG_ADRCMF = 0x84, /**< ADRCMF : First Intf NOR Allocation */ + SFC_REG_ADRCBS = 0x88, /**< ADRCBS : Second Intf NOR Addr Offset */ + SFC_REG_ADRCMS = 0x8C, /**< ADRCMS : Second Intf NOR Allocation */ + SFC_REG_OADRNB = 0x90, /**< OADRNB : Direct Access OBP Window Base Address */ + SFC_REG_OADRNS = 0x94, /**< OADRNS : DIrect Access OPB Window Size */ + SFC_REG_CHIPIDCONF = 0x9C, /**< CHIPIDCONF : config ChipId CMD */ + SFC_REG_ERRCONF = 0x6C, /**< ERRCONF : Configures error counts that + cause interupts */ + SFC_REG_ERRTAG = 0x1C, /**< ERRTAG : Holds Control Info of Error */ + SFC_REG_ERROFF = 0x20, /**< ERROFF : Holds Address Info of Error */ + SFC_REG_ERRSYN = 0x24, /**< ERRSYN : Holds Syndrome That Caused Error*/ + SFC_REG_ERRDATH = 0x28, /**< ERRDATH : Holds Most Signifcant Word of + Double Word That Caused Error */ + SFC_REG_ERRDATL = 0x2C, /**< ERRDATL : Holds Least Signifcant Word of + Double Word That Caused Error */ + SFC_REG_ERRCNT = 0x30, /**< ERRCNT : Counts The Number Of Errors */ + SFC_REG_CLRCNT = 0x34, /**< CLRCNT : Which Bits To Clear In ERRCNT */ + SFC_REG_ERRINJ = 0x38, /**< ERRINJ : Force Errors Into Read Paths */ + SFC_REG_PROTA = 0x70, /**< PROTA : Write Protect Range Address Base */ + SFC_REG_PROTM = 0x74, /**< PROTM : Write Protect Range Size */ + SFC_REG_ECCADDR = 0x78, /**< ECCADDR : ECC Disable Range Base Address */ + SFC_REG_ECCRNG = 0x7C, /**< ECCRNG : ECC Disable Range Size */ + SFC_REG_ERRORS = 0x00, /**< ERRORS : Collection of Error Status Bits */ + SFC_REG_INTMSK = 0x04, /**< INTMSK : Record of Events That Could Lead + To Interupt */ + SFC_REG_INTENM = 0x14, /**< INTENM : Controls Which Events Lead + To Interupts */ + SFC_REG_CONF2 = 0x18, /**< CONF2 : SPI Configuration */ + SFC_REG_CONF3 = 0x50, /**< CONF3 : SPI Recovery */ + + }; + + /** + * @brief LPC Slave Registers + * These are offsets within the LPC Slave Register Space + */ + enum LpcSlaveRegAddr + { + LPC_SLAVE_REG_STATUS = 0x14, /**< STATUS: read-only */ + LPC_SLAVE_REG_RESET = 0x14, /**< RESET : write-only */ + }; + + /** + * @brief LPC Slave Status Register Layout + */ + union LpcSlaveStatReg_t + { + uint32_t data32; + struct + { + uint32_t lbusowner : 2; /**< 0:1 = Local Bus Owner */ + uint32_t lbusparityerror : 1; /**< 2 = Local Bus Parity Error */ + uint32_t lbus2opberr : 3; /**< 3:5 = Errors From LBUS2OPB */ + uint32_t unused : 26; /**< 6:21 = Not Currently Used */ + }; + LpcSlaveStatReg_t() : data32(0) {}; + }; + + /** + * @brief LPC Slave Reset Register Layout + */ + union LpcSlaveResetReg_t + { + uint32_t data32; + struct + { + uint32_t lpcslave : 1; /**< 0 Reset LPC Slave */ + uint32_t lpcslaveerrs : 1; /**< 1 Reset LPC Slave Errors */ + uint32_t localbus : 1; /**< 2 Reset Local Bus */ + uint32_t unused : 29; /**< 4:31 = Not Currently Used */ + }; + LpcSlaveResetReg_t() : data32(0) {}; + }; + + + /** + * @brief LPC Slave LBUS2OPB Errors + * Translation of LPC Slave Status Register Bits 3:5 + */ + enum LpcSlaveLbus2OpbErrors { + LBUS2OPB_ADDR_PARITY_ERR = 0b010, /**< Address Parity Error */ + LBUS2OPB_INVALID_SELECT_ERR = 0b001, /**< Invalid Select Error */ + LBUS2OPB_DATA_PARITY_ERR = 0b011, /**< Data Parity Error */ + LBUS2OPB_MONITOR_ERR = 0b100, /**< Monitor Error */ + LBUS2OPB_TIMEOUT_ERR = 0b101, /**< Timeout Error */ + }; + + + + + /** + * @brief SFC Command Register Layout + */ + union SfcCmdReg_t + { + uint32_t data32; + struct + { + uint32_t reserved : 16; /**< 0:15 = Reserved */ + uint32_t opcode : 7; /**< 16:22 = OpCode */ + uint32_t length : 9; /**< 22:31 = Num bytes for Read/Write Raw */ + }; + SfcCmdReg_t() : data32(0) {}; + }; + + + /** + * @brief SFC Status Register Layout + */ + union SfcStatReg_t + { + uint32_t data32; + struct + { + uint32_t unused : 20; /**< 0:19 = Not Currently Used */ + uint32_t eccerrcntr : 1; /**< 20 Threshold for SRAM ECC errors */ + uint32_t eccues : 1; /**< 21 SRAM cmd uncorrectable ECC error*/ + uint32_t unused_22 : 3; /**< 22:24 = Not Currently Used */ + uint32_t cmdexe : 1; /**< 25 Previous cmd is in progress */ + uint32_t cmdwait : 1; /**< 26 Previous cmd waiting to execute */ + uint32_t illegal : 1; /**< 27 Previous op illegal */ + uint32_t eccerrcntn : 1; /**< 28 Threshold for Flash ECC errors */ + uint32_t eccuen : 1; /**< 29 Flash cmd uncorrectable ECC err */ + uint32_t timeout : 1; /**< 30 Timeout */ + uint32_t done : 1; /**< 31 Done */ + }; + SfcStatReg_t() : data32(0) {}; + }; + + + /** + * @brief SFC ConfId Register Layout + */ + union SfcCustomReg_t + { + uint32_t data32; + struct + { + uint32_t opcode : 8; + uint32_t read : 1; + uint32_t write : 1; + uint32_t needaddr : 1; + uint32_t clocks : 5; + uint32_t reserved : 8; + uint32_t length : 8; + }; + SfcCustomReg_t() : data32(0) {}; + }; + + /** + * @brief Write a SFC Register + * + * @parm i_range SFC Address Range + * @parm i_addr SFC Register to write + * @parm i_data Data to write + * + * @return Error from operation + */ + errlHndl_t writeReg(SfcRange i_range, + uint32_t i_addr, + uint32_t i_data); + + /** + * @brief Read a SFC Register + * + * @parm i_range SFC Address Range + * @parm i_addr SFC Register to read + * @parm o_data Data to write + * + * @return Error from operation + */ + errlHndl_t readReg(SfcRange i_range, + uint32_t i_addr, + uint32_t& o_data); + + /** + * @brief Poll for SFC operation to complete and look for + * errors + * + * @return Error from operation + */ + errlHndl_t pollOpComplete( void ); + + /** + * @brief Load SFC command buffer with data from PNOR + * + * @parm i_addr PNOR flash Address to read + * @parm i_size Number of bytes to read.to command buffer + * + * @return Error from operation + */ + errlHndl_t loadSfcBuf(uint32_t i_addr, + size_t i_size); + + /** + * @brief Flush SFC command buffer contents out to PNOR Flash + * + * @parm[in] i_addr PNOR flash Address to write + * @parm[in] i_size Number of bytes to write.to command buffer + * + * @return Error from operation + */ + errlHndl_t flushSfcBuf(uint32_t i_addr, + size_t i_size); + + /** + * @brief Read data in SFC Command buffer and put into buffer + * + * @parm[in] i_size Amount of data in Cmd Buffer to read, in bytes. + * @parm[out] o_data Buffer to read data into + * + * @return Error from operation + */ + errlHndl_t readFromBuffer(size_t i_size, + void* o_data); + + /** + * @brief Write data into SFC Command buffer + * + * @parm[in] i_size Amount of data in Cmd Buffer to write, in bytes. + * @parm[out] o_data Buffer to read data from + * + * @return Error from operation + */ + errlHndl_t writeIntoBuffer(size_t i_size, + void* i_data); + + /** + * @brief Convert a SFC address to a LPC address + * + * @parm[in] i_sfcRange SFC Address range + * @parm[in] i_sfcAddr SFC Address relative to i_sfcRange + * @parm[out] o_lpcRange LPC Address type + * @parm[out] o_lpcAddr LPC Address relative to o_lpcRange + */ + void sfc2lpc( SfcRange i_sfcRange, + uint32_t i_sfcAddr, + LPC::TransType& o_lpcRange, + uint32_t& o_lpcAddr ); + + /** + * @brief Check For Errors in SFC Status Registers + * + * @parm o_resetLevel if error, reset level to clear error + * @return Error log if error found + */ + errlHndl_t checkForErrors( ResetLevels &o_resetLevel ); + + + /** + * @brief Indicates if class is currently collecting FFDC data + */ + bool iv_ffdcActive; + + /** + * @brief Number of times recovered from an error + */ + uint32_t iv_errorHandledCount; + + /** + * @brief Indicates if class is currently doing a RESET procedure + */ + bool iv_resetActive; + + + // Needed for testcases + friend class PnorDdTest; +}; + + +#endif diff --git a/src/usr/pnor/sfcdd.C b/src/usr/pnor/sfcdd.C new file mode 100644 index 000000000..0aad547c1 --- /dev/null +++ b/src/usr/pnor/sfcdd.C @@ -0,0 +1,120 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfcdd.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 */ +/*****************************************************************************/ +// I n c l u d e s +/*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sfcdd.H" +#include "norflash.H" + + +/*****************************************************************************/ +// C o n s t a n t s +/*****************************************************************************/ + + + + +/*****************************************************************************/ +// G l o b a l s +/*****************************************************************************/ + +// Initialized in pnorrp.C +extern trace_desc_t* g_trac_pnor; + + +/*****************************************************************************/ +// M e t h o d s +/*****************************************************************************/ + +/** + * @brief Constructor + */ +SfcDD::SfcDD( errlHndl_t& o_err, + TARGETING::Target* i_proc ) +: iv_proc(i_proc) +, iv_flashWorkarounds(PNOR::HWWK_NO_WORKAROUNDS) +, iv_norChipId(PNOR::UNKNOWN_NOR_ID) +, iv_eraseSizeBytes(4*KILOBYTE) //default to 4KB blocks +{ + o_err = NULL; +} + +/** + * @brief Destructor + */ +SfcDD::~SfcDD() +{ + // Nothing to do by default +} + +/** + * @brief Read the NOR FLash ChipID + */ +errlHndl_t SfcDD::getNORChipId(uint32_t& o_chipId) +{ + errlHndl_t l_err = NULL; + + if( iv_norChipId == PNOR::UNKNOWN_NOR_ID ) + { + o_chipId = 0; + l_err = sendSpiCmd( PNOR::SPI_JEDEC_CHIPID, + SfcDD::NO_ADDRESS, + 0, NULL, + 4, reinterpret_cast(&o_chipId) ); + if( !l_err ) + { + // Only look at first 3 bytes of chipid + iv_norChipId = o_chipId & PNOR::ID_MASK; + } + } + + o_chipId = iv_norChipId; + TRACFCOMP( g_trac_pnor, "SfcDD::getNORChipId> chipid=%.8X", o_chipId ); + return l_err; +} + +/** + * @brief Informs caller if PNORDD is using + * L3 Cache for fake PNOR or not. + */ +bool SfcDD::usingL3Cache( void ) +{ + // by default we are not using the L3 + return false; +} diff --git a/src/usr/pnor/sfcdd.H b/src/usr/pnor/sfcdd.H new file mode 100644 index 000000000..7c27a1e00 --- /dev/null +++ b/src/usr/pnor/sfcdd.H @@ -0,0 +1,231 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/sfcdd.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 __PNOR_SFCDD_H +#define __PNOR_SFCDD_H + +#include +#include +#include + +/** @file sfc.H + * @brief Provides the logic to access and configure the + * Serial Flash Controller which provides access + * to the PNOR + */ + +/** + * @brief SFC Device Driver Class + * Provides the logic to access and configure the + * Serial Flash Controller which provides access + * to the PNOR + */ +class SfcDD +{ + + public: + /** + * @brief Initialize the SFC Hardware + * + * @return void + */ + virtual errlHndl_t hwInit() = 0; + + /** + * @brief Return first 3 bytes of NOR chip id + * + * @parm[out] NOR chip id + * + * @return Error from operation + */ + virtual errlHndl_t getNORChipId( uint32_t& o_chipId ); + + /** + * @brief Return size of erase block in bytes + * @return Size of erase block in bytes + */ + size_t eraseSizeBytes( void ) + { + return iv_eraseSizeBytes; + }; + + /** + * @brief Read data from the PNOR flash + * + * @parm[in] i_addr PNOR flash Address to read + * @parm[in] i_size Amount of data to read, in bytes. + * @parm[out] o_data Buffer to read data into + * + * @return Error from operation + */ + virtual errlHndl_t readFlash(uint32_t i_addr, + size_t i_size, + void* o_data) = 0; + + /** + * @brief Write data to the PNOR flash + * + * @parm[in] i_addr PNOR flash Address to write + * @parm[in] i_size Amount of data to write, in bytes. + * @parm[in] i_data Buffer containing data to write + * + * @return Error from operation + */ + virtual errlHndl_t writeFlash(uint32_t i_addr, + size_t i_size, + void* i_data) = 0; + + /** + * @brief Erase a block of flash + * + * @parm[in] i_address Offset into flash to erase, aligned to erase block + * + * @return Error from operation + */ + virtual errlHndl_t eraseFlash(uint32_t i_address) = 0; + + /** @brief Constant for sendSpiCmd parameter */ + static const uint32_t NO_ADDRESS = UINT32_MAX; + + /** + * @brief Send a SPI command + * + * @parm[in] i_opCode: command to send into controller first + * @parm[in] i_address: address for those commands that need it + * @parm[in] i_writeCnt: number of bytes to write to device + * @parm[in] i_writeData: write data buffer + * @parm[in] i_readCnt: number of bytes to read from device + * @parm[out] o_readData: read data buffer + * + * @return Error from operation + */ + virtual errlHndl_t sendSpiCmd( uint8_t i_opCode, + uint32_t i_address, + size_t i_writeCnt, + const uint8_t* i_writeData, + size_t i_readCnt, + uint8_t* o_readData ) = 0; + + /** + * @brief Informs caller if PNORDD is using + * L3 Cache for fake PNOR or not. + * + * @return Indicate state of fake PNOR usage + * true = using L3 Cache for fake PNOR + * false = not using L3 Cache for fake PNOR + */ + virtual bool usingL3Cache( void ); + + /** + * @brief Add error registers to an existing Error Log + * + * @param[in] io_errhdl: Error log to add data to + */ + virtual void addFFDC( errlHndl_t& io_errhdl ) + { + //do nothing by default + }; + + /** + * @brief Destructor + */ + virtual ~SfcDD(); + + protected: + /** + * @brief Constructor + * @param[out] Return any error in constructor + * @param[in] Processor target associated with the LPC master + */ + SfcDD( errlHndl_t& o_err, + TARGETING::Target* i_proc + = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ); + + + /** + * @brief Enums for different levels of resetting PNOR communication levels + */ + enum ResetLevels + { + RESET_CLEAR = 0x00000000, /**< Clear Reset Level */ + RESET_LPC_SLAVE = 0x00000008, /**< LPC Slave Logic on SFC */ + RESET_LPC_SLAVE_ERRS = 0x00000010, /**< LPC Slave Errors on SFC */ + RESET_SFC_LOCAL_BUS = 0x00000020, /**< SFC Local Bus */ + + // Known possible combination: + RESET_SFCBUS_LPCSLAVE_ERRS = RESET_LPC_SLAVE_ERRS + | RESET_SFC_LOCAL_BUS, + }; + + /** + * @brief Reset hardware to get into clean state + * + * @parm i_resetLevel How much SFC logic to reset + * + * @return errlHndl_t NULL on success, else error log + */ + virtual errlHndl_t hwReset( ResetLevels i_resetLevel ) + { + //do nothing by default + return NULL; + }; + + + + protected: + + /** + * @brief Processor target associated with the LPC logic + */ + TARGETING::Target* iv_proc; + + /** + * @brief Hardware workarounds + */ + uint32_t iv_flashWorkarounds; + + /** + * @brief PNOR Chip Id + */ + uint32_t iv_norChipId; + + /** + * @brief describes the erase block size, set based on NOR chip type + */ + uint32_t iv_eraseSizeBytes; + + +}; + +namespace PNOR { +/** + * @brief Wrapper for SfcDD constructor + */ +errlHndl_t create_SfcDD( SfcDD*& o_sfc, + TARGETING::Target* i_proc + = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL ); +}; + +#endif diff --git a/src/usr/pnor/test/makefile b/src/usr/pnor/test/makefile index d67b232e0..2c13da7b1 100644 --- a/src/usr/pnor/test/makefile +++ b/src/usr/pnor/test/makefile @@ -5,7 +5,10 @@ # # OpenPOWER HostBoot Project # -# COPYRIGHT International Business Machines Corp. 2011,2014 +# Contributors Listed Below - COPYRIGHT 2011,2014 +# [+] Google Inc. +# [+] 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. @@ -23,6 +26,11 @@ ROOTPATH = ../../../.. MODULE = testpnor -TESTS = *.H + +TESTS = pnorddtest.H ecctest.H pnorrptest.H + +#SFC Implementations +TESTS += $(if $(CONFIG_SFC_IS_IBM_DPSS),sfc_ibmtest.H) +TESTS += $(if $(CONFIG_SFC_IS_AST2400),sfc_ast2400test.H) include ${ROOTPATH}/config.mk diff --git a/src/usr/pnor/test/pnorddtest.H b/src/usr/pnor/test/pnorddtest.H index 97273cc34..3a38e88a6 100644 --- a/src/usr/pnor/test/pnorddtest.H +++ b/src/usr/pnor/test/pnorddtest.H @@ -6,6 +6,7 @@ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2011,2014 */ +/* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -28,7 +29,7 @@ /** * @file pnorddtest.H * - * @brief Test case for PNOR Resource Provider + * @brief Test case for PNOR Device Driver */ #include @@ -44,6 +45,10 @@ #include #include +#ifdef CONFIG_SFC_IS_IBM_DPSS +#include "../sfc_ibm.H" +#endif + extern trace_desc_t* g_trac_pnor; /* @@ -67,7 +72,7 @@ class PnorDdTest : public CxxTest::TestSuite do{ - // Get SPD PNOR section info from PNOR RP + // Get TEST PNOR section info from PNOR RP l_err = PNOR::getSectionInfo( PNOR::TEST, info ); if(l_err) @@ -449,659 +454,6 @@ class PnorDdTest : public CxxTest::TestSuite TRACFCOMP(g_trac_pnor, "PnorDdTest::test_crossblock> %d/%d fails", fails, total ); } - /** - * @brief PNOR DD readWriteTest modes - * Same as test_readwrite but forcing the use of all supported modes - */ - void test_readwrite_modes(void) - { - PnorDD* pnordd = NULL; - uint8_t* fake_space = NULL; - size_t l_size = sizeof(uint64_t); - errlHndl_t l_err = NULL; - uint64_t fails = 0; - uint64_t total = 0; - uint64_t scratch_space = 0; - uint64_t sect_size = 0; - - do{ - TS_TRACE("PnorDdTest::test_readwrite_modes: starting"); - - // list of all modes to test - std::list supported_modes; - supported_modes.push_back(PnorDD::MODEL_MEMCPY); - supported_modes.push_back(PnorDD::MODEL_LPC_MEM); - - if(!TARGETING::is_vpo()) - { - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_readwrite_modes> Adding REAL_CMD & MODEL_REAL_MMIO modes"); - supported_modes.push_back(PnorDD::MODEL_REAL_CMD); - supported_modes.push_back(PnorDD::MODEL_REAL_MMIO); - } - - // loop through all of the supported modes - for( std::list::iterator m - = supported_modes.begin(); - m != supported_modes.end(); - ++m ) - { - if( pnordd ) - { - delete pnordd; - } - - //Fake PNOR - if( (PnorDD::MODEL_MEMCPY == *m) || - (PnorDD::MODEL_LPC_MEM == *m) ) - { - //malloc some space to use as fake-PNOR - if( fake_space ) - { - delete fake_space; - } - fake_space = new uint8_t[4 * KILOBYTE]; - pnordd = new PnorDD(*m, - reinterpret_cast(fake_space), - (4 * KILOBYTE)); - //Adjusting the address by an arbitrary multiplier to keep - //successive tests from always using the same memory space. - scratch_space = (*m)*0x20; - } - //Real PNOR - else - { - pnordd = new PnorDD(*m); - - if(!getTestSection(scratch_space, sect_size)) - { - TRACFCOMP(g_trac_pnor, - "PnorDdTest::test_readwrite_modes> Skipped due to not finding test section in PNOR" ); - continue; - } - //Adjusting the address by an arbitrary multiplier to keep - //successive tests from always using the same memory space. - scratch_space += (*m)*0x20; - } - - // Perform PnorDD Write 1 - uint64_t l_address = scratch_space; - - uint64_t l_writeData = 0x12345678FEEDB0B0; - l_size = sizeof(uint64_t); - l_err = pnordd->writeFlash(&l_writeData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD write 1: writeFlash() failed! Error committed. mode=%d", - *m); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - total++; - if(l_size != sizeof(uint64_t)) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD write 1: Write length not expected value. Addr: 0x%llx, Exp: %d, Act: %d, mode=%d", - l_address, sizeof(uint64_t), l_size, *m); - fails++; - } - - // Perform PnorDD Write 2 - l_address = scratch_space+0x08; - l_writeData = 0xFEEDBEEF000ABCDE; - l_size = sizeof(uint64_t); - l_err = pnordd->writeFlash(&l_writeData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD write 2: writeFlash() failed! Error committed. mode=%d", - *m); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - total++; - if(l_size != sizeof(uint64_t)) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD write 2: Write length not expected value. Addr: 0x%llx, Exp: %d, Act: %d, mode=%d", - l_address, sizeof(uint64_t), l_size, *m); - fails++; - } - - // Perform PnorDD read 1 - l_address = scratch_space; - uint64_t l_readData = 0; - l_size = sizeof(uint64_t); - l_err = pnordd->readFlash(&l_readData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD read 1: readFlash() failed! Error committed. mode=%d", - *m); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - total++; - if(l_readData != 0x12345678FEEDB0B0) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD read 1: Read data not expected value. Addr: 0x%llx, ExpData: 0x12345678FEEDB0B0, ActData: 0x%llx, mode=%d", - l_address, (long long unsigned)l_readData, *m); - fails++; - } - total++; - if(l_size != sizeof(uint64_t)) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD read 1: Read length not expected value. Addr: 0x%llx, Exp: %d, Act: %d, mode=%d", - l_address, sizeof(uint64_t), l_size, *m); - fails++; - } - - // Perform PnorDD read 2 - l_address = scratch_space+0x08; - l_size = sizeof(uint64_t); - l_err = pnordd->readFlash(&l_readData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD read 2: readFlash() failed! Error committed. mode=%d", - *m); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - total++; - if(l_readData != 0xFEEDBEEF000ABCDE) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD read 2: Read data not expected value. Addr: 0x%llx, ExpData: 0xFEEDBEEF000ABCDE, ActData: 0x%llx, mode=%d", - l_address, (long long unsigned)l_readData, *m); - fails++; - } - total++; - if(l_size != sizeof(uint64_t)) - { - TS_FAIL("PnorDdTest::test_readwrite_modes: PNORDD read 2: Read length not expected value. Addr: 0x%llx, Exp: %d, Act: %d, mode=%d", - l_address, sizeof(uint64_t), l_size, *m); - fails++; - } - } - - }while(0); - - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_readwrite_modes> %d/%d fails", fails, total ); - - if( pnordd ) - { - delete pnordd; - } - if( fake_space ) - { - delete fake_space; - } - - } - - /** - * @brief PNOR DD smart write/erase test - * Same as test_smartwrite but forcing the use of all supported modes - */ - void test_smartwrite_modes(void) - { - PnorDD* pnordd = NULL; - uint8_t* fake_space = NULL; - size_t l_size = sizeof(uint64_t); - errlHndl_t l_err = NULL; - uint64_t fails = 0; - uint64_t total = 0; - uint64_t scratch_space = 0; - uint64_t sect_size = 0; - - do{ - TS_TRACE("PnorDdTest::test_smartwrite_modes: starting"); - - // list of all modes to test - std::list supported_modes; - supported_modes.push_back(PnorDD::MODEL_MEMCPY); - supported_modes.push_back(PnorDD::MODEL_LPC_MEM); - - if(!TARGETING::is_vpo()) - { - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_smartwrite_modes> Adding REAL_CMD & MODEL_REAL_MMIO modes"); - supported_modes.push_back(PnorDD::MODEL_REAL_CMD); - supported_modes.push_back(PnorDD::MODEL_REAL_MMIO); - } - - // loop through all of the supported modes - for( std::list::iterator m - = supported_modes.begin(); - m != supported_modes.end(); - ++m ) - { - if( pnordd ) - { - delete pnordd; - } - - //Fake PNOR - if( (PnorDD::MODEL_MEMCPY == *m) || - (PnorDD::MODEL_LPC_MEM == *m) ) - { - //malloc some space to use as fake-PNOR - if( fake_space ) - { - delete fake_space; - } - fake_space = new uint8_t[4 * KILOBYTE]; - pnordd = new PnorDD(*m, - reinterpret_cast(fake_space), - (4 * KILOBYTE)); - - //Adjusting the address by an arbitrary multiplier to keep - //successive tests from always using the same memory space. - scratch_space = (*m)*0x30; - - } - //Real PNOR - else - { - pnordd = new PnorDD(*m); - - if(!getTestSection(scratch_space, sect_size)) - { - TRACFCOMP(g_trac_pnor, - "PnorDdTest::test_readwrite_modes> Skipped due to not finding test section in PNOR" ); - continue; - } - //Adjusting the address by an arbitrary multiplier to keep - //successive tests from always using the same memory space. - scratch_space += (*m)*0x30; - } - - // Perform PnorDD Write 1 - uint64_t l_address = scratch_space+0x20; - uint64_t l_writeData = 0xAAAAAAAA55555555; - l_size = sizeof(uint64_t); - l_err = pnordd->writeFlash(&l_writeData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD write 1: writeFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - - // Perform PnorDD Write 2 - no erase - l_writeData = 0xAAAAAAAAFFFFFFFF; - l_size = sizeof(uint64_t); - l_err = pnordd->writeFlash(&l_writeData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD write 2: writeFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - - // Perform PnorDD Write 3 - put some words after the next write - l_writeData = 0x1234567887654321; - l_size = sizeof(uint64_t); - l_err = pnordd->writeFlash(&l_writeData, - l_size, - l_address+sizeof(uint64_t)); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD write 3: writeFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - - // Perform PnorDD Write 4 - requires erase - l_writeData = 0x8888888811111111; - l_size = sizeof(uint64_t); - l_err = pnordd->writeFlash(&l_writeData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD write 4: writeFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - - // Perform PnorDD read of the data we just wrote - uint64_t l_readData = 0; - l_size = sizeof(uint64_t); - l_err = pnordd->readFlash(&l_readData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD read: readFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - total++; - if(l_readData != l_writeData) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD read: Read data not expected value. Addr: 0x%llx, ExpData: 0x%llx, ActData: 0x%llx", - l_address, l_writeData, l_readData); - fails++; - } - - // Perform PnorDD read of the data after what we just wrote - // verifies that we restored the rest of the block - l_readData = 0; - l_size = sizeof(uint64_t); - l_err = pnordd->readFlash(&l_readData, - l_size, - l_address+sizeof(uint64_t)); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD read: readFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - total++; - if(l_readData != 0x1234567887654321) - { - TS_FAIL("PnorDdTest::test_smartwrite_modes: PNORDD read: Read data not expected value. Addr: 0x%llx, ExpData: 0x%llx, ActData: 0x%llx", - l_address, 0x1234567887654321, l_readData); - fails++; - } - } - - }while(0); - - if( pnordd ) - { - delete pnordd; - } - if( fake_space ) - { - delete fake_space; - } - - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_smartwrite> %d/%d fails", fails, total ); - } - - /** - * @brief PNOR DD Cross-Block testcase - * Same as test_crossblock but forcing the use of all supported modes - */ - void test_crossblock_modes(void) - { - PnorDD* pnordd = NULL; - uint8_t* fake_space = NULL; - size_t l_size = sizeof(uint64_t); - errlHndl_t l_err = NULL; - uint64_t fails = 0; - uint64_t total = 0; - uint64_t scratch_space = 0; - uint64_t sect_size = 0; - - do{ - TS_TRACE("PnorDdTest::test_crossblock_modes: starting"); - - // list of all modes to test - std::list supported_modes; - supported_modes.push_back(PnorDD::MODEL_MEMCPY); - supported_modes.push_back(PnorDD::MODEL_LPC_MEM); - - if(!TARGETING::is_vpo()) - { - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_crossblock_modes> Adding REAL_CMD & MODEL_REAL_MMIO mode"); - supported_modes.push_back(PnorDD::MODEL_REAL_CMD); - supported_modes.push_back(PnorDD::MODEL_REAL_MMIO); - } - - // loop through all of the supported modes - for( std::list::iterator m - = supported_modes.begin(); - m != supported_modes.end(); - ++m ) - { - if( pnordd ) - { - delete pnordd; - } - - - //Fake PNOR - if( (PnorDD::MODEL_MEMCPY == *m) || - (PnorDD::MODEL_LPC_MEM == *m) ) - { - //malloc some space to use as fake-PNOR - if( fake_space ) - { - delete fake_space; - } - fake_space = new uint8_t[8 * KILOBYTE]; - pnordd = new PnorDD(*m, - reinterpret_cast(fake_space), - (8 * KILOBYTE)); - scratch_space = 0; - sect_size = 8 * KILOBYTE; - } - //Real PNOR - else - { - pnordd = new PnorDD(*m); - - if(!getTestSection(scratch_space, sect_size)) - { - TRACFCOMP(g_trac_pnor, - "PnorDdTest::test_readwrite_modes> Skipped due to not finding test section in PNOR" ); - continue; - } - } - - // Find the nearest erase-block (4K) boundary - uint64_t l_boundary = (scratch_space+4096) - - (scratch_space%4096); - uint64_t l_address = 0; - - //make sure we don't go past the end of the section - if(l_boundary+0x4 > scratch_space+sect_size) - { - TS_FAIL("PnorDdTest::test_crossblock_modes: Test Case went beyond allocated space in test section."); - TRACFCOMP(g_trac_pnor, - "PnorDdTest::test_crossblock_modes: Test Case went beyond allocated space in test section.l_boundary=0x%X, scratch_space=0x%X, sect_size=0x%X", - l_boundary, scratch_space, sect_size); - TRACFCOMP(g_trac_pnor, - "PnorDdTest::test_crossblock_modes: sect_size=0x%X", - sect_size); - break; - } - - // Perform PnorDD Write 1 - write through boundary - l_address = l_boundary - sizeof(uint32_t); - uint64_t l_writeData = 0x6666666699999999; - l_size = sizeof(uint64_t); - l_err = pnordd->writeFlash(&l_writeData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_crossblock_modes: PNORDD write 1: writeFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - - // Perform PnorDD Read 1 - verify previous write - l_address = l_boundary - sizeof(uint32_t); - uint64_t l_readData = 0x0; - l_size = sizeof(uint64_t); - l_err = pnordd->readFlash(&l_readData, - l_size, - l_address); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_crossblock_modes: PNORDD Read 1: readFlash() failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - total++; - if(l_readData != l_writeData) - { - TS_FAIL("PnorDdTest::test_crossblock_modes: PNORDD read: Read data not expected value. Addr: 0x%.llx, ExpData: 0x%.llx, ActData: 0x%llx", l_address, l_writeData, l_readData); - fails++; - } - } - }while(0); - - if( pnordd ) - { - delete pnordd; - } - if( fake_space ) - { - delete fake_space; - } - - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_crossblock_modes> %d/%d fails", fails, total ); - } - - - /** - * @brief PNOR DD Force Fails testcase - */ -/****************************************************************************/ -/* NOTE: TEST DISABLED!!! remove "_" before test name to re-enable */ -/****************************************************************************/ - void _test_forceFails(void) - { - errlHndl_t l_err = NULL; - uint64_t fails = 0; - uint64_t total = 0; - PnorDD* pnordd = new PnorDD(); - uint32_t l_poll = 0; - PnorDD::SfcCmdReg_t sfc_cmd; - - do{ - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_forceFails> starting - expect to see errors"); - - /*******************************/ - /* Send in an invalid OP Code */ - /*******************************/ - sfc_cmd.opcode = PnorDD::SFC_OP_INVALID; - sfc_cmd.length = 0; - - mutex_lock(pnordd->iv_mutex_ptr); - l_err = pnordd->writeRegSfc(PnorDD::SFC_CMD_SPACE, - PnorDD::SFC_REG_CMD, - sfc_cmd.data32); - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_forceFails> Get Chip Id cmd failed! Error committed."); - fails++; - - // Unlock mutex for Error Log to be commited - mutex_unlock(pnordd->iv_mutex_ptr); - errlCommit(l_err,PNOR_COMP_ID); - - // Lock mutex for next operation - mutex_lock(pnordd->iv_mutex_ptr); - } - - // Poll for complete status without waiting - l_err = pnordd->pollSfcOpComplete(); - mutex_unlock(pnordd->iv_mutex_ptr); - - total++; - if ( l_err == NULL ) - { - TS_FAIL("PnorDdTest::test_forceFails> Failed to create illegal opcode error!"); - fails++; - } - else - { - // error correctly created - delete it - delete l_err; - } - - /*******************************************************/ - /* Issue a cmd but poll for completion without waiting */ - /*******************************************************/ - //Issue Get Chip ID command - sfc_cmd.opcode = PnorDD::SFC_OP_CHIPID; - sfc_cmd.length = 0; - - mutex_lock(pnordd->iv_mutex_ptr); - l_err = pnordd->writeRegSfc(PnorDD::SFC_CMD_SPACE, - PnorDD::SFC_REG_CMD, - sfc_cmd.data32); - - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_forceFails> Get Chip Id cmd failed! Error committed."); - fails++; - - // Unlock mutex for Error Log to be commited - mutex_unlock(pnordd->iv_mutex_ptr); - errlCommit(l_err,PNOR_COMP_ID); - - // Lock mutex for next operation - mutex_lock(pnordd->iv_mutex_ptr); - } - - // Poll for complete status without waiting - l_err = pnordd->pollSfcOpComplete(l_poll); - - total++; - if ( l_err == NULL ) - { - TS_FAIL("PnorDdTest::test_forceFails> pollSfcOpCompletel(l_poll=0) Failed to create an error!"); - fails++; - } - else - { - // error correctly created - delete it - delete l_err; - } - - /*******************************************************/ - /* Cleanup: poll to make sure last operation completes */ - /* before continuing */ - /*******************************************************/ - l_err = pnordd->pollSfcOpComplete(); - mutex_unlock(pnordd->iv_mutex_ptr); - - total++; - if (l_err) - { - TS_FAIL("PnorDdTest::test_forceFails> Cleanup polling failed! Error committed."); - errlCommit(l_err,PNOR_COMP_ID); - fails++; - } - - - }while(0); - - if( pnordd ) - { - delete pnordd; - } - - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_forceFails> %d/%d fails", fails, total ); - - } - }; /*Not really a real test, just using to verify ext image is loading properly. diff --git a/src/usr/pnor/test/pnorrptest.H b/src/usr/pnor/test/pnorrptest.H index 6c90f2a99..6c43f9d4a 100644 --- a/src/usr/pnor/test/pnorrptest.H +++ b/src/usr/pnor/test/pnorrptest.H @@ -6,6 +6,7 @@ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2011,2014 */ +/* [+] Google Inc. */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -544,12 +545,12 @@ class PnorRpTest : public CxxTest::TestSuite // Check if cur_TOC failed that other TOC is used PnorRP::getInstance().readTOC(); TOC_used = PnorRP::getInstance().iv_TOC_used; - TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC : TOC %d Corrupt Header Toc_used = %d", cur_TOC, TOC_used); + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC : TOC %d Corrupt Header, Toc_used = %d", cur_TOC, TOC_used); if (TOC_used == cur_TOC) { - TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC> ERROR : TOC %d is corrupted, did not use other TOC", cur_TOC); - TS_FAIL("PnorRpTest::test_TOC> ERROR : TOC %d is corrupted, did not use other TOC"); + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC> ERROR : TOC %d header is corrupted, did not use other TOC", cur_TOC); + TS_FAIL("PnorRpTest::test_TOC> ERROR : TOC %d header is corrupted, did not use other TOC"); } // Fix cur_TOC header @@ -571,12 +572,12 @@ class PnorRpTest : public CxxTest::TestSuite TOC_used = cur_TOC; PnorRP::getInstance().readTOC(); TOC_used = PnorRP::getInstance().iv_TOC_used; - TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC : TOC %d Corrupt Entry Toc_used = %d", cur_TOC, TOC_used); + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC : TOC %d Corrupt Entry, Toc_used = %d", cur_TOC, TOC_used); if (TOC_used == cur_TOC) { - TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC> ERROR : TOC %d is corrupted, did not use other TOC ENTRY", cur_TOC); - TS_FAIL("PnorRpTest::test_TOC> ERROR : TOC %d is corrupted, did not use other TOC", cur_TOC); + TRACFCOMP(g_trac_pnor, "PnorRpTest::test_TOC> ERROR : TOC %d entry is corrupted, did not use other TOC ENTRY", cur_TOC); + TS_FAIL("PnorRpTest::test_TOC> ERROR : TOC %d entry is corrupted, did not use other TOC", cur_TOC); } // Fix cur_TOC first entry diff --git a/src/usr/pnor/test/sfc_ast2400test.H b/src/usr/pnor/test/sfc_ast2400test.H new file mode 100644 index 000000000..4dc69e68e --- /dev/null +++ b/src/usr/pnor/test/sfc_ast2400test.H @@ -0,0 +1,305 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/test/sfc_ast2400test.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 __SFC_AST2400TEST_H +#define __SFC_AST2400TEST_H + +/** + * @file sfc_ast2400test.H + * + * @brief Test case for AST2400 Flash Controller +*/ + +#include +#include +#include +#include +#include +#include "../pnordd.H" +#include +#include +#include +#include "../sfc_ast2400.H" + +extern trace_desc_t* g_trac_pnor; + +class SfcAST2400Test : public CxxTest::TestSuite +{ + public: + + bool getTestSection(uint64_t &o_physAddr, uint64_t &o_size) + { + errlHndl_t l_err = NULL; + PNOR::SectionInfo_t info; + uint64_t chip_select = 0xF; + bool needs_ecc = false; + bool section_found = false; + + do{ + + // Get TEST PNOR section info from PNOR RP + l_err = PNOR::getSectionInfo( PNOR::TEST, + info ); + if(l_err) + { + if(l_err->reasonCode() == PNOR::RC_INVALID_SECTION) + { + //This is expected in some configurations, + // so just delete it. + delete l_err; + } + else + { + //Any other type of error is not expected, so commit it. + errlCommit(l_err,PNOR_COMP_ID); + } + break; + } + + l_err = PnorRP::getInstance().computeDeviceAddr((void*)info.vaddr, + o_physAddr, + chip_select, + needs_ecc); + if(l_err) + { + errlCommit(l_err,PNOR_COMP_ID); + break; + } + + o_size = info.size; + section_found = true; + + }while(0); + + return section_found; + } + + /** + * @brief Test Flash Reads + * Verify that reads work in normal mode and then do + * not work in command mode + */ + void test_FlashReads( void ) + { + PnorDD& pnordd = Singleton::instance(); + SfcAST2400* sfc = reinterpret_cast(pnordd.iv_sfc ); + mutex_t* l_mutex = pnordd.iv_mutex_ptr; + errlHndl_t l_err = NULL; + + //Find some pnor to read + uint64_t base_address; + uint64_t sect_size; + if(!getTestSection(base_address, sect_size)) + { + TRACFCOMP(g_trac_pnor, "SfcAST2400Test::test_FlashReads> Skipped due to not finding test section in PNOR" ); + TS_FAIL("SfcAST2400Test::test_FlashReads> Skipped due to not finding test section in PNOR"); + return; + } + + mutex_lock(l_mutex); + + //Prove reads work by default and that they fail in command mode + uint64_t l_readData = 0; + size_t l_size = sizeof(uint64_t); + l_err = pnordd._readFlash(base_address, + l_size, + &l_readData); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_FlashReads> Basic read failed"); + mutex_unlock(l_mutex);//unlock before commit + errlCommit(l_err,PNOR_COMP_ID); + return; //just give up if basic reads don't work + } + + // Put controller into command mode (instead of read mode) + l_err = sfc->commandMode( true ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_FlashReads> Error entering command mode"); + mutex_unlock(l_mutex);//unlock before commit + errlCommit(l_err,PNOR_COMP_ID); + mutex_lock(l_mutex);//lock again for next op + } + + // Reads should fail + l_err = pnordd._readFlash(base_address, + l_size, + &l_readData); + if( !l_err ) + { + TS_FAIL("SfcAST2400Test::test_FlashReads> Read did not fail in command mode"); + } + else + { + delete l_err; + } + + // Put controller back into read mode + l_err = sfc->commandMode( false ); + mutex_unlock(l_mutex); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_FlashReads> Error exiting command mode"); + errlCommit(l_err,PNOR_COMP_ID); + } + } + + /** + * @brief Test SIO access + * Use a SIO scratch register to verify reads and writes + */ + void test_SIO(void) + { + SfcAST2400* sfc = reinterpret_cast( + Singleton::instance().iv_sfc ); + errlHndl_t l_err = NULL; + + // Read SIO to BMC scratch reg 1,2 and save off values + uint8_t scratch1 = 0; + l_err = sfc->readRegSIO( 0x21, scratch1 ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> readRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + uint8_t scratch2 = 0; + l_err = sfc->readRegSIO( 0x22, scratch2 ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> readRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + + // Write test patterns into registers + uint8_t testdata = 0xA5; + l_err = sfc->writeRegSIO( 0x21, testdata ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> writeRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + testdata = 0x12; + l_err = sfc->writeRegSIO( 0x22, testdata ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> writeRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + + // Read the data back and compare to expected results + l_err = sfc->readRegSIO( 0x21, testdata ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> readRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + if( testdata != 0xA5 ) + { + TS_FAIL("SfcAST2400Test::test_SIO> Data mismatch on SIO 0x21 : Exp=0xA5, Act=%.2X", testdata); + } + l_err = sfc->readRegSIO( 0x22, testdata ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> readRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + if( testdata != 0x12 ) + { + TS_FAIL("SfcAST2400Test::test_SIO> Data mismatch on SIO 0x22 : Exp=0x12, Act=%.2X", testdata); + } + + // Restore the original data + l_err = sfc->writeRegSIO( 0x21, scratch1 ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> writeRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + l_err = sfc->writeRegSIO( 0x22, scratch2 ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SIO> writeRegSIO failed"); + errlCommit(l_err,PNOR_COMP_ID); + } + } + + /** + * @brief Test SPIC access + * Read and write data to the SPI Control register + */ + void test_SPIC( void ) + { + SfcAST2400* sfc = reinterpret_cast( + Singleton::instance().iv_sfc ); + mutex_t* l_mutex = Singleton::instance().iv_mutex_ptr; + errlHndl_t l_err = NULL; + + mutex_lock(l_mutex); + + uint32_t first = 0; + l_err = sfc->readRegSPIC( SfcAST2400::CTLREG_04, first ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SPIC> readRegSIO failed"); + mutex_unlock(l_mutex);//unlock before commit + errlCommit(l_err,PNOR_COMP_ID); + mutex_lock(l_mutex);//lock again for next op + } + uint32_t data1 = 0x12345678; + l_err = sfc->writeRegSPIC( SfcAST2400::CTLREG_04, data1 ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SPIC> readRegSIO failed"); + mutex_unlock(l_mutex);//unlock before commit + errlCommit(l_err,PNOR_COMP_ID); + mutex_lock(l_mutex);//lock again for next op + } + l_err = sfc->readRegSPIC( SfcAST2400::CTLREG_04, data1 ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SPIC> readRegSIO failed"); + mutex_unlock(l_mutex);//unlock before commit + errlCommit(l_err,PNOR_COMP_ID); + mutex_lock(l_mutex);//lock again for next op + } + if( data1 != 0x12345678 ) + { + TS_FAIL("SfcAST2400Test::test_SPIC> Unexpected result of %.8X (exp 0x12345678)",data1); + } + //put back the original + l_err = sfc->writeRegSPIC( SfcAST2400::CTLREG_04, first ); + if( l_err ) + { + TS_FAIL("SfcAST2400Test::test_SPIC> readRegSIO failed"); + mutex_unlock(l_mutex);//unlock before commit + errlCommit(l_err,PNOR_COMP_ID); + } + + mutex_unlock(l_mutex); + } +}; + +#endif diff --git a/src/usr/pnor/test/sfc_ibmtest.H b/src/usr/pnor/test/sfc_ibmtest.H new file mode 100644 index 000000000..967045ad0 --- /dev/null +++ b/src/usr/pnor/test/sfc_ibmtest.H @@ -0,0 +1,174 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/test/sfc_ibmtest.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2014 */ +/* [+] Google Inc. */ +/* [+] 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 __SFC_IBMTEST_H +#define __SFC_IBMTEST_H + +/** + * @file sfc_ibmtest.H + * + * @brief Test case for IBM DPSS Flash Controller +*/ + +#include +#include +#include +#include +#include +#include "../pnordd.H" +#include +#include +#include "../sfc_ibm.H" + +extern trace_desc_t* g_trac_pnor; + +class SfcIBMTest : public CxxTest::TestSuite +{ + public: + + /*************************************************************************/ + /* NOTE: TEST DISABLED!!! remove "_" before test name to re-enable */ + /*************************************************************************/ + /** + * @brief PNOR DD Force Fails testcase + */ + void _test_forceFails(void) + { + errlHndl_t l_err = NULL; + uint64_t fails = 0; + uint64_t total = 0; + SfcIBM* sfcdd = reinterpret_cast( + Singleton::instance().iv_sfc ); + mutex_t* l_mutex = Singleton::instance().iv_mutex_ptr; + SfcIBM::SfcCmdReg_t sfc_cmd; + + do{ + TRACFCOMP(g_trac_pnor, "SfcIBMTest::test_forceFails> starting - expect to see errors"); + + /*******************************/ + /* Send in an invalid OP Code */ + /*******************************/ + sfc_cmd.opcode = SfcIBM::SFC_OP_INVALID; + sfc_cmd.length = 0; + + mutex_lock(l_mutex); + l_err = sfcdd->writeReg(SfcIBM::SFC_CMD_SPACE, + SfcIBM::SFC_REG_CMD, + sfc_cmd.data32); + total++; + if (l_err) + { + TS_FAIL("SfcIBMTest::test_forceFails> writeReg failed! Error committed."); + fails++; + + // Unlock mutex for Error Log to be commited + mutex_unlock(l_mutex); + errlCommit(l_err,PNOR_COMP_ID); + + // Lock mutex for next operation + mutex_lock(l_mutex); + } + + // Poll for complete status without waiting + l_err = sfcdd->pollOpComplete(); + mutex_unlock(l_mutex); + + total++; + if ( l_err == NULL ) + { + TS_FAIL("SfcIBMTest::test_forceFails> Failed to create illegal opcode error!"); + fails++; + } + else + { + // error correctly created - delete it + delete l_err; + } + + /*******************************************************/ + /* Issue a cmd but poll for completion without waiting */ + /*******************************************************/ + //Issue Get Chip ID command + sfc_cmd.opcode = SfcIBM::SFC_OP_CHIPID; + sfc_cmd.length = 0; + + mutex_lock(l_mutex); + l_err = sfcdd->writeReg(SfcIBM::SFC_CMD_SPACE, + SfcIBM::SFC_REG_CMD, + sfc_cmd.data32); + + total++; + if (l_err) + { + TS_FAIL("SfcIBMTest::test_forceFails> Get Chip Id cmd failed! Error committed."); + fails++; + + // Unlock mutex for Error Log to be commited + mutex_unlock(l_mutex); + errlCommit(l_err,PNOR_COMP_ID); + + // Lock mutex for next operation + mutex_lock(l_mutex); + } + + // Poll for complete status without waiting + l_err = sfcdd->pollOpComplete(); + + total++; + if ( l_err == NULL ) + { + TS_FAIL("SfcIBMTest::test_forceFails> pollOpCompletel(l_poll=0) Failed to create an error!"); + fails++; + } + else + { + // error correctly created - delete it + delete l_err; + } + + /*******************************************************/ + /* Cleanup: poll to make sure last operation completes */ + /* before continuing */ + /*******************************************************/ + l_err = sfcdd->pollOpComplete(); + mutex_unlock(l_mutex); + + total++; + if (l_err) + { + TS_FAIL("SfcIBMTest::test_forceFails> Cleanup polling failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; + } + + + }while(0); + + TRACFCOMP(g_trac_pnor, "SfcIBMTest::test_forceFails> %d/%d fails", fails, total ); + + } +}; + +#endif -- cgit v1.2.1