| /* |
| * 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; |
| } |
| |
| |
| |