]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlash.c
ArmPlatformPkg: Apply uncrustify changes
[mirror_edk2.git] / ArmPlatformPkg / Drivers / NorFlashDxe / NorFlash.c
1 /** @file NorFlash.c
2
3 Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR>
4 Copyright (c) 2020, Linaro, Ltd. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <Library/BaseMemoryLib.h>
11
12 #include "NorFlash.h"
13
14 //
15 // Global variable declarations
16 //
17 extern NOR_FLASH_INSTANCE **mNorFlashInstances;
18 extern UINT32 mNorFlashDeviceCount;
19
20 UINT32
21 NorFlashReadStatusRegister (
22 IN NOR_FLASH_INSTANCE *Instance,
23 IN UINTN SR_Address
24 )
25 {
26 // Prepare to read the status register
27 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);
28 return MmioRead32 (Instance->DeviceBaseAddress);
29 }
30
31 STATIC
32 BOOLEAN
33 NorFlashBlockIsLocked (
34 IN NOR_FLASH_INSTANCE *Instance,
35 IN UINTN BlockAddress
36 )
37 {
38 UINT32 LockStatus;
39
40 // Send command for reading device id
41 SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
42
43 // Read block lock status
44 LockStatus = MmioRead32 (CREATE_NOR_ADDRESS (BlockAddress, 2));
45
46 // Decode block lock status
47 LockStatus = FOLD_32BIT_INTO_16BIT (LockStatus);
48
49 if ((LockStatus & 0x2) != 0) {
50 DEBUG ((DEBUG_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
51 }
52
53 return ((LockStatus & 0x1) != 0);
54 }
55
56 STATIC
57 EFI_STATUS
58 NorFlashUnlockSingleBlock (
59 IN NOR_FLASH_INSTANCE *Instance,
60 IN UINTN BlockAddress
61 )
62 {
63 UINT32 LockStatus;
64
65 // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
66 // and to protect shared data structures.
67
68 if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) {
69 do {
70 // Request a lock setup
71 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
72
73 // Request an unlock
74 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
75
76 // Send command for reading device id
77 SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
78
79 // Read block lock status
80 LockStatus = MmioRead32 (CREATE_NOR_ADDRESS (BlockAddress, 2));
81
82 // Decode block lock status
83 LockStatus = FOLD_32BIT_INTO_16BIT (LockStatus);
84 } while ((LockStatus & 0x1) == 1);
85 } else {
86 // Request a lock setup
87 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
88
89 // Request an unlock
90 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
91
92 // Wait until the status register gives us the all clear
93 do {
94 LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);
95 } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
96 }
97
98 // Put device back into Read Array mode
99 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY);
100
101 DEBUG ((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress));
102
103 return EFI_SUCCESS;
104 }
105
106 EFI_STATUS
107 NorFlashUnlockSingleBlockIfNecessary (
108 IN NOR_FLASH_INSTANCE *Instance,
109 IN UINTN BlockAddress
110 )
111 {
112 EFI_STATUS Status;
113
114 Status = EFI_SUCCESS;
115
116 if (NorFlashBlockIsLocked (Instance, BlockAddress)) {
117 Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);
118 }
119
120 return Status;
121 }
122
123 /**
124 * The following function presumes that the block has already been unlocked.
125 **/
126 EFI_STATUS
127 NorFlashEraseSingleBlock (
128 IN NOR_FLASH_INSTANCE *Instance,
129 IN UINTN BlockAddress
130 )
131 {
132 EFI_STATUS Status;
133 UINT32 StatusRegister;
134
135 Status = EFI_SUCCESS;
136
137 // Request a block erase and then confirm it
138 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP);
139 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM);
140
141 // Wait until the status register gives us the all clear
142 do {
143 StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);
144 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
145
146 if (StatusRegister & P30_SR_BIT_VPP) {
147 DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress));
148 Status = EFI_DEVICE_ERROR;
149 }
150
151 if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) {
152 DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress));
153 Status = EFI_DEVICE_ERROR;
154 }
155
156 if (StatusRegister & P30_SR_BIT_ERASE) {
157 DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister));
158 Status = EFI_DEVICE_ERROR;
159 }
160
161 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
162 // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
163 DEBUG ((DEBUG_INFO, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress));
164 Status = EFI_WRITE_PROTECTED;
165 }
166
167 if (EFI_ERROR (Status)) {
168 // Clear the Status Register
169 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
170 }
171
172 // Put device back into Read Array mode
173 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
174
175 return Status;
176 }
177
178 EFI_STATUS
179 NorFlashWriteSingleWord (
180 IN NOR_FLASH_INSTANCE *Instance,
181 IN UINTN WordAddress,
182 IN UINT32 WriteData
183 )
184 {
185 EFI_STATUS Status;
186 UINT32 StatusRegister;
187
188 Status = EFI_SUCCESS;
189
190 // Request a write single word command
191 SEND_NOR_COMMAND (WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);
192
193 // Store the word into NOR Flash;
194 MmioWrite32 (WordAddress, WriteData);
195
196 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
197 do {
198 // Prepare to read the status register
199 StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);
200 // The chip is busy while the WRITE bit is not asserted
201 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
202
203 // Perform a full status check:
204 // Mask the relevant bits of Status Register.
205 // Everything should be zero, if not, we have a problem
206
207 if (StatusRegister & P30_SR_BIT_VPP) {
208 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n", WordAddress));
209 Status = EFI_DEVICE_ERROR;
210 }
211
212 if (StatusRegister & P30_SR_BIT_PROGRAM) {
213 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n", WordAddress));
214 Status = EFI_DEVICE_ERROR;
215 }
216
217 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
218 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n", WordAddress));
219 Status = EFI_DEVICE_ERROR;
220 }
221
222 if (!EFI_ERROR (Status)) {
223 // Clear the Status Register
224 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
225 }
226
227 // Put device back into Read Array mode
228 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
229
230 return Status;
231 }
232
233 /*
234 * Writes data to the NOR Flash using the Buffered Programming method.
235 *
236 * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
237 * Therefore this function will only handle buffers up to 32 words or 128 bytes.
238 * To deal with larger buffers, call this function again.
239 *
240 * This function presumes that both the TargetAddress and the TargetAddress+BufferSize
241 * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
242 *
243 * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
244 * then programming time is doubled and power consumption is increased.
245 * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
246 * i.e. the last 4 bits of the target start address must be zero: 0x......00
247 */
248 EFI_STATUS
249 NorFlashWriteBuffer (
250 IN NOR_FLASH_INSTANCE *Instance,
251 IN UINTN TargetAddress,
252 IN UINTN BufferSizeInBytes,
253 IN UINT32 *Buffer
254 )
255 {
256 EFI_STATUS Status;
257 UINTN BufferSizeInWords;
258 UINTN Count;
259 volatile UINT32 *Data;
260 UINTN WaitForBuffer;
261 BOOLEAN BufferAvailable;
262 UINT32 StatusRegister;
263
264 WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS;
265 BufferAvailable = FALSE;
266
267 // Check that the target address does not cross a 32-word boundary.
268 if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) {
269 return EFI_INVALID_PARAMETER;
270 }
271
272 // Check there are some data to program
273 if (BufferSizeInBytes == 0) {
274 return EFI_BUFFER_TOO_SMALL;
275 }
276
277 // Check that the buffer size does not exceed the maximum hardware buffer size on chip.
278 if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) {
279 return EFI_BAD_BUFFER_SIZE;
280 }
281
282 // Check that the buffer size is a multiple of 32-bit words
283 if ((BufferSizeInBytes % 4) != 0) {
284 return EFI_BAD_BUFFER_SIZE;
285 }
286
287 // Pre-programming conditions checked, now start the algorithm.
288
289 // Prepare the data destination address
290 Data = (UINT32 *)TargetAddress;
291
292 // Check the availability of the buffer
293 do {
294 // Issue the Buffered Program Setup command
295 SEND_NOR_COMMAND (TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP);
296
297 // Read back the status register bit#7 from the same address
298 if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) {
299 BufferAvailable = TRUE;
300 }
301
302 // Update the loop counter
303 WaitForBuffer--;
304 } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE));
305
306 // The buffer was not available for writing
307 if (WaitForBuffer == 0) {
308 Status = EFI_DEVICE_ERROR;
309 goto EXIT;
310 }
311
312 // From now on we work in 32-bit words
313 BufferSizeInWords = BufferSizeInBytes / (UINTN)4;
314
315 // Write the word count, which is (buffer_size_in_words - 1),
316 // because word count 0 means one word.
317 SEND_NOR_COMMAND (TargetAddress, 0, (BufferSizeInWords - 1));
318
319 // Write the data to the NOR Flash, advancing each address by 4 bytes
320 for (Count = 0; Count < BufferSizeInWords; Count++, Data++, Buffer++) {
321 MmioWrite32 ((UINTN)Data, *Buffer);
322 }
323
324 // Issue the Buffered Program Confirm command, to start the programming operation
325 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);
326
327 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
328 do {
329 StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress);
330 // The chip is busy while the WRITE bit is not asserted
331 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
332
333 // Perform a full status check:
334 // Mask the relevant bits of Status Register.
335 // Everything should be zero, if not, we have a problem
336
337 Status = EFI_SUCCESS;
338
339 if (StatusRegister & P30_SR_BIT_VPP) {
340 DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress));
341 Status = EFI_DEVICE_ERROR;
342 }
343
344 if (StatusRegister & P30_SR_BIT_PROGRAM) {
345 DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress));
346 Status = EFI_DEVICE_ERROR;
347 }
348
349 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
350 DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n", TargetAddress));
351 Status = EFI_DEVICE_ERROR;
352 }
353
354 if (!EFI_ERROR (Status)) {
355 // Clear the Status Register
356 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
357 }
358
359 EXIT:
360 // Put device back into Read Array mode
361 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
362
363 return Status;
364 }
365
366 EFI_STATUS
367 NorFlashWriteBlocks (
368 IN NOR_FLASH_INSTANCE *Instance,
369 IN EFI_LBA Lba,
370 IN UINTN BufferSizeInBytes,
371 IN VOID *Buffer
372 )
373 {
374 UINT32 *pWriteBuffer;
375 EFI_STATUS Status;
376 EFI_LBA CurrentBlock;
377 UINT32 BlockSizeInWords;
378 UINT32 NumBlocks;
379 UINT32 BlockCount;
380
381 Status = EFI_SUCCESS;
382
383 // The buffer must be valid
384 if (Buffer == NULL) {
385 return EFI_INVALID_PARAMETER;
386 }
387
388 if (Instance->Media.ReadOnly == TRUE) {
389 return EFI_WRITE_PROTECTED;
390 }
391
392 // We must have some bytes to read
393 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));
394 if (BufferSizeInBytes == 0) {
395 return EFI_BAD_BUFFER_SIZE;
396 }
397
398 // The size of the buffer must be a multiple of the block size
399 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize));
400 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
401 return EFI_BAD_BUFFER_SIZE;
402 }
403
404 // All blocks must be within the device
405 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize;
406
407 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba));
408
409 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
410 DEBUG ((DEBUG_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
411 return EFI_INVALID_PARAMETER;
412 }
413
414 BlockSizeInWords = Instance->Media.BlockSize / 4;
415
416 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
417 // to a proper data type, so use *ReadBuffer
418 pWriteBuffer = (UINT32 *)Buffer;
419
420 CurrentBlock = Lba;
421 for (BlockCount = 0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {
422 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));
423
424 Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);
425
426 if (EFI_ERROR (Status)) {
427 break;
428 }
429 }
430
431 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));
432 return Status;
433 }
434
435 #define BOTH_ALIGNED(a, b, align) ((((UINTN)(a) | (UINTN)(b)) & ((align) - 1)) == 0)
436
437 /**
438 Copy Length bytes from Source to Destination, using aligned accesses only.
439 Note that this implementation uses memcpy() semantics rather then memmove()
440 semantics, i.e., SourceBuffer and DestinationBuffer should not overlap.
441
442 @param DestinationBuffer The target of the copy request.
443 @param SourceBuffer The place to copy from.
444 @param Length The number of bytes to copy.
445
446 @return Destination
447
448 **/
449 STATIC
450 VOID *
451 AlignedCopyMem (
452 OUT VOID *DestinationBuffer,
453 IN CONST VOID *SourceBuffer,
454 IN UINTN Length
455 )
456 {
457 UINT8 *Destination8;
458 CONST UINT8 *Source8;
459 UINT32 *Destination32;
460 CONST UINT32 *Source32;
461 UINT64 *Destination64;
462 CONST UINT64 *Source64;
463
464 if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 8) && (Length >= 8)) {
465 Destination64 = DestinationBuffer;
466 Source64 = SourceBuffer;
467 while (Length >= 8) {
468 *Destination64++ = *Source64++;
469 Length -= 8;
470 }
471
472 Destination8 = (UINT8 *)Destination64;
473 Source8 = (CONST UINT8 *)Source64;
474 } else if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 4) && (Length >= 4)) {
475 Destination32 = DestinationBuffer;
476 Source32 = SourceBuffer;
477 while (Length >= 4) {
478 *Destination32++ = *Source32++;
479 Length -= 4;
480 }
481
482 Destination8 = (UINT8 *)Destination32;
483 Source8 = (CONST UINT8 *)Source32;
484 } else {
485 Destination8 = DestinationBuffer;
486 Source8 = SourceBuffer;
487 }
488
489 while (Length-- != 0) {
490 *Destination8++ = *Source8++;
491 }
492
493 return DestinationBuffer;
494 }
495
496 EFI_STATUS
497 NorFlashReadBlocks (
498 IN NOR_FLASH_INSTANCE *Instance,
499 IN EFI_LBA Lba,
500 IN UINTN BufferSizeInBytes,
501 OUT VOID *Buffer
502 )
503 {
504 UINT32 NumBlocks;
505 UINTN StartAddress;
506
507 DEBUG ((
508 DEBUG_BLKIO,
509 "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
510 BufferSizeInBytes,
511 Instance->Media.BlockSize,
512 Instance->Media.LastBlock,
513 Lba
514 ));
515
516 // The buffer must be valid
517 if (Buffer == NULL) {
518 return EFI_INVALID_PARAMETER;
519 }
520
521 // Return if we have not any byte to read
522 if (BufferSizeInBytes == 0) {
523 return EFI_SUCCESS;
524 }
525
526 // The size of the buffer must be a multiple of the block size
527 if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
528 return EFI_BAD_BUFFER_SIZE;
529 }
530
531 // All blocks must be within the device
532 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize;
533
534 if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
535 DEBUG ((DEBUG_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
536 return EFI_INVALID_PARAMETER;
537 }
538
539 // Get the address to start reading from
540 StartAddress = GET_NOR_BLOCK_ADDRESS (
541 Instance->RegionBaseAddress,
542 Lba,
543 Instance->Media.BlockSize
544 );
545
546 // Put the device into Read Array mode
547 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
548
549 // Readout the data
550 AlignedCopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes);
551
552 return EFI_SUCCESS;
553 }
554
555 EFI_STATUS
556 NorFlashRead (
557 IN NOR_FLASH_INSTANCE *Instance,
558 IN EFI_LBA Lba,
559 IN UINTN Offset,
560 IN UINTN BufferSizeInBytes,
561 OUT VOID *Buffer
562 )
563 {
564 UINTN StartAddress;
565
566 // The buffer must be valid
567 if (Buffer == NULL) {
568 return EFI_INVALID_PARAMETER;
569 }
570
571 // Return if we have not any byte to read
572 if (BufferSizeInBytes == 0) {
573 return EFI_SUCCESS;
574 }
575
576 if (((Lba * Instance->Media.BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) {
577 DEBUG ((DEBUG_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));
578 return EFI_INVALID_PARAMETER;
579 }
580
581 // Get the address to start reading from
582 StartAddress = GET_NOR_BLOCK_ADDRESS (
583 Instance->RegionBaseAddress,
584 Lba,
585 Instance->Media.BlockSize
586 );
587
588 // Put the device into Read Array mode
589 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
590
591 // Readout the data
592 AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);
593
594 return EFI_SUCCESS;
595 }
596
597 /*
598 Write a full or portion of a block. It must not span block boundaries; that is,
599 Offset + *NumBytes <= Instance->Media.BlockSize.
600 */
601 EFI_STATUS
602 NorFlashWriteSingleBlock (
603 IN NOR_FLASH_INSTANCE *Instance,
604 IN EFI_LBA Lba,
605 IN UINTN Offset,
606 IN OUT UINTN *NumBytes,
607 IN UINT8 *Buffer
608 )
609 {
610 EFI_STATUS TempStatus;
611 UINT32 Tmp;
612 UINT32 TmpBuf;
613 UINT32 WordToWrite;
614 UINT32 Mask;
615 BOOLEAN DoErase;
616 UINTN BytesToWrite;
617 UINTN CurOffset;
618 UINTN WordAddr;
619 UINTN BlockSize;
620 UINTN BlockAddress;
621 UINTN PrevBlockAddress;
622
623 PrevBlockAddress = 0;
624
625 DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));
626
627 // Detect WriteDisabled state
628 if (Instance->Media.ReadOnly == TRUE) {
629 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));
630 // It is in WriteDisabled state, return an error right away
631 return EFI_ACCESS_DENIED;
632 }
633
634 // Cache the block size to avoid de-referencing pointers all the time
635 BlockSize = Instance->Media.BlockSize;
636
637 // The write must not span block boundaries.
638 // We need to check each variable individually because adding two large values together overflows.
639 if ((Offset >= BlockSize) ||
640 (*NumBytes > BlockSize) ||
641 ((Offset + *NumBytes) > BlockSize))
642 {
643 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize));
644 return EFI_BAD_BUFFER_SIZE;
645 }
646
647 // We must have some bytes to write
648 if (*NumBytes == 0) {
649 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize));
650 return EFI_BAD_BUFFER_SIZE;
651 }
652
653 // Pick 128bytes as a good start for word operations as opposed to erasing the
654 // block and writing the data regardless if an erase is really needed.
655 // It looks like most individual NV variable writes are smaller than 128bytes.
656 if (*NumBytes <= 128) {
657 // Check to see if we need to erase before programming the data into NOR.
658 // If the destination bits are only changing from 1s to 0s we can just write.
659 // After a block is erased all bits in the block is set to 1.
660 // If any byte requires us to erase we just give up and rewrite all of it.
661 DoErase = FALSE;
662 BytesToWrite = *NumBytes;
663 CurOffset = Offset;
664
665 while (BytesToWrite > 0) {
666 // Read full word from NOR, splice as required. A word is the smallest
667 // unit we can write.
668 TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof (Tmp), &Tmp);
669 if (EFI_ERROR (TempStatus)) {
670 return EFI_DEVICE_ERROR;
671 }
672
673 // Physical address of word in NOR to write.
674 WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (
675 Instance->RegionBaseAddress,
676 Lba,
677 BlockSize
678 );
679 // The word of data that is to be written.
680 TmpBuf = *((UINT32 *)(Buffer + (*NumBytes - BytesToWrite)));
681
682 // First do word aligned chunks.
683 if ((CurOffset & 0x3) == 0) {
684 if (BytesToWrite >= 4) {
685 // Is the destination still in 'erased' state?
686 if (~Tmp != 0) {
687 // Check to see if we are only changing bits to zero.
688 if ((Tmp ^ TmpBuf) & TmpBuf) {
689 DoErase = TRUE;
690 break;
691 }
692 }
693
694 // Write this word to NOR
695 WordToWrite = TmpBuf;
696 CurOffset += sizeof (TmpBuf);
697 BytesToWrite -= sizeof (TmpBuf);
698 } else {
699 // BytesToWrite < 4. Do small writes and left-overs
700 Mask = ~((~0) << (BytesToWrite * 8));
701 // Mask out the bytes we want.
702 TmpBuf &= Mask;
703 // Is the destination still in 'erased' state?
704 if ((Tmp & Mask) != Mask) {
705 // Check to see if we are only changing bits to zero.
706 if ((Tmp ^ TmpBuf) & TmpBuf) {
707 DoErase = TRUE;
708 break;
709 }
710 }
711
712 // Merge old and new data. Write merged word to NOR
713 WordToWrite = (Tmp & ~Mask) | TmpBuf;
714 CurOffset += BytesToWrite;
715 BytesToWrite = 0;
716 }
717 } else {
718 // Do multiple words, but starting unaligned.
719 if (BytesToWrite > (4 - (CurOffset & 0x3))) {
720 Mask = ((~0) << ((CurOffset & 0x3) * 8));
721 // Mask out the bytes we want.
722 TmpBuf &= Mask;
723 // Is the destination still in 'erased' state?
724 if ((Tmp & Mask) != Mask) {
725 // Check to see if we are only changing bits to zero.
726 if ((Tmp ^ TmpBuf) & TmpBuf) {
727 DoErase = TRUE;
728 break;
729 }
730 }
731
732 // Merge old and new data. Write merged word to NOR
733 WordToWrite = (Tmp & ~Mask) | TmpBuf;
734 BytesToWrite -= (4 - (CurOffset & 0x3));
735 CurOffset += (4 - (CurOffset & 0x3));
736 } else {
737 // Unaligned and fits in one word.
738 Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8);
739 // Mask out the bytes we want.
740 TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask;
741 // Is the destination still in 'erased' state?
742 if ((Tmp & Mask) != Mask) {
743 // Check to see if we are only changing bits to zero.
744 if ((Tmp ^ TmpBuf) & TmpBuf) {
745 DoErase = TRUE;
746 break;
747 }
748 }
749
750 // Merge old and new data. Write merged word to NOR
751 WordToWrite = (Tmp & ~Mask) | TmpBuf;
752 CurOffset += BytesToWrite;
753 BytesToWrite = 0;
754 }
755 }
756
757 //
758 // Write the word to NOR.
759 //
760
761 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
762 if (BlockAddress != PrevBlockAddress) {
763 TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
764 if (EFI_ERROR (TempStatus)) {
765 return EFI_DEVICE_ERROR;
766 }
767
768 PrevBlockAddress = BlockAddress;
769 }
770
771 TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite);
772 if (EFI_ERROR (TempStatus)) {
773 return EFI_DEVICE_ERROR;
774 }
775 }
776
777 // Exit if we got here and could write all the data. Otherwise do the
778 // Erase-Write cycle.
779 if (!DoErase) {
780 return EFI_SUCCESS;
781 }
782 }
783
784 // Check we did get some memory. Buffer is BlockSize.
785 if (Instance->ShadowBuffer == NULL) {
786 DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
787 return EFI_DEVICE_ERROR;
788 }
789
790 // Read NOR Flash data into shadow buffer
791 TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
792 if (EFI_ERROR (TempStatus)) {
793 // Return one of the pre-approved error statuses
794 return EFI_DEVICE_ERROR;
795 }
796
797 // Put the data at the appropriate location inside the buffer area
798 CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
799
800 // Write the modified buffer back to the NorFlash
801 TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
802 if (EFI_ERROR (TempStatus)) {
803 // Return one of the pre-approved error statuses
804 return EFI_DEVICE_ERROR;
805 }
806
807 return EFI_SUCCESS;
808 }
809
810 /*
811 Although DiskIoDxe will automatically install the DiskIO protocol whenever
812 we install the BlockIO protocol, its implementation is sub-optimal as it reads
813 and writes entire blocks using the BlockIO protocol. In fact we can access
814 NOR flash with a finer granularity than that, so we can improve performance
815 by directly producing the DiskIO protocol.
816 */
817
818 /**
819 Read BufferSize bytes from Offset into Buffer.
820
821 @param This Protocol instance pointer.
822 @param MediaId Id of the media, changes every time the media is replaced.
823 @param Offset The starting byte offset to read from
824 @param BufferSize Size of Buffer
825 @param Buffer Buffer containing read data
826
827 @retval EFI_SUCCESS The data was read correctly from the device.
828 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
829 @retval EFI_NO_MEDIA There is no media in the device.
830 @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
831 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
832 valid for the device.
833
834 **/
835 EFI_STATUS
836 EFIAPI
837 NorFlashDiskIoReadDisk (
838 IN EFI_DISK_IO_PROTOCOL *This,
839 IN UINT32 MediaId,
840 IN UINT64 DiskOffset,
841 IN UINTN BufferSize,
842 OUT VOID *Buffer
843 )
844 {
845 NOR_FLASH_INSTANCE *Instance;
846 UINT32 BlockSize;
847 UINT32 BlockOffset;
848 EFI_LBA Lba;
849
850 Instance = INSTANCE_FROM_DISKIO_THIS (This);
851
852 if (MediaId != Instance->Media.MediaId) {
853 return EFI_MEDIA_CHANGED;
854 }
855
856 BlockSize = Instance->Media.BlockSize;
857 Lba = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
858
859 return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer);
860 }
861
862 /**
863 Writes a specified number of bytes to a device.
864
865 @param This Indicates a pointer to the calling context.
866 @param MediaId ID of the medium to be written.
867 @param Offset The starting byte offset on the logical block I/O device to write.
868 @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
869 @param Buffer A pointer to the buffer containing the data to be written.
870
871 @retval EFI_SUCCESS The data was written correctly to the device.
872 @retval EFI_WRITE_PROTECTED The device can not be written to.
873 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
874 @retval EFI_NO_MEDIA There is no media in the device.
875 @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
876 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
877 valid for the device.
878
879 **/
880 EFI_STATUS
881 EFIAPI
882 NorFlashDiskIoWriteDisk (
883 IN EFI_DISK_IO_PROTOCOL *This,
884 IN UINT32 MediaId,
885 IN UINT64 DiskOffset,
886 IN UINTN BufferSize,
887 IN VOID *Buffer
888 )
889 {
890 NOR_FLASH_INSTANCE *Instance;
891 UINT32 BlockSize;
892 UINT32 BlockOffset;
893 EFI_LBA Lba;
894 UINTN RemainingBytes;
895 UINTN WriteSize;
896 EFI_STATUS Status;
897
898 Instance = INSTANCE_FROM_DISKIO_THIS (This);
899
900 if (MediaId != Instance->Media.MediaId) {
901 return EFI_MEDIA_CHANGED;
902 }
903
904 BlockSize = Instance->Media.BlockSize;
905 Lba = (EFI_LBA)DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
906
907 RemainingBytes = BufferSize;
908
909 // Write either all the remaining bytes, or the number of bytes that bring
910 // us up to a block boundary, whichever is less.
911 // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
912 // block boundary (even if it is already on one).
913 WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset);
914
915 do {
916 if (WriteSize == BlockSize) {
917 // Write a full block
918 Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32));
919 } else {
920 // Write a partial block
921 Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer);
922 }
923
924 if (EFI_ERROR (Status)) {
925 return Status;
926 }
927
928 // Now continue writing either all the remaining bytes or single blocks.
929 RemainingBytes -= WriteSize;
930 Buffer = (UINT8 *)Buffer + WriteSize;
931 Lba++;
932 BlockOffset = 0;
933 WriteSize = MIN (RemainingBytes, BlockSize);
934 } while (RemainingBytes);
935
936 return Status;
937 }
938
939 EFI_STATUS
940 NorFlashReset (
941 IN NOR_FLASH_INSTANCE *Instance
942 )
943 {
944 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
945 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
946 return EFI_SUCCESS;
947 }
948
949 /**
950 Fixup internal data so that EFI can be call in virtual mode.
951 Call the passed in Child Notify event and convert any pointers in
952 lib to virtual mode.
953
954 @param[in] Event The Event that is being processed
955 @param[in] Context Event Context
956 **/
957 VOID
958 EFIAPI
959 NorFlashVirtualNotifyEvent (
960 IN EFI_EVENT Event,
961 IN VOID *Context
962 )
963 {
964 UINTN Index;
965
966 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
967 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress);
968 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress);
969
970 // Convert BlockIo protocol
971 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);
972 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);
973 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);
974 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);
975
976 // Convert Fvb
977 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
978 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
979 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
980 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
981 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Read);
982 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
983 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Write);
984
985 if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
986 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->ShadowBuffer);
987 }
988 }
989
990 return;
991 }