]>
Commit | Line | Data |
---|---|---|
1d5d0ae9 | 1 | /** @file NorFlashDxe.c |
2 | ||
3 | Copyright (c) 2010, ARM Ltd. All rights reserved.<BR> | |
4 | This program and the accompanying materials | |
5 | are licensed and made available under the terms and conditions of the BSD License | |
6 | which accompanies this distribution. The full text of the license may be found at | |
7 | http://opensource.org/licenses/bsd-license.php | |
8 | ||
9 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
10 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
11 | ||
12 | **/ | |
13 | ||
14 | #include <Library/UefiLib.h> | |
15 | #include <Library/DebugLib.h> | |
16 | #include <Library/BaseMemoryLib.h> | |
17 | #include <Library/MemoryAllocationLib.h> | |
18 | #include <Library/UefiBootServicesTableLib.h> | |
19 | ||
20 | #include "NorFlashDxe.h" | |
21 | ||
22 | ||
23 | // | |
24 | // Global variable declarations | |
25 | // | |
26 | ||
27 | #define NOR_FLASH_LAST_DEVICE 4 | |
28 | ||
29 | NOR_FLASH_DESCRIPTION mNorFlashDescription[NOR_FLASH_LAST_DEVICE] = { | |
30 | { // BootMon | |
31 | ARM_VE_SMB_NOR0_BASE, | |
32 | SIZE_256KB * 255, | |
33 | SIZE_256KB, | |
34 | FALSE, | |
35 | {0xE7223039, 0x5836, 0x41E1, 0xB5, 0x42, 0xD7, 0xEC, 0x73, 0x6C, 0x5E, 0x59} | |
36 | }, | |
37 | { // BootMon non-volatile storage | |
38 | ARM_VE_SMB_NOR0_BASE + SIZE_256KB * 255, | |
39 | SIZE_64KB * 4, | |
40 | SIZE_64KB, | |
41 | FALSE, | |
42 | {0x02118005, 0x9DA7, 0x443A, 0x92, 0xD5, 0x78, 0x1F, 0x02, 0x2A, 0xED, 0xBB} | |
43 | }, | |
44 | { // UEFI | |
45 | ARM_VE_SMB_NOR1_BASE, | |
46 | SIZE_256KB * 255, | |
47 | SIZE_256KB, | |
48 | FALSE, | |
49 | {0x1F15DA3C, 0x37FF, 0x4070, 0xB4, 0x71, 0xBB, 0x4A, 0xF1, 0x2A, 0x72, 0x4A} | |
50 | }, | |
51 | { // UEFI Variable Services non-volatile storage | |
52 | ARM_VE_SMB_NOR1_BASE + SIZE_256KB * 255, | |
53 | SIZE_64KB * 3, //FIXME: Set 3 blocks because I did not succeed to copy 4 blocks into the ARM Versastile Express NOR Falsh in the last NOR Flash. It should be 4 blocks | |
54 | SIZE_64KB, | |
55 | TRUE, | |
56 | {0xCC2CBF29, 0x1498, 0x4CDD, 0x81, 0x71, 0xF8, 0xB6, 0xB4, 0x1D, 0x09, 0x09} | |
57 | } | |
58 | }; | |
59 | ||
60 | NOR_FLASH_INSTANCE *mNorFlashInstances[ NOR_FLASH_LAST_DEVICE ]; | |
61 | ||
62 | NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { | |
63 | NOR_FLASH_SIGNATURE, // Signature | |
64 | NULL, // Handle ... NEED TO BE FILLED | |
65 | ||
66 | FALSE, // Initialized | |
67 | NULL, // Initialize | |
68 | ||
69 | 0, // BaseAddress ... NEED TO BE FILLED | |
70 | 0, // Size ... NEED TO BE FILLED | |
71 | ||
72 | {\r | |
73 | EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision\r | |
74 | NULL, // Media ... NEED TO BE FILLED\r | |
75 | NorFlashBlockIoReset, // Reset;\r | |
76 | NorFlashBlockIoReadBlocks, // ReadBlocks | |
77 | NorFlashBlockIoWriteBlocks, // WriteBlocks | |
78 | NorFlashBlockIoFlushBlocks // FlushBlocks\r | |
79 | }, // BlockIoProtocol | |
80 | ||
81 | {\r | |
82 | 0, // MediaId ... NEED TO BE FILLED\r | |
83 | FALSE, // RemovableMedia\r | |
84 | TRUE, // MediaPresent\r | |
85 | FALSE, // LogicalPartition\r | |
86 | FALSE, // ReadOnly\r | |
87 | FALSE, // WriteCaching;\r | |
88 | 0, // BlockSize ... NEED TO BE FILLED\r | |
89 | 4, // IoAlign\r | |
90 | 0, // LastBlock ... NEED TO BE FILLED\r | |
91 | 0, // LowestAlignedLba\r | |
92 | 1, // LogicalBlocksPerPhysicalBlock\r | |
93 | }, //Media; | |
94 | ||
95 | FALSE, // SupportFvb ... NEED TO BE FILLED | |
96 | {\r | |
97 | FvbGetAttributes, // GetAttributes | |
98 | FvbSetAttributes, // SetAttributes | |
99 | FvbGetPhysicalAddress, // GetPhysicalAddress | |
100 | FvbGetBlockSize, // GetBlockSize | |
101 | FvbRead, // Read | |
102 | FvbWrite, // Write | |
103 | FvbEraseBlocks, // EraseBlocks\r | |
104 | NULL, //ParentHandle\r | |
105 | }, // FvbProtoccol; | |
106 | ||
107 | { | |
108 | { | |
109 | { | |
110 | HARDWARE_DEVICE_PATH, | |
111 | HW_VENDOR_DP, | |
112 | (UINT8)( sizeof(VENDOR_DEVICE_PATH) ), | |
113 | (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8), | |
114 | }, | |
115 | { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, // GUID ... NEED TO BE FILLED | |
116 | }, | |
117 | { | |
118 | END_DEVICE_PATH_TYPE, | |
119 | END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
120 | sizeof (EFI_DEVICE_PATH_PROTOCOL), | |
121 | 0 | |
122 | } | |
123 | } // DevicePath | |
124 | }; | |
125 | ||
126 | EFI_STATUS NorFlashCreateInstance( | |
127 | IN UINTN NorFlashBase, | |
128 | IN UINTN NorFlashSize, | |
129 | IN UINT32 MediaId, | |
130 | IN UINT32 BlockSize, | |
131 | IN BOOLEAN SupportFvb, | |
132 | IN CONST GUID *NorFlashGuid, | |
133 | OUT NOR_FLASH_INSTANCE** NorFlashInstance | |
134 | ) { | |
135 | EFI_STATUS Status; | |
136 | NOR_FLASH_INSTANCE* Instance; | |
137 | ||
138 | ASSERT(NorFlashInstance != NULL); | |
139 | ||
140 | Instance = AllocateCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate); | |
141 | if (Instance == NULL) { | |
142 | return EFI_OUT_OF_RESOURCES; | |
143 | } | |
144 | ||
145 | Instance->BaseAddress = NorFlashBase; | |
146 | Instance->Size = NorFlashSize; | |
147 | ||
148 | Instance->BlockIoProtocol.Media = &Instance->Media; | |
149 | Instance->Media.MediaId = MediaId; | |
150 | Instance->Media.BlockSize = BlockSize; | |
151 | Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1; | |
152 | \r | |
153 | CopyGuid (&Instance->DevicePath.Vendor.Guid,NorFlashGuid);\r | |
154 | ||
155 | if (SupportFvb) { | |
156 | Instance->SupportFvb = TRUE; | |
157 | Instance->Initialize = NorFlashFvbInitialize; | |
158 | ||
159 | Status = gBS->InstallMultipleProtocolInterfaces ( | |
160 | &Instance->Handle, | |
161 | &gEfiDevicePathProtocolGuid, &Instance->DevicePath, | |
162 | //&gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol, | |
163 | &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol, | |
164 | NULL | |
165 | ); | |
166 | if (EFI_ERROR(Status)) { | |
167 | FreePool(Instance); | |
168 | return Status; | |
169 | } | |
170 | } else { | |
171 | Instance->Initialize = NorFlashBlkIoInitialize; | |
172 | ||
173 | Status = gBS->InstallMultipleProtocolInterfaces ( | |
174 | &Instance->Handle, | |
175 | &gEfiDevicePathProtocolGuid, &Instance->DevicePath, | |
176 | &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol, | |
177 | NULL | |
178 | ); | |
179 | if (EFI_ERROR(Status)) { | |
180 | FreePool(Instance); | |
181 | return Status; | |
182 | } | |
183 | } | |
184 | \r | |
185 | *NorFlashInstance = Instance;\r | |
186 | return Status;\r | |
187 | } | |
188 | ||
189 | EFI_STATUS | |
190 | NorFlashReadCfiData ( | |
191 | IN UINTN BaseAddress, | |
192 | IN UINTN CFI_Offset, | |
193 | IN UINT32 NumberOfBytes, | |
194 | OUT UINT32 *Data | |
195 | ) | |
196 | { | |
197 | UINT32 CurrentByte; | |
198 | volatile UINTN *ReadAddress; | |
199 | UINT32 ReadData; | |
200 | UINT32 Byte1; | |
201 | UINT32 Byte2; | |
202 | UINT32 CombinedData = 0; | |
203 | EFI_STATUS Status = EFI_SUCCESS; | |
204 | ||
205 | ||
206 | if( NumberOfBytes > 4 ) { | |
207 | // Using 32 bit variable so can only read 4 bytes | |
208 | return EFI_INVALID_PARAMETER; | |
209 | } | |
210 | ||
211 | // First combine the base address with the offset address | |
212 | // to create an absolute read address. | |
213 | // However, because we are in little endian, read from the last address down to the first | |
214 | ReadAddress = CREATE_NOR_ADDRESS( BaseAddress, CFI_Offset ) + NumberOfBytes - 1; | |
215 | ||
216 | // Although each read returns 32 bits, because of the NOR Flash structure, | |
217 | // each 16 bits (16 MSB and 16 LSB) come from two different chips. | |
218 | // When in CFI mode, each chip read returns valid data in only the 8 LSBits; | |
219 | // the 8 MSBits are invalid and can be ignored. | |
220 | // Therefore, each read address returns one byte from each chip. | |
221 | // | |
222 | // Also note: As we are in little endian notation and we are reading | |
223 | // bytes from incremental addresses, we should assemble them in little endian order. | |
224 | for( CurrentByte=0; CurrentByte<NumberOfBytes; CurrentByte++ ) { | |
225 | ||
226 | // Read the bytes from the two chips | |
227 | ReadData = *ReadAddress; | |
228 | ||
229 | // Check the data validity: | |
230 | // The 'Dual Data' function means that | |
231 | // each chip should return identical data. | |
232 | // If that is not the case then we have a problem. | |
233 | Byte1 = GET_LOW_BYTE ( ReadData ); | |
234 | Byte2 = GET_HIGH_BYTE( ReadData ); | |
235 | ||
236 | if( Byte1 != Byte2 ) { | |
237 | // The two bytes should have been identical | |
238 | return EFI_DEVICE_ERROR; | |
239 | } else { | |
240 | ||
241 | // Each successive iteration of the 'for' loop reads a lower address. | |
242 | // As we read lower addresses and as we use little endian, | |
243 | // we read lower significance bytes. So combine them in the correct order. | |
244 | CombinedData = (CombinedData << 8) | Byte1; | |
245 | ||
246 | // Decrement down to the next address | |
247 | ReadAddress--; | |
248 | } | |
249 | } | |
250 | ||
251 | *Data = CombinedData; | |
252 | ||
253 | return Status; | |
254 | } | |
255 | ||
256 | EFI_STATUS | |
257 | NorFlashReadStatusRegister( | |
258 | IN UINTN SR_Address | |
259 | ) | |
260 | { | |
261 | volatile UINT32 *pStatusRegister; | |
262 | UINT32 StatusRegister; | |
263 | UINT32 ErrorMask; | |
264 | EFI_STATUS Status = EFI_SUCCESS; | |
265 | ||
266 | // Prepare the read address | |
267 | pStatusRegister = (UINT32 *) SR_Address; | |
268 | ||
269 | do { | |
270 | // Prepare to read the status register | |
271 | SEND_NOR_COMMAND( SR_Address, 0, P30_CMD_READ_STATUS_REGISTER ); | |
272 | // Snapshot the status register | |
273 | StatusRegister = *pStatusRegister; | |
274 | } | |
275 | // The chip is busy while the WRITE bit is not asserted | |
276 | while ( (StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE ); | |
277 | ||
278 | ||
279 | // Perform a full status check: | |
280 | // Mask the relevant bits of Status Register. | |
281 | // Everything should be zero, if not, we have a problem | |
282 | ||
283 | // Prepare the Error Mask by setting bits 5, 4, 3, 1 | |
284 | ErrorMask = P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM | P30_SR_BIT_VPP | P30_SR_BIT_BLOCK_LOCKED ; | |
285 | ||
286 | if ( (StatusRegister & ErrorMask) != 0 ) { | |
287 | if ( (StatusRegister & P30_SR_BIT_VPP) != 0 ) { | |
288 | DEBUG((EFI_D_ERROR,"NorFlashReadStatusRegister: VPP Range Error\n")); | |
289 | } else if ( (StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM) ) != 0 ) { | |
290 | DEBUG((EFI_D_ERROR,"NorFlashReadStatusRegister: Command Sequence Error\n")); | |
291 | } else if ( (StatusRegister & P30_SR_BIT_PROGRAM) != 0 ) { | |
292 | DEBUG((EFI_D_ERROR,"NorFlashReadStatusRegister: Program Error\n")); | |
293 | } else if ( (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) != 0 ) { | |
294 | DEBUG((EFI_D_ERROR,"NorFlashReadStatusRegister: Device Protect Error\n")); | |
295 | } else {\r | |
296 | DEBUG((EFI_D_ERROR,"NorFlashReadStatusRegister: Error (0x%X)\n",Status));\r | |
297 | } | |
298 | ||
299 | // If an error is detected we must clear the Status Register | |
300 | SEND_NOR_COMMAND( SR_Address, 0, P30_CMD_CLEAR_STATUS_REGISTER ); | |
301 | Status = EFI_DEVICE_ERROR; | |
302 | } | |
303 | ||
304 | SEND_NOR_COMMAND( SR_Address, 0, P30_CMD_READ_ARRAY ); | |
305 | ||
306 | return Status; | |
307 | } | |
308 | ||
309 | ||
310 | BOOLEAN | |
311 | NorFlashBlockIsLocked( | |
312 | IN UINTN BlockAddress | |
313 | ) | |
314 | { | |
315 | volatile UINT32 *pReadData; | |
316 | UINT32 LockStatus; | |
317 | BOOLEAN BlockIsLocked = TRUE; | |
318 | ||
319 | // Prepare the read address | |
320 | pReadData = (UINT32 *) CREATE_NOR_ADDRESS( BlockAddress, 2 ); | |
321 | ||
322 | // Send command for reading device id | |
323 | SEND_NOR_COMMAND( BlockAddress, 2, P30_CMD_READ_DEVICE_ID ); | |
324 | ||
325 | // Read block lock status | |
326 | LockStatus = *pReadData; | |
327 | ||
328 | // Decode block lock status | |
329 | LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus); | |
330 | ||
331 | if( (LockStatus & 0x2) != 0 ) { | |
332 | DEBUG((EFI_D_ERROR, "UnlockSingleBlock: WARNING: Block LOCKED DOWN\n")); | |
333 | } | |
334 | ||
335 | if( (LockStatus & 0x1) == 0 ) { | |
336 | // This means the block is unlocked | |
337 | DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress )); | |
338 | BlockIsLocked = FALSE; | |
339 | } | |
340 | ||
341 | return BlockIsLocked; | |
342 | } | |
343 | ||
344 | ||
345 | EFI_STATUS | |
346 | NorFlashUnlockSingleBlock( | |
347 | IN UINTN BlockAddress | |
348 | ) | |
349 | { | |
350 | EFI_STATUS Status = EFI_SUCCESS; | |
351 | ||
352 | // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations | |
353 | // and to protect shared data structures. | |
354 | ||
355 | //while( NorFlashBlockIsLocked( BlockAddress ) ) | |
356 | { | |
357 | // Request a lock setup | |
358 | SEND_NOR_COMMAND( BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP ); | |
359 | ||
360 | // Request an unlock | |
361 | SEND_NOR_COMMAND( BlockAddress, 0, P30_CMD_UNLOCK_BLOCK ); | |
362 | } | |
363 | ||
364 | // Put device back into Read Array mode | |
365 | SEND_NOR_COMMAND( BlockAddress, 0, P30_CMD_READ_ARRAY ); | |
366 | ||
367 | DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress, Status)); | |
368 | ||
369 | return Status; | |
370 | } | |
371 | ||
372 | ||
373 | EFI_STATUS | |
374 | NorFlashUnlockSingleBlockIfNecessary( | |
375 | IN UINTN BlockAddress | |
376 | ) | |
377 | { | |
378 | EFI_STATUS Status = EFI_SUCCESS; | |
379 | ||
380 | if ( NorFlashBlockIsLocked( BlockAddress ) == TRUE ) { | |
381 | Status = NorFlashUnlockSingleBlock( BlockAddress ); | |
382 | } | |
383 | ||
384 | return Status; | |
385 | } | |
386 | ||
387 | ||
388 | /** | |
389 | * The following function presumes that the block has already been unlocked. | |
390 | **/ | |
391 | EFI_STATUS | |
392 | NorFlashEraseSingleBlock( | |
393 | IN UINTN BlockAddress | |
394 | ) | |
395 | { | |
396 | EFI_STATUS Status = EFI_SUCCESS; | |
397 | ||
398 | // Request a block erase and then confirm it | |
399 | SEND_NOR_COMMAND( BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP ); | |
400 | SEND_NOR_COMMAND( BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM ); | |
401 | // Wait until the status register gives us the all clear | |
402 | Status = NorFlashReadStatusRegister( BlockAddress ); | |
403 | ||
404 | if (EFI_ERROR(Status)) { | |
405 | DEBUG((DEBUG_BLKIO, "EraseSingleBlock(BlockAddress=0x%08x) = '%r'\n", BlockAddress, Status)); | |
406 | } | |
407 | return Status; | |
408 | } | |
409 | ||
410 | /** | |
411 | * The following function presumes that the block has already been unlocked. | |
412 | **/ | |
413 | EFI_STATUS | |
414 | NorFlashUnlockAndEraseSingleBlock( | |
415 | IN UINTN BlockAddress | |
416 | ) | |
417 | { | |
418 | EFI_STATUS Status; | |
419 | ||
420 | // Unlock the block if we have to | |
421 | Status = NorFlashUnlockSingleBlockIfNecessary( BlockAddress ); | |
422 | if (!EFI_ERROR(Status)) { | |
423 | Status = NorFlashEraseSingleBlock( BlockAddress ); | |
424 | } | |
425 | ||
426 | return Status; | |
427 | } | |
428 | ||
429 | ||
430 | EFI_STATUS | |
431 | NorFlashWriteSingleWord ( | |
432 | IN UINTN WordAddress, | |
433 | IN UINT32 WriteData | |
434 | ) | |
435 | { | |
436 | EFI_STATUS Status; | |
437 | volatile UINT32 *Data; | |
438 | ||
439 | // Prepare the read address | |
440 | Data = (UINT32 *)WordAddress; | |
441 | ||
442 | // Request a write single word command | |
443 | SEND_NOR_COMMAND( WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP ); | |
444 | ||
445 | // Store the word into NOR Flash; | |
446 | *Data = WriteData; | |
447 | ||
448 | // Wait for the write to complete and then check for any errors; i.e. check the Status Register | |
449 | Status = NorFlashReadStatusRegister( WordAddress ); | |
450 | ||
451 | return Status; | |
452 | } | |
453 | ||
454 | /* | |
455 | * Writes data to the NOR Flash using the Buffered Programming method. | |
456 | * | |
457 | * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions. | |
458 | * Therefore this function will only handle buffers up to 32 words or 128 bytes. | |
459 | * To deal with larger buffers, call this function again. | |
460 | * | |
461 | * This function presumes that both the TargetAddress and the TargetAddress+BufferSize | |
462 | * exist entirely within the NOR Flash. Therefore these conditions will not be checked here. | |
463 | * | |
464 | * In buffered programming, if the target address not at the beginning of a 32-bit word boundary, | |
465 | * then programming time is doubled and power consumption is increased. | |
466 | * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries. | |
467 | * i.e. the last 4 bits of the target start address must be zero: 0x......00 | |
468 | */ | |
469 | EFI_STATUS | |
470 | NorFlashWriteBuffer ( | |
471 | IN UINTN TargetAddress, | |
472 | IN UINTN BufferSizeInBytes, | |
473 | IN UINT32 *Buffer | |
474 | ) | |
475 | { | |
476 | EFI_STATUS Status; | |
477 | UINTN BufferSizeInWords; | |
478 | UINTN Count; | |
479 | volatile UINT32 *Data; | |
480 | UINTN WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS; | |
481 | BOOLEAN BufferAvailable = FALSE; | |
482 | ||
483 | ||
484 | // Check that the target address does not cross a 32-word boundary. | |
485 | if ( (TargetAddress & BOUNDARY_OF_32_WORDS) != 0 ) { | |
486 | return EFI_INVALID_PARAMETER; | |
487 | } | |
488 | ||
489 | // Check there are some data to program | |
490 | if ( BufferSizeInBytes == 0 ) { | |
491 | return EFI_BUFFER_TOO_SMALL; | |
492 | } | |
493 | ||
494 | // Check that the buffer size does not exceed the maximum hardware buffer size on chip. | |
495 | if ( BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES ) { | |
496 | return EFI_BAD_BUFFER_SIZE; | |
497 | } | |
498 | ||
499 | // Check that the buffer size is a multiple of 32-bit words | |
500 | if ( (BufferSizeInBytes % 4) != 0 ) { | |
501 | return EFI_BAD_BUFFER_SIZE; | |
502 | } | |
503 | ||
504 | // Pre-programming conditions checked, now start the algorithm. | |
505 | ||
506 | // Prepare the data destination address | |
507 | Data = (UINT32 *)TargetAddress; | |
508 | ||
509 | // Check the availability of the buffer | |
510 | do { | |
511 | // Issue the Buffered Program Setup command | |
512 | SEND_NOR_COMMAND( TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP ); | |
513 | ||
514 | // Read back the status register bit#7 from the same address | |
515 | if ( ((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE ) { | |
516 | BufferAvailable = TRUE; | |
517 | } | |
518 | ||
519 | // Update the loop counter | |
520 | WaitForBuffer--; | |
521 | ||
522 | } while (( WaitForBuffer > 0 ) && ( BufferAvailable == FALSE )); | |
523 | ||
524 | // The buffer was not available for writing | |
525 | if ( WaitForBuffer == 0 ) { | |
526 | return EFI_DEVICE_ERROR; | |
527 | } | |
528 | ||
529 | // From now on we work in 32-bit words | |
530 | BufferSizeInWords = BufferSizeInBytes / (UINTN)4; | |
531 | ||
532 | // Write the word count, which is (buffer_size_in_words - 1), | |
533 | // because word count 0 means one word. | |
534 | SEND_NOR_COMMAND( TargetAddress, 0, (BufferSizeInWords - 1) ); | |
535 | ||
536 | // Write the data to the NOR Flash, advancing each address by 4 bytes | |
537 | for( Count=0; Count<BufferSizeInWords; Count++, Data++, Buffer++ ) { | |
538 | *Data = *Buffer; | |
539 | } | |
540 | ||
541 | // Issue the Buffered Program Confirm command, to start the programming operation | |
542 | SEND_NOR_COMMAND( TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM ); | |
543 | ||
544 | // Wait for the write to complete and then check for any errors; i.e. check the Status Register | |
545 | Status = NorFlashReadStatusRegister( TargetAddress ); | |
546 | ||
547 | return Status; | |
548 | } | |
549 | ||
550 | EFI_STATUS | |
551 | NorFlashWriteSingleBlock ( | |
552 | IN UINTN DeviceBaseAddress, | |
553 | IN EFI_LBA Lba, | |
554 | IN UINT32 *DataBuffer, | |
555 | IN UINT32 BlockSizeInWords | |
556 | ) | |
557 | { | |
558 | EFI_STATUS Status = EFI_SUCCESS; | |
559 | UINTN WordAddress; | |
560 | UINT32 WordIndex; | |
561 | UINTN BufferIndex; | |
562 | UINTN BlockAddress; | |
563 | UINTN BuffersInBlock; | |
564 | UINTN RemainingWords; | |
565 | ||
566 | // Get the physical address of the block | |
567 | BlockAddress = GET_NOR_BLOCK_ADDRESS(DeviceBaseAddress, Lba, BlockSizeInWords * 4); | |
568 | ||
569 | Status = NorFlashUnlockAndEraseSingleBlock( BlockAddress ); | |
570 | if (EFI_ERROR(Status)) { | |
571 | DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress)); | |
572 | return Status; | |
573 | } | |
574 | ||
575 | // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method. | |
576 | ||
577 | // Start writing from the first address at the start of the block | |
578 | WordAddress = BlockAddress; | |
579 | ||
580 | // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero | |
581 | if ( (WordAddress & BOUNDARY_OF_32_WORDS) == 0x00 ) { | |
582 | ||
583 | // First, break the entire block into buffer-sized chunks. | |
584 | BuffersInBlock = (UINTN)BlockSizeInWords / P30_MAX_BUFFER_SIZE_IN_BYTES; | |
585 | ||
586 | // Then feed each buffer chunk to the NOR Flash | |
587 | for( BufferIndex=0; | |
588 | BufferIndex < BuffersInBlock; | |
589 | BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS | |
590 | ) { | |
591 | Status = NorFlashWriteBuffer ( WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer ); | |
592 | if (EFI_ERROR(Status)) { | |
593 | goto EXIT; | |
594 | } | |
595 | } | |
596 | ||
597 | // Finally, finish off any remaining words that are less than the maximum size of the buffer | |
598 | RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS; | |
599 | ||
600 | if( RemainingWords != 0) { | |
601 | Status = NorFlashWriteBuffer ( WordAddress, (RemainingWords * 4), DataBuffer ); | |
602 | if (EFI_ERROR(Status)) { | |
603 | goto EXIT; | |
604 | } | |
605 | } | |
606 | ||
607 | } else { | |
608 | // For now, use the single word programming algorithm | |
609 | // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range, | |
610 | // i.e. which ends in the range 0x......01 - 0x......7F. | |
611 | for( WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4 ) { | |
612 | Status = NorFlashWriteSingleWord( WordAddress, *DataBuffer ); | |
613 | if (EFI_ERROR(Status)) { | |
614 | goto EXIT; | |
615 | } | |
616 | } | |
617 | } | |
618 | ||
619 | EXIT: | |
620 | if (EFI_ERROR(Status)) { | |
621 | DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status)); | |
622 | } | |
623 | return Status; | |
624 | } | |
625 | ||
626 | ||
627 | EFI_STATUS | |
628 | NorFlashWriteBlocks ( | |
629 | IN NOR_FLASH_INSTANCE *Instance, | |
630 | IN EFI_LBA Lba, | |
631 | IN UINTN BufferSizeInBytes, | |
632 | IN VOID *Buffer | |
633 | ) | |
634 | { | |
635 | UINT32 *pWriteBuffer; | |
636 | EFI_STATUS Status = EFI_SUCCESS; | |
637 | EFI_LBA CurrentBlock; | |
638 | UINT32 BlockSizeInWords; | |
639 | UINT32 NumBlocks; | |
640 | UINT32 BlockCount; | |
641 | volatile UINT32 *VersatileExpress_SYS_FLASH; | |
642 | ||
643 | // The buffer must be valid | |
644 | if (Buffer == NULL) { | |
645 | return EFI_INVALID_PARAMETER; | |
646 | } | |
647 | ||
648 | if( Instance->Media.ReadOnly == TRUE ) { | |
649 | return EFI_WRITE_PROTECTED; | |
650 | } | |
651 | ||
652 | // We must have some bytes to read | |
653 | DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes)); | |
654 | if( BufferSizeInBytes == 0 ) { | |
655 | return EFI_BAD_BUFFER_SIZE; | |
656 | } | |
657 | ||
658 | // The size of the buffer must be a multiple of the block size | |
659 | DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize )); | |
660 | if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { | |
661 | return EFI_BAD_BUFFER_SIZE; | |
662 | } | |
663 | ||
664 | // All blocks must be within the device | |
665 | NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; | |
666 | ||
667 | DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba)); | |
668 | ||
669 | if ( ( Lba + NumBlocks ) > ( Instance->Media.LastBlock + 1 ) ) { | |
670 | DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n")); | |
671 | return EFI_INVALID_PARAMETER; | |
672 | } | |
673 | ||
674 | // Everything seems ok so far, so now we need to disable the platform-specific | |
675 | // flash write protection for Versatile Express | |
676 | VersatileExpress_SYS_FLASH = (UINT32 *)VE_REGISTER_SYS_FLASH_ADDR; | |
677 | if( (*VersatileExpress_SYS_FLASH & 0x1) == 0 ) { | |
678 | // Writing to NOR FLASH is disabled, so enable it | |
679 | *VersatileExpress_SYS_FLASH = 0x1; | |
680 | DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: informational - Had to enable HSYS_FLASH flag.\n" )); | |
681 | } | |
682 | ||
683 | BlockSizeInWords = Instance->Media.BlockSize / 4; | |
684 | ||
685 | // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer | |
686 | // to a proper data type, so use *ReadBuffer | |
687 | pWriteBuffer = (UINT32 *)Buffer; | |
688 | ||
689 | CurrentBlock = Lba; | |
690 | for( BlockCount=0; BlockCount<NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords ) { | |
691 | ||
692 | DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock )); | |
693 | ||
694 | Status = NorFlashWriteSingleBlock( Instance->BaseAddress, CurrentBlock, pWriteBuffer, BlockSizeInWords ); | |
695 | ||
696 | if (EFI_ERROR(Status)) { | |
697 | break; | |
698 | } | |
699 | ||
700 | } | |
701 | ||
702 | DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status)); | |
703 | return Status; | |
704 | } | |
705 | ||
706 | ||
707 | EFI_STATUS | |
708 | NorFlashReadBlocks ( | |
709 | IN NOR_FLASH_INSTANCE *Instance, | |
710 | IN EFI_LBA Lba, | |
711 | IN UINTN BufferSizeInBytes, | |
712 | OUT VOID *Buffer | |
713 | ) | |
714 | { | |
715 | UINT32 NumBlocks; | |
716 | UINTN StartAddress; | |
717 | ||
718 | // The buffer must be valid | |
719 | if (Buffer == NULL) { | |
720 | return EFI_INVALID_PARAMETER; | |
721 | } | |
722 | ||
723 | // We must have some bytes to read | |
724 | DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%x bytes.\n", BufferSizeInBytes)); | |
725 | if( BufferSizeInBytes == 0 ) { | |
726 | return EFI_BAD_BUFFER_SIZE; | |
727 | } | |
728 | ||
729 | // The size of the buffer must be a multiple of the block size | |
730 | DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BlockSize=0x%x bytes.\n", Instance->Media.BlockSize )); | |
731 | if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { | |
732 | return EFI_BAD_BUFFER_SIZE; | |
733 | } | |
734 | ||
735 | // All blocks must be within the device | |
736 | NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; | |
737 | ||
738 | DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld\n", NumBlocks, Instance->Media.LastBlock, Lba)); | |
739 | ||
740 | if ( ( Lba + NumBlocks ) > (Instance->Media.LastBlock + 1) ) { | |
741 | DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n")); | |
742 | return EFI_INVALID_PARAMETER; | |
743 | } | |
744 | ||
745 | // Get the address to start reading from | |
746 | StartAddress = GET_NOR_BLOCK_ADDRESS( Instance->BaseAddress, | |
747 | Lba, | |
748 | Instance->Media.BlockSize | |
749 | ); | |
750 | ||
751 | // Put the device into Read Array mode | |
752 | SEND_NOR_COMMAND( Instance->BaseAddress, 0, P30_CMD_READ_ARRAY ); | |
753 | ||
754 | // Readout the data | |
755 | CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes); | |
756 | ||
757 | return EFI_SUCCESS; | |
758 | } | |
759 | ||
760 | ||
761 | EFI_STATUS | |
762 | NorFlashReset ( | |
763 | IN NOR_FLASH_INSTANCE *Instance | |
764 | ) | |
765 | { | |
766 | DEBUG((DEBUG_BLKIO, "NorFlashReset(BaseAddress=0x%08x)\n", Instance->BaseAddress)); | |
767 | ||
768 | // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode | |
769 | SEND_NOR_COMMAND( Instance->BaseAddress, 0, P30_CMD_READ_ARRAY ); | |
770 | ||
771 | return EFI_SUCCESS; | |
772 | } | |
773 | ||
774 | ||
775 | ||
776 | EFI_STATUS | |
777 | EFIAPI | |
778 | NorFlashInitialise ( | |
779 | IN EFI_HANDLE ImageHandle, | |
780 | IN EFI_SYSTEM_TABLE *SystemTable | |
781 | ) | |
782 | { | |
783 | EFI_STATUS Status = EFI_SUCCESS; | |
784 | UINT32 Index; | |
785 | ||
786 | for (Index = 0; Index < NOR_FLASH_LAST_DEVICE; Index++) { | |
787 | Status = NorFlashCreateInstance( | |
788 | mNorFlashDescription[Index].BaseAddress, | |
789 | mNorFlashDescription[Index].Size, | |
790 | Index, | |
791 | mNorFlashDescription[Index].BlockSize, | |
792 | mNorFlashDescription[Index].SupportFvb, | |
793 | &mNorFlashDescription[Index].Guid, | |
794 | &mNorFlashInstances[Index] | |
795 | ); | |
796 | if (EFI_ERROR(Status)) { | |
797 | DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index)); | |
798 | } | |
799 | } | |
800 | ||
801 | return Status; | |
802 | } |