sl@0: // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // template\Template_Variant\Specific\lffsdev.cpp sl@0: // Implementation of a Logging Flash file system (LFFS) physical device driver sl@0: // for a standard Common Flash Interface (CFI) based NOR flash chip. sl@0: // This file is part of the Template Base port sl@0: // N.B. This sample code assumes that: sl@0: // (1) the device does not provide an interrupt i.e. it needs to be polled using a timer sl@0: // to ascertain when an Erase/Write operation has completed. sl@0: // (2) the flash chip does not have 'read-while-write' support. sl@0: // sl@0: // sl@0: sl@0: #include "lffsdev.h" sl@0: #include "variant.h" sl@0: sl@0: #ifdef _DEBUG sl@0: #define CHANGE_ERASE_STATE(x) {TUint32 s=iEraseState; iEraseState=x; __KTRACE_OPT(KLOCDRV,Kern::Printf("ErSt: %d->%d",s,x));} sl@0: #else sl@0: #define CHANGE_ERASE_STATE(x) iEraseState=x sl@0: #endif sl@0: sl@0: // sl@0: // TO DO: (mandatory) sl@0: // sl@0: // Define the pyhsical base address of the NOR-Flash sl@0: // This is only example code... you will need to modify it for your hardware sl@0: const TPhysAddr KFlashPhysicalBaseAddress = 0x04000000; sl@0: sl@0: sl@0: /******************************************** sl@0: * Common Flash Interface (CFI) query stuff sl@0: ********************************************/ sl@0: sl@0: /** sl@0: Read an 8-bit value from the device at the specified offset sl@0: sl@0: @param aOffset the address in device words sl@0: */ sl@0: TUint32 DMediaDriverFlashTemplate::ReadQueryData8(TUint32 aOffset) sl@0: { sl@0: volatile TUint8* pF=(volatile TUint8*)(iBase+FLASH_ADDRESS_IN_BYTES(aOffset)); sl@0: return pF[0]; sl@0: } sl@0: sl@0: /** sl@0: Read a 16-bit value from the device at the specified offset sl@0: sl@0: @param aOffset the address in device words sl@0: */ sl@0: TUint32 DMediaDriverFlashTemplate::ReadQueryData16(TUint32 aOffset) sl@0: { sl@0: volatile TUint8* pF=(volatile TUint8*)(iBase); sl@0: return sl@0: pF[FLASH_ADDRESS_IN_BYTES(aOffset+0)] | sl@0: (pF[FLASH_ADDRESS_IN_BYTES(aOffset+1)] << 8); sl@0: } sl@0: sl@0: /** sl@0: Put the device into query mode to read the flash parameters. sl@0: */ sl@0: void DMediaDriverFlashTemplate::ReadFlashParameters() sl@0: { sl@0: volatile TFLASHWORD* pF=(volatile TFLASHWORD*)iBase + KCmdReadQueryOffset; sl@0: *pF=KCmdReadQuery; sl@0: sl@0: TUint32 qd=ReadQueryData16(KQueryOffsetQRY)|(ReadQueryData8(KQueryOffsetQRY+2)<<16); sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query QRY=%08x",qd)); sl@0: __ASSERT_ALWAYS(qd==0x595251,FLASH_FAULT()); sl@0: sl@0: qd = FLASH_BUS_DEVICES << ReadQueryData8(KQueryOffsetSizePower); sl@0: sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query Size=%08x",qd)); sl@0: iTotalSize=qd; sl@0: sl@0: qd = FLASH_BUS_DEVICES << ReadQueryData16(KQueryOffsetWriteBufferSizePower); sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query WBSize=%08x",qd)); sl@0: iWriteBufferSize=qd; sl@0: sl@0: qd = (ReadQueryData16(KQueryOffsetEraseBlockSize)) << (8 + FLASH_BUS_DEVICES-1); sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query EBSize=%08x",qd)); sl@0: iEraseBlockSize=qd; sl@0: sl@0: *pF=KCmdReadArray; sl@0: } sl@0: sl@0: sl@0: /******************************************** sl@0: * Common Flash Interface (CFI) main code sl@0: ********************************************/ sl@0: sl@0: /** sl@0: NOR flash LFFS constructor. sl@0: sl@0: @param aMediaId Media id number from ELOCD sl@0: */ sl@0: DMediaDriverFlashTemplate::DMediaDriverFlashTemplate(TInt aMediaId) sl@0: : DMediaDriverFlash(aMediaId), sl@0: iHoldOffTimer(HoldOffTimerFn,this), sl@0: iEventDfc(EventDfc,this,NULL,2) sl@0: { sl@0: // iWriteState = EWriteIdle; sl@0: // iEraseState = EEraseIdle; sl@0: } sl@0: sl@0: /** sl@0: Device specific implementation of the NOR LFFS initialisation routine. sl@0: sl@0: @see DMediaDriverFlash::Initialise sl@0: @return KErrNone unless the write data buffer couldn't be allocated or the sl@0: timer interrupt could not be bound. sl@0: */ sl@0: TInt DMediaDriverFlashTemplate::Initialise() sl@0: { sl@0: iEventDfc.SetDfcQ(iPrimaryMedia->iDfcQ); sl@0: iData=(TUint8*)Kern::Alloc(KDataBufSize); sl@0: if (!iData) sl@0: return KErrNoMemory; sl@0: sl@0: // Create temporary HW chunk to read FLASH device parameters (especially size) sl@0: DPlatChunkHw* pC = NULL; sl@0: TInt r = DPlatChunkHw::New(pC, KFlashPhysicalBaseAddress, 0x1000, EMapAttrSupRw|EMapAttrFullyBlocking); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: iBase = pC->LinearAddress(); sl@0: ReadFlashParameters(); sl@0: // close temporary chunk and open chunk with correct size sl@0: pC->Close(NULL); sl@0: r = DPlatChunkHw::New(iFlashChunk, KFlashPhysicalBaseAddress, iTotalSize, EMapAttrSupRw|EMapAttrFullyBlocking); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: iBase = iFlashChunk->LinearAddress(); sl@0: sl@0: r=Interrupt::Bind(KIntIdTimer1, Isr, this); sl@0: if (r!=KErrNone) sl@0: { sl@0: __KTRACE_OPT(KLOCDRV, Kern::Printf("Flash:Isr Bind failed")); sl@0: return r; sl@0: } sl@0: sl@0: // TO DO: (mandatory) sl@0: // Write to the appropriate hardware register(s) to sl@0: // configure (if necessary) and enable the timer hardware sl@0: // sl@0: sl@0: // Enable the timer interrupt sl@0: Interrupt::Enable(KIntIdTimer1); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: Used by the generic flash media driver code to get the erase block size in sl@0: bytes. sl@0: */ sl@0: TUint32 DMediaDriverFlashTemplate::EraseBlockSize() sl@0: { sl@0: return iEraseBlockSize; sl@0: } sl@0: sl@0: /** sl@0: @return Return size of lffs in bytes sl@0: */ sl@0: TUint32 DMediaDriverFlashTemplate::TotalSize() sl@0: { sl@0: return iTotalSize; sl@0: } sl@0: sl@0: /** sl@0: Read at the location indicated by DMediaDriverFlash::iReadReq. sl@0: Where Pos() is the read location sl@0: sl@0: @return >0 Defer request to ELOCD. A write is in progress sl@0: @return KErrNone Erase has been started sl@0: @return <0 An error has occured. sl@0: */ sl@0: TInt DMediaDriverFlashTemplate::DoRead() sl@0: { sl@0: if (iWriteReq) sl@0: return KMediaDriverDeferRequest; // write in progress so defer read sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoRead")); sl@0: if (iEraseState==EEraseIdle || iEraseState==ESuspended) sl@0: { sl@0: // can do the read now sl@0: TInt pos=(TInt)iReadReq->Pos(); sl@0: TInt len=(TInt)iReadReq->Length(); sl@0: sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoRead ibase: %x, pos: %x, len: %x",iBase,pos,len)); sl@0: sl@0: // Issue a read array command sl@0: // Porting note: Some devices may work without this step. sl@0: // Ensure that the write is always dword aligned sl@0: volatile TFLASHWORD* pF=(volatile TFLASHWORD*)((iBase+pos)&0xFFFFFFF0); sl@0: *pF=KCmdReadArray; sl@0: sl@0: TPtrC8 des((const TUint8*)(iBase+pos),len); sl@0: TInt r=iReadReq->WriteRemote(&des,0); sl@0: Complete(EReqRead,r); sl@0: sl@0: // resume erase if necessary sl@0: if (iEraseState==ESuspended) sl@0: StartErase(); sl@0: } sl@0: else if (iEraseState==EErase) sl@0: { sl@0: // erase in progress - suspend it sl@0: SuspendErase(); sl@0: } sl@0: else if (iEraseState==EEraseNoSuspend) sl@0: CHANGE_ERASE_STATE(ESuspendPending); // wait for suspend to complete sl@0: sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: /** sl@0: Write at the location indicated by DMediaDriverFlash::iWriteReq sl@0: sl@0: @return >0 Defer request to ELOCD. A read is in progress sl@0: @return KErrNone Erase has been started sl@0: @return <0 An error has occured. sl@0: */ sl@0: TInt DMediaDriverFlashTemplate::DoWrite() sl@0: { sl@0: if (iReadReq) sl@0: return KMediaDriverDeferRequest; // read in progress so defer write sl@0: sl@0: TInt pos=(TInt)iWriteReq->Pos(); sl@0: TInt len=(TInt)iWriteReq->Length(); sl@0: if (len==0) sl@0: return KErrCompletion; sl@0: TUint32 wb_mask=iWriteBufferSize-1; sl@0: iWritePos=pos & ~wb_mask; // round media position down to write buffer boundary sl@0: TInt wb_off=pos & wb_mask; // how many bytes of padding at beginning sl@0: TInt start_len=Min(len,KDataBufSize-(TInt)wb_off); sl@0: TInt write_len=(start_len+wb_off+wb_mask)&~wb_mask; sl@0: memset(iData,0xff,iWriteBufferSize); sl@0: memset(iData+write_len-iWriteBufferSize,0xff,iWriteBufferSize); sl@0: TPtr8 des(iData+wb_off,0,start_len); sl@0: TInt r=iWriteReq->ReadRemote(&des,0); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: iWriteReq->RemoteDesOffset()+=start_len; sl@0: iWriteReq->Length()-=start_len; sl@0: iDataBufPos=0; sl@0: iDataBufRemain=write_len; sl@0: iWriteError=KErrNone; sl@0: sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Write iWritePos=%08x iDataBufRemain=%x",iWritePos,iDataBufRemain)); sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Write Pos=%08x Length=%08x RemDesOff=%08x", sl@0: (TInt)iWriteReq->Pos(),(TInt)iWriteReq->Length(),iWriteReq->RemoteDesOffset())); sl@0: sl@0: if (iEraseState==EEraseIdle || iEraseState==ESuspended) sl@0: { sl@0: // can start the write now sl@0: iWriteState=EWriting; sl@0: WriteStep(); sl@0: } sl@0: else if (iEraseState==EErase) sl@0: { sl@0: // erase in progress - suspend it sl@0: SuspendErase(); sl@0: } sl@0: else if (iEraseState==EEraseNoSuspend) sl@0: CHANGE_ERASE_STATE(ESuspendPending); // wait for suspend to complete sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::WriteStep() sl@0: { sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:WriteStep @%08x",iWritePos)); sl@0: if (iDataBufRemain) sl@0: { sl@0: // still data left in buffer sl@0: volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iWritePos); sl@0: TInt i=KMaxWriteSetupAttempts; sl@0: *pF=KCmdClearStatusRegister; sl@0: TUint32 s=0; sl@0: for (; i>0 && ((s&KStsReady)!=KStsReady); --i) sl@0: { sl@0: *pF=KCmdWriteToBuffer; // send write command sl@0: *pF=KCmdReadStatusRegister; // send read status command sl@0: s=*pF; // read status reg sl@0: } sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("i=%d, s=%08x",i,s)); sl@0: sl@0: // calculate the buffer size in words -1 sl@0: TFLASHWORD l = (FLASH_BYTES_TO_WORDS(iWriteBufferSize)) - 1; sl@0: sl@0: #if FLASH_BUS_DEVICES == 2 // 2x16bit or 2x8bit devices sl@0: l|= l<< BUS_WIDTH_PER_DEVICE; sl@0: #elif FLASH_BUS_DEVICES == 4 // 4x8bit device sl@0: l|= (l<=0; len--) sl@0: { sl@0: *pF++=*pS++; sl@0: } sl@0: sl@0: *(volatile TFLASHWORD *)(iBase+iWritePos) = KCmdConfirm; sl@0: sl@0: // set up timer to poll for completion sl@0: StartPollTimer(KFlashWriteTimerPeriod,KFlashWriteTimerRetries); sl@0: sl@0: iWritePos+=iWriteBufferSize; sl@0: iDataBufPos+=iWriteBufferSize; sl@0: iDataBufRemain-=iWriteBufferSize; sl@0: if (!iDataBufRemain) sl@0: { sl@0: // refill buffer sl@0: TInt len=(TInt)iWriteReq->Length(); sl@0: if (!len) sl@0: return; // all data has been written, complete request next time sl@0: TUint32 wb_mask=iWriteBufferSize-1; sl@0: TInt block_len=Min(len,KDataBufSize); sl@0: TInt write_len=(block_len+wb_mask)&~wb_mask; sl@0: memset(iData+write_len-iWriteBufferSize,0xff,iWriteBufferSize); sl@0: TPtr8 des(iData,0,block_len); sl@0: TInt r=iWriteReq->ReadRemote(&des,0); sl@0: if (r!=KErrNone) sl@0: { sl@0: iWriteError=r; sl@0: return; // leave iDataBufRemain=0 so request is terminated when write completes sl@0: } sl@0: iWriteReq->RemoteDesOffset()+=block_len; sl@0: iWriteReq->Length()-=block_len; sl@0: iDataBufPos=0; sl@0: iDataBufRemain=write_len; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // write request should have completed, maybe with an error sl@0: __ASSERT_ALWAYS(iWriteReq->Length()==0 || iWriteError,FLASH_FAULT()); sl@0: iWriteState=EWriteIdle; sl@0: Complete(EReqWrite,iWriteError); sl@0: if (iEraseState==ESuspended) sl@0: StartErase(); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Erase at the location indicated by DMediaDriverFlash::iEraseReq sl@0: sl@0: @return >0 Defer request to ELOCD. Read or a write is in progress sl@0: @return KErrNone Erase has been started sl@0: @return <0 An error has occured. sl@0: */ sl@0: TInt DMediaDriverFlashTemplate::DoErase() sl@0: { sl@0: if (iReadReq || iWriteReq) sl@0: return KMediaDriverDeferRequest; // read or write in progress so defer this request sl@0: TUint32 pos=(TUint32)iEraseReq->Pos(); sl@0: TUint32 len=(TUint32)iEraseReq->Length(); sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoErase %d@%08x",len,pos)); sl@0: if (len!=iEraseBlockSize) sl@0: return KErrArgument; // only allow single-block erase sl@0: if (pos & (iEraseBlockSize-1)) sl@0: return KErrArgument; // start position must be on erase block boundary sl@0: iErasePos=pos; sl@0: __ASSERT_ALWAYS(iEraseState==EEraseIdle,FLASH_FAULT()); sl@0: StartErase(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::StartHoldOffTimer() sl@0: { sl@0: // if this is a retry, don't allow suspends sl@0: if (iEraseAttempt==0) sl@0: iHoldOffTimer.OneShot(KEraseSuspendHoldOffTime); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::CancelHoldOffTimer() sl@0: { sl@0: iHoldOffTimer.Cancel(); sl@0: ClearEvents(EHoldOffEnd); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::ClearEvents(TUint32 aEvents) sl@0: { sl@0: __e32_atomic_and_ord32(&iEvents, ~aEvents); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::HoldOffTimerFn(TAny* aPtr) sl@0: { sl@0: DMediaDriverFlashTemplate* p=(DMediaDriverFlashTemplate*)aPtr; sl@0: p->IPostEvents(EHoldOffEnd); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::StartPollTimer(TUint32 aPeriod, TUint32 aRetries) sl@0: { sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Tmr %d * %d",aPeriod,aRetries)); sl@0: sl@0: ClearEvents(EPollTimer); sl@0: iPollPeriod=aPeriod; sl@0: iPollRetries=aRetries; sl@0: StartPollTimer(); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::StartPollTimer() sl@0: { sl@0: // TO DO: (mandatory) sl@0: // Configure the hardware timer to expire after iPollPeriod ticks sl@0: // and start the timer sl@0: sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::EventDfc(TAny* aPtr) sl@0: { sl@0: DMediaDriverFlashTemplate* p=(DMediaDriverFlashTemplate*)aPtr; sl@0: TUint32 e = __e32_atomic_swp_ord32(&p->iEvents, 0); sl@0: if (e) sl@0: p->HandleEvents(e); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::HandleEvents(TUint32 aEvents) sl@0: { sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Events %x",aEvents)); sl@0: if (aEvents & EHoldOffEnd) sl@0: { sl@0: if (iEraseState==ESuspendPending) sl@0: { sl@0: SuspendErase(); sl@0: } sl@0: else if (iEraseState==EEraseNoSuspend) sl@0: { sl@0: CHANGE_ERASE_STATE(EErase); // can now be suspended sl@0: } sl@0: else sl@0: { sl@0: __KTRACE_OPT(KPANIC,Kern::Printf("iEraseState=%d",iEraseState)); sl@0: FLASH_FAULT(); sl@0: } sl@0: } sl@0: if (aEvents & EPollTimer) sl@0: { sl@0: volatile TFLASHWORD* pF=(volatile TFLASHWORD*)iBase; sl@0: *pF=KCmdReadStatusRegister; sl@0: if ((*pF & KStsReady)!=KStsReady) sl@0: { sl@0: // not ready yet sl@0: if (--iPollRetries) sl@0: { sl@0: // try again sl@0: StartPollTimer(); sl@0: } sl@0: else sl@0: // timed out sl@0: aEvents|=ETimeout; sl@0: } sl@0: else sl@0: { sl@0: // ready sl@0: TFLASHWORD s=*pF; // read full status value sl@0: *pF=KCmdClearStatusRegister; sl@0: DoFlashReady(s); sl@0: } sl@0: } sl@0: if (aEvents & ETimeout) sl@0: { sl@0: DoFlashTimeout(); sl@0: } sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::StartErase() sl@0: { sl@0: TFLASHWORD s=KStsReady; sl@0: TInt i; sl@0: volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iErasePos); sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:StartErase %08x",pF)); sl@0: switch (iEraseState) sl@0: { sl@0: case EEraseIdle: // first attempt to erase sl@0: iEraseAttempt=-1; sl@0: // coverity[fallthrough] sl@0: // fallthrough after attempt sl@0: case EErase: // retry after verify failed sl@0: case EEraseNoSuspend: sl@0: ++iEraseAttempt; sl@0: *pF=KCmdBlockErase; sl@0: *pF=KCmdConfirm; sl@0: CHANGE_ERASE_STATE(EEraseNoSuspend); sl@0: iEraseError=0; sl@0: StartHoldOffTimer(); sl@0: break; sl@0: case ESuspended: sl@0: *pF=KCmdClearStatusRegister; sl@0: *pF=KCmdEraseResume; sl@0: CHANGE_ERASE_STATE(EEraseNoSuspend); sl@0: i=KMaxEraseResumeAttempts; sl@0: for (; i>0 && ((s&KStsReady)!=0); --i) sl@0: { sl@0: *pF=KCmdReadStatusRegister; // send read status command sl@0: s=*pF; // read status reg sl@0: s=*pF; // read status reg sl@0: } sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("RESUME: i=%d, s=%08x",i,s)); sl@0: StartHoldOffTimer(); sl@0: break; sl@0: default: sl@0: __KTRACE_OPT(KPANIC,Kern::Printf("iEraseState=%d",iEraseState)); sl@0: FLASH_FAULT(); sl@0: } sl@0: StartPollTimer(KFlashEraseTimerPeriod,KFlashEraseTimerRetries); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::SuspendErase() sl@0: { sl@0: __ASSERT_ALWAYS(iEraseState==EErase || iEraseState==ESuspendPending,FLASH_FAULT()); sl@0: volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iErasePos); sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:SuspendErase %08x",pF)); sl@0: *pF=KCmdEraseSuspend; sl@0: CHANGE_ERASE_STATE(ESuspending); sl@0: StartPollTimer(KFlashSuspendTimerPeriod,KFlashSuspendTimerRetries); sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::StartPendingRW() sl@0: { sl@0: // start any pending read or write requests sl@0: if (iReadReq) sl@0: DoRead(); sl@0: if (iWriteReq) sl@0: { sl@0: // can start the write now sl@0: iWriteState=EWriting; sl@0: WriteStep(); sl@0: } sl@0: } sl@0: sl@0: void DMediaDriverFlashTemplate::DoFlashReady(TUint32 aStatus) sl@0: { sl@0: // could be write completion, erase completion or suspend completion sl@0: if (iWriteState==EWriting) sl@0: { sl@0: // write completion sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:WriteComplete %08x",aStatus)); sl@0: TUint32 err=aStatus & (KStsWriteError|KStsVppLow|KStsLocked); sl@0: if (err) sl@0: { sl@0: iWriteState=EWriteIdle; sl@0: Complete(EReqWrite,KErrGeneral); sl@0: if (iEraseState==ESuspended) sl@0: StartErase(); sl@0: } sl@0: else sl@0: WriteStep(); sl@0: return; sl@0: } sl@0: sl@0: // put the FLASH back into read mode sl@0: volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iErasePos); sl@0: *pF=KCmdReadArray; sl@0: sl@0: if (iEraseState==ESuspending) sl@0: { sl@0: // erase suspend completion sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:SuspendComplete %08x",aStatus)); sl@0: sl@0: // accumulate errors during erase sl@0: iEraseError|=(aStatus & (KStsEraseError|KStsVppLow|KStsLocked)); sl@0: sl@0: if (aStatus & KStsSuspended) sl@0: { sl@0: // at least one of the two FLASH devices has suspended sl@0: CHANGE_ERASE_STATE(ESuspended); sl@0: sl@0: // start any pending read or write requests sl@0: StartPendingRW(); sl@0: return; // in case erase has been resumed by DoRead() sl@0: } sl@0: sl@0: // erase completed before we suspended it sl@0: CHANGE_ERASE_STATE(EErase); sl@0: } sl@0: if (iEraseState==EErase || iEraseState==EEraseNoSuspend) sl@0: { sl@0: // erase completion sl@0: __KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:EraseComplete %08x",aStatus)); sl@0: CancelHoldOffTimer(); sl@0: sl@0: // accumulate errors during erase sl@0: iEraseError|=(aStatus & (KStsEraseError|KStsVppLow|KStsLocked)); sl@0: sl@0: TFLASHWORD x = FLASH_ERASE_WORD_VALUE; sl@0: sl@0: // if no device error, verify that erase was successful sl@0: if (!iEraseError) sl@0: { sl@0: volatile TFLASHWORD* p=pF; sl@0: volatile TFLASHWORD* pE=p + FLASH_BYTES_TO_WORDS(iEraseBlockSize); sl@0: while(p