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