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