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