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