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