// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// ubootldr\flash_nor.cpp
//
//
#define FILE_ID 0x464C5348
#include "bootldr.h"
#include "ubootldrldd.h"
#include <e32std.h>
#include <e32std_private.h>
#include <e32svr.h>
#include <e32cons.h>
#include <f32file.h>
#include <hal.h>
#include <u32hal.h>
#include "flash_nor.h"
const TUint KFlashRetries = 1000000;
#ifdef __SUPPORT_FLASH_REPRO__
_LIT(KLitThreadName,"Flash");
TLinAddr FlashImageAddr;
TUint32 FlashImageSize;
TUint32 * FlashAddress;
volatile TUint32 Available;
volatile TBool Complete;
#define addr_to_page(a) (a&~(0x1000-1))
#define addr_pageoff(a) (a&(0x1000-1))
// Memory
RUBootldrLdd LddFlash;
RChunk TheFlashChunk;
TUint FlashId = FLASH_TYPE_UNKNOWN;
#define PRINTF(x)
#define SPANSION_PRINTF(x)
#define TYAX_PRINTF(x)
#define WRITE_PRINTF(x)
// Reset prototypes
TInt cfiReset (TUint32 flashId, TUint32 address);
TInt tyaxReset(TUint32 flashId, TUint32 address);
// Erase prototypes
TInt spansionErase(TUint32 flashId, TUint32 aBase, TUint32 anAddr, TUint32 aSize);
TInt tyaxErase (TUint32 flashId, TUint32 aBase, TUint32 anAddr, TUint32 aSize);
// Write prototypes
TInt spansionWrite(TUint32 flashId, TUint32 anAddr, TUint32 aSize, const TUint32* aPS);
TInt tyaxWrite (TUint32 flashId, TUint32 anAddr, TUint32 aSize, const TUint32* aPS);
///////////////////////////////////////////////////////////////////////////////
//
// FLASH INFO
//
// This table holds all the information we have about supported flash devices
//
///////////////////////////////////////////////////////////////////////////////
const TFlashInfo flashInfo [] =
{
// Description Manufacturer ID Device ID Reset fn Erase fn Write fn Comments
{_L(""), CFI_MANUF_ANY, CFI_DEV_ANY, cfiReset, NULL, NULL, }, // This is the catch-all entry in case we aren't initialised
// {_L("Spansion xyz"), CFI_MANUF_SPANSION, CFI_DEV_xyz, xyzReset, xyzErase, xyzWrite, }, // Put new Spansion flash types here, before the CFI_DEV_ANY, or they won't get detected
{_L("Spansion S29GL512N"), CFI_MANUF_SPANSION, CFI_DEV_S29GL512N, cfiReset, spansionErase, spansionWrite, }, // NaviEngine Rev B & C
{_L("Spansion Generic"), CFI_MANUF_SPANSION, CFI_DEV_ANY, cfiReset, spansionErase, spansionWrite, }, // Generic Spansion flash types
// {_L("Intel xyz"), CFI_MANUF_INTEL, CFI_DEV_xyz, xyzReset, xyzErase, xyzWrite, }, // Put new Intel flash types here, before the CFI_DEV_ANY, or they won't get detected
{_L("Intel Sibley"), CFI_MANUF_INTEL, CFI_DEV_SIBLEY, tyaxReset, tyaxErase, tyaxWrite, }, // H4 with Intel Tyax flash parts
{_L("Intel 28F256L18T"), CFI_MANUF_INTEL, CFI_DEV_28F256L18T, tyaxReset, tyaxErase, tyaxWrite, }, // H4 with Intel Tyax flash parts
{_L("Intel Tyax"), CFI_MANUF_INTEL, CFI_DEV_ANY, tyaxReset, tyaxErase, tyaxWrite, }, // Generic Intel Tyax flash support
// End Of Table - no more entries after here
{_L(""), 0, 0, NULL, NULL, NULL } // NULL entry used to mark end of table
};
///////////////////////////////////////////////////////////////////////////////
// CFI Commands
///////////////////////////////////////////////////////////////////////////////
// Query
const TCfiCommands CfiQuery [] =
{
{CFI_BASE8, 0xAAA, 0xAA},
{CFI_BASE8, 0x555, 0x55},
{CFI_BASE8, 0xAAA, 0x90},
{CFI_END, CFI_END, CFI_END} // Termination of command sequence - this entry is not a command
};
// Erase
const TCfiCommands CfiErase [] =
{
{CFI_BASE8, 0xAAA, 0xAA},
{CFI_BASE8, 0x555, 0x55},
{CFI_BASE8, 0xAAA, 0x80},
{CFI_BASE8, 0xAAA, 0xAA},
{CFI_BASE8, 0x555, 0x55},
{CFI_SECTOR8, 0x000, 0x30},
{CFI_END, CFI_END, CFI_END} // Termination of command sequence - this entry is not a command
};
// Write
const TCfiCommands CfiWrite [] =
{
{CFI_BASE8, 0xAAA, 0xAA},
{CFI_BASE8, 0x555, 0x55},
{CFI_BASE8, 0xAAA, 0xA0},
{CFI_END, CFI_END, CFI_END} // Termination of command sequence - this entry is not a command
};
///////////////////////////////////////////////////////////////////////////////
//
// CFI Command execution
//
// CFI implements a generic set of commands that can be used on all CFI flash
// parts.
//
// The commands usually write to the base address of the device + an offset,
// or to the sector/block address for some commands.
//
///////////////////////////////////////////////////////////////////////////////
TInt CfiCommand(TUint32 base, TUint32 sector, const TCfiCommands * commands)
{
if (commands != NULL)
{
const TCfiCommands * pCmd = commands;
while (pCmd->location != CFI_END)
{
switch (pCmd->location)
{
case CFI_BASE8:
{
*(volatile TUint8*)(base + pCmd->offset) = pCmd->command;
}
break;
case CFI_SECTOR8:
{
*(volatile TUint8*)(sector + pCmd->offset) = pCmd->command;
}
break;
default:
return KErrNotSupported;
}
pCmd++;
}
}
return KErrNone;
}
///////////////////////////////////////////////////////////////////////////////
//
// TYAX specific routines
//
///////////////////////////////////////////////////////////////////////////////
// Clear the status register
///////////////////////////////////////////////////////////////////////////////
void tyaxClearStatus(TUint32 address)
{
volatile TUint16 *p = (TUint16 *)address;
*p=KCmdClearStatus; // clear status reg
}
///////////////////////////////////////////////////////////////////////////////
// Wait until cmd completes
///////////////////////////////////////////////////////////////////////////////
void tyaxWaitUntilReady(TUint32 address, TUint16 cmd)
{
volatile TUint16 *pF = (TUint16 *)address;
TUint16 s=0;
TInt i=KFlashRetries;
for (; i>0 && ((s&KStatusBusy)!=KStatusBusy); --i) // check ready bit
{
*pF=cmd;
s=*pF;
}
if (i==0)
{
PrintToScreen(_L("Write timed out"));
BOOT_FAULT();
}
if (s&KStatusCmdSeqError)
{
PrintToScreen(_L("Write error s=%x pF=0x%x\n"), s, pF);
}
}
///////////////////////////////////////////////////////////////////////////////
// Unlock Flash
///////////////////////////////////////////////////////////////////////////////
void tyaxUnlock(TUint32 address)
{
TYAX_PRINTF(RDebug::Printf("tyaxUnlock(0x%08x)", address));
TUint16 * pF = (TUint16*)address;
// Unlock
*pF=KCmdClearBlockLockBit1;
*pF=KCmdClearBlockLockBit2;
}
///////////////////////////////////////////////////////////////////////////////
//
// GENERIC - implementations of the generic routines
//
// - reset
// - erase
// - write
//
///////////////////////////////////////////////////////////////////////////////
// Reset Flash
///////////////////////////////////////////////////////////////////////////////
TInt cfiReset(TUint32 flashId, TUint32 address)
{
SPANSION_PRINTF(RDebug::Printf("cfiReset(0x%08x)", address));
volatile TUint8 * p = (TUint8*)address;
*(p)=0xF0; // reset spansion flash
return KErrNone;
}
///////////////////////////////////////////////////////////////////////////////
// Reset Flash
///////////////////////////////////////////////////////////////////////////////
TInt tyaxReset(TUint32 flashId, TUint32 address)
{
TYAX_PRINTF(RDebug::Printf("tyaxReset(0x%08x)", address));
TUint16 * p = (TUint16*)address;
// clear the status register
tyaxClearStatus((TUint32)address);
// write to linear base and set strataflash into readarray mode
*p=KCmdReadArrayMode;
return KErrNone;
}
///////////////////////////////////////////////////////////////////////////////
// Erase a block of flash
///////////////////////////////////////////////////////////////////////////////
TInt spansionErase(TUint32 flashId, TUint32 aBase, TUint32 anAddr, TUint32 aSize)
{
SPANSION_PRINTF(RDebug::Printf("spansionErase 0x%08x", anAddr));
volatile TUint32 base=anAddr&~(KFlashEraseBlockSize-1); // round base address down to block
volatile TUint32 end=anAddr+aSize;
end=(end+KFlashEraseBlockSize-1)&~(KFlashEraseBlockSize-1); // round end address up to block
TUint32 size=end-base;
volatile TUint8* p=(volatile TUint8*)base;
SPANSION_PRINTF(RDebug::Printf("Erase anAddr=0x%08x, aSize=0x%08x, base=0x%08x, end=0x%08x, size=0x%08x, p=0x%08x", anAddr, aSize, base, end, size, p));
cfiReset(flashId, aBase);
for (; size; size-=KFlashEraseBlockSize, p+=(KFlashEraseBlockSize>>1))
{
CfiCommand(aBase, base, CfiErase);
TUint retries = KFlashRetries;
while ((*(volatile TUint8*)anAddr != 0xFF) && (retries != 0))
{
retries--;
}
if (retries==0)
{
RDebug::Printf("Erase Failed anAddr=0x%08x, aSize=0x%08x, base=0x%08x, end=0x%08x, size=0x%08x, p=0x%08x", anAddr, aSize, base, end, size, p);
}
cfiReset(flashId, aBase);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// Erase a block of flash
///////////////////////////////////////////////////////////////////////////////
TInt tyaxErase(TUint32 flashId, TUint32 aBase, TUint32 anAddr, TUint32 aSize)
{
TUint32 base=anAddr&~(KFlashEraseBlockSize-1); // round base address down to block
TUint32 end=anAddr+aSize;
end=(end+KFlashEraseBlockSize-1)&~(KFlashEraseBlockSize-1); // round end address up to block
TUint32 size=end-base;
volatile TUint16* p=(volatile TUint16*)base;
// write to linear base and set strataflash into readarray mode
*p=KCmdReadArrayMode;
// clear the status register
*p=KCmdClearStatus;
for (; size; size-=KFlashEraseBlockSize, p+=(KFlashEraseBlockSize>>1))
{
// Unlock
*p=KCmdClearBlockLockBit1;
*p=KCmdClearBlockLockBit2;
// Erase
*p=KCmdBlockErase1; // block erase
*p=KCmdBlockErase2; // block erase confirm
// wait for the erase to finish
while ((*p & KStatusBusy)!=KStatusBusy);
// put the flash block back to normal
TUint32 s=*p;
*p=KCmdClearStatus; // clear status reg
*p=KCmdReadArrayMode;
if (s & KStatusLockBitError)
{
// error
RDebug::Printf("Erase Failed: addr:0x%x status: 0x%x", p, s);
return (TUint32)p-anAddr+1;
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// Write a block of flash
///////////////////////////////////////////////////////////////////////////////
TInt spansionWrite(TUint32 flashId, TUint32 anAddr, TUint32 aSize, const TUint32* aPS)
// Assume aSize <= KFlashWriteBufSize
{
SPANSION_PRINTF(WRITE_PRINTF(RDebug::Printf("spansionWrite anAddr=0x%08x, aSize=0x%08x", anAddr, aSize)));
volatile TUint8 * base = (TUint8 *)FlashAddress;
volatile TUint16 * pDest = (TUint16*)anAddr;
volatile TUint16 * pSrc = (TUint16*)aPS;
volatile TUint16 * pEnd = (TUint16*)(anAddr+aSize);
for (; pDest < pEnd; pDest++, pSrc++)
{
CfiCommand((TUint32)base, (TUint32)base, CfiWrite);
*pDest = *pSrc;
TUint retries = KFlashRetries;
while ((*pDest != *pSrc) && (retries != 0))
{
retries--;
}
if (*pDest != *pSrc)
{
RDebug::Printf("Write failed 0x%x=0x%x == 0x%x", pDest, *pSrc, *pDest);
return 1;
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// Write a block of flash
///////////////////////////////////////////////////////////////////////////////
// Assume aSize <= KFlashWriteBufSize
TInt tyaxWrite(TUint32 flashId, TUint32 anAddr, TUint32 aSize, const TUint32* aPS)
{
TYAX_PRINTF(WRITE_PRINTF(RDebug::Printf("tyaxWrite anAddr=0x%08x, aSize=0x%08x", anAddr, aSize)));
volatile TUint16* pF=(volatile TUint16*)anAddr;
tyaxUnlock(anAddr);
tyaxClearStatus(anAddr);
if (flashInfo[flashId].deviceId == CFI_DEV_SIBLEY)
{
tyaxWaitUntilReady(anAddr, KCmdWriteStatusSibley);
}
else
{
tyaxWaitUntilReady(anAddr, KCmdWriteStatus);
}
// convert to words - 1
TInt16 l=(aSize>>1)-1;
*pF=l; // Write no of words
const TUint16* pS=(const TUint16*)aPS;
for (;l>=0;l--)
{
*pF++=*pS++;
}
pF=(volatile TUint16*)anAddr;
*pF=0xD0; // Confirm
tyaxWaitUntilReady(anAddr, KCmdReadStatus);
tyaxReset(flashId, anAddr);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// WRAPPERS
//
// A top level routine to prevent each function checking the flash type
//
///////////////////////////////////////////////////////////////////////////////
TInt flashReset(TUint32 flashId, TUint32 address)
{
PRINTF(RDebug::Printf("flashReset()"));
TInt retVal = KErrNotSupported;
if (flashInfo[flashId].reset != NULL)
{
retVal = flashInfo[flashId].reset(flashId, address);
}
return retVal;
}
TInt flashErase(TUint32 flashId, TUint32 base, TUint32 address, TUint32 size)
{
PRINTF(RDebug::Printf("flashErase()"));
TInt retVal = KErrNone;
if (flashInfo[flashId].erase != NULL)
{
retVal = flashInfo[flashId].erase(flashId, base, address, size);
}
return retVal;
}
TInt flashWrite(TUint32 flashId, TUint32 anAddr, TUint32 aSize, const TUint32* aPS)
{
WRITE_PRINTF(RDebug::Printf("flashWrite()"));
TInt retVal = KErrNone;
if (flashInfo[flashId].write != NULL)
{
retVal = flashInfo[flashId].write(flashId, anAddr, aSize, aPS);
}
return retVal;
}
///////////////////////////////////////////////////////////////////////////////
//
// Flash ID
//
// Identify the flash part at the given address
// returns an index into the flashInfo structure
///////////////////////////////////////////////////////////////////////////////
TInt flashId(TUint32 address)
{
TUint deviceIndex = FLASH_TYPE_UNKNOWN;
volatile TUint16* p16=(volatile TUint16*)address; // used for 16 bit read/write to the flash
// Put flash into CFI query mode using 8 bit writes
CfiCommand(address, address, CfiQuery);
// Read ID codes using 16 bit reads
// if we ever need to support 8 bit devices, we may need to change this to 2 x 8 bit reads per attribute
TUint16 manufacturerId = *(p16 );
TUint16 deviceId = *(p16+1);
for (TUint32 i=0; flashInfo[i].manufacturerId !=0; i++)
{
PRINTF(RDebug::Printf("Check device: M 0x%04x D 0x%04x", flashInfo[i].manufacturerId, flashInfo[i].deviceId));
if ( ( flashInfo[i].manufacturerId == manufacturerId)
&& ( (flashInfo[i].deviceId == CFI_DEV_ANY ) // support generic flash devices
||(flashInfo[i].deviceId == deviceId )
)
)
{
PRINTF(RDebug::Print(_L("Found device: %s (Manufacturer=%x Device=%x)"), flashInfo[i].name.Ptr(), flashInfo[i].manufacturerId, flashInfo[i].deviceId));
deviceIndex = i;
break;
}
}
if (deviceIndex == FLASH_TYPE_UNKNOWN)
{
RDebug::Printf("Flash type unknown: Manufacturer ID = %04x, Device ID = %04x", manufacturerId, deviceId );
}
flashReset(deviceIndex, (TUint32)FlashAddress);
return deviceIndex;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
GLDEF_C TUint32 * GetFlashChunk()
{
// return if already initialised
if (FlashAddress != NULL)
return FlashAddress;
TInt r = User::LoadLogicalDevice(KBootldrLddName);
r = LddFlash.Open();
if (r!=KErrNone)
{
PrintToScreen(_L("FAULT due to LddFlash open\r\n"));
BOOT_FAULT();
}
TUint8* kernelAddress;
r=LddFlash.CreateChunk(KNORFlashTargetSize,(TAny**)&kernelAddress);
if (r!=KErrNone)
{
PrintToScreen(_L("FAULT due to chunk create\r\n"));
BOOT_FAULT();
}
// If we're running from RAM flash will be in a different place...
r = LddFlash.CommitMemory(KNORFlashTargetSize,addr_to_page(KNORFlashTargetAddr));
if (r!=KErrNone)
{
PrintToScreen(_L("FAULT due to commit\r\n"));
BOOT_FAULT();
}
r = LddFlash.GetChunkHandle(TheFlashChunk);
if (r!=KErrNone)
{
PrintToScreen(_L("FAULT due to handle\r\n"));
BOOT_FAULT();
}
TUint8* Base = TheFlashChunk.Base();
FlashAddress = (TUint32*)Base;
FlashId = flashId((TUint32)FlashAddress);
return FlashAddress;
}
GLDEF_C void NotifyDataAvailable(TInt aTotalAmount)
{
Available=(TUint32)aTotalAmount;
}
GLDEF_C void NotifyDownloadComplete()
{
Complete=ETrue;
}
GLDEF_C TBool BlankCheck(TUint32 anAddr, TUint32 aSize)
{
const TUint16* p=(const TUint16*)anAddr;
const TUint16* pE=p+(aSize>>1);
TBool rv=ETrue;
while(p<pE)
{
if (*p!=0xffff)
{
PRINTF(RDebug::Printf("BlankCheck %x is not blank! anAddr=0x%08x, aSize=0x%08x, p=0x%08x, *p=0x%08x", anAddr, anAddr, aSize, (TUint32)p, (TUint32)*p));
rv=EFalse;
break;
}
p++;
}
if (rv)
{
PRINTF(RDebug::Printf("BlankCheck: %x is blank", anAddr));
}
return rv;
}
///////////////////////////////////////////////////////////////////////////////
//
// Erase
//
// This function is used by the variant code. The variant code shouldn't care
// about the Flash ID, so I've left this function here as a wrapper for the
// internal flashErase function, passing in a nasty global variable containing
// the Flash ID.
//
///////////////////////////////////////////////////////////////////////////////
GLDEF_C TInt Erase(TUint32 anAddr, TUint32 aSize)
{
flashErase(FlashId, (TUint32)FlashAddress, anAddr, aSize);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// Write
//
// This function is used by the variant code. As well as the Flash ID comment
// from above (see Erase), the variant shouldn't have to care about internal
// buffer sizes, etc.
//
///////////////////////////////////////////////////////////////////////////////
GLDEF_C TInt Write(TUint32 anAddr, TUint32 aSize, const TUint32* aPS)
{
TInt rv=0;
do
{
if ((rv=flashWrite(FlashId, anAddr, KFlashWriteBufSize, aPS))!=0)
{
break;
}
anAddr+=KFlashWriteBufSize;
aPS+=KFlashWriteBufSize>>2;
aSize-=KFlashWriteBufSize;
} while(aSize);
return rv;
}
TInt FlashThread(TAny*)
{
// If this thread crashes we want it to take the system down
User::SetCritical(User::ESystemPermanent);
GetFlashChunk();
if (FlashBootLoader)
{
PrintToScreen(_L("*** Reflashing bootloader ***\r\n"));
FlashImageAddr=(TLinAddr)FlashAddress;
// sanity check...
if ((TUint32)ImageSize > KNORFlashMaxBootloaderSize)
{
PrintToScreen(_L("Image is larger than the flash area (%d > %d) bytes.\r\n"), ImageSize, KNORFlashMaxBootloaderSize);
return KErrNotSupported;
}
}
else
{
PrintToScreen(_L("*** Writing to NOR Flash ***\r\n"));
FlashImageAddr=(TLinAddr)FlashAddress+KNORFlashMaxBootloaderSize;
// sanity check...
if ((TUint32)ImageSize > KNORFlashMaxImageSize)
{
PrintToScreen(_L("Image is larger than the flash area (%d > %d) bytes.\r\n"), ImageSize, KNORFlashMaxImageSize);
return KErrNotSupported;
}
}
FlashImageSize=(TUint32)ImageSize;
Complete=EFalse;
TUint32 imgSzMb=(FlashImageSize+0xfffff)&~0xfffff; // round image size up to 1Mb
InitProgressBar(1,imgSzMb,_L("ERASE"));
TUint32 base=FlashImageAddr;
TUint32 end=base+imgSzMb;
TInt r=KErrNone;
while(base<end)
{
if (!BlankCheck(base,KFlashEraseBlockSize))
{
r=Erase(base, KFlashEraseBlockSize);
if (r!=KErrNone)
{
PrintToScreen(_L("Erase failed 0x%x\r\n"), r);
RDebug::Printf("Erase failed 0x%x", r);
// make this a rdebug
BOOT_FAULT();
}
}
if (!BlankCheck(base,KFlashEraseBlockSize))
{
PrintToScreen(_L("BlankCheck failed 0x%x\r\n"),base);
RDebug::Printf("BlankCheck failed at adress 0x%08x with error code 0x%x",base,r);
//BOOT_FAULT(); // why crash at this point, retry is better, surely?
}
else
{
// only move to next block and update progress if the block erase passed
base+=KFlashEraseBlockSize;
UpdateProgressBar(1,base-FlashImageAddr);
}
}
base=FlashImageAddr;
while(base<end)
{
if (!BlankCheck(base,KFlashEraseBlockSize))
{
PrintToScreen(_L("BlankCheck 2 failed 0x%x\r\n"),base);
RDebug::Printf("BlankCheck 2 failed at adress 0x%08x with error code 0x%x",base,r);
BOOT_FAULT();
}
base+=KFlashEraseBlockSize;
}
InitProgressBar(1,FlashImageSize,_L("WRITE"));
TUint32 source=DestinationAddress(); // start of image in RAM
if (ImageHeaderPresent)
source+=256; // skip header if present
TUint32 target=FlashImageAddr; // target in flash
TBool complete=EFalse;
TUint32 used_bytes=0;
// while the image hasn't been written fully
while ((target-FlashImageAddr) < FlashImageSize)
{
used_bytes=source-DestinationAddress();
complete=Complete; // must check Complete before Available
// if there isn't anything ready, go back to the top
if (Available<(used_bytes+256) && !complete)
{
continue; // wait for 256 bytes more data
}
TUint32 write_block_size=Available-used_bytes; // how much is ready
write_block_size &= ~(KFlashWriteBufSize-1); // only write whole buffers
while (write_block_size)
{
TUint32 write_size=Min(write_block_size,(TUint32)0x400); // update progress after each 1K
r=Write(target,write_size,(const TUint32*)source);
if (r!=KErrNone)
{
PrintToScreen(_L("Write failed 0x%x"),r);
BOOT_FAULT();
}
target+=write_size;
source+=write_size;
write_block_size-=write_size;
UpdateProgressBar(1,target-FlashImageAddr);
}
}
PrintToScreen(_L("Verifying image...\r\n"));
source=DestinationAddress(); // start of image in RAM
if (ImageHeaderPresent)
source+=256; // skip header if present
base=FlashImageAddr;
volatile TUint16* pRam=(volatile TUint16*)source;
volatile TUint16* pFlash=(volatile TUint16*)base;
volatile TUint16* pFlashEnd=pFlash+(FlashImageSize>>1);
InitProgressBar(1, FlashImageSize, _L("VERIFY"));
while(pFlash<pFlashEnd)
{
if (*pFlash++ != *pRam++)
{
PrintToScreen(_L("Verify error at byte %d (0x%x != 0x%x)\r\n"),
((pFlash-1) - (volatile TUint16*)base) * 2, (*(pFlash-1)), (*(pRam-1)));
PrintToScreen(_L("VERIFY %d"),(TInt)(pFlash-1));
BOOT_FAULT();
}
if (!((TUint32)pFlash % 0x400))
UpdateProgressBar(1,(TUint32)pFlash-(TUint32)FlashImageAddr);
}
PrintToScreen(_L("Verify complete\r\n"));
if (FlashBootLoader)
{
PrintToScreen(_L("Rebooting in %d Seconds...\r\n"), KRebootDelaySecs);
InitProgressBar(1, KRebootDelaySecs, _L("DELAY "));
for (TUint i=0 ; i<KRebootDelaySecs ; ++i)
{
User::After(1000000); // Sleep in millisecs
UpdateProgressBar(1, i);
}
UpdateProgressBar(1, KRebootDelaySecs); // let it get to the end
PrintToScreen(_L("Rebooting...\r\n"));
User::After(10000);
Restart(KtRestartReasonHardRestart);
}
PrintToScreen(_L("Booting Image...\r\n"));
Restart(KtRestartReasonBootRestart | KtRestartReasonNORImage);
// NOTREACHED
return 0;
}
GLDEF_C TInt InitFlashWrite()
{
// start thread
RThread t;
TInt r=t.Create(KLitThreadName,FlashThread,0x2000,NULL,NULL);
if (r!=KErrNone)
{
return r;
}
t.SetPriority(EPriorityLess);
t.Resume();
return KErrNone;
}
#endif //__SUPPORT_FLASH_REPRO__