![David Woodhouse](/assets/img/avatar_default.png)
It'll take some work before this is really shippable. Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
911 lines
28 KiB
C
911 lines
28 KiB
C
/*
|
|
* 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;
|
|
}
|
|
|
|
|
|
|