]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c
ArmPlatformPkg: Provide mechanism to get BootMonFS file information (cont.)
[mirror_edk2.git] / ArmPlatformPkg / Drivers / NorFlashDxe / NorFlashDxe.c
CommitLineData
1e57a462 1/** @file NorFlashDxe.c\r
2\r
2dff0c1a 3 Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>\r
1e57a462 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
1dbbfc17 23STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent;\r
1e57a462 24\r
25//\r
26// Global variable declarations\r
27//\r
28NOR_FLASH_INSTANCE **mNorFlashInstances;\r
1dbbfc17 29UINT32 mNorFlashDeviceCount;\r
1e57a462 30\r
31NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = {\r
32 NOR_FLASH_SIGNATURE, // Signature\r
33 NULL, // Handle ... NEED TO BE FILLED\r
34\r
35 FALSE, // Initialized\r
36 NULL, // Initialize\r
37\r
38 0, // DeviceBaseAddress ... NEED TO BE FILLED\r
39 0, // RegionBaseAddress ... NEED TO BE FILLED\r
40 0, // Size ... NEED TO BE FILLED\r
41 0, // StartLba\r
42\r
1d5d0ae9 43 {\r
44 EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision\r
45 NULL, // Media ... NEED TO BE FILLED\r
46 NorFlashBlockIoReset, // Reset;\r
1e57a462 47 NorFlashBlockIoReadBlocks, // ReadBlocks\r
48 NorFlashBlockIoWriteBlocks, // WriteBlocks\r
1d5d0ae9 49 NorFlashBlockIoFlushBlocks // FlushBlocks\r
1e57a462 50 }, // BlockIoProtocol\r
51\r
1d5d0ae9 52 {\r
53 0, // MediaId ... NEED TO BE FILLED\r
54 FALSE, // RemovableMedia\r
55 TRUE, // MediaPresent\r
56 FALSE, // LogicalPartition\r
57 FALSE, // ReadOnly\r
58 FALSE, // WriteCaching;\r
59 0, // BlockSize ... NEED TO BE FILLED\r
60 4, // IoAlign\r
61 0, // LastBlock ... NEED TO BE FILLED\r
62 0, // LowestAlignedLba\r
63 1, // LogicalBlocksPerPhysicalBlock\r
1e57a462 64 }, //Media;\r
65\r
452a9ee1
BJ
66 {\r
67 EFI_DISK_IO_PROTOCOL_REVISION, // Revision\r
68 NorFlashDiskIoReadDisk, // ReadDisk\r
69 NorFlashDiskIoWriteDisk // WriteDisk\r
70 },\r
71\r
1e57a462 72 FALSE, // SupportFvb ... NEED TO BE FILLED\r
1d5d0ae9 73 {\r
1e57a462 74 FvbGetAttributes, // GetAttributes\r
75 FvbSetAttributes, // SetAttributes\r
76 FvbGetPhysicalAddress, // GetPhysicalAddress\r
77 FvbGetBlockSize, // GetBlockSize\r
78 FvbRead, // Read\r
79 FvbWrite, // Write\r
1d5d0ae9 80 FvbEraseBlocks, // EraseBlocks\r
81 NULL, //ParentHandle\r
1e57a462 82 }, // FvbProtoccol;\r
452a9ee1 83 NULL, // ShadowBuffer\r
1e57a462 84 {\r
85 {\r
86 {\r
87 HARDWARE_DEVICE_PATH,\r
88 HW_VENDOR_DP,\r
b0fdce95 89 { (UINT8)sizeof(VENDOR_DEVICE_PATH), (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8) }\r
1e57a462 90 },\r
b0fdce95 91 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED\r
1e57a462 92 },\r
93 {\r
94 END_DEVICE_PATH_TYPE,\r
95 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
b0fdce95 96 { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }\r
1e57a462 97 }\r
98 } // DevicePath\r
99};\r
100\r
101EFI_STATUS\r
102NorFlashCreateInstance (\r
103 IN UINTN NorFlashDeviceBase,\r
104 IN UINTN NorFlashRegionBase,\r
105 IN UINTN NorFlashSize,\r
106 IN UINT32 MediaId,\r
107 IN UINT32 BlockSize,\r
108 IN BOOLEAN SupportFvb,\r
109 IN CONST GUID *NorFlashGuid,\r
110 OUT NOR_FLASH_INSTANCE** NorFlashInstance\r
111 )\r
112{\r
113 EFI_STATUS Status;\r
114 NOR_FLASH_INSTANCE* Instance;\r
115\r
116 ASSERT(NorFlashInstance != NULL);\r
117\r
2dff0c1a 118 Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);\r
1e57a462 119 if (Instance == NULL) {\r
120 return EFI_OUT_OF_RESOURCES;\r
121 }\r
122\r
123 Instance->DeviceBaseAddress = NorFlashDeviceBase;\r
124 Instance->RegionBaseAddress = NorFlashRegionBase;\r
125 Instance->Size = NorFlashSize;\r
126\r
127 Instance->BlockIoProtocol.Media = &Instance->Media;\r
128 Instance->Media.MediaId = MediaId;\r
129 Instance->Media.BlockSize = BlockSize;\r
130 Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;\r
1d5d0ae9 131\r
2dff0c1a 132 CopyGuid (&Instance->DevicePath.Vendor.Guid, NorFlashGuid);\r
1e57a462 133\r
452a9ee1
BJ
134 Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);;\r
135 if (Instance->ShadowBuffer == NULL) {\r
136 return EFI_OUT_OF_RESOURCES;\r
137 }\r
138\r
1e57a462 139 if (SupportFvb) {\r
140 Instance->SupportFvb = TRUE;\r
141 Instance->Initialize = NorFlashFvbInitialize;\r
142\r
143 Status = gBS->InstallMultipleProtocolInterfaces (\r
144 &Instance->Handle,\r
145 &gEfiDevicePathProtocolGuid, &Instance->DevicePath,\r
146 &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol,\r
147 &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol,\r
148 NULL\r
149 );\r
150 if (EFI_ERROR(Status)) {\r
2dff0c1a 151 FreePool (Instance);\r
1e57a462 152 return Status;\r
153 }\r
154 } else {\r
155 Instance->Initialized = TRUE;\r
156\r
157 Status = gBS->InstallMultipleProtocolInterfaces (\r
158 &Instance->Handle,\r
159 &gEfiDevicePathProtocolGuid, &Instance->DevicePath,\r
160 &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol,\r
452a9ee1 161 &gEfiDiskIoProtocolGuid, &Instance->DiskIoProtocol,\r
1e57a462 162 NULL\r
163 );\r
164 if (EFI_ERROR(Status)) {\r
2dff0c1a 165 FreePool (Instance);\r
1e57a462 166 return Status;\r
167 }\r
168 }\r
1d5d0ae9 169\r
170 *NorFlashInstance = Instance;\r
1e57a462 171 return Status;\r
172}\r
173\r
174UINT32\r
175NorFlashReadStatusRegister (\r
176 IN NOR_FLASH_INSTANCE *Instance,\r
177 IN UINTN SR_Address\r
178 )\r
179{\r
180 // Prepare to read the status register\r
181 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);\r
182 return MmioRead32 (Instance->DeviceBaseAddress);\r
183}\r
184\r
185\r
186BOOLEAN\r
187NorFlashBlockIsLocked (\r
188 IN NOR_FLASH_INSTANCE *Instance,\r
189 IN UINTN BlockAddress\r
190 )\r
191{\r
192 UINT32 LockStatus;\r
193 BOOLEAN BlockIsLocked;\r
194\r
195 BlockIsLocked = TRUE;\r
196\r
197 // Send command for reading device id\r
198 SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);\r
199\r
200 // Read block lock status\r
201 LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2));\r
202\r
203 // Decode block lock status\r
204 LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus);\r
205\r
206 if ((LockStatus & 0x2) != 0) {\r
207 DEBUG((EFI_D_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));\r
208 }\r
209\r
210 if ((LockStatus & 0x1) == 0) {\r
211 // This means the block is unlocked\r
212 DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress));\r
213 BlockIsLocked = FALSE;\r
214 }\r
215\r
216 return BlockIsLocked;\r
217}\r
218\r
219\r
220EFI_STATUS\r
221NorFlashUnlockSingleBlock (\r
222 IN NOR_FLASH_INSTANCE *Instance,\r
223 IN UINTN BlockAddress\r
224 )\r
225{\r
226 EFI_STATUS Status = EFI_SUCCESS;\r
227 UINT32 LockStatus;\r
228\r
229 // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations\r
230 // and to protect shared data structures.\r
231\r
232 if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) {\r
233 do {\r
234 // Request a lock setup\r
235 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);\r
236\r
237 // Request an unlock\r
238 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);\r
239\r
240 // Send command for reading device id\r
241 SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);\r
242\r
243 // Read block lock status\r
244 LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2));\r
245\r
246 // Decode block lock status\r
247 LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus);\r
248 } while ((LockStatus & 0x1) == 1);\r
249 } else {\r
250 // Request a lock setup\r
251 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);\r
252\r
253 // Request an unlock\r
254 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);\r
255\r
256 // Wait until the status register gives us the all clear\r
257 do {\r
258 LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);\r
259 } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
260 }\r
261\r
262 // Put device back into Read Array mode\r
263 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY);\r
264\r
265 DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress, Status));\r
266\r
267 return Status;\r
268}\r
269\r
270\r
271EFI_STATUS\r
272NorFlashUnlockSingleBlockIfNecessary (\r
273 IN NOR_FLASH_INSTANCE *Instance,\r
274 IN UINTN BlockAddress\r
275 )\r
276{\r
277 EFI_STATUS Status = EFI_SUCCESS;\r
278\r
279 if (NorFlashBlockIsLocked (Instance, BlockAddress) == TRUE) {\r
280 Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);\r
281 }\r
282\r
283 return Status;\r
284}\r
285\r
286\r
287/**\r
288 * The following function presumes that the block has already been unlocked.\r
289 **/\r
290EFI_STATUS\r
291NorFlashEraseSingleBlock (\r
292 IN NOR_FLASH_INSTANCE *Instance,\r
293 IN UINTN BlockAddress\r
294 )\r
295{\r
296 EFI_STATUS Status;\r
297 UINT32 StatusRegister;\r
298\r
299 Status = EFI_SUCCESS;\r
300\r
301 // Request a block erase and then confirm it\r
302 SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP);\r
303 SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM);\r
304\r
305 // Wait until the status register gives us the all clear\r
306 do {\r
307 StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);\r
308 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
309\r
310 if (StatusRegister & P30_SR_BIT_VPP) {\r
311 DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress));\r
312 Status = EFI_DEVICE_ERROR;\r
313 }\r
314\r
315 if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) {\r
316 DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress));\r
317 Status = EFI_DEVICE_ERROR;\r
318 }\r
319\r
320 if (StatusRegister & P30_SR_BIT_ERASE) {\r
321 DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister));\r
322 Status = EFI_DEVICE_ERROR;\r
323 }\r
324\r
325 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {\r
326 // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...\r
327 DEBUG((EFI_D_INFO,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress));\r
328 Status = EFI_WRITE_PROTECTED;\r
329 }\r
330\r
331 if (EFI_ERROR(Status)) {\r
332 // Clear the Status Register\r
333 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);\r
334 }\r
335\r
336 // Put device back into Read Array mode\r
337 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
338\r
339 return Status;\r
340}\r
341\r
342/**\r
343 * The following function presumes that the block has already been unlocked.\r
344 **/\r
345EFI_STATUS\r
346NorFlashUnlockAndEraseSingleBlock (\r
347 IN NOR_FLASH_INSTANCE *Instance,\r
348 IN UINTN BlockAddress\r
349 )\r
350{\r
351 EFI_STATUS Status;\r
352 UINTN Index;\r
353 EFI_TPL OriginalTPL;\r
354\r
2dff0c1a
OM
355 if (!EfiAtRuntime ()) {\r
356 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
357 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
358 } else {\r
359 // This initialization is only to prevent the compiler to complain about the\r
360 // use of uninitialized variables\r
361 OriginalTPL = TPL_HIGH_LEVEL;\r
362 }\r
1e57a462 363\r
364 Index = 0;\r
365 // The block erase might fail a first time (SW bug ?). Retry it ...\r
366 do {\r
367 // Unlock the block if we have to\r
368 Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);\r
369 if (!EFI_ERROR(Status)) {\r
370 Status = NorFlashEraseSingleBlock (Instance, BlockAddress);\r
371 }\r
372 Index++;\r
373 } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));\r
374\r
375 if (Index == NOR_FLASH_ERASE_RETRY) {\r
376 DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress,Index));\r
377 }\r
378\r
2dff0c1a
OM
379 if (!EfiAtRuntime ()) {\r
380 // Interruptions can resume.\r
381 gBS->RestoreTPL (OriginalTPL);\r
382 }\r
1e57a462 383\r
384 return Status;\r
385}\r
386\r
387\r
452a9ee1 388STATIC\r
1e57a462 389EFI_STATUS\r
390NorFlashWriteSingleWord (\r
391 IN NOR_FLASH_INSTANCE *Instance,\r
392 IN UINTN WordAddress,\r
393 IN UINT32 WriteData\r
394 )\r
395{\r
396 EFI_STATUS Status;\r
397 UINT32 StatusRegister;\r
398\r
399 Status = EFI_SUCCESS;\r
400\r
401 // Request a write single word command\r
402 SEND_NOR_COMMAND(WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);\r
403\r
404 // Store the word into NOR Flash;\r
405 MmioWrite32 (WordAddress, WriteData);\r
406\r
407 // Wait for the write to complete and then check for any errors; i.e. check the Status Register\r
408 do {\r
409 // Prepare to read the status register\r
410 StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);\r
411 // The chip is busy while the WRITE bit is not asserted\r
412 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
413\r
414\r
415 // Perform a full status check:\r
416 // Mask the relevant bits of Status Register.\r
417 // Everything should be zero, if not, we have a problem\r
418\r
419 if (StatusRegister & P30_SR_BIT_VPP) {\r
420 DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress));\r
421 Status = EFI_DEVICE_ERROR;\r
422 }\r
423\r
424 if (StatusRegister & P30_SR_BIT_PROGRAM) {\r
425 DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress));\r
426 Status = EFI_DEVICE_ERROR;\r
427 }\r
428\r
429 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {\r
430 DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress));\r
431 Status = EFI_DEVICE_ERROR;\r
432 }\r
433\r
434 if (!EFI_ERROR(Status)) {\r
435 // Clear the Status Register\r
436 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);\r
437 }\r
438\r
439 // Put device back into Read Array mode\r
440 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
441\r
442 return Status;\r
443}\r
444\r
445/*\r
446 * Writes data to the NOR Flash using the Buffered Programming method.\r
447 *\r
448 * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.\r
449 * Therefore this function will only handle buffers up to 32 words or 128 bytes.\r
450 * To deal with larger buffers, call this function again.\r
451 *\r
452 * This function presumes that both the TargetAddress and the TargetAddress+BufferSize\r
453 * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.\r
454 *\r
455 * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,\r
456 * then programming time is doubled and power consumption is increased.\r
457 * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.\r
458 * i.e. the last 4 bits of the target start address must be zero: 0x......00\r
459 */\r
460EFI_STATUS\r
461NorFlashWriteBuffer (\r
462 IN NOR_FLASH_INSTANCE *Instance,\r
463 IN UINTN TargetAddress,\r
464 IN UINTN BufferSizeInBytes,\r
465 IN UINT32 *Buffer\r
466 )\r
467{\r
468 EFI_STATUS Status;\r
469 UINTN BufferSizeInWords;\r
470 UINTN Count;\r
471 volatile UINT32 *Data;\r
472 UINTN WaitForBuffer;\r
473 BOOLEAN BufferAvailable;\r
474 UINT32 StatusRegister;\r
475\r
476 WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS;\r
477 BufferAvailable = FALSE;\r
478\r
479 // Check that the target address does not cross a 32-word boundary.\r
480 if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) {\r
481 return EFI_INVALID_PARAMETER;\r
482 }\r
483\r
484 // Check there are some data to program\r
485 if (BufferSizeInBytes == 0) {\r
486 return EFI_BUFFER_TOO_SMALL;\r
487 }\r
488\r
489 // Check that the buffer size does not exceed the maximum hardware buffer size on chip.\r
490 if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) {\r
491 return EFI_BAD_BUFFER_SIZE;\r
492 }\r
493\r
494 // Check that the buffer size is a multiple of 32-bit words\r
495 if ((BufferSizeInBytes % 4) != 0) {\r
496 return EFI_BAD_BUFFER_SIZE;\r
497 }\r
498\r
499 // Pre-programming conditions checked, now start the algorithm.\r
500\r
501 // Prepare the data destination address\r
502 Data = (UINT32 *)TargetAddress;\r
503\r
504 // Check the availability of the buffer\r
505 do {\r
506 // Issue the Buffered Program Setup command\r
507 SEND_NOR_COMMAND(TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP);\r
508\r
509 // Read back the status register bit#7 from the same address\r
510 if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) {\r
511 BufferAvailable = TRUE;\r
512 }\r
513\r
514 // Update the loop counter\r
515 WaitForBuffer--;\r
516\r
517 } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE));\r
518\r
519 // The buffer was not available for writing\r
520 if (WaitForBuffer == 0) {\r
521 Status = EFI_DEVICE_ERROR;\r
522 goto EXIT;\r
523 }\r
524\r
525 // From now on we work in 32-bit words\r
526 BufferSizeInWords = BufferSizeInBytes / (UINTN)4;\r
527\r
528 // Write the word count, which is (buffer_size_in_words - 1),\r
529 // because word count 0 means one word.\r
530 SEND_NOR_COMMAND(TargetAddress, 0, (BufferSizeInWords - 1));\r
531\r
532 // Write the data to the NOR Flash, advancing each address by 4 bytes\r
533 for(Count=0; Count < BufferSizeInWords; Count++, Data++, Buffer++) {\r
534 *Data = *Buffer;\r
535 }\r
536\r
537 // Issue the Buffered Program Confirm command, to start the programming operation\r
538 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);\r
539\r
540 // Wait for the write to complete and then check for any errors; i.e. check the Status Register\r
541 do {\r
542 StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress);\r
543 // The chip is busy while the WRITE bit is not asserted\r
544 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
545\r
546\r
547 // Perform a full status check:\r
548 // Mask the relevant bits of Status Register.\r
549 // Everything should be zero, if not, we have a problem\r
550\r
551 Status = EFI_SUCCESS;\r
552\r
553 if (StatusRegister & P30_SR_BIT_VPP) {\r
554 DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress));\r
555 Status = EFI_DEVICE_ERROR;\r
556 }\r
557\r
558 if (StatusRegister & P30_SR_BIT_PROGRAM) {\r
559 DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress));\r
560 Status = EFI_DEVICE_ERROR;\r
561 }\r
562\r
563 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {\r
564 DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress));\r
565 Status = EFI_DEVICE_ERROR;\r
566 }\r
567\r
568 if (!EFI_ERROR(Status)) {\r
569 // Clear the Status Register\r
570 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);\r
571 }\r
572\r
573EXIT:\r
574 // Put device back into Read Array mode\r
575 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
576\r
577 return Status;\r
578}\r
579\r
452a9ee1 580STATIC\r
1e57a462 581EFI_STATUS\r
452a9ee1 582NorFlashWriteFullBlock (\r
1e57a462 583 IN NOR_FLASH_INSTANCE *Instance,\r
584 IN EFI_LBA Lba,\r
585 IN UINT32 *DataBuffer,\r
586 IN UINT32 BlockSizeInWords\r
587 )\r
588{\r
589 EFI_STATUS Status;\r
590 UINTN WordAddress;\r
591 UINT32 WordIndex;\r
592 UINTN BufferIndex;\r
593 UINTN BlockAddress;\r
594 UINTN BuffersInBlock;\r
595 UINTN RemainingWords;\r
596 EFI_TPL OriginalTPL;\r
518c243d 597 UINTN Cnt;\r
1e57a462 598\r
599 Status = EFI_SUCCESS;\r
600\r
601 // Get the physical address of the block\r
602 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);\r
603\r
604 // Start writing from the first address at the start of the block\r
605 WordAddress = BlockAddress;\r
606\r
2dff0c1a
OM
607 if (!EfiAtRuntime ()) {\r
608 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
609 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
610 } else {\r
611 // This initialization is only to prevent the compiler to complain about the\r
612 // use of uninitialized variables\r
613 OriginalTPL = TPL_HIGH_LEVEL;\r
614 }\r
1e57a462 615\r
616 Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);\r
617 if (EFI_ERROR(Status)) {\r
618 DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));\r
619 goto EXIT;\r
620 }\r
621\r
622 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.\r
623\r
624 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero\r
625 if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {\r
626\r
627 // First, break the entire block into buffer-sized chunks.\r
82745213 628 BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;\r
1e57a462 629\r
630 // Then feed each buffer chunk to the NOR Flash\r
518c243d 631 // If a buffer does not contain any data, don't write it.\r
1e57a462 632 for(BufferIndex=0;\r
633 BufferIndex < BuffersInBlock;\r
634 BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS\r
635 ) {\r
518c243d
HL
636 // Check the buffer to see if it contains any data (not set all 1s).\r
637 for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {\r
638 if (~DataBuffer[Cnt] != 0 ) {\r
639 // Some data found, write the buffer.\r
640 Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES,\r
641 DataBuffer);\r
642 if (EFI_ERROR(Status)) {\r
643 goto EXIT;\r
644 }\r
645 break;\r
646 }\r
1e57a462 647 }\r
648 }\r
649\r
650 // Finally, finish off any remaining words that are less than the maximum size of the buffer\r
651 RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;\r
652\r
653 if(RemainingWords != 0) {\r
654 Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);\r
655 if (EFI_ERROR(Status)) {\r
656 goto EXIT;\r
657 }\r
658 }\r
659\r
660 } else {\r
661 // For now, use the single word programming algorithm\r
662 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,\r
663 // i.e. which ends in the range 0x......01 - 0x......7F.\r
664 for(WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {\r
665 Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);\r
666 if (EFI_ERROR(Status)) {\r
667 goto EXIT;\r
668 }\r
669 }\r
670 }\r
671\r
672EXIT:\r
2dff0c1a
OM
673 if (!EfiAtRuntime ()) {\r
674 // Interruptions can resume.\r
675 gBS->RestoreTPL (OriginalTPL);\r
676 }\r
1e57a462 677\r
678 if (EFI_ERROR(Status)) {\r
679 DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));\r
680 }\r
681 return Status;\r
682}\r
683\r
684\r
685EFI_STATUS\r
686NorFlashWriteBlocks (\r
687 IN NOR_FLASH_INSTANCE *Instance,\r
688 IN EFI_LBA Lba,\r
689 IN UINTN BufferSizeInBytes,\r
690 IN VOID *Buffer\r
691 )\r
692{\r
693 UINT32 *pWriteBuffer;\r
694 EFI_STATUS Status = EFI_SUCCESS;\r
695 EFI_LBA CurrentBlock;\r
696 UINT32 BlockSizeInWords;\r
697 UINT32 NumBlocks;\r
698 UINT32 BlockCount;\r
699\r
700 // The buffer must be valid\r
701 if (Buffer == NULL) {\r
702 return EFI_INVALID_PARAMETER;\r
703 }\r
704\r
705 if(Instance->Media.ReadOnly == TRUE) {\r
706 return EFI_WRITE_PROTECTED;\r
707 }\r
708\r
709 // We must have some bytes to read\r
710 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));\r
711 if(BufferSizeInBytes == 0) {\r
712 return EFI_BAD_BUFFER_SIZE;\r
713 }\r
714\r
715 // The size of the buffer must be a multiple of the block size\r
716 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize));\r
717 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {\r
718 return EFI_BAD_BUFFER_SIZE;\r
719 }\r
720\r
721 // All blocks must be within the device\r
722 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;\r
723\r
724 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba));\r
725\r
726 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {\r
727 DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));\r
728 return EFI_INVALID_PARAMETER;\r
729 }\r
730\r
731 BlockSizeInWords = Instance->Media.BlockSize / 4;\r
732\r
733 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer\r
734 // to a proper data type, so use *ReadBuffer\r
735 pWriteBuffer = (UINT32 *)Buffer;\r
736\r
737 CurrentBlock = Lba;\r
738 for (BlockCount=0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {\r
739\r
740 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));\r
741\r
452a9ee1 742 Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);\r
1e57a462 743\r
744 if (EFI_ERROR(Status)) {\r
745 break;\r
746 }\r
747 }\r
748\r
749 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));\r
750 return Status;\r
751}\r
752\r
753EFI_STATUS\r
754NorFlashReadBlocks (\r
755 IN NOR_FLASH_INSTANCE *Instance,\r
756 IN EFI_LBA Lba,\r
757 IN UINTN BufferSizeInBytes,\r
758 OUT VOID *Buffer\r
759 )\r
760{\r
761 UINT32 NumBlocks;\r
762 UINTN StartAddress;\r
763\r
3d783074 764 DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",\r
765 BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba));\r
766\r
1e57a462 767 // The buffer must be valid\r
768 if (Buffer == NULL) {\r
769 return EFI_INVALID_PARAMETER;\r
770 }\r
771\r
452a9ee1 772 // Return if we have not any byte to read\r
3d783074 773 if (BufferSizeInBytes == 0) {\r
774 return EFI_SUCCESS;\r
1e57a462 775 }\r
776\r
777 // The size of the buffer must be a multiple of the block size\r
1e57a462 778 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {\r
779 return EFI_BAD_BUFFER_SIZE;\r
780 }\r
781\r
782 // All blocks must be within the device\r
783 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;\r
784\r
1e57a462 785 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {\r
786 DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));\r
787 return EFI_INVALID_PARAMETER;\r
788 }\r
789\r
790 // Get the address to start reading from\r
791 StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,\r
792 Lba,\r
793 Instance->Media.BlockSize\r
794 );\r
795\r
796 // Put the device into Read Array mode\r
797 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
798\r
799 // Readout the data\r
800 CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes);\r
801\r
802 return EFI_SUCCESS;\r
803}\r
804\r
518c243d
HL
805EFI_STATUS\r
806NorFlashRead (\r
807 IN NOR_FLASH_INSTANCE *Instance,\r
808 IN EFI_LBA Lba,\r
809 IN UINTN Offset,\r
810 IN UINTN BufferSizeInBytes,\r
811 OUT VOID *Buffer\r
812 )\r
813{\r
814 UINT32 NumBlocks;\r
815 UINTN StartAddress;\r
816\r
817 // The buffer must be valid\r
818 if (Buffer == NULL) {\r
819 return EFI_INVALID_PARAMETER;\r
820 }\r
821\r
822 // Return if we have not any byte to read\r
823 if (BufferSizeInBytes == 0) {\r
824 return EFI_SUCCESS;\r
825 }\r
826\r
827 // All blocks must be within the device\r
828 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;\r
829\r
830 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {\r
831 DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed last block\n"));\r
832 return EFI_INVALID_PARAMETER;\r
833 }\r
834\r
835 if (Offset + BufferSizeInBytes >= Instance->Size) {\r
836 DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));\r
837 return EFI_INVALID_PARAMETER;\r
838 }\r
839\r
840 // Get the address to start reading from\r
841 StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,\r
842 Lba,\r
843 Instance->Media.BlockSize\r
844 );\r
845\r
846 // Put the device into Read Array mode\r
847 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
848\r
849 // Readout the data\r
850 CopyMem (Buffer, (UINTN *)(StartAddress + Offset), BufferSizeInBytes);\r
851\r
852 return EFI_SUCCESS;\r
853}\r
854\r
452a9ee1
BJ
855/*\r
856 Write a full or portion of a block. It must not span block boundaries; that is,\r
857 Offset + *NumBytes <= Instance->Media.BlockSize.\r
858*/\r
859EFI_STATUS\r
860NorFlashWriteSingleBlock (\r
861 IN NOR_FLASH_INSTANCE *Instance,\r
862 IN EFI_LBA Lba,\r
863 IN UINTN Offset,\r
864 IN OUT UINTN *NumBytes,\r
865 IN UINT8 *Buffer\r
866 )\r
867{\r
868 EFI_STATUS TempStatus;\r
869 UINT32 Tmp;\r
870 UINT32 TmpBuf;\r
871 UINT32 WordToWrite;\r
872 UINT32 Mask;\r
873 BOOLEAN DoErase;\r
874 UINTN BytesToWrite;\r
875 UINTN CurOffset;\r
876 UINTN WordAddr;\r
877 UINTN BlockSize;\r
878 UINTN BlockAddress;\r
879 UINTN PrevBlockAddress;\r
880\r
881 PrevBlockAddress = 0;\r
882\r
883 if (!Instance->Initialized && Instance->Initialize) {\r
884 Instance->Initialize(Instance);\r
885 }\r
886\r
36d66acf 887 DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));\r
452a9ee1
BJ
888\r
889 // Detect WriteDisabled state\r
890 if (Instance->Media.ReadOnly == TRUE) {\r
891 DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));\r
892 // It is in WriteDisabled state, return an error right away\r
893 return EFI_ACCESS_DENIED;\r
894 }\r
895\r
896 // Cache the block size to avoid de-referencing pointers all the time\r
897 BlockSize = Instance->Media.BlockSize;\r
898\r
899 // The write must not span block boundaries.\r
900 // We need to check each variable individually because adding two large values together overflows.\r
901 if ( ( Offset >= BlockSize ) ||\r
902 ( *NumBytes > BlockSize ) ||\r
903 ( (Offset + *NumBytes) > BlockSize ) ) {\r
904 DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
905 return EFI_BAD_BUFFER_SIZE;\r
906 }\r
907\r
908 // We must have some bytes to write\r
909 if (*NumBytes == 0) {\r
910 DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));\r
911 return EFI_BAD_BUFFER_SIZE;\r
912 }\r
913\r
914 // Pick 128bytes as a good start for word operations as opposed to erasing the\r
915 // block and writing the data regardless if an erase is really needed.\r
916 // It looks like most individual NV variable writes are smaller than 128bytes.\r
917 if (*NumBytes <= 128) {\r
918 // Check to see if we need to erase before programming the data into NOR.\r
919 // If the destination bits are only changing from 1s to 0s we can just write.\r
920 // After a block is erased all bits in the block is set to 1.\r
921 // If any byte requires us to erase we just give up and rewrite all of it.\r
922 DoErase = FALSE;\r
923 BytesToWrite = *NumBytes;\r
924 CurOffset = Offset;\r
925\r
926 while (BytesToWrite > 0) {\r
927 // Read full word from NOR, splice as required. A word is the smallest\r
928 // unit we can write.\r
36d66acf 929 TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof(Tmp), &Tmp);\r
452a9ee1
BJ
930 if (EFI_ERROR (TempStatus)) {\r
931 return EFI_DEVICE_ERROR;\r
932 }\r
933\r
934 // Physical address of word in NOR to write.\r
935 WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,\r
936 Lba, BlockSize);\r
937 // The word of data that is to be written.\r
938 TmpBuf = *((UINT32*)(Buffer + (*NumBytes - BytesToWrite)));\r
939\r
940 // First do word aligned chunks.\r
941 if ((CurOffset & 0x3) == 0) {\r
942 if (BytesToWrite >= 4) {\r
943 // Is the destination still in 'erased' state?\r
944 if (~Tmp != 0) {\r
945 // Check to see if we are only changing bits to zero.\r
946 if ((Tmp ^ TmpBuf) & TmpBuf) {\r
947 DoErase = TRUE;\r
948 break;\r
949 }\r
950 }\r
951 // Write this word to NOR\r
952 WordToWrite = TmpBuf;\r
953 CurOffset += sizeof(TmpBuf);\r
954 BytesToWrite -= sizeof(TmpBuf);\r
955 } else {\r
956 // BytesToWrite < 4. Do small writes and left-overs\r
957 Mask = ~((~0) << (BytesToWrite * 8));\r
958 // Mask out the bytes we want.\r
959 TmpBuf &= Mask;\r
960 // Is the destination still in 'erased' state?\r
961 if ((Tmp & Mask) != Mask) {\r
962 // Check to see if we are only changing bits to zero.\r
963 if ((Tmp ^ TmpBuf) & TmpBuf) {\r
964 DoErase = TRUE;\r
965 break;\r
966 }\r
967 }\r
968 // Merge old and new data. Write merged word to NOR\r
969 WordToWrite = (Tmp & ~Mask) | TmpBuf;\r
970 CurOffset += BytesToWrite;\r
971 BytesToWrite = 0;\r
972 }\r
973 } else {\r
974 // Do multiple words, but starting unaligned.\r
975 if (BytesToWrite > (4 - (CurOffset & 0x3))) {\r
976 Mask = ((~0) << ((CurOffset & 0x3) * 8));\r
977 // Mask out the bytes we want.\r
978 TmpBuf &= Mask;\r
979 // Is the destination still in 'erased' state?\r
980 if ((Tmp & Mask) != Mask) {\r
981 // Check to see if we are only changing bits to zero.\r
982 if ((Tmp ^ TmpBuf) & TmpBuf) {\r
983 DoErase = TRUE;\r
984 break;\r
985 }\r
986 }\r
987 // Merge old and new data. Write merged word to NOR\r
988 WordToWrite = (Tmp & ~Mask) | TmpBuf;\r
989 BytesToWrite -= (4 - (CurOffset & 0x3));\r
990 CurOffset += (4 - (CurOffset & 0x3));\r
991 } else {\r
992 // Unaligned and fits in one word.\r
993 Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8);\r
994 // Mask out the bytes we want.\r
995 TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask;\r
996 // Is the destination still in 'erased' state?\r
997 if ((Tmp & Mask) != Mask) {\r
998 // Check to see if we are only changing bits to zero.\r
999 if ((Tmp ^ TmpBuf) & TmpBuf) {\r
1000 DoErase = TRUE;\r
1001 break;\r
1002 }\r
1003 }\r
1004 // Merge old and new data. Write merged word to NOR\r
1005 WordToWrite = (Tmp & ~Mask) | TmpBuf;\r
1006 CurOffset += BytesToWrite;\r
1007 BytesToWrite = 0;\r
1008 }\r
1009 }\r
1010\r
1011 //\r
1012 // Write the word to NOR.\r
1013 //\r
1014\r
1015 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);\r
1016 if (BlockAddress != PrevBlockAddress) {\r
1017 TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);\r
1018 if (EFI_ERROR (TempStatus)) {\r
1019 return EFI_DEVICE_ERROR;\r
1020 }\r
1021 PrevBlockAddress = BlockAddress;\r
1022 }\r
1023 TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite);\r
1024 if (EFI_ERROR (TempStatus)) {\r
1025 return EFI_DEVICE_ERROR;\r
1026 }\r
1027 }\r
1028 // Exit if we got here and could write all the data. Otherwise do the\r
1029 // Erase-Write cycle.\r
1030 if (!DoErase) {\r
1031 return EFI_SUCCESS;\r
1032 }\r
1033 }\r
1034\r
1035 // Check we did get some memory. Buffer is BlockSize.\r
1036 if (Instance->ShadowBuffer == NULL) {\r
1037 DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));\r
1038 return EFI_DEVICE_ERROR;\r
1039 }\r
1040\r
1041 // Read NOR Flash data into shadow buffer\r
36d66acf 1042 TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);\r
452a9ee1
BJ
1043 if (EFI_ERROR (TempStatus)) {\r
1044 // Return one of the pre-approved error statuses\r
1045 return EFI_DEVICE_ERROR;\r
1046 }\r
1047\r
1048 // Put the data at the appropriate location inside the buffer area\r
1049 CopyMem ((VOID*)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);\r
1050\r
1051 // Write the modified buffer back to the NorFlash\r
36d66acf 1052 TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);\r
452a9ee1
BJ
1053 if (EFI_ERROR (TempStatus)) {\r
1054 // Return one of the pre-approved error statuses\r
1055 return EFI_DEVICE_ERROR;\r
1056 }\r
1057\r
1058 return EFI_SUCCESS;\r
1059}\r
1060\r
1061/*\r
1062 Although DiskIoDxe will automatically install the DiskIO protocol whenever\r
1063 we install the BlockIO protocol, its implementation is sub-optimal as it reads\r
1064 and writes entire blocks using the BlockIO protocol. In fact we can access\r
1065 NOR flash with a finer granularity than that, so we can improve performance\r
1066 by directly producing the DiskIO protocol.\r
1067*/\r
1068\r
1069/**\r
1070 Read BufferSize bytes from Offset into Buffer.\r
1071\r
1072 @param This Protocol instance pointer.\r
1073 @param MediaId Id of the media, changes every time the media is replaced.\r
1074 @param Offset The starting byte offset to read from\r
1075 @param BufferSize Size of Buffer\r
1076 @param Buffer Buffer containing read data\r
1077\r
1078 @retval EFI_SUCCESS The data was read correctly from the device.\r
1079 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.\r
1080 @retval EFI_NO_MEDIA There is no media in the device.\r
1081 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.\r
1082 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not\r
1083 valid for the device.\r
1084\r
1085**/\r
1086EFI_STATUS\r
1087EFIAPI\r
1088NorFlashDiskIoReadDisk (\r
1089 IN EFI_DISK_IO_PROTOCOL *This,\r
1090 IN UINT32 MediaId,\r
1091 IN UINT64 DiskOffset,\r
1092 IN UINTN BufferSize,\r
1093 OUT VOID *Buffer\r
1094 )\r
1095{\r
1096 NOR_FLASH_INSTANCE *Instance;\r
1097 UINT32 BlockSize;\r
1098 UINT32 BlockOffset;\r
1099 EFI_LBA Lba;\r
1100\r
1101 Instance = INSTANCE_FROM_DISKIO_THIS(This);\r
1102\r
1103 if (MediaId != Instance->Media.MediaId) {\r
1104 return EFI_MEDIA_CHANGED;\r
1105 }\r
1106\r
1107 BlockSize = Instance->Media.BlockSize;\r
1108 Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);\r
1109\r
1110 return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer);\r
1111}\r
1112\r
1113/**\r
1114 Writes a specified number of bytes to a device.\r
1115\r
1116 @param This Indicates a pointer to the calling context.\r
1117 @param MediaId ID of the medium to be written.\r
1118 @param Offset The starting byte offset on the logical block I/O device to write.\r
1119 @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device.\r
1120 @param Buffer A pointer to the buffer containing the data to be written.\r
1121\r
1122 @retval EFI_SUCCESS The data was written correctly to the device.\r
1123 @retval EFI_WRITE_PROTECTED The device can not be written to.\r
1124 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.\r
1125 @retval EFI_NO_MEDIA There is no media in the device.\r
1126 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.\r
1127 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not\r
1128 valid for the device.\r
1129\r
1130**/\r
1131EFI_STATUS\r
1132EFIAPI\r
1133NorFlashDiskIoWriteDisk (\r
1134 IN EFI_DISK_IO_PROTOCOL *This,\r
1135 IN UINT32 MediaId,\r
1136 IN UINT64 DiskOffset,\r
1137 IN UINTN BufferSize,\r
1138 IN VOID *Buffer\r
1139 )\r
1140{\r
1141 NOR_FLASH_INSTANCE *Instance;\r
1142 UINT32 BlockSize;\r
1143 UINT32 BlockOffset;\r
1144 EFI_LBA Lba;\r
1145 UINTN RemainingBytes;\r
1146 UINTN WriteSize;\r
1147 EFI_STATUS Status;\r
1148\r
1149 Instance = INSTANCE_FROM_DISKIO_THIS(This);\r
1150\r
1151 if (MediaId != Instance->Media.MediaId) {\r
1152 return EFI_MEDIA_CHANGED;\r
1153 }\r
1154\r
1155 BlockSize = Instance->Media.BlockSize;\r
1156 Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);\r
1157\r
1158 RemainingBytes = BufferSize;\r
1159\r
1160 // Write either all the remaining bytes, or the number of bytes that bring\r
1161 // us up to a block boundary, whichever is less.\r
1162 // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next\r
1163 // block boundary (even if it is already on one).\r
1164 WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset);\r
1165\r
1166 do {\r
1167 if (WriteSize == BlockSize) {\r
1168 // Write a full block\r
1169 Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32));\r
1170 } else {\r
1171 // Write a partial block\r
1172 Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer);\r
1173 }\r
1174 if (EFI_ERROR (Status)) {\r
1175 return Status;\r
1176 }\r
1177 // Now continue writing either all the remaining bytes or single blocks.\r
1178 RemainingBytes -= WriteSize;\r
1179 Buffer = (UINT8 *) Buffer + WriteSize;\r
1180 Lba++;\r
1181 BlockOffset = 0;\r
1182 WriteSize = MIN (RemainingBytes, BlockSize);\r
1183 } while (RemainingBytes);\r
1184\r
1185 return Status;\r
1186}\r
518c243d 1187\r
1e57a462 1188EFI_STATUS\r
1189NorFlashReset (\r
1190 IN NOR_FLASH_INSTANCE *Instance\r
1191 )\r
1192{\r
1193 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode\r
1194 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
1195 return EFI_SUCCESS;\r
1196}\r
1197\r
1dbbfc17
OM
1198/**\r
1199 Fixup internal data so that EFI can be call in virtual mode.\r
1200 Call the passed in Child Notify event and convert any pointers in\r
1201 lib to virtual mode.\r
1202\r
1203 @param[in] Event The Event that is being processed\r
1204 @param[in] Context Event Context\r
1205**/\r
1206VOID\r
1207EFIAPI\r
1208NorFlashVirtualNotifyEvent (\r
1209 IN EFI_EVENT Event,\r
1210 IN VOID *Context\r
1211 )\r
1212{\r
1213 UINTN Index;\r
1214\r
1215 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {\r
1216 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress);\r
1217 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress);\r
1218\r
1219 // Convert BlockIo protocol\r
1220 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);\r
1221 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);\r
1222 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);\r
1223 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);\r
1224\r
1225 // Convert Fvb\r
1226 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);\r
1227 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);\r
1228 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);\r
1229 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);\r
1230 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read);\r
1231 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);\r
1232 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write);\r
1233\r
452a9ee1
BJ
1234 if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {\r
1235 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->ShadowBuffer);\r
1dbbfc17
OM
1236 }\r
1237 }\r
1238\r
1239 return;\r
1240}\r
1241\r
1e57a462 1242EFI_STATUS\r
1243EFIAPI\r
1244NorFlashInitialise (\r
1245 IN EFI_HANDLE ImageHandle,\r
1246 IN EFI_SYSTEM_TABLE *SystemTable\r
1247 )\r
1248{\r
1249 EFI_STATUS Status;\r
1250 UINT32 Index;\r
1251 NOR_FLASH_DESCRIPTION* NorFlashDevices;\r
1e57a462 1252 BOOLEAN ContainVariableStorage;\r
1253\r
1254 Status = NorFlashPlatformInitialization ();\r
1255 if (EFI_ERROR(Status)) {\r
1256 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));\r
1257 return Status;\r
1258 }\r
1259\r
1dbbfc17 1260 Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);\r
1e57a462 1261 if (EFI_ERROR(Status)) {\r
1262 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));\r
1263 return Status;\r
1264 }\r
1265\r
1dbbfc17 1266 mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);\r
1e57a462 1267\r
1dbbfc17 1268 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {\r
1e57a462 1269 // Check if this NOR Flash device contain the variable storage region\r
1270 ContainVariableStorage =\r
1271 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&\r
1272 (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);\r
1273\r
1274 Status = NorFlashCreateInstance (\r
1275 NorFlashDevices[Index].DeviceBaseAddress,\r
1276 NorFlashDevices[Index].RegionBaseAddress,\r
1277 NorFlashDevices[Index].Size,\r
1278 Index,\r
1279 NorFlashDevices[Index].BlockSize,\r
1280 ContainVariableStorage,\r
1281 &NorFlashDevices[Index].Guid,\r
1282 &mNorFlashInstances[Index]\r
1283 );\r
1284 if (EFI_ERROR(Status)) {\r
1285 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));\r
1286 }\r
1287 }\r
1288\r
1dbbfc17
OM
1289 //\r
1290 // Register for the virtual address change event\r
1291 //\r
1292 Status = gBS->CreateEventEx (\r
1293 EVT_NOTIFY_SIGNAL,\r
1294 TPL_NOTIFY,\r
1295 NorFlashVirtualNotifyEvent,\r
1296 NULL,\r
1297 &gEfiEventVirtualAddressChangeGuid,\r
1298 &mNorFlashVirtualAddrChangeEvent\r
1299 );\r
1300 ASSERT_EFI_ERROR (Status);\r
1301\r
1e57a462 1302 return Status;\r
1303}\r