| /****************************************************************************** |
| ** |
| ** COPYRIGHT (C) 2002, 2003 Intel Corporation. |
| ** |
| ** This software as well as the software described in it is furnished under |
| ** license and may only be used or copied in accordance with the terms of the |
| ** license. The information in this file is furnished for informational use |
| ** only, is subject to change without notice, and should not be construed as |
| ** a commitment by Intel Corporation. Intel Corporation assumes no |
| ** responsibility or liability for any errors or inaccuracies that may appear |
| ** in this document or any software that may be provided in association with |
| ** this document. |
| ** Except as permitted by such license, no part of this document may be |
| ** reproduced, stored in a retrieval system, or transmitted in any form or by |
| ** any means without the express written consent of Intel Corporation. |
| ** |
| ** FILENAME: Flash.c |
| ** |
| ** PURPOSE: Contain template OEM boot code flash operations |
| ** |
| ******************************************************************************/ |
| |
| #include "Flash.h" |
| #include "nand.h" |
| #include "general.h" |
| #include "xllp_dfc.h" |
| #include "FM.h" |
| #include "ProtocolManager.h" |
| #if COPYIMAGESTOFLASH |
| #include "BootLoader.h" |
| #endif |
| #if USE_DMA |
| #include "xllp_dmac.h" |
| #endif |
| #include "dma.h" |
| #include "BBM.h" |
| |
| NAND_Properties_T NAND_Prop; // Only need one |
| extern UINT_T NandID; |
| extern UINT_T nand_data_dma, nand_cmd_dma; |
| |
| P_NAND_Properties_T GetNANDProperties(void) |
| { |
| return &NAND_Prop; |
| } |
| |
| UINT_T GetSpareAreaSize(FlashBootType_T fbt) |
| { |
| return GetNANDProperties()->SpareAreaSize; |
| } |
| |
| UINT_T GetBlockSize(FlashBootType_T fbt) |
| { |
| return GetNANDProperties()->BlockSize; |
| } |
| |
| UINT_T GetPageSize(FlashBootType_T fbt) |
| { |
| return GetNANDProperties()->PageSize; |
| } |
| |
| /* |
| * Function initializes NAND device and fills out Flash Properties Struct |
| */ |
| |
| UINT_T InitializeNANDDevice(UINT8_T FlashNum, FlashBootType_T FlashBootType, UINT8_T* P_DefaultPartitionNum) |
| { |
| UINT_T Retval = NoError; |
| P_NAND_Properties_T pNandP = GetNANDProperties(); |
| P_FlashProperties_T pFlashP = GetFlashProperties(FlashBootType); |
| // Set Current FlashBootType so XllpDfcInit can determine why it is called. |
| SetCurrentFlashBootType(FlashBootType); |
| |
| // Pick correct bus width and set the ECC type |
| switch (FlashNum) |
| { |
| case NAND_FLASH_X16_HM_P: |
| pNandP->FlashBusWidth = FlashBusWidth16; |
| pNandP->ECCMode = ECC_HAMMING; |
| break; |
| case NAND_FLASH_X16_BCH_P: |
| pNandP->FlashBusWidth = FlashBusWidth16; |
| pNandP->ECCMode = ECC_BCH; |
| break; |
| case NAND_FLASH_X8_HM_P: |
| pNandP->FlashBusWidth = FlashBusWidth8; |
| pNandP->ECCMode = ECC_HAMMING; |
| break; |
| case NAND_FLASH_X8_BCH_P: |
| pNandP->FlashBusWidth = FlashBusWidth8; |
| pNandP->ECCMode = ECC_BCH; |
| break; |
| default: |
| return DFCInitFailed; |
| } |
| |
| PlatformNandClocksEnable(); |
| |
| Retval = XllpDfcInit(pNandP->FlashBusWidth, pNandP); |
| |
| if (Retval != NoError) |
| //return DFCInitFailed; |
| return NANDNotFound; |
| |
| //set ECC with the correct ECCMode |
| xdfc_enable_ecc( 1 ); |
| //define functions |
| pFlashP->ReadFromFlash = &ReadNAND; |
| pFlashP->WriteToFlash = &WriteNAND; |
| pFlashP->EraseFlash = &EraseNAND; |
| pFlashP->ResetFlash = &ResetNAND; |
| pFlashP->BlockSize = pNandP->BlockSize; |
| pFlashP->PageSize = pNandP->PageSize; |
| pFlashP->NumBlocks = pNandP->NumOfBlocks; |
| pFlashP->FlashSettings.UseBBM = 1; |
| pFlashP->FlashSettings.UseSpareArea = 0; |
| pFlashP->FlashSettings.SASize = 0; |
| pFlashP->FlashSettings.UseHwEcc = 1; // default is HW ECC ON |
| pFlashP->FlashType = NAND_FLASH; |
| pFlashP->GenerateFBBT = GenerateFBBTNAND; |
| // init the TIM load address |
| //--------------------------------------- |
| pFlashP->TimFlashAddress = TIMOffset_NAND; |
| pFlashP->StreamingFlash = FALSE; |
| *P_DefaultPartitionNum = NAND_DEFAULT_PART; |
| pFlashP->FinalizeFlash = NULL; |
| |
| NandID = (pNandP->FlashID << 8) | pNandP->ManufacturerCode; |
| |
| GetDMAReqNum(&nand_data_dma, &nand_cmd_dma); |
| |
| return Retval; |
| } |
| |
| UINT_T GetDMAReqNum(UINT_T *data, UINT_T *cmd) |
| { |
| UINT_T NAND_DMA_DataReqNum = DMAC_NAND_DATA; |
| UINT_T NAND_DMA_CMDReqNum = DMAC_NAND_CMD; |
| |
| #if NZA3 |
| switch (PlatformGetRevisionID()) |
| { |
| case 0xF0: |
| case 0xF2: |
| NAND_DMA_DataReqNum = DMAC_NAND_DATA_Z2; |
| NAND_DMA_CMDReqNum = DMAC_NAND_CMD_Z2; |
| break; |
| |
| case 0xF3: |
| default: |
| break; |
| } |
| #endif |
| |
| *data = NAND_DMA_DataReqNum; |
| *cmd = NAND_DMA_CMDReqNum; |
| } |
| |
| /* |
| This routine will read in data from the DFC. |
| */ |
| #if !SHRINK_BINARY_SIZE |
| UINT_T ReadNAND_nonDMA(UINT_T Address, UINT_T Destination, UINT_T ReadAmount, FlashBootType_T FlashBootType) |
| { |
| P_NAND_Properties_T pNAND_Prop; |
| pNAND_Prop = GetNANDProperties(); |
| |
| UINT_T temp_buffer[2]; // temp use, no data needed |
| |
| // For monolithic operation call legacy read routine |
| if (pNAND_Prop->PageSize <= TWO_KB) |
| { |
| if (upload_nand_spare == TRUE) |
| { |
| return xdfc_read_nonDMA((UINT_T *)Destination, Address, ReadAmount, temp_buffer, pNAND_Prop); |
| } |
| else |
| { |
| return xdfc_read_nonDMA((UINT_T *)Destination, Address, ReadAmount, NULL, pNAND_Prop); |
| } |
| } |
| else{ // Call the new routine that reads page at a time. |
| #if NAND_LP |
| return xdfc_read_LP((UINT_T *)Destination, Address, ReadAmount, NULL, pNAND_Prop); |
| #else |
| return NoError; |
| #endif |
| } |
| } |
| #endif |
| |
| UINT_T ReadNAND (UINT_T Address, UINT_T Destination, UINT_T ReadAmount, FlashBootType_T FlashBootType) |
| { |
| return xdfc_read(Destination, Address, ReadAmount, NULL, GetNANDProperties()); |
| } |
| |
| /* |
| WriteNAND - This function is a wrapper to work with DFC |
| */ |
| |
| UINT_T WriteNAND (UINT_T flash_addr, UINT_T source, UINT_T WriteAmount, FlashBootType_T FlashBootType) |
| { |
| UINT_T Retval = NoError; |
| P_NAND_Properties_T pNandP = GetNANDProperties(); |
| P_FlashProperties_T pFlashP = GetFlashProperties(FlashBootType); |
| if (pNandP->PageSize <= TWO_KB) |
| { |
| Retval = xdfc_write((UINT_T *) source, |
| flash_addr, |
| WriteAmount, |
| pFlashP->FlashSettings.UseSpareArea, |
| pFlashP->FlashSettings.UseHwEcc, |
| pNandP); |
| } |
| else |
| { |
| #if NAND_LP |
| Retval = xdfc_write_LP((UINT_T *) source, |
| flash_addr, |
| WriteAmount, |
| pFlashP->FlashSettings.UseSpareArea, |
| pNandP); |
| #endif |
| } |
| return Retval; // Return value |
| } |
| |
| /* |
| * |
| * Erases one block at a time |
| * note: if user inputs determine that a partial block should be erased, |
| * this function will erase that WHOLE block: no partial blocks allowed |
| * |
| */ |
| UINT_T EraseNAND (UINT_T flashoffset, UINT_T size, FlashBootType_T fbt) |
| { |
| return xdfc_erase(flashoffset, GetNANDProperties()); |
| } |
| |
| //Wrapper to link the dfc reset routine to the flash API |
| UINT_T ResetNAND(FlashBootType_T fbt) |
| { |
| return xdfc_reset(GetNANDProperties()); |
| } |
| |
| static UINT8_T page_buffer[2048]; //temp buffer to read dummy |
| UINT_T GenerateFBBTNAND(UINT16 *badblocklist) |
| { |
| unsigned int Retval, SA, i, j, temp, max, zero = 0, badblocks = 0; |
| unsigned int PageSize, BlockSize, block_good; |
| unsigned int *pRB0, *pRB1; |
| P_NAND_Properties_T pNandP = GetNANDProperties(); |
| P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| UINT_T bUseHwEcc = FALSE; |
| unsigned int *dumy_buf = page_buffer; |
| unsigned char *ptemp = 0; |
| |
| PageSize = pNandP->PageSize; |
| BlockSize = pNandP->BlockSize; |
| max = (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100; //max number of relocation is 2% of device |
| |
| ptemp = malloc(BlockSize); |
| if(ptemp == NULL) |
| return HeapExhaustedError; |
| |
| //buffers used to capture the entire spare area for page 0 and 1, respectively |
| pRB0 = (unsigned int *) (((unsigned int)ptemp + 16) & 0xFFFFFFF0); //align to 16 byte aligned |
| pRB1 = (unsigned int *) (((unsigned int)pRB0 + pNandP->SpareAreaSize + 16) & 0xFFFFFFF0); //ditto |
| |
| //AddMessageError(REPORT_NOTIFICATION, PlatformBusy); |
| bUseHwEcc = GetUseHwEcc(BOOT_FLASH ); |
| SetUseSpareArea( 1, BOOT_FLASH ); |
| SetUseHwEcc( FALSE , BOOT_FLASH); //Turn Off ECC |
| |
| //Block 0 is for TIM, scan from block1 |
| for (i = 1; i < pFlashProp->NumBlocks; i++) |
| { |
| // address of 1st page in each block |
| SA = i * BlockSize; |
| // Read first page(including spare area) and second page of the block. |
| Retval = xdfc_read(dumy_buf, SA , PageSize, pRB0, pNandP); |
| Retval |= xdfc_read(dumy_buf, SA + PageSize, PageSize, pRB1, pNandP); |
| |
| if(Retval != NoError) break; //read failure? something wrong with driver. return |
| |
| //finished reading Spare Area in both page 0 and 1 |
| //now check it: |
| // if it is all FFFFs, page is good |
| // else if byte 0 or byte 6 are not FFs, factory marked bad |
| // else try a erase/write cycle to termine bad v. just needed an erase cycle |
| block_good = TRUE; |
| for(j = 0; (block_good == TRUE) && (j < xdfc_getSpareAreaSize(pNandP)); j++) |
| if( (pRB0[j] != 0xFFFFFFFF) || (pRB1[j] != 0xFFFFFFFF) ) |
| block_good = FALSE; //check for non FFFFs in spare area |
| |
| //is this block 'good'? |
| if(block_good == FALSE) //nope |
| { //check bytes 0 and 6 of both spare areas. if any byte != FF, block is factory bad |
| if( ((pRB0[0] & 0xFF) != 0xFF) || ((pRB0[1] & 0xFF0000) != 0xFF0000) || |
| ((pRB1[0] & 0xFF) != 0xFF) || ((pRB1[1] & 0xFF0000) != 0xFF0000) ) |
| badblocklist[badblocks++] = i; |
| else |
| { //try erase/program attempt |
| memcpy(pRB0, &zero, PageSize); //get a page worth of zeros |
| //erase the block |
| Retval = xdfc_erase(SA, pNandP); |
| //try writing all the pages to 0 |
| for(j = 0; (j < BlockSize) && (Retval == NoError); j += PageSize) |
| Retval = WriteNAND (SA + j, (unsigned int) pRB0, PageSize, BOOT_FLASH); |
| //now erase the block again (to see if any bits get stuck at 0) |
| if(Retval == NoError) |
| Retval = xdfc_erase(SA, pNandP); |
| //any failure indicates a bad block |
| if(Retval != NoError) |
| badblocklist[badblocks++] = i; |
| } |
| } |
| //if we hit a critical number, return now and let the upper level handle it |
| if(badblocks >= max) |
| break; |
| |
| } |
| SetUseHwEcc( bUseHwEcc , BOOT_FLASH); //Turn On ECC |
| //AddMessageError(REPORT_NOTIFICATION, PlatformReady); |
| SetUseSpareArea( 0 , BOOT_FLASH); |
| free(ptemp); |
| return badblocks; |
| } |
| |
| UINT32 OBMI_FlashEntryAddr = 0; |
| UINT32 OBMI_ImageSize = 0; |
| static INT_T OBMBlock = -1; |
| |
| void DisableSpareAreaForOBM(UINT_T OBMSartAddr, UINT_T OBMSize) |
| { |
| P_NAND_Properties_T pNandP = GetNANDProperties(); |
| UINT_T BlockNum; |
| if(OBMSize > pNandP->BlockSize) |
| { |
| obm_printf("Error: OBM Size too big.\n\r"); |
| } |
| |
| if(OBMBlock == -1) { |
| OBMBlock = OBMSartAddr/pNandP->BlockSize; |
| } |
| |
| if(OBMSize == 0){ //OBMSize == 0, means OBM block is relocating |
| BlockNum = OBMSartAddr/pNandP->BlockSize; |
| }else{ |
| BlockNum = LocateBlock(OBMBlock, BOOT_FLASH); |
| } |
| OBMI_FlashEntryAddr = BlockNum * pNandP->BlockSize; |
| |
| if(OBMSize != 0) |
| OBMI_ImageSize = OBMSize; |
| } |
| |
| INT_T IsAccessOBMBlock(UINT32 Address) |
| { |
| UINT32 BlockSize = GetNANDProperties()->BlockSize; |
| UINT32 BlockNum; |
| BlockNum = Address/BlockSize; |
| |
| return (BlockNum == OBMBlock); |
| } |