/* * 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 "lld.h" #include "lld_nand.h" #include "lld_cdma.h" #include "spectraswconfig.h" #include "flash.h" #include "ffsdefs.h" #include #include #include #include #include "nand_regs.h" #define SPECTRA_NAND_NAME "nd" #define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y))) #define MAX_PAGES_PER_RW 128 #define INT_IDLE_STATE 0 #define INT_READ_PAGE_MAIN 0x01 #define INT_WRITE_PAGE_MAIN 0x02 #define INT_PIPELINE_READ_AHEAD 0x04 #define INT_PIPELINE_WRITE_AHEAD 0x08 #define INT_MULTI_PLANE_READ 0x10 #define INT_MULTI_PLANE_WRITE 0x11 static u32 enable_ecc; struct mrst_nand_info info; int totalUsedBanks; u32 GLOB_valid_banks[LLD_MAX_FLASH_BANKS]; void __iomem *FlashReg; void __iomem *FlashMem; u16 conf_parameters[] = { 0x0000, 0x0000, 0x01F4, 0x01F4, 0x01F4, 0x01F4, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0040, 0x0001, 0x000A, 0x000A, 0x000A, 0x0000, 0x0000, 0x0005, 0x0012, 0x000C }; u16 NAND_Get_Bad_Block(u32 block) { u32 status = PASS; u32 flag_bytes = 0; u32 skip_bytes = DeviceInfo.wSpareSkipBytes; u32 page, i; u8 *pReadSpareBuf = buf_get_bad_block; if (enable_ecc) flag_bytes = DeviceInfo.wNumPageSpareFlag; for (page = 0; page < 2; page++) { status = NAND_Read_Page_Spare(pReadSpareBuf, block, page, 1); if (status != PASS) return READ_ERROR; for (i = flag_bytes; i < (flag_bytes + skip_bytes); i++) if (pReadSpareBuf[i] != 0xff) return DEFECTIVE_BLOCK; } for (page = 1; page < 3; page++) { status = NAND_Read_Page_Spare(pReadSpareBuf, block, DeviceInfo.wPagesPerBlock - page , 1); if (status != PASS) return READ_ERROR; for (i = flag_bytes; i < (flag_bytes + skip_bytes); i++) if (pReadSpareBuf[i] != 0xff) return DEFECTIVE_BLOCK; } return GOOD_BLOCK; } u16 NAND_Flash_Reset(void) { u32 i; u32 intr_status_rst_comp[4] = {INTR_STATUS0__RST_COMP, INTR_STATUS1__RST_COMP, INTR_STATUS2__RST_COMP, INTR_STATUS3__RST_COMP}; u32 intr_status_time_out[4] = {INTR_STATUS0__TIME_OUT, INTR_STATUS1__TIME_OUT, INTR_STATUS2__TIME_OUT, INTR_STATUS3__TIME_OUT}; u32 intr_status[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u32 device_reset_banks[4] = {DEVICE_RESET__BANK0, DEVICE_RESET__BANK1, DEVICE_RESET__BANK2, DEVICE_RESET__BANK3}; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) iowrite32(intr_status_rst_comp[i] | intr_status_time_out[i], FlashReg + intr_status[i]); for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { iowrite32(device_reset_banks[i], FlashReg + DEVICE_RESET); while (!(ioread32(FlashReg + intr_status[i]) & (intr_status_rst_comp[i] | intr_status_time_out[i]))) ; if (ioread32(FlashReg + intr_status[i]) & intr_status_time_out[i]) nand_dbg_print(NAND_DBG_WARN, "NAND Reset operation timed out on bank %d\n", i); } for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) iowrite32(intr_status_rst_comp[i] | intr_status_time_out[i], FlashReg + intr_status[i]); return PASS; } static void NAND_ONFi_Timing_Mode(u16 mode) { u16 Trea[6] = {40, 30, 25, 20, 20, 16}; u16 Trp[6] = {50, 25, 17, 15, 12, 10}; u16 Treh[6] = {30, 15, 15, 10, 10, 7}; u16 Trc[6] = {100, 50, 35, 30, 25, 20}; u16 Trhoh[6] = {0, 15, 15, 15, 15, 15}; u16 Trloh[6] = {0, 0, 0, 0, 5, 5}; u16 Tcea[6] = {100, 45, 30, 25, 25, 25}; u16 Tadl[6] = {200, 100, 100, 100, 70, 70}; u16 Trhw[6] = {200, 100, 100, 100, 100, 100}; u16 Trhz[6] = {200, 100, 100, 100, 100, 100}; u16 Twhr[6] = {120, 80, 80, 60, 60, 60}; u16 Tcs[6] = {70, 35, 25, 25, 20, 15}; u16 TclsRising = 1; u16 data_invalid_rhoh, data_invalid_rloh, data_invalid; u16 dv_window = 0; u16 en_lo, en_hi; u16 acc_clks; u16 addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); en_lo = CEIL_DIV(Trp[mode], CLK_X); en_hi = CEIL_DIV(Treh[mode], CLK_X); #if ONFI_BLOOM_TIME if ((en_hi * CLK_X) < (Treh[mode] + 2)) en_hi++; #endif if ((en_lo + en_hi) * CLK_X < Trc[mode]) en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X); if ((en_lo + en_hi) < CLK_MULTI) en_lo += CLK_MULTI - en_lo - en_hi; while (dv_window < 8) { data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode]; data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode]; data_invalid = data_invalid_rhoh < data_invalid_rloh ? data_invalid_rhoh : data_invalid_rloh; dv_window = data_invalid - Trea[mode]; if (dv_window < 8) en_lo++; } acc_clks = CEIL_DIV(Trea[mode], CLK_X); while (((acc_clks * CLK_X) - Trea[mode]) < 3) acc_clks++; if ((data_invalid - acc_clks * CLK_X) < 2) nand_dbg_print(NAND_DBG_WARN, "%s, Line %d: Warning!\n", __FILE__, __LINE__); addr_2_data = CEIL_DIV(Tadl[mode], CLK_X); re_2_we = CEIL_DIV(Trhw[mode], CLK_X); re_2_re = CEIL_DIV(Trhz[mode], CLK_X); we_2_re = CEIL_DIV(Twhr[mode], CLK_X); cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X); if (!TclsRising) cs_cnt = CEIL_DIV(Tcs[mode], CLK_X); if (cs_cnt == 0) cs_cnt = 1; if (Tcea[mode]) { while (((cs_cnt * CLK_X) + Trea[mode]) < Tcea[mode]) cs_cnt++; } #if MODE5_WORKAROUND if (mode == 5) acc_clks = 5; #endif /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */ if ((ioread32(FlashReg + MANUFACTURER_ID) == 0) && (ioread32(FlashReg + DEVICE_ID) == 0x88)) acc_clks = 6; iowrite32(acc_clks, FlashReg + ACC_CLKS); iowrite32(re_2_we, FlashReg + RE_2_WE); iowrite32(re_2_re, FlashReg + RE_2_RE); iowrite32(we_2_re, FlashReg + WE_2_RE); iowrite32(addr_2_data, FlashReg + ADDR_2_DATA); iowrite32(en_lo, FlashReg + RDWR_EN_LO_CNT); iowrite32(en_hi, FlashReg + RDWR_EN_HI_CNT); iowrite32(cs_cnt, FlashReg + CS_SETUP_CNT); } static void index_addr(u32 address, u32 data) { iowrite32(address, FlashMem); iowrite32(data, FlashMem + 0x10); } static void index_addr_read_data(u32 address, u32 *pdata) { iowrite32(address, FlashMem); *pdata = ioread32(FlashMem + 0x10); } static void set_ecc_config(void) { #if SUPPORT_8BITECC if ((ioread32(FlashReg + DEVICE_MAIN_AREA_SIZE) < 4096) || (ioread32(FlashReg + DEVICE_SPARE_AREA_SIZE) <= 128)) iowrite32(8, FlashReg + ECC_CORRECTION); #endif if ((ioread32(FlashReg + ECC_CORRECTION) & ECC_CORRECTION__VALUE) == 1) { DeviceInfo.wECCBytesPerSector = 4; DeviceInfo.wECCBytesPerSector *= DeviceInfo.wDevicesConnected; DeviceInfo.wNumPageSpareFlag = DeviceInfo.wPageSpareSize - DeviceInfo.wPageDataSize / (ECC_SECTOR_SIZE * DeviceInfo.wDevicesConnected) * DeviceInfo.wECCBytesPerSector - DeviceInfo.wSpareSkipBytes; } else { DeviceInfo.wECCBytesPerSector = (ioread32(FlashReg + ECC_CORRECTION) & ECC_CORRECTION__VALUE) * 13 / 8; if ((DeviceInfo.wECCBytesPerSector) % 2 == 0) DeviceInfo.wECCBytesPerSector += 2; else DeviceInfo.wECCBytesPerSector += 1; DeviceInfo.wECCBytesPerSector *= DeviceInfo.wDevicesConnected; DeviceInfo.wNumPageSpareFlag = DeviceInfo.wPageSpareSize - DeviceInfo.wPageDataSize / (ECC_SECTOR_SIZE * DeviceInfo.wDevicesConnected) * DeviceInfo.wECCBytesPerSector - DeviceInfo.wSpareSkipBytes; } } static u16 get_onfi_nand_para(void) { int i; u16 blks_lun_l, blks_lun_h, n_of_luns; u32 blockperlun, id; iowrite32(DEVICE_RESET__BANK0, FlashReg + DEVICE_RESET); while (!((ioread32(FlashReg + INTR_STATUS0) & INTR_STATUS0__RST_COMP) | (ioread32(FlashReg + INTR_STATUS0) & INTR_STATUS0__TIME_OUT))) ; if (ioread32(FlashReg + INTR_STATUS0) & INTR_STATUS0__RST_COMP) { iowrite32(DEVICE_RESET__BANK1, FlashReg + DEVICE_RESET); while (!((ioread32(FlashReg + INTR_STATUS1) & INTR_STATUS1__RST_COMP) | (ioread32(FlashReg + INTR_STATUS1) & INTR_STATUS1__TIME_OUT))) ; if (ioread32(FlashReg + INTR_STATUS1) & INTR_STATUS1__RST_COMP) { iowrite32(DEVICE_RESET__BANK2, FlashReg + DEVICE_RESET); while (!((ioread32(FlashReg + INTR_STATUS2) & INTR_STATUS2__RST_COMP) | (ioread32(FlashReg + INTR_STATUS2) & INTR_STATUS2__TIME_OUT))) ; if (ioread32(FlashReg + INTR_STATUS2) & INTR_STATUS2__RST_COMP) { iowrite32(DEVICE_RESET__BANK3, FlashReg + DEVICE_RESET); while (!((ioread32(FlashReg + INTR_STATUS3) & INTR_STATUS3__RST_COMP) | (ioread32(FlashReg + INTR_STATUS3) & INTR_STATUS3__TIME_OUT))) ; } else { printk(KERN_ERR "Getting a time out for bank 2!\n"); } } else { printk(KERN_ERR "Getting a time out for bank 1!\n"); } } iowrite32(INTR_STATUS0__TIME_OUT, FlashReg + INTR_STATUS0); iowrite32(INTR_STATUS1__TIME_OUT, FlashReg + INTR_STATUS1); iowrite32(INTR_STATUS2__TIME_OUT, FlashReg + INTR_STATUS2); iowrite32(INTR_STATUS3__TIME_OUT, FlashReg + INTR_STATUS3); DeviceInfo.wONFIDevFeatures = ioread32(FlashReg + ONFI_DEVICE_FEATURES); DeviceInfo.wONFIOptCommands = ioread32(FlashReg + ONFI_OPTIONAL_COMMANDS); DeviceInfo.wONFITimingMode = ioread32(FlashReg + ONFI_TIMING_MODE); DeviceInfo.wONFIPgmCacheTimingMode = ioread32(FlashReg + ONFI_PGM_CACHE_TIMING_MODE); n_of_luns = ioread32(FlashReg + ONFI_DEVICE_NO_OF_LUNS) & ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS; blks_lun_l = ioread32(FlashReg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L); blks_lun_h = ioread32(FlashReg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U); blockperlun = (blks_lun_h << 16) | blks_lun_l; DeviceInfo.wTotalBlocks = n_of_luns * blockperlun; if (!(ioread32(FlashReg + ONFI_TIMING_MODE) & ONFI_TIMING_MODE__VALUE)) return FAIL; for (i = 5; i > 0; i--) { if (ioread32(FlashReg + ONFI_TIMING_MODE) & (0x01 << i)) break; } NAND_ONFi_Timing_Mode(i); index_addr(MODE_11 | 0, 0x90); index_addr(MODE_11 | 1, 0); for (i = 0; i < 3; i++) index_addr_read_data(MODE_11 | 2, &id); nand_dbg_print(NAND_DBG_DEBUG, "3rd ID: 0x%x\n", id); DeviceInfo.MLCDevice = id & 0x0C; /* By now, all the ONFI devices we know support the page cache */ /* rw feature. So here we enable the pipeline_rw_ahead feature */ /* iowrite32(1, FlashReg + CACHE_WRITE_ENABLE); */ /* iowrite32(1, FlashReg + CACHE_READ_ENABLE); */ return PASS; } static void get_samsung_nand_para(void) { u8 no_of_planes; u32 blk_size; u64 plane_size, capacity; u32 id_bytes[5]; int i; index_addr((u32)(MODE_11 | 0), 0x90); index_addr((u32)(MODE_11 | 1), 0); for (i = 0; i < 5; i++) index_addr_read_data((u32)(MODE_11 | 2), &id_bytes[i]); nand_dbg_print(NAND_DBG_DEBUG, "ID bytes: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", id_bytes[0], id_bytes[1], id_bytes[2], id_bytes[3], id_bytes[4]); if ((id_bytes[1] & 0xff) == 0xd3) { /* Samsung K9WAG08U1A */ /* Set timing register values according to datasheet */ iowrite32(5, FlashReg + ACC_CLKS); iowrite32(20, FlashReg + RE_2_WE); iowrite32(12, FlashReg + WE_2_RE); iowrite32(14, FlashReg + ADDR_2_DATA); iowrite32(3, FlashReg + RDWR_EN_LO_CNT); iowrite32(2, FlashReg + RDWR_EN_HI_CNT); iowrite32(2, FlashReg + CS_SETUP_CNT); } no_of_planes = 1 << ((id_bytes[4] & 0x0c) >> 2); plane_size = (u64)64 << ((id_bytes[4] & 0x70) >> 4); blk_size = 64 << ((ioread32(FlashReg + DEVICE_PARAM_1) & 0x30) >> 4); capacity = (u64)128 * plane_size * no_of_planes; DeviceInfo.wTotalBlocks = (u32)GLOB_u64_Div(capacity, blk_size); } static void get_toshiba_nand_para(void) { void __iomem *scratch_reg; u32 tmp; /* Workaround to fix a controller bug which reports a wrong */ /* spare area size for some kind of Toshiba NAND device */ if ((ioread32(FlashReg + DEVICE_MAIN_AREA_SIZE) == 4096) && (ioread32(FlashReg + DEVICE_SPARE_AREA_SIZE) == 64)) { iowrite32(216, FlashReg + DEVICE_SPARE_AREA_SIZE); tmp = ioread32(FlashReg + DEVICES_CONNECTED) * ioread32(FlashReg + DEVICE_SPARE_AREA_SIZE); iowrite32(tmp, FlashReg + LOGICAL_PAGE_SPARE_SIZE); #if SUPPORT_15BITECC iowrite32(15, FlashReg + ECC_CORRECTION); #elif SUPPORT_8BITECC iowrite32(8, FlashReg + ECC_CORRECTION); #endif } /* As Toshiba NAND can not provide it's block number, */ /* so here we need user to provide the correct block */ /* number in a scratch register before the Linux NAND */ /* driver is loaded. If no valid value found in the scratch */ /* register, then we use default block number value */ scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); if (!scratch_reg) { printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", __FILE__, __LINE__); DeviceInfo.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; } else { nand_dbg_print(NAND_DBG_WARN, "Spectra: ioremap reg address: 0x%p\n", scratch_reg); DeviceInfo.wTotalBlocks = 1 << ioread8(scratch_reg); if (DeviceInfo.wTotalBlocks < 512) DeviceInfo.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; iounmap(scratch_reg); } } static void get_hynix_nand_para(void) { void __iomem *scratch_reg; u32 main_size, spare_size; switch (DeviceInfo.wDeviceID) { case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */ case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */ iowrite32(128, FlashReg + PAGES_PER_BLOCK); iowrite32(4096, FlashReg + DEVICE_MAIN_AREA_SIZE); iowrite32(224, FlashReg + DEVICE_SPARE_AREA_SIZE); main_size = 4096 * ioread32(FlashReg + DEVICES_CONNECTED); spare_size = 224 * ioread32(FlashReg + DEVICES_CONNECTED); iowrite32(main_size, FlashReg + LOGICAL_PAGE_DATA_SIZE); iowrite32(spare_size, FlashReg + LOGICAL_PAGE_SPARE_SIZE); iowrite32(0, FlashReg + DEVICE_WIDTH); #if SUPPORT_15BITECC iowrite32(15, FlashReg + ECC_CORRECTION); #elif SUPPORT_8BITECC iowrite32(8, FlashReg + ECC_CORRECTION); #endif DeviceInfo.MLCDevice = 1; break; default: nand_dbg_print(NAND_DBG_WARN, "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." "Will use default parameter values instead.\n", DeviceInfo.wDeviceID); } scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); if (!scratch_reg) { printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", __FILE__, __LINE__); DeviceInfo.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; } else { nand_dbg_print(NAND_DBG_WARN, "Spectra: ioremap reg address: 0x%p\n", scratch_reg); DeviceInfo.wTotalBlocks = 1 << ioread8(scratch_reg); if (DeviceInfo.wTotalBlocks < 512) DeviceInfo.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; iounmap(scratch_reg); } } static void find_valid_banks(void) { u32 id[LLD_MAX_FLASH_BANKS]; int i; totalUsedBanks = 0; for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { index_addr((u32)(MODE_11 | (i << 24) | 0), 0x90); index_addr((u32)(MODE_11 | (i << 24) | 1), 0); index_addr_read_data((u32)(MODE_11 | (i << 24) | 2), &id[i]); nand_dbg_print(NAND_DBG_DEBUG, "Return 1st ID for bank[%d]: %x\n", i, id[i]); if (i == 0) { if (id[i] & 0x0ff) GLOB_valid_banks[i] = 1; } else { if ((id[i] & 0x0ff) == (id[0] & 0x0ff)) GLOB_valid_banks[i] = 1; } totalUsedBanks += GLOB_valid_banks[i]; } nand_dbg_print(NAND_DBG_DEBUG, "totalUsedBanks: %d\n", totalUsedBanks); } static void detect_partition_feature(void) { if (ioread32(FlashReg + FEATURES) & FEATURES__PARTITION) { if ((ioread32(FlashReg + PERM_SRC_ID_1) & PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { DeviceInfo.wSpectraStartBlock = ((ioread32(FlashReg + MIN_MAX_BANK_1) & MIN_MAX_BANK_1__MIN_VALUE) * DeviceInfo.wTotalBlocks) + (ioread32(FlashReg + MIN_BLK_ADDR_1) & MIN_BLK_ADDR_1__VALUE); DeviceInfo.wSpectraEndBlock = (((ioread32(FlashReg + MIN_MAX_BANK_1) & MIN_MAX_BANK_1__MAX_VALUE) >> 2) * DeviceInfo.wTotalBlocks) + (ioread32(FlashReg + MAX_BLK_ADDR_1) & MAX_BLK_ADDR_1__VALUE); DeviceInfo.wTotalBlocks *= totalUsedBanks; if (DeviceInfo.wSpectraEndBlock >= DeviceInfo.wTotalBlocks) { DeviceInfo.wSpectraEndBlock = DeviceInfo.wTotalBlocks - 1; } DeviceInfo.wDataBlockNum = DeviceInfo.wSpectraEndBlock - DeviceInfo.wSpectraStartBlock + 1; } else { DeviceInfo.wTotalBlocks *= totalUsedBanks; DeviceInfo.wSpectraStartBlock = SPECTRA_START_BLOCK; DeviceInfo.wSpectraEndBlock = DeviceInfo.wTotalBlocks - 1; DeviceInfo.wDataBlockNum = DeviceInfo.wSpectraEndBlock - DeviceInfo.wSpectraStartBlock + 1; } } else { DeviceInfo.wTotalBlocks *= totalUsedBanks; DeviceInfo.wSpectraStartBlock = SPECTRA_START_BLOCK; DeviceInfo.wSpectraEndBlock = DeviceInfo.wTotalBlocks - 1; DeviceInfo.wDataBlockNum = DeviceInfo.wSpectraEndBlock - DeviceInfo.wSpectraStartBlock + 1; } } static void dump_device_info(void) { nand_dbg_print(NAND_DBG_DEBUG, "DeviceInfo:\n"); nand_dbg_print(NAND_DBG_DEBUG, "DeviceMaker: 0x%x\n", DeviceInfo.wDeviceMaker); nand_dbg_print(NAND_DBG_DEBUG, "DeviceID: 0x%x\n", DeviceInfo.wDeviceID); nand_dbg_print(NAND_DBG_DEBUG, "DeviceType: 0x%x\n", DeviceInfo.wDeviceType); nand_dbg_print(NAND_DBG_DEBUG, "SpectraStartBlock: %d\n", DeviceInfo.wSpectraStartBlock); nand_dbg_print(NAND_DBG_DEBUG, "SpectraEndBlock: %d\n", DeviceInfo.wSpectraEndBlock); nand_dbg_print(NAND_DBG_DEBUG, "TotalBlocks: %d\n", DeviceInfo.wTotalBlocks); nand_dbg_print(NAND_DBG_DEBUG, "PagesPerBlock: %d\n", DeviceInfo.wPagesPerBlock); nand_dbg_print(NAND_DBG_DEBUG, "PageSize: %d\n", DeviceInfo.wPageSize); nand_dbg_print(NAND_DBG_DEBUG, "PageDataSize: %d\n", DeviceInfo.wPageDataSize); nand_dbg_print(NAND_DBG_DEBUG, "PageSpareSize: %d\n", DeviceInfo.wPageSpareSize); nand_dbg_print(NAND_DBG_DEBUG, "NumPageSpareFlag: %d\n", DeviceInfo.wNumPageSpareFlag); nand_dbg_print(NAND_DBG_DEBUG, "ECCBytesPerSector: %d\n", DeviceInfo.wECCBytesPerSector); nand_dbg_print(NAND_DBG_DEBUG, "BlockSize: %d\n", DeviceInfo.wBlockSize); nand_dbg_print(NAND_DBG_DEBUG, "BlockDataSize: %d\n", DeviceInfo.wBlockDataSize); nand_dbg_print(NAND_DBG_DEBUG, "DataBlockNum: %d\n", DeviceInfo.wDataBlockNum); nand_dbg_print(NAND_DBG_DEBUG, "PlaneNum: %d\n", DeviceInfo.bPlaneNum); nand_dbg_print(NAND_DBG_DEBUG, "DeviceMainAreaSize: %d\n", DeviceInfo.wDeviceMainAreaSize); nand_dbg_print(NAND_DBG_DEBUG, "DeviceSpareAreaSize: %d\n", DeviceInfo.wDeviceSpareAreaSize); nand_dbg_print(NAND_DBG_DEBUG, "DevicesConnected: %d\n", DeviceInfo.wDevicesConnected); nand_dbg_print(NAND_DBG_DEBUG, "DeviceWidth: %d\n", DeviceInfo.wDeviceWidth); nand_dbg_print(NAND_DBG_DEBUG, "HWRevision: 0x%x\n", DeviceInfo.wHWRevision); nand_dbg_print(NAND_DBG_DEBUG, "HWFeatures: 0x%x\n", DeviceInfo.wHWFeatures); nand_dbg_print(NAND_DBG_DEBUG, "ONFIDevFeatures: 0x%x\n", DeviceInfo.wONFIDevFeatures); nand_dbg_print(NAND_DBG_DEBUG, "ONFIOptCommands: 0x%x\n", DeviceInfo.wONFIOptCommands); nand_dbg_print(NAND_DBG_DEBUG, "ONFITimingMode: 0x%x\n", DeviceInfo.wONFITimingMode); nand_dbg_print(NAND_DBG_DEBUG, "ONFIPgmCacheTimingMode: 0x%x\n", DeviceInfo.wONFIPgmCacheTimingMode); nand_dbg_print(NAND_DBG_DEBUG, "MLCDevice: %s\n", DeviceInfo.MLCDevice ? "Yes" : "No"); nand_dbg_print(NAND_DBG_DEBUG, "SpareSkipBytes: %d\n", DeviceInfo.wSpareSkipBytes); nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageNumber: %d\n", DeviceInfo.nBitsInPageNumber); nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageDataSize: %d\n", DeviceInfo.nBitsInPageDataSize); nand_dbg_print(NAND_DBG_DEBUG, "BitsInBlockDataSize: %d\n", DeviceInfo.nBitsInBlockDataSize); } u16 NAND_Read_Device_ID(void) { u16 status = PASS; u8 no_of_planes; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); iowrite32(0x02, FlashReg + SPARE_AREA_SKIP_BYTES); iowrite32(0xffff, FlashReg + SPARE_AREA_MARKER); DeviceInfo.wDeviceMaker = ioread32(FlashReg + MANUFACTURER_ID); DeviceInfo.wDeviceID = ioread32(FlashReg + DEVICE_ID); DeviceInfo.MLCDevice = ioread32(FlashReg + DEVICE_PARAM_0) & 0x0c; if (ioread32(FlashReg + ONFI_DEVICE_NO_OF_LUNS) & ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */ if (FAIL == get_onfi_nand_para()) return FAIL; } else if (DeviceInfo.wDeviceMaker == 0xEC) { /* Samsung NAND */ get_samsung_nand_para(); } else if (DeviceInfo.wDeviceMaker == 0x98) { /* Toshiba NAND */ get_toshiba_nand_para(); } else if (DeviceInfo.wDeviceMaker == 0xAD) { /* Hynix NAND */ get_hynix_nand_para(); } else { DeviceInfo.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; } nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" "acc_clks: %d, re_2_we: %d, we_2_re: %d," "addr_2_data: %d, rdwr_en_lo_cnt: %d, " "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", ioread32(FlashReg + ACC_CLKS), ioread32(FlashReg + RE_2_WE), ioread32(FlashReg + WE_2_RE), ioread32(FlashReg + ADDR_2_DATA), ioread32(FlashReg + RDWR_EN_LO_CNT), ioread32(FlashReg + RDWR_EN_HI_CNT), ioread32(FlashReg + CS_SETUP_CNT)); DeviceInfo.wHWRevision = ioread32(FlashReg + REVISION); DeviceInfo.wHWFeatures = ioread32(FlashReg + FEATURES); DeviceInfo.wDeviceMainAreaSize = ioread32(FlashReg + DEVICE_MAIN_AREA_SIZE); DeviceInfo.wDeviceSpareAreaSize = ioread32(FlashReg + DEVICE_SPARE_AREA_SIZE); DeviceInfo.wPageDataSize = ioread32(FlashReg + LOGICAL_PAGE_DATA_SIZE); /* Note: When using the Micon 4K NAND device, the controller will report * Page Spare Size as 216 bytes. But Micron's Spec say it's 218 bytes. * And if force set it to 218 bytes, the controller can not work * correctly. So just let it be. But keep in mind that this bug may * cause * other problems in future. - Yunpeng 2008-10-10 */ DeviceInfo.wPageSpareSize = ioread32(FlashReg + LOGICAL_PAGE_SPARE_SIZE); DeviceInfo.wPagesPerBlock = ioread32(FlashReg + PAGES_PER_BLOCK); DeviceInfo.wPageSize = DeviceInfo.wPageDataSize + DeviceInfo.wPageSpareSize; DeviceInfo.wBlockSize = DeviceInfo.wPageSize * DeviceInfo.wPagesPerBlock; DeviceInfo.wBlockDataSize = DeviceInfo.wPagesPerBlock * DeviceInfo.wPageDataSize; DeviceInfo.wDeviceWidth = ioread32(FlashReg + DEVICE_WIDTH); DeviceInfo.wDeviceType = ((ioread32(FlashReg + DEVICE_WIDTH) > 0) ? 16 : 8); DeviceInfo.wDevicesConnected = ioread32(FlashReg + DEVICES_CONNECTED); DeviceInfo.wSpareSkipBytes = ioread32(FlashReg + SPARE_AREA_SKIP_BYTES) * DeviceInfo.wDevicesConnected; 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); set_ecc_config(); no_of_planes = ioread32(FlashReg + NUMBER_OF_PLANES) & NUMBER_OF_PLANES__VALUE; switch (no_of_planes) { case 0: case 1: case 3: case 7: DeviceInfo.bPlaneNum = no_of_planes + 1; break; default: status = FAIL; break; } find_valid_banks(); detect_partition_feature(); dump_device_info(); return status; } u16 NAND_UnlockArrayAll(void) { u64 start_addr, end_addr; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); start_addr = 0; end_addr = ((u64)DeviceInfo.wBlockSize * (DeviceInfo.wTotalBlocks - 1)) >> DeviceInfo.nBitsInPageDataSize; index_addr((u32)(MODE_10 | (u32)start_addr), 0x10); index_addr((u32)(MODE_10 | (u32)end_addr), 0x11); return PASS; } void NAND_LLD_Enable_Disable_Interrupts(u16 INT_ENABLE) { nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); if (INT_ENABLE) iowrite32(1, FlashReg + GLOBAL_INT_ENABLE); else iowrite32(0, FlashReg + GLOBAL_INT_ENABLE); } u16 NAND_Erase_Block(u32 block) { u16 status = PASS; u64 flash_add; u16 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); if (block >= DeviceInfo.wTotalBlocks) status = FAIL; if (status == PASS) { intr_status = intr_status_addresses[flash_bank]; iowrite32(INTR_STATUS0__ERASE_COMP | INTR_STATUS0__ERASE_FAIL, FlashReg + intr_status); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 1); while (!(ioread32(FlashReg + intr_status) & (INTR_STATUS0__ERASE_COMP | INTR_STATUS0__ERASE_FAIL))) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ERASE_FAIL) status = FAIL; iowrite32(INTR_STATUS0__ERASE_COMP | INTR_STATUS0__ERASE_FAIL, FlashReg + intr_status); } return status; } static u32 Boundary_Check_Block_Page(u32 block, u16 page, u16 page_count) { u32 status = PASS; if (block >= DeviceInfo.wTotalBlocks) status = FAIL; if (page + page_count > DeviceInfo.wPagesPerBlock) status = FAIL; return status; } u16 NAND_Read_Page_Spare(u8 *read_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u32 i; u64 flash_add; u32 PageSpareSize = DeviceInfo.wPageSpareSize; u32 spareFlagBytes = DeviceInfo.wNumPageSpareFlag; u32 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u8 *page_spare = buf_read_page_spare; if (block >= DeviceInfo.wTotalBlocks) { printk(KERN_ERR "block too big: %d\n", (int)block); status = FAIL; } if (page >= DeviceInfo.wPagesPerBlock) { printk(KERN_ERR "page too big: %d\n", page); status = FAIL; } if (page_count > 1) { printk(KERN_ERR "page count too big: %d\n", page_count); status = FAIL; } flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); if (status == PASS) { intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x41); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x2000 | page_count); while (!(ioread32(FlashReg + intr_status) & INTR_STATUS0__LOAD_COMP)) ; iowrite32((u32)(MODE_01 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), FlashMem); for (i = 0; i < (PageSpareSize / 4); i++) *((u32 *)page_spare + i) = ioread32(FlashMem + 0x10); if (enable_ecc) { for (i = 0; i < spareFlagBytes; i++) read_data[i] = page_spare[PageSpareSize - spareFlagBytes + i]; for (i = 0; i < (PageSpareSize - spareFlagBytes); i++) read_data[spareFlagBytes + i] = page_spare[i]; } else { for (i = 0; i < PageSpareSize; i++) read_data[i] = page_spare[i]; } index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x42); } return status; } /* No use function. Should be removed later */ u16 NAND_Write_Page_Spare(u8 *write_data, u32 block, u16 page, u16 page_count) { printk(KERN_ERR "Error! This function (NAND_Write_Page_Spare) should never" " be called!\n"); return ERR; } /* op value: 0 - DDMA read; 1 - DDMA write */ static void ddma_trans(u8 *data, u64 flash_add, u32 flash_bank, int op, u32 numPages) { u32 data_addr; /* Map virtual address to bus address for DDMA */ data_addr = virt_to_bus(data); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), (u16)(2 << 12) | (op << 8) | numPages); index_addr((u32)(MODE_10 | (flash_bank << 24) | ((u16)(0x0FFFF & (data_addr >> 16)) << 8)), (u16)(2 << 12) | (2 << 8) | 0); index_addr((u32)(MODE_10 | (flash_bank << 24) | ((u16)(0x0FFFF & data_addr) << 8)), (u16)(2 << 12) | (3 << 8) | 0); index_addr((u32)(MODE_10 | (flash_bank << 24) | (1 << 16) | (0x40 << 8)), (u16)(2 << 12) | (4 << 8) | 0); } /* If data in buf are all 0xff, then return 1; otherwise return 0 */ static int check_all_1(u8 *buf) { int i, j, cnt; for (i = 0; i < DeviceInfo.wPageDataSize; i++) { if (buf[i] != 0xff) { cnt = 0; nand_dbg_print(NAND_DBG_WARN, "the first non-0xff data byte is: %d\n", i); for (j = i; j < DeviceInfo.wPageDataSize; j++) { nand_dbg_print(NAND_DBG_WARN, "0x%x ", buf[j]); cnt++; if (cnt > 8) break; } nand_dbg_print(NAND_DBG_WARN, "\n"); return 0; } } return 1; } static int do_ecc_new(unsigned long bank, u8 *buf, u32 block, u16 page) { int status = PASS; u16 err_page = 0; u16 err_byte; u8 err_sect; u8 err_dev; u16 err_fix_info; u16 err_addr; u32 ecc_sect_size; u8 *err_pos; u32 err_page_addr[4] = {ERR_PAGE_ADDR0, ERR_PAGE_ADDR1, ERR_PAGE_ADDR2, ERR_PAGE_ADDR3}; ecc_sect_size = ECC_SECTOR_SIZE * (DeviceInfo.wDevicesConnected); do { err_page = ioread32(FlashReg + err_page_addr[bank]); err_addr = ioread32(FlashReg + ECC_ERROR_ADDRESS); err_byte = err_addr & ECC_ERROR_ADDRESS__OFFSET; err_sect = ((err_addr & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12); err_fix_info = ioread32(FlashReg + ERR_CORRECTION_INFO); err_dev = ((err_fix_info & ERR_CORRECTION_INFO__DEVICE_NR) >> 8); if (err_fix_info & ERR_CORRECTION_INFO__ERROR_TYPE) { nand_dbg_print(NAND_DBG_WARN, "%s, Line %d Uncorrectable ECC error " "when read block %d page %d." "PTN_INTR register: 0x%x " "err_page: %d, err_sect: %d, err_byte: %d, " "err_dev: %d, ecc_sect_size: %d, " "err_fix_info: 0x%x\n", __FILE__, __LINE__, block, page, ioread32(FlashReg + PTN_INTR), err_page, err_sect, err_byte, err_dev, ecc_sect_size, (u32)err_fix_info); if (check_all_1(buf)) nand_dbg_print(NAND_DBG_WARN, "%s, Line %d" "All 0xff!\n", __FILE__, __LINE__); else nand_dbg_print(NAND_DBG_WARN, "%s, Line %d" "Not all 0xff!\n", __FILE__, __LINE__); status = FAIL; } else { nand_dbg_print(NAND_DBG_WARN, "%s, Line %d Found ECC error " "when read block %d page %d." "err_page: %d, err_sect: %d, err_byte: %d, " "err_dev: %d, ecc_sect_size: %d, " "err_fix_info: 0x%x\n", __FILE__, __LINE__, block, page, err_page, err_sect, err_byte, err_dev, ecc_sect_size, (u32)err_fix_info); if (err_byte < ECC_SECTOR_SIZE) { err_pos = buf + (err_page - page) * DeviceInfo.wPageDataSize + err_sect * ecc_sect_size + err_byte * DeviceInfo.wDevicesConnected + err_dev; *err_pos ^= err_fix_info & ERR_CORRECTION_INFO__BYTEMASK; } } } while (!(err_fix_info & ERR_CORRECTION_INFO__LAST_ERR_INFO)); return status; } u16 NAND_Read_Page_Main_Polling(u8 *read_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u64 flash_add; u32 intr_status = 0; u32 flash_bank; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u8 *read_data_l; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); if (status != PASS) return status; flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); iowrite32(0, FlashReg + TRANSFER_SPARE_REG); intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); if (page_count > 1) { read_data_l = read_data; while (page_count > MAX_PAGES_PER_RW) { if (ioread32(FlashReg + MULTIPLANE_OPERATION)) status = NAND_Multiplane_Read(read_data_l, block, page, MAX_PAGES_PER_RW); else status = NAND_Pipeline_Read_Ahead_Polling( read_data_l, block, page, MAX_PAGES_PER_RW); if (status == FAIL) return status; read_data_l += DeviceInfo.wPageDataSize * MAX_PAGES_PER_RW; page_count -= MAX_PAGES_PER_RW; page += MAX_PAGES_PER_RW; } if (ioread32(FlashReg + MULTIPLANE_OPERATION)) status = NAND_Multiplane_Read(read_data_l, block, page, page_count); else status = NAND_Pipeline_Read_Ahead_Polling( read_data_l, block, page, page_count); return status; } iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); ddma_trans(read_data, flash_add, flash_bank, 0, 1); if (enable_ecc) { while (!(ioread32(FlashReg + intr_status) & (INTR_STATUS0__ECC_TRANSACTION_DONE | INTR_STATUS0__ECC_ERR))) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_ERR) { iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + intr_status); status = do_ecc_new(flash_bank, read_data, block, page); } if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_TRANSACTION_DONE & INTR_STATUS0__ECC_ERR) iowrite32(INTR_STATUS0__ECC_TRANSACTION_DONE | INTR_STATUS0__ECC_ERR, FlashReg + intr_status); else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_TRANSACTION_DONE) iowrite32(INTR_STATUS0__ECC_TRANSACTION_DONE, FlashReg + intr_status); else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_ERR) iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + intr_status); } else { while (!(ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP)) ; iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; return status; } u16 NAND_Pipeline_Read_Ahead_Polling(u8 *read_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u32 NumPages = page_count; u64 flash_add; u32 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u32 ecc_done_OR_dma_comp; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); if (page_count < 2) status = FAIL; flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) *DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); if (status == PASS) { intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x42); ddma_trans(read_data, flash_add, flash_bank, 0, NumPages); ecc_done_OR_dma_comp = 0; while (1) { if (enable_ecc) { while (!ioread32(FlashReg + intr_status)) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_ERR) { iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + intr_status); status = do_ecc_new(flash_bank, read_data, block, page); } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP) { iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); if (1 == ecc_done_OR_dma_comp) break; ecc_done_OR_dma_comp = 1; } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_TRANSACTION_DONE) { iowrite32( INTR_STATUS0__ECC_TRANSACTION_DONE, FlashReg + intr_status); if (1 == ecc_done_OR_dma_comp) break; ecc_done_OR_dma_comp = 1; } } else { while (!(ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP)) ; iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); break; } iowrite32((~INTR_STATUS0__ECC_ERR) & (~INTR_STATUS0__ECC_TRANSACTION_DONE) & (~INTR_STATUS0__DMA_CMD_COMP), FlashReg + intr_status); } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; } return status; } u16 NAND_Read_Page_Main(u8 *read_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u64 flash_add; u32 intr_status = 0; u32 flash_bank; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; int ret; u8 *read_data_l; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); if (status != PASS) return status; flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); iowrite32(0, FlashReg + TRANSFER_SPARE_REG); intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); if (page_count > 1) { read_data_l = read_data; while (page_count > MAX_PAGES_PER_RW) { if (ioread32(FlashReg + MULTIPLANE_OPERATION)) status = NAND_Multiplane_Read(read_data_l, block, page, MAX_PAGES_PER_RW); else status = NAND_Pipeline_Read_Ahead( read_data_l, block, page, MAX_PAGES_PER_RW); if (status == FAIL) return status; read_data_l += DeviceInfo.wPageDataSize * MAX_PAGES_PER_RW; page_count -= MAX_PAGES_PER_RW; page += MAX_PAGES_PER_RW; } if (ioread32(FlashReg + MULTIPLANE_OPERATION)) status = NAND_Multiplane_Read(read_data_l, block, page, page_count); else status = NAND_Pipeline_Read_Ahead( read_data_l, block, page, page_count); return status; } iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); /* Fill the mrst_nand_info structure */ info.state = INT_READ_PAGE_MAIN; info.read_data = read_data; info.flash_bank = flash_bank; info.block = block; info.page = page; info.ret = PASS; ddma_trans(read_data, flash_add, flash_bank, 0, 1); iowrite32(1, FlashReg + GLOBAL_INT_ENABLE); /* Enable Interrupt */ ret = wait_for_completion_timeout(&info.complete, 10 * HZ); if (!ret) { printk(KERN_ERR "Wait for completion timeout " "in %s, Line %d\n", __FILE__, __LINE__); status = ERR; } else { status = info.ret; } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; return status; } void Conv_Spare_Data_Log2Phy_Format(u8 *data) { int i; const u32 spareFlagBytes = DeviceInfo.wNumPageSpareFlag; const u32 PageSpareSize = DeviceInfo.wPageSpareSize; if (enable_ecc) { for (i = spareFlagBytes - 1; i >= 0; i--) data[PageSpareSize - spareFlagBytes + i] = data[i]; } } void Conv_Spare_Data_Phy2Log_Format(u8 *data) { int i; const u32 spareFlagBytes = DeviceInfo.wNumPageSpareFlag; const u32 PageSpareSize = DeviceInfo.wPageSpareSize; if (enable_ecc) { for (i = 0; i < spareFlagBytes; i++) data[i] = data[PageSpareSize - spareFlagBytes + i]; } } void Conv_Main_Spare_Data_Log2Phy_Format(u8 *data, u16 page_count) { const u32 PageSize = DeviceInfo.wPageSize; const u32 PageDataSize = DeviceInfo.wPageDataSize; const u32 eccBytes = DeviceInfo.wECCBytesPerSector; const u32 spareSkipBytes = DeviceInfo.wSpareSkipBytes; const u32 spareFlagBytes = DeviceInfo.wNumPageSpareFlag; u32 eccSectorSize; u32 page_offset; int i, j; eccSectorSize = ECC_SECTOR_SIZE * (DeviceInfo.wDevicesConnected); if (enable_ecc) { while (page_count > 0) { page_offset = (page_count - 1) * PageSize; j = (DeviceInfo.wPageDataSize / eccSectorSize); for (i = spareFlagBytes - 1; i >= 0; i--) data[page_offset + (eccSectorSize + eccBytes) * j + i] = data[page_offset + PageDataSize + i]; for (j--; j >= 1; j--) { for (i = eccSectorSize - 1; i >= 0; i--) data[page_offset + (eccSectorSize + eccBytes) * j + i] = data[page_offset + eccSectorSize * j + i]; } for (i = (PageSize - spareSkipBytes) - 1; i >= PageDataSize; i--) data[page_offset + i + spareSkipBytes] = data[page_offset + i]; page_count--; } } } void Conv_Main_Spare_Data_Phy2Log_Format(u8 *data, u16 page_count) { const u32 PageSize = DeviceInfo.wPageSize; const u32 PageDataSize = DeviceInfo.wPageDataSize; const u32 eccBytes = DeviceInfo.wECCBytesPerSector; const u32 spareSkipBytes = DeviceInfo.wSpareSkipBytes; const u32 spareFlagBytes = DeviceInfo.wNumPageSpareFlag; u32 eccSectorSize; u32 page_offset; int i, j; eccSectorSize = ECC_SECTOR_SIZE * (DeviceInfo.wDevicesConnected); if (enable_ecc) { while (page_count > 0) { page_offset = (page_count - 1) * PageSize; for (i = PageDataSize; i < PageSize - spareSkipBytes; i++) data[page_offset + i] = data[page_offset + i + spareSkipBytes]; for (j = 1; j < DeviceInfo.wPageDataSize / eccSectorSize; j++) { for (i = 0; i < eccSectorSize; i++) data[page_offset + eccSectorSize * j + i] = data[page_offset + (eccSectorSize + eccBytes) * j + i]; } for (i = 0; i < spareFlagBytes; i++) data[page_offset + PageDataSize + i] = data[page_offset + (eccSectorSize + eccBytes) * j + i]; page_count--; } } } /* Un-tested function */ u16 NAND_Multiplane_Read(u8 *read_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u32 NumPages = page_count; u64 flash_add; u32 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u32 ecc_done_OR_dma_comp; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); if (status == PASS) { intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + TRANSFER_SPARE_REG); iowrite32(0x01, FlashReg + MULTIPLANE_OPERATION); iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x42); ddma_trans(read_data, flash_add, flash_bank, 0, NumPages); ecc_done_OR_dma_comp = 0; while (1) { if (enable_ecc) { while (!ioread32(FlashReg + intr_status)) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_ERR) { iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + intr_status); status = do_ecc_new(flash_bank, read_data, block, page); } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP) { iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); if (1 == ecc_done_OR_dma_comp) break; ecc_done_OR_dma_comp = 1; } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_TRANSACTION_DONE) { iowrite32( INTR_STATUS0__ECC_TRANSACTION_DONE, FlashReg + intr_status); if (1 == ecc_done_OR_dma_comp) break; ecc_done_OR_dma_comp = 1; } } else { while (!(ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP)) ; iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); break; } iowrite32((~INTR_STATUS0__ECC_ERR) & (~INTR_STATUS0__ECC_TRANSACTION_DONE) & (~INTR_STATUS0__DMA_CMD_COMP), FlashReg + intr_status); } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + MULTIPLANE_OPERATION); } return status; } u16 NAND_Pipeline_Read_Ahead(u8 *read_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u32 NumPages = page_count; u64 flash_add; u32 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; int ret; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); if (page_count < 2) status = FAIL; if (status != PASS) return status; flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) *DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); /* Fill the mrst_nand_info structure */ info.state = INT_PIPELINE_READ_AHEAD; info.read_data = read_data; info.flash_bank = flash_bank; info.block = block; info.page = page; info.ret = PASS; index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x42); ddma_trans(read_data, flash_add, flash_bank, 0, NumPages); iowrite32(1, FlashReg + GLOBAL_INT_ENABLE); /* Enable Interrupt */ ret = wait_for_completion_timeout(&info.complete, 10 * HZ); if (!ret) { printk(KERN_ERR "Wait for completion timeout " "in %s, Line %d\n", __FILE__, __LINE__); status = ERR; } else { status = info.ret; } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; return status; } u16 NAND_Write_Page_Main(u8 *write_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u64 flash_add; u32 intr_status = 0; u32 flash_bank; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; int ret; u8 *write_data_l; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); if (status != PASS) return status; flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); intr_status = intr_status_addresses[flash_bank]; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); iowrite32(INTR_STATUS0__PROGRAM_COMP | INTR_STATUS0__PROGRAM_FAIL, FlashReg + intr_status); if (page_count > 1) { write_data_l = write_data; while (page_count > MAX_PAGES_PER_RW) { if (ioread32(FlashReg + MULTIPLANE_OPERATION)) status = NAND_Multiplane_Write(write_data_l, block, page, MAX_PAGES_PER_RW); else status = NAND_Pipeline_Write_Ahead( write_data_l, block, page, MAX_PAGES_PER_RW); if (status == FAIL) return status; write_data_l += DeviceInfo.wPageDataSize * MAX_PAGES_PER_RW; page_count -= MAX_PAGES_PER_RW; page += MAX_PAGES_PER_RW; } if (ioread32(FlashReg + MULTIPLANE_OPERATION)) status = NAND_Multiplane_Write(write_data_l, block, page, page_count); else status = NAND_Pipeline_Write_Ahead(write_data_l, block, page, page_count); return status; } iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); /* Fill the mrst_nand_info structure */ info.state = INT_WRITE_PAGE_MAIN; info.write_data = write_data; info.flash_bank = flash_bank; info.block = block; info.page = page; info.ret = PASS; ddma_trans(write_data, flash_add, flash_bank, 1, 1); iowrite32(1, FlashReg + GLOBAL_INT_ENABLE); /* Enable interrupt */ ret = wait_for_completion_timeout(&info.complete, 10 * HZ); if (!ret) { printk(KERN_ERR "Wait for completion timeout " "in %s, Line %d\n", __FILE__, __LINE__); status = ERR; } else { status = info.ret; } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while (ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG) ; return status; } void NAND_ECC_Ctrl(int enable) { if (enable) { nand_dbg_print(NAND_DBG_WARN, "Will enable ECC in %s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); iowrite32(1, FlashReg + ECC_ENABLE); enable_ecc = 1; } else { nand_dbg_print(NAND_DBG_WARN, "Will disable ECC in %s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); iowrite32(0, FlashReg + ECC_ENABLE); enable_ecc = 0; } } u16 NAND_Write_Page_Main_Spare(u8 *write_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u32 i, j, page_num = 0; u32 PageSize = DeviceInfo.wPageSize; u32 PageDataSize = DeviceInfo.wPageDataSize; u32 eccBytes = DeviceInfo.wECCBytesPerSector; u32 spareFlagBytes = DeviceInfo.wNumPageSpareFlag; u32 spareSkipBytes = DeviceInfo.wSpareSkipBytes; u64 flash_add; u32 eccSectorSize; u32 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u8 *page_main_spare = buf_write_page_main_spare; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); eccSectorSize = ECC_SECTOR_SIZE * (DeviceInfo.wDevicesConnected); status = Boundary_Check_Block_Page(block, page, page_count); flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); if (status == PASS) { intr_status = intr_status_addresses[flash_bank]; iowrite32(1, FlashReg + TRANSFER_SPARE_REG); while ((status != FAIL) && (page_count > 0)) { flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32((u32)(MODE_01 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), FlashMem); if (enable_ecc) { for (j = 0; j < DeviceInfo.wPageDataSize / eccSectorSize; j++) { for (i = 0; i < eccSectorSize; i++) page_main_spare[(eccSectorSize + eccBytes) * j + i] = write_data[eccSectorSize * j + i]; for (i = 0; i < eccBytes; i++) page_main_spare[(eccSectorSize + eccBytes) * j + eccSectorSize + i] = write_data[PageDataSize + spareFlagBytes + eccBytes * j + i]; } for (i = 0; i < spareFlagBytes; i++) page_main_spare[(eccSectorSize + eccBytes) * j + i] = write_data[PageDataSize + i]; for (i = PageSize - 1; i >= PageDataSize + spareSkipBytes; i--) page_main_spare[i] = page_main_spare[i - spareSkipBytes]; for (i = PageDataSize; i < PageDataSize + spareSkipBytes; i++) page_main_spare[i] = 0xff; for (i = 0; i < PageSize / 4; i++) iowrite32( *((u32 *)page_main_spare + i), FlashMem + 0x10); } else { for (i = 0; i < PageSize / 4; i++) iowrite32(*((u32 *)write_data + i), FlashMem + 0x10); } while (!(ioread32(FlashReg + intr_status) & (INTR_STATUS0__PROGRAM_COMP | INTR_STATUS0__PROGRAM_FAIL))) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__PROGRAM_FAIL) status = FAIL; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); page_num++; page_count--; write_data += PageSize; } iowrite32(0, FlashReg + TRANSFER_SPARE_REG); } return status; } u16 NAND_Read_Page_Main_Spare(u8 *read_data, u32 block, u16 page, u16 page_count) { u32 status = PASS; u32 i, j; u64 flash_add = 0; u32 PageSize = DeviceInfo.wPageSize; u32 PageDataSize = DeviceInfo.wPageDataSize; u32 PageSpareSize = DeviceInfo.wPageSpareSize; u32 eccBytes = DeviceInfo.wECCBytesPerSector; u32 spareFlagBytes = DeviceInfo.wNumPageSpareFlag; u32 spareSkipBytes = DeviceInfo.wSpareSkipBytes; u32 eccSectorSize; u32 flash_bank; u32 intr_status = 0; u8 *read_data_l = read_data; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u8 *page_main_spare = buf_read_page_main_spare; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); eccSectorSize = ECC_SECTOR_SIZE * (DeviceInfo.wDevicesConnected); status = Boundary_Check_Block_Page(block, page, page_count); flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); if (status == PASS) { intr_status = intr_status_addresses[flash_bank]; iowrite32(1, FlashReg + TRANSFER_SPARE_REG); iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); while ((status != FAIL) && (page_count > 0)) { flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x43); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x2000 | page_count); while (!(ioread32(FlashReg + intr_status) & INTR_STATUS0__LOAD_COMP)) ; iowrite32((u32)(MODE_01 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), FlashMem); for (i = 0; i < PageSize / 4; i++) *(((u32 *)page_main_spare) + i) = ioread32(FlashMem + 0x10); if (enable_ecc) { for (i = PageDataSize; i < PageSize - spareSkipBytes; i++) page_main_spare[i] = page_main_spare[i + spareSkipBytes]; for (j = 0; j < DeviceInfo.wPageDataSize / eccSectorSize; j++) { for (i = 0; i < eccSectorSize; i++) read_data_l[eccSectorSize * j + i] = page_main_spare[ (eccSectorSize + eccBytes) * j + i]; for (i = 0; i < eccBytes; i++) read_data_l[PageDataSize + spareFlagBytes + eccBytes * j + i] = page_main_spare[ (eccSectorSize + eccBytes) * j + eccSectorSize + i]; } for (i = 0; i < spareFlagBytes; i++) read_data_l[PageDataSize + i] = page_main_spare[(eccSectorSize + eccBytes) * j + i]; } else { for (i = 0; i < (PageDataSize + PageSpareSize); i++) read_data_l[i] = page_main_spare[i]; } if (enable_ecc) { while (!(ioread32(FlashReg + intr_status) & (INTR_STATUS0__ECC_TRANSACTION_DONE | INTR_STATUS0__ECC_ERR))) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_ERR) { iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + intr_status); status = do_ecc_new(flash_bank, read_data, block, page); } if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_TRANSACTION_DONE & INTR_STATUS0__ECC_ERR) { iowrite32(INTR_STATUS0__ECC_ERR | INTR_STATUS0__ECC_TRANSACTION_DONE, FlashReg + intr_status); } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_TRANSACTION_DONE) { iowrite32( INTR_STATUS0__ECC_TRANSACTION_DONE, FlashReg + intr_status); } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_ERR) { iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + intr_status); } } page++; page_count--; read_data_l += PageSize; } } iowrite32(0, FlashReg + TRANSFER_SPARE_REG); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x42); return status; } u16 NAND_Pipeline_Write_Ahead(u8 *write_data, u32 block, u16 page, u16 page_count) { u16 status = PASS; u32 NumPages = page_count; u64 flash_add; u32 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; int ret; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); if (page_count < 2) status = FAIL; if (status != PASS) return status; flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); /* Fill the mrst_nand_info structure */ info.state = INT_PIPELINE_WRITE_AHEAD; info.write_data = write_data; info.flash_bank = flash_bank; info.block = block; info.page = page; info.ret = PASS; index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x42); ddma_trans(write_data, flash_add, flash_bank, 1, NumPages); iowrite32(1, FlashReg + GLOBAL_INT_ENABLE); /* Enable interrupt */ ret = wait_for_completion_timeout(&info.complete, 10 * HZ); if (!ret) { printk(KERN_ERR "Wait for completion timeout " "in %s, Line %d\n", __FILE__, __LINE__); status = ERR; } else { status = info.ret; } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; return status; } /* Un-tested function */ u16 NAND_Multiplane_Write(u8 *write_data, u32 block, u16 page, u16 page_count) { u16 status = PASS; u32 NumPages = page_count; u64 flash_add; u32 flash_bank; u32 intr_status = 0; u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u16 status2 = PASS; u32 t; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); status = Boundary_Check_Block_Page(block, page, page_count); if (status != PASS) return status; flash_add = (u64)(block % (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)page * DeviceInfo.wPageDataSize; flash_bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); intr_status = intr_status_addresses[flash_bank]; iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + TRANSFER_SPARE_REG); iowrite32(0x01, FlashReg + MULTIPLANE_OPERATION); iowrite32(1, FlashReg + DMA_ENABLE); while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + TRANSFER_SPARE_REG); index_addr((u32)(MODE_10 | (flash_bank << 24) | (flash_add >> DeviceInfo.nBitsInPageDataSize)), 0x42); ddma_trans(write_data, flash_add, flash_bank, 1, NumPages); while (1) { while (!ioread32(FlashReg + intr_status)) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP) { iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); status = PASS; if (status2 == FAIL) status = FAIL; break; } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__PROGRAM_FAIL) { status2 = FAIL; status = FAIL; t = ioread32(FlashReg + intr_status) & INTR_STATUS0__PROGRAM_FAIL; iowrite32(t, FlashReg + intr_status); } else { iowrite32((~INTR_STATUS0__PROGRAM_FAIL) & (~INTR_STATUS0__DMA_CMD_COMP), FlashReg + intr_status); } } iowrite32(ioread32(FlashReg + intr_status), FlashReg + intr_status); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; iowrite32(0, FlashReg + MULTIPLANE_OPERATION); return status; } #if CMD_DMA static irqreturn_t cdma_isr(int irq, void *dev_id) { struct mrst_nand_info *dev = dev_id; int first_failed_cmd; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); if (!is_cdma_interrupt()) return IRQ_NONE; /* Disable controller interrupts */ iowrite32(0, FlashReg + GLOBAL_INT_ENABLE); GLOB_FTL_Event_Status(&first_failed_cmd); complete(&dev->complete); return IRQ_HANDLED; } #else static void handle_nand_int_read(struct mrst_nand_info *dev) { u32 intr_status_addresses[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u32 intr_status; u32 ecc_done_OR_dma_comp = 0; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); dev->ret = PASS; intr_status = intr_status_addresses[dev->flash_bank]; while (1) { if (enable_ecc) { if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_ERR) { iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + intr_status); dev->ret = do_ecc_new(dev->flash_bank, dev->read_data, dev->block, dev->page); } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP) { iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); if (1 == ecc_done_OR_dma_comp) break; ecc_done_OR_dma_comp = 1; } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__ECC_TRANSACTION_DONE) { iowrite32(INTR_STATUS0__ECC_TRANSACTION_DONE, FlashReg + intr_status); if (1 == ecc_done_OR_dma_comp) break; ecc_done_OR_dma_comp = 1; } } else { if (ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP) { iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); break; } else { printk(KERN_ERR "Illegal INTS " "(offset addr 0x%x) value: 0x%x\n", intr_status, ioread32(FlashReg + intr_status)); } } iowrite32((~INTR_STATUS0__ECC_ERR) & (~INTR_STATUS0__ECC_TRANSACTION_DONE) & (~INTR_STATUS0__DMA_CMD_COMP), FlashReg + intr_status); } } static void handle_nand_int_write(struct mrst_nand_info *dev) { u32 intr_status; u32 intr[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; int status = PASS; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); dev->ret = PASS; intr_status = intr[dev->flash_bank]; while (1) { while (!ioread32(FlashReg + intr_status)) ; if (ioread32(FlashReg + intr_status) & INTR_STATUS0__DMA_CMD_COMP) { iowrite32(INTR_STATUS0__DMA_CMD_COMP, FlashReg + intr_status); if (FAIL == status) dev->ret = FAIL; break; } else if (ioread32(FlashReg + intr_status) & INTR_STATUS0__PROGRAM_FAIL) { status = FAIL; iowrite32(INTR_STATUS0__PROGRAM_FAIL, FlashReg + intr_status); } else { iowrite32((~INTR_STATUS0__PROGRAM_FAIL) & (~INTR_STATUS0__DMA_CMD_COMP), FlashReg + intr_status); } } } static irqreturn_t ddma_isr(int irq, void *dev_id) { struct mrst_nand_info *dev = dev_id; u32 int_mask, ints0, ints1, ints2, ints3, ints_offset; u32 intr[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; int_mask = INTR_STATUS0__DMA_CMD_COMP | INTR_STATUS0__ECC_TRANSACTION_DONE | INTR_STATUS0__ECC_ERR | INTR_STATUS0__PROGRAM_FAIL | INTR_STATUS0__ERASE_FAIL; ints0 = ioread32(FlashReg + INTR_STATUS0); ints1 = ioread32(FlashReg + INTR_STATUS1); ints2 = ioread32(FlashReg + INTR_STATUS2); ints3 = ioread32(FlashReg + INTR_STATUS3); ints_offset = intr[dev->flash_bank]; nand_dbg_print(NAND_DBG_DEBUG, "INTR0: 0x%x, INTR1: 0x%x, INTR2: 0x%x, INTR3: 0x%x, " "DMA_INTR: 0x%x, " "dev->state: 0x%x, dev->flash_bank: %d\n", ints0, ints1, ints2, ints3, ioread32(FlashReg + DMA_INTR), dev->state, dev->flash_bank); if (!(ioread32(FlashReg + ints_offset) & int_mask)) { iowrite32(ints0, FlashReg + INTR_STATUS0); iowrite32(ints1, FlashReg + INTR_STATUS1); iowrite32(ints2, FlashReg + INTR_STATUS2); iowrite32(ints3, FlashReg + INTR_STATUS3); nand_dbg_print(NAND_DBG_WARN, "ddma_isr: Invalid interrupt for NAND controller. " "Ignore it\n"); return IRQ_NONE; } switch (dev->state) { case INT_READ_PAGE_MAIN: case INT_PIPELINE_READ_AHEAD: /* Disable controller interrupts */ iowrite32(0, FlashReg + GLOBAL_INT_ENABLE); handle_nand_int_read(dev); break; case INT_WRITE_PAGE_MAIN: case INT_PIPELINE_WRITE_AHEAD: iowrite32(0, FlashReg + GLOBAL_INT_ENABLE); handle_nand_int_write(dev); break; default: printk(KERN_ERR "ddma_isr - Illegal state: 0x%x\n", dev->state); return IRQ_NONE; } dev->state = INT_IDLE_STATE; complete(&dev->complete); return IRQ_HANDLED; } #endif static const struct pci_device_id nand_pci_ids[] = { { .vendor = 0x8086, .device = 0x0809, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, { /* end: all zeroes */ } }; static int nand_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { int ret = -ENODEV; unsigned long csr_base; unsigned long csr_len; struct mrst_nand_info *pndev = &info; u32 int_mask; ret = pci_enable_device(dev); if (ret) { printk(KERN_ERR "Spectra: pci_enable_device failed.\n"); return ret; } nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); FlashReg = ioremap_nocache(GLOB_HWCTL_REG_BASE, GLOB_HWCTL_REG_SIZE); if (!FlashReg) { printk(KERN_ERR "Spectra: ioremap_nocache failed!"); goto failed_disable; } nand_dbg_print(NAND_DBG_WARN, "Spectra: Remapped reg base address: " "0x%p, len: %d\n", FlashReg, GLOB_HWCTL_REG_SIZE); FlashMem = ioremap_nocache(GLOB_HWCTL_MEM_BASE, GLOB_HWCTL_MEM_SIZE); if (!FlashMem) { printk(KERN_ERR "Spectra: ioremap_nocache failed!"); iounmap(FlashReg); goto failed_disable; } nand_dbg_print(NAND_DBG_WARN, "Spectra: Remapped flash base address: " "0x%p, len: %d\n", (void *)FlashMem, GLOB_HWCTL_MEM_SIZE); nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" "acc_clks: %d, re_2_we: %d, we_2_re: %d," "addr_2_data: %d, rdwr_en_lo_cnt: %d, " "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", ioread32(FlashReg + ACC_CLKS), ioread32(FlashReg + RE_2_WE), ioread32(FlashReg + WE_2_RE), ioread32(FlashReg + ADDR_2_DATA), ioread32(FlashReg + RDWR_EN_LO_CNT), ioread32(FlashReg + RDWR_EN_HI_CNT), ioread32(FlashReg + CS_SETUP_CNT)); NAND_Flash_Reset(); iowrite32(0, FlashReg + GLOBAL_INT_ENABLE); #if CMD_DMA info.pcmds_num = 0; info.flash_bank = 0; info.cdma_num = 0; int_mask = (DMA_INTR__DESC_COMP_CHANNEL0 | DMA_INTR__DESC_COMP_CHANNEL1 | DMA_INTR__DESC_COMP_CHANNEL2 | DMA_INTR__DESC_COMP_CHANNEL3 | DMA_INTR__MEMCOPY_DESC_COMP); iowrite32(int_mask, FlashReg + DMA_INTR_EN); iowrite32(0xFFFF, FlashReg + DMA_INTR); int_mask = (INTR_STATUS0__ECC_ERR | INTR_STATUS0__PROGRAM_FAIL | INTR_STATUS0__ERASE_FAIL); #else int_mask = INTR_STATUS0__DMA_CMD_COMP | INTR_STATUS0__ECC_TRANSACTION_DONE | INTR_STATUS0__ECC_ERR | INTR_STATUS0__PROGRAM_FAIL | INTR_STATUS0__ERASE_FAIL; #endif iowrite32(int_mask, FlashReg + INTR_EN0); iowrite32(int_mask, FlashReg + INTR_EN1); iowrite32(int_mask, FlashReg + INTR_EN2); iowrite32(int_mask, FlashReg + INTR_EN3); /* Clear all status bits */ iowrite32(0xFFFF, FlashReg + INTR_STATUS0); iowrite32(0xFFFF, FlashReg + INTR_STATUS1); iowrite32(0xFFFF, FlashReg + INTR_STATUS2); iowrite32(0xFFFF, FlashReg + INTR_STATUS3); iowrite32(0x0F, FlashReg + RB_PIN_ENABLED); iowrite32(CHIP_EN_DONT_CARE__FLAG, FlashReg + CHIP_ENABLE_DONT_CARE); /* Should set value for these registers when init */ iowrite32(0, FlashReg + TWO_ROW_ADDR_CYCLES); iowrite32(1, FlashReg + ECC_ENABLE); enable_ecc = 1; pci_set_master(dev); pndev->dev = dev; csr_base = pci_resource_start(dev, 0); if (!csr_base) { printk(KERN_ERR "Spectra: pci_resource_start failed!\n"); ret = -ENODEV; goto failed_req_csr; } csr_len = pci_resource_len(dev, 0); if (!csr_len) { printk(KERN_ERR "Spectra: pci_resource_len failed!\n"); ret = -ENODEV; goto failed_req_csr; } ret = pci_request_regions(dev, SPECTRA_NAND_NAME); if (ret) { printk(KERN_ERR "Spectra: Unable to request " "memory region\n"); goto failed_req_csr; } pndev->ioaddr = ioremap_nocache(csr_base, csr_len); if (!pndev->ioaddr) { printk(KERN_ERR "Spectra: Unable to remap memory region\n"); ret = -ENOMEM; goto failed_remap_csr; } nand_dbg_print(NAND_DBG_DEBUG, "Spectra: CSR 0x%08lx -> 0x%p (0x%lx)\n", csr_base, pndev->ioaddr, csr_len); init_completion(&pndev->complete); nand_dbg_print(NAND_DBG_DEBUG, "Spectra: IRQ %d\n", dev->irq); #if CMD_DMA if (request_irq(dev->irq, cdma_isr, IRQF_SHARED, SPECTRA_NAND_NAME, &info)) { printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); ret = -ENODEV; iounmap(pndev->ioaddr); goto failed_remap_csr; } #else if (request_irq(dev->irq, ddma_isr, IRQF_SHARED, SPECTRA_NAND_NAME, &info)) { printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); ret = -ENODEV; iounmap(pndev->ioaddr); goto failed_remap_csr; } #endif pci_set_drvdata(dev, pndev); ret = GLOB_LLD_Read_Device_ID(); if (ret) { iounmap(pndev->ioaddr); goto failed_remap_csr; } ret = register_spectra_ftl(); if (ret) { iounmap(pndev->ioaddr); goto failed_remap_csr; } return 0; failed_remap_csr: pci_release_regions(dev); failed_req_csr: iounmap(FlashMem); iounmap(FlashReg); failed_disable: pci_disable_device(dev); return ret; } static void nand_pci_remove(struct pci_dev *dev) { struct mrst_nand_info *pndev = pci_get_drvdata(dev); nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); #if CMD_DMA free_irq(dev->irq, pndev); #endif iounmap(pndev->ioaddr); pci_release_regions(dev); pci_disable_device(dev); } MODULE_DEVICE_TABLE(pci, nand_pci_ids); static struct pci_driver nand_pci_driver = { .name = SPECTRA_NAND_NAME, .id_table = nand_pci_ids, .probe = nand_pci_probe, .remove = nand_pci_remove, }; int NAND_Flash_Init(void) { int retval; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); retval = pci_register_driver(&nand_pci_driver); if (retval) return -ENOMEM; return PASS; } /* Free memory */ int nand_release_spectra(void) { pci_unregister_driver(&nand_pci_driver); iounmap(FlashMem); iounmap(FlashReg); return 0; }