]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c
ArmPlatformPkg/ArmVExpressSecLibRTSM: Only use extended name of system registers...
[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
66 FALSE, // SupportFvb ... NEED TO BE FILLED\r
1d5d0ae9 67 {\r
1e57a462 68 FvbGetAttributes, // GetAttributes\r
69 FvbSetAttributes, // SetAttributes\r
70 FvbGetPhysicalAddress, // GetPhysicalAddress\r
71 FvbGetBlockSize, // GetBlockSize\r
72 FvbRead, // Read\r
73 FvbWrite, // Write\r
1d5d0ae9 74 FvbEraseBlocks, // EraseBlocks\r
75 NULL, //ParentHandle\r
1e57a462 76 }, // FvbProtoccol;\r
2dff0c1a 77 NULL, // FvbBuffer\r
1e57a462 78 {\r
79 {\r
80 {\r
81 HARDWARE_DEVICE_PATH,\r
82 HW_VENDOR_DP,\r
83 (UINT8)( sizeof(VENDOR_DEVICE_PATH) ),\r
84 (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8),\r
85 },\r
86 { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, // GUID ... NEED TO BE FILLED\r
87 },\r
88 {\r
89 END_DEVICE_PATH_TYPE,\r
90 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
91 sizeof (EFI_DEVICE_PATH_PROTOCOL),\r
92 0\r
93 }\r
94 } // DevicePath\r
95};\r
96\r
97EFI_STATUS\r
98NorFlashCreateInstance (\r
99 IN UINTN NorFlashDeviceBase,\r
100 IN UINTN NorFlashRegionBase,\r
101 IN UINTN NorFlashSize,\r
102 IN UINT32 MediaId,\r
103 IN UINT32 BlockSize,\r
104 IN BOOLEAN SupportFvb,\r
105 IN CONST GUID *NorFlashGuid,\r
106 OUT NOR_FLASH_INSTANCE** NorFlashInstance\r
107 )\r
108{\r
109 EFI_STATUS Status;\r
110 NOR_FLASH_INSTANCE* Instance;\r
111\r
112 ASSERT(NorFlashInstance != NULL);\r
113\r
2dff0c1a 114 Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);\r
1e57a462 115 if (Instance == NULL) {\r
116 return EFI_OUT_OF_RESOURCES;\r
117 }\r
118\r
119 Instance->DeviceBaseAddress = NorFlashDeviceBase;\r
120 Instance->RegionBaseAddress = NorFlashRegionBase;\r
121 Instance->Size = NorFlashSize;\r
122\r
123 Instance->BlockIoProtocol.Media = &Instance->Media;\r
124 Instance->Media.MediaId = MediaId;\r
125 Instance->Media.BlockSize = BlockSize;\r
126 Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;\r
1d5d0ae9 127\r
2dff0c1a 128 CopyGuid (&Instance->DevicePath.Vendor.Guid, NorFlashGuid);\r
1e57a462 129\r
130 if (SupportFvb) {\r
131 Instance->SupportFvb = TRUE;\r
132 Instance->Initialize = NorFlashFvbInitialize;\r
2dff0c1a
OM
133 Instance->FvbBuffer = AllocateRuntimePool (BlockSize);;\r
134 if (Instance->FvbBuffer == NULL) {\r
135 return EFI_OUT_OF_RESOURCES;\r
136 }\r
1e57a462 137\r
138 Status = gBS->InstallMultipleProtocolInterfaces (\r
139 &Instance->Handle,\r
140 &gEfiDevicePathProtocolGuid, &Instance->DevicePath,\r
141 &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol,\r
142 &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol,\r
143 NULL\r
144 );\r
145 if (EFI_ERROR(Status)) {\r
2dff0c1a 146 FreePool (Instance);\r
1e57a462 147 return Status;\r
148 }\r
149 } else {\r
150 Instance->Initialized = TRUE;\r
151\r
152 Status = gBS->InstallMultipleProtocolInterfaces (\r
153 &Instance->Handle,\r
154 &gEfiDevicePathProtocolGuid, &Instance->DevicePath,\r
155 &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol,\r
156 NULL\r
157 );\r
158 if (EFI_ERROR(Status)) {\r
2dff0c1a 159 FreePool (Instance);\r
1e57a462 160 return Status;\r
161 }\r
162 }\r
1d5d0ae9 163\r
164 *NorFlashInstance = Instance;\r
1e57a462 165 return Status;\r
166}\r
167\r
168UINT32\r
169NorFlashReadStatusRegister (\r
170 IN NOR_FLASH_INSTANCE *Instance,\r
171 IN UINTN SR_Address\r
172 )\r
173{\r
174 // Prepare to read the status register\r
175 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);\r
176 return MmioRead32 (Instance->DeviceBaseAddress);\r
177}\r
178\r
179\r
180BOOLEAN\r
181NorFlashBlockIsLocked (\r
182 IN NOR_FLASH_INSTANCE *Instance,\r
183 IN UINTN BlockAddress\r
184 )\r
185{\r
186 UINT32 LockStatus;\r
187 BOOLEAN BlockIsLocked;\r
188\r
189 BlockIsLocked = TRUE;\r
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
204 if ((LockStatus & 0x1) == 0) {\r
205 // This means the block is unlocked\r
206 DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress));\r
207 BlockIsLocked = FALSE;\r
208 }\r
209\r
210 return BlockIsLocked;\r
211}\r
212\r
213\r
214EFI_STATUS\r
215NorFlashUnlockSingleBlock (\r
216 IN NOR_FLASH_INSTANCE *Instance,\r
217 IN UINTN BlockAddress\r
218 )\r
219{\r
220 EFI_STATUS Status = EFI_SUCCESS;\r
221 UINT32 LockStatus;\r
222\r
223 // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations\r
224 // and to protect shared data structures.\r
225\r
226 if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) {\r
227 do {\r
228 // Request a lock setup\r
229 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);\r
230\r
231 // Request an unlock\r
232 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);\r
233\r
234 // Send command for reading device id\r
235 SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);\r
236\r
237 // Read block lock status\r
238 LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2));\r
239\r
240 // Decode block lock status\r
241 LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus);\r
242 } while ((LockStatus & 0x1) == 1);\r
243 } else {\r
244 // Request a lock setup\r
245 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);\r
246\r
247 // Request an unlock\r
248 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);\r
249\r
250 // Wait until the status register gives us the all clear\r
251 do {\r
252 LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);\r
253 } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
254 }\r
255\r
256 // Put device back into Read Array mode\r
257 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY);\r
258\r
259 DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress, Status));\r
260\r
261 return Status;\r
262}\r
263\r
264\r
265EFI_STATUS\r
266NorFlashUnlockSingleBlockIfNecessary (\r
267 IN NOR_FLASH_INSTANCE *Instance,\r
268 IN UINTN BlockAddress\r
269 )\r
270{\r
271 EFI_STATUS Status = EFI_SUCCESS;\r
272\r
273 if (NorFlashBlockIsLocked (Instance, BlockAddress) == TRUE) {\r
274 Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);\r
275 }\r
276\r
277 return Status;\r
278}\r
279\r
280\r
281/**\r
282 * The following function presumes that the block has already been unlocked.\r
283 **/\r
284EFI_STATUS\r
285NorFlashEraseSingleBlock (\r
286 IN NOR_FLASH_INSTANCE *Instance,\r
287 IN UINTN BlockAddress\r
288 )\r
289{\r
290 EFI_STATUS Status;\r
291 UINT32 StatusRegister;\r
292\r
293 Status = EFI_SUCCESS;\r
294\r
295 // Request a block erase and then confirm it\r
296 SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP);\r
297 SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM);\r
298\r
299 // Wait until the status register gives us the all clear\r
300 do {\r
301 StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);\r
302 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
303\r
304 if (StatusRegister & P30_SR_BIT_VPP) {\r
305 DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress));\r
306 Status = EFI_DEVICE_ERROR;\r
307 }\r
308\r
309 if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) {\r
310 DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress));\r
311 Status = EFI_DEVICE_ERROR;\r
312 }\r
313\r
314 if (StatusRegister & P30_SR_BIT_ERASE) {\r
315 DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister));\r
316 Status = EFI_DEVICE_ERROR;\r
317 }\r
318\r
319 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {\r
320 // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...\r
321 DEBUG((EFI_D_INFO,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress));\r
322 Status = EFI_WRITE_PROTECTED;\r
323 }\r
324\r
325 if (EFI_ERROR(Status)) {\r
326 // Clear the Status Register\r
327 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);\r
328 }\r
329\r
330 // Put device back into Read Array mode\r
331 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
332\r
333 return Status;\r
334}\r
335\r
336/**\r
337 * The following function presumes that the block has already been unlocked.\r
338 **/\r
339EFI_STATUS\r
340NorFlashUnlockAndEraseSingleBlock (\r
341 IN NOR_FLASH_INSTANCE *Instance,\r
342 IN UINTN BlockAddress\r
343 )\r
344{\r
345 EFI_STATUS Status;\r
346 UINTN Index;\r
347 EFI_TPL OriginalTPL;\r
348\r
2dff0c1a
OM
349 if (!EfiAtRuntime ()) {\r
350 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
351 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
352 } else {\r
353 // This initialization is only to prevent the compiler to complain about the\r
354 // use of uninitialized variables\r
355 OriginalTPL = TPL_HIGH_LEVEL;\r
356 }\r
1e57a462 357\r
358 Index = 0;\r
359 // The block erase might fail a first time (SW bug ?). Retry it ...\r
360 do {\r
361 // Unlock the block if we have to\r
362 Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);\r
363 if (!EFI_ERROR(Status)) {\r
364 Status = NorFlashEraseSingleBlock (Instance, BlockAddress);\r
365 }\r
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
382EFI_STATUS\r
383NorFlashWriteSingleWord (\r
384 IN NOR_FLASH_INSTANCE *Instance,\r
385 IN UINTN WordAddress,\r
386 IN UINT32 WriteData\r
387 )\r
388{\r
389 EFI_STATUS Status;\r
390 UINT32 StatusRegister;\r
391\r
392 Status = EFI_SUCCESS;\r
393\r
394 // Request a write single word command\r
395 SEND_NOR_COMMAND(WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);\r
396\r
397 // Store the word into NOR Flash;\r
398 MmioWrite32 (WordAddress, WriteData);\r
399\r
400 // Wait for the write to complete and then check for any errors; i.e. check the Status Register\r
401 do {\r
402 // Prepare to read the status register\r
403 StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);\r
404 // The chip is busy while the WRITE bit is not asserted\r
405 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
406\r
407\r
408 // Perform a full status check:\r
409 // Mask the relevant bits of Status Register.\r
410 // Everything should be zero, if not, we have a problem\r
411\r
412 if (StatusRegister & P30_SR_BIT_VPP) {\r
413 DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress));\r
414 Status = EFI_DEVICE_ERROR;\r
415 }\r
416\r
417 if (StatusRegister & P30_SR_BIT_PROGRAM) {\r
418 DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress));\r
419 Status = EFI_DEVICE_ERROR;\r
420 }\r
421\r
422 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {\r
423 DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress));\r
424 Status = EFI_DEVICE_ERROR;\r
425 }\r
426\r
427 if (!EFI_ERROR(Status)) {\r
428 // Clear the Status Register\r
429 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);\r
430 }\r
431\r
432 // Put device back into Read Array mode\r
433 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
434\r
435 return Status;\r
436}\r
437\r
438/*\r
439 * Writes data to the NOR Flash using the Buffered Programming method.\r
440 *\r
441 * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.\r
442 * Therefore this function will only handle buffers up to 32 words or 128 bytes.\r
443 * To deal with larger buffers, call this function again.\r
444 *\r
445 * This function presumes that both the TargetAddress and the TargetAddress+BufferSize\r
446 * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.\r
447 *\r
448 * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,\r
449 * then programming time is doubled and power consumption is increased.\r
450 * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.\r
451 * i.e. the last 4 bits of the target start address must be zero: 0x......00\r
452 */\r
453EFI_STATUS\r
454NorFlashWriteBuffer (\r
455 IN NOR_FLASH_INSTANCE *Instance,\r
456 IN UINTN TargetAddress,\r
457 IN UINTN BufferSizeInBytes,\r
458 IN UINT32 *Buffer\r
459 )\r
460{\r
461 EFI_STATUS Status;\r
462 UINTN BufferSizeInWords;\r
463 UINTN Count;\r
464 volatile UINT32 *Data;\r
465 UINTN WaitForBuffer;\r
466 BOOLEAN BufferAvailable;\r
467 UINT32 StatusRegister;\r
468\r
469 WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS;\r
470 BufferAvailable = FALSE;\r
471\r
472 // Check that the target address does not cross a 32-word boundary.\r
473 if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) {\r
474 return EFI_INVALID_PARAMETER;\r
475 }\r
476\r
477 // Check there are some data to program\r
478 if (BufferSizeInBytes == 0) {\r
479 return EFI_BUFFER_TOO_SMALL;\r
480 }\r
481\r
482 // Check that the buffer size does not exceed the maximum hardware buffer size on chip.\r
483 if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) {\r
484 return EFI_BAD_BUFFER_SIZE;\r
485 }\r
486\r
487 // Check that the buffer size is a multiple of 32-bit words\r
488 if ((BufferSizeInBytes % 4) != 0) {\r
489 return EFI_BAD_BUFFER_SIZE;\r
490 }\r
491\r
492 // Pre-programming conditions checked, now start the algorithm.\r
493\r
494 // Prepare the data destination address\r
495 Data = (UINT32 *)TargetAddress;\r
496\r
497 // Check the availability of the buffer\r
498 do {\r
499 // Issue the Buffered Program Setup command\r
500 SEND_NOR_COMMAND(TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP);\r
501\r
502 // Read back the status register bit#7 from the same address\r
503 if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) {\r
504 BufferAvailable = TRUE;\r
505 }\r
506\r
507 // Update the loop counter\r
508 WaitForBuffer--;\r
509\r
510 } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE));\r
511\r
512 // The buffer was not available for writing\r
513 if (WaitForBuffer == 0) {\r
514 Status = EFI_DEVICE_ERROR;\r
515 goto EXIT;\r
516 }\r
517\r
518 // From now on we work in 32-bit words\r
519 BufferSizeInWords = BufferSizeInBytes / (UINTN)4;\r
520\r
521 // Write the word count, which is (buffer_size_in_words - 1),\r
522 // because word count 0 means one word.\r
523 SEND_NOR_COMMAND(TargetAddress, 0, (BufferSizeInWords - 1));\r
524\r
525 // Write the data to the NOR Flash, advancing each address by 4 bytes\r
526 for(Count=0; Count < BufferSizeInWords; Count++, Data++, Buffer++) {\r
527 *Data = *Buffer;\r
528 }\r
529\r
530 // Issue the Buffered Program Confirm command, to start the programming operation\r
531 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);\r
532\r
533 // Wait for the write to complete and then check for any errors; i.e. check the Status Register\r
534 do {\r
535 StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress);\r
536 // The chip is busy while the WRITE bit is not asserted\r
537 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);\r
538\r
539\r
540 // Perform a full status check:\r
541 // Mask the relevant bits of Status Register.\r
542 // Everything should be zero, if not, we have a problem\r
543\r
544 Status = EFI_SUCCESS;\r
545\r
546 if (StatusRegister & P30_SR_BIT_VPP) {\r
547 DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress));\r
548 Status = EFI_DEVICE_ERROR;\r
549 }\r
550\r
551 if (StatusRegister & P30_SR_BIT_PROGRAM) {\r
552 DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress));\r
553 Status = EFI_DEVICE_ERROR;\r
554 }\r
555\r
556 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {\r
557 DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress));\r
558 Status = EFI_DEVICE_ERROR;\r
559 }\r
560\r
561 if (!EFI_ERROR(Status)) {\r
562 // Clear the Status Register\r
563 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);\r
564 }\r
565\r
566EXIT:\r
567 // Put device back into Read Array mode\r
568 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
569\r
570 return Status;\r
571}\r
572\r
573EFI_STATUS\r
574NorFlashWriteSingleBlock (\r
575 IN NOR_FLASH_INSTANCE *Instance,\r
576 IN EFI_LBA Lba,\r
577 IN UINT32 *DataBuffer,\r
578 IN UINT32 BlockSizeInWords\r
579 )\r
580{\r
581 EFI_STATUS Status;\r
582 UINTN WordAddress;\r
583 UINT32 WordIndex;\r
584 UINTN BufferIndex;\r
585 UINTN BlockAddress;\r
586 UINTN BuffersInBlock;\r
587 UINTN RemainingWords;\r
588 EFI_TPL OriginalTPL;\r
518c243d 589 UINTN Cnt;\r
1e57a462 590\r
591 Status = EFI_SUCCESS;\r
592\r
593 // Get the physical address of the block\r
594 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);\r
595\r
596 // Start writing from the first address at the start of the block\r
597 WordAddress = BlockAddress;\r
598\r
2dff0c1a
OM
599 if (!EfiAtRuntime ()) {\r
600 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.\r
601 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
602 } else {\r
603 // This initialization is only to prevent the compiler to complain about the\r
604 // use of uninitialized variables\r
605 OriginalTPL = TPL_HIGH_LEVEL;\r
606 }\r
1e57a462 607\r
608 Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);\r
609 if (EFI_ERROR(Status)) {\r
610 DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));\r
611 goto EXIT;\r
612 }\r
613\r
614 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.\r
615\r
616 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero\r
617 if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {\r
618\r
619 // First, break the entire block into buffer-sized chunks.\r
82745213 620 BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;\r
1e57a462 621\r
622 // Then feed each buffer chunk to the NOR Flash\r
518c243d 623 // If a buffer does not contain any data, don't write it.\r
1e57a462 624 for(BufferIndex=0;\r
625 BufferIndex < BuffersInBlock;\r
626 BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS\r
627 ) {\r
518c243d
HL
628 // Check the buffer to see if it contains any data (not set all 1s).\r
629 for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {\r
630 if (~DataBuffer[Cnt] != 0 ) {\r
631 // Some data found, write the buffer.\r
632 Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES,\r
633 DataBuffer);\r
634 if (EFI_ERROR(Status)) {\r
635 goto EXIT;\r
636 }\r
637 break;\r
638 }\r
1e57a462 639 }\r
640 }\r
641\r
642 // Finally, finish off any remaining words that are less than the maximum size of the buffer\r
643 RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;\r
644\r
645 if(RemainingWords != 0) {\r
646 Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);\r
647 if (EFI_ERROR(Status)) {\r
648 goto EXIT;\r
649 }\r
650 }\r
651\r
652 } else {\r
653 // For now, use the single word programming algorithm\r
654 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,\r
655 // i.e. which ends in the range 0x......01 - 0x......7F.\r
656 for(WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {\r
657 Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);\r
658 if (EFI_ERROR(Status)) {\r
659 goto EXIT;\r
660 }\r
661 }\r
662 }\r
663\r
664EXIT:\r
2dff0c1a
OM
665 if (!EfiAtRuntime ()) {\r
666 // Interruptions can resume.\r
667 gBS->RestoreTPL (OriginalTPL);\r
668 }\r
1e57a462 669\r
670 if (EFI_ERROR(Status)) {\r
671 DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));\r
672 }\r
673 return Status;\r
674}\r
675\r
676\r
677EFI_STATUS\r
678NorFlashWriteBlocks (\r
679 IN NOR_FLASH_INSTANCE *Instance,\r
680 IN EFI_LBA Lba,\r
681 IN UINTN BufferSizeInBytes,\r
682 IN VOID *Buffer\r
683 )\r
684{\r
685 UINT32 *pWriteBuffer;\r
686 EFI_STATUS Status = EFI_SUCCESS;\r
687 EFI_LBA CurrentBlock;\r
688 UINT32 BlockSizeInWords;\r
689 UINT32 NumBlocks;\r
690 UINT32 BlockCount;\r
691\r
692 // The buffer must be valid\r
693 if (Buffer == NULL) {\r
694 return EFI_INVALID_PARAMETER;\r
695 }\r
696\r
697 if(Instance->Media.ReadOnly == TRUE) {\r
698 return EFI_WRITE_PROTECTED;\r
699 }\r
700\r
701 // We must have some bytes to read\r
702 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));\r
703 if(BufferSizeInBytes == 0) {\r
704 return EFI_BAD_BUFFER_SIZE;\r
705 }\r
706\r
707 // The size of the buffer must be a multiple of the block size\r
708 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize));\r
709 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {\r
710 return EFI_BAD_BUFFER_SIZE;\r
711 }\r
712\r
713 // All blocks must be within the device\r
714 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;\r
715\r
716 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba));\r
717\r
718 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {\r
719 DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));\r
720 return EFI_INVALID_PARAMETER;\r
721 }\r
722\r
723 BlockSizeInWords = Instance->Media.BlockSize / 4;\r
724\r
725 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer\r
726 // to a proper data type, so use *ReadBuffer\r
727 pWriteBuffer = (UINT32 *)Buffer;\r
728\r
729 CurrentBlock = Lba;\r
730 for (BlockCount=0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {\r
731\r
732 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));\r
733\r
734 Status = NorFlashWriteSingleBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);\r
735\r
736 if (EFI_ERROR(Status)) {\r
737 break;\r
738 }\r
739 }\r
740\r
741 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));\r
742 return Status;\r
743}\r
744\r
745EFI_STATUS\r
746NorFlashReadBlocks (\r
747 IN NOR_FLASH_INSTANCE *Instance,\r
748 IN EFI_LBA Lba,\r
749 IN UINTN BufferSizeInBytes,\r
750 OUT VOID *Buffer\r
751 )\r
752{\r
753 UINT32 NumBlocks;\r
754 UINTN StartAddress;\r
755\r
3d783074 756 DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",\r
757 BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba));\r
758\r
1e57a462 759 // The buffer must be valid\r
760 if (Buffer == NULL) {\r
761 return EFI_INVALID_PARAMETER;\r
762 }\r
763\r
3d783074 764 // Return if we have not any byte to read \r
765 if (BufferSizeInBytes == 0) {\r
766 return EFI_SUCCESS;\r
1e57a462 767 }\r
768\r
769 // The size of the buffer must be a multiple of the block size\r
1e57a462 770 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {\r
771 return EFI_BAD_BUFFER_SIZE;\r
772 }\r
773\r
774 // All blocks must be within the device\r
775 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;\r
776\r
1e57a462 777 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {\r
778 DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));\r
779 return EFI_INVALID_PARAMETER;\r
780 }\r
781\r
782 // Get the address to start reading from\r
783 StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,\r
784 Lba,\r
785 Instance->Media.BlockSize\r
786 );\r
787\r
788 // Put the device into Read Array mode\r
789 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
790\r
791 // Readout the data\r
792 CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes);\r
793\r
794 return EFI_SUCCESS;\r
795}\r
796\r
518c243d
HL
797EFI_STATUS\r
798NorFlashRead (\r
799 IN NOR_FLASH_INSTANCE *Instance,\r
800 IN EFI_LBA Lba,\r
801 IN UINTN Offset,\r
802 IN UINTN BufferSizeInBytes,\r
803 OUT VOID *Buffer\r
804 )\r
805{\r
806 UINT32 NumBlocks;\r
807 UINTN StartAddress;\r
808\r
809 // The buffer must be valid\r
810 if (Buffer == NULL) {\r
811 return EFI_INVALID_PARAMETER;\r
812 }\r
813\r
814 // Return if we have not any byte to read\r
815 if (BufferSizeInBytes == 0) {\r
816 return EFI_SUCCESS;\r
817 }\r
818\r
819 // All blocks must be within the device\r
820 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;\r
821\r
822 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {\r
823 DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed last block\n"));\r
824 return EFI_INVALID_PARAMETER;\r
825 }\r
826\r
827 if (Offset + BufferSizeInBytes >= Instance->Size) {\r
828 DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));\r
829 return EFI_INVALID_PARAMETER;\r
830 }\r
831\r
832 // Get the address to start reading from\r
833 StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,\r
834 Lba,\r
835 Instance->Media.BlockSize\r
836 );\r
837\r
838 // Put the device into Read Array mode\r
839 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
840\r
841 // Readout the data\r
842 CopyMem (Buffer, (UINTN *)(StartAddress + Offset), BufferSizeInBytes);\r
843\r
844 return EFI_SUCCESS;\r
845}\r
846\r
847\r
1e57a462 848EFI_STATUS\r
849NorFlashReset (\r
850 IN NOR_FLASH_INSTANCE *Instance\r
851 )\r
852{\r
853 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode\r
854 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);\r
855 return EFI_SUCCESS;\r
856}\r
857\r
1dbbfc17
OM
858/**\r
859 Fixup internal data so that EFI can be call in virtual mode.\r
860 Call the passed in Child Notify event and convert any pointers in\r
861 lib to virtual mode.\r
862\r
863 @param[in] Event The Event that is being processed\r
864 @param[in] Context Event Context\r
865**/\r
866VOID\r
867EFIAPI\r
868NorFlashVirtualNotifyEvent (\r
869 IN EFI_EVENT Event,\r
870 IN VOID *Context\r
871 )\r
872{\r
873 UINTN Index;\r
874\r
875 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {\r
876 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress);\r
877 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress);\r
878\r
879 // Convert BlockIo protocol\r
880 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);\r
881 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);\r
882 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);\r
883 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);\r
884\r
885 // Convert Fvb\r
886 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);\r
887 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);\r
888 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);\r
889 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);\r
890 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read);\r
891 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);\r
892 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write);\r
893\r
894 if (mNorFlashInstances[Index]->FvbBuffer != NULL) {\r
895 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbBuffer);\r
896 }\r
897 }\r
898\r
899 return;\r
900}\r
901\r
1e57a462 902EFI_STATUS\r
903EFIAPI\r
904NorFlashInitialise (\r
905 IN EFI_HANDLE ImageHandle,\r
906 IN EFI_SYSTEM_TABLE *SystemTable\r
907 )\r
908{\r
909 EFI_STATUS Status;\r
910 UINT32 Index;\r
911 NOR_FLASH_DESCRIPTION* NorFlashDevices;\r
1e57a462 912 BOOLEAN ContainVariableStorage;\r
913\r
914 Status = NorFlashPlatformInitialization ();\r
915 if (EFI_ERROR(Status)) {\r
916 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));\r
917 return Status;\r
918 }\r
919\r
1dbbfc17 920 Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);\r
1e57a462 921 if (EFI_ERROR(Status)) {\r
922 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));\r
923 return Status;\r
924 }\r
925\r
1dbbfc17 926 mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);\r
1e57a462 927\r
1dbbfc17 928 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {\r
1e57a462 929 // Check if this NOR Flash device contain the variable storage region\r
930 ContainVariableStorage =\r
931 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&\r
932 (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);\r
933\r
934 Status = NorFlashCreateInstance (\r
935 NorFlashDevices[Index].DeviceBaseAddress,\r
936 NorFlashDevices[Index].RegionBaseAddress,\r
937 NorFlashDevices[Index].Size,\r
938 Index,\r
939 NorFlashDevices[Index].BlockSize,\r
940 ContainVariableStorage,\r
941 &NorFlashDevices[Index].Guid,\r
942 &mNorFlashInstances[Index]\r
943 );\r
944 if (EFI_ERROR(Status)) {\r
945 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));\r
946 }\r
947 }\r
948\r
1dbbfc17
OM
949 //\r
950 // Register for the virtual address change event\r
951 //\r
952 Status = gBS->CreateEventEx (\r
953 EVT_NOTIFY_SIGNAL,\r
954 TPL_NOTIFY,\r
955 NorFlashVirtualNotifyEvent,\r
956 NULL,\r
957 &gEfiEventVirtualAddressChangeGuid,\r
958 &mNorFlashVirtualAddrChangeEvent\r
959 );\r
960 ASSERT_EFI_ERROR (Status);\r
961\r
1e57a462 962 return Status;\r
963}\r