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