/*************************************************************************** * Broadcom Corp. Confidential * Copyright 2001 Broadcom Corp. All Rights Reserved. * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED * SOFTWARE LICENSE AGREEMENT BETWEEN THE USER AND BROADCOM. * YOU HAVE NO RIGHT TO USE OR EXPLOIT THIS MATERIAL EXCEPT * SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * *************************************************************************** * File Name : bcm63xx_flash.c * * Description: This file contains the flash device driver for bcm63xx board. Very similar to * board.c in linux development. * * Created on : 4/18/2002 seanl * ***************************************************************************/ /* Includes. */ #include "lib_types.h" #include "lib_malloc.h" #include "lib_string.h" #include "lib_printf.h" #include "bcm_map.h" #include "bcm_hwdefs.h" #include "dev_bcm63xx_flash.h" #include "flash_api.h" #include "boardparms.h" #include "boardparms_voice.h" #include "bcm63xx_util.h" //#define DEBUG_FLASH /* This driver determines the NVRAM and persistent storage flash address and * length. */ /* Foxconn start jenny add for timeout */ extern int NMRPKeepAlive(void); extern int NMRPTFTPWaiting(void); extern int g_nmrp_keepalive; extern int nmrp_server_detected; /* Foxconn end jenny add for timeout*/ static FLASH_ADDR_INFO fInfo; //************************************************************************************** // Flash read/write and image downloading.. //************************************************************************************** void kerSysFlashInit( void ) { NVRAM_DATA nvramData; flash_init(); while ((readNvramData(&nvramData) != 0) || (BpSetBoardId(nvramData.szBoardId) != BP_SUCCESS)) { printf("\n*** Board is not initialized properly ***\n\n"); //setBoardParam(); /* Bob removed to set default board parameters, 11/01/2010 */ setDefaultBoardParam(); /* Bob added to set default board parameters, 11/01/2010 */ } fInfo.flash_rootfs_start_offset = flash_get_sector_size(0); if( fInfo.flash_rootfs_start_offset < FLASH_LENGTH_BOOT_ROM ) fInfo.flash_rootfs_start_offset = FLASH_LENGTH_BOOT_ROM; flash_init_info(&nvramData, &fInfo); #if (INC_NAND_FLASH_DRIVER==1) validateNandPartTbl(&nvramData); /* Check if spare area data contains non 0xff values after JFFS2 clean * marker. Early version of this CFE driver filled bytes 8 - 11 with * 0 which Linux does not like. */ { extern int read_spare_data(int blk, unsigned char *buf, int bufsize); int blk_size = flash_get_sector_size(0) / 1024; int blk_start = nvramData.ulNandPartOfsKb[NP_DATA] / blk_size; unsigned char spare[64]; memset(spare, 0xff, sizeof(spare)); if( read_spare_data(blk_start, spare, sizeof(spare)) == FLASH_API_OK ) { const int spare_len = 8; /* Linux JFFS2 spare area is 8 bytes */ int i; for( i = spare_len; i < sizeof(spare); i++ ) { if( spare[i] != 0xff ) { printf("Data spare area is not correct, erasing psi\n"); printf("%8.8lx %8.8lx %8.8lx %8.8lx\n", *(unsigned long *) &spare[0],*(unsigned long *)&spare[4], *(unsigned long *) &spare[8],*(unsigned long *)&spare[12]); kerSysErasePsi(); break; } } } } #endif } #if (INC_NAND_FLASH_DRIVER==1) || (INC_SPI_PROG_NAND==1) /*********************************************************************** * Function Name: validateNandPartTbl * Description : Checks the NAND partition table fields in NVRAM data. * If nay of the fields are not valid, new values are set. * Returns : None. ***********************************************************************/ void validateNandPartTbl(PNVRAM_DATA pNvramData) { unsigned long ulBlockSizeKb = (unsigned long)flash_get_sector_size(0)/1024; unsigned long ulTotalSizeKb = (unsigned long)flash_get_total_size() / 1024; #if (INC_SPI_PROG_NAND==1) if( flash_get_flash_type() != FLASH_IFC_NAND ) return; #endif if( pNvramData->ulNandPartSizeKb[NP_BOOT] != ulBlockSizeKb || pNvramData->ulNandPartSizeKb[NP_DATA] != NAND_DATA_SIZE_KB || (pNvramData->ulNandPartSizeKb[NP_BBT] != NAND_BBT_SMALL_SIZE_KB && pNvramData->ulNandPartSizeKb[NP_BBT] != NAND_BBT_BIG_SIZE_KB) ) { /* Initialize NAND flash partition table. */ unsigned long ulRootfsSizeKb; unsigned long ulBbtSizeKb = (ulTotalSizeKb > NAND_BBT_THRESHOLD_KB) ? NAND_BBT_BIG_SIZE_KB : NAND_BBT_SMALL_SIZE_KB; /* The Boot partition is first and is one block in size. */ pNvramData->ulNandPartOfsKb[NP_BOOT] = 0; pNvramData->ulNandPartSizeKb[NP_BOOT] = ulBlockSizeKb; /* The Bad Block Table partition is last and is a constant size. */ pNvramData->ulNandPartOfsKb[NP_BBT] = ulTotalSizeKb - ulBbtSizeKb; pNvramData->ulNandPartSizeKb[NP_BBT] = ulBbtSizeKb; /* The Data partition is before the BBT and is a constant size. */ pNvramData->ulNandPartOfsKb[NP_DATA] = pNvramData->ulNandPartOfsKb[NP_BBT] - NAND_DATA_SIZE_KB; pNvramData->ulNandPartSizeKb[NP_DATA] = NAND_DATA_SIZE_KB; /* The first rootfs partition starts at the second sector. */ pNvramData->ulNandPartOfsKb[NP_ROOTFS_1] = ulBlockSizeKb; /* The size of the two root file system partitions is whatever is left * after the Boot, Data and BBT paritions divided by 2 and evenly * divisible by the NAND flash block size. */ ulRootfsSizeKb = ((pNvramData->ulNandPartOfsKb[NP_DATA] - pNvramData->ulNandPartOfsKb[NP_ROOTFS_1]) / 2); ulRootfsSizeKb = (ulRootfsSizeKb / ulBlockSizeKb) * ulBlockSizeKb; #if 1 /* support two file system partitions */ pNvramData->ulNandPartSizeKb[NP_ROOTFS_1] = ulRootfsSizeKb; pNvramData->ulNandPartOfsKb[NP_ROOTFS_2] = pNvramData->ulNandPartOfsKb[NP_ROOTFS_1] + ulRootfsSizeKb; pNvramData->ulNandPartSizeKb[NP_ROOTFS_2] = ulRootfsSizeKb; #else /* support one file system partition */ pNvramData->ulNandPartSizeKb[NP_ROOTFS_1] = ulRootfsSizeKb * 2; pNvramData->ulNandPartOfsKb[NP_ROOTFS_2] = ulTotalSizeKb; pNvramData->ulNandPartSizeKb[NP_ROOTFS_2] = 0; #endif writeNvramData(pNvramData); #if defined(DEBUG_FLASH) printf("boot offset=0x%8.8lx, size=0x%8.8lx\n", pNvramData->ulNandPartOfsKb[NP_BOOT], pNvramData->ulNandPartSizeKb[NP_BOOT]); printf("rootfs1 offset=0x%8.8lx, size=0x%8.8lx\n", pNvramData->ulNandPartOfsKb[NP_ROOTFS_1], pNvramData->ulNandPartSizeKb[NP_ROOTFS_1]); printf("rootfs2 offset=0x%8.8lx, size=0x%8.8lx\n", pNvramData->ulNandPartOfsKb[NP_ROOTFS_2], pNvramData->ulNandPartSizeKb[NP_ROOTFS_2]); printf("data offset=0x%8.8lx, size=0x%8.8lx\n", pNvramData->ulNandPartOfsKb[NP_DATA], pNvramData->ulNandPartSizeKb[NP_DATA]); printf("bbt offset=0x%8.8lx, size=0x%8.8lx\n", pNvramData->ulNandPartOfsKb[NP_BBT], pNvramData->ulNandPartSizeKb[NP_BBT]); #endif } } #else void validateNandPartTbl(PNVRAM_DATA pNvramData) { } #endif /*********************************************************************** * Function Name: kerSysFlashAddrInfoGet * Description : Fills in a structure with information about the NVRAM * and persistent storage sections of flash memory. * Returns : None. ***********************************************************************/ void kerSysFlashAddrInfoGet(PFLASH_ADDR_INFO pflash_addr_info) { memcpy(pflash_addr_info, &fInfo, sizeof(FLASH_ADDR_INFO)); } // get shared blks into *** pTempBuf *** which has to be released bye the caller! // return: if pTempBuf != NULL, poits to the data with the dataSize of the buffer // !NULL -- ok // NULL -- fail static unsigned char *getSharedBlks(int start_blk, int num_blks) { int i = 0; int usedBlkSize = 0; int sect_size = 0; unsigned char *pTempBuf = NULL; unsigned char *pBuf = NULL; for (i = start_blk; i < (start_blk + num_blks); i++) usedBlkSize += flash_get_sector_size((unsigned short) i); if ((pTempBuf = (unsigned char *) KMALLOC(usedBlkSize, sizeof(long))) == NULL) { printf("failed to allocate memory with size: %d\n", usedBlkSize); return pTempBuf; } pBuf = pTempBuf; for (i = start_blk; i < (start_blk + num_blks); i++) { sect_size = flash_get_sector_size((unsigned short) i); #if defined(DEBUG_FLASH) printf("getShareBlks: blk=%d, sect_size=%d\n", i, sect_size); #endif flash_read_buf((unsigned short)i, 0, pBuf, sect_size); pBuf += sect_size; } return pTempBuf; } // Set the pTempBuf to flash from start_blk for num_blks // return: // 0 -- ok // -1 -- fail static int setSharedBlks(int start_blk, int num_blks, unsigned char *pTempBuf) { int i = 0; int sect_size = 0; int sts = 0; unsigned char *pBuf = pTempBuf; for (i = start_blk; i < (start_blk + num_blks); i++) { sect_size = flash_get_sector_size((unsigned short) i); flash_sector_erase_int(i); if (flash_write_buf(i, 0, pBuf, sect_size) != sect_size) { printf("Error writing flash sector %d.", i); sts = -1; break; } #if defined(DEBUG_FLASH) printf("setShareBlks: blk=%d, sect_size=%d\n", i, sect_size); #endif pBuf += sect_size; } return sts; } /******************************************************************************* * NVRAM functions *******************************************************************************/ // get nvram data // return: // 0 - ok // -1 - fail int kerSysNvRamGet(unsigned char *string, int strLen, int offset) { unsigned char *pBuf = NULL; if ((pBuf = getSharedBlks(NVRAM_SECTOR, 1)) == NULL) return -1; // get string off the memory buffer memcpy(string, (pBuf + NVRAM_DATA_OFFSET + offset), strLen); KFREE(pBuf); return 0; } // set nvram // return: // 0 - ok // -1 - fail int kerSysNvRamSet(unsigned char *string, int strLen, int offset) { int sts = 0; unsigned char *pBuf = NULL; if ((pBuf = getSharedBlks(NVRAM_SECTOR, 1)) == NULL) return -1; // set string to the memory buffer memcpy((pBuf + NVRAM_DATA_OFFSET + offset), string, strLen); if (setSharedBlks(NVRAM_SECTOR, 1, pBuf) != 0) sts = -1; KFREE(pBuf); return sts; } /*********************************************************************** * Function Name: kerSysEraseNvRam * Description : Erase the NVRAM storage section of flash memory. * Returns : 1 -- ok, 0 -- fail ***********************************************************************/ int kerSysEraseNvRam(void) { int sts = 1; unsigned char *tempStorage = KMALLOC(NVRAM_LENGTH, sizeof(long)); // just write the whole buf with '0xff' to the flash if (!tempStorage) sts = 0; else { memset(tempStorage, 0xff, NVRAM_LENGTH); if (kerSysNvRamSet(tempStorage, NVRAM_LENGTH, 0) != 0) sts = 0; KFREE(tempStorage); } return sts; } /******************************************************************************* * PSI functions *******************************************************************************/ #if (INC_NAND_FLASH_DRIVER!=1) /** set psi while preserving any other data that might be sharing sectors with * the psi, e.g. scratch pad. * * @param string (IN) buffer that holds the data to be written. * @param strLen (IN) length of buffer. * * @return 0 if OK, -1 on failure. */ static int kerSysPsiSet(const unsigned char *string, int strLen) { int sts = 0; unsigned char *pBuf = NULL; if ((pBuf = getSharedBlks(fInfo.flash_persistent_start_blk, fInfo.flash_persistent_number_blk)) == NULL) return -1; // set string to the memory buffer memcpy((pBuf + fInfo.flash_persistent_blk_offset), string, strLen); if (setSharedBlks(fInfo.flash_persistent_start_blk, fInfo.flash_persistent_number_blk, pBuf) != 0) sts = -1; KFREE(pBuf); return sts; } /** set backup psi * * Backup PSI does not share its sectors with anything else, so this * function does not need to read first and write. Just write. * This function expects the length of the buffer to be exactly the * length of the entire PSI. * * @param string (IN) buffer that holds the data to be written. * * @return 0 if OK, -1 on failure. */ static int kerSysBackupPsiSet(const unsigned char *string) { int sts = 0; if (setSharedBlks(fInfo.flash_backup_psi_start_blk, fInfo.flash_backup_psi_number_blk, (unsigned char *) string) != 0) sts = -1; return sts; } /*********************************************************************** * Function Name: kerSysErasePsi * Description : Erase the Psi storage section of flash memory. * Returns : 1 -- ok, 0 -- fail ***********************************************************************/ int kerSysErasePsi(void) { int sts = 1; unsigned char *tempStorage; if (fInfo.flash_persistent_start_blk == 0) { sts = 0; } else { tempStorage = KMALLOC(fInfo.flash_persistent_length, sizeof(long)); // just write the whole buf with '0xff' to the flash if (!tempStorage) sts = 0; else { memset(tempStorage, 0xff, fInfo.flash_persistent_length); if (kerSysPsiSet(tempStorage, fInfo.flash_persistent_length) != 0) sts = 0; // Also erase backup psi if it is there if (fInfo.flash_backup_psi_number_blk > 0) { if (kerSysBackupPsiSet(tempStorage) != 0) sts = 0; } KFREE(tempStorage); } } return sts; } #else int kerSysErasePsi(void) { int sts = 1; NVRAM_DATA nvramData; if( readNvramData(&nvramData) == 0 ) { int blk_size = flash_get_sector_size(0) / 1024; int blk_start = nvramData.ulNandPartOfsKb[NP_DATA] / blk_size; int total_blks = blk_start + (nvramData.ulNandPartSizeKb[NP_DATA]) / blk_size; while( blk_start < total_blks ) { flash_sector_erase_int(blk_start); blk_start++; } sts = 0; } return(sts); } #endif // flash bcm image // return: // 0 - ok // !0 - the sector number fail to be flashed (should not be 0) int kerSysBcmImageSet( int flash_start_addr, unsigned char *string, int size, int fWholeImage) { int sts; int sect_size; int blk_start; // int savedSize = size; int total_blks = flash_get_numsectors(); /* Foxconn add by Cliff Wang, 03/23/2010 */ unsigned char *pTempBuf = NULL; int savedBlkStart; /* Foxconn add end by Cliff Wang, 03/23/2010 */ if( flash_get_flash_type() == FLASH_IFC_NAND ) { if( flash_start_addr == FLASH_BASE ) total_blks = 1; else { NVRAM_DATA nvramData; if( readNvramData(&nvramData) == 0 ) { sect_size = flash_get_sector_size(0); int rootfs = ((flash_start_addr - FLASH_BASE) / 1024 == nvramData.ulNandPartOfsKb[NP_ROOTFS_2]) ? NP_ROOTFS_2 : NP_ROOTFS_1; total_blks = (nvramData.ulNandPartOfsKb[rootfs] + nvramData.ulNandPartSizeKb[rootfs]) / (sect_size/ 1024); } } } #if defined(DEBUG_FLASH) printf("kerSysBcmImageSet: flash_start_addr=0x%x string=%p len=%d wholeImage=%d\n", flash_start_addr, string, size, fWholeImage); #endif blk_start = flash_get_blk(flash_start_addr); /* Foxconn add start by Cliff Wang, 03/23/2010 */ savedBlkStart = blk_start; if( blk_start < 0 ) return( -1 ); /* Foxconn add end by Cliff Wang, 03/23/2010 */ /* write image to flash memory */ do { g_nmrp_keepalive = 1; /* Foxconn add by Cliff Wang, 03/23/2010 */ sect_size = flash_get_sector_size(blk_start); #if defined(DEBUG_FLASH) printf("Image flashing on block: %d\n", blk_start); #endif // share the blk with psi only when fWholeImage == 0 // Foxconn modified by Silver Shih for burn board Id if ((!fWholeImage && blk_start == fInfo.flash_persistent_start_blk) || (fWholeImage == 5)) { #if 0 if (size > (sect_size - fInfo.flash_persistent_length)) { printf("Image is too big\n"); break; // image is too big. Can not overwrite to psi } #endif if ((pTempBuf = (unsigned char *) KMALLOC(sect_size, sizeof(long))) == NULL) { printf("Failed to allocate memory with size: %d\n", sect_size); break; } flash_read_buf((unsigned short)blk_start, 0, pTempBuf, sect_size); memcpy(pTempBuf, string, size); flash_sector_erase_int(blk_start); // erase blk before flash if (flash_write_buf(blk_start, 0, pTempBuf, sect_size) == sect_size) size = 0; // break out and say all is ok break; } flash_sector_erase_int(blk_start); // erase blk before flash if (sect_size > size) { if (size & 1) size++; sect_size = size; } if (flash_write_buf(blk_start, 0, string, sect_size) != sect_size) { if( flash_get_flash_type() != FLASH_IFC_NAND ) break; blk_start++; } else { printf("."); blk_start++; string += sect_size; size -= sect_size; /* Foxconn added start by jenny @NMRP */ if(nmrp_server_detected==1) // in NMRP mode { if(blk_start - savedBlkStart == 30) { savedBlkStart = blk_start; printf("\n"); NMRPKeepAlive(); } } /* Foxconn added end by jenny @NMRP */ } } while (size > 0); g_nmrp_keepalive = 0; /* Foxconn added by jenny add for timeout */ #if 0 /* Foxconn removed by EricHuang */ if (size == 0 && fWholeImage && savedSize > FLASH_LENGTH_BOOT_ROM) { // If flashing a whole image, erase to end of flash. while( blk_start < total_blks ) { flash_sector_erase_int(blk_start); printf("."); blk_start++; } } #endif printf("\n\n"); if( size == 0 ) sts = 0; // ok else sts = blk_start; // failed to flash this sector g_nmrp_keepalive = 0; /* Foxconn jenny add for timeout */ return sts; } unsigned long kerSysReadFromFlash( void *toaddr, unsigned long fromaddr, unsigned long len ) { int sect = flash_get_blk((int) fromaddr); unsigned char *start = flash_get_memptr(sect); flash_read_buf( sect, (int) fromaddr - (int) start, toaddr, len ); return(len); }