/* * NAND Flash Controller Device Driver * Copyright (c) 2008-2010, Intel Corporation and its suppliers. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "lld.h" #include "lld_emu.h" #ifdef ELDORA // Code for LLD_EMU under Eldora LLD #include "defs.h" #else // Code for LLD_EMU under Spectra LLD #include "testcnf.h" #include "flash.h" #include "ffsdefs.h" #endif #if (CMD_DMA && FLASH_EMU) #include "lld_cdma.h" uint32 GLOB_totalUsedBanks; uint32 GLOB_valid_banks[LLD_NUM_FLASH_CHANNELS]; uint32 GLOB_cmd_tag_count; BLOCKNODE discarded_blocks[LLD_NUM_FLASH_CHANNELS]; #endif #if FLASH_EMU // this is for entire module //unsigned char *GLOB_flash_memory = NULL; //&& flag for valid flash array malloc //extern uint16 FIQ_FLAG; // global flag for interrupt testing; sb just a temp extern byte *g_pMemPoolFree; unsigned char * GLOB_flash_memory[GLOB_LLD_BLOCKS*GLOB_LLD_PAGES]; #if CMD_DMA extern PENDING_CMD GLOB_PendingCMD[MAX_DESCRIPTORS + LLD_NUM_FLASH_CHANNELS]; #if TEST_FTL extern byte forced_error; extern byte forced_error_index; extern uint32 forced_error_pba; extern byte tag_for_forced_error_command ; extern struct uncorrectable_error_tag { int random_write_index; BLOCKNODE block_num; PAGENUMTYPE page_num; BLOCKNODE pba; }uncorrectable_error[32]; #endif #endif #if EMU_BAD_BLOCK static uint32 rand_seed = 0; BLOCKNODE bad_blocks_array[MAX_BAD_BLOCKS]; void emu_init_bad_block_info(void) { unsigned int i; for ( i = 0; i < MAX_BAD_BLOCKS; i++) { bad_blocks_array[i] = -1; } } void emu_append_bad_block_info(BLOCKNODE *array, unsigned int len) { unsigned int i,j; #if 0 rand_seed = 1226360438; printf("random seed %lu for finding random bad blocks\n", rand_seed); srand(rand_seed); #endif for(j=0; (jf_path.dentry) { inode = nef_filp->f_path.dentry->d_inode; } else { printk(KERN_ERR "Can not get valid inode!\n"); goto out; } nef_size = i_size_read(inode->i_mapping->host); if (nef_size <= 0) { printk(KERN_ERR "Invalid nand emu file size: 0x%llx\n", nef_size); goto out; } else { printk(KERN_ALERT "nand emu file size: %lld\n", nef_size); } file_offset = 0; for (i = 0; i < GLOB_LLD_BLOCKS * GLOB_LLD_PAGES; i++) { tmp_file_offset = file_offset; nread = vfs_read(nef_filp, (char __user *)GLOB_flash_memory[i], GLOB_LLD_PAGE_SIZE, &tmp_file_offset); if (nread < GLOB_LLD_PAGE_SIZE) { printk(KERN_ERR "%s, Line %d - nand emu file " "partial read: %d bytes\n", __FILE__, __LINE__, (int)nread); goto out; } file_offset += GLOB_LLD_PAGE_SIZE; } rc = 0; out: filp_close(nef_filp, current->files); set_fs(fs); #else FILE *fp; int i; uint32 blocks, pages, page_data_size, page_spare_size; fp = fopen(MEMFILENAME, "rb"); if (!fp) { print("MEMFILE could not load file %s\n", MEMFILENAME); rc = -1; } if (!rc) { fscanf(fp, "%d:%d:%d:%d:", &blocks, &pages, &page_data_size, &page_spare_size); if ((blocks == GLOB_LLD_BLOCKS) && (pages == GLOB_LLD_PAGES) && (page_data_size == GLOB_LLD_PAGE_DATA_SIZE) && (page_spare_size == GLOB_LLD_PAGE_SPARE_SIZE)) { for (i = 0; (i < GLOB_LLD_PAGE_SIZE * GLOB_LLD_BLOCKS * GLOB_LLD_PAGES) && (!feof(fp)); i++) { *(GLOB_flash_memory[0] + i) = fgetc(fp); } if (if_path.dentry) { inode = nef_filp->f_path.dentry->d_inode; } else { printk(KERN_ERR "Invalid nef_filp->f_path.dentry value!\n"); goto out; } nef_size = i_size_read(inode->i_mapping->host); if (nef_size <= 0) { printk(KERN_ERR "Invalid nand emu file size: 0x%llx\n", nef_size); goto out; } else { printk(KERN_ALERT "nand emu file size: %lld\n", nef_size); } file_offset = 0; for (i = 0; i < GLOB_LLD_BLOCKS * GLOB_LLD_PAGES; i++) { tmp_file_offset = file_offset; nwritten = vfs_write(nef_filp, (char __user *)GLOB_flash_memory[i], GLOB_LLD_PAGE_SIZE, &tmp_file_offset); if (nwritten < GLOB_LLD_PAGE_SIZE) { printk(KERN_ERR "%s, Line %d - nand emu file " "partial write: %d bytes\n", __FILE__, __LINE__, (int)nwritten); goto out; } file_offset += GLOB_LLD_PAGE_SIZE; } rc = 0; out: filp_close(nef_filp, current->files); set_fs(fs); #else FILE *fp; int i; fp = fopen(MEMFILENAME, "wb"); if (!fp) { print("MEMFILE could not open file %s\n", MEMFILENAME); rc = -1; } if (!rc) { fprintf(fp, "%d:%d:%d:%d:", GLOB_LLD_BLOCKS, GLOB_LLD_PAGES, GLOB_LLD_PAGE_DATA_SIZE, GLOB_LLD_PAGE_SPARE_SIZE); for (i = 0; (i < (GLOB_LLD_PAGE_SIZE * GLOB_LLD_BLOCKS * GLOB_LLD_PAGES)); i++) { fputc(*(GLOB_flash_memory[0] + i), fp); } fclose(fp); } #endif #endif return rc; } #endif /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: emu_Flash_Init * Inputs: none * Outputs: PASS=0 (notice 0=ok here) * Description: Creates & initializes the flash RAM array. * *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ uint16 emu_Flash_Init() { int i; #if FLASH_EMU_STATIC_MALLOC GLOB_flash_memory[0] = (unsigned char *)GLOB_MALLOC(GLOB_LLD_PAGE_SIZE * GLOB_LLD_BLOCKS*GLOB_LLD_PAGES * sizeof(unsigned char)); memset((char *)(GLOB_flash_memory[0]), 0xFF, GLOB_LLD_PAGE_SIZE * GLOB_LLD_BLOCKS*GLOB_LLD_PAGES * sizeof(unsigned char)); #else GLOB_flash_memory[0] = NULL; #endif for(i=1;i= GLOB_DeviceInfo.wTotalBlocks) { status = FAIL; } if ((PASS == status) && !GLOB_flash_memory) { status = FAIL; } print("ERASING BLOCK %d\n",block_add); #if EMU_BAD_BLOCK if ( FAIL == emu_bad_block_check(block_add)) { status = FAIL; } #endif if (PASS == status) { for(i=block_add*GLOB_LLD_PAGES; i<((block_add+1)*GLOB_LLD_PAGES); i++) if (GLOB_flash_memory[i]) { #if FLASH_EMU_STATIC_MALLOC memset((unsigned char *)(GLOB_flash_memory[i]), 0xFF, GLOB_DeviceInfo.wPageSize*sizeof(unsigned char)); #else #ifdef ELDORA free(GLOB_flash_memory[i]); #else GLOB_FREE(GLOB_flash_memory[i]); #endif GLOB_flash_memory[i] = NULL; #endif } } return status; } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: emu_Write_Page_Main * Inputs: Write buffer address pointer * Block number * Page number * Number of pages to process * Outputs: PASS=0 (notice 0=ok here) * Description: Write the data in the buffer to main area of flash * *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ uint16 emu_Write_Page_Main(byte * write_data, BLOCKNODE Block, PAGENUMTYPE Page, PAGENUMTYPE PageCount) { int i; int status = PASS; if(Block >= GLOB_DeviceInfo.wTotalBlocks) { status = FAIL; } if(Page+PageCount > GLOB_DeviceInfo.wPagesPerBlock) { status = FAIL; } if ((PASS == status) && !GLOB_flash_memory) { status = FAIL; } print("emu_Write_Page_Main: lba %u Page %u PageCount\ %u\n",(unsigned int)Block,(unsigned int)Page,(unsigned int)PageCount); #if EMU_BAD_BLOCK if ( FAIL == emu_bad_block_check(Block)) { status = FAIL; } #endif if (PASS == status) { for(i=0; i= GLOB_DeviceInfo.wTotalBlocks) { status = FAIL; } if(Page+PageCount > GLOB_DeviceInfo.wPagesPerBlock) { status = FAIL; } if ((PASS == status) && !GLOB_flash_memory) { status = FAIL; } print("emu_Read_Page_Main: lba %u Page %u PageCount\ %u\n",(unsigned int)Block,(unsigned int)Page,(unsigned int)PageCount); #if EMU_BAD_BLOCK if ( FAIL == emu_bad_block_check(Block)) { status = FAIL; } #endif if (PASS == status) { for(i=0; i= GLOB_DeviceInfo.wTotalBlocks) { printstr("Read Page Main+Spare:Error has occured: Block Address too big\n"); status = FAIL; } if(Page+PageCount > GLOB_DeviceInfo.wPagesPerBlock) { printstr("Read Page Main+Spare:Error has occured: Page Address too big\n"); status = FAIL; } if ((PASS == status) && !GLOB_flash_memory) { print("Read Page Main+Spare: No allocated for operations\n"); status = FAIL; } #if EMU_BAD_BLOCK if ( FAIL == emu_bad_block_check(Block)) { status = FAIL; } #endif if (PASS == status) { print("\n Read Page Main+Spare - No. of pages %u block %u start page %u\n",(unsigned int)PageCount,(unsigned int)Block,(unsigned int)Page); for(i=0; i= GLOB_DeviceInfo.wTotalBlocks) { printstr("Write Page Main+Spare:Error has occured: Block Address too big\n"); status = FAIL; } if(Page+page_count > GLOB_DeviceInfo.wPagesPerBlock) { printstr("Write Page Main+Spare:Error has occured: Page Address too big\n"); status = FAIL; } #if EMU_BAD_BLOCK if ( FAIL == emu_bad_block_check(Block)) { status = FAIL; } #endif if ((PASS == status) && !GLOB_flash_memory) { print("Write Page Main+Spare: No allocated for operations\n"); status = FAIL; } if (PASS == status) { print("\n Write Page Main+Spare - No. of pages %u block %u start page %u\n",(unsigned int)page_count,(unsigned int)Block,(unsigned int)Page); for(i=0; i= GLOB_DeviceInfo.wTotalBlocks) { printstr("Read Page Spare:Error has occured: Block Address too big\n"); status = FAIL; } if(Page+PageCount > GLOB_DeviceInfo.wPagesPerBlock) { printstr("Read Page Spare:Error has occured: Page Address too big\n"); status = FAIL; } #if EMU_BAD_BLOCK if ( FAIL == emu_bad_block_check(Block)) { status = FAIL; } #endif if ((PASS == status) && !GLOB_flash_memory) { print("Write Page Spare: No allocated for operations\n"); status = FAIL; } if (PASS == status) { print(" Write Page Spare- block %u page %u \n",(unsigned int)Block,(unsigned int)Page); if(GLOB_flash_memory[Block*GLOB_LLD_PAGES+ Page]== NULL) GLOB_flash_memory[Block*GLOB_LLD_PAGES+ Page] = (unsigned char *)GLOB_MALLOC(GLOB_DeviceInfo.wPageSize*sizeof(unsigned char)); if(GLOB_flash_memory[Block*GLOB_LLD_PAGES+ Page]== NULL) { print("RAN OUT OF MEMORY\n"); return FAIL; } memcpy((byte *)(GLOB_flash_memory[Block*GLOB_LLD_PAGES+ Page] + GLOB_DeviceInfo.wPageDataSize), write_data, (GLOB_DeviceInfo.wPageSize - GLOB_DeviceInfo.wPageDataSize)); } return status; } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: emu_Read_Page_Spare * Inputs: Write Buffer * Address * Buffer size * Outputs: PASS=0 (notice 0=ok here) * Description: Read data from the spare area * *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ uint16 emu_Read_Page_Spare(byte* write_data, BLOCKNODE Block,PAGENUMTYPE Page,PAGENUMTYPE PageCount) { int status = PASS; if(Block >= GLOB_DeviceInfo.wTotalBlocks) { printstr("Read Page Spare:Error has occured: Block Address too big\n"); status = FAIL; } if(Page+PageCount > GLOB_DeviceInfo.wPagesPerBlock) { printstr("Read Page Spare:Error has occured: Page Address too big\n"); status = FAIL; } #if EMU_BAD_BLOCK if ( FAIL == emu_bad_block_check(Block)) { status = FAIL; } #endif if ((PASS == status) && !GLOB_flash_memory) { print("Read Page Spare: No allocated for operations\n"); status = FAIL; } if (PASS == status) { print(" Read Page Spare- block %u page %u\n",(unsigned int)Block,(unsigned int)Page); if( (GLOB_flash_memory[Block*GLOB_LLD_PAGES + Page] == NULL)) { memset(write_data,0xFF,(GLOB_DeviceInfo.wPageSize-GLOB_DeviceInfo.wPageDataSize)); } else memcpy(write_data,(byte *)(GLOB_flash_memory[Block*GLOB_LLD_PAGES+ Page] + GLOB_DeviceInfo.wPageDataSize), (GLOB_DeviceInfo.wPageSize-GLOB_DeviceInfo.wPageDataSize)); } return status; } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: emu_Enable_Disable_Interrupts * Inputs: enable or disable * Outputs: none * Description: NOP *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ void emu_Enable_Disable_Interrupts(uint16 INT_ENABLE) { } #if CMD_DMA /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Support for CDMA functions ************************************ * emu_CDMA_Flash_Init * CDMA_process_data command (use LLD_CDMA) * CDMA_MemCopy_CMD (use LLD_CDMA) * emu_CDMA_execute all commands * emu_CDMA_Event_Status *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ #if DEBUG_SYNC extern uint32 debug_sync_cnt; #endif /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: CDMA_Flash_Init * Inputs: none * Outputs: PASS=0 (notice 0=ok here) * Description: This should be called at power up. * It disables interrupts and clears status bits * issues flash reset command * configures the controller registers * It sets the interrupt mask and enables interrupts * It pre-builds special descriptors *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ uint16 emu_CDMA_Flash_Init (void) { uint16 i; for (i=0; i