b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | /****************************************************************************** |
| 2 | ** |
| 3 | ** COPYRIGHT (C) 2002, 2003 Intel Corporation. |
| 4 | ** |
| 5 | ** This software as well as the software described in it is furnished under |
| 6 | ** license and may only be used or copied in accordance with the terms of the |
| 7 | ** license. The information in this file is furnished for informational use |
| 8 | ** only, is subject to change without notice, and should not be construed as |
| 9 | ** a commitment by Intel Corporation. Intel Corporation assumes no |
| 10 | ** responsibility or liability for any errors or inaccuracies that may appear |
| 11 | ** in this document or any software that may be provided in association with |
| 12 | ** this document. |
| 13 | ** Except as permitted by such license, no part of this document may be |
| 14 | ** reproduced, stored in a retrieval system, or transmitted in any form or by |
| 15 | ** any means without the express written consent of Intel Corporation. |
| 16 | ** |
| 17 | ** FILENAME: Flash.c |
| 18 | ** |
| 19 | ** PURPOSE: Contain template OEM boot code flash operations |
| 20 | ** |
| 21 | ******************************************************************************/ |
| 22 | |
| 23 | #include "Flash.h" |
| 24 | #include "nand.h" |
| 25 | #include "general.h" |
| 26 | #include "xllp_dfc.h" |
| 27 | #include "FM.h" |
| 28 | #include "ProtocolManager.h" |
| 29 | #if COPYIMAGESTOFLASH |
| 30 | #include "BootLoader.h" |
| 31 | #endif |
| 32 | #if USE_DMA |
| 33 | #include "xllp_dmac.h" |
| 34 | #endif |
| 35 | #include "dma.h" |
| 36 | #include "BBM.h" |
| 37 | |
| 38 | NAND_Properties_T NAND_Prop; // Only need one |
| 39 | extern UINT_T NandID; |
| 40 | extern UINT_T nand_data_dma, nand_cmd_dma; |
| 41 | |
| 42 | P_NAND_Properties_T GetNANDProperties(void) |
| 43 | { |
| 44 | return &NAND_Prop; |
| 45 | } |
| 46 | |
| 47 | UINT_T GetSpareAreaSize(FlashBootType_T fbt) |
| 48 | { |
| 49 | return GetNANDProperties()->SpareAreaSize; |
| 50 | } |
| 51 | |
| 52 | UINT_T GetBlockSize(FlashBootType_T fbt) |
| 53 | { |
| 54 | return GetNANDProperties()->BlockSize; |
| 55 | } |
| 56 | |
| 57 | UINT_T GetPageSize(FlashBootType_T fbt) |
| 58 | { |
| 59 | return GetNANDProperties()->PageSize; |
| 60 | } |
| 61 | |
| 62 | /* |
| 63 | * Function initializes NAND device and fills out Flash Properties Struct |
| 64 | */ |
| 65 | |
| 66 | UINT_T InitializeNANDDevice(UINT8_T FlashNum, FlashBootType_T FlashBootType, UINT8_T* P_DefaultPartitionNum) |
| 67 | { |
| 68 | UINT_T Retval = NoError; |
| 69 | P_NAND_Properties_T pNandP = GetNANDProperties(); |
| 70 | P_FlashProperties_T pFlashP = GetFlashProperties(FlashBootType); |
| 71 | // Set Current FlashBootType so XllpDfcInit can determine why it is called. |
| 72 | SetCurrentFlashBootType(FlashBootType); |
| 73 | |
| 74 | // Pick correct bus width and set the ECC type |
| 75 | switch (FlashNum) |
| 76 | { |
| 77 | case NAND_FLASH_X16_HM_P: |
| 78 | pNandP->FlashBusWidth = FlashBusWidth16; |
| 79 | pNandP->ECCMode = ECC_HAMMING; |
| 80 | break; |
| 81 | case NAND_FLASH_X16_BCH_P: |
| 82 | pNandP->FlashBusWidth = FlashBusWidth16; |
| 83 | pNandP->ECCMode = ECC_BCH; |
| 84 | break; |
| 85 | case NAND_FLASH_X8_HM_P: |
| 86 | pNandP->FlashBusWidth = FlashBusWidth8; |
| 87 | pNandP->ECCMode = ECC_HAMMING; |
| 88 | break; |
| 89 | case NAND_FLASH_X8_BCH_P: |
| 90 | pNandP->FlashBusWidth = FlashBusWidth8; |
| 91 | pNandP->ECCMode = ECC_BCH; |
| 92 | break; |
| 93 | default: |
| 94 | return DFCInitFailed; |
| 95 | } |
| 96 | |
| 97 | PlatformNandClocksEnable(); |
| 98 | |
| 99 | Retval = XllpDfcInit(pNandP->FlashBusWidth, pNandP); |
| 100 | |
| 101 | if (Retval != NoError) |
| 102 | //return DFCInitFailed; |
| 103 | return NANDNotFound; |
| 104 | |
| 105 | //set ECC with the correct ECCMode |
| 106 | xdfc_enable_ecc( 1 ); |
| 107 | //define functions |
| 108 | pFlashP->ReadFromFlash = &ReadNAND; |
| 109 | pFlashP->WriteToFlash = &WriteNAND; |
| 110 | pFlashP->EraseFlash = &EraseNAND; |
| 111 | pFlashP->ResetFlash = &ResetNAND; |
| 112 | pFlashP->BlockSize = pNandP->BlockSize; |
| 113 | pFlashP->PageSize = pNandP->PageSize; |
| 114 | pFlashP->NumBlocks = pNandP->NumOfBlocks; |
| 115 | pFlashP->FlashSettings.UseBBM = 1; |
| 116 | pFlashP->FlashSettings.UseSpareArea = 0; |
| 117 | pFlashP->FlashSettings.SASize = 0; |
| 118 | pFlashP->FlashSettings.UseHwEcc = 1; // default is HW ECC ON |
| 119 | pFlashP->FlashType = NAND_FLASH; |
| 120 | pFlashP->GenerateFBBT = GenerateFBBTNAND; |
| 121 | // init the TIM load address |
| 122 | //--------------------------------------- |
| 123 | pFlashP->TimFlashAddress = TIMOffset_NAND; |
| 124 | pFlashP->StreamingFlash = FALSE; |
| 125 | *P_DefaultPartitionNum = NAND_DEFAULT_PART; |
| 126 | pFlashP->FinalizeFlash = NULL; |
| 127 | |
| 128 | NandID = (pNandP->FlashID << 8) | pNandP->ManufacturerCode; |
| 129 | |
| 130 | GetDMAReqNum(&nand_data_dma, &nand_cmd_dma); |
| 131 | |
| 132 | return Retval; |
| 133 | } |
| 134 | |
| 135 | UINT_T GetDMAReqNum(UINT_T *data, UINT_T *cmd) |
| 136 | { |
| 137 | UINT_T NAND_DMA_DataReqNum = DMAC_NAND_DATA; |
| 138 | UINT_T NAND_DMA_CMDReqNum = DMAC_NAND_CMD; |
| 139 | |
| 140 | #if NZA3 |
| 141 | switch (PlatformGetRevisionID()) |
| 142 | { |
| 143 | case 0xF0: |
| 144 | case 0xF2: |
| 145 | NAND_DMA_DataReqNum = DMAC_NAND_DATA_Z2; |
| 146 | NAND_DMA_CMDReqNum = DMAC_NAND_CMD_Z2; |
| 147 | break; |
| 148 | |
| 149 | case 0xF3: |
| 150 | default: |
| 151 | break; |
| 152 | } |
| 153 | #endif |
| 154 | |
| 155 | *data = NAND_DMA_DataReqNum; |
| 156 | *cmd = NAND_DMA_CMDReqNum; |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | This routine will read in data from the DFC. |
| 161 | */ |
| 162 | #if !SHRINK_BINARY_SIZE |
| 163 | UINT_T ReadNAND_nonDMA(UINT_T Address, UINT_T Destination, UINT_T ReadAmount, FlashBootType_T FlashBootType) |
| 164 | { |
| 165 | P_NAND_Properties_T pNAND_Prop; |
| 166 | pNAND_Prop = GetNANDProperties(); |
| 167 | |
| 168 | UINT_T temp_buffer[2]; // temp use, no data needed |
| 169 | |
| 170 | // For monolithic operation call legacy read routine |
| 171 | if (pNAND_Prop->PageSize <= TWO_KB) |
| 172 | { |
| 173 | if (upload_nand_spare == TRUE) |
| 174 | { |
| 175 | return xdfc_read_nonDMA((UINT_T *)Destination, Address, ReadAmount, temp_buffer, pNAND_Prop); |
| 176 | } |
| 177 | else |
| 178 | { |
| 179 | return xdfc_read_nonDMA((UINT_T *)Destination, Address, ReadAmount, NULL, pNAND_Prop); |
| 180 | } |
| 181 | } |
| 182 | else{ // Call the new routine that reads page at a time. |
| 183 | #if NAND_LP |
| 184 | return xdfc_read_LP((UINT_T *)Destination, Address, ReadAmount, NULL, pNAND_Prop); |
| 185 | #else |
| 186 | return NoError; |
| 187 | #endif |
| 188 | } |
| 189 | } |
| 190 | #endif |
| 191 | |
| 192 | UINT_T ReadNAND (UINT_T Address, UINT_T Destination, UINT_T ReadAmount, FlashBootType_T FlashBootType) |
| 193 | { |
| 194 | return xdfc_read(Destination, Address, ReadAmount, NULL, GetNANDProperties()); |
| 195 | } |
| 196 | |
| 197 | /* |
| 198 | WriteNAND - This function is a wrapper to work with DFC |
| 199 | */ |
| 200 | |
| 201 | UINT_T WriteNAND (UINT_T flash_addr, UINT_T source, UINT_T WriteAmount, FlashBootType_T FlashBootType) |
| 202 | { |
| 203 | UINT_T Retval = NoError; |
| 204 | P_NAND_Properties_T pNandP = GetNANDProperties(); |
| 205 | P_FlashProperties_T pFlashP = GetFlashProperties(FlashBootType); |
| 206 | if (pNandP->PageSize <= TWO_KB) |
| 207 | { |
| 208 | Retval = xdfc_write((UINT_T *) source, |
| 209 | flash_addr, |
| 210 | WriteAmount, |
| 211 | pFlashP->FlashSettings.UseSpareArea, |
| 212 | pFlashP->FlashSettings.UseHwEcc, |
| 213 | pNandP); |
| 214 | } |
| 215 | else |
| 216 | { |
| 217 | #if NAND_LP |
| 218 | Retval = xdfc_write_LP((UINT_T *) source, |
| 219 | flash_addr, |
| 220 | WriteAmount, |
| 221 | pFlashP->FlashSettings.UseSpareArea, |
| 222 | pNandP); |
| 223 | #endif |
| 224 | } |
| 225 | return Retval; // Return value |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | * |
| 230 | * Erases one block at a time |
| 231 | * note: if user inputs determine that a partial block should be erased, |
| 232 | * this function will erase that WHOLE block: no partial blocks allowed |
| 233 | * |
| 234 | */ |
| 235 | UINT_T EraseNAND (UINT_T flashoffset, UINT_T size, FlashBootType_T fbt) |
| 236 | { |
| 237 | return xdfc_erase(flashoffset, GetNANDProperties()); |
| 238 | } |
| 239 | |
| 240 | //Wrapper to link the dfc reset routine to the flash API |
| 241 | UINT_T ResetNAND(FlashBootType_T fbt) |
| 242 | { |
| 243 | return xdfc_reset(GetNANDProperties()); |
| 244 | } |
| 245 | |
| 246 | static UINT8_T page_buffer[2048]; //temp buffer to read dummy |
| 247 | UINT_T GenerateFBBTNAND(UINT16 *badblocklist) |
| 248 | { |
| 249 | unsigned int Retval, SA, i, j, temp, max, zero = 0, badblocks = 0; |
| 250 | unsigned int PageSize, BlockSize, block_good; |
| 251 | unsigned int *pRB0, *pRB1; |
| 252 | P_NAND_Properties_T pNandP = GetNANDProperties(); |
| 253 | P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH); |
| 254 | UINT_T bUseHwEcc = FALSE; |
| 255 | unsigned int *dumy_buf = page_buffer; |
| 256 | unsigned char *ptemp = 0; |
| 257 | |
| 258 | PageSize = pNandP->PageSize; |
| 259 | BlockSize = pNandP->BlockSize; |
| 260 | max = (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100; //max number of relocation is 2% of device |
| 261 | |
| 262 | ptemp = malloc(BlockSize); |
| 263 | if(ptemp == NULL) |
| 264 | return HeapExhaustedError; |
| 265 | |
| 266 | //buffers used to capture the entire spare area for page 0 and 1, respectively |
| 267 | pRB0 = (unsigned int *) (((unsigned int)ptemp + 16) & 0xFFFFFFF0); //align to 16 byte aligned |
| 268 | pRB1 = (unsigned int *) (((unsigned int)pRB0 + pNandP->SpareAreaSize + 16) & 0xFFFFFFF0); //ditto |
| 269 | |
| 270 | //AddMessageError(REPORT_NOTIFICATION, PlatformBusy); |
| 271 | bUseHwEcc = GetUseHwEcc(BOOT_FLASH ); |
| 272 | SetUseSpareArea( 1, BOOT_FLASH ); |
| 273 | SetUseHwEcc( FALSE , BOOT_FLASH); //Turn Off ECC |
| 274 | |
| 275 | //Block 0 is for TIM, scan from block1 |
| 276 | for (i = 1; i < pFlashProp->NumBlocks; i++) |
| 277 | { |
| 278 | // address of 1st page in each block |
| 279 | SA = i * BlockSize; |
| 280 | // Read first page(including spare area) and second page of the block. |
| 281 | Retval = xdfc_read(dumy_buf, SA , PageSize, pRB0, pNandP); |
| 282 | Retval |= xdfc_read(dumy_buf, SA + PageSize, PageSize, pRB1, pNandP); |
| 283 | |
| 284 | if(Retval != NoError) break; //read failure? something wrong with driver. return |
| 285 | |
| 286 | //finished reading Spare Area in both page 0 and 1 |
| 287 | //now check it: |
| 288 | // if it is all FFFFs, page is good |
| 289 | // else if byte 0 or byte 6 are not FFs, factory marked bad |
| 290 | // else try a erase/write cycle to termine bad v. just needed an erase cycle |
| 291 | block_good = TRUE; |
| 292 | for(j = 0; (block_good == TRUE) && (j < xdfc_getSpareAreaSize(pNandP)); j++) |
| 293 | if( (pRB0[j] != 0xFFFFFFFF) || (pRB1[j] != 0xFFFFFFFF) ) |
| 294 | block_good = FALSE; //check for non FFFFs in spare area |
| 295 | |
| 296 | //is this block 'good'? |
| 297 | if(block_good == FALSE) //nope |
| 298 | { //check bytes 0 and 6 of both spare areas. if any byte != FF, block is factory bad |
| 299 | if( ((pRB0[0] & 0xFF) != 0xFF) || ((pRB0[1] & 0xFF0000) != 0xFF0000) || |
| 300 | ((pRB1[0] & 0xFF) != 0xFF) || ((pRB1[1] & 0xFF0000) != 0xFF0000) ) |
| 301 | badblocklist[badblocks++] = i; |
| 302 | else |
| 303 | { //try erase/program attempt |
| 304 | memcpy(pRB0, &zero, PageSize); //get a page worth of zeros |
| 305 | //erase the block |
| 306 | Retval = xdfc_erase(SA, pNandP); |
| 307 | //try writing all the pages to 0 |
| 308 | for(j = 0; (j < BlockSize) && (Retval == NoError); j += PageSize) |
| 309 | Retval = WriteNAND (SA + j, (unsigned int) pRB0, PageSize, BOOT_FLASH); |
| 310 | //now erase the block again (to see if any bits get stuck at 0) |
| 311 | if(Retval == NoError) |
| 312 | Retval = xdfc_erase(SA, pNandP); |
| 313 | //any failure indicates a bad block |
| 314 | if(Retval != NoError) |
| 315 | badblocklist[badblocks++] = i; |
| 316 | } |
| 317 | } |
| 318 | //if we hit a critical number, return now and let the upper level handle it |
| 319 | if(badblocks >= max) |
| 320 | break; |
| 321 | |
| 322 | } |
| 323 | SetUseHwEcc( bUseHwEcc , BOOT_FLASH); //Turn On ECC |
| 324 | //AddMessageError(REPORT_NOTIFICATION, PlatformReady); |
| 325 | SetUseSpareArea( 0 , BOOT_FLASH); |
| 326 | free(ptemp); |
| 327 | return badblocks; |
| 328 | } |
| 329 | |
| 330 | UINT32 OBMI_FlashEntryAddr = 0; |
| 331 | UINT32 OBMI_ImageSize = 0; |
| 332 | static INT_T OBMBlock = -1; |
| 333 | |
| 334 | void DisableSpareAreaForOBM(UINT_T OBMSartAddr, UINT_T OBMSize) |
| 335 | { |
| 336 | P_NAND_Properties_T pNandP = GetNANDProperties(); |
| 337 | UINT_T BlockNum; |
| 338 | if(OBMSize > pNandP->BlockSize) |
| 339 | { |
| 340 | obm_printf("Error: OBM Size too big.\n\r"); |
| 341 | } |
| 342 | |
| 343 | if(OBMBlock == -1) { |
| 344 | OBMBlock = OBMSartAddr/pNandP->BlockSize; |
| 345 | } |
| 346 | |
| 347 | if(OBMSize == 0){ //OBMSize == 0, means OBM block is relocating |
| 348 | BlockNum = OBMSartAddr/pNandP->BlockSize; |
| 349 | }else{ |
| 350 | BlockNum = LocateBlock(OBMBlock, BOOT_FLASH); |
| 351 | } |
| 352 | OBMI_FlashEntryAddr = BlockNum * pNandP->BlockSize; |
| 353 | |
| 354 | if(OBMSize != 0) |
| 355 | OBMI_ImageSize = OBMSize; |
| 356 | } |
| 357 | |
| 358 | INT_T IsAccessOBMBlock(UINT32 Address) |
| 359 | { |
| 360 | UINT32 BlockSize = GetNANDProperties()->BlockSize; |
| 361 | UINT32 BlockNum; |
| 362 | BlockNum = Address/BlockSize; |
| 363 | |
| 364 | return (BlockNum == OBMBlock); |
| 365 | } |