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