diff options
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/pnor/pnordd.C | 1128 | ||||
-rw-r--r-- | src/usr/pnor/pnordd.H | 319 | ||||
-rw-r--r-- | src/usr/pnor/test/pnorddtest.H | 75 |
3 files changed, 1096 insertions, 426 deletions
diff --git a/src/usr/pnor/pnordd.C b/src/usr/pnor/pnordd.C index 36a014412..5f53a9c84 100644 --- a/src/usr/pnor/pnordd.C +++ b/src/usr/pnor/pnordd.C @@ -54,6 +54,7 @@ void erase_fake_pnor( uint64_t i_pnorAddr, size_t i_size ); extern trace_desc_t* g_trac_pnor; + namespace PNOR { @@ -130,7 +131,7 @@ errlHndl_t ddRead(DeviceFW::OperationType i_opType, * @return errlHndl_t */ errlHndl_t ddWrite(DeviceFW::OperationType i_opType, - TARGETING::Target* i_target, + TARGETING::Target* i_target, void* io_buffer, size_t& io_buflen, int64_t i_accessType, @@ -159,6 +160,82 @@ errlHndl_t ddWrite(DeviceFW::OperationType i_opType, return l_err; } +/** + * @brief Used for VPO testing via istep hack. Will be removed once VPO Bringup is done. + * TODO (RTC:42487: Remove this once VPO PNOR bringup is done. + */ +void testRealPnor(void *io_pArgs) +{ + // TARGETING::Target* l_testTarget = + // TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; + size_t l_size = sizeof(uint64_t); + errlHndl_t l_err = NULL; + uint64_t fails = 0; + uint64_t total = 0; + + TRACFCOMP(g_trac_pnor, "PNOR::testRealPnor> starting" ); + + PnorDD* pnordd = NULL; + pnordd = new PnorDD(PnorDD::MODEL_REAL_CMD); + + + // Perform PnorDD read + const uint64_t l_address = 0x4; + uint64_t l_readData = 0; + l_size = sizeof(uint64_t); + l_err = pnordd->readFlash(&l_readData, + l_size, + l_address); + total++; + if (l_err) + { + TRACFCOMP(g_trac_pnor, "E>PnorDdTest::testRealPnor: PNORDD read 1: readFlash() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; + } + total++; + + TRACFCOMP(g_trac_pnor, "PNOR::testRealPnor> l_readData=0x%.16x", l_readData ); + TRACFCOMP(g_trac_pnor, "PNOR::testRealPnor> Try writing data" ); + + uint64_t l_writeData = 0x12345678FEEDB0B0; + l_size = sizeof(uint64_t); + l_err = pnordd->writeFlash(&l_writeData, + l_size, + l_address); + + total++; + if (l_err) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::testRealPnor: PNORDD write 1: writeFlash() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; + } + total++; + + // Perform PnorDD read + l_readData = 0; + l_size = sizeof(uint64_t); + l_err = pnordd->readFlash(&l_readData, + l_size, + l_address); + total++; + if (l_err) + { + TRACFCOMP(g_trac_pnor, "E>PNOR::testRealPnor: PNORDD read 2: readFlash() failed! Error committed."); + errlCommit(l_err,PNOR_COMP_ID); + fails++; + } + total++; + + TRACFCOMP(g_trac_pnor, "PNOR::testRealPnor> PNORDD read 2: l_readData=0x%.16x", l_readData ); + + TRACFCOMP(g_trac_pnor, "PNOR::testRealPnor> %d/%d fails", fails, total ); + if( pnordd ) + { + delete pnordd; + } +} // Register PNORDD access functions to DD framework DEVICE_REGISTER_ROUTE(DeviceFW::READ, @@ -207,22 +284,13 @@ errlHndl_t PnorDD::readFlash(void* o_buffer, break; } - // 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) ) - { - // flash is mapped directly in the FW space - l_err = readLPC( addr + LPCHC_FW_SPACE, - word_ptr[words_read] ); - if( l_err ) { break; } + //If we get here we're doing either MODEL_LPC_MEM or MODEL_REAL_CMD + mutex_lock(&cv_mutex); + l_err = bufferedSfcRead(i_address, io_buflen, o_buffer); + mutex_unlock(&cv_mutex); + + if(l_err) { break;} - words_read++; - } - io_buflen = words_read*sizeof(uint32_t); - if( l_err ) { break; } }while(0); return l_err; @@ -256,45 +324,58 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer, break; } - // 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 an entire block of data at a time. - uint32_t* word_ptr = static_cast<uint32_t*>(i_buffer); - uint32_t cur_addr = static_cast<uint32_t>(l_address); - uint64_t num_blocks = getNumAffectedBlocks(cur_addr,io_buflen); + //If we get here we're doing either MODEL_LPC_MEM or MODEL_REAL_CMD + + // 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. + + uint32_t cur_writeStart_addr = static_cast<uint32_t>(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; + uint64_t num_blocks = getNumAffectedBlocks(cur_writeStart_addr,io_buflen); 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 ) { - TRACDCOMP( g_trac_pnor, "Block %d: bytes_left=%d, cur_addr=0x%.8X", block, bytes_left, cur_addr ); + write_bytes = iv_erasesize_bytes; + if(bytes_left < iv_erasesize_bytes ) + { + uint32_t end_waste = 0; + //deduct any unused space at the end of the erase block + if( cur_blkEnd_addr > (cur_writeStart_addr + bytes_left)) + { + end_waste = cur_blkEnd_addr - (cur_writeStart_addr + bytes_left); + write_bytes -= end_waste; + } + + //deduct any unused space at the beginning of the erase block + write_bytes = write_bytes - (cur_writeStart_addr - cur_blkStart_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 ); + mutex_lock(&cv_mutex); + l_err = compareAndWriteBlock(cur_blkStart_addr, + cur_writeStart_addr, + write_bytes, + (void*)((uint64_t)i_buffer + ((uint64_t)cur_writeStart_addr-l_address))); + mutex_unlock(&cv_mutex); + if( l_err ) { break; } //@todo (RTC:37744) - 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 = (l_address + io_buflen - cur_addr); - } - word_ptr += ((bytes_left-1)/sizeof(uint32_t))+1; + 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. + cur_writeStart_addr += write_bytes; + bytes_left -= write_bytes; + } if( l_err ) { break; } @@ -315,6 +396,7 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer, /******************** Private/Protected Methods ********************/ +mutex_t PnorDD::cv_mutex = MUTEX_INITIALIZER; /** * @brief Constructor @@ -322,19 +404,12 @@ errlHndl_t PnorDD::writeFlash(void* i_buffer, PnorDD::PnorDD( PnorMode_t i_mode ) : iv_mode(i_mode) { - mutex_init(&iv_mutex); - - for( uint64_t x=0; x < (PNORSIZE/ERASESIZE_BYTES); x++ ) - { - iv_erases[x] = 0; - } + iv_erasesize_bytes = ERASESIZE_BYTES_DEFAULT; + iv_erases = NULL; //In the normal case we will choose the mode for the caller if( MODEL_UNKNOWN == iv_mode ) { - //Use ECCB scoms to drive LPC, flat memory map behind ECCB, no SPI - //iv_mode = MODEL_FLAT_ECCB; - //Break into 32-bit LPC ops but use memcpy into cache area iv_mode = MODEL_LPC_MEM; @@ -342,6 +417,11 @@ PnorDD::PnorDD( PnorMode_t i_mode ) //@fixme - how?? I can't use targetting yet to tell I'm in VPO... } + if( MODEL_REAL_CMD == iv_mode ) + { + sfcInit( ); + } + TRACFCOMP(g_trac_pnor, "PnorDD::PnorDD()> Using mode %d", iv_mode); } @@ -350,8 +430,132 @@ PnorDD::PnorDD( PnorMode_t i_mode ) */ PnorDD::~PnorDD() { + if(iv_erases) + { + delete iv_erases; + } +} + +bool PnorDD::cv_sfcInitDone = false; //Static flag to ensure we only init the SFC one time. +uint32_t PnorDD::cv_nor_chipid = 0; //Detected NOR Flash Type + +/** + * STATIC + * @brief Static Initializer + */ +void PnorDD::sfcInit( ) +{ + TRACDCOMP(g_trac_pnor, "PnorDD::sfcInit> iv_mode=0x%.8x", iv_mode ); + errlHndl_t l_err = NULL; + + //Initial configuration settings for SFC: + #define oadrnb_init 0x04000000 //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 { + mutex_lock(&cv_mutex); + + if(!cv_sfcInitDone) + { + 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; } + + cv_sfcInitDone = true; + + //Determine NOR Flash type, configure SFC and PNOR DD as needed + l_err = getNORChipId(cv_nor_chipid); + TRACFCOMP(g_trac_pnor, "PnorDD::sfcInit: cv_nor_chipid=0x%.8x> ", cv_nor_chipid ); + + //TODO: Need to add support for VPO (RTC: 42325), Spansion NOR (RTC: 42326), + // Micron NOR (RTC: 42328), Macronix (RTC: 42330), and Simics (RTC: 42331) + // There will probably be some overlap between those stories, but keeping them + // all separate for now to ensure everything is covered. + if(SIMICS_NOR_ID == cv_nor_chipid) + { + TRACFCOMP(g_trac_pnor, "PnorDD::sfcInit: Configuring SFC for SIMICS NOR> " ); + uint32_t sm_erase_op = SPI_SIM_SM_ERASE_OP; + iv_erasesize_bytes = SPI_SIM_SM_ERASE_SZ; + + /*Simics model doesn't currently support this*/ + l_err = writeRegSfc(SFC_CMD_SPACE, + SFC_REG_CONF4, + sm_erase_op); + if(l_err) { break; } + + + l_err = writeRegSfc(SFC_CMD_SPACE, + SFC_REG_CONF5, + iv_erasesize_bytes); + if(l_err) { break; } + + //create array to count erases. + iv_erases = new uint8_t[PNORSIZE/iv_erasesize_bytes]; + for( uint64_t x=0; x < (PNORSIZE/iv_erasesize_bytes); x++ ) + { + iv_erases[x] = 0; + } + + if(l_err) { break; } + + + } + else + { + TRACFCOMP( g_trac_pnor, "PnorDD::sfcInit> Unsupported NOR type detected : cv_nor_chipid=%d", cv_nor_chipid ); + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORDD_SFCINIT + * @reasoncode PNOR::RC_UNSUPORTED_HARDWARE + * @userdata1 NOR Flash Chip ID + * @userdata2 <not used> + * @devdesc PnorDD::sfcInit> + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_SFCINIT, + PNOR::RC_UNSUPORTED_HARDWARE, + TO_UINT64(cv_nor_chipid)); + + //Set chip ID back to zero to avoid later chip specific logic. + cv_nor_chipid = 0; + } + + } + + }while(0); + + mutex_unlock(&cv_mutex); + + if( l_err ) + { + errlCommit(l_err,PNOR_COMP_ID); + } + - //Nothing to do for now } /** @@ -392,125 +596,412 @@ errlHndl_t PnorDD::verifyFlashAddressRange(uint64_t i_address, } /** - * @brief Read a LPC Host Controller Register + * @brief Write a SFC Register */ -errlHndl_t PnorDD::readRegLPC(LpcRegAddr i_addr, - uint32_t& o_data) +errlHndl_t PnorDD::writeRegSfc(SfcRange i_range, + uint32_t i_addr, + uint32_t i_data) { errlHndl_t l_err = NULL; + uint32_t lpc_addr; - // add the offset into the LPC register space - uint32_t lpc_addr = i_addr + LPCHC_REG_SPACE; + if(SFC_CMD_SPACE == i_range) + { + lpc_addr = LPC_SFC_CMDREG_OFFSET | i_addr; + } else { + lpc_addr = LPC_SFC_CMDBUF_OFFSET | i_addr; + } + + 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); - // call the generic LPC function - l_err = readLPC( lpc_addr, o_data ); return l_err; } /** - * @brief Write a LPC Host Controller Register + * @brief Read a SFC Register */ -errlHndl_t PnorDD::writeRegLPC(LpcRegAddr i_addr, - uint32_t i_data) +errlHndl_t PnorDD::readRegSfc(SfcRange i_range, + uint32_t i_addr, + uint32_t& o_data) { errlHndl_t l_err = NULL; + uint32_t lpc_addr; + + if(SFC_CMD_SPACE == i_range) + { + lpc_addr = LPC_SFC_CMDREG_OFFSET | i_addr; + } else { + lpc_addr = LPC_SFC_CMDBUF_OFFSET | i_addr; + } - // add the offset into the LPC register space - uint32_t lpc_addr = i_addr + LPCHC_REG_SPACE; + l_err = readLPC(lpc_addr, o_data); + TRACDCOMP( g_trac_pnor, "PnorDD::readRegSfc> lpc_addr=0x%.8x, o_data=0x%.8x", + lpc_addr, o_data ); - // call the generic LPC function - l_err = writeLPC( lpc_addr, i_data ); return l_err; } + /** - * @brief Read a SPI Register + * @brief Poll for SFC Op Complete */ -errlHndl_t PnorDD::readRegSPI(SpiRegAddr i_addr, - uint32_t& o_data) +errlHndl_t PnorDD::pollSfcOpComplete(uint64_t i_pollTime) { - //@todo (RTC:35728) - SPI Support - 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); - - /* Anything more than this?? - - // add the offset into the LPC register space - uint32_t lpc_addr = i_addr + LPC_SPI_REG_OFFSET; - - // call the generic LPC function - l_err = readLPC( lpc_addr, o_data ); - - */ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "PnorDD::pollSfcOpComplete> i_pollTime=0x%.8x", + i_pollTime ); + + 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 ) + { + 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 + nanosleep( 0, SFC_POLL_INCR_NS*(++loop) ); + poll_time += SFC_POLL_INCR_NS*loop; + } + if( l_err ) { break; } + + // check for errors or timeout + // TODO: What errors do we check? + if( (sfc_stat.done == 0) ) + { + TRACFCOMP(g_trac_pnor, "PnorDD::pollSfcOpComplete> Error or timeout from LPC Status Register" ); + + /*@ + * @errortype + * @moduleid PNOR::MOD_PNORDD_POLLSFCOPCOMPLETE + * @reasoncode PNOR::RC_LPC_ERROR + * @userdata1[0:31] NOR Flash Chip ID + * @userdata1[32:63] Total poll time (ns) + * @userdata2[0:31] ECCB Status Register + * @devdesc PnorDD::readLPC> Error or timeout from + * LPC Status Register + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_POLLSFCOPCOMPLETE, + PNOR::RC_LPC_ERROR, + TWO_UINT32_TO_UINT64(cv_nor_chipid,poll_time), + TWO_UINT32_TO_UINT64(sfc_stat.data32,0)); + + l_err->collectTrace("PNOR"); + l_err->collectTrace("XSCOM"); + //@todo (RTC:37744) - Any cleanup or recovery needed? + break; + } + + + }while(0); + + return l_err; + } /** - * @brief Write a SPI Register + * @brief Read the NOR FLash ChipID */ -errlHndl_t PnorDD::writeRegSPI(SpiRegAddr i_addr, - uint32_t i_data) +errlHndl_t PnorDD::getNORChipId(uint32_t& o_chipId, + uint32_t i_spiOpcode) { - //@todo (RTC:35728) - SPI Support - 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); - - /* Anything more than this?? - - // add the offset into the LPC register space - uint32_t lpc_addr = i_addr + LPC_SPI_REG_OFFSET; - - // call the generic LPC function - l_err = writeLPC( lpc_addr, i_data ); - - */ - + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "PnorDD::getNORChipId> i_spiOpcode=0x%.8x", + i_spiOpcode ); + + do { + + //Issue Get Chip ID command + SfcCmdReg_t sfc_cmd; + sfc_cmd.opcode = i_spiOpcode; + 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; + } + + }while(0); + + return l_err; + } /** - * @brief Read an address from LPC space + * @brief Load SFC command buffer with data from PNOR */ -errlHndl_t PnorDD::readLPC(uint32_t i_addr, - uint32_t& o_data) +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; - bool need_unlock = false; + 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; } + + }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) +{ + errlHndl_t l_err = NULL; + TRACDCOMP( g_trac_pnor, "PnorDD::bufferedSfcRead> i_addr=0x%.8x, i_size=0x%.8x", + i_addr, i_size ); + + do{ + if( MODEL_LPC_MEM == iv_mode ) { - read_fake_pnor( i_addr - LPCHC_FW_SPACE, - static_cast<void*>(&o_data), - sizeof(uint32_t) ); + read_fake_pnor( i_addr, + o_data, + i_size ); break; } - // Note: If we got here then iv_mode is - // either MODEL_FLAT_ECCB or MODEL_REAL + // Note: If we got here then iv_mode is 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; + } + }while(0); + + 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; + + do{ + if( MODEL_LPC_MEM == iv_mode ) + { + write_fake_pnor( i_addr, + i_data, + i_size ); + break; + } + + // Note: If we got here then iv_mode is 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; + } + + //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; + } + }while(0); + + 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<uint32_t*>(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_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<uint32_t*>(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) +{ + errlHndl_t l_err = NULL; + + do { //@todo (RTC:36950) - add non-master support TARGETING::Target* scom_target = @@ -519,10 +1010,6 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr, // 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; @@ -588,21 +1075,12 @@ errlHndl_t PnorDD::readLPC(uint32_t i_addr, } - // atomic section << - mutex_unlock(&iv_mutex); - need_unlock = false; // copy data out to caller's buffer o_data = eccb_stat.read_data; } while(0); - if( need_unlock ) - { - mutex_unlock(&iv_mutex); - need_unlock = false; - } - return l_err; } @@ -613,21 +1091,10 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr, uint32_t i_data) { errlHndl_t l_err = NULL; - bool need_unlock = false; - //TRACFCOMP(g_trac_pnor, "writeLPC> %.8X = %.8X", i_addr, i_data ); + TRACDCOMP(g_trac_pnor, "writeLPC> %.8X = %.8X", i_addr, i_data ); do { - if( MODEL_LPC_MEM == iv_mode ) - { - write_fake_pnor( i_addr - LPCHC_FW_SPACE, - static_cast<void*>(&i_data), - sizeof(uint32_t) ); - break; - } - - // Note: If we got here then iv_mode is - // either MODEL_FLAT_ECCB or MODEL_REAL //@todo (RTC:36950) - add non-master support TARGETING::Target* scom_target = @@ -636,11 +1103,9 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr, // 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 + TRACDCOMP(g_trac_pnor, "writeLPC> Write ECCB data register"); + uint64_t eccb_data = static_cast<uint64_t>(i_data); eccb_data = eccb_data << 32; //left-justify my data l_err = deviceOp( DeviceFW::WRITE, @@ -651,6 +1116,7 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr, if( l_err ) { break; } // write command register with LPC address to write + TRACDCOMP(g_trac_pnor, "writeLPC> Write ECCB command register, cmd=0x%.16x", eccb_cmd.data64 ); ControlReg_t eccb_cmd; eccb_cmd.read_op = 0; eccb_cmd.address = i_addr; @@ -661,6 +1127,7 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr, DEVICE_SCOM_ADDRESS(ECCB_CTL_REG) ); if( l_err ) { break; } + // poll for complete StatusReg_t eccb_stat; uint64_t poll_time = 0; @@ -672,6 +1139,8 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr, &(eccb_stat.data64), scom_size, DEVICE_SCOM_ADDRESS(ECCB_STAT_REG) ); + TRACDCOMP(g_trac_pnor, "writeLPC> Poll on ECCB Status, poll_time=0x%.16x, stat=0x%.16x", eccb_stat.data64, poll_time ); + if( l_err ) { break; } if( eccb_stat.op_done == 1 ) @@ -713,208 +1182,160 @@ errlHndl_t PnorDD::writeLPC(uint32_t i_addr, break; } - // atomic section << - mutex_unlock(&iv_mutex); - need_unlock = false; } 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) +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,%d,%p)", i_targetAddr, i_wordsToWrite, 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 - typedef struct - { - uint32_t data; - bool wasRead; - bool diff; - } readflag_t; - readflag_t* read_data = NULL; + // remember any data we read so we don't have to reread it later + read_data = new uint8_t[iv_erasesize_bytes]; - do { - // skip the erase block logic if we're in a memcpy mode - if( (MODEL_MEMCPY == iv_mode) || (MODEL_LPC_MEM == iv_mode) ) + // 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++) { - // 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) ) + // look for any bits being changed (using XOR) + if(read_start[cword] ^ i_dataWord[cword] ) { - // flash is mapped directly in the FW space - l_err = writeLPC( addr + LPCHC_FW_SPACE, - i_data[words_written] ); - if( l_err ) { break; } + need_write = true; - words_written++; - } - if( l_err ) { break; } + //Can only write zeros to NOR, see if any bits changed from 0->1 + if( (~(read_start[cword])) & (i_dataWord[cword]) ) + { + need_erase = true; - // all done - break; + // skip comparing the rest of the block, + // just start writing it + break; + } + } } - - // 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++ ) + if(need_write == false) { - read_data[x].wasRead = false; - read_data[x].diff = false; + //No write actually needed, break out here + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> NO Write Needed! Exiting FUnction"); + break; } - // 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++ ) + //STEP 3: If the need to erase was detected, read out the rest of the Erase block + if(need_erase) { - // 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 ) + TRACDCOMP(g_trac_pnor,"compareAndWriteBlock> Need to perform Erase"); + //Get data before write section + if(i_writeStart > i_blockStart) { - continue; + 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); } - // 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)) ) + + //Get data after write section + if((i_writeStart+i_bytesToWrite) < (i_blockStart + iv_erasesize_bytes)) { - // done looking now - break; + 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; } } - // otherwise we need to compare our data with what is in flash now - else - { - l_err = readLPC( block_addr + bword*sizeof(uint32_t) - + LPCHC_FW_SPACE, - read_data[bword].data ); - if( l_err ) { break; } - read_data[bword].wasRead = true; + // 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; } - // 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 + //STEP 4: Write the data back out - need to write everything since we erased the block - // 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]; - - // skip comparing the rest of the block, - // just start writing it - break; - } - - // push the user data into the read buffer - // to get written later - read_data[bword].data = i_data[dword]; - } + //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; } - // 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++ ) + //Write data after new data to write + if((i_writeStart+i_bytesToWrite) < (i_blockStart + iv_erasesize_bytes)) { - // 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) - + LPCHC_FW_SPACE, - 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) - + LPCHC_FW_SPACE, - read_data[bword].data ); - if( l_err ) { break; } - } - // otherwise we will use the write data directly - else - { - read_data[bword].data = i_data[dword]; - } + 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; } } - if( l_err ) { break; } - // erase the flash - l_err = eraseFlash( block_addr ); + //Write the new data - always do this + l_err = bufferedSfcWrite(i_writeStart, + i_bytesToWrite, + i_data); 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++ ) + else // { - // only write what we have to - if( !(read_data[bword_written].diff) ) + //STEP 4 ALT: No erase needed, only write the parts that changed. + + for(uint32_t cword = 0; cword < wordsToWrite; cword++) { - continue; - } + // 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; } - // write the word out to the flash - l_err = writeLPC( block_addr + bword_written*sizeof(uint32_t) - + LPCHC_FW_SPACE, - read_data[bword_written].data ); - if( l_err ) { break; } - //@todo (RTC:37744) - How should we handle PNOR errors? + } } - if( l_err ) { break; } } while(0); @@ -923,6 +1344,9 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_targetAddr, delete[] read_data; } + TRACDCOMP(g_trac_pnor,"<<compareAndWriteBlock() Exit"); + + return l_err; } @@ -932,6 +1356,7 @@ errlHndl_t PnorDD::compareAndWriteBlock(uint32_t i_targetAddr, errlHndl_t PnorDD::eraseFlash(uint32_t i_address) { errlHndl_t l_err = NULL; + TRACDCOMP(g_trac_pnor, ">>PnorDD::eraseFlash> Block 0x%.8X", i_address ); do { if( findEraseBlock(i_address) != i_address ) @@ -952,30 +1377,55 @@ errlHndl_t PnorDD::eraseFlash(uint32_t 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] ); + if(iv_erases) + { + // log the erase of this block + iv_erases[i_address/iv_erasesize_bytes]++; + TRACFCOMP(g_trac_pnor, "PnorDD::eraseFlash> Block 0x%.8X has %d erasures", i_address, iv_erases[i_address/iv_erasesize_bytes] ); + } - if( MODEL_REAL != iv_mode ) + if( (MODEL_MEMCPY == iv_mode) || (MODEL_LPC_MEM == iv_mode)) { - erase_fake_pnor( i_address, ERASESIZE_BYTES ); + erase_fake_pnor( i_address, iv_erasesize_bytes ); break; //all done } - //@todo (RTC:35728) - issue some LPC/SPI commands to erase the block - /*@ - * @errortype - * @moduleid PNOR::MOD_PNORDD_ERASEFLASH - * @reasoncode PNOR::RC_UNSUPPORTED_OPERATION - * @userdata1 Model mode - * @userdata2 LPC Address to erase - * @devdesc PnorDD::eraseFlash> No support for MODEL_REAL yet - */ - l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, - PNOR::MOD_PNORDD_ERASEFLASH, - PNOR::RC_UNSUPPORTED_OPERATION, - static_cast<uint64_t>(iv_mode), - i_address); + if(cv_nor_chipid != 0) + { + TRACDCOMP(g_trac_pnor, "PnorDD::eraseFlash> Erasing flash for cv_nor_chipid=0x%.8x, iv_mode=0x%.8x", cv_nor_chipid, iv_mode); + //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; } + + } + else + { + TRACFCOMP(g_trac_pnor, "PnorDD::eraseFlash> Erase not supported for cv_nor_chipid=%d", cv_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 yet + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + PNOR::MOD_PNORDD_ERASEFLASH, + PNOR::RC_UNSUPPORTED_OPERATION, + static_cast<uint64_t>(cv_nor_chipid), + i_address); + } } while(0); return l_err; diff --git a/src/usr/pnor/pnordd.H b/src/usr/pnor/pnordd.H index 0ce538b4d..ed88695af 100644 --- a/src/usr/pnor/pnordd.H +++ b/src/usr/pnor/pnordd.H @@ -38,6 +38,13 @@ class PnorDD public: /** + * @brief Initializer called by PnorRP::init() to init the SFC + * + * @return void + */ + void sfcInit( ); + + /** * @brief Performs a PNOR Read Operation * * @parm o_buffer Buffer to read data into @@ -65,14 +72,12 @@ class PnorDD size_t& io_buflen, uint64_t i_address); - - protected: enum PnorMode_t { - MODEL_UNKNOWN, //Invalid - MODEL_MEMCPY, //No LPC logic, just do memcpy into cache area - MODEL_LPC_MEM, //Break into 32-bit LPC ops but use memcpy into cache area - MODEL_FLAT_ECCB, //Use ECCB scoms to drive LPC, flat memory map behind ECCB, no SPI - MODEL_REAL, //Code for real hardware or complete sim model + MODEL_UNKNOWN, /**< Invalid */ + MODEL_MEMCPY, /**< No LPC logic, just do memcpy into cache area */ + MODEL_LPC_MEM, /**< Break into 32-bit LPC ops but use memcpy into cache area */ + MODEL_REAL_CMD, /**< Code for real hardware or complete sim model using Commands based reads */ + MODEL_REAL_MMIO, /**< Code for real hardware or complete sim model using MMIO reads-Not currently implemented */ }; /** @@ -86,6 +91,9 @@ class PnorDD */ ~PnorDD(); + protected: + + /** * @brief Verify flash request is in appropriate address range * @@ -109,84 +117,232 @@ class PnorDD }; /** - * @brief Read a LPC Host Controller Register + * @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_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 * - * @parm i_addr Register address, relative to the - * LPC Host Controller Register Space - * @parm o_data Buffer to read data into + */ + enum NorChipIDs + { + SIMICS_NOR_ID = 0x20ba2000, /**< CHIP ID returned by Simics NOR chip */ + }; + + + enum SfcRange { + SFC_CMD_SPACE, /**< Indicate accessing command reg */ + SFC_CMDBUF_SPACE, /**< Indicate accessing command buffer 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_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_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 */ + + + }; + + /** + * @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 */ + }; + + + enum { + SFC_CMDBUF_SIZE = 256, /**< SFC Command buffer is 0x100/256 bytes/0x40 words */ + + SFC_POLL_TIME_NS = 400000, /**< todo: Find out Max time to wait */ + SFC_POLL_INCR_NS = 10, /**< minimum increment during poll */ + + }; + + /** + * @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 ReadRaw/WriteRaw */ + }; + SfcCmdReg_t() : data32(0) {}; + }; + + /** + * @brief SFC Status Register Layout + */ + union SfcStatReg_t + { + uint32_t data32; + struct + { + uint32_t unused : 30; /**< 0:29 = Not Currently Used */ + uint32_t timeout : 1; /**< 30 = Timeout */ + uint32_t done : 1; /**< 31 = Done */ + }; + SfcStatReg_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 readRegLPC(LpcRegAddr i_addr, + 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 Write a LPC Host Controller Register + * @brief Poll for SFC Op Complete * - * @parm i_addr Register address, relative to the - * LPC Host Controller Register Space - * @parm o_data Data to write + * @parm i_pollTime Amount of time to Poll, default SFC_POLL_TIME_NS * * @return Error from operation */ - errlHndl_t writeRegLPC(LpcRegAddr i_addr, - uint32_t i_data); + errlHndl_t pollSfcOpComplete(uint64_t i_pollTime = SFC_POLL_TIME_NS); + /** - * @brief SPI Registers - * These are offsets within the SPI Register Space + * @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 */ - enum SpiRegAddr { - SPI_REG_CMD = 0x40, /**< CMD : Command */ - SPI_REG_ADR = 0x44, /**< ADR : Address */ - SPI_REG_ERASMS = 0x48, /**< ERASMS : Small Erase Block Size */ - SPI_REG_ERASLGS = 0x4C, /**< ERALGS : Large Erase Block Size */ - SPI_REG_ADRCBF = 0x80, /**< ADRCBF : First Intf NOR Addr Offset */ - SPI_REG_ADRCMF = 0x84, /**< ADRCMF : First Intf NOR Allocation */ - SPI_REG_ADRCBS = 0x88, /**< ADRCBS : Second Intf NOR Addr Offset */ - SPI_REG_ADRCMS = 0x8C, /**< ADRCMS : Second Intf NOR Allocation */ - }; + errlHndl_t getNORChipId(uint32_t& o_chipId, + uint32_t i_spiOpcode = SPI_GET_CHIPID_OP); /** - * @brief SPI Op Codes - * OP Codes for the SPI Command Register - */ - enum SpiOpCodes { - SPI_OP_READRAW = 0x03, /**< Read Raw */ - SPI_OP_WRITERAW = 0x02, /**< Write Raw */ - SPI_OP_ERASM = 0x32, /**< Erase Small */ - SPI_OP_ERALG = 0x34, /**< Erase Large */ - SPI_OP_ENWRITPROT = 0x53, /**< Enable WRite Protect */ - SPI_OP_CHIPID = 0x1F, /**< Get Chip ID */ - SPI_OP_STATUS = 0x05, /**< Get Status */ - SPI_OP_TURNOFF = 0x5E, /**< Turn Off */ - SPI_OP_TURNON = 0x50, /**< Turn On */ - SPI_OP_ABORT = 0x6F, /**< Super-Abort */ - SPI_OP_START4BA = 0x37, /**< Start 4BA */ - SPI_OP_END4BA = 0x69, /**< End 4BA */ - }; + * @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 a SPI Register + * @brief Read data in SFC Command buffer and put into buffer * - * @parm i_addr Register address, relative to the SPI engine + * @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 readRegSPI(SpiRegAddr i_addr, - uint32_t& o_data); + errlHndl_t readSfcBuffer(size_t i_size, + void* o_data); /** - * @brief Write a SPI Register + * @brief Write data to SFC Command buffer * - * @parm i_addr Register address, relative to the SPI engine - * @parm o_data Data to write + * @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 writeRegSPI(SpiRegAddr i_addr, - uint32_t i_data); + 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); /** @@ -200,15 +356,13 @@ class PnorDD LPCHC_REG_SPACE = 0xC0012000, /**< LPC Host Ctlr Register Space */ LPC_DIRECT_READ_OFFSET = 0xFC000000, - LPC_SPI_REG_OFFSET = 0xF0000C00, + LPC_SFC_CMDREG_OFFSET = 0xF0000C00, /** LPC Offest to SFC Command Registers */ + LPC_SFC_CMDBUF_OFFSET = 0xF0000D00, /** LPC Offest to SFC Command Buffer space */ LPC_TOP_OF_FLASH_OFFSET = 0xFFFFFFFF, ECCB_CTL_REG = 0x000B0020, /**< ECCB Control Reg (FW) */ ECCB_STAT_REG = 0x000B0022, /**< ECCB Status Reg (FW) */ ECCB_DATA_REG = 0x000B0023, /**< ECCB Data Reg (FW) */ - //ECCB_CTL_REG = 0x00090020, /**< ECCB Control Reg (FW) */ - //ECCB_STAT_REG = 0x00090022, /**< ECCB Status Reg (FW) */ - //ECCB_DATA_REG = 0x00090023, /**< ECCB Data Reg (FW) */ // Default Values to set for all operations // 1101.0100.0000.000x.0000.0001.0000.0000.<address> @@ -217,9 +371,7 @@ class PnorDD LPC_STAT_REG_ERROR_MASK = 0xFC0000000007F700, /**< Error Bits */ PNORSIZE = 4 * 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) */ - + ERASESIZE_BYTES_DEFAULT = 4 * KILOBYTE, /**< Minimum Erase Block (bytes) */ ECCB_POLL_TIME_NS = 400000, /**< max time from Manfred Walz is 400ms */ ECCB_POLL_INCR_NS = 10, /**< minimum increment during poll */ }; @@ -260,15 +412,17 @@ class PnorDD * @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 flash address to write - * @parm i_wordsToWrite Number of 32-bit words to write + * @parm i_blockStart Start of Erase Block we're writing to + * @parm i_writeStart Starting address where we want to write data. + * @parm i_bytesToWrite 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); + errlHndl_t compareAndWriteBlock(uint32_t i_blockStart, + uint32_t i_writeStart, + size_t i_bytesToWrite, + void* i_data); /** * @brief Determine the nearest flash address aligned to an erase block @@ -279,7 +433,7 @@ class PnorDD */ uint32_t findEraseBlock(uint32_t i_address) { - return (i_address - i_address%ERASESIZE_BYTES); + return (i_address - i_address%iv_erasesize_bytes); }; /** @@ -299,7 +453,7 @@ class PnorDD while( findEraseBlock(addr) < (i_address+i_byteSize) ) { blocks++; - addr += ERASESIZE_BYTES; + addr += iv_erasesize_bytes; } return blocks; }; @@ -349,15 +503,17 @@ class PnorDD private: // Variables /** * @brief Mutex to prevent concurrent PNOR accesses + * This needs to be static so we can mutex across multiple instances of PnorDD */ - mutex_t iv_mutex; + static mutex_t cv_mutex; + //TODO: Make this dynamically sized. /** * @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]; + uint8_t* iv_erases; /** * @brief Determine how much of the PNOR logic to use, @@ -366,6 +522,25 @@ class PnorDD */ PnorMode_t iv_mode; + /** + * @brief describes the erase block size, set based on NOR chip type + * + */ + uint32_t iv_erasesize_bytes; + + /** + * @brief CHIP ID or the NOR chip attached to SFC. + * + */ + static uint32_t cv_nor_chipid; + + /** + * @brief indicates if SFC initialization has been performed. + * + */ + static bool cv_sfcInitDone; + + // Needed for testcases friend class PnorDdTest; }; diff --git a/src/usr/pnor/test/pnorddtest.H b/src/usr/pnor/test/pnorddtest.H index 29456b9d0..5a7c88b07 100644 --- a/src/usr/pnor/test/pnorddtest.H +++ b/src/usr/pnor/test/pnorddtest.H @@ -41,8 +41,6 @@ #include <list> #include <targeting/common/attributes.H> -//$$ #define BASE_SCRATCH_SPACE (3*1024*1024+1024*512) //3.5MB offset in fake PNOR -//#define BASE_SCRATCH_SPACE (3*1024*1024) //3.0MB offset in fake PNOR #define BASE_SCRATCH_SPACE (0x360000) // 3.5MB - 128K offset in fake PNOR extern trace_desc_t* g_trac_pnor; @@ -67,7 +65,7 @@ class PnorDdTest : public CxxTest::TestSuite { TARGETING::Target* l_testTarget = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; - size_t l_size = sizeof(uint64_t); + size_t l_size = sizeof(uint64_t); errlHndl_t l_err = NULL; uint64_t fails = 0; uint64_t total = 0; @@ -93,7 +91,7 @@ class PnorDdTest : public CxxTest::TestSuite total++; if(l_size != sizeof(uint64_t)) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 1: Write length not expected value. Addr: 0x%llx, Exp: %d, Act: %d", l_address, + 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++; } @@ -116,7 +114,7 @@ class PnorDdTest : public CxxTest::TestSuite total++; if(l_size != sizeof(uint64_t)) { - TS_FAIL("PnorDdTest::test_readwrite: PNORDD write 2: Write length not expected value. Addr: 0x%llx, Exp: %d, Act: %d", l_address, + 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++; } @@ -146,7 +144,7 @@ class PnorDdTest : public CxxTest::TestSuite total++; 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, + 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++; } @@ -175,7 +173,7 @@ class PnorDdTest : public CxxTest::TestSuite total++; 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, + 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++; } @@ -194,7 +192,7 @@ class PnorDdTest : public CxxTest::TestSuite { TARGETING::Target* l_testTarget = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; - size_t l_size = sizeof(uint64_t); + size_t l_size = sizeof(uint64_t); errlHndl_t l_err = NULL; uint64_t fails = 0; uint64_t total = 0; @@ -321,7 +319,7 @@ class PnorDdTest : public CxxTest::TestSuite { TARGETING::Target* l_testTarget = TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; - size_t l_size = sizeof(uint64_t); + size_t l_size = sizeof(uint64_t); errlHndl_t l_err = NULL; uint64_t fails = 0; uint64_t total = 0; @@ -329,7 +327,7 @@ class PnorDdTest : public CxxTest::TestSuite do{ TS_TRACE("PnorDdTest::test_crossblock: starting"); - // Find the nearest erase-block (4K) boundary + // Find the nearest erase-block (4K) boundary uint64_t l_boundary = (BASE_SCRATCH_SPACE+4096) - (BASE_SCRATCH_SPACE%4096); uint64_t l_address = 0; @@ -384,7 +382,7 @@ class PnorDdTest : public CxxTest::TestSuite void test_readwrite_modes(void) { PnorDD* pnordd = NULL; - size_t l_size = sizeof(uint64_t); + size_t l_size = sizeof(uint64_t); errlHndl_t l_err = NULL; uint64_t fails = 0; uint64_t total = 0; @@ -397,6 +395,22 @@ class PnorDdTest : public CxxTest::TestSuite supported_modes.push_back(PnorDD::MODEL_MEMCPY); supported_modes.push_back(PnorDD::MODEL_LPC_MEM); + //Hack to check if this is a VENICE config since Real PNOR doesn't work in simics right now. + //TODO: Remove this hack when simics is fixed. (RTC: 42625) + + TARGETING::EntityPath epath(TARGETING::EntityPath::PATH_PHYSICAL); + epath.addLast(TARGETING::TYPE_SYS,0); + epath.addLast(TARGETING::TYPE_NODE,0); + epath.addLast(TARGETING::TYPE_PROC,9); + TARGETING::Target* veniceProc = TARGETING::targetService().toTarget(epath); + + if((!TARGETING::is_vpo()) && + (veniceProc == NULL)) + { + TRACFCOMP(g_trac_pnor, "PnorDdTest::test_readwrite_modes> Adding REAL_CMD mode"); + supported_modes.push_back(PnorDD::MODEL_REAL_CMD); + } + uint64_t scratch_space = BASE_SCRATCH_SPACE; // loop through all of the supported modes @@ -536,7 +550,7 @@ class PnorDdTest : public CxxTest::TestSuite void test_smartwrite_modes(void) { PnorDD* pnordd = NULL; - size_t l_size = sizeof(uint64_t); + size_t l_size = sizeof(uint64_t); errlHndl_t l_err = NULL; uint64_t fails = 0; uint64_t total = 0; @@ -549,6 +563,22 @@ class PnorDdTest : public CxxTest::TestSuite supported_modes.push_back(PnorDD::MODEL_MEMCPY); supported_modes.push_back(PnorDD::MODEL_LPC_MEM); + //Hack to check if this is a VENICE config since Real PNOR doesn't work in simics right now. + //TODO: Remove this hack when simics is fixed. (RTC: 42625) + + TARGETING::EntityPath epath(TARGETING::EntityPath::PATH_PHYSICAL); + epath.addLast(TARGETING::TYPE_SYS,0); + epath.addLast(TARGETING::TYPE_NODE,0); + epath.addLast(TARGETING::TYPE_PROC,9); + TARGETING::Target* veniceProc = TARGETING::targetService().toTarget(epath); + + if((!TARGETING::is_vpo()) && + (veniceProc == NULL)) + { + TRACFCOMP(g_trac_pnor, "PnorDdTest::test_smartwrite_modes> Adding REAL_CMD mode"); + supported_modes.push_back(PnorDD::MODEL_REAL_CMD); + } + uint64_t scratch_space = BASE_SCRATCH_SPACE; // loop through all of the supported modes @@ -683,7 +713,7 @@ class PnorDdTest : public CxxTest::TestSuite void test_crossblock_modes(void) { PnorDD* pnordd = NULL; - size_t l_size = sizeof(uint64_t); + size_t l_size = sizeof(uint64_t); errlHndl_t l_err = NULL; uint64_t fails = 0; uint64_t total = 0; @@ -695,6 +725,21 @@ class PnorDdTest : public CxxTest::TestSuite std::list<PnorDD::PnorMode_t> supported_modes; supported_modes.push_back(PnorDD::MODEL_MEMCPY); supported_modes.push_back(PnorDD::MODEL_LPC_MEM); + //Hack to check if this is a VENICE config since Real PNOR doesn't work in simics right now. + //TODO: Remove this hack when simics is fixed. (RTC: 42625) + + TARGETING::EntityPath epath(TARGETING::EntityPath::PATH_PHYSICAL); + epath.addLast(TARGETING::TYPE_SYS,0); + epath.addLast(TARGETING::TYPE_NODE,0); + epath.addLast(TARGETING::TYPE_PROC,9); + TARGETING::Target* veniceProc = TARGETING::targetService().toTarget(epath); + + if((!TARGETING::is_vpo()) && + (veniceProc == NULL)) + { + TRACFCOMP(g_trac_pnor, "PnorDdTest::test_crossblock_modes> Adding REAL_CMD mode"); + supported_modes.push_back(PnorDD::MODEL_REAL_CMD); + } uint64_t scratch_space = BASE_SCRATCH_SPACE; @@ -712,7 +757,7 @@ class PnorDdTest : public CxxTest::TestSuite } pnordd = new PnorDD(*m); - // Find the nearest erase-block (4K) boundary + // Find the nearest erase-block (4K) boundary uint64_t l_boundary = (BASE_SCRATCH_SPACE+4096) - (BASE_SCRATCH_SPACE%4096); uint64_t l_address = 0; @@ -772,7 +817,7 @@ Leaving it commented out because the test-case will not dynamically find the ext { TARGETING::Target* l_testTarget = MASTER_PROCESSOR_CHIP_TARGET_SENTINEL; - size_t l_size = sizeof(uint64_t); + size_t l_size = sizeof(uint64_t); errlHndl_t l_err = NULL; do{ |