diff options
author | Dan Crowell <dcrowell@us.ibm.com> | 2012-01-20 15:05:21 -0600 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2012-02-16 09:00:57 -0600 |
commit | 10bc99ba760ce7e67b005b7b61f884581c75d6ad (patch) | |
tree | 200a48511b3f83b22e4c9f2c400fb108e2abc9f9 /src | |
parent | c948c7adaf78a9925298e597493201fddd8102a7 (diff) | |
download | talos-hostboot-10bc99ba760ce7e67b005b7b61f884581c75d6ad.tar.gz talos-hostboot-10bc99ba760ce7e67b005b7b61f884581c75d6ad.zip |
RTC Story 34595
-Add first pass of LPC logic to PNOR driver
-Add interfaces to handle future SPI changes
-Remove support for MMRD/PMRW modes from driver and RP
Code will still use our fake PNOR image in memory but it will
exercise more of the full driver path by reading/writing in
32-bit chunks.
Change-Id: I753c71926bd9e67d22ac06c3204a0daf8b2f222e
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/637
Tested-by: Jenkins Server
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/include/usr/pnor/pnor_reasoncodes.H | 30 | ||||
-rw-r--r-- | src/usr/pnor/pnordd.C | 756 | ||||
-rw-r--r-- | src/usr/pnor/pnordd.H | 284 | ||||
-rw-r--r-- | src/usr/pnor/pnorrp.C | 42 | ||||
-rw-r--r-- | src/usr/pnor/pnorrp.H | 6 | ||||
-rw-r--r-- | src/usr/pnor/test/pnorddtest.H | 262 |
6 files changed, 1132 insertions, 248 deletions
diff --git a/src/include/usr/pnor/pnor_reasoncodes.H b/src/include/usr/pnor/pnor_reasoncodes.H index d16c6d7f1..0269f6cbc 100644 --- a/src/include/usr/pnor/pnor_reasoncodes.H +++ b/src/include/usr/pnor/pnor_reasoncodes.H @@ -29,16 +29,26 @@ namespace PNOR { enum PNORModuleId { - MOD_PNORRP_WAITFORMESSAGE = 0x01, /**< pnorrp.C : PnorRP::waitForMessage */ - MOD_PNORRP_COMPUTEDEVICEADDR = 0x02, /**< pnorrp.C : PnorRP::computeDeviceAddr */ - MOD_PNORRP_GETSECTIONINFO = 0x03, /**< pnorrp.C : PnorRP::getSectionInfo */ - MOD_PNORRP_COMPUTESECTION = 0x04, /**< pnorrp.C : PnorRP::computeSection */ - MOD_PNORRP_INITDAEMON = 0x05, /**< pnorrp.C : PnorRP::initDaemon */ - MOD_PNORRP_READTOC = 0x06, /**< pnorrp.C : PnorRP::readTOC */ - MOD_PNORRP_READFROMDEVICE = 0x07, /**< pnorrp.C : PnorRP::readFromDevice */ - MOD_PNORRP_WRITETODEVICE = 0x08, /**< pnorrp.C : PnorRP::writeToDevice */ + MOD_PNORRP_WAITFORMESSAGE = 0x01, /**< pnorrp.C : PnorRP::waitForMessage */ + MOD_PNORRP_COMPUTEDEVICEADDR = 0x02, /**< pnorrp.C : PnorRP::computeDeviceAddr */ + MOD_PNORRP_GETSECTIONINFO = 0x03, /**< pnorrp.C : PnorRP::getSectionInfo */ + MOD_PNORRP_COMPUTESECTION = 0x04, /**< pnorrp.C : PnorRP::computeSection */ + MOD_PNORRP_INITDAEMON = 0x05, /**< pnorrp.C : PnorRP::initDaemon */ + MOD_PNORRP_READTOC = 0x06, /**< pnorrp.C : PnorRP::readTOC */ + MOD_PNORRP_READFROMDEVICE = 0x07, /**< pnorrp.C : PnorRP::readFromDevice */ + MOD_PNORRP_WRITETODEVICE = 0x08, /**< pnorrp.C : PnorRP::writeToDevice */ - MOD_PNORDD_VERIFYADDRESSRANGE = 0x11 /**< pnordd.C : PnorDD::verifyAddressRange */ + MOD_PNORDD_VERIFYADDRESSRANGE = 0x11, /**< pnordd.C : PnorDD::verifyAddressRange */ + MOD_PNORDD_READFLASH = 0x12, /**< pnordd.C : PnorDD::readFlash */ + MOD_PNORDD_WRITEFLASH = 0x13, /**< pnordd.C : PnorDD::writeFlash */ + MOD_PNORDD_READREGLPC = 0x14, /**< pnordd.C : PnorDD::readRegLPC */ + MOD_PNORDD_WRITEREGLPC = 0x15, /**< pnordd.C : PnorDD::writeRegLPC */ + MOD_PNORDD_READREGSPI = 0x16, /**< pnordd.C : PnorDD::readRegSPI */ + MOD_PNORDD_WRITEREGSPI = 0x17, /**< pnordd.C : PnorDD::writeRegSPI */ + MOD_PNORDD_READLPC = 0x18, /**< pnordd.C : PnorDD::readLPC */ + MOD_PNORDD_WRITELPC = 0x19, /**< pnordd.C : PnorDD::writeLPC */ + MOD_PNORDD_ERASEFLASH = 0x1A, /**< pnordd.C : PnorDD::eraseFlash */ + MOD_PNORDD_COMPAREANDWRITEBLOCK = 0x1B, /**< pnordd.C : PnorDD::compareAndWriteBlock */ }; enum PNORReasonCode @@ -49,6 +59,8 @@ namespace PNOR RC_EXTERNAL_ERROR = PNOR_COMP_ID | 0x04, RC_STARTUP_FAIL = PNOR_COMP_ID | 0x05, RC_INVALID_ASYNC_MESSAGE = PNOR_COMP_ID | 0x06, + RC_UNSUPPORTED_OPERATION = PNOR_COMP_ID | 0x07, + RC_LPC_ERROR = PNOR_COMP_ID | 0x08, }; }; diff --git a/src/usr/pnor/pnordd.C b/src/usr/pnor/pnordd.C index 7708dcd1d..86f4efd7b 100644 --- a/src/usr/pnor/pnordd.C +++ b/src/usr/pnor/pnordd.C @@ -42,17 +42,29 @@ #include <pnor/pnorif.H> #include <pnor/pnor_reasoncodes.H> -#define FAKE_PNOR_START 5*1024*1024 -#define FAKE_PNOR_END 8*1024*1024 -#define FAKE_PNOR_SIZE 3*1024*1024 +// Uncomment this to use the fake PNOR implementation (vs the LPC path) +// @todo - Switch with RTC 36901 +#define USE_FAKE_PNOR + +// Uncomment this to skip the LPC code and just do page copies +//#define FAST_FAKE_PNOR + +// Uncomment this to enable smart writing +#define SMART_WRITE + +#ifdef USE_FAKE_PNOR +#define FAKE_PNOR_START 5*MEGABYTE +#define FAKE_PNOR_END 8*MEGABYTE +#define FAKE_PNOR_SIZE 3*MEGABYTE +void write_fake_pnor( uint64_t i_pnorAddr, void* i_buffer, size_t i_size ); +void read_fake_pnor( uint64_t i_pnorAddr, void* o_buffer, size_t i_size ); +#endif extern trace_desc_t* g_trac_pnor; namespace PNOR { - - /** * @brief Performs an PNOR Read Operation * This function performs a PNOR Read operation. It follows a pre-defined @@ -85,9 +97,14 @@ errlHndl_t ddRead(DeviceFW::OperationType i_opType, uint64_t l_addr = va_arg(i_args,uint64_t); do{ - l_err = Singleton<PnorDD>::instance().read(io_buffer, - io_buflen, - l_addr); + //TODO - Fix with Story 34763 + // Ensure we are operating on a 32-bit (4-byte) boundary + assert( reinterpret_cast<uint64_t>(io_buffer) % 4 == 0 ); + + // Read the flash + l_err = Singleton<PnorDD>::instance().readFlash(io_buffer, + io_buflen, + l_addr); if(l_err) { break; @@ -130,9 +147,14 @@ errlHndl_t ddWrite(DeviceFW::OperationType i_opType, uint64_t l_addr = va_arg(i_args,uint64_t); do{ - l_err = Singleton<PnorDD>::instance().write(io_buffer, - io_buflen, - l_addr); + //TODO - Fix with Story 34763 + // Ensure we are operating on a 32-bit (4-byte) boundary + assert( reinterpret_cast<uint64_t>(io_buffer) % 4 == 0 ); + + // Write the flash + l_err = Singleton<PnorDD>::instance().writeFlash(io_buffer, + io_buflen, + l_addr); if(l_err) { break; @@ -157,118 +179,135 @@ DEVICE_REGISTER_ROUTE(DeviceFW::WRITE, /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -errlHndl_t setLSCAccessMode(lscMode i_mode) -{ - errlHndl_t l_err = NULL; - - do{ - l_err = Singleton<PnorDD>::instance().setAccessMode(i_mode); - if(l_err) - { - break; - } - - }while(0); - - return l_err; -} /** - * @brief Read PNOR + * @brief Performs a PNOR Read Operation */ -errlHndl_t PnorDD::read(void* o_buffer, - size_t& io_buflen, - uint64_t i_address) +errlHndl_t PnorDD::readFlash(void* o_buffer, + size_t& io_buflen, + uint64_t i_address) { - //TRACDCOMP(g_trac_pnor, "PnorDD::read(i_address=0x%llx)> ", 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; + uint64_t l_address = i_address & 0x00000000FFFFFFFF; - l_err = verifyAddressRange(l_address, io_buflen); + l_err = verifyFlashAddressRange(l_address, io_buflen); if(l_err) { io_buflen = 0; break; } - //create a pointer to the offset start. - char * srcPtr = (char *)(FAKE_PNOR_START+l_address); - - //@TODO: likely need a mutex around HW access - - //copy data from memory into the buffer. - memcpy(o_buffer, srcPtr, io_buflen); +#ifdef FAST_FAKE_PNOR + read_fake_pnor( i_address, o_buffer, io_buflen ); + break; +#endif + + // LPC is accessed 32-bits at a time... + uint32_t* word_ptr = static_cast<uint32_t*>(o_buffer); + uint64_t words_read = 0; + for( uint32_t addr = i_address; + addr < (i_address+io_buflen); + addr += sizeof(uint32_t) ) + { + uint32_t read_data = 0; + l_err = readLPC( addr, read_data ); + if( l_err ) { break; } + memcpy( word_ptr+words_read, &read_data, sizeof(uint32_t) ); + words_read++; + } + io_buflen = words_read*sizeof(uint32_t); + if( l_err ) { break; } }while(0); return l_err; } /** - * @brief Write PNOR + * @brief Performs a PNOR Write Operation */ -errlHndl_t PnorDD::write(void* i_buffer, - size_t& io_buflen, - uint64_t i_address) +errlHndl_t PnorDD::writeFlash(void* i_buffer, + size_t& io_buflen, + uint64_t i_address) { - //TRACDCOMP(g_trac_pnor, "PnorDD::write(i_address=0x%llx)> ", i_address); + //TRACDCOMP(g_trac_pnor, "PnorDD::writeFlash(i_address=0x%llx)> ", i_address); errlHndl_t l_err = NULL; - do{ + do{ + TRACDCOMP(g_trac_pnor,"PNOR write %.8X", i_address); + //mask off chip select for now, will probably break up fake PNOR into //multiple fake chips eventually - uint64_t l_address = i_address & 0x00000000FFFFFFFF; - - - l_err = verifyAddressRange(l_address, io_buflen); - if(l_err) + uint64_t l_address = i_address & 0x00000000FFFFFFFF; + + // make sure this is a valid address + l_err = verifyFlashAddressRange(l_address, io_buflen); + if(l_err) { break; } + + +#ifdef FAST_FAKE_PNOR + write_fake_pnor( i_address, i_buffer, io_buflen ); + break; +#endif + + // LPC is accessed 32-bits at a time, but we also need to be + // smart about handling erases. In NOR flash we can set bits + // without an erase but we cannot clear them. When we erase + // we have to erase and entire block of data at a time. + uint32_t* word_ptr = static_cast<uint32_t*>(i_buffer); + uint64_t num_blocks = getNumAffectedBlocks(i_address,io_buflen); + uint32_t cur_addr = i_address; + uint64_t bytes_left = io_buflen; + + // loop through erase blocks until we've gotten through all + // affected blocks + for( uint64_t block = 0; + block < num_blocks; + ++block ) { - io_buflen = 0; - break; + TRACDCOMP( g_trac_pnor, "Block %d: bytes_left=%d, cur_addr=0x%.8X", block, bytes_left, cur_addr ); + + // write a single block of data out to flash efficiently + l_err = compareAndWriteBlock( cur_addr, + (bytes_left-1)/sizeof(uint32_t)+1, + word_ptr ); + if( l_err ) { break; } + //@todo - How should we handle PNOR errors? + + // move on to the next block + if( bytes_left > ERASESIZE_BYTES ) + { + bytes_left -= ERASESIZE_BYTES; + cur_addr += ERASESIZE_BYTES; + } + else + { + // final block of partial data + // align cur_addr to the beginning of the block + cur_addr = findEraseBlock(cur_addr+bytes_left); + // figure out the remaining data in the last block + bytes_left = (i_address - cur_addr); + } + word_ptr += (bytes_left-1)/sizeof(uint32_t); } - - //create a pointer to the offset start. - char * destPtr = (char *)(FAKE_PNOR_START+l_address); - - //@TODO: likely need a mutex around HW access - - //copy data from memory into the buffer. - memcpy(destPtr, i_buffer, io_buflen); - + if( l_err ) { break; } }while(0); - return l_err; -} - -/** - * @brief Set PNOR to desired mode - */ -errlHndl_t PnorDD::setAccessMode(lscMode i_mode) -{ - errlHndl_t l_err = NULL; - TRACFCOMP(g_trac_pnor, "PnorDD::setAccessMode(0x%llx)> ", i_mode); - - do{ - //@TODO: real impelementation needed - - //Once we have a 'real' implementation, it will likely drive the need for mutexes - //throughout the PnorDD interfaces, to avoid issues with weak consistency or a - //read/write occuring while we're updating the mode, but - //skipping that until it's actually needed. - - //Eventually need to actually change HW state here. - //For now, just record the new mode. - iv_lscMode = i_mode; - - - }while(0); + // keeping track of every actual byte written is complicated and it can + // be misleading in the cases where we end up erasing and writing an + // entire block, instead just return zero for any failures + if( l_err ) + { + io_buflen = 0; + } return l_err; } @@ -283,10 +322,14 @@ errlHndl_t PnorDD::setAccessMode(lscMode i_mode) * @brief Constructor */ PnorDD::PnorDD() -: iv_lscMode(MMRD) { TRACFCOMP(g_trac_pnor, "PnorDD::PnorDD()> "); + mutex_init(&iv_mutex); + for( uint64_t x=0; x < (PNORSIZE/ERASESIZE_BYTES); x++ ) + { + iv_erases[x] = 0; + } } /** @@ -298,20 +341,25 @@ PnorDD::~PnorDD() //Nothing to do for now } -errlHndl_t PnorDD::verifyAddressRange(uint64_t i_address, - size_t& i_length) +/** + * @brief Verify flash request is in appropriate address range + */ +errlHndl_t PnorDD::verifyFlashAddressRange(uint64_t i_address, + size_t& i_length) { errlHndl_t l_err = NULL; do{ + //@todo - Do we really need any checking here? +#ifdef USE_FAKE_PNOR if((i_address+i_length) > FAKE_PNOR_SIZE) { TRACFCOMP( g_trac_pnor, "PnorDD::verifyAddressRange> Invalid Address Requested : i_address=%d", i_address ); /*@ * @errortype * @moduleid PNOR::MOD_PNORDD_VERIFYADDRESSRANGE - * @reasoncode PNOR::RC_INVALID_SECTION + * @reasoncode PNOR::RC_INVALID_ADDRESS * @userdata1 Requested Address * @userdata2 Requested Length * @devdesc PnorDD::verifyAddressRange> Invalid Address requested @@ -323,7 +371,7 @@ errlHndl_t PnorDD::verifyAddressRange(uint64_t i_address, TO_UINT64(i_length)); break; } - +#endif }while(0); @@ -331,6 +379,530 @@ errlHndl_t PnorDD::verifyAddressRange(uint64_t i_address, return l_err; } +/** + * @brief Read a LPC Host Controller Register + */ +errlHndl_t PnorDD::readRegLPC(LpcRegAddr i_addr, + uint32_t& o_data) +{ + errlHndl_t l_err = NULL; + + do { + //@todo - RTC 36901 or 35728 + } while(0); + + return l_err; +} + +/** + * @brief Write a LPC Host Controller Register + */ +errlHndl_t PnorDD::writeRegLPC(LpcRegAddr i_addr, + uint32_t i_data) +{ + return NULL; //@todo - RTC 36901 or 35728 +} + +/** + * @brief Read a SPI Register + */ +errlHndl_t PnorDD::readRegSPI(uint32_t i_addr, + uint32_t& o_data) +{ + //@todo - Finish with Story 35728 + TRACFCOMP( g_trac_pnor, "PnorDD::readRegSPI> Unsupported Operation : i_addr=%d", i_addr ); + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORDD_READREGSPI + * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION + * @userdata1 Requested Address + * @userdata2 <unused> + * @devdesc PnorDD::readRegSPI> Unsupported Operation + */ + return new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_READREGSPI, + PNOR::RC_UNSUPPORTED_OPERATION, + TO_UINT64(i_addr), + 0); + +} + +/** + * @brief Write a SPI Register + */ +errlHndl_t PnorDD::writeRegSPI(uint32_t i_addr, + uint32_t i_data) +{ + //@todo - Finish with Story 35728 + TRACFCOMP( g_trac_pnor, "PnorDD::writeRegSPI> Unsupported Operation : i_addr=%d", i_addr ); + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORDD_WRITEREGSPI + * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION + * @userdata1 Requested Address + * @userdata2 <unused> + * @devdesc PnorDD::writeRegSPI> Unsupported Operation + */ + return new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_WRITEREGSPI, + PNOR::RC_UNSUPPORTED_OPERATION, + TO_UINT64(i_addr), + 0); + +} + + +/** + * @brief Read an address from LPC space + */ +errlHndl_t PnorDD::readLPC(uint32_t i_addr, + uint32_t& o_data) +{ + errlHndl_t l_err = NULL; + bool need_unlock = false; + + do { +#ifdef USE_FAKE_PNOR + read_fake_pnor( i_addr, static_cast<void*>(&o_data), + sizeof(uint32_t) ); +#else + //@todo - fill in with RTC 36901 + + //@fixme - add non-master support (RTC 36950) + TARGETING::Target* xscom_target = + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + + // always read/write 64 bits to SCOM + size_t scom_size = sizeof(uint64_t); + + // atomic section >> + mutex_lock(&iv_mutex); + need_unlock = true; + + // write command register with LPC address to read + ControlReg_t eccb_cmd; + eccb_cmd.read_op = 1; + eccb_cmd.address = i_addr; + l_err = deviceOp( DeviceFW::WRITE, + xscom_target, + &(eccb_cmd.data64), + scom_size, + DEVICE_XSCOM_ADDRESS(ECCB_CTL_REG) ); + if( l_err ) { break; } + + // poll for complete and get the data back + StatusReg_t eccb_stat; + while(1) //@fixme - need a timeout value + { + l_err = deviceOp( DeviceFW::READ, + xscom_target, + &(eccb_stat.data64), + scom_size, + DEVICE_XSCOM_ADDRESS(ECCB_STAT_REG) ); + if( l_err ) { break; } + + if( eccb_stat.op_done == 1 ) + { + break; + } + + //@fixme - simics doesn't set the done bit yet (RTC 36901) + break; + } + if( l_err ) { break; } + + // check for errors + if( eccb_stat.data64 & LPC_STAT_REG_ERROR_MASK ) + { + TRACFCOMP(g_trac_pnor, "PnorDD::readLPC> Error from LPC Status Register : i_addr=0x%.8X, status=0x%.16X", i_addr, eccb_stat.data64 ); + + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORDD_READLPC + * @reasoncode PNOR::RC_LPC_ERROR + * @userdata1 LPC Address + * @userdata2 ECCB Status Register + * @devdesc PnorDD::readLPC> Error from LPC Status Register + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_READLPC, + PNOR::RC_LPC_ERROR, + TWO_UINT32_TO_UINT64(0,i_addr), + eccb_stat.data64); + l_err->collectTrace("PNOR"); + l_err->collectTrace("XSCOM"); + //@todo - Any cleanup or recovery needed? + break; + } + + // atomic section << + mutex_unlock(&iv_mutex); + need_unlock = false; + + // copy data out to caller's buffer + o_data = eccb_stat.read_data; +#endif + } while(0); + + if( need_unlock ) + { + mutex_unlock(&iv_mutex); + need_unlock = false; + } + + return l_err; +} + +/** + * @brief Write an address from LPC space + */ +errlHndl_t PnorDD::writeLPC(uint32_t i_addr, + uint32_t i_data) +{ + errlHndl_t l_err = NULL; + bool need_unlock = false; + + do { +#ifdef USE_FAKE_PNOR + write_fake_pnor( i_addr, static_cast<void*>(&i_data), + sizeof(uint32_t) ); +#else + //@todo - fill in with RTC 36901 + + //@fixme - add non-master support (RTC 36950) + TARGETING::Target* xscom_target = + TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + + // always read/write 64 bits to SCOM + size_t scom_size = sizeof(uint64_t); + + // atomic section >> + mutex_lock(&iv_mutex); + need_unlock = true; + + // write data register + uint64_t eccb_data = static_cast<uint64_t>(i_data); + l_err = deviceOp( DeviceFW::WRITE, + xscom_target, + &eccb_data, + scom_size, + DEVICE_XSCOM_ADDRESS(ECCB_DATA_REG) ); + if( l_err ) { break; } + + // write command register with LPC address to write + ControlReg_t eccb_cmd; + eccb_cmd.read_op = 0; + eccb_cmd.address = i_addr; + l_err = deviceOp( DeviceFW::WRITE, + xscom_target, + &(eccb_cmd.data64), + scom_size, + DEVICE_XSCOM_ADDRESS(ECCB_CTL_REG) ); + if( l_err ) { break; } + + // poll for complete and get the data back + StatusReg_t eccb_stat; + while(1) //@fixme - need a timeout value + { + l_err = deviceOp( DeviceFW::READ, + xscom_target, + &(eccb_stat.data64), + scom_size, + DEVICE_XSCOM_ADDRESS(ECCB_STAT_REG) ); + if( l_err ) { break; } + + if( eccb_stat.op_done == 1 ) + { + break; + } + + //@fixme - simics doesn't set the done bit yet : RTC 36901 + break; + } + if( l_err ) { break; } + + // check for errors + if( eccb_stat.data64 & LPC_STAT_REG_ERROR_MASK ) + { + TRACFCOMP(g_trac_pnor, "PnorDD::writeLPC> Error from LPC Status Register : i_addr=0x%.8X, status=0x%.16X", i_addr, eccb_stat.data64 ); + + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORDD_WRITELPC + * @reasoncode PNOR::RC_LPC_ERROR + * @userdata1 LPC Address + * @userdata2 ECCB Status Register + * @devdesc PnorDD::writeLPC> Error from LPC Status Register + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_WRITELPC, + PNOR::RC_LPC_ERROR, + TWO_UINT32_TO_UINT64(0,i_addr), + eccb_stat.data64); + l_err->collectTrace("PNOR"); + l_err->collectTrace("XSCOM"); + //@todo - Any cleanup or recovery needed? + break; + } + + // atomic section << + mutex_unlock(&iv_mutex); + need_unlock = false; +#endif + } while(0); + + if( need_unlock ) + { + mutex_unlock(&iv_mutex); + need_unlock = false; + } + + 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_targetAddr, + uint32_t i_wordsToWrite, + uint32_t* i_data) +{ + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock(0x%.8X,%d,%p)", i_targetAddr, i_wordsToWrite, i_data); + errlHndl_t l_err = NULL; + + // remember any data we read so we don't have to reread it later + typedef struct + { + uint32_t data; + bool wasRead; + bool diff; + } readflag_t; + readflag_t* read_data = NULL; + + do { +#ifndef SMART_WRITE + // LPC is accessed 32-bits at a time... + uint64_t words_written = 0; + for( uint32_t addr = i_targetAddr; + addr < (i_targetAddr+i_wordsToWrite*sizeof(uint32_t)); + addr += sizeof(uint32_t) ) + { + l_err = writeLPC( addr, i_data[words_written] ); + if( l_err ) { break; } + + words_written++; + } + o_bytesWritten = words_written*sizeof(uint32_t); + if( l_err ) { break; } + + +#else + + // remember any data we read so we don't have to reread it later + read_data = new readflag_t[ERASESIZE_WORD32]; + for( size_t x = 0; x < ERASESIZE_WORD32; x++ ) + { + read_data[x].wasRead = false; + read_data[x].diff = false; + } + + // remember if we need to erase the block or not + bool need_erase = false; + + // walk through every word of data to see what changed + const uint32_t block_addr = findEraseBlock(i_targetAddr); + for( uint64_t bword = 0; bword < ERASESIZE_WORD32; bword++ ) + { + // note: bword is the offset into the flash block + read_data[bword].diff = false; + + // no need to check data before where the write starts + if( (block_addr + bword*sizeof(uint32_t)) < i_targetAddr ) + { + continue; + } + // no need to check data after where the write ends + else if( (block_addr + bword*sizeof(uint32_t)) >= + (i_targetAddr + i_wordsToWrite*sizeof(uint32_t)) ) + { + // done looking now + break; + } + // otherwise we need to compare our data with what is in flash now + else + { + l_err = readLPC( block_addr + bword*sizeof(uint32_t), + read_data[bword].data ); + if( l_err ) { break; } + + read_data[bword].wasRead = true; + + // dword is the offset into the input data + uint64_t dword = (block_addr + bword*sizeof(uint32_t)); + dword -= i_targetAddr; //offset into user data + dword = dword / sizeof(uint32_t); //convert bytes to words + + // look for any bits being changed (using XOR) + if( read_data[bword].data ^ i_data[dword] ) + { + read_data[bword].diff = true; + + // look for any bits that go from 1->0 + if( read_data[bword].data & ~(i_data[dword]) ) + { + need_erase = true; + } + + // push the user data into the read buffer + // to get written later + read_data[bword].data = i_data[dword]; + } + } + } + if( l_err ) { break; } + + // erase the block if we need to + if( need_erase ) + { + // first we need to save off any data we didn't read yet + // that is not part of the data we are writing + for( uint64_t bword = 0; bword < ERASESIZE_WORD32; bword++ ) + { + // mark the word as different to force a write below + read_data[bword].diff = true; + + // skip what we already read + if( read_data[bword].wasRead ) + { + continue; + } + + // dword is the offset into the input data + uint64_t dword = (block_addr + bword*sizeof(uint32_t)); + dword -= i_targetAddr; //offset into user data + dword = dword / sizeof(uint32_t); //convert bytes to words + + // restore the data before the write section + if( (block_addr + bword*sizeof(uint32_t)) < i_targetAddr ) + { + l_err = readLPC( block_addr + bword*sizeof(uint32_t), + read_data[bword].data ); + if( l_err ) { break; } + } + // restore the data after the write section + else if( (block_addr + bword*sizeof(uint32_t)) >= + (i_targetAddr + i_wordsToWrite*sizeof(uint32_t)) ) + { + l_err = readLPC( block_addr + bword*sizeof(uint32_t), + read_data[bword].data ); + if( l_err ) { break; } + } + // otherwise we will use the write data directly + else + { + read_data[bword].data = i_data[dword]; + } + } + if( l_err ) { break; } + + // erase the flash + l_err = eraseFlash( block_addr ); + if( l_err ) { break; } + } + + // walk through every word again to write the data back out + uint64_t bword_written = 0; + for( bword_written = 0; + bword_written < ERASESIZE_WORD32; + bword_written++ ) + { + // only write what we have to + if( !(read_data[bword_written].diff) ) + { + continue; + } + + // write the word out to the flash + l_err = writeLPC( block_addr + bword_written*sizeof(uint32_t), + read_data[bword_written].data ); + if( l_err ) { break; } + //@todo - How should we handle PNOR errors? + } + if( l_err ) { break; } + +#endif + + } while(0); + + if( read_data ) + { + delete[] read_data; + } + + return l_err; +} + +/** + * @brief Erase a block of flash + */ +errlHndl_t PnorDD::eraseFlash(uint32_t i_address) +{ + errlHndl_t l_err = NULL; + + do { + if( findEraseBlock(i_address) != i_address ) + { + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORDD_ERASEFLASH + * @reasoncode PNOR::RC_LPC_ERROR + * @userdata1 LPC Address + * @userdata2 Nearest Erase Boundary + * @devdesc PnorDD::eraseFlash> Address not on erase boundary + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_ERASEFLASH, + PNOR::RC_LPC_ERROR, + TWO_UINT32_TO_UINT64(0,i_address), + findEraseBlock(i_address)); + break; + } + + // log the erase of this block + iv_erases[i_address/ERASESIZE_BYTES]++; + TRACFCOMP(g_trac_pnor, "PnorDD::eraseFlash> Block 0x%.8X has %d erasures", i_address, iv_erases[i_address/ERASESIZE_BYTES] ); + + //@todo - issue some LPC/SPI commands to erase the block RTC 35728 + char* ptr = (char*)(FAKE_PNOR_START+i_address); + memset( ptr, 0, ERASESIZE_BYTES ); + } while(0); + + return l_err; +} + + }; //end PNOR namespace + + + +#ifdef USE_FAKE_PNOR +void write_fake_pnor( uint64_t i_pnorAddr, void* i_buffer, size_t i_size ) +{ + //create a pointer to the offset start. + char * destPtr = (char *)(FAKE_PNOR_START+i_pnorAddr); + + //copy data from memory into the buffer. + memcpy(destPtr, i_buffer, i_size); +} +void read_fake_pnor( uint64_t i_pnorAddr, void* o_buffer, size_t i_size ) +{ + //create a pointer to the offset start. + char * srcPtr = (char *)(FAKE_PNOR_START+i_pnorAddr); + + //copy data from memory into the buffer. + memcpy(o_buffer, srcPtr, i_size); +} +#endif diff --git a/src/usr/pnor/pnordd.H b/src/usr/pnor/pnordd.H index 8d3b7a9a9..6b102ea3b 100644 --- a/src/usr/pnor/pnordd.H +++ b/src/usr/pnor/pnordd.H @@ -30,89 +30,285 @@ namespace PNOR { -//Not sure if this is the right place for these enums, open to suggestions. /** - * PNOR Modes + * @brief Type definition for PNOR address */ -enum lscMode +typedef uint64_t PNORAddress_t; + + +class PnorDD { - MMRD = 0, /**< Indicates PNOR is in MMRD Mode */ - PMWR, /**< Indicates PNOR is in PMWR Mode */ - LAST_MODE, -}; + public: + /** + * @brief Performs a PNOR Read Operation + * + * @parm o_buffer Buffer to read data into + * @parm io_buflen Input: Number of bytes to read, + * Output: Number of bytes actually read + * @parm i_address Offset into flash to read + * + * @return Error from operation + */ + errlHndl_t readFlash(void* o_buffer, + size_t& io_buflen, + uint64_t i_address); + /** + * @brief Performs a PNOR Write Operation + * + * @parm i_buffer Buffer to write data from + * @parm io_buflen Input: Number of bytes to write, + * Output: Number of bytes actually written + * @parm i_address Offset into flash to write + * + * @return Error from operation + */ + errlHndl_t writeFlash(void* i_buffer, + size_t& io_buflen, + uint64_t i_address); -/** - * @brief Type definition for PNOR address - */ -typedef uint64_t PNORAddress_t; + protected: + /** + * @brief Constructor + */ + PnorDD(); -//@TODO: comment args -/** - * @brief External interface for setting LSC mode - */ -errlHndl_t setLSCAccessMode(lscMode i_mode); + /** + * @brief Destructor + */ + ~PnorDD(); + /** + * @brief Verify flash request is in appropriate address range + * + * @i_address Flash address being operated on + * @i_length Length of chunk being operated on + * + * @return Error if requested address range is invalid + */ + errlHndl_t verifyFlashAddressRange(uint64_t i_address, + size_t& i_length); -class PnorDD -{ + /** + * @brief LPC HC Registers + * These are offsets within the LPC Host Controller Register Space + */ + enum LpcRegAddr { + LPC_REG_BAR0 = 0x00, /**< BAR0 : OPB register */ + LPC_REG_BAR1 = 0x04, /**< BAR1 : LPC I/O space */ + LPC_REG_BAR2 = 0x08, /**< BAR2 : LPC Memory space */ + LPC_REG_BAR3 = 0x0C, /**< BAR3 : LPC Firmware space */ + }; + + /** + * @brief Read a LPC Host Controller Register + * + * @parm i_addr Register address, relative to the + * LPC Host Controller Register Space + * @parm o_data Buffer to read data into + * + * @return Error from operation + */ + errlHndl_t readRegLPC(LpcRegAddr i_addr, + uint32_t& o_data); - public: - //@TODO: comment args /** - * @brief Performs an PNOR Read Operation + * @brief Write a LPC Host Controller Register + * + * @parm i_addr Register address, relative to the + * LPC Host Controller Register Space + * @parm o_data Data to write + * + * @return Error from operation */ - errlHndl_t read(void* o_buffer, - size_t& io_buflen, - uint64_t i_address); + errlHndl_t writeRegLPC(LpcRegAddr i_addr, + uint32_t i_data); - //@TODO: comment args /** - * @brief Performs an PNOR Write Operation + * @brief Read a SPI Register + * + * @parm i_addr Register address, relative to the SPI engine + * @parm o_data Buffer to read data into + * + * @return Error from operation */ - errlHndl_t write(void* i_buffer, - size_t& io_buflen, - uint64_t i_address); + errlHndl_t readRegSPI(uint32_t i_addr, + uint32_t& o_data); - //@TODO: comment args /** - * @brief Set LSC to desired access mode + * @brief Write a SPI Register + * + * @parm i_addr Register address, relative to the SPI engine + * @parm o_data Data to write + * + * @return Error from operation */ - errlHndl_t setAccessMode(lscMode i_mode); + errlHndl_t writeRegSPI(uint32_t i_addr, + uint32_t i_data); + /** + * @brief Some general constants + * + */ + enum { + LPCHC_FW_SPACE = 0xF0000000, /**< LPC Host Controller FW Space */ + LPCHC_MEM_SPACE = 0xE0000000, /**< LPC Host Controller Mem Space */ + LPCHC_IO_SPACE = 0xD0010000, /**< LPC Host Controller I/O Space */ + LPCHC_REG_SPACE = 0xC0012000, /**< LPC Host Ctlr Register Space */ + + ECCB_CTL_REG = 0x000B0020, /**< ECCB Control Reg (FW) */ + ECCB_STAT_REG = 0x000B0022, /**< ECCB Status Reg (FW) */ + ECCB_DATA_REG = 0x000B0023, /**< ECCB Data Reg (FW) */ + + // Default Values to set for all operations + // 1101.0100.0000.000x.0000.0001.0000.0000.<address> + LPC_CTL_REG_DEFAULT = 0xD400010000000000, + + LPC_STAT_REG_ERROR_MASK = 0xFC0000000007F700, /**< Error Bits */ + + PNORSIZE = 3 * MEGABYTE, //@fixme - read from TOC instead + ERASESIZE_BYTES = 4 * KILOBYTE, /**< Minimum Erase Block (bytes) */ + ERASESIZE_WORD32 = ERASESIZE_BYTES/(sizeof(uint32_t)), /**< Erase Block (32-bit words) */ + }; + - protected: /** - * @brief Constructor + * @brief Read an address from LPC space + * + * @parm i_addr Absolute LPC Address + * @parm o_data Buffer to read data into + * + * @return Error from operation */ - PnorDD(); + errlHndl_t readLPC(uint32_t i_addr, + uint32_t& o_data); + /** + * @brief Write an address from LPC space + * + * @parm i_addr Absolute LPC Address + * @parm o_data Data to write + * + * @return Error from operation + */ + errlHndl_t writeLPC(uint32_t i_addr, + uint32_t i_data); /** - * @brief Destructor + * @brief Erase a block of flash + * + * @parm i_address Offset into flash to erase, aligned to erase block + * + * @return Error from operation */ - ~PnorDD(); + errlHndl_t eraseFlash(uint32_t i_address); + + /** + * @brief Compare the existing data in 1 erase block of the flash with + * the incoming data and write or erase as needed + * + * @parm i_targetAddr Starting address to write + * @parm i_wordsToWrite Number of 32-bit words to write + * @parm i_data Buffer of data to write + * + * @return Error from operation + */ + errlHndl_t compareAndWriteBlock(uint32_t i_targetAddr, + uint32_t i_wordsToWrite, + uint32_t* i_data); + + /** + * @brief Determine the nearest flash address aligned to an erase block + * + * @parm i_address Offset into flash + * + * @return Block-aligned flash address + */ + uint32_t findEraseBlock(uint32_t i_address) + { + return (i_address - i_address%ERASESIZE_BYTES); + }; - //@TODO: comment args /** - * Verify Request is in appropriate address range + * @brief Determine the number of erase blocks that are included in + * the given range + * + * @parm i_address Offset into flash + * @parm i_byteSize Number of bytes in range * + * @return Number of full or partial erase blocks */ - errlHndl_t verifyAddressRange(uint64_t i_address, - size_t& i_length); + uint32_t getNumAffectedBlocks(uint32_t i_address, + size_t i_byteSize) + { + uint32_t blocks = 0; + uint32_t addr = i_address; + while( findEraseBlock(addr) < (i_address+i_byteSize) ) + { + blocks++; + addr += ERASESIZE_BYTES; + } + return blocks; + }; - private: + /** + * @brief ECCB Control Register Layout + */ + union ControlReg_t + { + uint64_t data64; + struct + { + // unused sections should be set to zero + uint64_t magic1 : 8; /**< 0:7 = b11010100 per spec */ + uint64_t unused1 : 7; /**< 8:14 */ + uint64_t read_op : 1; /**< 15 = set for read operation */ + uint64_t unused2 : 7; /**< 16:22 */ + uint64_t addr_len : 3; /**< 23:25 = 100 means 4 byte */ + uint64_t unused3 : 6; /**< 26:31 */ + uint64_t address : 32; /**< 32:63 = LPC Address */ + }; + + ControlReg_t() : data64(LPC_CTL_REG_DEFAULT) {}; + }; /** - * PNOR Mode Flag + * @brief ECCB Status Register Layout */ - lscMode iv_lscMode; + union StatusReg_t + { + uint64_t data64; + struct + { + uint64_t pib_errors : 6; /**< 0:5 */ + uint64_t read_data : 32; /**< 6:37 */ + uint64_t unused1 : 6; /**< 38:43 */ + uint64_t busy : 1; /**< 44 = Operation Busy */ + uint64_t errors1 : 7; /**< 45:51 */ + uint64_t op_done : 1; /**< 52 */ + uint64_t errors2 : 3; /**< 53:55 */ + uint64_t unused2 : 8; /**< 56:63 */ + }; + StatusReg_t() : data64(0) {}; + }; + private: // Variables + /** + * @brief Mutex to prevent concurrent PNOR accesses + */ + mutex_t iv_mutex; + + /** + * @brief Track PNOR erases for wear monitoring + * (making this static so tools can find it easier) + * track writes by page (=erase block) + */ + uint8_t iv_erases[PNORSIZE/ERASESIZE_BYTES]; }; }; //end PNOR namespace diff --git a/src/usr/pnor/pnorrp.C b/src/usr/pnor/pnorrp.C index e6987d553..481653ac7 100644 --- a/src/usr/pnor/pnorrp.C +++ b/src/usr/pnor/pnorrp.C @@ -33,6 +33,7 @@ #include <sys/mm.h> #include <errno.h> #include <initservice/initserviceif.H> +#include "pnordd.H" // Trace definition trace_desc_t* g_trac_pnor = NULL; @@ -313,8 +314,7 @@ errlHndl_t PnorRP::readTOC() iv_TOC[side][id].id = id; iv_TOC[side][id].side = (PNOR::SideSelect)side; iv_TOC[side][id].chip = 0; - iv_TOC[side][id].mmrdAddr = 0; - iv_TOC[side][id].pmrwAddr = 0; + iv_TOC[side][id].flashAddr = 0; iv_TOC[side][id].virtAddr = 0; iv_TOC[side][id].size = 0; iv_TOC[side][id].eccProtected = 0; @@ -322,10 +322,10 @@ errlHndl_t PnorRP::readTOC() } //@todo - Add in some dummy values for now - // Will update under Story 3547 + // Will update under Story 3871 // assume 1 chip with only 1 side for now, no sideless - // TOC starts at offset zero in MMRD mode + // TOC starts at offset zero // put some random sizes in here iv_TOC[PNOR::SIDE_A][PNOR::TOC].size = 8 + 8 + PNOR::NUM_SECTIONS*sizeof(TOCEntry_t); @@ -339,21 +339,16 @@ errlHndl_t PnorRP::readTOC() iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].virtAddr = iv_TOC[PNOR::SIDE_A][PNOR::TOC].virtAddr + iv_TOC[PNOR::SIDE_A][PNOR::TOC].size; iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].virtAddr = iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].virtAddr + iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].size; iv_TOC[PNOR::SIDE_A][PNOR::HB_DATA].virtAddr = iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].virtAddr + iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].size; - // MMRD offsets - iv_TOC[PNOR::SIDE_A][PNOR::TOC].mmrdAddr = 0; - iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].mmrdAddr = iv_TOC[PNOR::SIDE_A][PNOR::TOC].mmrdAddr + iv_TOC[PNOR::SIDE_A][PNOR::TOC].size; - iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].mmrdAddr = iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].mmrdAddr + iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].size; - iv_TOC[PNOR::SIDE_A][PNOR::HB_DATA].mmrdAddr = iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].mmrdAddr + iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].size; - // PMRW offsets - no ECC support yet so just equal to MMRD - iv_TOC[PNOR::SIDE_A][PNOR::TOC].pmrwAddr = iv_TOC[PNOR::SIDE_A][PNOR::TOC].mmrdAddr; - iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].pmrwAddr = iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].mmrdAddr; - iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].pmrwAddr = iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].mmrdAddr; - iv_TOC[PNOR::SIDE_A][PNOR::HB_DATA].pmrwAddr = iv_TOC[PNOR::SIDE_A][PNOR::HB_DATA].mmrdAddr; + // flash + iv_TOC[PNOR::SIDE_A][PNOR::TOC].flashAddr = 0; + iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].flashAddr = iv_TOC[PNOR::SIDE_A][PNOR::TOC].flashAddr + iv_TOC[PNOR::SIDE_A][PNOR::TOC].size; + iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].flashAddr = iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].flashAddr + iv_TOC[PNOR::SIDE_A][PNOR::HB_EXT_CODE].size; + iv_TOC[PNOR::SIDE_A][PNOR::HB_DATA].flashAddr = iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].flashAddr + iv_TOC[PNOR::SIDE_A][PNOR::GLOBAL_DATA].size; //@todo - end fake data //@todo - load flash layout (how many chips) - //@todo - read TOC on each chip/bank/whatever + //@todo - read TOC on each chip/bank/whatever TRACUCOMP(g_trac_pnor, "< PnorRP::readTOC" ); return l_errhdl; @@ -391,17 +386,14 @@ void PnorRP::waitForMessage() eff_addr = (uint8_t*)message->data[0]; user_addr = (uint8_t*)message->data[1]; - //@todo - assuming MMRD mode for now (Story 3548) - l_errhdl = computeDeviceAddr( eff_addr, PNOR::MMRD, dev_offset, chip_select, needs_ecc ); + //figure out the real pnor offset + l_errhdl = computeDeviceAddr( eff_addr, dev_offset, chip_select, needs_ecc ); if( l_errhdl ) { status_rc = -EFAULT; /* Bad address */ } else { - //@todo - handle MMRD/PMRW mode - // if MMRD then needs_ecc = false - switch(message->type) { case( MSG_MM_RP_READ ): @@ -594,7 +586,6 @@ errlHndl_t PnorRP::writeToDevice( uint64_t i_offset, * @brief Convert a virtual address into the PNOR device address */ errlHndl_t PnorRP::computeDeviceAddr( void* i_vaddr, - PNOR::lscMode i_mode, uint64_t& o_offset, uint64_t& o_chip, bool& o_ecc ) @@ -639,14 +630,7 @@ errlHndl_t PnorRP::computeDeviceAddr( void* i_vaddr, o_chip = iv_TOC[side][id].chip; o_ecc = iv_TOC[side][id].eccProtected; o_offset = l_vaddr - iv_TOC[side][id].virtAddr; //offset into pnor - if( PNOR::MMRD == i_mode ) - { - o_offset += iv_TOC[side][id].mmrdAddr; - } - else - { - o_offset += iv_TOC[side][id].pmrwAddr; - } + o_offset += iv_TOC[side][id].flashAddr; TRACUCOMP( g_trac_pnor, "< PnorRP::computeDeviceAddr: o_offset=0x%X, o_chip=%d", o_offset, o_chip ); return l_errhdl; diff --git a/src/usr/pnor/pnorrp.H b/src/usr/pnor/pnorrp.H index 79d2728a2..6d387c203 100644 --- a/src/usr/pnor/pnorrp.H +++ b/src/usr/pnor/pnorrp.H @@ -29,7 +29,6 @@ #include <builtins.h> #include <errl/errlentry.H> #include <vmmconst.h> -#include "pnordd.H" /** * PNOR Resource Provider @@ -116,8 +115,7 @@ class PnorRP uint64_t virtAddr; /**< Virtual address for the start of the section */ - uint32_t mmrdAddr; /**< Address in MMRD mode (no ECC) */ - uint32_t pmrwAddr; /**< Address in PMRW mode (with ECC) */ + uint32_t flashAddr; /**< Address in flash */ uint32_t size; /**< Actual size of content in bytes (not including ECC) */ uint8_t chip; /**< Chip Select */ @@ -192,7 +190,6 @@ class PnorRP * @brief Convert a virtual address into the PNOR device address * * @param[in] i_vaddr Virtual address of page - * @param[in] i_mode PNOR Controller mode * @param[out] o_offset Offset into PNOR chip * @param[out] o_chip Which PNOR chip * @param[out] o_ecc true=data is ECC-protected @@ -200,7 +197,6 @@ class PnorRP * @return Error if VA is bad */ errlHndl_t computeDeviceAddr( void* i_vaddr, - PNOR::lscMode i_mode, uint64_t& o_offset, uint64_t& o_chip, bool& o_ecc ); diff --git a/src/usr/pnor/test/pnorddtest.H b/src/usr/pnor/test/pnorddtest.H index d8eb4af63..eb874904a 100644 --- a/src/usr/pnor/test/pnorddtest.H +++ b/src/usr/pnor/test/pnorddtest.H @@ -34,13 +34,14 @@ #include <errl/errlentry.H> #include <pnor/pnorif.H> #include <devicefw/userif.H> +#include <kernel/console.H> +#include <sys/time.h> #define BASE_SCRATCH_SPACE 2*1024*1024+1024*512 //2.5MB offset in fake PNOR extern trace_desc_t* g_trac_pnor; using namespace TARGETING; - class PnorDdTest : public CxxTest::TestSuite { public: @@ -52,95 +53,218 @@ class PnorDdTest : public CxxTest::TestSuite */ void test_readwrite(void) { - //@TODO: make this table driven so it can test more values - //@TODO: Add some more interesting tests - - TARGETING::Target* l_testTarget = MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; - size_t l_size = sizeof(uint64_t); - errlHndl_t l_err = NULL; - uint64_t fails = 0; - uint64_t total = 4; - - do{ - TS_TRACE("PnorDdTest::test_readwrite: starting"); - - // Perform PnorDD Write 1 - uint64_t l_address = BASE_SCRATCH_SPACE+0x100; - uint64_t l_writeData = 0x12345678FEEDB0B0; - l_err = deviceWrite(l_testTarget, - &l_writeData, - l_size, - DEVICE_PNOR_ADDRESS(0, l_address)); - if (l_err) + //@TODO: make this table driven so it can test more values + //@TODO: Add some more interesting tests + + TARGETING::Target* l_testTarget = MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + size_t l_size = sizeof(uint64_t); + errlHndl_t l_err = NULL; + uint64_t fails = 0; + uint64_t total = 8; + + do{ + TS_TRACE("PnorDdTest::test_readwrite: starting"); + + // Perform PnorDD Write 1 + uint64_t l_address = BASE_SCRATCH_SPACE+0x100; + uint64_t l_writeData = 0x12345678FEEDB0B0; + l_size = sizeof(uint64_t); + l_err = deviceWrite(l_testTarget, + &l_writeData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 1: deviceWrite() failed! Error committed."); - fails++; - break; + TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 1: deviceWrite() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; } - - - // Perform PnorDD Write 2 - l_address = BASE_SCRATCH_SPACE+0x108; - l_writeData = 0xFEEDBEEF000ABCDE; - l_err = deviceWrite(l_testTarget, - &l_writeData, - l_size, - DEVICE_PNOR_ADDRESS(0, l_address)); - if (l_err) + if(l_size != sizeof(uint64_t)) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 2: deviceWrite() failed! Error committed."); - fails++; - break; + TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 1: Write length not expected value. Addr: 0x%llx, Exp: %d, Act: %d", l_address, + sizeof(uint64_t), l_size); + fails++; } - // Perform PnorDD read 1 - l_address = BASE_SCRATCH_SPACE+0x100; - uint64_t l_readData = 0; - l_err = deviceRead(l_testTarget, - &l_readData, - l_size, - DEVICE_PNOR_ADDRESS(0, l_address)); - if (l_err) + // Perform PnorDD Write 2 + l_address = BASE_SCRATCH_SPACE+0x108; + l_writeData = 0xFEEDBEEF000ABCDE; + l_size = sizeof(uint64_t); + l_err = deviceWrite(l_testTarget, + &l_writeData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 1: deviceRead() failed! Error committed."); - fails++; - break; + TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 2: deviceWrite() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; } - else if(l_readData != 0x12345678FEEDB0B0) + if(l_size != sizeof(uint64_t)) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 1: Read data not expected value. Addr: 0x%llx, ExpData: 0x12345678FEEDB0B0, ActData: 0x%llx", - l_address, (long long unsigned)l_readData); - fails++; - break; + TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 2: Write length not expected value. Addr: 0x%llx, Exp: %d, Act: %d", l_address, + sizeof(uint64_t), l_size); + fails++; } - // Perform PnorDD read 2 - l_address = BASE_SCRATCH_SPACE+0x108; - l_err = deviceRead(l_testTarget, - &l_readData, - l_size, - DEVICE_PNOR_ADDRESS(0, l_address)); - if (l_err) + // Perform PnorDD read 1 + l_address = BASE_SCRATCH_SPACE+0x100; + uint64_t l_readData = 0; + l_size = sizeof(uint64_t); + l_err = deviceRead(l_testTarget, + &l_readData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 2: deviceRead() failed! Error committed."); - break; + TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 1: deviceRead() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; } - else if(l_readData != 0xFEEDBEEF000ABCDE) + if(l_readData != 0x12345678FEEDB0B0) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 2: Read data not expected value. Addr: 0x%llx, ExpData: 0xFEEDBEEF000ABCDE, ActData: 0x%llx", - l_address, (long long unsigned)l_readData ); - fails++; - break; + TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 1: Read data not expected value. Addr: 0x%llx, ExpData: 0x12345678FEEDB0B0, ActData: 0x%llx", + l_address, (long long unsigned)l_readData); + fails++; + } + if(l_size != sizeof(uint64_t)) + { + TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 1: Read length not expected value. Addr: 0x%llx, Exp: %d, Act: %d", l_address, + sizeof(uint64_t), l_size); + fails++; } - }while(0); + // Perform PnorDD read 2 + l_address = BASE_SCRATCH_SPACE+0x108; + l_size = sizeof(uint64_t); + l_err = deviceRead(l_testTarget, + &l_readData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) + { + TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 2: deviceRead() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; + break; + } + if(l_readData != 0xFEEDBEEF000ABCDE) + { + TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 2: Read data not expected value. Addr: 0x%llx, ExpData: 0xFEEDBEEF000ABCDE, ActData: 0x%llx", + l_address, (long long unsigned)l_readData ); + fails++; + break; + } + if(l_size != sizeof(uint64_t)) + { + TS_FAIL("PnorDdTest::test_readwrite: PNORDD read 2: Read length not expected value. Addr: 0x%llx, Exp: %d, Act: %d", l_address, + sizeof(uint64_t), l_size); + fails++; + } + }while(0); - TRACFCOMP(g_trac_pnor, "PnorDdTest::test_readwrite> %d/%d fails", fails, total ); + TRACFCOMP(g_trac_pnor, "PnorDdTest::test_readwrite> %d/%d fails", fails, total ); }; + /** + * @brief PNOR DD smart write/erase test + * Write some data to PNOR to force an erase + */ + void test_smartwrite(void) + { + TARGETING::Target* l_testTarget = MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + size_t l_size = sizeof(uint64_t); + errlHndl_t l_err = NULL; + uint64_t fails = 0; + uint64_t total = 8; + + do{ + TS_TRACE("PnorDdTest::test_smartwrite: starting"); + + // Perform PnorDD Write 1 + uint64_t l_address = BASE_SCRATCH_SPACE+0x120; + uint64_t l_writeData = 0xAAAAAAAA55555555; + l_size = sizeof(uint64_t); + l_err = deviceWrite(l_testTarget, + &l_writeData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) + { + TS_FAIL("PnorDdTest::test_smartwrite: PNORDD write 1: deviceWrite() 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 = deviceWrite(l_testTarget, + &l_writeData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) + { + TS_FAIL("PnorDdTest::test_smartwrite: PNORDD write 2: deviceWrite() 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 = deviceWrite(l_testTarget, + &l_writeData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address+sizeof(uint64_t))); + if (l_err) + { + TS_FAIL("PnorDdTest::test_smartwrite: PNORDD write 3: deviceWrite() 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 = deviceWrite(l_testTarget, + &l_writeData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) + { + TS_FAIL("PnorDdTest::test_smartwrite: PNORDD write 4: deviceWrite() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; + } + + // Perform PnorDD read + uint64_t l_readData = 0; + l_size = sizeof(uint64_t); + l_err = deviceRead(l_testTarget, + &l_readData, + l_size, + DEVICE_PNOR_ADDRESS(0, l_address)); + if (l_err) + { + TS_FAIL("PnorDdTest::test_smartwrite: PNORDD read: deviceRead() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; + } + if(l_readData != l_writeData) + { + TS_FAIL("PnorDdTest::test_smartwrite: 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); + TRACFCOMP(g_trac_pnor, "PnorDdTest::test_smartwrite> %d/%d fails", fails, total ); + } + /*Not really a real test, just using to verify ext image is loading properly. Leaving it commented out because the test-case will not dynamically find the extended image based on the TOC // void testPnorDD2(void) |