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