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