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