| /* |
| * NAND Flash Controller Device Driver |
| * Copyright (c) 2009, 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 <linux/fs.h> |
| #include <linux/slab.h> |
| #include "flash.h" |
| #include "ffsdefs.h" |
| #include "lld_emu.h" |
| #include "lld.h" |
| #if CMD_DMA |
| #include "lld_cdma.h" |
| #if FLASH_EMU |
| u32 totalUsedBanks; |
| u32 valid_banks[MAX_CHANS]; |
| #endif |
| #endif |
| |
| #define GLOB_LLD_PAGES 64 |
| #define GLOB_LLD_PAGE_SIZE (512+16) |
| #define GLOB_LLD_PAGE_DATA_SIZE 512 |
| #define GLOB_LLD_BLOCKS 2048 |
| |
| #if FLASH_EMU /* This is for entire module */ |
| |
| static u8 *flash_memory[GLOB_LLD_BLOCKS * GLOB_LLD_PAGES]; |
| |
| /* Read nand emu file and then fill it's content to flash_memory */ |
| int emu_load_file_to_mem(void) |
| { |
| mm_segment_t fs; |
| struct file *nef_filp = NULL; |
| struct inode *inode = NULL; |
| loff_t nef_size = 0; |
| loff_t tmp_file_offset, file_offset; |
| ssize_t nread; |
| int i, rc = -EINVAL; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| fs = get_fs(); |
| set_fs(get_ds()); |
| |
| nef_filp = filp_open("/root/nand_emu_file", O_RDWR | O_LARGEFILE, 0); |
| if (IS_ERR(nef_filp)) { |
| printk(KERN_ERR "filp_open error: " |
| "Unable to open nand emu file!\n"); |
| return PTR_ERR(nef_filp); |
| } |
| |
| if (nef_filp->f_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 { |
| nand_dbg_print(NAND_DBG_DEBUG, "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 *)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); |
| return rc; |
| } |
| |
| /* Write contents of flash_memory to nand emu file */ |
| int emu_write_mem_to_file(void) |
| { |
| mm_segment_t fs; |
| struct file *nef_filp = NULL; |
| struct inode *inode = NULL; |
| loff_t nef_size = 0; |
| loff_t tmp_file_offset, file_offset; |
| ssize_t nwritten; |
| int i, rc = -EINVAL; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| fs = get_fs(); |
| set_fs(get_ds()); |
| |
| nef_filp = filp_open("/root/nand_emu_file", O_RDWR | O_LARGEFILE, 0); |
| if (IS_ERR(nef_filp)) { |
| printk(KERN_ERR "filp_open error: " |
| "Unable to open nand emu file!\n"); |
| return PTR_ERR(nef_filp); |
| } |
| |
| if (nef_filp->f_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 { |
| nand_dbg_print(NAND_DBG_DEBUG, "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 *)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); |
| return rc; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Flash_Init |
| * Inputs: none |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Creates & initializes the flash RAM array. |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Flash_Init(void) |
| { |
| int i; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| flash_memory[0] = vmalloc(GLOB_LLD_PAGE_SIZE * GLOB_LLD_BLOCKS * |
| GLOB_LLD_PAGES * sizeof(u8)); |
| if (!flash_memory[0]) { |
| printk(KERN_ERR "Fail to allocate memory " |
| "for nand emulator!\n"); |
| return ERR; |
| } |
| |
| memset((char *)(flash_memory[0]), 0xFF, |
| GLOB_LLD_PAGE_SIZE * GLOB_LLD_BLOCKS * GLOB_LLD_PAGES * |
| sizeof(u8)); |
| |
| for (i = 1; i < GLOB_LLD_BLOCKS * GLOB_LLD_PAGES; i++) |
| flash_memory[i] = flash_memory[i - 1] + GLOB_LLD_PAGE_SIZE; |
| |
| emu_load_file_to_mem(); /* Load nand emu file to mem */ |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Flash_Release |
| * Inputs: none |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Releases the flash. |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| int emu_Flash_Release(void) |
| { |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| emu_write_mem_to_file(); /* Write back mem to nand emu file */ |
| |
| vfree(flash_memory[0]); |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Read_Device_ID |
| * Inputs: none |
| * Outputs: PASS=1 FAIL=0 |
| * Description: Reads the info from the controller registers. |
| * Sets up DeviceInfo structure with device parameters |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| |
| u16 emu_Read_Device_ID(void) |
| { |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| DeviceInfo.wDeviceMaker = 0; |
| DeviceInfo.wDeviceType = 8; |
| DeviceInfo.wSpectraStartBlock = 36; |
| DeviceInfo.wSpectraEndBlock = GLOB_LLD_BLOCKS - 1; |
| DeviceInfo.wTotalBlocks = GLOB_LLD_BLOCKS; |
| DeviceInfo.wPagesPerBlock = GLOB_LLD_PAGES; |
| DeviceInfo.wPageSize = GLOB_LLD_PAGE_SIZE; |
| DeviceInfo.wPageDataSize = GLOB_LLD_PAGE_DATA_SIZE; |
| DeviceInfo.wPageSpareSize = GLOB_LLD_PAGE_SIZE - |
| GLOB_LLD_PAGE_DATA_SIZE; |
| DeviceInfo.wBlockSize = DeviceInfo.wPageSize * GLOB_LLD_PAGES; |
| DeviceInfo.wBlockDataSize = DeviceInfo.wPageDataSize * GLOB_LLD_PAGES; |
| DeviceInfo.wDataBlockNum = (u32) (DeviceInfo.wSpectraEndBlock - |
| DeviceInfo.wSpectraStartBlock |
| + 1); |
| DeviceInfo.MLCDevice = 1; /* Emulate MLC device */ |
| DeviceInfo.nBitsInPageNumber = |
| (u8)GLOB_Calc_Used_Bits(DeviceInfo.wPagesPerBlock); |
| DeviceInfo.nBitsInPageDataSize = |
| (u8)GLOB_Calc_Used_Bits(DeviceInfo.wPageDataSize); |
| DeviceInfo.nBitsInBlockDataSize = |
| (u8)GLOB_Calc_Used_Bits(DeviceInfo.wBlockDataSize); |
| |
| #if CMD_DMA |
| totalUsedBanks = 4; |
| valid_banks[0] = 1; |
| valid_banks[1] = 1; |
| valid_banks[2] = 1; |
| valid_banks[3] = 1; |
| #endif |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Flash_Reset |
| * Inputs: none |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Reset the flash |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Flash_Reset(void) |
| { |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Erase_Block |
| * Inputs: Address |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Erase a block |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Erase_Block(u32 block_add) |
| { |
| int i; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| if (block_add >= DeviceInfo.wTotalBlocks) { |
| printk(KERN_ERR "emu_Erase_Block error! " |
| "Too big block address: %d\n", block_add); |
| return FAIL; |
| } |
| |
| nand_dbg_print(NAND_DBG_DEBUG, "Erasing block %d\n", |
| (int)block_add); |
| |
| for (i = block_add * GLOB_LLD_PAGES; |
| i < ((block_add + 1) * GLOB_LLD_PAGES); i++) { |
| if (flash_memory[i]) { |
| memset((u8 *)(flash_memory[i]), 0xFF, |
| DeviceInfo.wPageSize * sizeof(u8)); |
| } |
| } |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * 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 |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Write_Page_Main(u8 *write_data, u32 Block, |
| u16 Page, u16 PageCount) |
| { |
| int i; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| if (Block >= DeviceInfo.wTotalBlocks) |
| return FAIL; |
| |
| if (Page + PageCount > DeviceInfo.wPagesPerBlock) |
| return FAIL; |
| |
| nand_dbg_print(NAND_DBG_DEBUG, "emu_Write_Page_Main: " |
| "lba %u Page %u PageCount %u\n", |
| (unsigned int)Block, |
| (unsigned int)Page, (unsigned int)PageCount); |
| |
| for (i = 0; i < PageCount; i++) { |
| if (NULL == flash_memory[Block * GLOB_LLD_PAGES + Page]) { |
| printk(KERN_ERR "Run out of memory\n"); |
| return FAIL; |
| } |
| memcpy((u8 *) (flash_memory[Block * GLOB_LLD_PAGES + Page]), |
| write_data, DeviceInfo.wPageDataSize); |
| write_data += DeviceInfo.wPageDataSize; |
| Page++; |
| } |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Read_Page_Main |
| * Inputs: Read buffer address pointer |
| * Block number |
| * Page number |
| * Number of pages to process |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Read the data from the flash main area to the buffer |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Read_Page_Main(u8 *read_data, u32 Block, |
| u16 Page, u16 PageCount) |
| { |
| int i; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| if (Block >= DeviceInfo.wTotalBlocks) |
| return FAIL; |
| |
| if (Page + PageCount > DeviceInfo.wPagesPerBlock) |
| return FAIL; |
| |
| nand_dbg_print(NAND_DBG_DEBUG, "emu_Read_Page_Main: " |
| "lba %u Page %u PageCount %u\n", |
| (unsigned int)Block, |
| (unsigned int)Page, (unsigned int)PageCount); |
| |
| for (i = 0; i < PageCount; i++) { |
| if (NULL == flash_memory[Block * GLOB_LLD_PAGES + Page]) { |
| memset(read_data, 0xFF, DeviceInfo.wPageDataSize); |
| } else { |
| memcpy(read_data, |
| (u8 *) (flash_memory[Block * GLOB_LLD_PAGES |
| + Page]), |
| DeviceInfo.wPageDataSize); |
| } |
| read_data += DeviceInfo.wPageDataSize; |
| Page++; |
| } |
| |
| return PASS; |
| } |
| |
| #ifndef ELDORA |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Read_Page_Main_Spare |
| * Inputs: Write Buffer |
| * Address |
| * Buffer size |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Read from flash main+spare area |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Read_Page_Main_Spare(u8 *read_data, u32 Block, |
| u16 Page, u16 PageCount) |
| { |
| int i; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| if (Block >= DeviceInfo.wTotalBlocks) { |
| printk(KERN_ERR "Read Page Main+Spare " |
| "Error: Block Address too big\n"); |
| return FAIL; |
| } |
| |
| if (Page + PageCount > DeviceInfo.wPagesPerBlock) { |
| printk(KERN_ERR "Read Page Main+Spare " |
| "Error: Page number too big\n"); |
| return FAIL; |
| } |
| |
| nand_dbg_print(NAND_DBG_DEBUG, "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 < PageCount; i++) { |
| if (NULL == flash_memory[Block * GLOB_LLD_PAGES + Page]) { |
| memset(read_data, 0xFF, DeviceInfo.wPageSize); |
| } else { |
| memcpy(read_data, (u8 *) (flash_memory[Block * |
| GLOB_LLD_PAGES |
| + Page]), |
| DeviceInfo.wPageSize); |
| } |
| |
| read_data += DeviceInfo.wPageSize; |
| Page++; |
| } |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Write_Page_Main_Spare |
| * Inputs: Write buffer |
| * address |
| * buffer length |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Write the buffer to main+spare area of flash |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Write_Page_Main_Spare(u8 *write_data, u32 Block, |
| u16 Page, u16 page_count) |
| { |
| u16 i; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| if (Block >= DeviceInfo.wTotalBlocks) { |
| printk(KERN_ERR "Write Page Main + Spare " |
| "Error: Block Address too big\n"); |
| return FAIL; |
| } |
| |
| if (Page + page_count > DeviceInfo.wPagesPerBlock) { |
| printk(KERN_ERR "Write Page Main + Spare " |
| "Error: Page number too big\n"); |
| return FAIL; |
| } |
| |
| nand_dbg_print(NAND_DBG_DEBUG, "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 < page_count; i++) { |
| if (NULL == flash_memory[Block * GLOB_LLD_PAGES + Page]) { |
| printk(KERN_ERR "Run out of memory!\n"); |
| return FAIL; |
| } |
| memcpy((u8 *) (flash_memory[Block * GLOB_LLD_PAGES + Page]), |
| write_data, DeviceInfo.wPageSize); |
| write_data += DeviceInfo.wPageSize; |
| Page++; |
| } |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Write_Page_Spare |
| * Inputs: Write buffer |
| * Address |
| * buffer size |
| * Outputs: PASS=0 (notice 0=ok here) |
| * Description: Write the buffer in the spare area |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Write_Page_Spare(u8 *write_data, u32 Block, |
| u16 Page, u16 PageCount) |
| { |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| if (Block >= DeviceInfo.wTotalBlocks) { |
| printk(KERN_ERR "Read Page Spare Error: " |
| "Block Address too big\n"); |
| return FAIL; |
| } |
| |
| if (Page + PageCount > DeviceInfo.wPagesPerBlock) { |
| printk(KERN_ERR "Read Page Spare Error: " |
| "Page number too big\n"); |
| return FAIL; |
| } |
| |
| nand_dbg_print(NAND_DBG_DEBUG, "Write Page Spare- " |
| "block %u page %u\n", |
| (unsigned int)Block, (unsigned int)Page); |
| |
| if (NULL == flash_memory[Block * GLOB_LLD_PAGES + Page]) { |
| printk(KERN_ERR "Run out of memory!\n"); |
| return FAIL; |
| } |
| |
| memcpy((u8 *) (flash_memory[Block * GLOB_LLD_PAGES + Page] + |
| DeviceInfo.wPageDataSize), write_data, |
| (DeviceInfo.wPageSize - DeviceInfo.wPageDataSize)); |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * 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 |
| * |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_Read_Page_Spare(u8 *write_data, u32 Block, |
| u16 Page, u16 PageCount) |
| { |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| if (Block >= DeviceInfo.wTotalBlocks) { |
| printk(KERN_ERR "Read Page Spare " |
| "Error: Block Address too big\n"); |
| return FAIL; |
| } |
| |
| if (Page + PageCount > DeviceInfo.wPagesPerBlock) { |
| printk(KERN_ERR "Read Page Spare " |
| "Error: Page number too big\n"); |
| return FAIL; |
| } |
| |
| nand_dbg_print(NAND_DBG_DEBUG, "Read Page Spare- " |
| "block %u page %u\n", |
| (unsigned int)Block, (unsigned int)Page); |
| |
| if (NULL == flash_memory[Block * GLOB_LLD_PAGES + Page]) { |
| memset(write_data, 0xFF, |
| (DeviceInfo.wPageSize - DeviceInfo.wPageDataSize)); |
| } else { |
| memcpy(write_data, |
| (u8 *) (flash_memory[Block * GLOB_LLD_PAGES + Page] |
| + DeviceInfo.wPageDataSize), |
| (DeviceInfo.wPageSize - DeviceInfo.wPageDataSize)); |
| } |
| |
| return PASS; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Enable_Disable_Interrupts |
| * Inputs: enable or disable |
| * Outputs: none |
| * Description: NOP |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| void emu_Enable_Disable_Interrupts(u16 INT_ENABLE) |
| { |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| } |
| |
| u16 emu_Get_Bad_Block(u32 block) |
| { |
| return 0; |
| } |
| |
| #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 |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_CDMA_Flash_Init(void) |
| { |
| u16 i; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| for (i = 0; i < MAX_DESCS + MAX_CHANS; i++) { |
| PendingCMD[i].CMD = 0; |
| PendingCMD[i].Tag = 0; |
| PendingCMD[i].DataAddr = 0; |
| PendingCMD[i].Block = 0; |
| PendingCMD[i].Page = 0; |
| PendingCMD[i].PageCount = 0; |
| PendingCMD[i].DataDestAddr = 0; |
| PendingCMD[i].DataSrcAddr = 0; |
| PendingCMD[i].MemCopyByteCnt = 0; |
| PendingCMD[i].ChanSync[0] = 0; |
| PendingCMD[i].ChanSync[1] = 0; |
| PendingCMD[i].ChanSync[2] = 0; |
| PendingCMD[i].ChanSync[3] = 0; |
| PendingCMD[i].ChanSync[4] = 0; |
| PendingCMD[i].Status = 3; |
| } |
| |
| return PASS; |
| } |
| |
| static void emu_isr(int irq, void *dev_id) |
| { |
| /* TODO: ... */ |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: CDMA_Execute_CMDs |
| * Inputs: tag_count: the number of pending cmds to do |
| * Outputs: PASS/FAIL |
| * Description: execute each command in the pending CMD array |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_CDMA_Execute_CMDs(u16 tag_count) |
| { |
| u16 i, j; |
| u8 CMD; /* cmd parameter */ |
| u8 *data; |
| u32 block; |
| u16 page; |
| u16 count; |
| u16 status = PASS; |
| |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| nand_dbg_print(NAND_DBG_TRACE, "At start of Execute CMDs: " |
| "Tag Count %u\n", tag_count); |
| |
| for (i = 0; i < totalUsedBanks; i++) { |
| PendingCMD[i].CMD = DUMMY_CMD; |
| PendingCMD[i].Tag = 0xFF; |
| PendingCMD[i].Block = |
| (DeviceInfo.wTotalBlocks / totalUsedBanks) * i; |
| |
| for (j = 0; j <= MAX_CHANS; j++) |
| PendingCMD[i].ChanSync[j] = 0; |
| } |
| |
| CDMA_Execute_CMDs(tag_count); |
| |
| print_pending_cmds(tag_count); |
| |
| #if DEBUG_SYNC |
| } |
| debug_sync_cnt++; |
| #endif |
| |
| for (i = MAX_CHANS; |
| i < tag_count + MAX_CHANS; i++) { |
| CMD = PendingCMD[i].CMD; |
| data = PendingCMD[i].DataAddr; |
| block = PendingCMD[i].Block; |
| page = PendingCMD[i].Page; |
| count = PendingCMD[i].PageCount; |
| |
| switch (CMD) { |
| case ERASE_CMD: |
| emu_Erase_Block(block); |
| PendingCMD[i].Status = PASS; |
| break; |
| case WRITE_MAIN_CMD: |
| emu_Write_Page_Main(data, block, page, count); |
| PendingCMD[i].Status = PASS; |
| break; |
| case WRITE_MAIN_SPARE_CMD: |
| emu_Write_Page_Main_Spare(data, block, page, count); |
| PendingCMD[i].Status = PASS; |
| break; |
| case READ_MAIN_CMD: |
| emu_Read_Page_Main(data, block, page, count); |
| PendingCMD[i].Status = PASS; |
| break; |
| case MEMCOPY_CMD: |
| memcpy(PendingCMD[i].DataDestAddr, |
| PendingCMD[i].DataSrcAddr, |
| PendingCMD[i].MemCopyByteCnt); |
| case DUMMY_CMD: |
| PendingCMD[i].Status = PASS; |
| break; |
| default: |
| PendingCMD[i].Status = FAIL; |
| break; |
| } |
| } |
| |
| /* |
| * Temperory adding code to reset PendingCMD array for basic testing. |
| * It should be done at the end of event status function. |
| */ |
| for (i = tag_count + MAX_CHANS; i < MAX_DESCS; i++) { |
| PendingCMD[i].CMD = 0; |
| PendingCMD[i].Tag = 0; |
| PendingCMD[i].DataAddr = 0; |
| PendingCMD[i].Block = 0; |
| PendingCMD[i].Page = 0; |
| PendingCMD[i].PageCount = 0; |
| PendingCMD[i].DataDestAddr = 0; |
| PendingCMD[i].DataSrcAddr = 0; |
| PendingCMD[i].MemCopyByteCnt = 0; |
| PendingCMD[i].ChanSync[0] = 0; |
| PendingCMD[i].ChanSync[1] = 0; |
| PendingCMD[i].ChanSync[2] = 0; |
| PendingCMD[i].ChanSync[3] = 0; |
| PendingCMD[i].ChanSync[4] = 0; |
| PendingCMD[i].Status = CMD_NOT_DONE; |
| } |
| |
| nand_dbg_print(NAND_DBG_TRACE, "At end of Execute CMDs.\n"); |
| |
| emu_isr(0, 0); /* This is a null isr now. Need fill it in future */ |
| |
| return status; |
| } |
| |
| /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
| * Function: emu_Event_Status |
| * Inputs: none |
| * Outputs: Event_Status code |
| * Description: This function can also be used to force errors |
| *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ |
| u16 emu_CDMA_Event_Status(void) |
| { |
| nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", |
| __FILE__, __LINE__, __func__); |
| |
| return EVENT_PASS; |
| } |
| |
| #endif /* CMD_DMA */ |
| #endif /* !ELDORA */ |
| #endif /* FLASH_EMU */ |