]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c
82b9f3f742b9917e525a5f259be49350c541e827
[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 UINTN Cnt;
590
591 Status = EFI_SUCCESS;
592
593 // Get the physical address of the block
594 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);
595
596 // Start writing from the first address at the start of the block
597 WordAddress = BlockAddress;
598
599 if (!EfiAtRuntime ()) {
600 // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
601 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
602 } else {
603 // This initialization is only to prevent the compiler to complain about the
604 // use of uninitialized variables
605 OriginalTPL = TPL_HIGH_LEVEL;
606 }
607
608 Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);
609 if (EFI_ERROR(Status)) {
610 DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));
611 goto EXIT;
612 }
613
614 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
615
616 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
617 if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {
618
619 // First, break the entire block into buffer-sized chunks.
620 BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
621
622 // Then feed each buffer chunk to the NOR Flash
623 // If a buffer does not contain any data, don't write it.
624 for(BufferIndex=0;
625 BufferIndex < BuffersInBlock;
626 BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
627 ) {
628 // Check the buffer to see if it contains any data (not set all 1s).
629 for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {
630 if (~DataBuffer[Cnt] != 0 ) {
631 // Some data found, write the buffer.
632 Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES,
633 DataBuffer);
634 if (EFI_ERROR(Status)) {
635 goto EXIT;
636 }
637 break;
638 }
639 }
640 }
641
642 // Finally, finish off any remaining words that are less than the maximum size of the buffer
643 RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;
644
645 if(RemainingWords != 0) {
646 Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);
647 if (EFI_ERROR(Status)) {
648 goto EXIT;
649 }
650 }
651
652 } else {
653 // For now, use the single word programming algorithm
654 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
655 // i.e. which ends in the range 0x......01 - 0x......7F.
656 for(WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {
657 Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);
658 if (EFI_ERROR(Status)) {
659 goto EXIT;
660 }
661 }
662 }
663
664 EXIT:
665 if (!EfiAtRuntime ()) {
666 // Interruptions can resume.
667 gBS->RestoreTPL (OriginalTPL);
668 }
669
670 if (EFI_ERROR(Status)) {
671 DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
672 }
673 return Status;
674 }
675
676
677 EFI_STATUS
678 NorFlashWriteBlocks (
679 IN NOR_FLASH_INSTANCE *Instance,
680 IN EFI_LBA Lba,
681 IN UINTN BufferSizeInBytes,
682 IN VOID *Buffer
683 )
684 {
685 UINT32 *pWriteBuffer;
686 EFI_STATUS Status = EFI_SUCCESS;
687 EFI_LBA CurrentBlock;
688 UINT32 BlockSizeInWords;
689 UINT32 NumBlocks;
690 UINT32 BlockCount;
691
692 // The buffer must be valid
693 if (Buffer == NULL) {
694 return EFI_INVALID_PARAMETER;
695 }
696
697 if(Instance->Media.ReadOnly == TRUE) {
698 return EFI_WRITE_PROTECTED;
699 }
700
701 // We must have some bytes to read
702 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));
703 if(BufferSizeInBytes == 0) {
704 return EFI_BAD_BUFFER_SIZE;
705 }
706
707 // The size of the buffer must be a multiple of the block size
708 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize));
709 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
710 return EFI_BAD_BUFFER_SIZE;
711 }
712
713 // All blocks must be within the device
714 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
715
716 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba));
717
718 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
719 DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
720 return EFI_INVALID_PARAMETER;
721 }
722
723 BlockSizeInWords = Instance->Media.BlockSize / 4;
724
725 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
726 // to a proper data type, so use *ReadBuffer
727 pWriteBuffer = (UINT32 *)Buffer;
728
729 CurrentBlock = Lba;
730 for (BlockCount=0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {
731
732 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));
733
734 Status = NorFlashWriteSingleBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);
735
736 if (EFI_ERROR(Status)) {
737 break;
738 }
739 }
740
741 DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));
742 return Status;
743 }
744
745 EFI_STATUS
746 NorFlashReadBlocks (
747 IN NOR_FLASH_INSTANCE *Instance,
748 IN EFI_LBA Lba,
749 IN UINTN BufferSizeInBytes,
750 OUT VOID *Buffer
751 )
752 {
753 UINT32 NumBlocks;
754 UINTN StartAddress;
755
756 DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
757 BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba));
758
759 // The buffer must be valid
760 if (Buffer == NULL) {
761 return EFI_INVALID_PARAMETER;
762 }
763
764 // Return if we have not any byte to read
765 if (BufferSizeInBytes == 0) {
766 return EFI_SUCCESS;
767 }
768
769 // The size of the buffer must be a multiple of the block size
770 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
771 return EFI_BAD_BUFFER_SIZE;
772 }
773
774 // All blocks must be within the device
775 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
776
777 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
778 DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
779 return EFI_INVALID_PARAMETER;
780 }
781
782 // Get the address to start reading from
783 StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
784 Lba,
785 Instance->Media.BlockSize
786 );
787
788 // Put the device into Read Array mode
789 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
790
791 // Readout the data
792 CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes);
793
794 return EFI_SUCCESS;
795 }
796
797 EFI_STATUS
798 NorFlashRead (
799 IN NOR_FLASH_INSTANCE *Instance,
800 IN EFI_LBA Lba,
801 IN UINTN Offset,
802 IN UINTN BufferSizeInBytes,
803 OUT VOID *Buffer
804 )
805 {
806 UINT32 NumBlocks;
807 UINTN StartAddress;
808
809 // The buffer must be valid
810 if (Buffer == NULL) {
811 return EFI_INVALID_PARAMETER;
812 }
813
814 // Return if we have not any byte to read
815 if (BufferSizeInBytes == 0) {
816 return EFI_SUCCESS;
817 }
818
819 // All blocks must be within the device
820 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
821
822 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
823 DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed last block\n"));
824 return EFI_INVALID_PARAMETER;
825 }
826
827 if (Offset + BufferSizeInBytes >= Instance->Size) {
828 DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));
829 return EFI_INVALID_PARAMETER;
830 }
831
832 // Get the address to start reading from
833 StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
834 Lba,
835 Instance->Media.BlockSize
836 );
837
838 // Put the device into Read Array mode
839 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
840
841 // Readout the data
842 CopyMem (Buffer, (UINTN *)(StartAddress + Offset), BufferSizeInBytes);
843
844 return EFI_SUCCESS;
845 }
846
847
848 EFI_STATUS
849 NorFlashReset (
850 IN NOR_FLASH_INSTANCE *Instance
851 )
852 {
853 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
854 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
855 return EFI_SUCCESS;
856 }
857
858 /**
859 Fixup internal data so that EFI can be call in virtual mode.
860 Call the passed in Child Notify event and convert any pointers in
861 lib to virtual mode.
862
863 @param[in] Event The Event that is being processed
864 @param[in] Context Event Context
865 **/
866 VOID
867 EFIAPI
868 NorFlashVirtualNotifyEvent (
869 IN EFI_EVENT Event,
870 IN VOID *Context
871 )
872 {
873 UINTN Index;
874
875 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
876 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress);
877 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress);
878
879 // Convert BlockIo protocol
880 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);
881 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);
882 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);
883 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);
884
885 // Convert Fvb
886 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
887 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
888 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
889 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
890 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read);
891 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
892 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write);
893
894 if (mNorFlashInstances[Index]->FvbBuffer != NULL) {
895 EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbBuffer);
896 }
897 }
898
899 return;
900 }
901
902 EFI_STATUS
903 EFIAPI
904 NorFlashInitialise (
905 IN EFI_HANDLE ImageHandle,
906 IN EFI_SYSTEM_TABLE *SystemTable
907 )
908 {
909 EFI_STATUS Status;
910 UINT32 Index;
911 NOR_FLASH_DESCRIPTION* NorFlashDevices;
912 BOOLEAN ContainVariableStorage;
913
914 Status = NorFlashPlatformInitialization ();
915 if (EFI_ERROR(Status)) {
916 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
917 return Status;
918 }
919
920 Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
921 if (EFI_ERROR(Status)) {
922 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
923 return Status;
924 }
925
926 mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);
927
928 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
929 // Check if this NOR Flash device contain the variable storage region
930 ContainVariableStorage =
931 (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&
932 (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
933
934 Status = NorFlashCreateInstance (
935 NorFlashDevices[Index].DeviceBaseAddress,
936 NorFlashDevices[Index].RegionBaseAddress,
937 NorFlashDevices[Index].Size,
938 Index,
939 NorFlashDevices[Index].BlockSize,
940 ContainVariableStorage,
941 &NorFlashDevices[Index].Guid,
942 &mNorFlashInstances[Index]
943 );
944 if (EFI_ERROR(Status)) {
945 DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));
946 }
947 }
948
949 //
950 // Register for the virtual address change event
951 //
952 Status = gBS->CreateEventEx (
953 EVT_NOTIFY_SIGNAL,
954 TPL_NOTIFY,
955 NorFlashVirtualNotifyEvent,
956 NULL,
957 &gEfiEventVirtualAddressChangeGuid,
958 &mNorFlashVirtualAddrChangeEvent
959 );
960 ASSERT_EFI_ERROR (Status);
961
962 return Status;
963 }