]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c
ArmPlatformPkg/Drivers/NorFlashDxe: Directly implement DiskIO protocol
[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", Instance->StartLba + 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, Instance->StartLba + Lba,
932 CurOffset & ~(0x3), sizeof(Tmp), &Tmp);
933 if (EFI_ERROR (TempStatus)) {
934 return EFI_DEVICE_ERROR;
935 }
936
937 // Physical address of word in NOR to write.
938 WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
939 Lba, BlockSize);
940 // The word of data that is to be written.
941 TmpBuf = *((UINT32*)(Buffer + (*NumBytes - BytesToWrite)));
942
943 // First do word aligned chunks.
944 if ((CurOffset & 0x3) == 0) {
945 if (BytesToWrite >= 4) {
946 // Is the destination still in 'erased' state?
947 if (~Tmp != 0) {
948 // Check to see if we are only changing bits to zero.
949 if ((Tmp ^ TmpBuf) & TmpBuf) {
950 DoErase = TRUE;
951 break;
952 }
953 }
954 // Write this word to NOR
955 WordToWrite = TmpBuf;
956 CurOffset += sizeof(TmpBuf);
957 BytesToWrite -= sizeof(TmpBuf);
958 } else {
959 // BytesToWrite < 4. Do small writes and left-overs
960 Mask = ~((~0) << (BytesToWrite * 8));
961 // Mask out the bytes we want.
962 TmpBuf &= Mask;
963 // Is the destination still in 'erased' state?
964 if ((Tmp & Mask) != Mask) {
965 // Check to see if we are only changing bits to zero.
966 if ((Tmp ^ TmpBuf) & TmpBuf) {
967 DoErase = TRUE;
968 break;
969 }
970 }
971 // Merge old and new data. Write merged word to NOR
972 WordToWrite = (Tmp & ~Mask) | TmpBuf;
973 CurOffset += BytesToWrite;
974 BytesToWrite = 0;
975 }
976 } else {
977 // Do multiple words, but starting unaligned.
978 if (BytesToWrite > (4 - (CurOffset & 0x3))) {
979 Mask = ((~0) << ((CurOffset & 0x3) * 8));
980 // Mask out the bytes we want.
981 TmpBuf &= Mask;
982 // Is the destination still in 'erased' state?
983 if ((Tmp & Mask) != Mask) {
984 // Check to see if we are only changing bits to zero.
985 if ((Tmp ^ TmpBuf) & TmpBuf) {
986 DoErase = TRUE;
987 break;
988 }
989 }
990 // Merge old and new data. Write merged word to NOR
991 WordToWrite = (Tmp & ~Mask) | TmpBuf;
992 BytesToWrite -= (4 - (CurOffset & 0x3));
993 CurOffset += (4 - (CurOffset & 0x3));
994 } else {
995 // Unaligned and fits in one word.
996 Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8);
997 // Mask out the bytes we want.
998 TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask;
999 // Is the destination still in 'erased' state?
1000 if ((Tmp & Mask) != Mask) {
1001 // Check to see if we are only changing bits to zero.
1002 if ((Tmp ^ TmpBuf) & TmpBuf) {
1003 DoErase = TRUE;
1004 break;
1005 }
1006 }
1007 // Merge old and new data. Write merged word to NOR
1008 WordToWrite = (Tmp & ~Mask) | TmpBuf;
1009 CurOffset += BytesToWrite;
1010 BytesToWrite = 0;
1011 }
1012 }
1013
1014 //
1015 // Write the word to NOR.
1016 //
1017
1018 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
1019 if (BlockAddress != PrevBlockAddress) {
1020 TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
1021 if (EFI_ERROR (TempStatus)) {
1022 return EFI_DEVICE_ERROR;
1023 }
1024 PrevBlockAddress = BlockAddress;
1025 }
1026 TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite);
1027 if (EFI_ERROR (TempStatus)) {
1028 return EFI_DEVICE_ERROR;
1029 }
1030 }
1031 // Exit if we got here and could write all the data. Otherwise do the
1032 // Erase-Write cycle.
1033 if (!DoErase) {
1034 return EFI_SUCCESS;
1035 }
1036 }
1037
1038 // Check we did get some memory. Buffer is BlockSize.
1039 if (Instance->ShadowBuffer == NULL) {
1040 DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
1041 return EFI_DEVICE_ERROR;
1042 }
1043
1044 // Read NOR Flash data into shadow buffer
1045 TempStatus = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, BlockSize, Instance->ShadowBuffer);
1046 if (EFI_ERROR (TempStatus)) {
1047 // Return one of the pre-approved error statuses
1048 return EFI_DEVICE_ERROR;
1049 }
1050
1051 // Put the data at the appropriate location inside the buffer area
1052 CopyMem ((VOID*)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
1053
1054 // Write the modified buffer back to the NorFlash
1055 TempStatus = NorFlashWriteBlocks (Instance, Instance->StartLba + Lba, BlockSize, Instance->ShadowBuffer);
1056 if (EFI_ERROR (TempStatus)) {
1057 // Return one of the pre-approved error statuses
1058 return EFI_DEVICE_ERROR;
1059 }
1060
1061 return EFI_SUCCESS;
1062 }
1063
1064 /*
1065 Although DiskIoDxe will automatically install the DiskIO protocol whenever
1066 we install the BlockIO protocol, its implementation is sub-optimal as it reads
1067 and writes entire blocks using the BlockIO protocol. In fact we can access
1068 NOR flash with a finer granularity than that, so we can improve performance
1069 by directly producing the DiskIO protocol.
1070 */
1071
1072 /**
1073 Read BufferSize bytes from Offset into Buffer.
1074
1075 @param This Protocol instance pointer.
1076 @param MediaId Id of the media, changes every time the media is replaced.
1077 @param Offset The starting byte offset to read from
1078 @param BufferSize Size of Buffer
1079 @param Buffer Buffer containing read data
1080
1081 @retval EFI_SUCCESS The data was read correctly from the device.
1082 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
1083 @retval EFI_NO_MEDIA There is no media in the device.
1084 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1085 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
1086 valid for the device.
1087
1088 **/
1089 EFI_STATUS
1090 EFIAPI
1091 NorFlashDiskIoReadDisk (
1092 IN EFI_DISK_IO_PROTOCOL *This,
1093 IN UINT32 MediaId,
1094 IN UINT64 DiskOffset,
1095 IN UINTN BufferSize,
1096 OUT VOID *Buffer
1097 )
1098 {
1099 NOR_FLASH_INSTANCE *Instance;
1100 UINT32 BlockSize;
1101 UINT32 BlockOffset;
1102 EFI_LBA Lba;
1103
1104 Instance = INSTANCE_FROM_DISKIO_THIS(This);
1105
1106 if (MediaId != Instance->Media.MediaId) {
1107 return EFI_MEDIA_CHANGED;
1108 }
1109
1110 BlockSize = Instance->Media.BlockSize;
1111 Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
1112
1113 return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer);
1114 }
1115
1116 /**
1117 Writes a specified number of bytes to a device.
1118
1119 @param This Indicates a pointer to the calling context.
1120 @param MediaId ID of the medium to be written.
1121 @param Offset The starting byte offset on the logical block I/O device to write.
1122 @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
1123 @param Buffer A pointer to the buffer containing the data to be written.
1124
1125 @retval EFI_SUCCESS The data was written correctly to the device.
1126 @retval EFI_WRITE_PROTECTED The device can not be written to.
1127 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
1128 @retval EFI_NO_MEDIA There is no media in the device.
1129 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1130 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
1131 valid for the device.
1132
1133 **/
1134 EFI_STATUS
1135 EFIAPI
1136 NorFlashDiskIoWriteDisk (
1137 IN EFI_DISK_IO_PROTOCOL *This,
1138 IN UINT32 MediaId,
1139 IN UINT64 DiskOffset,
1140 IN UINTN BufferSize,
1141 IN VOID *Buffer
1142 )
1143 {
1144 NOR_FLASH_INSTANCE *Instance;
1145 UINT32 BlockSize;
1146 UINT32 BlockOffset;
1147 EFI_LBA Lba;
1148 UINTN RemainingBytes;
1149 UINTN WriteSize;
1150 EFI_STATUS Status;
1151
1152 Instance = INSTANCE_FROM_DISKIO_THIS(This);
1153
1154 if (MediaId != Instance->Media.MediaId) {
1155 return EFI_MEDIA_CHANGED;
1156 }
1157
1158 BlockSize = Instance->Media.BlockSize;
1159 Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
1160
1161 RemainingBytes = BufferSize;
1162
1163 // Write either all the remaining bytes, or the number of bytes that bring
1164 // us up to a block boundary, whichever is less.
1165 // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
1166 // block boundary (even if it is already on one).
1167 WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset);
1168
1169 do {
1170 if (WriteSize == BlockSize) {
1171 // Write a full block
1172 Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32));
1173 } else {
1174 // Write a partial block
1175 Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer);
1176 }
1177 if (EFI_ERROR (Status)) {
1178 return Status;
1179 }
1180 // Now continue writing either all the remaining bytes or single blocks.
1181 RemainingBytes -= WriteSize;
1182 Buffer = (UINT8 *) Buffer + WriteSize;
1183 Lba++;
1184 BlockOffset = 0;
1185 WriteSize = MIN (RemainingBytes, BlockSize);
1186 } while (RemainingBytes);
1187
1188 return Status;
1189 }
1190
1191 EFI_STATUS
1192 NorFlashReset (
1193 IN NOR_FLASH_INSTANCE *Instance
1194 )
1195 {
1196 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
1197 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
1198 return EFI_SUCCESS;
1199 }
1200
1201 /**
1202 Fixup internal data so that EFI can be call in virtual mode.
1203 Call the passed in Child Notify event and convert any pointers in
1204 lib to virtual mode.
1205
1206 @param[in] Event The Event that is being processed
1207 @param[in] Context Event Context
1208 **/
1209 VOID
1210 EFIAPI
1211 NorFlashVirtualNotifyEvent (
1212 IN EFI_EVENT Event,
1213 IN VOID *Context
1214 )
1215 {
1216 UINTN Index;
1217
1218 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
1219 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress);
1220 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress);
1221
1222 // Convert BlockIo protocol
1223 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);
1224 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);
1225 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);
1226 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);
1227
1228 // Convert Fvb
1229 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
1230 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
1231 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
1232 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
1233 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read);
1234 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
1235 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write);
1236
1237 if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
1238 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->ShadowBuffer);
1239 }
1240 }
1241
1242 return;
1243 }
1244
1245 EFI_STATUS
1246 EFIAPI
1247 NorFlashInitialise (
1248 IN EFI_HANDLE ImageHandle,
1249 IN EFI_SYSTEM_TABLE *SystemTable
1250 )
1251 {
1252 EFI_STATUS Status;
1253 UINT32 Index;
1254 NOR_FLASH_DESCRIPTION* NorFlashDevices;
1255 BOOLEAN ContainVariableStorage;
1256
1257 Status = NorFlashPlatformInitialization ();
1258 if (EFI_ERROR(Status)) {
1259 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
1260 return Status;
1261 }
1262
1263 Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
1264 if (EFI_ERROR(Status)) {
1265 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
1266 return Status;
1267 }
1268
1269 mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);
1270
1271 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
1272 // Check if this NOR Flash device contain the variable storage region
1273 ContainVariableStorage =
1274 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&
1275 (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
1276
1277 Status = NorFlashCreateInstance (
1278 NorFlashDevices[Index].DeviceBaseAddress,
1279 NorFlashDevices[Index].RegionBaseAddress,
1280 NorFlashDevices[Index].Size,
1281 Index,
1282 NorFlashDevices[Index].BlockSize,
1283 ContainVariableStorage,
1284 &NorFlashDevices[Index].Guid,
1285 &mNorFlashInstances[Index]
1286 );
1287 if (EFI_ERROR(Status)) {
1288 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));
1289 }
1290 }
1291
1292 //
1293 // Register for the virtual address change event
1294 //
1295 Status = gBS->CreateEventEx (
1296 EVT_NOTIFY_SIGNAL,
1297 TPL_NOTIFY,
1298 NorFlashVirtualNotifyEvent,
1299 NULL,
1300 &gEfiEventVirtualAddressChangeGuid,
1301 &mNorFlashVirtualAddrChangeEvent
1302 );
1303 ASSERT_EFI_ERROR (Status);
1304
1305 return Status;
1306 }