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