os/kernelhwsrv/bsptemplate/asspandvariant/template_variant/specific/lffsdev.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // template\Template_Variant\Specific\lffsdev.cpp
    15 // Implementation of a Logging Flash file system (LFFS) physical device driver 
    16 // for a standard Common Flash Interface (CFI) based NOR flash chip.
    17 // This file is part of the Template Base port
    18 // N.B. This sample code assumes that:
    19 // (1)	the device does not provide an interrupt i.e. it needs to be polled using a timer 
    20 // to ascertain when an Erase/Write operation has completed.
    21 // (2) the flash chip does not have 'read-while-write' support.
    22 // 
    23 //
    24 
    25 #include "lffsdev.h"
    26 #include "variant.h"
    27 
    28 #ifdef _DEBUG
    29 #define CHANGE_ERASE_STATE(x)	{TUint32 s=iEraseState; iEraseState=x; __KTRACE_OPT(KLOCDRV,Kern::Printf("ErSt: %d->%d",s,x));}
    30 #else
    31 #define CHANGE_ERASE_STATE(x)	iEraseState=x
    32 #endif
    33 
    34 //
    35 // TO DO: (mandatory)
    36 //
    37 // Define the pyhsical base address of the NOR-Flash
    38 // This is only example code... you will need to modify it for your hardware
    39 const TPhysAddr KFlashPhysicalBaseAddress = 0x04000000;
    40 
    41 
    42 /********************************************
    43  * Common Flash Interface (CFI) query stuff
    44  ********************************************/
    45 
    46 /**
    47 Read an 8-bit value from the device at the specified offset
    48 
    49 @param	aOffset	the address in device words 
    50 */
    51 TUint32 DMediaDriverFlashTemplate::ReadQueryData8(TUint32 aOffset)
    52 	{
    53 	volatile TUint8* pF=(volatile TUint8*)(iBase+FLASH_ADDRESS_IN_BYTES(aOffset));
    54 	return pF[0];
    55 	}
    56 
    57 /**
    58 Read a 16-bit value from the device at the specified offset
    59 
    60 @param	aOffset	the address in device words 
    61 */
    62 TUint32 DMediaDriverFlashTemplate::ReadQueryData16(TUint32 aOffset)
    63 	{
    64 	volatile TUint8* pF=(volatile TUint8*)(iBase);
    65 	return 
    66 		 pF[FLASH_ADDRESS_IN_BYTES(aOffset+0)] | 
    67 		(pF[FLASH_ADDRESS_IN_BYTES(aOffset+1)] << 8);
    68 	}
    69 
    70 /**
    71  Put the device into query mode to read the flash parameters.
    72  */
    73 void DMediaDriverFlashTemplate::ReadFlashParameters()
    74 	{
    75 	volatile TFLASHWORD* pF=(volatile TFLASHWORD*)iBase + KCmdReadQueryOffset;
    76 	*pF=KCmdReadQuery;
    77 
    78 	TUint32 qd=ReadQueryData16(KQueryOffsetQRY)|(ReadQueryData8(KQueryOffsetQRY+2)<<16);
    79 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query QRY=%08x",qd));
    80 	__ASSERT_ALWAYS(qd==0x595251,FLASH_FAULT());
    81 
    82 	qd = FLASH_BUS_DEVICES << ReadQueryData8(KQueryOffsetSizePower);
    83 
    84 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query Size=%08x",qd));
    85 	iTotalSize=qd;
    86 
    87 	qd = FLASH_BUS_DEVICES << ReadQueryData16(KQueryOffsetWriteBufferSizePower);
    88 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query WBSize=%08x",qd));
    89 	iWriteBufferSize=qd;
    90 
    91 	qd = (ReadQueryData16(KQueryOffsetEraseBlockSize)) << (8 + FLASH_BUS_DEVICES-1);
    92 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Query EBSize=%08x",qd));
    93 	iEraseBlockSize=qd;
    94 
    95 	*pF=KCmdReadArray;
    96 	}
    97 
    98 
    99 /********************************************
   100  * Common Flash Interface (CFI) main code
   101  ********************************************/
   102 
   103 /**
   104 NOR flash LFFS constructor.
   105 
   106 @param aMediaId            Media id number from ELOCD
   107 */
   108 DMediaDriverFlashTemplate::DMediaDriverFlashTemplate(TInt aMediaId)
   109 	:	DMediaDriverFlash(aMediaId),
   110 		iHoldOffTimer(HoldOffTimerFn,this),
   111 		iEventDfc(EventDfc,this,NULL,2)
   112 	{
   113 	// iWriteState = EWriteIdle;
   114 	// iEraseState = EEraseIdle;
   115 	}
   116 
   117 /**
   118 Device specific implementation of the NOR LFFS initialisation routine.
   119 
   120 @see DMediaDriverFlash::Initialise
   121 @return KErrNone unless the write data buffer couldn't be allocated or the
   122          timer interrupt could not be bound.
   123  */
   124 TInt DMediaDriverFlashTemplate::Initialise()
   125 	{
   126 	iEventDfc.SetDfcQ(iPrimaryMedia->iDfcQ);
   127 	iData=(TUint8*)Kern::Alloc(KDataBufSize);
   128 	if (!iData)
   129 		return KErrNoMemory;
   130 
   131 	// Create temporary HW chunk to read FLASH device parameters (especially size)
   132 	DPlatChunkHw* pC = NULL;
   133 	TInt r = DPlatChunkHw::New(pC, KFlashPhysicalBaseAddress, 0x1000, EMapAttrSupRw|EMapAttrFullyBlocking);
   134 	if (r!=KErrNone)
   135 		return r;
   136 	iBase = pC->LinearAddress();
   137 	ReadFlashParameters();
   138 	// close temporary chunk and open chunk with correct size
   139 	pC->Close(NULL);
   140 	r = DPlatChunkHw::New(iFlashChunk, KFlashPhysicalBaseAddress, iTotalSize, EMapAttrSupRw|EMapAttrFullyBlocking);
   141 	if (r!=KErrNone)
   142 		return r;
   143 	iBase = iFlashChunk->LinearAddress();
   144 
   145 	r=Interrupt::Bind(KIntIdTimer1, Isr, this);
   146 	if (r!=KErrNone)
   147 		{
   148 		__KTRACE_OPT(KLOCDRV, Kern::Printf("Flash:Isr Bind failed"));
   149 		return r;
   150 		}
   151 
   152 	// TO DO: (mandatory)
   153 	// Write to the appropriate hardware register(s) to
   154 	// configure (if necessary) and enable the timer hardware
   155 	//
   156 
   157 	// Enable the timer interrupt
   158 	Interrupt::Enable(KIntIdTimer1);
   159 	
   160 	return KErrNone;
   161 	}
   162 
   163 /**
   164 Used by the generic flash media driver code to get the erase block size in
   165 bytes. 
   166  */
   167 TUint32 DMediaDriverFlashTemplate::EraseBlockSize()
   168 	{
   169 	return iEraseBlockSize;
   170 	}
   171 
   172 /**
   173 @return Return size of lffs in bytes
   174 */
   175 TUint32 DMediaDriverFlashTemplate::TotalSize()
   176 	{
   177 	return iTotalSize;
   178 	}
   179 
   180 /**
   181 Read at the location indicated by DMediaDriverFlash::iReadReq. 
   182 Where Pos() is the read location
   183 
   184 @return >0			Defer request to ELOCD. A write is in progress
   185 @return KErrNone	Erase has been started
   186 @return <0			An error has occured.
   187 */
   188 TInt DMediaDriverFlashTemplate::DoRead()
   189 	{
   190 	if (iWriteReq)
   191 		return KMediaDriverDeferRequest;	// write in progress so defer read
   192 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoRead"));
   193 	if (iEraseState==EEraseIdle || iEraseState==ESuspended)
   194 		{
   195 		// can do the read now
   196 		TInt pos=(TInt)iReadReq->Pos();
   197 		TInt len=(TInt)iReadReq->Length();
   198 
   199 		__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoRead ibase: %x, pos: %x, len: %x",iBase,pos,len));
   200 
   201 		// Issue a read array command
   202 		// Porting note: Some devices may work without this step.
   203 		// Ensure that the write is always dword aligned
   204 		volatile TFLASHWORD* pF=(volatile TFLASHWORD*)((iBase+pos)&0xFFFFFFF0);
   205 		*pF=KCmdReadArray;
   206 
   207 		TPtrC8 des((const TUint8*)(iBase+pos),len);
   208 		TInt r=iReadReq->WriteRemote(&des,0);
   209 		Complete(EReqRead,r);
   210 
   211 		// resume erase if necessary
   212 		if (iEraseState==ESuspended)
   213 			StartErase();
   214 		}
   215 	else if (iEraseState==EErase)
   216 		{
   217 		// erase in progress - suspend it
   218 		SuspendErase();
   219 		}
   220 	else if (iEraseState==EEraseNoSuspend)
   221 		CHANGE_ERASE_STATE(ESuspendPending);	// wait for suspend to complete
   222 	
   223 	
   224 	return KErrNone;
   225 	}
   226 
   227 /**
   228 Write at the location indicated by DMediaDriverFlash::iWriteReq
   229 
   230 @return >0			Defer request to ELOCD. A read is in progress
   231 @return KErrNone	Erase has been started
   232 @return <0			An error has occured.
   233  */
   234 TInt DMediaDriverFlashTemplate::DoWrite()
   235 	{
   236 	if (iReadReq)
   237 		return KMediaDriverDeferRequest;	// read in progress so defer write
   238 
   239 	TInt pos=(TInt)iWriteReq->Pos();
   240 	TInt len=(TInt)iWriteReq->Length();
   241 	if (len==0)
   242 		return KErrCompletion;
   243 	TUint32 wb_mask=iWriteBufferSize-1;
   244 	iWritePos=pos & ~wb_mask;	// round media position down to write buffer boundary
   245 	TInt wb_off=pos & wb_mask;	// how many bytes of padding at beginning
   246 	TInt start_len=Min(len,KDataBufSize-(TInt)wb_off);
   247 	TInt write_len=(start_len+wb_off+wb_mask)&~wb_mask;
   248 	memset(iData,0xff,iWriteBufferSize);
   249 	memset(iData+write_len-iWriteBufferSize,0xff,iWriteBufferSize);
   250 	TPtr8 des(iData+wb_off,0,start_len);
   251 	TInt r=iWriteReq->ReadRemote(&des,0);
   252 	if (r!=KErrNone)
   253 		return r;
   254 	iWriteReq->RemoteDesOffset()+=start_len;
   255 	iWriteReq->Length()-=start_len;
   256 	iDataBufPos=0;
   257 	iDataBufRemain=write_len;
   258 	iWriteError=KErrNone;
   259 
   260 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Write iWritePos=%08x iDataBufRemain=%x",iWritePos,iDataBufRemain));
   261 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Write Pos=%08x Length=%08x RemDesOff=%08x",
   262 										(TInt)iWriteReq->Pos(),(TInt)iWriteReq->Length(),iWriteReq->RemoteDesOffset()));
   263 
   264 	if (iEraseState==EEraseIdle || iEraseState==ESuspended)
   265 		{
   266 		// can start the write now
   267 		iWriteState=EWriting;
   268 		WriteStep();
   269 		}
   270 	else if (iEraseState==EErase)
   271 		{
   272 		// erase in progress - suspend it
   273 		SuspendErase();
   274 		}
   275 	else if (iEraseState==EEraseNoSuspend)
   276 		CHANGE_ERASE_STATE(ESuspendPending);	// wait for suspend to complete
   277 	
   278 	return KErrNone;
   279 	}
   280 
   281 void DMediaDriverFlashTemplate::WriteStep()
   282 	{
   283 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:WriteStep @%08x",iWritePos));
   284 	if (iDataBufRemain)
   285 		{
   286 		// still data left in buffer
   287 		volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iWritePos);
   288 		TInt i=KMaxWriteSetupAttempts;
   289 		*pF=KCmdClearStatusRegister;
   290 		TUint32 s=0;
   291 		for (; i>0 && ((s&KStsReady)!=KStsReady); --i)
   292 			{
   293 			*pF=KCmdWriteToBuffer;		// send write command
   294 			*pF=KCmdReadStatusRegister;	// send read status command
   295 			s=*pF;						// read status reg
   296 			}
   297 		__KTRACE_OPT(KLOCDRV,Kern::Printf("i=%d, s=%08x",i,s));
   298 
   299 		// calculate the buffer size in words -1 
   300 		TFLASHWORD l = (FLASH_BYTES_TO_WORDS(iWriteBufferSize)) - 1;
   301 
   302 #if FLASH_BUS_DEVICES == 2		// 2x16bit or 2x8bit devices
   303 		l|= l<< BUS_WIDTH_PER_DEVICE;
   304 #elif FLASH_BUS_DEVICES == 4	// 4x8bit device
   305 		l|= (l<<BUS_WIDTH_PER_DEVICE) | (l<<BUS_WIDTH_PER_DEVICE*2) (l<<BUS_WIDTH_PER_DEVICE*3);
   306 #endif
   307 
   308 		// write the data length in words to the device(s)
   309 		*pF=l;
   310 
   311 		const TFLASHWORD* pS=(const TFLASHWORD*)(iData+iDataBufPos);
   312 
   313 		// write the data
   314 		TInt len;
   315 		for (len = l; len>=0; len--)
   316 			{
   317 			*pF++=*pS++;
   318 			}
   319 	
   320 		*(volatile TFLASHWORD *)(iBase+iWritePos) = KCmdConfirm; 
   321 
   322 		// set up timer to poll for completion
   323 		StartPollTimer(KFlashWriteTimerPeriod,KFlashWriteTimerRetries);
   324 
   325 		iWritePos+=iWriteBufferSize;
   326 		iDataBufPos+=iWriteBufferSize;
   327 		iDataBufRemain-=iWriteBufferSize;
   328 		if (!iDataBufRemain)
   329 			{
   330 			// refill buffer
   331 			TInt len=(TInt)iWriteReq->Length();
   332 			if (!len)
   333 				return;	// all data has been written, complete request next time
   334 			TUint32 wb_mask=iWriteBufferSize-1;
   335 			TInt block_len=Min(len,KDataBufSize);
   336 			TInt write_len=(block_len+wb_mask)&~wb_mask;
   337 			memset(iData+write_len-iWriteBufferSize,0xff,iWriteBufferSize);
   338 			TPtr8 des(iData,0,block_len);
   339 			TInt r=iWriteReq->ReadRemote(&des,0);
   340 			if (r!=KErrNone)
   341 				{
   342 				iWriteError=r;
   343 				return;	// leave iDataBufRemain=0 so request is terminated when write completes
   344 				}
   345 			iWriteReq->RemoteDesOffset()+=block_len;
   346 			iWriteReq->Length()-=block_len;
   347 			iDataBufPos=0;
   348 			iDataBufRemain=write_len;
   349 			}
   350 		}
   351 	else
   352 		{
   353 		// write request should have completed, maybe with an error
   354 		__ASSERT_ALWAYS(iWriteReq->Length()==0 || iWriteError,FLASH_FAULT());
   355 		iWriteState=EWriteIdle;
   356 		Complete(EReqWrite,iWriteError);
   357 		if (iEraseState==ESuspended)
   358 			StartErase();
   359 		}
   360 	}
   361 
   362 /**
   363 Erase at the location indicated by DMediaDriverFlash::iEraseReq
   364 
   365 @return >0			Defer request to ELOCD. Read or a write is in progress
   366 @return KErrNone	Erase has been started
   367 @return <0			An error has occured.
   368  */
   369 TInt DMediaDriverFlashTemplate::DoErase()
   370 	{
   371 	if (iReadReq || iWriteReq)
   372 		return KMediaDriverDeferRequest;		// read or write in progress so defer this request
   373 	TUint32 pos=(TUint32)iEraseReq->Pos();
   374 	TUint32 len=(TUint32)iEraseReq->Length();
   375 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoErase %d@%08x",len,pos));
   376 	if (len!=iEraseBlockSize)
   377 		return KErrArgument;	// only allow single-block erase
   378 	if (pos & (iEraseBlockSize-1))
   379 		return KErrArgument;	// start position must be on erase block boundary
   380 	iErasePos=pos;
   381 	__ASSERT_ALWAYS(iEraseState==EEraseIdle,FLASH_FAULT());
   382 	StartErase();
   383 	return KErrNone;
   384 	}
   385 
   386 void DMediaDriverFlashTemplate::StartHoldOffTimer()
   387 	{
   388 	// if this is a retry, don't allow suspends
   389 	if (iEraseAttempt==0)
   390 		iHoldOffTimer.OneShot(KEraseSuspendHoldOffTime);
   391 	}
   392 
   393 void DMediaDriverFlashTemplate::CancelHoldOffTimer()
   394 	{
   395 	iHoldOffTimer.Cancel();
   396 	ClearEvents(EHoldOffEnd);
   397 	}
   398 
   399 void DMediaDriverFlashTemplate::ClearEvents(TUint32 aEvents)
   400 	{
   401 	__e32_atomic_and_ord32(&iEvents, ~aEvents);
   402 	}
   403 
   404 void DMediaDriverFlashTemplate::HoldOffTimerFn(TAny* aPtr)
   405 	{
   406 	DMediaDriverFlashTemplate* p=(DMediaDriverFlashTemplate*)aPtr;
   407 	p->IPostEvents(EHoldOffEnd);
   408 	}
   409 
   410 void DMediaDriverFlashTemplate::StartPollTimer(TUint32 aPeriod, TUint32 aRetries)
   411 	{
   412 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Tmr %d * %d",aPeriod,aRetries));
   413 
   414 	ClearEvents(EPollTimer);
   415 	iPollPeriod=aPeriod;
   416 	iPollRetries=aRetries;
   417 	StartPollTimer();
   418 	}
   419 
   420 void DMediaDriverFlashTemplate::StartPollTimer()
   421 	{
   422 	// TO DO: (mandatory)
   423 	// Configure the hardware timer to expire after iPollPeriod ticks
   424 	// and start the timer
   425 	
   426 	}
   427 
   428 void DMediaDriverFlashTemplate::EventDfc(TAny* aPtr)
   429 	{
   430 	DMediaDriverFlashTemplate* p=(DMediaDriverFlashTemplate*)aPtr;
   431 	TUint32 e = __e32_atomic_swp_ord32(&p->iEvents, 0);
   432 	if (e)
   433 		p->HandleEvents(e);
   434 	}
   435 
   436 void DMediaDriverFlashTemplate::HandleEvents(TUint32 aEvents)
   437 	{
   438 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Events %x",aEvents));
   439 	if (aEvents & EHoldOffEnd)
   440 		{
   441 		if (iEraseState==ESuspendPending)
   442 			{
   443 			SuspendErase();
   444 			}
   445 		else if (iEraseState==EEraseNoSuspend)
   446 			{
   447 			CHANGE_ERASE_STATE(EErase);	// can now be suspended
   448 			}
   449 		else
   450 			{
   451 			__KTRACE_OPT(KPANIC,Kern::Printf("iEraseState=%d",iEraseState));
   452 			FLASH_FAULT();
   453 			}
   454 		}
   455 	if (aEvents & EPollTimer)
   456 		{
   457 		volatile TFLASHWORD* pF=(volatile TFLASHWORD*)iBase;
   458 		*pF=KCmdReadStatusRegister;
   459 		if ((*pF & KStsReady)!=KStsReady)
   460 			{
   461 			// not ready yet
   462 			if (--iPollRetries)
   463 				{
   464 				// try again
   465 				StartPollTimer();
   466 				}
   467 			else
   468 				// timed out
   469 				aEvents|=ETimeout;
   470 			}
   471 		else
   472 			{
   473 			// ready
   474 			TFLASHWORD s=*pF;	// read full status value
   475 			*pF=KCmdClearStatusRegister;
   476 			DoFlashReady(s);
   477 			}
   478 		}
   479 	if (aEvents & ETimeout)
   480 		{
   481 		DoFlashTimeout();
   482 		}
   483 	}
   484 
   485 void DMediaDriverFlashTemplate::StartErase()
   486 	{
   487 	TFLASHWORD s=KStsReady;
   488 	TInt i;
   489 	volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iErasePos);
   490 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:StartErase %08x",pF));
   491 	switch (iEraseState)
   492 		{
   493 		case EEraseIdle:	// first attempt to erase
   494 			iEraseAttempt=-1;
   495 			// coverity[fallthrough]
   496 			// fallthrough after attempt
   497 		case EErase:	// retry after verify failed
   498 		case EEraseNoSuspend:
   499 			++iEraseAttempt;
   500 			*pF=KCmdBlockErase;
   501 			*pF=KCmdConfirm;
   502 			CHANGE_ERASE_STATE(EEraseNoSuspend);
   503 			iEraseError=0;
   504 			StartHoldOffTimer();
   505 			break;
   506 		case ESuspended:
   507 			*pF=KCmdClearStatusRegister;
   508 			*pF=KCmdEraseResume;
   509 			CHANGE_ERASE_STATE(EEraseNoSuspend);
   510 			i=KMaxEraseResumeAttempts;
   511 			for (; i>0 && ((s&KStsReady)!=0); --i)
   512 				{
   513 				*pF=KCmdReadStatusRegister;	// send read status command
   514 				s=*pF;						// read status reg
   515 				s=*pF;						// read status reg
   516 				}
   517 			__KTRACE_OPT(KLOCDRV,Kern::Printf("RESUME: i=%d, s=%08x",i,s));
   518 			StartHoldOffTimer();
   519 			break;
   520 		default:
   521 			__KTRACE_OPT(KPANIC,Kern::Printf("iEraseState=%d",iEraseState));
   522 			FLASH_FAULT();
   523 		}
   524 	StartPollTimer(KFlashEraseTimerPeriod,KFlashEraseTimerRetries);
   525 	}
   526 
   527 void DMediaDriverFlashTemplate::SuspendErase()
   528 	{
   529 	__ASSERT_ALWAYS(iEraseState==EErase || iEraseState==ESuspendPending,FLASH_FAULT());
   530 	volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iErasePos);
   531 	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:SuspendErase %08x",pF));
   532 	*pF=KCmdEraseSuspend;
   533 	CHANGE_ERASE_STATE(ESuspending);
   534 	StartPollTimer(KFlashSuspendTimerPeriod,KFlashSuspendTimerRetries);
   535 	}
   536 
   537 void DMediaDriverFlashTemplate::StartPendingRW()
   538 	{
   539 	// start any pending read or write requests
   540 	if (iReadReq)
   541 		DoRead();
   542 	if (iWriteReq)
   543 		{
   544 		// can start the write now
   545 		iWriteState=EWriting;
   546 		WriteStep();
   547 		}
   548 	}
   549 
   550 void DMediaDriverFlashTemplate::DoFlashReady(TUint32 aStatus)
   551 	{
   552 	// could be write completion, erase completion or suspend completion
   553 	if (iWriteState==EWriting)
   554 		{
   555 		// write completion
   556 		__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:WriteComplete %08x",aStatus));
   557 		TUint32 err=aStatus & (KStsWriteError|KStsVppLow|KStsLocked);
   558 		if (err)
   559 			{
   560 			iWriteState=EWriteIdle;
   561 			Complete(EReqWrite,KErrGeneral);
   562 			if (iEraseState==ESuspended)
   563 				StartErase();
   564 			}
   565 		else
   566 			WriteStep();
   567 		return;
   568 		}
   569 
   570 	// put the FLASH back into read mode
   571 	volatile TFLASHWORD* pF=(volatile TFLASHWORD*)(iBase+iErasePos);
   572 	*pF=KCmdReadArray;
   573 
   574 	if (iEraseState==ESuspending)
   575 		{
   576 		// erase suspend completion
   577 		__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:SuspendComplete %08x",aStatus));
   578 
   579 		// accumulate errors during erase
   580 		iEraseError|=(aStatus & (KStsEraseError|KStsVppLow|KStsLocked));
   581 
   582 		if (aStatus & KStsSuspended)
   583 			{
   584 			// at least one of the two FLASH devices has suspended
   585 			CHANGE_ERASE_STATE(ESuspended);
   586 
   587 			// start any pending read or write requests
   588 			StartPendingRW();
   589 			return;					// in case erase has been resumed by DoRead()
   590 			}
   591 
   592 		// erase completed before we suspended it
   593 		CHANGE_ERASE_STATE(EErase);
   594 		}
   595 	if (iEraseState==EErase || iEraseState==EEraseNoSuspend)
   596 		{
   597 		// erase completion
   598 		__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:EraseComplete %08x",aStatus));
   599 		CancelHoldOffTimer();
   600 
   601 		// accumulate errors during erase
   602 		iEraseError|=(aStatus & (KStsEraseError|KStsVppLow|KStsLocked));
   603 
   604 		TFLASHWORD x = FLASH_ERASE_WORD_VALUE;
   605 
   606 		// if no device error, verify that erase was successful
   607 		if (!iEraseError)
   608 			{
   609 			volatile TFLASHWORD* p=pF;
   610 			volatile TFLASHWORD* pE=p + FLASH_BYTES_TO_WORDS(iEraseBlockSize);
   611 			while(p<pE)
   612 				x&=*p++;
   613 			}
   614 		else
   615 			{
   616 			}
   617 		if (x == FLASH_ERASE_WORD_VALUE)
   618 			{
   619 			// erase OK
   620 			__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:VerifyErase OK"));
   621 			CHANGE_ERASE_STATE(EEraseIdle);
   622 
   623 			// complete the erase request
   624 			TInt r=iEraseError?KErrGeneral:KErrNone;
   625 			Complete(EReqErase,r);
   626 
   627 			// start any pending read or write requests
   628 			StartPendingRW();
   629 			}
   630 		else
   631 			{
   632 			// erase failed, so retry
   633 			__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:VerifyErase BAD"));
   634 			StartErase();
   635 			}
   636 		}
   637 	}
   638 
   639 void DMediaDriverFlashTemplate::DoFlashTimeout()
   640 	{
   641 	// TO DO: (optional)
   642 	// Take appropriate action to handle a timeout.
   643 	FLASH_FAULT();	// // EXAMPLE ONLY:
   644 	}
   645 
   646 DMediaDriverFlash* DMediaDriverFlash::New(TInt aMediaId)
   647 	{
   648 	return new DMediaDriverFlashTemplate(aMediaId);
   649 	}
   650 
   651 void DMediaDriverFlashTemplate::Isr(TAny* aPtr)
   652 	{
   653 	DMediaDriverFlashTemplate& d=*(DMediaDriverFlashTemplate*)aPtr;
   654 
   655 	
   656 	// TO DO: (mandatory)
   657 	// Write to the timer hardware register(s) to
   658 	// clear the timer interrupt
   659 	//
   660 	
   661 	d.IPostEvents(EPollTimer);
   662 	}
   663 
   664 void DMediaDriverFlashTemplate::IPostEvents(TUint32 aEvents)
   665 	{
   666 	iEvents|=aEvents;
   667 	iEventDfc.Add();
   668 	}