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