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