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