blob: 91e72ab4a5d1e54ad68cd05b9e1fd28d46011944 [file] [log] [blame]
/******************************************************************************
*
* (C)Copyright 2013 Marvell. All Rights Reserved.
*
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MARVELL.
* The copyright notice above does not evidence any actual or intended
* publication of such source code.
* This Module contains Proprietary Information of Marvell and should be
* treated as Confidential.
* The information in this file is provided for the exclusive use of the
* licensees of Marvell.
* Such users have the right to use, modify, and incorporate this code into
* products for purposes authorized by the license agreement provided they
* include this notice and the associated copyright notice with any such
* product.
* The information in this file is provided "AS IS" without warranty.
*
******************************************************************************/
#include "SPINAND.h"
#include "spi.h"
#include "Errors.h"
#include "misc.h"
#include "timer.h"
#include "PlatformConfig.h"
#include "platform_interrupts.h"
#include "xllp_dmac.h"
#define DEFAULT_TIMEOUT 3000
#define DEFAULT_WAIT_TIME 1000
#define WRITE_SIZE (256)
extern UINT_T MPUFlag;
//limited for timing and stack concerns (see SPI_page_write routine)
#define SSP_READ_TIME_OUT_MILLI 0x2000
#define SSP_READ_DMA_DESC 0x10 //total RX descriptors
#define SSP_READ_DMA_SIZE 0x1ff8 //bytes per descriptor
#define SSP_MAX_TX_SIZE_WORDS 64
#define SSP_MAX_TX_SIZE_BYTES SSP_MAX_TX_SIZE_WORDS << 2
__attribute__ ((aligned(32))) XLLP_DMAC_DESCRIPTOR_T pRX_data, pTX_cmd;
__attribute__ ((aligned(32)))unsigned int tx_command[2112] = {0};
/***********************************************************
* InitializeSPINAND()
* Initializes the SSP port on the platform and issues
* a Read ID command to see if a device is connected
* returns:
* NoError - on a successful read ID
* NotFoundError - when read ID value is bogus (0xFFFF or 0x0000)
************************************************************/
UINT_T InitializeSPINAND(UINT8_T FlashNum, UINT8_T* P_DefaultPartitionNum)
{
unsigned char mid, did, sn_feature;
P_FlashProperties_T pFlashP = GetFlashProperties(BOOT_FLASH);
int retval;
UINT_T ID;
ChipSelectSPINAND();
SPI_ConfigCS();
SPINAND_Reset(BOOT_FLASH);
retval = SPINAND_CheckReady();
if(retval != NoError)
{
obm_printf("SPINAND_Reset error\n\r");
}
SPINAND_ReadID(&mid, &did);
obm_printf("Manufactory and Device ID: 0x%08x\n\r", (mid<<8)|(did));
SPINAND_UnProtectBlocks();
sn_feature = SPINAND_GetFeatures(SN_FEATURE);
obm_printf("SN_FEATURE: 0x%x\n\r", sn_feature);
if ((sn_feature & GIGA_SPINAND_FEATURE_ECC_EN) == 0)
SPINAND_SetFeatures(SN_FEATURE, (sn_feature | GIGA_SPINAND_FEATURE_ECC_EN));
//define functions
pFlashP->ReadFromFlash = &SPINAND_Read;
pFlashP->WriteToFlash = &SPINAND_Write;
pFlashP->EraseFlash = &SPINAND_Erase;
pFlashP->ResetFlash = &SPINAND_Reset;
pFlashP->FlashSettings.UseBBM = 1;
pFlashP->FlashSettings.UseSpareArea = 0;
pFlashP->PageSize = 2048;
pFlashP->BlockSize = 128*1024;
pFlashP->FlashSettings.SASize = 0;
pFlashP->FlashSettings.UseHwEcc = 0;
pFlashP->StreamingFlash = FALSE;
pFlashP->FlashType = SPI_NAND;
pFlashP->FinalizeFlash = NULL;
pFlashP->TimFlashAddress = 0;
pFlashP->NumBlocks = 1020;
*P_DefaultPartitionNum = 0;
//fill tx dummy buffer with special pattern
memset(tx_command, 0xA5, sizeof(tx_command));
return NoError;
}
/***********************************************************
* SPINAND_ReadID
* Reads the 16 bit MID and DID
* Returns:
* None
*************************************************************/
void SPINAND_ReadID(unsigned char *mid, unsigned char *did)
{
unsigned char cmd[4], data[4];
cmd[0] = SN_READ_ID;
cmd[1] = 0;
cmd[2] = 0;
cmd[3] = 0;
SPI_DisableSSP();
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
Deassert_CS();
SPI_DisableSSP();
*mid = data[2];
*did = data[3];
return;
}
// SPI NAND READ: using DMA
unsigned int SPINAND_Read(unsigned int FlashOffset, unsigned int Buffer, unsigned int Size)
{
unsigned int retval = NoError, read_size;
unsigned char status;
//calculate the initial read size (for the first page)
read_size = SN_PAGE_SIZE - (FlashOffset & (SN_PAGE_SIZE - 1)); //whole page - pre_bytes
//for each loop iteration, one page of flash will be read into internal memory
while (Size > 0)
{ //update read_size if there are post_bytes (i.e. leftover for the last page)
read_size = read_size > Size ? Size : read_size;
/** Step 1: Cache Load: Load the correct page into cache **/
SPINAND_CacheLoad(FlashOffset);
/** Step 2: Get Features: wait for OIP to be cleared **/
retval = SPINAND_CheckReady();
if(retval != NoError)
break;
/** Step 3: Cache Read: Read the page in cache to memory (DMA) **/
retval = SPINAND_CacheRead_4Bytes(FlashOffset, Buffer, read_size);
if(retval != NoError)
break;
retval = SPINAND_CheckReady();
if(retval != NoError)
break;
/** Step 4: Update counters: make sure to adjust up to a page boundary each iteration **/
FlashOffset += read_size;
Buffer += read_size;
Size -= read_size;
read_size = SN_PAGE_SIZE;
}
return retval;
}
/***********************************************************
* SPINAND_CacheLoad
* Issues command to read a page of NAND into the cache
* Inputs:
* The flash address of the page to be loaded
* Returns:
* no returns
*************************************************************/
void SPINAND_CacheLoad(unsigned int Address)
{
SN_ADDRESS_FIELD_T addr;
unsigned int input;
//store the command into the highest byte
input = SN_CACHE_LOAD << 24;
//put the 16 bits of Row Address (RA) into the lowest two bytes
addr.value = Address;
input |= (addr.fields.RA & 0xFFFF);
SPI_DisableSSP();
//setup in 32 bit mode
SPI_ConfigDSS(32);
Assert_CS();
//fire it up
SPI_FireUp();
//load the input data
SPI_WriteData(input);
SPI_WaitSSPComplete();
SPI_DisableSSP();
Deassert_CS();
return;
}
/***********************************************************
* SPINAND_GetFeatures
* Reads the corresponding feature value of SPI device.
* Inputs:
* The feature type to be read out
* Returns:
* status value
*************************************************************/
unsigned char SPINAND_GetFeatures(SN_Feature_E feature_type)
{
UINT status, i;
UINT8 cmd[3], data[3];
//fill out the command (2 bytes) plus dummy bytes for the readback
cmd[0] = SN_GET_FEATURES;
cmd[1] = feature_type;
cmd[2] = 0;
//make sure SSP is disabled
SPI_DisableSSP();
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
//make sure SSP is disabled
SPI_DisableSSP();
Deassert_CS();
//return last known status
return data[2];
}
unsigned char SPINAND_SetFeatures(SN_Feature_E feature_type, unsigned char feature)
{
UINT i;
UINT8 cmd[3], data[3];;
//fill out the command (2 bytes) plus data byte
cmd[0] = SN_SET_FEATURES;
cmd[1] = feature_type;
cmd[2] = feature;
//make sure SSP is disabled
SPI_DisableSSP();
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
//make sure SSP is disabled
SPI_DisableSSP();
Deassert_CS();
return 0;
}
/***********************************************************
* SPINAND_CacheRead
* Reads the loaded page from Cache to internal memory using DMA
* Inputs:
* Offset into page
* Buffer location
* Size to be read
* Returns:
* error status
*************************************************************/
unsigned int SPINAND_CacheRead_4Bytes(unsigned int Offset, unsigned int Buffer, unsigned int Size)
{
unsigned int command, dummy, start_time, i, Retval = NoError;
SN_ADDRESS_FIELD_T addr;
DMA_CMDx_T RX_data, TX_cmd;
unsigned int timeout = 0xFFFFF;
unsigned int recv_buf = 0;
#ifndef HW_ENDIAN_CONVERT
unsigned int * convert_buff, * orginal_buff, convert_size;
recv_buf = malloc(Size);
if(recv_buf == 0)
return HeapExhaustedError;
#else
recv_buf = Buffer;
#endif
//fill out the "command" sequence: CMD + Column Address (CA) + dummy byte
addr.value = Offset;
command = SN_CACHE_READ << 24;
command |= (addr.fields.CA << 8); //(upper bits will be 0's for the 'wrap')
command |= 0xA5; //0xA5 is a dummy byte that easily seen on a scope
//fill out commands
RX_data.value = 0;
RX_data.bits.IncTrgAddr = 1;
RX_data.bits.FlowSrc = 1;
RX_data.bits.Width = 3;
RX_data.bits.MaxBurstSize = 1;
TX_cmd.value = 0;
TX_cmd.bits.IncSrcAddr = 1;
TX_cmd.bits.FlowTrg = 1;
TX_cmd.bits.Width = 3;
TX_cmd.bits.MaxBurstSize = 1;
//Map Device to Channels
XllpDmacMapDeviceToChannel(SSP_RX_DMA_DEVICE, SSP_RX_CHANNEL);
XllpDmacMapDeviceToChannel(SSP_TX_DMA_DEVICE, SSP_TX_CHANNEL);
//turn ON user alignment - in case buffer address is 64bit aligned
alignChannel(SSP_RX_CHANNEL, 1);
// * configure RX & TX descriptors * //
configDescriptor(&pRX_data, NULL, (unsigned int)SSP_DR, recv_buf, &RX_data, Size, 1);
configDescriptor(&pTX_cmd,
NULL,
(unsigned int)&tx_command[0],
(unsigned int)SSP_DR,
&TX_cmd,
Size,
1);
//Load descriptors
loadDescriptor (&pRX_data, SSP_RX_CHANNEL);
loadDescriptor (&pTX_cmd, SSP_TX_CHANNEL);
SPI_DisableSSP();
//setup in 32 bit mode
SPI_ConfigDSS(32);
//setup for DMA: TX and RX DMA request + DMA handles Trailing bytes
#ifdef HW_ENDIAN_CONVERT
SPI_ConfigDMA(4, 3, 2, 0, 1);
#else
SPI_ConfigDMA(4, 3, 0, 0, 0);
#endif
//setup IER
SPI_ConfigInt(0);
Assert_CS();
//fire it up
SPI_FireUp();
#if ENABLE_MMU
dcache_clean_range(&pRX_data, sizeof(XLLP_DMAC_DESCRIPTOR_T));
dcache_clean_range(&pTX_cmd, sizeof(XLLP_DMAC_DESCRIPTOR_T));
dcache_clean_range(&tx_command[0], sizeof(tx_command));
dcache_invalidate_range(recv_buf, Size);
#endif
SPI_WriteData(command);
SPI_WaitSSPComplete();
dummy = SPI_ReadData();
//Kick off DMA's
XllpDmacStartTransfer(SSP_TX_CHANNEL);
XllpDmacStartTransfer(SSP_RX_CHANNEL);
//wait until the RX channel gets the stop interrupt
timeout = 0xFFFFF;
while( (readDmaStatusRegister(SSP_RX_CHANNEL) & XLLP_DMAC_DCSR_STOP_INTR) != XLLP_DMAC_DCSR_STOP_INTR )
{
ROW_DELAY(DEFAULT_TIMEOUT);
if((timeout--) <= 0)
{
obm_printf("RX channel gets the stop interrupt timeout\n\r");
break;
}
}
timeout = 0xFFFFF;
while( (readDmaStatusRegister(SSP_TX_CHANNEL) & XLLP_DMAC_DCSR_STOP_INTR) != XLLP_DMAC_DCSR_STOP_INTR )
{
ROW_DELAY(DEFAULT_TIMEOUT);
if((timeout--) <= 0)
{
obm_printf("TX channel gets the stop interrupt timeout\n\r");
break;
}
}
SPI_WaitSSPComplete();
Deassert_CS();
SPI_DisableSSP();
//clear out DMA settings
XllpDmacUnMapDeviceToChannel(SSP_RX_DMA_DEVICE, SSP_RX_CHANNEL);
XllpDmacUnMapDeviceToChannel(SSP_TX_DMA_DEVICE, SSP_TX_CHANNEL);
#if LZMA_SUPPORT
//if (MPUFlag == 1) // only for boot
// CacheInvalidateMemory(Buffer, Size + 4);
#endif
#ifndef HW_ENDIAN_CONVERT
//postprocessing... endian convert
convert_buff = (unsigned int *) Buffer;
orginal_buff = (unsigned int *) recv_buf;
convert_size = Size >> 2; //convert Size from bytes to words
for(i = 0; i < convert_size; i++)
convert_buff[i] = Endian_Convert(orginal_buff[i]);
free(recv_buf);
#endif
return Retval;
}
// SPI NAND WRITE: using DMA
unsigned int SPINAND_Write(unsigned int FlashOffset, unsigned int Buffer, unsigned int Size)
{
unsigned int retval = NoError, write_size;
//calculate the initial write size (for the first page)
//write_size = SN_PAGE_SIZE - (FlashOffset & (SN_PAGE_SIZE - 1)); //whole page - pre_bytes
//unlock all blocks before programming any thing
SPINAND_SetFeatures(SN_PROTECTION, 0x00);
//for each loop iteration, one page of flash will be written
while (Size > 0)
{ //update write_size if there are post_bytes (i.e. leftover for the last page)
write_size = Size > SN_PAGE_SIZE ? SN_PAGE_SIZE : Size;
/** Step 0: Write Enable: set the write enable bit **/
SPINAND_WriteEnable();
/** Step 1: Program Load: Load the data into cache (DMA) **/
retval = SPINAND_ProgramLoad_4Bytes(FlashOffset, Buffer, write_size);
//retval = SPINAND_ProgramLoadPIO(FlashOffset, Buffer, write_size);
if(retval != NoError)
break;
retval = SPINAND_CheckReady();
if(retval != NoError)
break;
/** Step 2: Write Enable: set the write enable bit **/
SPINAND_WriteEnable();
/** Step 3: Program Execute: Write the page in cache to flash **/
SPINAND_ProgramExecute(FlashOffset);
/** Step 4: Get Features: wait for OIP to be cleared **/
retval = SPINAND_CheckReady();
if(retval != NoError)
break;
/** Step 5: Update counters: make sure to adjust up to a page boundary each iteration **/
FlashOffset += write_size;
Buffer += write_size;
Size -= write_size;
write_size = SN_PAGE_SIZE;
}
return retval;
}
unsigned int SPINAND_ProgramLoad_4Bytes(unsigned int Address, unsigned int Buffer, unsigned int Size)
{
unsigned int Retval = NoError, i;
DMA_CMDx_T TX_data;
unsigned char cmd[3], data[3];
unsigned int convert_size = Size;
volatile int timeout = 0xFFFFF;
cmd[0] = SN_PROGRAM_LOAD;
cmd[1] = 0;
cmd[2] = 0;
TX_data.value = 0;
TX_data.bits.IncSrcAddr = 1;
TX_data.bits.FlowTrg = 1;
TX_data.bits.Width = 1; //Don't need to convert Endian if transmit by byte
TX_data.bits.MaxBurstSize = 1;
TX_data.bits.Length = Size;
//Map Device to Channels
XllpDmacMapDeviceToChannel(SSP_TX_DMA_DEVICE, SSP_TX_CHANNEL);
XllpDmacNoDescriptorFetch( SSP_TX_CHANNEL );
//turn ON user alignment - in case buffer address is 64bit aligned
alignChannel(SSP_TX_CHANNEL, 1);
loadNonDescriptor((unsigned int)Buffer, (unsigned int) SSP_DR, &TX_data, SSP_TX_CHANNEL);
SPI_DisableSSP();
//setup for DMA: TX and RX DMA request + DMA handles Trailing bytes
SPI_ConfigDMA(4, 3, 0, 0, 0);
SPI_ConfigInt(0);
//setup IER
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
//Kick off DMA's
XllpDmacStartTransfer(SSP_TX_CHANNEL);
//wait until the TX channel gets the stop interrupt
timeout = 0xFFFFFF;
while( (readDmaStatusRegister(SSP_TX_CHANNEL) & XLLP_DMAC_DCSR_STOP_INTR) != XLLP_DMAC_DCSR_STOP_INTR )
{
ROW_DELAY(DEFAULT_TIMEOUT);
//if we've waited long enough, fail
if((timeout--) <= 0)
{
obm_printf("TX channel gets the stop interrupt timeout\n\r");
break;
}
}
SPI_WaitSSPComplete();
Deassert_CS();
SPI_DisableSSP();
//clear out DMA settings
XllpDmacUnMapDeviceToChannel(SSP_TX_DMA_DEVICE, SSP_TX_CHANNEL);
return Retval;
}
/***********************************************************
* SPINAND_ProgramLoad
* Writes a page of data to Cache from internal memory using DMA
* Inputs:
* Offset into page
* Buffer location
* Size to be write
* Returns:
* error status
*************************************************************/
unsigned int SPINAND_ProgramLoadPIO(unsigned int Address, unsigned int Buffer, unsigned int Size)
{
int i = 0;
unsigned int Retval = NoError;
unsigned char cmd[3], dummy, *data;
#if 0
P_FlashProperties_T pFlashP = GetFlashProperties(BOOT_FLASH);
//fill out the command (2 bytes) plus data byte
cmd[0] = SN_PROGRAM_LOAD;
cmd[1] = ((Address & (pFlashP->PageSize - 1)) >> 8) & 0xFF;
cmd[2] = Address & 0xFF;
#else
cmd[0] = SN_PROGRAM_LOAD;
cmd[1] = 0;
cmd[2] = 0;
#endif
//make sure SSP is disabled
SPI_DisableSSP();
//fire it up
SPI_FireUp();
Assert_CS();
for(i = 0; i < 3; i++)
{
*SSP_DR = cmd[i];
SPI_WaitSSPComplete();
dummy = *SSP_DR;
}
data = (UINT8 *)Buffer;
for(i = 0; i < Size; i++)
{
*SSP_DR = data[i];
SPI_WaitSSPComplete();
dummy = *SSP_DR;
}
Deassert_CS();
//make sure SSP is disabled
SPI_DisableSSP();
return Retval;
}
/***********************************************************
* SPINAND_WriteEnable
* Sets the WEL bit in the SPI NAND status register,
* allowing the device to be programmed or erased
* Returns:
* none
*************************************************************/
void SPINAND_WriteEnable(void)
{
unsigned char cmd[1], data[1];
cmd[0] = SN_WRITE_ENABLE;
SPI_DisableSSP();
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
Deassert_CS();
SPI_DisableSSP();
}
/***********************************************************
* SPINAND_ProgramExecute
* Issues command to program a page of NAND from the cache
* Inputs:
* The flash address of the page to be loaded
* Returns:
* no returns
*************************************************************/
void SPINAND_ProgramExecute(unsigned int Address)
{
unsigned char cmd[4], data[4];
cmd[0] = SN_PROGRAM_EXECUTE;
cmd[1] = ((Address>>11)&0xffff) >> 16;
cmd[2] = ((Address>>11)&0xffff) >> 8;
cmd[3] = ((Address>>11)&0xffff);
SPI_DisableSSP();
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
Deassert_CS();
SPI_DisableSSP();
return;
}
// SPI NAND ERASE: Erases 1 whole block at a time
unsigned int SPINAND_Erase(unsigned int FlashOffset, unsigned int Size)
{
unsigned int retval = NoError;
//adjust the size/offset fields to start on a block boundary
Size += FlashOffset & (SN_BLOCK_SIZE - 1);
FlashOffset &= ~(SN_BLOCK_SIZE - 1);
//unlock all blocks before erase
SPINAND_SetFeatures(SN_PROTECTION, 0x00);
//for each loop iteration, one block of flash will be erased
while (Size > 0)
{ /** Step 1: Write Enable: set the write enable bit **/
SPINAND_WriteEnable();
/** Step 2: Block Erase: erase 1 block **/
SPINAND_BlockErase(FlashOffset);
/** Step 3: Get Features: wait for OIP to be cleared **/
retval = SPINAND_CheckReady();
if(retval != NoError)
break;
/** Step 4: Update counters **/
FlashOffset += SN_BLOCK_SIZE;
Size = (Size > SN_BLOCK_SIZE) ? (Size - SN_BLOCK_SIZE) : 0;
}
return retval;
}
/***********************************************************
* SPINAND_BlockErase
* Issues command to erase 1 block of flash
* Inputs:
* The flash address of the block to be erased
* Returns:
* no returns
*************************************************************/
void SPINAND_BlockErase(unsigned int Address)
{
unsigned char cmd[4], data[4];
cmd[0] = SN_BLOCK_ERASE;
cmd[1] = ((Address>>11)&0xffff) >> 16;
cmd[2] = ((Address>>11)&0xffff) >> 8;
cmd[3] = ((Address>>11)&0xffff);
SPI_DisableSSP();
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
Deassert_CS();
SPI_DisableSSP();
return;
}
/***********************************************************
* SPINAND_Reset
* Issues command to reset flash
* Inputs:
* The flash boot type
* Returns:
* no error
*************************************************************/
unsigned int SPINAND_Reset(FlashBootType_T fbt)
{
unsigned char cmd[1], data[1];
cmd[0] = SN_RESET;
SPI_DisableSSP();
Assert_CS();
//fire it up
SPI_FireUp();
SPI_Write_Read(cmd, data, sizeof(cmd));
Deassert_CS();
SPI_DisableSSP();
return NoError;
}
/***********************************************************
* SPINAND_UnProtectBlocks
* unprotect the protected flash blocks
* Inputs:
* none
* Returns:
* none
*************************************************************/
void SPINAND_UnProtectBlocks(void)
{
unsigned char protection;
protection = SPINAND_GetFeatures(SN_PROTECTION);
obm_printf("SN_PROTECTION: 0x%x\n\r", protection);
if ((protection & (GIGA_SPINAND_PROTECTION_BP3 |
GIGA_SPINAND_PROTECTION_BP2 |
GIGA_SPINAND_PROTECTION_BP1 |
GIGA_SPINAND_PROTECTION_BP0)) == 0)
{
obm_printf("SPINAND is already unprotected\n\r");
return;
}
else
{
obm_printf("SPINAND is not unprotected\n\r");
}
// clear BP0/BP1/BP2
protection &= ~(GIGA_SPINAND_PROTECTION_BP3 |
GIGA_SPINAND_PROTECTION_BP2 |
GIGA_SPINAND_PROTECTION_BP1 |
GIGA_SPINAND_PROTECTION_BP0);
//SPINAND_SetFeatures(SN_PROTECTION, protection);
SPINAND_SetFeatures(SN_PROTECTION, 0);
obm_printf("SPINAND unprotect done\n\r");
}
UINT_T SPINAND_CheckReady(void)
{
unsigned int start_time, retval = NoError;
unsigned char status;
start_time = GetOSCR0();
do
{ //Read the Status register on the device
status = SPINAND_GetFeatures(SN_STATUS);
//Check for timeout
if( OSCR0IntervalInMilli(start_time, GetOSCR0()) > 5 )
{
obm_printf("SPINAND_CheckReady: SPINANDCheckReadyTimeOutError\n\r");
retval = SPINANDCheckReadyTimeOutError;
}
if (status & GIGA_SPINAND_STATUS_P_FAIL)
{
obm_printf("SPINAND_CheckReady: SPI_PROGRAMFAIL\n\r");
retval = SPI_PROGRAMFAIL;
}
if (status & GIGA_SPINAND_STATUS_E_FAIL)
{
obm_printf("SPINAND_CheckReady: SPI_ERASEFAIL\n\r");
retval = SPI_ERASEFAIL;
}
//stay in loop if OIP bit is still high AND no timeout error has occured
} while( (status & GIGA_SPINAND_STATUS_OIP) && (retval == NoError) );
return retval;
}