a3f98646 |
1 | /** @file |
2 | |
3 | Copyright (c) 2008-2009, Apple Inc. All rights reserved. |
4 | |
5 | All rights reserved. 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 "Flash.h" |
16 | |
17 | NAND_PART_INFO_TABLE gNandPartInfoTable[1] = { |
18 | { 0x2C, 0xBA, 17, 11 } |
19 | }; |
20 | |
21 | NAND_FLASH_INFO *gNandFlashInfo = NULL; |
22 | UINT8 *gEccCode; |
23 | UINTN gNum512BytesChunks = 0; |
24 | |
026e30c4 |
25 | // |
26 | |
27 | // Device path for SemiHosting. It contains our autogened Caller ID GUID. |
28 | |
29 | // |
30 | |
31 | typedef struct { |
32 | |
33 | VENDOR_DEVICE_PATH Guid; |
34 | |
35 | EFI_DEVICE_PATH_PROTOCOL End; |
36 | |
37 | } FLASH_DEVICE_PATH; |
38 | |
39 | |
40 | |
41 | FLASH_DEVICE_PATH gDevicePath = { |
42 | |
43 | { |
44 | |
45 | { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH), 0 }, |
46 | |
47 | EFI_CALLER_ID_GUID |
48 | |
49 | }, |
50 | |
51 | { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, sizeof (EFI_DEVICE_PATH_PROTOCOL), 0} |
52 | |
53 | }; |
54 | |
a3f98646 |
55 | |
56 | |
57 | //Actual page address = Column address + Page address + Block address. |
58 | UINTN |
59 | GetActualPageAddressInBytes ( |
60 | UINTN BlockIndex, |
61 | UINTN PageIndex |
62 | ) |
63 | { |
64 | //BlockAddressStart = Start of the Block address in actual NAND |
65 | //PageAddressStart = Start of the Page address in actual NAND |
66 | return ((BlockIndex << gNandFlashInfo->BlockAddressStart) + (PageIndex << gNandFlashInfo->PageAddressStart)); |
67 | } |
68 | |
69 | VOID |
70 | NandSendCommand ( |
71 | UINT8 Command |
72 | ) |
73 | { |
74 | MmioWrite16(GPMC_NAND_COMMAND_0, Command); |
75 | } |
76 | |
77 | VOID |
78 | NandSendAddress ( |
79 | UINT8 Address |
80 | ) |
81 | { |
82 | MmioWrite16(GPMC_NAND_ADDRESS_0, Address); |
83 | } |
84 | |
85 | UINT16 |
86 | NandReadStatus ( |
87 | VOID |
88 | ) |
89 | { |
90 | //Send READ STATUS command |
91 | NandSendCommand(READ_STATUS_CMD); |
92 | |
93 | //Read status. |
94 | return MmioRead16(GPMC_NAND_DATA_0); |
95 | } |
96 | |
97 | VOID |
98 | NandSendAddressCycles ( |
99 | UINTN Address |
100 | ) |
101 | { |
102 | //Column address |
103 | NandSendAddress(Address & 0xff); |
104 | Address >>= 8; |
105 | |
106 | //Column address |
107 | NandSendAddress(Address & 0x07); |
108 | Address >>= 3; |
109 | |
110 | //Page and Block address |
111 | NandSendAddress(Address & 0xff); |
112 | Address >>= 8; |
113 | |
114 | //Block address |
115 | NandSendAddress(Address & 0xff); |
116 | Address >>= 8; |
117 | |
118 | //Block address |
119 | NandSendAddress(Address & 0x01); |
120 | } |
121 | |
122 | VOID |
123 | GpmcInit ( |
124 | VOID |
125 | ) |
126 | { |
127 | //Enable Smart-idle mode. |
026e30c4 |
128 | MmioWrite32 (GPMC_SYSCONFIG, SMARTIDLEMODE); |
a3f98646 |
129 | |
130 | //Set IRQSTATUS and IRQENABLE to the reset value |
026e30c4 |
131 | MmioWrite32 (GPMC_IRQSTATUS, 0x0); |
132 | MmioWrite32 (GPMC_IRQENABLE, 0x0); |
a3f98646 |
133 | |
134 | //Disable GPMC timeout control. |
026e30c4 |
135 | MmioWrite32 (GPMC_TIMEOUT_CONTROL, TIMEOUTDISABLE); |
a3f98646 |
136 | |
137 | //Set WRITEPROTECT bit to enable write access. |
026e30c4 |
138 | MmioWrite32 (GPMC_CONFIG, WRITEPROTECT_HIGH); |
a3f98646 |
139 | |
140 | //NOTE: Following GPMC_CONFIGi_0 register settings are taken from u-boot memory dump. |
026e30c4 |
141 | MmioWrite32 (GPMC_CONFIG1_0, DEVICETYPE_NAND | DEVICESIZE_X16); |
142 | MmioWrite32 (GPMC_CONFIG2_0, CSRDOFFTIME | CSWROFFTIME); |
143 | MmioWrite32 (GPMC_CONFIG3_0, ADVRDOFFTIME | ADVWROFFTIME); |
144 | MmioWrite32 (GPMC_CONFIG4_0, OEONTIME | OEOFFTIME | WEONTIME | WEOFFTIME); |
145 | MmioWrite32 (GPMC_CONFIG5_0, RDCYCLETIME | WRCYCLETIME | RDACCESSTIME | PAGEBURSTACCESSTIME); |
146 | MmioWrite32 (GPMC_CONFIG6_0, WRACCESSTIME | WRDATAONADMUXBUS | CYCLE2CYCLEDELAY | CYCLE2CYCLESAMECSEN); |
147 | MmioWrite32 (GPMC_CONFIG7_0, MASKADDRESS_128MB | CSVALID | BASEADDRESS); |
a3f98646 |
148 | } |
149 | |
150 | EFI_STATUS |
151 | NandDetectPart ( |
152 | VOID |
153 | ) |
154 | { |
155 | UINT8 NandInfo = 0; |
156 | UINT8 PartInfo[5]; |
157 | UINTN Index; |
158 | BOOLEAN Found = FALSE; |
159 | |
160 | //Send READ ID command |
161 | NandSendCommand(READ_ID_CMD); |
162 | |
163 | //Send one address cycle. |
164 | NandSendAddress(0); |
165 | |
166 | //Read 5-bytes to idenfity code programmed into the NAND flash devices. |
167 | //BYTE 0 = Manufacture ID |
168 | //Byte 1 = Device ID |
169 | //Byte 2, 3, 4 = Nand part specific information (Page size, Block size etc) |
170 | for (Index = 0; Index < sizeof(PartInfo); Index++) { |
171 | PartInfo[Index] = MmioRead16(GPMC_NAND_DATA_0); |
172 | } |
173 | |
174 | //Check if the ManufactureId and DeviceId are part of the currently supported nand parts. |
175 | for (Index = 0; Index < sizeof(gNandPartInfoTable)/sizeof(NAND_PART_INFO_TABLE); Index++) { |
176 | if (gNandPartInfoTable[Index].ManufactureId == PartInfo[0] && gNandPartInfoTable[Index].DeviceId == PartInfo[1]) { |
177 | gNandFlashInfo->BlockAddressStart = gNandPartInfoTable[Index].BlockAddressStart; |
178 | gNandFlashInfo->PageAddressStart = gNandPartInfoTable[Index].PageAddressStart; |
179 | Found = TRUE; |
180 | break; |
181 | } |
182 | } |
183 | |
184 | if (Found == FALSE) { |
185 | DEBUG ((EFI_D_ERROR, "Nand part is not currently supported. Manufacture id: %x, Device id: %x\n", PartInfo[0], PartInfo[1])); |
186 | return EFI_NOT_FOUND; |
187 | } |
188 | |
189 | //Populate NAND_FLASH_INFO based on the result of READ ID command. |
190 | gNandFlashInfo->ManufactureId = PartInfo[0]; |
191 | gNandFlashInfo->DeviceId = PartInfo[1]; |
192 | NandInfo = PartInfo[3]; |
193 | |
194 | if (PAGE_SIZE(NandInfo) == PAGE_SIZE_2K_VAL) { |
195 | gNandFlashInfo->PageSize = PAGE_SIZE_2K; |
196 | } else { |
197 | DEBUG ((EFI_D_ERROR, "Unknown Page size.\n")); |
198 | return EFI_DEVICE_ERROR; |
199 | } |
200 | |
201 | if (SPARE_AREA_SIZE(NandInfo) == SPARE_AREA_SIZE_64B_VAL) { |
202 | gNandFlashInfo->SparePageSize = SPARE_AREA_SIZE_64B; |
203 | } else { |
204 | DEBUG ((EFI_D_ERROR, "Unknown Spare area size.\n")); |
205 | return EFI_DEVICE_ERROR; |
206 | } |
207 | |
208 | if (BLOCK_SIZE(NandInfo) == BLOCK_SIZE_128K_VAL) { |
209 | gNandFlashInfo->BlockSize = BLOCK_SIZE_128K; |
210 | } else { |
211 | DEBUG ((EFI_D_ERROR, "Unknown Block size.\n")); |
212 | return EFI_DEVICE_ERROR; |
213 | } |
214 | |
215 | if (ORGANIZATION(NandInfo) == ORGANIZATION_X8) { |
216 | gNandFlashInfo->Organization = 0; |
217 | } else if (ORGANIZATION(NandInfo) == ORGANIZATION_X16) { |
218 | gNandFlashInfo->Organization = 1; |
219 | } |
220 | |
221 | //Calculate total number of blocks. |
222 | gNandFlashInfo->NumPagesPerBlock = DivU64x32(gNandFlashInfo->BlockSize, gNandFlashInfo->PageSize); |
223 | |
224 | return EFI_SUCCESS; |
225 | } |
226 | |
227 | VOID |
228 | NandConfigureEcc ( |
229 | VOID |
230 | ) |
231 | { |
232 | //Define ECC size 0 and size 1 to 512 bytes |
026e30c4 |
233 | MmioWrite32 (GPMC_ECC_SIZE_CONFIG, (ECCSIZE0_512BYTES | ECCSIZE1_512BYTES)); |
a3f98646 |
234 | } |
235 | |
236 | VOID |
237 | NandEnableEcc ( |
238 | VOID |
239 | ) |
240 | { |
241 | //Clear all the ECC result registers and select ECC result register 1 |
026e30c4 |
242 | MmioWrite32 (GPMC_ECC_CONTROL, (ECCCLEAR | ECCPOINTER_REG1)); |
a3f98646 |
243 | |
244 | //Enable ECC engine on CS0 |
026e30c4 |
245 | MmioWrite32 (GPMC_ECC_CONFIG, (ECCENABLE | ECCCS_0 | ECC16B)); |
a3f98646 |
246 | } |
247 | |
248 | VOID |
249 | NandDisableEcc ( |
250 | VOID |
251 | ) |
252 | { |
253 | //Turn off ECC engine. |
026e30c4 |
254 | MmioWrite32 (GPMC_ECC_CONFIG, ECCDISABLE); |
a3f98646 |
255 | } |
256 | |
257 | VOID |
258 | NandCalculateEcc ( |
259 | VOID |
260 | ) |
261 | { |
262 | UINTN Index; |
263 | UINTN EccResultRegister; |
264 | UINTN EccResult; |
265 | |
266 | //Capture 32-bit ECC result for each 512-bytes chunk. |
267 | //In our case PageSize is 2K so read ECC1-ECC4 result registers and |
268 | //generate total of 12-bytes of ECC code for the particular page. |
269 | |
270 | EccResultRegister = GPMC_ECC1_RESULT; |
271 | |
272 | for (Index = 0; Index < gNum512BytesChunks; Index++) { |
273 | |
43263288 |
274 | EccResult = MmioRead32 (EccResultRegister); |
a3f98646 |
275 | |
276 | //Calculate ECC code from 32-bit ECC result value. |
277 | //NOTE: Following calculation is not part of TRM. We got this information |
278 | //from Beagleboard mailing list. |
279 | gEccCode[Index * 3] = EccResult & 0xFF; |
280 | gEccCode[(Index * 3) + 1] = (EccResult >> 16) & 0xFF; |
281 | gEccCode[(Index * 3) + 2] = (((EccResult >> 20) & 0xF0) | ((EccResult >> 8) & 0x0F)); |
282 | |
283 | //Point to next ECC result register. |
284 | EccResultRegister += 4; |
285 | } |
286 | } |
287 | |
288 | EFI_STATUS |
289 | NandReadPage ( |
290 | IN UINTN BlockIndex, |
291 | IN UINTN PageIndex, |
292 | OUT VOID *Buffer, |
293 | OUT UINT8 *SpareBuffer |
294 | ) |
295 | { |
296 | UINTN Address; |
297 | UINTN Index; |
298 | UINTN NumMainAreaWords = (gNandFlashInfo->PageSize/2); |
299 | UINTN NumSpareAreaWords = (gNandFlashInfo->SparePageSize/2); |
300 | UINT16 *MainAreaWordBuffer = Buffer; |
301 | UINT16 *SpareAreaWordBuffer = (UINT16 *)SpareBuffer; |
302 | UINTN Timeout = MAX_RETRY_COUNT; |
303 | |
304 | //Generate device address in bytes to access specific block and page index |
305 | Address = GetActualPageAddressInBytes(BlockIndex, PageIndex); |
306 | |
307 | //Send READ command |
308 | NandSendCommand(PAGE_READ_CMD); |
309 | |
310 | //Send 5 Address cycles to access specific device address |
311 | NandSendAddressCycles(Address); |
312 | |
313 | //Send READ CONFIRM command |
314 | NandSendCommand(PAGE_READ_CONFIRM_CMD); |
315 | |
316 | //Poll till device is busy. |
317 | while (Timeout) { |
318 | if ((NandReadStatus() & NAND_READY) == NAND_READY) { |
319 | break; |
320 | } |
321 | Timeout--; |
322 | } |
323 | |
324 | if (Timeout == 0) { |
325 | DEBUG ((EFI_D_ERROR, "Read page timed out.\n")); |
326 | return EFI_TIMEOUT; |
327 | } |
328 | |
329 | //Reissue READ command |
330 | NandSendCommand(PAGE_READ_CMD); |
331 | |
332 | //Enable ECC engine. |
333 | NandEnableEcc(); |
334 | |
335 | //Read data into the buffer. |
336 | for (Index = 0; Index < NumMainAreaWords; Index++) { |
337 | *MainAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0); |
338 | } |
339 | |
340 | //Read spare area into the buffer. |
341 | for (Index = 0; Index < NumSpareAreaWords; Index++) { |
342 | *SpareAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0); |
343 | } |
344 | |
345 | //Calculate ECC. |
346 | NandCalculateEcc(); |
347 | |
348 | //Turn off ECC engine. |
349 | NandDisableEcc(); |
350 | |
351 | //Perform ECC correction. |
352 | //Need to implement.. |
353 | |
354 | return EFI_SUCCESS; |
355 | } |
356 | |
357 | EFI_STATUS |
358 | NandWritePage ( |
359 | IN UINTN BlockIndex, |
360 | IN UINTN PageIndex, |
361 | OUT VOID *Buffer, |
362 | IN UINT8 *SpareBuffer |
363 | ) |
364 | { |
365 | UINTN Address; |
366 | UINT16 *MainAreaWordBuffer = Buffer; |
367 | UINT16 *SpareAreaWordBuffer = (UINT16 *)SpareBuffer; |
368 | UINTN Index; |
369 | UINTN NandStatus; |
370 | UINTN Timeout = MAX_RETRY_COUNT; |
371 | |
372 | //Generate device address in bytes to access specific block and page index |
373 | Address = GetActualPageAddressInBytes(BlockIndex, PageIndex); |
374 | |
375 | //Send SERIAL DATA INPUT command |
376 | NandSendCommand(PROGRAM_PAGE_CMD); |
377 | |
378 | //Send 5 Address cycles to access specific device address |
379 | NandSendAddressCycles(Address); |
380 | |
381 | //Enable ECC engine. |
382 | NandEnableEcc(); |
383 | |
384 | //Data input from Buffer |
385 | for (Index = 0; Index < (gNandFlashInfo->PageSize/2); Index++) { |
386 | MmioWrite16(GPMC_NAND_DATA_0, *MainAreaWordBuffer++); |
387 | |
388 | //After each write access, device has to wait to accept data. |
389 | //Currently we may not be programming proper timing parameters to |
390 | //the GPMC_CONFIGi_0 registers and we would need to figure that out. |
391 | //Without following delay, page programming fails. |
392 | gBS->Stall(1); |
393 | } |
394 | |
395 | //Calculate ECC. |
396 | NandCalculateEcc(); |
397 | |
398 | //Turn off ECC engine. |
399 | NandDisableEcc(); |
400 | |
401 | //Prepare Spare area buffer with ECC codes. |
402 | SetMem(SpareBuffer, gNandFlashInfo->SparePageSize, 0xFF); |
403 | CopyMem(&SpareBuffer[ECC_POSITION], gEccCode, gNum512BytesChunks * 3); |
404 | |
405 | //Program spare area with calculated ECC. |
406 | for (Index = 0; Index < (gNandFlashInfo->SparePageSize/2); Index++) { |
407 | MmioWrite16(GPMC_NAND_DATA_0, *SpareAreaWordBuffer++); |
408 | } |
409 | |
410 | //Send PROGRAM command |
411 | NandSendCommand(PROGRAM_PAGE_CONFIRM_CMD); |
412 | |
413 | //Poll till device is busy. |
414 | while (Timeout) { |
415 | NandStatus = NandReadStatus(); |
416 | if ((NandStatus & NAND_READY) == NAND_READY) { |
417 | break; |
418 | } |
419 | Timeout--; |
420 | } |
421 | |
422 | if (Timeout == 0) { |
423 | DEBUG ((EFI_D_ERROR, "Program page timed out.\n")); |
424 | return EFI_TIMEOUT; |
425 | } |
426 | |
427 | //Bit0 indicates Pass/Fail status |
428 | if (NandStatus & NAND_FAILURE) { |
429 | return EFI_DEVICE_ERROR; |
430 | } |
431 | |
432 | return EFI_SUCCESS; |
433 | } |
434 | |
435 | EFI_STATUS |
436 | NandEraseBlock ( |
437 | IN UINTN BlockIndex |
438 | ) |
439 | { |
440 | UINTN Address; |
441 | UINTN NandStatus; |
442 | UINTN Timeout = MAX_RETRY_COUNT; |
443 | |
444 | //Generate device address in bytes to access specific block and page index |
445 | Address = GetActualPageAddressInBytes(BlockIndex, 0); |
446 | |
447 | //Send ERASE SETUP command |
448 | NandSendCommand(BLOCK_ERASE_CMD); |
449 | |
450 | //Send 3 address cycles to device to access Page address and Block address |
451 | Address >>= 11; //Ignore column addresses |
452 | |
453 | NandSendAddress(Address & 0xff); |
454 | Address >>= 8; |
455 | |
456 | NandSendAddress(Address & 0xff); |
457 | Address >>= 8; |
458 | |
459 | NandSendAddress(Address & 0xff); |
460 | |
461 | //Send ERASE CONFIRM command |
462 | NandSendCommand(BLOCK_ERASE_CONFIRM_CMD); |
463 | |
464 | //Poll till device is busy. |
465 | while (Timeout) { |
466 | NandStatus = NandReadStatus(); |
467 | if ((NandStatus & NAND_READY) == NAND_READY) { |
468 | break; |
469 | } |
470 | Timeout--; |
471 | gBS->Stall(1); |
472 | } |
473 | |
474 | if (Timeout == 0) { |
475 | DEBUG ((EFI_D_ERROR, "Erase block timed out for Block: %d.\n", BlockIndex)); |
476 | return EFI_TIMEOUT; |
477 | } |
478 | |
479 | //Bit0 indicates Pass/Fail status |
480 | if (NandStatus & NAND_FAILURE) { |
481 | return EFI_DEVICE_ERROR; |
482 | } |
483 | |
484 | return EFI_SUCCESS; |
485 | } |
486 | |
487 | EFI_STATUS |
488 | NandReadBlock ( |
489 | IN UINTN StartBlockIndex, |
490 | IN UINTN EndBlockIndex, |
491 | OUT VOID *Buffer, |
492 | OUT VOID *SpareBuffer |
493 | ) |
494 | { |
495 | UINTN BlockIndex; |
496 | UINTN PageIndex; |
497 | EFI_STATUS Status = EFI_SUCCESS; |
498 | |
499 | for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) { |
500 | //For each block read number of pages |
501 | for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) { |
502 | Status = NandReadPage(BlockIndex, PageIndex, Buffer, SpareBuffer); |
503 | if (EFI_ERROR(Status)) { |
504 | return Status; |
505 | } |
506 | Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize); |
507 | } |
508 | } |
509 | |
510 | return Status; |
511 | } |
512 | |
513 | EFI_STATUS |
514 | NandWriteBlock ( |
515 | IN UINTN StartBlockIndex, |
516 | IN UINTN EndBlockIndex, |
517 | OUT VOID *Buffer, |
518 | OUT VOID *SpareBuffer |
519 | ) |
520 | { |
521 | UINTN BlockIndex; |
522 | UINTN PageIndex; |
523 | EFI_STATUS Status = EFI_SUCCESS; |
524 | |
525 | for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) { |
526 | //Page programming. |
527 | for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) { |
528 | Status = NandWritePage(BlockIndex, PageIndex, Buffer, SpareBuffer); |
529 | if (EFI_ERROR(Status)) { |
530 | return Status; |
531 | } |
532 | Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize); |
533 | } |
534 | } |
535 | |
536 | return Status; |
537 | } |
538 | |
539 | EFI_STATUS |
540 | EFIAPI |
541 | NandFlashReset ( |
542 | IN EFI_BLOCK_IO_PROTOCOL *This, |
543 | IN BOOLEAN ExtendedVerification |
544 | ) |
545 | { |
546 | UINTN BusyStall = 50; // microSeconds |
547 | UINTN ResetBusyTimeout = (1000000 / BusyStall); // 1 Second |
548 | |
549 | //Send RESET command to device. |
550 | NandSendCommand(RESET_CMD); |
551 | |
552 | //Wait for 1ms before we check status register. |
553 | gBS->Stall(1000); |
554 | |
555 | //Check BIT#5 & BIT#6 in Status register to make sure RESET is done. |
556 | while ((NandReadStatus() & NAND_RESET_STATUS) != NAND_RESET_STATUS) { |
557 | |
558 | //In case of extended verification, wait for extended amount of time |
559 | //to make sure device is reset. |
560 | if (ExtendedVerification) { |
561 | if (ResetBusyTimeout == 0) { |
562 | return EFI_DEVICE_ERROR; |
563 | } |
564 | |
565 | gBS->Stall(BusyStall); |
566 | ResetBusyTimeout--; |
567 | } |
568 | } |
569 | |
570 | return EFI_SUCCESS; |
571 | } |
572 | |
573 | EFI_STATUS |
574 | EFIAPI |
575 | NandFlashReadBlocks ( |
576 | IN EFI_BLOCK_IO_PROTOCOL *This, |
577 | IN UINT32 MediaId, |
578 | IN EFI_LBA Lba, |
579 | IN UINTN BufferSize, |
580 | OUT VOID *Buffer |
581 | ) |
582 | { |
583 | UINTN NumBlocks; |
584 | UINTN EndBlockIndex; |
585 | EFI_STATUS Status; |
586 | UINT8 *SpareBuffer = NULL; |
587 | |
588 | if (Buffer == NULL) { |
589 | Status = EFI_INVALID_PARAMETER; |
590 | goto exit; |
591 | } |
592 | |
593 | if (Lba > LAST_BLOCK) { |
594 | Status = EFI_INVALID_PARAMETER; |
595 | goto exit; |
596 | } |
597 | |
598 | if ((BufferSize % gNandFlashInfo->BlockSize) != 0) { |
599 | Status = EFI_BAD_BUFFER_SIZE; |
600 | goto exit; |
601 | } |
602 | |
603 | NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize); |
604 | EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1; |
605 | |
606 | SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize); |
607 | if (SpareBuffer == NULL) { |
608 | Status = EFI_OUT_OF_RESOURCES; |
609 | goto exit; |
610 | } |
611 | |
612 | //Read block |
613 | Status = NandReadBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer); |
614 | if (EFI_ERROR(Status)) { |
615 | DEBUG((EFI_D_ERROR, "Read block fails: %x\n", Status)); |
616 | goto exit; |
617 | } |
618 | |
619 | exit: |
620 | if (SpareBuffer != NULL) { |
621 | FreePool (SpareBuffer); |
622 | } |
623 | |
624 | return Status; |
625 | } |
626 | |
627 | EFI_STATUS |
628 | EFIAPI |
629 | NandFlashWriteBlocks ( |
630 | IN EFI_BLOCK_IO_PROTOCOL *This, |
631 | IN UINT32 MediaId, |
632 | IN EFI_LBA Lba, |
633 | IN UINTN BufferSize, |
634 | IN VOID *Buffer |
635 | ) |
636 | { |
637 | UINTN BlockIndex; |
638 | UINTN NumBlocks; |
639 | UINTN EndBlockIndex; |
640 | EFI_STATUS Status; |
641 | UINT8 *SpareBuffer = NULL; |
642 | |
643 | if (Buffer == NULL) { |
644 | Status = EFI_INVALID_PARAMETER; |
645 | goto exit; |
646 | } |
647 | |
648 | if (Lba > LAST_BLOCK) { |
649 | Status = EFI_INVALID_PARAMETER; |
650 | goto exit; |
651 | } |
652 | |
653 | if ((BufferSize % gNandFlashInfo->BlockSize) != 0) { |
654 | Status = EFI_BAD_BUFFER_SIZE; |
655 | goto exit; |
656 | } |
657 | |
658 | NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize); |
659 | EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1; |
660 | |
661 | SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize); |
662 | if (SpareBuffer == NULL) { |
663 | Status = EFI_OUT_OF_RESOURCES; |
664 | goto exit; |
665 | } |
666 | |
667 | // Erase block |
668 | for (BlockIndex = (UINTN)Lba; BlockIndex <= EndBlockIndex; BlockIndex++) { |
669 | Status = NandEraseBlock(BlockIndex); |
670 | if (EFI_ERROR(Status)) { |
671 | DEBUG((EFI_D_ERROR, "Erase block failed. Status: %x\n", Status)); |
672 | goto exit; |
673 | } |
674 | } |
675 | |
676 | // Program data |
677 | Status = NandWriteBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer); |
678 | if (EFI_ERROR(Status)) { |
679 | DEBUG((EFI_D_ERROR, "Block write fails: %x\n", Status)); |
680 | goto exit; |
681 | } |
682 | |
683 | exit: |
684 | if (SpareBuffer != NULL) { |
685 | FreePool (SpareBuffer); |
686 | } |
687 | |
688 | return Status; |
689 | } |
690 | |
691 | EFI_STATUS |
692 | EFIAPI |
693 | NandFlashFlushBlocks ( |
694 | IN EFI_BLOCK_IO_PROTOCOL *This |
695 | ) |
696 | { |
697 | return EFI_SUCCESS; |
698 | } |
699 | |
700 | |
701 | |
702 | EFI_BLOCK_IO_MEDIA gNandFlashMedia = { |
703 | SIGNATURE_32('n','a','n','d'), // MediaId |
704 | FALSE, // RemovableMedia |
705 | TRUE, // MediaPresent |
706 | FALSE, // LogicalPartition |
707 | FALSE, // ReadOnly |
708 | FALSE, // WriteCaching |
709 | 0, // BlockSize |
710 | 2, // IoAlign |
711 | 0, // Pad |
712 | 0 // LastBlock |
713 | }; |
714 | |
715 | EFI_BLOCK_IO_PROTOCOL BlockIo = |
716 | { |
717 | EFI_BLOCK_IO_INTERFACE_REVISION, // Revision |
718 | &gNandFlashMedia, // *Media |
719 | NandFlashReset, // Reset |
720 | NandFlashReadBlocks, // ReadBlocks |
721 | NandFlashWriteBlocks, // WriteBlocks |
722 | NandFlashFlushBlocks // FlushBlocks |
723 | }; |
724 | |
725 | EFI_STATUS |
726 | NandFlashInitialize ( |
727 | IN EFI_HANDLE ImageHandle, |
728 | IN EFI_SYSTEM_TABLE *SystemTable |
729 | ) |
730 | { |
731 | EFI_STATUS Status; |
732 | |
733 | gNandFlashInfo = (NAND_FLASH_INFO *)AllocateZeroPool (sizeof(NAND_FLASH_INFO)); |
734 | |
735 | //Initialize GPMC module. |
736 | GpmcInit(); |
737 | |
738 | //Reset NAND part |
739 | NandFlashReset(&BlockIo, FALSE); |
740 | |
741 | //Detect NAND part and populate gNandFlashInfo structure |
742 | Status = NandDetectPart (); |
743 | if (EFI_ERROR(Status)) { |
744 | DEBUG((EFI_D_ERROR, "Nand part id detection failure: Status: %x\n", Status)); |
745 | return Status; |
746 | } |
747 | |
748 | //Count total number of 512Bytes chunk based on the page size. |
749 | if (gNandFlashInfo->PageSize == PAGE_SIZE_512B) { |
750 | gNum512BytesChunks = 1; |
751 | } else if (gNandFlashInfo->PageSize == PAGE_SIZE_2K) { |
752 | gNum512BytesChunks = 4; |
753 | } else if (gNandFlashInfo->PageSize == PAGE_SIZE_4K) { |
754 | gNum512BytesChunks = 8; |
755 | } |
756 | |
757 | gEccCode = (UINT8 *)AllocatePool(gNum512BytesChunks * 3); |
758 | if (gEccCode == NULL) { |
759 | return EFI_OUT_OF_RESOURCES; |
760 | } |
761 | |
762 | //Configure ECC |
763 | NandConfigureEcc (); |
764 | |
765 | //Patch EFI_BLOCK_IO_MEDIA structure. |
766 | gNandFlashMedia.BlockSize = gNandFlashInfo->BlockSize; |
767 | gNandFlashMedia.LastBlock = LAST_BLOCK; |
768 | |
769 | //Publish BlockIO. |
770 | Status = gBS->InstallMultipleProtocolInterfaces ( |
771 | &ImageHandle, |
772 | &gEfiBlockIoProtocolGuid, &BlockIo, |
773 | &gEfiDevicePathProtocolGuid, &gDevicePath, |
774 | NULL |
775 | ); |
776 | return Status; |
777 | } |
778 | |