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