|
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 } |