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