diff -r 000000000000 -r a41df078684a brdbootldr/ubootldr/flash_nor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/brdbootldr/ubootldr/flash_nor.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,850 @@ +// 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 +#include +#include +#include +#include +#include +#include +#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>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>1); + + InitProgressBar(1, FlashImageSize, _L("VERIFY")); + while(pFlash