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