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