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