/* * 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 "spectraswconfig.h" #include "lld.h" #include "lld_nand.h" #include "lld_cdma.h" #include "lld_emu.h" #include "flash.h" #include "nand_regs.h" #define MAX_PENDING_CMDS 4 #define MODE_02 (0x2 << 26) /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: CDMA_Data_Cmd * Inputs: cmd code (aligned for hw) * data: pointer to source or destination * block: block address * page: page address * num: num pages to transfer * Outputs: PASS * Description: This function takes the parameters and puts them * into the "pending commands" array. * It does not parse or validate the parameters. * The array index is same as the tag. *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ u16 CDMA_Data_CMD(u8 cmd, u8 *data, u32 block, u16 page, u16 num, u16 flags) { u8 bank; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); if (0 == cmd) nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Illegal cmd (0)\n", __FILE__, __LINE__); /* If a command of another bank comes, then first execute */ /* pending commands of the current bank, then set the new */ /* bank as current bank */ bank = block / (DeviceInfo.wTotalBlocks / totalUsedBanks); if (bank != info.flash_bank) { nand_dbg_print(NAND_DBG_WARN, "Will access new bank. old bank: %d, new bank: %d\n", info.flash_bank, bank); if (CDMA_Execute_CMDs()) { printk(KERN_ERR "CDMA_Execute_CMDs fail!\n"); return FAIL; } info.flash_bank = bank; } info.pcmds[info.pcmds_num].CMD = cmd; info.pcmds[info.pcmds_num].DataAddr = data; info.pcmds[info.pcmds_num].Block = block; info.pcmds[info.pcmds_num].Page = page; info.pcmds[info.pcmds_num].PageCount = num; info.pcmds[info.pcmds_num].DataDestAddr = 0; info.pcmds[info.pcmds_num].DataSrcAddr = 0; info.pcmds[info.pcmds_num].MemCopyByteCnt = 0; info.pcmds[info.pcmds_num].Flags = flags; info.pcmds[info.pcmds_num].Status = 0xB0B; switch (cmd) { case WRITE_MAIN_SPARE_CMD: Conv_Main_Spare_Data_Log2Phy_Format(data, num); break; case WRITE_SPARE_CMD: Conv_Spare_Data_Log2Phy_Format(data); break; default: break; } info.pcmds_num++; if (info.pcmds_num >= MAX_PENDING_CMDS) { if (CDMA_Execute_CMDs()) { printk(KERN_ERR "CDMA_Execute_CMDs fail!\n"); return FAIL; } } return PASS; } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: CDMA_MemCopy_CMD * Inputs: dest: pointer to destination * src: pointer to source * count: num bytes to transfer * Outputs: PASS * Description: This function takes the parameters and puts them * into the "pending commands" array. * It does not parse or validate the parameters. * The array index is same as the tag. *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ u16 CDMA_MemCopy_CMD(u8 *dest, u8 *src, u32 byte_cnt, u16 flags) { nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); info.pcmds[info.pcmds_num].CMD = MEMCOPY_CMD; info.pcmds[info.pcmds_num].DataAddr = 0; info.pcmds[info.pcmds_num].Block = 0; info.pcmds[info.pcmds_num].Page = 0; info.pcmds[info.pcmds_num].PageCount = 0; info.pcmds[info.pcmds_num].DataDestAddr = dest; info.pcmds[info.pcmds_num].DataSrcAddr = src; info.pcmds[info.pcmds_num].MemCopyByteCnt = byte_cnt; info.pcmds[info.pcmds_num].Flags = flags; info.pcmds[info.pcmds_num].Status = 0xB0B; info.pcmds_num++; if (info.pcmds_num >= MAX_PENDING_CMDS) { if (CDMA_Execute_CMDs()) { printk(KERN_ERR "CDMA_Execute_CMDs fail!\n"); return FAIL; } } return PASS; } #if 0 /* Prints the PendingCMDs array */ void print_pending_cmds(void) { u16 i; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); for (i = 0; i < info.pcmds_num; i++) { nand_dbg_print(NAND_DBG_DEBUG, "\ni: %d\n", i); switch (info.pcmds[i].CMD) { case ERASE_CMD: nand_dbg_print(NAND_DBG_DEBUG, "Erase Command (0x%x)\n", info.pcmds[i].CMD); break; case WRITE_MAIN_CMD: nand_dbg_print(NAND_DBG_DEBUG, "Write Main Command (0x%x)\n", info.pcmds[i].CMD); break; case WRITE_MAIN_SPARE_CMD: nand_dbg_print(NAND_DBG_DEBUG, "Write Main Spare Command (0x%x)\n", info.pcmds[i].CMD); break; case READ_MAIN_SPARE_CMD: nand_dbg_print(NAND_DBG_DEBUG, "Read Main Spare Command (0x%x)\n", info.pcmds[i].CMD); break; case READ_MAIN_CMD: nand_dbg_print(NAND_DBG_DEBUG, "Read Main Command (0x%x)\n", info.pcmds[i].CMD); break; case MEMCOPY_CMD: nand_dbg_print(NAND_DBG_DEBUG, "Memcopy Command (0x%x)\n", info.pcmds[i].CMD); break; case DUMMY_CMD: nand_dbg_print(NAND_DBG_DEBUG, "Dummy Command (0x%x)\n", info.pcmds[i].CMD); break; default: nand_dbg_print(NAND_DBG_DEBUG, "Illegal Command (0x%x)\n", info.pcmds[i].CMD); break; } nand_dbg_print(NAND_DBG_DEBUG, "DataAddr: 0x%x\n", (u32)info.pcmds[i].DataAddr); nand_dbg_print(NAND_DBG_DEBUG, "Block: %d\n", info.pcmds[i].Block); nand_dbg_print(NAND_DBG_DEBUG, "Page: %d\n", info.pcmds[i].Page); nand_dbg_print(NAND_DBG_DEBUG, "PageCount: %d\n", info.pcmds[i].PageCount); nand_dbg_print(NAND_DBG_DEBUG, "DataDestAddr: 0x%x\n", (u32)info.pcmds[i].DataDestAddr); nand_dbg_print(NAND_DBG_DEBUG, "DataSrcAddr: 0x%x\n", (u32)info.pcmds[i].DataSrcAddr); nand_dbg_print(NAND_DBG_DEBUG, "MemCopyByteCnt: %d\n", info.pcmds[i].MemCopyByteCnt); nand_dbg_print(NAND_DBG_DEBUG, "Flags: 0x%x\n", info.pcmds[i].Flags); nand_dbg_print(NAND_DBG_DEBUG, "Status: 0x%x\n", info.pcmds[i].Status); } } /* Print the CDMA descriptors */ void print_cdma_descriptors(void) { struct cdma_descriptor *pc; int i; pc = (struct cdma_descriptor *)info.cdma_desc_buf; nand_dbg_print(NAND_DBG_DEBUG, "\nWill dump cdma descriptors:\n"); for (i = 0; i < info.cdma_num; i++) { nand_dbg_print(NAND_DBG_DEBUG, "\ni: %d\n", i); nand_dbg_print(NAND_DBG_DEBUG, "NxtPointerHi: 0x%x, NxtPointerLo: 0x%x\n", pc[i].NxtPointerHi, pc[i].NxtPointerLo); nand_dbg_print(NAND_DBG_DEBUG, "FlashPointerHi: 0x%x, FlashPointerLo: 0x%x\n", pc[i].FlashPointerHi, pc[i].FlashPointerLo); nand_dbg_print(NAND_DBG_DEBUG, "CommandType: 0x%x\n", pc[i].CommandType); nand_dbg_print(NAND_DBG_DEBUG, "MemAddrHi: 0x%x, MemAddrLo: 0x%x\n", pc[i].MemAddrHi, pc[i].MemAddrLo); nand_dbg_print(NAND_DBG_DEBUG, "CommandFlags: 0x%x\n", pc[i].CommandFlags); nand_dbg_print(NAND_DBG_DEBUG, "Channel: %d, Status: 0x%x\n", pc[i].Channel, pc[i].Status); nand_dbg_print(NAND_DBG_DEBUG, "MemCopyPointerHi: 0x%x, MemCopyPointerLo: 0x%x\n", pc[i].MemCopyPointerHi, pc[i].MemCopyPointerLo); nand_dbg_print(NAND_DBG_DEBUG, "Reserved12: 0x%x, Reserved13: 0x%x, " "Reserved14: 0x%x, pcmd: %d\n", pc[i].Reserved12, pc[i].Reserved13, pc[i].Reserved14, pc[i].pcmd); } } /* Print the Memory copy descriptors */ static void print_memcp_descriptors(void) { struct memcpy_descriptor *pm; int i; pm = (struct memcpy_descriptor *)info.memcp_desc_buf; nand_dbg_print(NAND_DBG_DEBUG, "\nWill dump mem_cpy descriptors:\n"); for (i = 0; i < info.cdma_num; i++) { nand_dbg_print(NAND_DBG_DEBUG, "\ni: %d\n", i); nand_dbg_print(NAND_DBG_DEBUG, "NxtPointerHi: 0x%x, NxtPointerLo: 0x%x\n", pm[i].NxtPointerHi, pm[i].NxtPointerLo); nand_dbg_print(NAND_DBG_DEBUG, "SrcAddrHi: 0x%x, SrcAddrLo: 0x%x\n", pm[i].SrcAddrHi, pm[i].SrcAddrLo); nand_dbg_print(NAND_DBG_DEBUG, "DestAddrHi: 0x%x, DestAddrLo: 0x%x\n", pm[i].DestAddrHi, pm[i].DestAddrLo); nand_dbg_print(NAND_DBG_DEBUG, "XferSize: %d\n", pm[i].XferSize); nand_dbg_print(NAND_DBG_DEBUG, "MemCopyFlags: 0x%x\n", pm[i].MemCopyFlags); nand_dbg_print(NAND_DBG_DEBUG, "MemCopyStatus: %d\n", pm[i].MemCopyStatus); nand_dbg_print(NAND_DBG_DEBUG, "reserved9: 0x%x\n", pm[i].reserved9); nand_dbg_print(NAND_DBG_DEBUG, "reserved10: 0x%x\n", pm[i].reserved10); nand_dbg_print(NAND_DBG_DEBUG, "reserved11: 0x%x\n", pm[i].reserved11); nand_dbg_print(NAND_DBG_DEBUG, "reserved12: 0x%x\n", pm[i].reserved12); nand_dbg_print(NAND_DBG_DEBUG, "reserved13: 0x%x\n", pm[i].reserved13); nand_dbg_print(NAND_DBG_DEBUG, "reserved14: 0x%x\n", pm[i].reserved14); nand_dbg_print(NAND_DBG_DEBUG, "reserved15: 0x%x\n", pm[i].reserved15); } } #endif /* Reset cdma_descriptor chain to 0 */ static void reset_cdma_desc(int i) { struct cdma_descriptor *ptr; BUG_ON(i >= MAX_DESCS); ptr = (struct cdma_descriptor *)info.cdma_desc_buf; ptr[i].NxtPointerHi = 0; ptr[i].NxtPointerLo = 0; ptr[i].FlashPointerHi = 0; ptr[i].FlashPointerLo = 0; ptr[i].CommandType = 0; ptr[i].MemAddrHi = 0; ptr[i].MemAddrLo = 0; ptr[i].CommandFlags = 0; ptr[i].Channel = 0; ptr[i].Status = 0; ptr[i].MemCopyPointerHi = 0; ptr[i].MemCopyPointerLo = 0; } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: CDMA_UpdateEventStatus * Inputs: none * Outputs: none * Description: This function update the event status of all the channels * when an error condition is reported. *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ void CDMA_UpdateEventStatus(void) { int i, j, active_chan; struct cdma_descriptor *ptr; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); ptr = (struct cdma_descriptor *)info.cdma_desc_buf; for (j = 0; j < info.cdma_num; j++) { /* Check for the descriptor with failure */ if ((ptr[j].Status & CMD_DMA_DESC_FAIL)) break; } /* All the previous cmd's status for this channel must be good */ for (i = 0; i < j; i++) { if (ptr[i].pcmd != 0xff) info.pcmds[ptr[i].pcmd].Status = CMD_PASS; } /* Abort the channel with type 0 reset command. It resets the */ /* selected channel after the descriptor completes the flash */ /* operation and status has been updated for the descriptor. */ /* Memory Copy and Sync associated with this descriptor will */ /* not be executed */ active_chan = ioread32(FlashReg + CHNL_ACTIVE); if ((active_chan & (1 << info.flash_bank)) == (1 << info.flash_bank)) { iowrite32(MODE_02 | (0 << 4), FlashMem); /* Type 0 reset */ iowrite32((0xF << 4) | info.flash_bank, FlashMem + 0x10); } else { /* Should not reached here */ printk(KERN_ERR "Error! Used bank is not set in" " reg CHNL_ACTIVE\n"); } } static void cdma_trans(u16 chan) { u32 addr; addr = info.cdma_desc; iowrite32(MODE_10 | (chan << 24), FlashMem); iowrite32((1 << 7) | chan, FlashMem + 0x10); iowrite32(MODE_10 | (chan << 24) | ((0x0FFFF & (addr >> 16)) << 8), FlashMem); iowrite32((1 << 7) | (1 << 4) | 0, FlashMem + 0x10); iowrite32(MODE_10 | (chan << 24) | ((0x0FFFF & addr) << 8), FlashMem); iowrite32((1 << 7) | (1 << 5) | 0, FlashMem + 0x10); iowrite32(MODE_10 | (chan << 24), FlashMem); iowrite32((1 << 7) | (1 << 5) | (1 << 4) | 0, FlashMem + 0x10); } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: CDMA_Execute_CMDs (for use with CMD_DMA) * Inputs: tag_count: the number of pending cmds to do * Outputs: PASS/FAIL * Description: Build the SDMA chain(s) by making one CMD-DMA descriptor * for each pending command, start the CDMA engine, and return. *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ u16 CDMA_Execute_CMDs(void) { int i, ret; u64 flash_add; u32 ptr; dma_addr_t map_addr, next_ptr; u16 status = PASS; u16 tmp_c; struct cdma_descriptor *pc; struct memcpy_descriptor *pm; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); /* No pending cmds to execute, just exit */ if (0 == info.pcmds_num) { nand_dbg_print(NAND_DBG_TRACE, "No pending cmds to execute. Just exit.\n"); return PASS; } for (i = 0; i < MAX_DESCS; i++) reset_cdma_desc(i); pc = (struct cdma_descriptor *)info.cdma_desc_buf; pm = (struct memcpy_descriptor *)info.memcp_desc_buf; info.cdma_desc = virt_to_bus(info.cdma_desc_buf); info.memcp_desc = virt_to_bus(info.memcp_desc_buf); next_ptr = info.cdma_desc; info.cdma_num = 0; for (i = 0; i < info.pcmds_num; i++) { if (info.pcmds[i].Block >= DeviceInfo.wTotalBlocks) { info.pcmds[i].Status = CMD_NOT_DONE; continue; } next_ptr += sizeof(struct cdma_descriptor); pc[info.cdma_num].NxtPointerHi = next_ptr >> 16; pc[info.cdma_num].NxtPointerLo = next_ptr & 0xffff; /* Use the Block offset within a bank */ tmp_c = info.pcmds[i].Block / (DeviceInfo.wTotalBlocks / totalUsedBanks); flash_add = (u64)(info.pcmds[i].Block - tmp_c * (DeviceInfo.wTotalBlocks / totalUsedBanks)) * DeviceInfo.wBlockDataSize + (u64)(info.pcmds[i].Page) * DeviceInfo.wPageDataSize; ptr = MODE_10 | (info.flash_bank << 24) | (u32)GLOB_u64_Div(flash_add, DeviceInfo.wPageDataSize); pc[info.cdma_num].FlashPointerHi = ptr >> 16; pc[info.cdma_num].FlashPointerLo = ptr & 0xffff; if ((info.pcmds[i].CMD == WRITE_MAIN_SPARE_CMD) || (info.pcmds[i].CMD == READ_MAIN_SPARE_CMD)) { /* Descriptor to set Main+Spare Access Mode */ pc[info.cdma_num].CommandType = 0x43; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; pc[info.cdma_num].MemAddrHi = 0; pc[info.cdma_num].MemAddrLo = 0; pc[info.cdma_num].Channel = 0; pc[info.cdma_num].Status = 0; pc[info.cdma_num].pcmd = i; info.cdma_num++; BUG_ON(info.cdma_num >= MAX_DESCS); reset_cdma_desc(info.cdma_num); next_ptr += sizeof(struct cdma_descriptor); pc[info.cdma_num].NxtPointerHi = next_ptr >> 16; pc[info.cdma_num].NxtPointerLo = next_ptr & 0xffff; pc[info.cdma_num].FlashPointerHi = ptr >> 16; pc[info.cdma_num].FlashPointerLo = ptr & 0xffff; } switch (info.pcmds[i].CMD) { case ERASE_CMD: pc[info.cdma_num].CommandType = 1; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; pc[info.cdma_num].MemAddrHi = 0; pc[info.cdma_num].MemAddrLo = 0; break; case WRITE_MAIN_CMD: pc[info.cdma_num].CommandType = 0x2100 | info.pcmds[i].PageCount; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; map_addr = virt_to_bus(info.pcmds[i].DataAddr); pc[info.cdma_num].MemAddrHi = map_addr >> 16; pc[info.cdma_num].MemAddrLo = map_addr & 0xffff; break; case READ_MAIN_CMD: pc[info.cdma_num].CommandType = 0x2000 | info.pcmds[i].PageCount; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; map_addr = virt_to_bus(info.pcmds[i].DataAddr); pc[info.cdma_num].MemAddrHi = map_addr >> 16; pc[info.cdma_num].MemAddrLo = map_addr & 0xffff; break; case WRITE_MAIN_SPARE_CMD: pc[info.cdma_num].CommandType = 0x2100 | info.pcmds[i].PageCount; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; map_addr = virt_to_bus(info.pcmds[i].DataAddr); pc[info.cdma_num].MemAddrHi = map_addr >> 16; pc[info.cdma_num].MemAddrLo = map_addr & 0xffff; break; case READ_MAIN_SPARE_CMD: pc[info.cdma_num].CommandType = 0x2000 | info.pcmds[i].PageCount; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; map_addr = virt_to_bus(info.pcmds[i].DataAddr); pc[info.cdma_num].MemAddrHi = map_addr >> 16; pc[info.cdma_num].MemAddrLo = map_addr & 0xffff; break; case MEMCOPY_CMD: pc[info.cdma_num].CommandType = 0xFFFF; /* NOP cmd */ /* Set bit 11 to let the CDMA engine continue to */ /* execute only after it has finished processing */ /* the memcopy descriptor. */ /* Also set bit 10 and bit 9 to 1 */ pc[info.cdma_num].CommandFlags = 0x0E40; map_addr = info.memcp_desc + info.cdma_num * sizeof(struct memcpy_descriptor); pc[info.cdma_num].MemCopyPointerHi = map_addr >> 16; pc[info.cdma_num].MemCopyPointerLo = map_addr & 0xffff; pm[info.cdma_num].NxtPointerHi = 0; pm[info.cdma_num].NxtPointerLo = 0; map_addr = virt_to_bus(info.pcmds[i].DataSrcAddr); pm[info.cdma_num].SrcAddrHi = map_addr >> 16; pm[info.cdma_num].SrcAddrLo = map_addr & 0xffff; map_addr = virt_to_bus(info.pcmds[i].DataDestAddr); pm[info.cdma_num].DestAddrHi = map_addr >> 16; pm[info.cdma_num].DestAddrLo = map_addr & 0xffff; pm[info.cdma_num].XferSize = info.pcmds[i].MemCopyByteCnt; pm[info.cdma_num].MemCopyFlags = (0 << 15 | 0 << 14 | 27 << 8 | 0x40); pm[info.cdma_num].MemCopyStatus = 0; break; case DUMMY_CMD: default: pc[info.cdma_num].CommandType = 0XFFFF; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; pc[info.cdma_num].MemAddrHi = 0; pc[info.cdma_num].MemAddrLo = 0; break; } pc[info.cdma_num].Channel = 0; pc[info.cdma_num].Status = 0; pc[info.cdma_num].pcmd = i; info.cdma_num++; BUG_ON(info.cdma_num >= MAX_DESCS); if ((info.pcmds[i].CMD == WRITE_MAIN_SPARE_CMD) || (info.pcmds[i].CMD == READ_MAIN_SPARE_CMD)) { /* Descriptor to set back Main Area Access Mode */ reset_cdma_desc(info.cdma_num); next_ptr += sizeof(struct cdma_descriptor); pc[info.cdma_num].NxtPointerHi = next_ptr >> 16; pc[info.cdma_num].NxtPointerLo = next_ptr & 0xffff; pc[info.cdma_num].FlashPointerHi = ptr >> 16; pc[info.cdma_num].FlashPointerLo = ptr & 0xffff; pc[info.cdma_num].CommandType = 0x42; pc[info.cdma_num].CommandFlags = (0 << 10) | (1 << 9) | (0 << 8) | 0x40; pc[info.cdma_num].MemAddrHi = 0; pc[info.cdma_num].MemAddrLo = 0; pc[info.cdma_num].Channel = 0; pc[info.cdma_num].Status = 0; pc[info.cdma_num].pcmd = i; info.cdma_num++; BUG_ON(info.cdma_num >= MAX_DESCS); } } /* Add a dummy descriptor at end of the CDMA chain */ reset_cdma_desc(info.cdma_num); ptr = MODE_10 | (info.flash_bank << 24); pc[info.cdma_num].FlashPointerHi = ptr >> 16; pc[info.cdma_num].FlashPointerLo = ptr & 0xffff; pc[info.cdma_num].CommandType = 0xFFFF; /* NOP command */ /* Set Command Flags for the last CDMA descriptor: */ /* set Continue bit (bit 9) to 0 and Interrupt bit (bit 8) to 1 */ pc[info.cdma_num].CommandFlags = (0 << 10) | (0 << 9) | (1 << 8) | 0x40; pc[info.cdma_num].pcmd = 0xff; /* Set it to an illegal value */ info.cdma_num++; BUG_ON(info.cdma_num >= MAX_DESCS); iowrite32(1, FlashReg + GLOBAL_INT_ENABLE); /* Enable Interrupt */ iowrite32(1, FlashReg + DMA_ENABLE); /* Wait for DMA to be enabled before issuing the next command */ while (!(ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; cdma_trans(info.flash_bank); ret = wait_for_completion_timeout(&info.complete, 50 * HZ); if (!ret) printk(KERN_ERR "Wait for completion timeout " "in %s, Line %d\n", __FILE__, __LINE__); status = info.ret; info.pcmds_num = 0; /* Clear the pending cmds number to 0 */ return status; } int is_cdma_interrupt(void) { u32 ints_b0, ints_b1, ints_b2, ints_b3, ints_cdma; u32 int_en_mask; u32 cdma_int_en_mask; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); /* Set the global Enable masks for only those interrupts * that are supported */ cdma_int_en_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); int_en_mask = (INTR_STATUS0__ECC_ERR | INTR_STATUS0__PROGRAM_FAIL | INTR_STATUS0__ERASE_FAIL); ints_b0 = ioread32(FlashReg + INTR_STATUS0) & int_en_mask; ints_b1 = ioread32(FlashReg + INTR_STATUS1) & int_en_mask; ints_b2 = ioread32(FlashReg + INTR_STATUS2) & int_en_mask; ints_b3 = ioread32(FlashReg + INTR_STATUS3) & int_en_mask; ints_cdma = ioread32(FlashReg + DMA_INTR) & cdma_int_en_mask; nand_dbg_print(NAND_DBG_WARN, "ints_bank0 to ints_bank3: " "0x%x, 0x%x, 0x%x, 0x%x, ints_cdma: 0x%x\n", ints_b0, ints_b1, ints_b2, ints_b3, ints_cdma); if (ints_b0 || ints_b1 || ints_b2 || ints_b3 || ints_cdma) { return 1; } else { iowrite32(ints_b0, FlashReg + INTR_STATUS0); iowrite32(ints_b1, FlashReg + INTR_STATUS1); iowrite32(ints_b2, FlashReg + INTR_STATUS2); iowrite32(ints_b3, FlashReg + INTR_STATUS3); nand_dbg_print(NAND_DBG_DEBUG, "Not a NAND controller interrupt! Ignore it.\n"); return 0; } } static void update_event_status(void) { int i; struct cdma_descriptor *ptr; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); ptr = (struct cdma_descriptor *)info.cdma_desc_buf; for (i = 0; i < info.cdma_num; i++) { if (ptr[i].pcmd != 0xff) info.pcmds[ptr[i].pcmd].Status = CMD_PASS; if ((ptr[i].CommandType == 0x41) || (ptr[i].CommandType == 0x42) || (ptr[i].CommandType == 0x43)) continue; switch (info.pcmds[ptr[i].pcmd].CMD) { case READ_MAIN_SPARE_CMD: Conv_Main_Spare_Data_Phy2Log_Format( info.pcmds[ptr[i].pcmd].DataAddr, info.pcmds[ptr[i].pcmd].PageCount); break; case READ_SPARE_CMD: Conv_Spare_Data_Phy2Log_Format( info.pcmds[ptr[i].pcmd].DataAddr); break; } } } static u16 do_ecc_for_desc(u32 ch, u8 *buf, u16 page) { u16 event = EVENT_NONE; u16 err_byte; u16 err_page = 0; u8 err_sector; u8 err_device; u16 ecc_correction_info; u16 err_address; u32 eccSectorSize; u8 *err_pos; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); eccSectorSize = ECC_SECTOR_SIZE * (DeviceInfo.wDevicesConnected); do { if (0 == ch) err_page = ioread32(FlashReg + ERR_PAGE_ADDR0); else if (1 == ch) err_page = ioread32(FlashReg + ERR_PAGE_ADDR1); else if (2 == ch) err_page = ioread32(FlashReg + ERR_PAGE_ADDR2); else if (3 == ch) err_page = ioread32(FlashReg + ERR_PAGE_ADDR3); err_address = ioread32(FlashReg + ECC_ERROR_ADDRESS); err_byte = err_address & ECC_ERROR_ADDRESS__OFFSET; err_sector = ((err_address & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12); ecc_correction_info = ioread32(FlashReg + ERR_CORRECTION_INFO); err_device = ((ecc_correction_info & ERR_CORRECTION_INFO__DEVICE_NR) >> 8); if (ecc_correction_info & ERR_CORRECTION_INFO__ERROR_TYPE) { event = EVENT_UNCORRECTABLE_DATA_ERROR; } else { event = EVENT_CORRECTABLE_DATA_ERROR_FIXED; if (err_byte < ECC_SECTOR_SIZE) { err_pos = buf + (err_page - page) * DeviceInfo.wPageDataSize + err_sector * eccSectorSize + err_byte * DeviceInfo.wDevicesConnected + err_device; *err_pos ^= ecc_correction_info & ERR_CORRECTION_INFO__BYTEMASK; } } } while (!(ecc_correction_info & ERR_CORRECTION_INFO__LAST_ERR_INFO)); return event; } static u16 process_ecc_int(u32 c, u16 *p_desc_num) { struct cdma_descriptor *ptr; u16 j; int event = EVENT_PASS; nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); if (c != info.flash_bank) printk(KERN_ERR "Error!info.flash_bank is %d, while c is %d\n", info.flash_bank, c); ptr = (struct cdma_descriptor *)info.cdma_desc_buf; for (j = 0; j < info.cdma_num; j++) if ((ptr[j].Status & CMD_DMA_DESC_COMP) != CMD_DMA_DESC_COMP) break; *p_desc_num = j; /* Pass the descripter number found here */ if (j >= info.cdma_num) { printk(KERN_ERR "Can not find the correct descriptor number " "when ecc interrupt triggered!" "info.cdma_num: %d, j: %d\n", info.cdma_num, j); return EVENT_UNCORRECTABLE_DATA_ERROR; } event = do_ecc_for_desc(c, info.pcmds[ptr[j].pcmd].DataAddr, info.pcmds[ptr[j].pcmd].Page); if (EVENT_UNCORRECTABLE_DATA_ERROR == event) { printk(KERN_ERR "Uncorrectable ECC error!" "info.cdma_num: %d, j: %d, " "pending cmd CMD: 0x%x, " "Block: 0x%x, Page: 0x%x, PageCount: 0x%x\n", info.cdma_num, j, info.pcmds[ptr[j].pcmd].CMD, info.pcmds[ptr[j].pcmd].Block, info.pcmds[ptr[j].pcmd].Page, info.pcmds[ptr[j].pcmd].PageCount); if (ptr[j].pcmd != 0xff) info.pcmds[ptr[j].pcmd].Status = CMD_FAIL; CDMA_UpdateEventStatus(); } return event; } static void process_prog_erase_fail_int(u16 desc_num) { struct cdma_descriptor *ptr; nand_dbg_print(NAND_DBG_DEBUG, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); ptr = (struct cdma_descriptor *)info.cdma_desc_buf; if (ptr[desc_num].pcmd != 0xFF) info.pcmds[ptr[desc_num].pcmd].Status = CMD_FAIL; CDMA_UpdateEventStatus(); } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& * Function: CDMA_Event_Status (for use with CMD_DMA) * Inputs: none * Outputs: Event_Status code * Description: This function is called after an interrupt has happened * It reads the HW status register and ...tbd * It returns the appropriate event status *&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ u16 CDMA_Event_Status(void) { u32 ints_addr[4] = {INTR_STATUS0, INTR_STATUS1, INTR_STATUS2, INTR_STATUS3}; u32 dma_intr_bit[4] = {DMA_INTR__DESC_COMP_CHANNEL0, DMA_INTR__DESC_COMP_CHANNEL1, DMA_INTR__DESC_COMP_CHANNEL2, DMA_INTR__DESC_COMP_CHANNEL3}; u32 cdma_int_status, int_status; u32 ecc_enable = 0; u16 event = EVENT_PASS; u16 cur_desc = 0; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); ecc_enable = ioread32(FlashReg + ECC_ENABLE); while (1) { int_status = ioread32(FlashReg + ints_addr[info.flash_bank]); if (ecc_enable && (int_status & INTR_STATUS0__ECC_ERR)) { event = process_ecc_int(info.flash_bank, &cur_desc); iowrite32(INTR_STATUS0__ECC_ERR, FlashReg + ints_addr[info.flash_bank]); if (EVENT_UNCORRECTABLE_DATA_ERROR == event) { nand_dbg_print(NAND_DBG_WARN, "ints_bank0 to ints_bank3: " "0x%x, 0x%x, 0x%x, 0x%x, " "ints_cdma: 0x%x\n", ioread32(FlashReg + INTR_STATUS0), ioread32(FlashReg + INTR_STATUS1), ioread32(FlashReg + INTR_STATUS2), ioread32(FlashReg + INTR_STATUS3), ioread32(FlashReg + DMA_INTR)); break; } } else if (int_status & INTR_STATUS0__PROGRAM_FAIL) { printk(KERN_ERR "NAND program fail interrupt!\n"); process_prog_erase_fail_int(cur_desc); event = EVENT_PROGRAM_FAILURE; break; } else if (int_status & INTR_STATUS0__ERASE_FAIL) { printk(KERN_ERR "NAND erase fail interrupt!\n"); process_prog_erase_fail_int(cur_desc); event = EVENT_ERASE_FAILURE; break; } else { cdma_int_status = ioread32(FlashReg + DMA_INTR); if (cdma_int_status & dma_intr_bit[info.flash_bank]) { iowrite32(dma_intr_bit[info.flash_bank], FlashReg + DMA_INTR); update_event_status(); event = EVENT_PASS; break; } } } int_status = ioread32(FlashReg + ints_addr[info.flash_bank]); iowrite32(int_status, FlashReg + ints_addr[info.flash_bank]); cdma_int_status = ioread32(FlashReg + DMA_INTR); iowrite32(cdma_int_status, FlashReg + DMA_INTR); iowrite32(0, FlashReg + DMA_ENABLE); while ((ioread32(FlashReg + DMA_ENABLE) & DMA_ENABLE__FLAG)) ; return event; }