5745 lines
143 KiB
C
Executable File
5745 lines
143 KiB
C
Executable File
/******************************************************************************
|
|
* mt6573_nand.c - MT6573 NAND Flash Device Driver
|
|
*
|
|
* Copyright 2009-2010 MediaTek Co.,Ltd.
|
|
*
|
|
* DESCRIPTION:
|
|
* This file provid the other drivers nand relative functions
|
|
*
|
|
* modification history
|
|
* ----------------------------------------
|
|
* v2.0, 11 Feb 2010, mtk02528 written
|
|
* ----------------------------------------
|
|
******************************************************************************/
|
|
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/types.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/nand.h>
|
|
#include <linux/mtd/partitions.h>
|
|
#include <linux/mtd/nand_ecc.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/time.h>
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/version.h>
|
|
|
|
//#include <mach/mt6573.h>
|
|
// #include <mach/mtk_nand_device.h>
|
|
//#include <mach/dma.h>
|
|
//#include <mach/mt6573_devs.h>
|
|
//#include <mach/mt6573_reg_base.h>
|
|
#include "mt6573_typedefs.h"
|
|
/* Koshi for mt6573 porting */
|
|
//#include <mach/mt6573_pll.h>
|
|
/*JL*/
|
|
//#include <mach/mt6573_nand.h>
|
|
#include "mt6573_nand.h"
|
|
/* Koshi for mt6573 porting */
|
|
#include "../econet/bmt.h"
|
|
//#include "bmt.h"
|
|
// #include <mach/partition.h>
|
|
|
|
/* Added for TCM used */
|
|
//#include <asm/tcm.h>
|
|
//#include <asm/system.h>
|
|
//#include "partition_define.h"
|
|
#include "nand_devicelist.h"
|
|
//#include "nand_customer.h"
|
|
#include <linux/mtd/map.h>
|
|
#include <asm/tc3162/tc3162.h>
|
|
|
|
#if defined (TCSUPPORT_GPON_DUAL_IMAGE) || defined (TCSUPPORT_EPON_DUAL_IMAGE)
|
|
#include "flash_layout/tc_partition.h"
|
|
#endif
|
|
|
|
#define VERSION "v2.0"
|
|
#define MODULE_NAME "# MT6573 NAND #"
|
|
#define PROCNAME "driver/nand"
|
|
#define PROCNAME_ECC "driver/nand_ecc"
|
|
|
|
|
|
//#define NAND_ECC_TEST
|
|
//#define USE_AHB_MODE 1
|
|
BOOL g_bUseAHBMode=true;
|
|
//#if USE_AHB_MODE
|
|
BOOL g_bAutoFMT=true;
|
|
BOOL g_bHwEcc=true;
|
|
BOOL g_bOOB_Test=false;
|
|
BOOL g_bReadEraseStatus=false;
|
|
#ifdef NAND_ECC_TEST
|
|
int g_hw_ecc_bit = 4;
|
|
int g_spare_size = 16;
|
|
#endif
|
|
//#else
|
|
//BOOL g_bHwEcc=false;
|
|
//#endif
|
|
#define BMT_BAD_BLOCK_INDEX_OFFSET (1)
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
#define POOL_GOOD_BLOCK_PERCENT 8/100
|
|
#define SLAVE_IMAGE_OFFSET 0xf00000
|
|
static int bmt_pool_size = 0;
|
|
static bmt_struct *g_bmt = NULL;
|
|
static init_bbt_struct *g_bbt = NULL;
|
|
extern int nand_logic_size;
|
|
extern int nand_flash_avalable_size;
|
|
#endif
|
|
|
|
#define _MTK_NAND_DUMMY_DRIVER_
|
|
|
|
#ifndef NAND_OTP_SUPPORT
|
|
#define NAND_OTP_SUPPORT 0
|
|
#endif
|
|
|
|
#if NAND_OTP_SUPPORT
|
|
|
|
#define SAMSUNG_OTP_SUPPORT 1
|
|
#define OTP_MAGIC_NUM 0x4E3AF28B
|
|
#define SAMSUNG_OTP_PAGE_NUM 6
|
|
|
|
static const unsigned int Samsung_OTP_Page[SAMSUNG_OTP_PAGE_NUM] = {0x15, 0x16, 0x17, 0x18, 0x19, 0x1b};
|
|
|
|
static struct mt6573_otp_config g_mt6573_otp_fuc;
|
|
static spinlock_t g_OTPLock;
|
|
|
|
#define OTP_MAGIC 'k'
|
|
|
|
/* NAND OTP IO control number */
|
|
#define OTP_GET_LENGTH _IOW(OTP_MAGIC, 1, int)
|
|
#define OTP_READ _IOW(OTP_MAGIC, 2, int)
|
|
#define OTP_WRITE _IOW(OTP_MAGIC, 3, int)
|
|
|
|
#define FS_OTP_READ 0
|
|
#define FS_OTP_WRITE 1
|
|
|
|
/* NAND OTP Error codes */
|
|
#define OTP_SUCCESS 0
|
|
#define OTP_ERROR_OVERSCOPE -1
|
|
#define OTP_ERROR_TIMEOUT -2
|
|
#define OTP_ERROR_BUSY -3
|
|
#define OTP_ERROR_NOMEM -4
|
|
#define OTP_ERROR_RESET -5
|
|
|
|
#endif
|
|
|
|
#if NAND_OTP_SUPPORT
|
|
struct mt6573_otp_config
|
|
{
|
|
u32 (* OTPRead) (u32 PageAddr, void *BufferPtr, void *SparePtr);
|
|
u32 (* OTPWrite) (u32 PageAddr, void *BufferPtr, void *SparePtr);
|
|
u32 (* OTPQueryLength) (u32 *Length);
|
|
};
|
|
|
|
struct otp_ctl
|
|
{
|
|
unsigned int QLength;
|
|
unsigned int Offset;
|
|
unsigned int Length;
|
|
char *BufferPtr;
|
|
unsigned int status;
|
|
};
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* Macro definition
|
|
*******************************************************************************/
|
|
//#define NFI_SET_REG32(reg, value) (DRV_WriteReg32(reg, DRV_Reg32(reg) | (value)))
|
|
//#define NFI_SET_REG16(reg, value) (DRV_WriteReg16(reg, DRV_Reg16(reg) | (value)))
|
|
//#define NFI_CLN_REG32(reg, value) (DRV_WriteReg32(reg, DRV_Reg32(reg) & (~(value))))
|
|
//#define NFI_CLN_REG16(reg, value) (DRV_WriteReg16(reg, DRV_Reg16(reg) & (~(value))))
|
|
|
|
#define NFI_SET_REG32(reg, value) \
|
|
do { \
|
|
g_value = (DRV_Reg32(reg) | (value));\
|
|
DRV_WriteReg32(reg, g_value); \
|
|
} while(0)
|
|
|
|
#define NFI_SET_REG16(reg, value) \
|
|
do { \
|
|
g_value = (DRV_Reg16(reg) | (value));\
|
|
DRV_WriteReg16(reg, g_value); \
|
|
} while(0)
|
|
|
|
#define NFI_CLN_REG32(reg, value) \
|
|
do { \
|
|
g_value = (DRV_Reg32(reg) & (~(value)));\
|
|
DRV_WriteReg32(reg, g_value); \
|
|
} while(0)
|
|
|
|
#define NFI_CLN_REG16(reg, value) \
|
|
do { \
|
|
g_value = (DRV_Reg16(reg) & (~(value)));\
|
|
DRV_WriteReg16(reg, g_value); \
|
|
} while(0)
|
|
|
|
#define PIO_BIG_ENDIAN (DRV_Reg16(NFI_CNFG_REG16) & CNFG_PIO_BIG_ENDIAN)
|
|
|
|
|
|
#define NFI_WAIT_STATE_DONE(state) do{;}while (__raw_readl(NFI_STA_REG32) & state)
|
|
#define NFI_WAIT_TO_READY() do{;}while (!(__raw_readl(NFI_STA_REG32) & STA_BUSY2READY))
|
|
|
|
|
|
#define NAND_SECTOR_SIZE (512)
|
|
#define OOB_PER_SECTOR (16)
|
|
#define OOB_AVAI_PER_SECTOR (8)
|
|
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
#define BMT_POOL_SIZE (80)
|
|
#else
|
|
#define BMT_POOL_SIZE (0)
|
|
#endif
|
|
|
|
#define PMT_POOL_SIZE (2)
|
|
/*******************************************************************************
|
|
* Gloable Varible Definition
|
|
*******************************************************************************/
|
|
#ifdef NAND_PFM
|
|
static suseconds_t g_PFM_R = 0;
|
|
static suseconds_t g_PFM_W = 0;
|
|
static suseconds_t g_PFM_E = 0;
|
|
static u32 g_PFM_RNum = 0;
|
|
static u32 g_PFM_RD = 0;
|
|
static u32 g_PFM_WD = 0;
|
|
static struct timeval g_now;
|
|
|
|
#define PFM_BEGIN(time) \
|
|
do_gettimeofday(&g_now); \
|
|
(time) = g_now;
|
|
|
|
#define PFM_END_R(time, n) \
|
|
do_gettimeofday(&g_now); \
|
|
g_PFM_R += (g_now.tv_sec * 1000000 + g_now.tv_usec) - (time.tv_sec * 1000000 + time.tv_usec); \
|
|
g_PFM_RNum += 1; \
|
|
g_PFM_RD += n; \
|
|
MSG(PERFORMANCE, "%s - Read PFM: %lu, data: %d, ReadOOB: %d (%d, %d)\n", MODULE_NAME , g_PFM_R, g_PFM_RD, g_kCMD.pureReadOOB, g_kCMD.pureReadOOBNum, g_PFM_RNum);
|
|
|
|
#define PFM_END_W(time, n) \
|
|
do_gettimeofday(&g_now); \
|
|
g_PFM_W += (g_now.tv_sec * 1000000 + g_now.tv_usec) - (time.tv_sec * 1000000 + time.tv_usec); \
|
|
g_PFM_WD += n; \
|
|
MSG(PERFORMANCE, "%s - Write PFM: %lu, data: %d\n", MODULE_NAME, g_PFM_W, g_PFM_WD);
|
|
|
|
#define PFM_END_E(time) \
|
|
do_gettimeofday(&g_now); \
|
|
g_PFM_E += (g_now.tv_sec * 1000000 + g_now.tv_usec) - (time.tv_sec * 1000000 + time.tv_usec); \
|
|
MSG(PERFORMANCE, "%s - Erase PFM: %lu\n", MODULE_NAME, g_PFM_E);
|
|
#else
|
|
#define PFM_BEGIN(time)
|
|
#define PFM_END_R(time, n)
|
|
#define PFM_END_W(time, n)
|
|
#define PFM_END_E(time)
|
|
#endif
|
|
|
|
/* MT6573 NAND Driver */
|
|
struct mt6573_nand_host_hw mt6573_nand_hw = {
|
|
.nfi_bus_width = 8,
|
|
.nfi_access_timing = NFI_DEFAULT_ACCESS_TIMING,
|
|
.nfi_cs_num = NFI_CS_NUM,
|
|
.nand_sec_size = 512,
|
|
.nand_sec_shift = 9,
|
|
.nand_ecc_size = 2048,
|
|
.nand_ecc_bytes = 32,
|
|
.nand_ecc_mode = NAND_ECC_NONE,
|
|
};
|
|
|
|
#ifdef TCSUPPORT_DUAL_IMAGE_ENHANCE
|
|
extern int offset;
|
|
#endif
|
|
|
|
|
|
static struct mtd_partition mt6573_partitions[] = {
|
|
{ /* First partition */
|
|
name : "NAND Flash", /* Section */
|
|
size : 0x0, /* Size */
|
|
offset : 0 /* Offset from start of flash- location 0x0*/
|
|
},
|
|
};
|
|
|
|
|
|
#define TIMEOUT_1 0x1fff
|
|
#define TIMEOUT_2 0x8ff
|
|
#define TIMEOUT_3 0xffff
|
|
#define TIMEOUT_4 5000 //PIO
|
|
|
|
#define NFI_ISSUE_COMMAND(cmd, col_addr, row_addr, col_num, row_num) \
|
|
do { \
|
|
DRV_WriteReg(NFI_CMD_REG16,cmd);\
|
|
while (DRV_Reg32(NFI_STA_REG32) & STA_CMD_STATE);\
|
|
DRV_WriteReg32(NFI_COLADDR_REG32, col_addr);\
|
|
DRV_WriteReg32(NFI_ROWADDR_REG32, row_addr);\
|
|
DRV_WriteReg(NFI_ADDRNOB_REG16, col_num | (row_num<<ADDR_ROW_NOB_SHIFT));\
|
|
while (DRV_Reg32(NFI_STA_REG32) & STA_ADDR_STATE);\
|
|
}while(0);
|
|
|
|
//-------------------------------------------------------------------------------
|
|
static struct completion g_comp_AHB_Done;
|
|
static struct mt6573_CMD g_kCMD;
|
|
static u32 g_u4ChipVer;
|
|
bool g_bInitDone;
|
|
static int g_i4Interrupt;
|
|
static bool g_bcmdstatus;
|
|
static u32 g_value = 0;
|
|
static int g_page_size;
|
|
|
|
static u8 *local_buffer_16_align; // 16 byte aligned buffer, for HW issue
|
|
static u8 local_buffer[4096+16];
|
|
|
|
extern void nand_release_device(struct mtd_info *mtd);
|
|
//extern int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state);
|
|
extern int nand_get_device(struct mtd_info *mtd, int new_state);
|
|
|
|
|
|
extern unsigned int (*ranand_read_byte)(unsigned long long);
|
|
extern unsigned int (*ranand_read_dword)(unsigned long long);
|
|
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
static bmt_struct *g_bmt;
|
|
#endif
|
|
struct mt6573_nand_host *host;
|
|
|
|
//extern struct mtd_partition g_pasStatic_Partition[] ;
|
|
//extern int part_num;
|
|
#ifdef PMT
|
|
extern void part_init_pmt(struct mtd_info *mtd, u8 *buf);
|
|
extern struct mtd_partition g_exist_Partition[] ;
|
|
#endif
|
|
int manu_id;
|
|
int dev_id;
|
|
|
|
static u8 local_oob_buf[234];
|
|
|
|
#ifdef _MTK_NAND_DUMMY_DRIVER_
|
|
int dummy_driver_debug;
|
|
#endif
|
|
|
|
|
|
void dump_buf(uint8_t *buf, int len)
|
|
{
|
|
int i;
|
|
printk("len=%d\n",len);
|
|
|
|
for(i = 0; i < (len); i++){
|
|
if (i>0 && !(i%16)) {
|
|
printk("\n");
|
|
}
|
|
printk("%x ", *buf++);
|
|
}
|
|
|
|
printk("\n");
|
|
}
|
|
|
|
void nand_enable_clock(void)
|
|
{
|
|
//(void)hwEnableClock(MT65XX_PDN_PERI_NFI, "NAND");
|
|
}
|
|
|
|
void nand_disable_clock(void)
|
|
{
|
|
//(void)hwDisableClock(MT65XX_PDN_PERI_NFI, "NAND");
|
|
}
|
|
|
|
static struct nand_ecclayout nand_oob_16 = {
|
|
.eccbytes = 8,
|
|
.eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
|
|
.oobfree = {{4,3}, {0, 0}}
|
|
};
|
|
|
|
struct nand_ecclayout nand_oob_64 = {
|
|
.eccbytes = 32,
|
|
.eccpos = {32, 33, 34, 35, 36, 37, 38, 39,
|
|
40, 41, 42, 43, 44, 45, 46, 47,
|
|
48, 49, 50, 51, 52, 53, 54, 55,
|
|
56, 57, 58, 59, 60, 61, 62, 63},
|
|
.oobfree = {{4, 4}, {9, 7}, {17, 7}, {25, 6}, {0, 0}}
|
|
};
|
|
|
|
struct nand_ecclayout nand_oob_128 = {
|
|
.eccbytes = 64,
|
|
.eccpos = {
|
|
64, 65, 66, 67, 68, 69, 70, 71,
|
|
72, 73, 74, 75, 76, 77, 78, 79,
|
|
80, 81, 82, 83, 84, 85, 86, 86,
|
|
88, 89, 90, 91, 92, 93, 94, 95,
|
|
96, 97, 98, 99, 100, 101, 102, 103,
|
|
104, 105, 106, 107, 108, 109, 110, 111,
|
|
112, 113, 114, 115, 116, 117, 118, 119,
|
|
120, 121, 122, 123, 124, 125, 126, 127},
|
|
.oobfree = {{4, 4}, {9, 7}, {17, 7}, {25, 7}, {33, 7}, {41, 7}, {49, 7}, {57, 6}}
|
|
};
|
|
|
|
flashdev_info devinfo;
|
|
|
|
#ifdef TCSUPPORT_AUTOBENCH
|
|
flashdev_info *nandDevInfo = &devinfo;
|
|
EXPORT_SYMBOL(nandDevInfo);
|
|
#endif
|
|
|
|
static void mt6573_nand_configure_fdm(u16 u2FDMSize);
|
|
|
|
|
|
void dump_nfi(void)
|
|
{
|
|
#if __DEBUG_NAND
|
|
printk(KERN_INFO "NFI_ACCCON: 0x%x\n", DRV_Reg32(NFI_ACCCON_REG32));
|
|
printk(KERN_INFO "NFI_PAGEFMT: 0x%x\n", DRV_Reg16(NFI_PAGEFMT_REG16));
|
|
printk(KERN_INFO "NFI_CNFG: 0x%x\n", DRV_Reg16(NFI_CNFG_REG16));
|
|
printk(KERN_INFO "NFI_CON: 0x%x\n", DRV_Reg16(NFI_CON_REG16));
|
|
printk(KERN_INFO "NFI_STRDATA: 0x%x\n", DRV_Reg16(NFI_STRDATA_REG16));
|
|
printk(KERN_INFO "NFI_ADDRCNTR: 0x%x\n", DRV_Reg16(NFI_ADDRCNTR_REG16));
|
|
printk(KERN_INFO "NFI_FIFOSTA: 0x%x\n", DRV_Reg16(NFI_FIFOSTA_REG16));
|
|
printk(KERN_INFO "NFI_ADDRNOB: 0x%x\n", DRV_Reg16(NFI_ADDRNOB_REG16));
|
|
printk(KERN_INFO "NFI_FDM_0L: 0x%x\n", DRV_Reg32(NFI_FDM0L_REG32));
|
|
printk(KERN_INFO "NFI_STA: 0x%x\n", DRV_Reg32(NFI_STA_REG32));
|
|
printk(KERN_INFO "NFI_FDM_0M: 0x%x\n", DRV_Reg32(NFI_FDM0M_REG32));
|
|
printk(KERN_INFO "NFI_IOCON: 0x%x\n", DRV_Reg16(NFI_IOCON_REG16));
|
|
printk(KERN_INFO "NFI_BYTELEN: 0x%x\n", DRV_Reg16(NFI_BYTELEN_REG16));
|
|
printk(KERN_INFO "NFI_COLADDR: 0x%x\n", DRV_Reg32(NFI_COLADDR_REG32));
|
|
printk(KERN_INFO "NFI_ROWADDR: 0x%x\n", DRV_Reg32(NFI_ROWADDR_REG32));
|
|
printk(KERN_INFO "ECC_ENCCNFG: 0x%x\n", DRV_Reg32(ECC_ENCCNFG_REG32));
|
|
printk(KERN_INFO "ECC_ENCCON: 0x%x\n", DRV_Reg16(ECC_ENCCON_REG16));
|
|
printk(KERN_INFO "ECC_DECCNFG: 0x%x\n", DRV_Reg32(ECC_DECCNFG_REG32));
|
|
printk(KERN_INFO "ECC_DECCON: 0x%x\n", DRV_Reg16(ECC_DECCON_REG16));
|
|
printk(KERN_INFO "NFI_CSEL: 0x%x\n", DRV_Reg16(NFI_CSEL_REG16));
|
|
//ECC
|
|
|
|
printk(KERN_INFO "NFI_STRADDR: 0x%x\n", DRV_Reg32(NFI_STRADDR_REG32));
|
|
printk(KERN_INFO "ECC_DECDIADDR: 0x%x\n", DRV_Reg32(ECC_DECDIADDR_REG32));
|
|
printk(KERN_INFO "ECC_FDMADDR_REG32: 0x%x\n", DRV_Reg32(ECC_FDMADDR_REG32));
|
|
printk(KERN_INFO "ECC_DECFSM_REG32: 0x%x\n", DRV_Reg32(ECC_DECFSM_REG32));
|
|
printk(KERN_INFO "ECC_SYNSTA_REG32: 0x%x\n", DRV_Reg32(ECC_SYNSTA_REG32));
|
|
printk(KERN_INFO "ECC_DECNFIDI_REG32: 0x%x\n", DRV_Reg32(ECC_DECNFIDI_REG32));
|
|
printk(KERN_INFO "ECC_SYN0_REG32: 0x%x\n", DRV_Reg32(ECC_SYN0_REG32));
|
|
// printk(KERN_INFO "NFI clock register: 0x%x: %s\n", DRV_Reg32((volatile u32 *)0x00000000),
|
|
// (DRV_Reg32((volatile u32 *)0xF0039300) & (1 << 17)) ? "miss" : "OK");
|
|
#endif
|
|
}
|
|
|
|
|
|
bool get_device_info(u16 id, u32 ext_id, flashdev_info *pdevinfo)
|
|
{
|
|
u32 index;
|
|
for(index=0;gen_FlashTable[index].id!=0;index++)
|
|
{
|
|
//if(id==gen_FlashTable[index].id && ext_id == gen_FlashTable[index].ext_id)
|
|
if (id == gen_FlashTable[index].id)
|
|
{
|
|
ext_id = ((gen_FlashTable[index].ext_id&0xFF)==0xFF)?(ext_id|0xFF) : ext_id;
|
|
if(ext_id == gen_FlashTable[index].ext_id){
|
|
pdevinfo->id = gen_FlashTable[index].id;
|
|
pdevinfo->ext_id = gen_FlashTable[index].ext_id;
|
|
pdevinfo->blocksize = gen_FlashTable[index].blocksize;
|
|
pdevinfo->addr_cycle = gen_FlashTable[index].addr_cycle;
|
|
pdevinfo->iowidth = gen_FlashTable[index].iowidth;
|
|
pdevinfo->timmingsetting = gen_FlashTable[index].timmingsetting;
|
|
pdevinfo->advancedmode = gen_FlashTable[index].advancedmode;
|
|
pdevinfo->pagesize = gen_FlashTable[index].pagesize;
|
|
pdevinfo->totalsize = gen_FlashTable[index].totalsize;
|
|
memcpy(pdevinfo->devciename,gen_FlashTable[index].devciename,sizeof(pdevinfo->devciename));
|
|
printk(KERN_INFO "Device found in MTK table, ID: %x\n", id);
|
|
|
|
goto find;
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
for (index = 0; cust_FlashTable[index].id != 0; index++)
|
|
{
|
|
if (id == cust_FlashTable[index].id)
|
|
{
|
|
pdevinfo->id = cust_FlashTable[index].id;
|
|
pdevinfo->blocksize = cust_FlashTable[index].blocksize;
|
|
pdevinfo->addr_cycle = cust_FlashTable[index].addr_cycle;
|
|
pdevinfo->iowidth = cust_FlashTable[index].iowidth;
|
|
pdevinfo->timmingsetting = cust_FlashTable[index].timmingsetting;
|
|
pdevinfo->advancedmode = cust_FlashTable[index].advancedmode;
|
|
pdevinfo->pagesize = cust_FlashTable[index].pagesize;
|
|
pdevinfo->totalsize = cust_FlashTable[index].totalsize;
|
|
memcpy(pdevinfo->devciename, cust_FlashTable[index].devciename, sizeof(pdevinfo->devciename));
|
|
printk(KERN_INFO "Device found in customer table, ID: %x\n", id);
|
|
|
|
goto find;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
find:
|
|
if(0==pdevinfo->id)
|
|
{
|
|
printk(KERN_INFO "Device not found, ID: %x\n", id);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
/******************************************************************************
|
|
* mt6573_nand_irq_handler
|
|
*
|
|
* DESCRIPTION:
|
|
* NAND interrupt handler!
|
|
*
|
|
* PARAMETERS:
|
|
* int irq
|
|
* void *dev_id
|
|
*
|
|
* RETURNS:
|
|
* IRQ_HANDLED : Successfully handle the IRQ
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
/* Modified for TCM used */
|
|
//static __tcmfunc irqreturn_t mt6573_nand_irq_handler(int irqno, void *dev_id)
|
|
static irqreturn_t mt6573_nand_irq_handler(int irqno, void *dev_id)
|
|
{
|
|
u16 u16IntStatus = DRV_Reg16(NFI_INTR_REG16);
|
|
(void)irqno;
|
|
|
|
if (u16IntStatus & (u16)INTR_AHB_DONE_EN)
|
|
{
|
|
complete(&g_comp_AHB_Done);
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* ECC_Config
|
|
*
|
|
* DESCRIPTION:
|
|
* Configure HW ECC!
|
|
*
|
|
* PARAMETERS:
|
|
* struct mt6573_nand_host_hw *hw
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int ECC_Config(struct mt6573_nand_host_hw *hw)
|
|
{
|
|
u32 u4ENCODESize;
|
|
u32 u4DECODESize;
|
|
|
|
u32 timeout = 0xFFFF;
|
|
|
|
DRV_WriteReg16(ECC_DECCON_REG16, DEC_DE);
|
|
do{
|
|
timeout--;
|
|
if(timeout == 0){
|
|
printk("NFI ECC Config: ECC_DECIDLE timeout\n");
|
|
return -1;
|
|
}
|
|
}while (!DRV_Reg16(ECC_DECIDLE_REG16));
|
|
|
|
DRV_WriteReg16(ECC_ENCCON_REG16, ENC_DE);
|
|
do{;}while (!DRV_Reg32(ECC_ENCIDLE_REG32));
|
|
|
|
/* setup FDM register base */
|
|
DRV_WriteReg32(ECC_FDMADDR_REG32, NFI_FDM0L_REG32);
|
|
|
|
/* Sector + FDM */
|
|
u4ENCODESize = (hw->nand_sec_size + 8) << 3;
|
|
/* Sector + FDM + YAFFS2 meta data bits */
|
|
u4DECODESize = ((hw->nand_sec_size + 8) << 3) + 4 * 13;
|
|
|
|
/* configure ECC decoder && encoder*/
|
|
DRV_WriteReg32(ECC_DECCNFG_REG32,
|
|
ECC_CNFG_ECC4|DEC_CNFG_NFI|DEC_CNFG_EMPTY_EN|DEC_CNFG_BURST_EN |
|
|
(u4DECODESize << DEC_CNFG_CODE_SHIFT));
|
|
|
|
DRV_WriteReg32(ECC_ENCCNFG_REG32,
|
|
ECC_CNFG_ECC4|ENC_CNFG_NFI|
|
|
(u4ENCODESize << ENC_CNFG_MSG_SHIFT));
|
|
|
|
|
|
//#if USE_AHB_MODE
|
|
if(g_bUseAHBMode)
|
|
NFI_SET_REG32(ECC_DECCNFG_REG32, DEC_CNFG_CORRECT);
|
|
else
|
|
NFI_SET_REG32(ECC_DECCNFG_REG32, DEC_CNFG_EL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NAND_ECC_TEST
|
|
static void ECC_Config_All_Bits(int hw_ecc_bit)
|
|
{
|
|
int ecc_bit, ecc_bytes, spare_size, fdm_meta_size;
|
|
u32 u4ENCODESize;
|
|
u32 u4DECODESize;
|
|
u32 ecc_conf;
|
|
|
|
if(hw_ecc_bit < 4 || hw_ecc_bit > 16){
|
|
printk("hw_ecc_bit is out of range:%d\n", hw_ecc_bit);
|
|
printk("hw_ecc_bit should be 4,6,8,10,12,14,16\n");
|
|
return;
|
|
}
|
|
|
|
//spare_size = 16 * (devinfo.pagesize / 512);
|
|
spare_size = g_spare_size;
|
|
|
|
ecc_bit = hw_ecc_bit*13;
|
|
ecc_bytes = (ecc_bit + (1<<3)-1)>>3;
|
|
|
|
if(ecc_bytes > spare_size)
|
|
{
|
|
printk("ecc_bytes:%d > spare_size:%d\n", ecc_bytes, spare_size);
|
|
return;
|
|
}
|
|
|
|
fdm_meta_size = spare_size - ecc_bytes;
|
|
if(fdm_meta_size > 8)
|
|
{
|
|
fdm_meta_size = 8;
|
|
}
|
|
printk("ECC_Config_All_Bits:spare_size=%d,ecc_bytes=%d, fdm_meta_size=%d\n",spare_size, ecc_bytes, fdm_meta_size);
|
|
g_hw_ecc_bit = hw_ecc_bit;
|
|
|
|
ecc_conf = (hw_ecc_bit - 4)/2;
|
|
|
|
NFI_CLN_REG32(ECC_DECCNFG_REG32, ECC_CNFG_ECC_MASK);
|
|
NFI_SET_REG32(ECC_DECCNFG_REG32, ecc_conf);
|
|
|
|
NFI_CLN_REG32(ECC_ENCCNFG_REG32, ECC_CNFG_ECC_MASK);
|
|
NFI_SET_REG32(ECC_ENCCNFG_REG32, ecc_conf);
|
|
|
|
|
|
mt6573_nand_configure_fdm(fdm_meta_size); //FDM SIZE=FDM ECC SIZE
|
|
|
|
/*Configure spare size*/
|
|
NFI_CLN_REG16(NFI_PAGEFMT_REG16, PAGEFMT_SPARE_MASK);
|
|
|
|
switch(g_spare_size)
|
|
{
|
|
case 16:
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT);
|
|
break;
|
|
case 26:
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, PAGEFMT_SPARE_26 << PAGEFMT_SPARE_SHIFT);
|
|
break;
|
|
case 27:
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, PAGEFMT_SPARE_27 << PAGEFMT_SPARE_SHIFT);
|
|
break;
|
|
case 28:
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, PAGEFMT_SPARE_28 << PAGEFMT_SPARE_SHIFT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
u4ENCODESize = (fdm_meta_size<<3)+(1<<(9+3));
|
|
|
|
|
|
printk("ECC_Config_All_Bits:u4ENCODESize=%x\n",u4ENCODESize);
|
|
/* Set-up ECC encode message size = data + FDM_ECC */
|
|
NFI_CLN_REG32(ECC_ENCCNFG_REG32, ENC_CNFG_MSG_MASK);
|
|
NFI_SET_REG32(ECC_ENCCNFG_REG32, u4ENCODESize << ENC_CNFG_MSG_SHIFT);
|
|
|
|
|
|
/* Set-up ECC decode message size = ECC message + T*13 */
|
|
u4DECODESize = u4ENCODESize + ecc_bit;
|
|
printk("ECC_Config_All_Bits:u4DECODESize=%x\n",u4DECODESize);
|
|
NFI_CLN_REG32(ECC_DECCNFG_REG32, DEC_CNFG_CODE_MASK);
|
|
NFI_SET_REG32(ECC_DECCNFG_REG32, u4DECODESize << DEC_CNFG_CODE_SHIFT);
|
|
|
|
dump_nfi();
|
|
return;
|
|
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* ECC_Decode_Start
|
|
*
|
|
* DESCRIPTION:
|
|
* HW ECC Decode Start !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void ECC_Decode_Start(void)
|
|
{
|
|
/* wait for device returning idle */
|
|
while(!(DRV_Reg16(ECC_DECIDLE_REG16) & DEC_IDLE));
|
|
DRV_WriteReg16(ECC_DECCON_REG16, DEC_EN);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* ECC_Decode_End
|
|
*
|
|
* DESCRIPTION:
|
|
* HW ECC Decode End !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void ECC_Decode_End(void)
|
|
{
|
|
/* wait for device returning idle */
|
|
while(!(DRV_Reg16(ECC_DECIDLE_REG16) & DEC_IDLE));
|
|
DRV_WriteReg16(ECC_DECCON_REG16, DEC_DE);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* ECC_Encode_Start
|
|
*
|
|
* DESCRIPTION:
|
|
* HW ECC Encode Start !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void ECC_Encode_Start(void)
|
|
{
|
|
/* wait for device returning idle */
|
|
while(!(DRV_Reg32(ECC_ENCIDLE_REG32) & ENC_IDLE));
|
|
mb();
|
|
DRV_WriteReg16(ECC_ENCCON_REG16, ENC_EN);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* ECC_Encode_End
|
|
*
|
|
* DESCRIPTION:
|
|
* HW ECC Encode End !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void ECC_Encode_End(void)
|
|
{
|
|
/* wait for device returning idle */
|
|
while(!(DRV_Reg32(ECC_ENCIDLE_REG32) & ENC_IDLE));
|
|
mb();
|
|
DRV_WriteReg16(ECC_ENCCON_REG16, ENC_DE);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_check_bch_error
|
|
*
|
|
* DESCRIPTION:
|
|
* Check BCH error or not !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd
|
|
* u8* pDataBuf
|
|
* u32 u4SecIndex
|
|
* u32 u4PageAddr
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_check_bch_error(
|
|
struct mtd_info *mtd, u8* pDataBuf, u32 u4SecIndex, u32 u4PageAddr)
|
|
{
|
|
bool bRet = true;
|
|
u16 u2SectorDoneMask = 1 << u4SecIndex;
|
|
u32 u4ErrorNumDebug, i, u4ErrNum, u4ErrorNumDebug1;
|
|
u32 timeout = 0xFFFF;
|
|
u32 correct_count = 0;
|
|
// int el;
|
|
//#if !USE_AHB_MODE
|
|
#if 0
|
|
u32 au4ErrBitLoc[6];
|
|
u32 u4ErrByteLoc, u4BitOffset;
|
|
u32 u4ErrBitLoc1th, u4ErrBitLoc2nd;
|
|
#endif
|
|
//#endif
|
|
|
|
//4 // Wait for Decode Done
|
|
while (0 == (u2SectorDoneMask & DRV_Reg16(ECC_DECDONE_REG16)))
|
|
{
|
|
timeout--;
|
|
if (0 == timeout)
|
|
{
|
|
printk("mt6573_nand_check_bch_error return timeout \n");
|
|
//dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
//#if (USE_AHB_MODE)
|
|
|
|
if(g_bUseAHBMode){
|
|
u4ErrorNumDebug = DRV_Reg32(ECC_DECENUM0_REG32);
|
|
u4ErrorNumDebug1 = DRV_Reg32(ECC_DECENUM1_REG32);
|
|
if ((0 != (u4ErrorNumDebug & 0xFFFFF)) || (0 != (u4ErrorNumDebug1 & 0xFFFFF)))
|
|
{
|
|
for (i = 0; i <= u4SecIndex; ++i)
|
|
{
|
|
if(i<4){
|
|
u4ErrNum = DRV_Reg32(ECC_DECENUM0_REG32) >> (i*5);
|
|
}
|
|
else{
|
|
u4ErrNum = DRV_Reg32(ECC_DECENUM1_REG32) >> ((i-4)*5);
|
|
}
|
|
u4ErrNum &= 0x1F;
|
|
correct_count += u4ErrNum;
|
|
|
|
if (0x1F == u4ErrNum)
|
|
{
|
|
mtd->ecc_stats.failed++;
|
|
bRet = false;
|
|
printk("UnCorrectable at PageAddr=%d, Sector=%d\n", u4PageAddr, i);
|
|
}
|
|
else
|
|
{
|
|
if (u4ErrNum)
|
|
{
|
|
printk("Correct %d at PageAddr=%d, Sector=%d\n", u4ErrNum, u4PageAddr, i);
|
|
}
|
|
}
|
|
}
|
|
if ((correct_count > 2) && bRet)
|
|
{
|
|
mtd->ecc_stats.corrected++;
|
|
}
|
|
else
|
|
{
|
|
//printk("Less than 2 bit error, ignore\n");
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
#if 0 //correct function for PIO mode need develop
|
|
/* We will manually correct the error bits in the last sector, not all the sectors of the page!*/
|
|
memset(au4ErrBitLoc, 0x0, sizeof(au4ErrBitLoc));
|
|
u4ErrorNumDebug = DRV_Reg32(ECC_DECENUM0_REG32);
|
|
u4ErrNum = DRV_Reg32(ECC_DECENUM0_REG32) >> (u4SecIndex*5);
|
|
u4ErrNum &= 0x1F;
|
|
|
|
if (u4ErrNum)
|
|
{
|
|
if (0x1F == u4ErrNum)
|
|
{
|
|
mtd->ecc_stats.failed++;
|
|
bRet = false;
|
|
printk("UnCorrectable at PageAddr=%d\n", u4PageAddr);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < ((u4ErrNum+1)>>1); ++i)
|
|
{
|
|
au4ErrBitLoc[i] = DRV_Reg32(ECC_DECEL0_REG32 + i);
|
|
u4ErrBitLoc1th = au4ErrBitLoc[i] & 0x1FFF;
|
|
|
|
if (u4ErrBitLoc1th < 0x1000)
|
|
{
|
|
u4ErrByteLoc = u4ErrBitLoc1th/8;
|
|
u4BitOffset = u4ErrBitLoc1th%8;
|
|
pDataBuf[u4ErrByteLoc] = pDataBuf[u4ErrByteLoc]^(1<<u4BitOffset);
|
|
mtd->ecc_stats.corrected++;
|
|
}
|
|
else
|
|
{
|
|
mtd->ecc_stats.failed++;
|
|
printk("UnCorrectable ErrLoc=%d\n", au4ErrBitLoc[i]);
|
|
}
|
|
u4ErrBitLoc2nd = (au4ErrBitLoc[i] >> 16) & 0x1FFF;
|
|
if (0 != u4ErrBitLoc2nd)
|
|
{
|
|
if (u4ErrBitLoc2nd < 0x1000)
|
|
{
|
|
u4ErrByteLoc = u4ErrBitLoc2nd/8;
|
|
u4BitOffset = u4ErrBitLoc2nd%8;
|
|
pDataBuf[u4ErrByteLoc] = pDataBuf[u4ErrByteLoc]^(1<<u4BitOffset);
|
|
mtd->ecc_stats.corrected++;
|
|
}
|
|
else
|
|
{
|
|
mtd->ecc_stats.failed++;
|
|
printk("UnCorrectable High ErrLoc=%d\n", au4ErrBitLoc[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (0 == (DRV_Reg16(ECC_DECFER_REG16) & (1 << u4SecIndex)))
|
|
{
|
|
bRet = false;
|
|
printk("mt6573_nand_check_bch_error return false 1111\n");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_RFIFOValidSize
|
|
*
|
|
* DESCRIPTION:
|
|
* Check the Read FIFO data bytes !
|
|
*
|
|
* PARAMETERS:
|
|
* u16 u2Size
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_RFIFOValidSize(u16 u2Size)
|
|
{
|
|
u32 timeout = 0xFFFF;
|
|
while (FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) < u2Size)
|
|
{
|
|
timeout--;
|
|
if (0 == timeout){
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_WFIFOValidSize
|
|
*
|
|
* DESCRIPTION:
|
|
* Check the Write FIFO data bytes !
|
|
*
|
|
* PARAMETERS:
|
|
* u16 u2Size
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_WFIFOValidSize(u16 u2Size)
|
|
{
|
|
u32 timeout = 0xFFFF;
|
|
while (FIFO_WR_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) > u2Size)
|
|
{
|
|
timeout--;
|
|
if (0 == timeout)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_status_ready
|
|
*
|
|
* DESCRIPTION:
|
|
* Indicate the NAND device is ready or not !
|
|
*
|
|
* PARAMETERS:
|
|
* u32 u4Status
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_status_ready(u32 u4Status)
|
|
{
|
|
u32 timeout = 0xFFFF;
|
|
while ((DRV_Reg32(NFI_STA_REG32) & u4Status) != 0)
|
|
{
|
|
timeout--;
|
|
if (0 == timeout)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_reset
|
|
*
|
|
* DESCRIPTION:
|
|
* Reset the NAND device hardware component !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mt6573_nand_host *host (Initial setting data)
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_reset(void)
|
|
{
|
|
// HW recommended reset flow
|
|
int timeout = 0xFFFF;
|
|
if (DRV_Reg16(NFI_MASTERSTA_REG16)) // master is busy
|
|
{
|
|
mb();
|
|
DRV_WriteReg16(NFI_CON_REG16, CON_FIFO_FLUSH | CON_NFI_RST);
|
|
while (DRV_Reg16(NFI_MASTERSTA_REG16))
|
|
{
|
|
timeout--;
|
|
if (!timeout)
|
|
{
|
|
MSG(INIT, "Wait for NFI_MASTERSTA timeout\n");
|
|
}
|
|
}
|
|
}
|
|
/* issue reset operation */
|
|
mb();
|
|
DRV_WriteReg16(NFI_CON_REG16, CON_FIFO_FLUSH | CON_NFI_RST);
|
|
|
|
return mt6573_nand_status_ready(STA_NFI_FSM_MASK|STA_NAND_BUSY) &&
|
|
mt6573_nand_RFIFOValidSize(0) &&
|
|
mt6573_nand_WFIFOValidSize(0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_set_mode
|
|
*
|
|
* DESCRIPTION:
|
|
* Set the oepration mode !
|
|
*
|
|
* PARAMETERS:
|
|
* u16 u2OpMode (read/write)
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_set_mode(u16 u2OpMode)
|
|
{
|
|
u16 u2Mode = DRV_Reg16(NFI_CNFG_REG16);
|
|
u2Mode &= ~CNFG_OP_MODE_MASK;
|
|
u2Mode |= u2OpMode;
|
|
DRV_WriteReg16(NFI_CNFG_REG16, u2Mode);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_set_autoformat
|
|
*
|
|
* DESCRIPTION:
|
|
* Enable/Disable hardware autoformat !
|
|
*
|
|
* PARAMETERS:
|
|
* bool bEnable (Enable/Disable)
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_set_autoformat(bool bEnable)
|
|
{
|
|
if (g_bAutoFMT && bEnable)
|
|
{
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_AUTO_FMT_EN);
|
|
}
|
|
else
|
|
{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AUTO_FMT_EN);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_configure_fdm
|
|
*
|
|
* DESCRIPTION:
|
|
* Configure the FDM data size !
|
|
*
|
|
* PARAMETERS:
|
|
* u16 u2FDMSize
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_configure_fdm(u16 u2FDMSize)
|
|
{
|
|
NFI_CLN_REG16(NFI_PAGEFMT_REG16, PAGEFMT_FDM_MASK | PAGEFMT_FDM_ECC_MASK);
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, u2FDMSize << PAGEFMT_FDM_SHIFT);
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, u2FDMSize << PAGEFMT_FDM_ECC_SHIFT);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_configure_lock
|
|
*
|
|
* DESCRIPTION:
|
|
* Configure the NAND lock !
|
|
*
|
|
* PARAMETERS:
|
|
* u16 u2FDMSize
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_configure_lock(void)
|
|
{
|
|
u32 u4WriteColNOB = 2;
|
|
u32 u4WriteRowNOB = 3;
|
|
u32 u4EraseColNOB = 0;
|
|
u32 u4EraseRowNOB = 3;
|
|
DRV_WriteReg16(NFI_LOCKANOB_REG16,
|
|
(u4WriteColNOB << PROG_CADD_NOB_SHIFT) |
|
|
(u4WriteRowNOB << PROG_RADD_NOB_SHIFT) |
|
|
(u4EraseColNOB << ERASE_CADD_NOB_SHIFT) |
|
|
(u4EraseRowNOB << ERASE_RADD_NOB_SHIFT));
|
|
|
|
if (CHIPVER_ECO_1 == g_u4ChipVer)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
DRV_WriteReg32(NFI_LOCK00ADD_REG32 + (i << 1), 0xFFFFFFFF);
|
|
DRV_WriteReg32(NFI_LOCK00FMT_REG32 + (i << 1), 0xFFFFFFFF);
|
|
}
|
|
//DRV_WriteReg16(NFI_LOCKANOB_REG16, 0x0);
|
|
DRV_WriteReg32(NFI_LOCKCON_REG32, 0xFFFFFFFF);
|
|
DRV_WriteReg16(NFI_LOCK_REG16, NFI_LOCK_ON);
|
|
}
|
|
}
|
|
|
|
static bool mt6573_nand_pio_ready(void)
|
|
{
|
|
int count = 0;
|
|
while ( !(DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1) )
|
|
{
|
|
count++;
|
|
if (count > 0xffff)
|
|
{
|
|
printk("PIO_DIRDY timeout\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_set_command
|
|
*
|
|
* DESCRIPTION:
|
|
* Send hardware commands to NAND devices !
|
|
*
|
|
* PARAMETERS:
|
|
* u16 command
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_set_command(u16 command)
|
|
{
|
|
/* Write command to device */
|
|
mb();
|
|
|
|
DRV_WriteReg16(NFI_CMD_REG16, command);
|
|
return mt6573_nand_status_ready(STA_CMD_STATE);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_set_address
|
|
*
|
|
* DESCRIPTION:
|
|
* Set the hardware address register !
|
|
*
|
|
* PARAMETERS:
|
|
* struct nand_chip *nand, u32 u4RowAddr
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_set_address(u32 u4ColAddr, u32 u4RowAddr, u16 u2ColNOB, u16 u2RowNOB)
|
|
{
|
|
u32 coladdr = u4ColAddr, rowaddr = u4RowAddr;
|
|
/* fill cycle addr */
|
|
mb();
|
|
|
|
DRV_WriteReg32(NFI_COLADDR_REG32, coladdr);
|
|
DRV_WriteReg32(NFI_ROWADDR_REG32, rowaddr);
|
|
DRV_WriteReg16(NFI_ADDRNOB_REG16, u2ColNOB|(u2RowNOB << ADDR_ROW_NOB_SHIFT));
|
|
return mt6573_nand_status_ready(STA_ADDR_STATE);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_check_RW_count
|
|
*
|
|
* DESCRIPTION:
|
|
* Check the RW how many sectors !
|
|
*
|
|
* PARAMETERS:
|
|
* u16 u2WriteSize
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_check_RW_count(u16 u2WriteSize)
|
|
{
|
|
u32 timeout = 0xFFFF;
|
|
u16 u2SecNum = u2WriteSize >> 9;
|
|
|
|
while (ADDRCNTR_CNTR(DRV_Reg16(NFI_ADDRCNTR_REG16)) < u2SecNum)
|
|
{
|
|
timeout--;
|
|
if (0 == timeout)
|
|
{
|
|
printk("[%s] timeout\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_ready_for_read
|
|
*
|
|
* DESCRIPTION:
|
|
* Prepare hardware environment for read !
|
|
*
|
|
* PARAMETERS:
|
|
* struct nand_chip *nand, u32 u4RowAddr
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_ready_for_read(struct nand_chip *nand, u32 u4RowAddr, u32 u4ColAddr, bool full, u8 *buf)
|
|
{
|
|
/* Reset NFI HW internal state machine and flush NFI in/out FIFO */
|
|
bool bRet = false;
|
|
u16 sec_num = 1 << (nand->page_shift - 9);
|
|
u32 col_addr = u4ColAddr;
|
|
u32 colnob=2, rownob;
|
|
|
|
|
|
if (nand->options & NAND_BUSWIDTH_16)
|
|
col_addr /= 2;
|
|
|
|
if (!mt6573_nand_reset())
|
|
{
|
|
goto cleanup;
|
|
}
|
|
if(g_bHwEcc){
|
|
/* Enable HW ECC */
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
|
|
mt6573_nand_set_mode(CNFG_OP_READ);
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_READ_EN);
|
|
DRV_WriteReg16(NFI_CON_REG16, sec_num << CON_NFI_SEC_SHIFT);
|
|
|
|
if (full)
|
|
{
|
|
if(g_bUseAHBMode){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
DRV_WriteReg32(NFI_STRADDR_REG32, virt_to_phys(buf));
|
|
}
|
|
else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
}
|
|
|
|
if(g_bHwEcc){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
}
|
|
|
|
mt6573_nand_set_autoformat(full);
|
|
if (full){
|
|
if(g_bHwEcc){
|
|
ECC_Decode_Start();
|
|
}
|
|
}
|
|
if((devinfo.pagesize == 512) && (u4ColAddr == devinfo.pagesize)){ // read oob for 512 page size
|
|
if (!mt6573_nand_set_command(NAND_CMD_READOOB))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else{
|
|
|
|
if (!mt6573_nand_set_command(NAND_CMD_READ0))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if(devinfo.pagesize == 512)
|
|
colnob = 1;
|
|
else
|
|
colnob = 2;
|
|
|
|
rownob=devinfo.addr_cycle - colnob;
|
|
|
|
//1 FIXED ME: For Any Kind of AddrCycle
|
|
if (!mt6573_nand_set_address(col_addr, u4RowAddr, colnob, rownob))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
if(devinfo.pagesize != 512){
|
|
if (!mt6573_nand_set_command(NAND_CMD_READSTART))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
bRet = true;
|
|
|
|
cleanup:
|
|
return bRet;
|
|
}
|
|
|
|
/**********************************************************
|
|
Description : SAL_NFI_Pointer_Operation
|
|
Input : 0
|
|
Output : 0
|
|
***********************************************************/
|
|
static void SAL_NFI_Pointer_Operation(u16 command)
|
|
{
|
|
#if 0
|
|
kal_uint32 reg_val = 0;
|
|
|
|
DRV_WriteReg(NFI_CMD, ptr_cmd);
|
|
while (DRV_Reg32(NFI_STA) & STA_CMD_STATE);
|
|
reg_val = DRV_Reg(NFI_CON);
|
|
reg_val |= CON_NFI_RST;
|
|
DRV_WriteReg(NFI_CON, reg_val);
|
|
#endif
|
|
mb();
|
|
|
|
DRV_WriteReg16(NFI_CMD_REG16, command);
|
|
mt6573_nand_status_ready(STA_CMD_STATE);
|
|
|
|
NFI_SET_REG16(NFI_CON_REG16, CON_NFI_RST);
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_ready_for_write
|
|
*
|
|
* DESCRIPTION:
|
|
* Prepare hardware environment for write !
|
|
*
|
|
* PARAMETERS:
|
|
* struct nand_chip *nand, u32 u4RowAddr
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_ready_for_write(
|
|
struct nand_chip *nand, u32 u4RowAddr, u32 col_addr, bool full, u8 *buf)
|
|
{
|
|
bool bRet = false;
|
|
u32 sec_num = 1 << (nand->page_shift - 9);
|
|
u32 colnob=2, rownob;
|
|
|
|
if (nand->options & NAND_BUSWIDTH_16)
|
|
col_addr /= 2;
|
|
|
|
|
|
/* Reset NFI HW internal state machine and flush NFI in/out FIFO */
|
|
if (!mt6573_nand_reset())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
mt6573_nand_set_mode(CNFG_OP_PRGM);
|
|
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_READ_EN);
|
|
|
|
DRV_WriteReg16(NFI_CON_REG16, sec_num << CON_NFI_SEC_SHIFT);
|
|
|
|
if (full)
|
|
{
|
|
if(g_bUseAHBMode){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
DRV_WriteReg32(NFI_STRADDR_REG32, virt_to_phys(buf));
|
|
}
|
|
else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
}
|
|
|
|
if(g_bHwEcc){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
}
|
|
|
|
mt6573_nand_set_autoformat(full);
|
|
|
|
if (full){
|
|
if(g_bHwEcc){
|
|
ECC_Encode_Start();
|
|
}
|
|
}
|
|
|
|
|
|
if(devinfo.pagesize == 512){
|
|
if(col_addr == devinfo.pagesize){ //write oob
|
|
SAL_NFI_Pointer_Operation(0x50);
|
|
}
|
|
else{
|
|
SAL_NFI_Pointer_Operation(0);
|
|
}
|
|
}
|
|
|
|
|
|
if (!mt6573_nand_set_command(NAND_CMD_SEQIN)){
|
|
goto cleanup;
|
|
}
|
|
|
|
if(devinfo.pagesize == 512)
|
|
colnob = 1;
|
|
else
|
|
colnob = 2;
|
|
|
|
rownob=devinfo.addr_cycle - colnob;
|
|
|
|
//1 FIXED ME: For Any Kind of AddrCycle
|
|
if (!mt6573_nand_set_address(col_addr, u4RowAddr, colnob, rownob)){
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY)){
|
|
goto cleanup;
|
|
}
|
|
|
|
bRet = true;
|
|
cleanup:
|
|
|
|
return bRet;
|
|
}
|
|
|
|
static bool mt6573_nand_check_dececc_done(u32 u4SecNum)
|
|
{
|
|
u32 timeout, dec_mask;
|
|
timeout = 0xffff;
|
|
dec_mask = (1<<u4SecNum)-1;
|
|
while((dec_mask != (DRV_Reg(ECC_DECDONE_REG16) & 0xFF)) && timeout>0){
|
|
timeout--;
|
|
}
|
|
if(timeout == 0){
|
|
//MSG(VERIFY, "ECC_DECDONE: timeout\n");
|
|
//dump_nfi();
|
|
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_page_data
|
|
*
|
|
* DESCRIPTION:
|
|
* Fill the page data into buffer !
|
|
*
|
|
* PARAMETERS:
|
|
* u8* pDataBuf, u32 u4Size
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_dma_read_data(struct mtd_info *mtd, u8 *buf, u32 length)
|
|
{
|
|
int interrupt_en = g_i4Interrupt;
|
|
int timeout = 0xffff;
|
|
struct scatterlist sg;
|
|
enum dma_data_direction dir = DMA_FROM_DEVICE;
|
|
|
|
sg_init_one(&sg, buf, length);
|
|
dma_map_sg(&(mtd->dev), &sg, 1, dir);
|
|
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
// DRV_WriteReg32(NFI_STRADDR_REG32, __virt_to_phys(pDataBuf));
|
|
|
|
if ((unsigned int)buf % 16) // TODO: can not use AHB mode here
|
|
{
|
|
printk(KERN_INFO "Un-16-aligned address\n");
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_DMA_BURST_EN);
|
|
}
|
|
else
|
|
{
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_DMA_BURST_EN);
|
|
}
|
|
|
|
DRV_Reg16(NFI_INTR_REG16);
|
|
DRV_WriteReg16(NFI_INTR_EN_REG16, INTR_AHB_DONE_EN);
|
|
|
|
//dump_nfi();
|
|
//printk("NFI_STRADDR_REG32=%x\n", DRV_Reg32(NFI_STRADDR_REG32));
|
|
|
|
if (interrupt_en)
|
|
{
|
|
init_completion(&g_comp_AHB_Done);
|
|
}
|
|
//dmac_inv_range(pDataBuf, pDataBuf + u4Size);
|
|
mb();
|
|
NFI_SET_REG16(NFI_CON_REG16, CON_NFI_BRD);
|
|
|
|
if (interrupt_en)
|
|
{
|
|
if (!wait_for_completion_timeout(&g_comp_AHB_Done, 10))
|
|
{
|
|
MSG(READ, "wait for completion timeout happened @ [%s]: %d\n", __FUNCTION__, __LINE__);
|
|
printk( "[%s] wait for completion timeout happened\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
while ( (length >> 9) > ((DRV_Reg16(NFI_BYTELEN_REG16) & 0xf000) >> 12) )
|
|
{
|
|
timeout--;
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] poll BYTELEN error\n", __FUNCTION__);
|
|
return false; //4 // AHB Mode Time Out!
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (!DRV_Reg16(NFI_INTR_REG16))
|
|
{
|
|
timeout--;
|
|
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] poll nfi_intr error\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false; //4 // AHB Mode Time Out!
|
|
}
|
|
}
|
|
while ( (length >> 9) > ((DRV_Reg16(NFI_BYTELEN_REG16) & 0xf000) >> 12) )
|
|
{
|
|
timeout--;
|
|
|
|
if (0 == timeout)
|
|
{
|
|
printk( "[%s] poll BYTELEN error\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false; //4 // AHB Mode Time Out!
|
|
}
|
|
}
|
|
}
|
|
|
|
dma_unmap_sg(&(mtd->dev), &sg, 1, dir);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool mt6573_nand_mcu_read_data(u8 *buf, u32 length)
|
|
{
|
|
int timeout = 0xffff;
|
|
u32 i, sec_num, sec_idx, temp;
|
|
u32* buf32 = (u32 *)buf;
|
|
int oob_per_sector = 16;
|
|
#ifdef TESTTIME
|
|
unsigned long long time1,time2;
|
|
time1 = sched_clock();
|
|
#endif
|
|
if ((u32)buf % 4 || length % 4 || oob_per_sector % 4)
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
else
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
|
|
//DRV_WriteReg32(NFI_STRADDR_REG32, 0);
|
|
mb();
|
|
NFI_SET_REG16(NFI_CON_REG16, CON_NFI_BRD);
|
|
|
|
#ifdef NAND_ECC_TEST
|
|
oob_per_sector = g_spare_size;
|
|
#endif
|
|
if ((u32)buf % 4 || length % 4 || oob_per_sector % 4)
|
|
{
|
|
if(g_bAutoFMT || (length < NAND_SECTOR_SIZE)){
|
|
for (i = 0; (i < (length))&&(timeout > 0);)
|
|
{
|
|
//if (FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) >= 4)
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
if(PIO_BIG_ENDIAN)
|
|
*buf++ = (u8)(DRV_Reg32(NFI_DATAR_REG32)>>24);
|
|
else
|
|
*buf++ = (u8)DRV_Reg32(NFI_DATAR_REG32);
|
|
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
sec_num = length / NAND_SECTOR_SIZE;
|
|
for(sec_idx = 0 ; sec_idx < sec_num ; sec_idx++)
|
|
{
|
|
timeout = 0xFFFF;
|
|
for (i = 0; (i < (NAND_SECTOR_SIZE))&&(timeout > 0);)
|
|
{
|
|
//if (FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) >= 4)
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
|
|
if(PIO_BIG_ENDIAN)
|
|
*buf++ = (u8)(DRV_Reg32(NFI_DATAR_REG32)>>24);
|
|
else
|
|
*buf++ = (u8)DRV_Reg32(NFI_DATAR_REG32);
|
|
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
for (i = 0; (i < (oob_per_sector))&&(timeout > 0);)
|
|
{
|
|
//if (FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) >= 4)
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
if(PIO_BIG_ENDIAN)
|
|
*buf++ = (u8)(DRV_Reg32(NFI_DATAR_REG32)>>24);
|
|
else
|
|
*buf++ = (u8)DRV_Reg32(NFI_DATAR_REG32);
|
|
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(g_bAutoFMT || (length < NAND_SECTOR_SIZE)){
|
|
for (i = 0; (i < (length >> 2))&&(timeout > 0);)
|
|
{
|
|
//if (FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) >= 4)
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
*buf32++ = DRV_Reg32(NFI_DATAR_REG32);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
sec_num = length / NAND_SECTOR_SIZE;
|
|
for(sec_idx = 0 ; sec_idx < sec_num ; sec_idx++)
|
|
{
|
|
timeout = 0xFFFF;
|
|
for (i = 0; (i < (NAND_SECTOR_SIZE >> 2))&&(timeout > 0);)
|
|
{
|
|
//if (FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) >= 4)
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
*buf32++ = DRV_Reg32(NFI_DATAR_REG32);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
for (i = 0; (i < (oob_per_sector >> 2))&&(timeout > 0);)
|
|
{
|
|
//if (FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) >= 4)
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
temp = DRV_Reg32(NFI_DATAR_REG32);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
#ifdef TESTTIME
|
|
time2 = sched_clock()-time1;
|
|
if(!readdatatime)
|
|
{
|
|
readdatatime=(time2);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
static bool mt6573_nand_read_page_data(struct mtd_info *mtd, u8* pDataBuf, u32 u4Size)
|
|
{
|
|
if(g_bUseAHBMode)
|
|
return mt6573_nand_dma_read_data(mtd, pDataBuf, u4Size);
|
|
else
|
|
//return mt6573_nand_mcu_read_data(mtd, pDataBuf, u4Size);
|
|
return mt6573_nand_mcu_read_data(pDataBuf, u4Size);
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_write_page_data
|
|
*
|
|
* DESCRIPTION:
|
|
* Fill the page data into buffer !
|
|
*
|
|
* PARAMETERS:
|
|
* u8* pDataBuf, u32 u4Size
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static bool mt6573_nand_dma_write_data(struct mtd_info *mtd, u8 *pDataBuf, u32 u4Size)
|
|
{
|
|
int i4Interrupt = g_i4Interrupt; //g_i4Interrupt;
|
|
u32 timeout = 0xFFFF;
|
|
struct scatterlist sg;
|
|
enum dma_data_direction dir = DMA_TO_DEVICE;
|
|
//u16 reg_status = 0;
|
|
sg_init_one(&sg, pDataBuf, u4Size);
|
|
dma_map_sg(&(mtd->dev), &sg, 1, dir);
|
|
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
DRV_Reg16(NFI_INTR_REG16);
|
|
DRV_WriteReg16(NFI_INTR_EN_REG16, 0);
|
|
// DRV_WriteReg32(NFI_STRADDR_REG32, (u32*)virt_to_phys(pDataBuf));
|
|
|
|
if ((unsigned int)pDataBuf % 16) // TODO: can not use AHB mode here
|
|
{
|
|
printk(KERN_INFO "Un-16-aligned address\n");
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_DMA_BURST_EN);
|
|
}
|
|
else
|
|
{
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_DMA_BURST_EN);
|
|
}
|
|
|
|
if (i4Interrupt)
|
|
{
|
|
init_completion(&g_comp_AHB_Done);
|
|
DRV_Reg16(NFI_INTR_REG16);
|
|
DRV_WriteReg16(NFI_INTR_EN_REG16, INTR_AHB_DONE_EN);
|
|
}
|
|
//dmac_clean_range(pDataBuf, pDataBuf + u4Size);
|
|
mb();
|
|
NFI_SET_REG16(NFI_CON_REG16, CON_NFI_BWR);
|
|
|
|
|
|
if (i4Interrupt)
|
|
{
|
|
if (!wait_for_completion_timeout(&g_comp_AHB_Done, 10))
|
|
{
|
|
MSG(READ, "wait for completion timeout happened @ [%s]: %d\n", __FUNCTION__, __LINE__);
|
|
printk("wait for completion timeout happened @ [%s]: %d\n", __FUNCTION__, __LINE__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
// wait_for_completion(&g_comp_AHB_Done);
|
|
}
|
|
else
|
|
{
|
|
while ( (u4Size >> 9) > ((DRV_Reg16(NFI_BYTELEN_REG16) & 0xf000) >> 12) )
|
|
{
|
|
timeout--;
|
|
if (0 == timeout)
|
|
{
|
|
printk( "[%s] poll BYTELEN error\n", __FUNCTION__);
|
|
return false; //4 // AHB Mode Time Out!
|
|
}
|
|
}
|
|
}
|
|
|
|
dma_unmap_sg(&(mtd->dev), &sg, 1, dir);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool mt6573_nand_mcu_write_data(struct mtd_info *mtd, const u8 *buf, u32 length)
|
|
{
|
|
u32 timeout = 0xFFFF;
|
|
u32 i, sec_idx, sec_num;
|
|
u32* pBuf32, *pOOBBuf32 = NULL;
|
|
int oob_per_sector = 16;
|
|
u8* pOOBBuf = NULL;
|
|
u32 data_w;
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
mb();
|
|
NFI_SET_REG16(NFI_CON_REG16, CON_NFI_BWR);
|
|
pBuf32 = (u32*)buf;
|
|
#ifdef NAND_ECC_TEST
|
|
oob_per_sector = g_spare_size;
|
|
#endif
|
|
|
|
if ((u32)buf % 4 || length % 4 || oob_per_sector % 4)
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
else
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
|
|
if ((u32)buf % 4 || length % 4 || oob_per_sector % 4)
|
|
{
|
|
if(g_bAutoFMT || (length < NAND_SECTOR_SIZE)){
|
|
for (i = 0; (i < (length))&&(timeout > 0);)
|
|
{
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
if(PIO_BIG_ENDIAN){
|
|
data_w = (*buf) << 24;
|
|
DRV_WriteReg32(NFI_DATAW_REG32, data_w);
|
|
buf++;
|
|
}
|
|
else{
|
|
DRV_WriteReg32(NFI_DATAW_REG32, *buf++);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
if(g_bOOB_Test){
|
|
pOOBBuf = local_oob_buf;
|
|
}
|
|
sec_num = length / NAND_SECTOR_SIZE;
|
|
for(sec_idx = 0 ; sec_idx < sec_num ; sec_idx++)
|
|
{
|
|
for (i = 0; (i < (NAND_SECTOR_SIZE))&&(timeout > 0);)
|
|
{
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
if(PIO_BIG_ENDIAN){
|
|
data_w = (*buf) << 24;
|
|
DRV_WriteReg32(NFI_DATAW_REG32, data_w);
|
|
buf++;
|
|
}
|
|
else{
|
|
DRV_WriteReg32(NFI_DATAW_REG32, *buf++);
|
|
}
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
for (i = 0; (i < (oob_per_sector))&&(timeout > 0);)
|
|
{
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
if(g_bOOB_Test && pOOBBuf != NULL){
|
|
if(PIO_BIG_ENDIAN){
|
|
data_w = (*pOOBBuf) << 24;
|
|
DRV_WriteReg32(NFI_DATAW_REG32, data_w);
|
|
pOOBBuf++;
|
|
}
|
|
else{
|
|
DRV_WriteReg32(NFI_DATAW_REG32, *pOOBBuf++);
|
|
}
|
|
}
|
|
else{
|
|
DRV_WriteReg32(NFI_DATAW_REG32, 0xff);
|
|
}
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(g_bAutoFMT || (length < NAND_SECTOR_SIZE)){
|
|
for (i = 0; (i < (length >> 2)) && (timeout > 0); )
|
|
{
|
|
// if (FIFO_WR_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)) <= 12)
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
DRV_WriteReg32(NFI_DATAW_REG32, *pBuf32++);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
if(g_bOOB_Test){
|
|
pOOBBuf32 = (u32*)local_oob_buf;
|
|
}
|
|
sec_num = length / NAND_SECTOR_SIZE;
|
|
for(sec_idx = 0 ; sec_idx < sec_num ; sec_idx++)
|
|
{
|
|
timeout = 0xFFFF;
|
|
for (i = 0; (i < (NAND_SECTOR_SIZE >> 2)) && (timeout > 0); )
|
|
{
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
DRV_WriteReg32(NFI_DATAW_REG32, *pBuf32++);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
|
|
return false;
|
|
}
|
|
}
|
|
for (i = 0; (i < (oob_per_sector >> 2)) && (timeout > 0); )
|
|
{
|
|
if (DRV_Reg16(NFI_PIO_DIRDY_REG16) & 1)
|
|
{
|
|
if(g_bOOB_Test && (pOOBBuf32 != NULL)){
|
|
DRV_WriteReg32(NFI_DATAW_REG32, *pOOBBuf32++);
|
|
}
|
|
else{
|
|
DRV_WriteReg32(NFI_DATAW_REG32, 0xffffffff);
|
|
}
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
timeout--;
|
|
}
|
|
if (0 == timeout)
|
|
{
|
|
printk(KERN_ERR "[%s] timeout\n", __FUNCTION__);
|
|
dump_nfi();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool mt6573_nand_write_page_data(struct mtd_info *mtd, u8* buf, u32 size)
|
|
{
|
|
if(g_bUseAHBMode)
|
|
return mt6573_nand_dma_write_data(mtd, buf, size);
|
|
else
|
|
return mt6573_nand_mcu_write_data(mtd, buf, size);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_fdm_data
|
|
*
|
|
* DESCRIPTION:
|
|
* Read a fdm data !
|
|
*
|
|
* PARAMETERS:
|
|
* u8* pDataBuf, u32 u4SecNum
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_read_fdm_data(u8* pDataBuf, u32 u4SecNum)
|
|
{
|
|
u32 i;
|
|
u32* pBuf32 = (u32*)pDataBuf;
|
|
|
|
if (pBuf32)
|
|
{
|
|
for (i = 0; i < u4SecNum; ++i)
|
|
{
|
|
*pBuf32++ = DRV_Reg32(NFI_FDM0L_REG32 + (i<<1));
|
|
*pBuf32++ = DRV_Reg32(NFI_FDM0M_REG32 + (i<<1));
|
|
//*pBuf32++ = DRV_Reg32((u32)NFI_FDM0L_REG32 + (i<<3));
|
|
//*pBuf32++ = DRV_Reg32((u32)NFI_FDM0M_REG32 + (i<<3));
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_write_fdm_data
|
|
*
|
|
* DESCRIPTION:
|
|
* Write a fdm data !
|
|
*
|
|
* PARAMETERS:
|
|
* u8* pDataBuf, u32 u4SecNum
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static u8 fdm_buf[64];
|
|
static void mt6573_nand_write_fdm_data(struct nand_chip *chip, u8* pDataBuf, u32 u4SecNum)
|
|
{
|
|
u32 i, j;
|
|
u8 checksum = 0;
|
|
bool empty = true;
|
|
struct nand_oobfree *free_entry;
|
|
u32* pBuf32;
|
|
|
|
memcpy(fdm_buf, pDataBuf, u4SecNum * 8);
|
|
|
|
// printk("mt6573_nand_write_fdm_data enter\n");
|
|
free_entry = chip->ecc.layout->oobfree;
|
|
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free_entry[i].length; i++)
|
|
{
|
|
for (j = 0; j < free_entry[i].length; j++)
|
|
{
|
|
if (pDataBuf[free_entry[i].offset + j] != 0xFF)
|
|
empty = false;
|
|
checksum ^= pDataBuf[free_entry[i].offset + j];
|
|
}
|
|
}
|
|
|
|
if (!empty)
|
|
{
|
|
fdm_buf[free_entry[i-1].offset + free_entry[i-1].length] = checksum;
|
|
}
|
|
|
|
|
|
pBuf32 = (u32*)fdm_buf;
|
|
for (i = 0; i < u4SecNum; ++i)
|
|
{
|
|
DRV_WriteReg32(NFI_FDM0L_REG32 + (i<<1), *pBuf32++);
|
|
DRV_WriteReg32(NFI_FDM0M_REG32 + (i<<1), *pBuf32++);
|
|
//DRV_WriteReg32((u32)NFI_FDM0L_REG32 + (i<<3), *pBuf32++);
|
|
//DRV_WriteReg32((u32)NFI_FDM0M_REG32 + (i<<3), *pBuf32++);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_stop_read
|
|
*
|
|
* DESCRIPTION:
|
|
* Stop read operation !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_stop_read(void)
|
|
{
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_BRD);
|
|
mt6573_nand_reset();
|
|
if(g_bHwEcc){
|
|
ECC_Decode_End();
|
|
}
|
|
DRV_WriteReg16(NFI_INTR_EN_REG16, 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_stop_write
|
|
*
|
|
* DESCRIPTION:
|
|
* Stop write operation !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_stop_write(void)
|
|
{
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_BWR);
|
|
if(g_bHwEcc){
|
|
ECC_Encode_End();
|
|
}
|
|
DRV_WriteReg16(NFI_INTR_EN_REG16, 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_exec_read_page
|
|
*
|
|
* DESCRIPTION:
|
|
* Read a page data !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, u32 u4RowAddr, u32 u4PageSize,
|
|
* u8* pPageBuf, u8* pFDMBuf
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
int mt6573_nand_exec_read_page(
|
|
struct mtd_info *mtd, u32 u4RowAddr, u32 u4PageSize, u8* pPageBuf, u8* pFDMBuf)
|
|
{
|
|
u8 *buf;
|
|
int bRet = 0;
|
|
struct nand_chip *nand = mtd->priv;
|
|
u32 u4SecNum = u4PageSize >> 9;
|
|
#ifdef NAND_PFM
|
|
struct timeval pfm_time_read;
|
|
#endif
|
|
PFM_BEGIN(pfm_time_read);
|
|
|
|
#if (0)
|
|
buf = local_buffer_16_align;
|
|
#else
|
|
if (((u32)pPageBuf % 16) && local_buffer_16_align)
|
|
{
|
|
#ifndef TCSUPPORT_XPON_HAL_API_EXT
|
|
//printk(KERN_INFO "Data buffer not 16 bytes aligned: %p\n", pPageBuf);
|
|
#endif
|
|
buf = local_buffer_16_align;
|
|
}
|
|
else
|
|
buf = pPageBuf;
|
|
#endif
|
|
|
|
|
|
|
|
if (mt6573_nand_ready_for_read(nand, u4RowAddr, 0, true, buf))
|
|
{
|
|
if (!mt6573_nand_read_page_data(mtd, buf, u4PageSize))
|
|
{
|
|
printk("mt6573_nand_read_page_data return false\n");
|
|
bRet = -1;
|
|
}
|
|
|
|
|
|
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY))
|
|
{
|
|
printk("mt6573_nand_status_ready return false\n");
|
|
bRet = -1;
|
|
}
|
|
|
|
//dump_buf(local_buffer_16_align, u4PageSize);
|
|
if(g_bHwEcc){
|
|
if(!mt6573_nand_check_dececc_done(u4SecNum))
|
|
{
|
|
bRet = -1;//ECC Done error
|
|
}
|
|
}
|
|
|
|
mt6573_nand_read_fdm_data(pFDMBuf, u4SecNum);
|
|
|
|
if(g_bHwEcc){
|
|
if (!mt6573_nand_check_bch_error(mtd, buf, u4SecNum - 1, u4RowAddr))
|
|
{
|
|
bRet = -1; //ECC Done error
|
|
}
|
|
}
|
|
mt6573_nand_stop_read();
|
|
}
|
|
|
|
if (buf == local_buffer_16_align)
|
|
memcpy(pPageBuf, buf, u4PageSize);
|
|
|
|
|
|
PFM_END_R(pfm_time_read, u4PageSize + 32);
|
|
return bRet;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_exec_write_page
|
|
*
|
|
* DESCRIPTION:
|
|
* Write a page data !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, u32 u4RowAddr, u32 u4PageSize,
|
|
* u8* pPageBuf, u8* pFDMBuf
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
int mt6573_nand_exec_write_page(
|
|
struct mtd_info *mtd, u32 u4RowAddr, u32 u4PageSize, u8* pPageBuf, u8* pFDMBuf)
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
u32 u4SecNum = u4PageSize >> 9;
|
|
u8 *buf;
|
|
u8 status;
|
|
|
|
//MSG(WRITE, "mt6573_nand_exec_write_page, page: 0x%x\n", u4RowAddr);
|
|
#ifdef _MTK_NAND_DUMMY_DRIVER_
|
|
if (dummy_driver_debug)
|
|
{
|
|
unsigned long long time = sched_clock();
|
|
if (!((time * 123 + 59 ) % 32768))
|
|
{
|
|
printk(KERN_INFO "[NAND_DUMMY_DRIVER] Simulate write error at page: 0x%x\n", u4RowAddr);
|
|
return -EIO;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef NAND_PFM
|
|
struct timeval pfm_time_write;
|
|
#endif
|
|
PFM_BEGIN(pfm_time_write);
|
|
|
|
if (((u32)pPageBuf % 16) && local_buffer_16_align)
|
|
{
|
|
#ifndef TCSUPPORT_XPON_HAL_API_EXT
|
|
printk(KERN_INFO "Data buffer not 16 bytes aligned: %p\n", pPageBuf);
|
|
#endif
|
|
memcpy(local_buffer_16_align, pPageBuf, mtd->writesize);
|
|
buf = local_buffer_16_align;
|
|
}
|
|
else{
|
|
buf = pPageBuf;
|
|
}
|
|
|
|
|
|
|
|
if (mt6573_nand_ready_for_write(chip, u4RowAddr, 0, true, buf))
|
|
{
|
|
|
|
mt6573_nand_write_fdm_data(chip, pFDMBuf, u4SecNum);
|
|
|
|
(void)mt6573_nand_write_page_data(mtd, buf, u4PageSize);
|
|
(void)mt6573_nand_check_RW_count(u4PageSize);
|
|
mt6573_nand_stop_write();
|
|
(void)mt6573_nand_set_command(NAND_CMD_PAGEPROG);
|
|
while(DRV_Reg32(NFI_STA_REG32) & STA_NAND_BUSY);
|
|
|
|
|
|
}
|
|
|
|
PFM_END_W(pfm_time_write, u4PageSize + 32);
|
|
|
|
status = chip->waitfunc(mtd, chip);
|
|
|
|
if (status & NAND_STATUS_FAIL){
|
|
return -EIO;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Write a page to a logical address
|
|
*
|
|
*****************************************************************************/
|
|
static int mt6573_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
uint32_t offset, int data_len, const uint8_t *buf,
|
|
int oob_required, int page, int cached, int raw)
|
|
#if 0
|
|
static int mt6573_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
const u8 *buf, int page, int cached, int raw)
|
|
#endif
|
|
{
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int block = page / page_per_block;
|
|
u16 page_in_block = page % page_per_block;
|
|
u16 phy_block_bbt;
|
|
int mapped_block = get_mapping_block_index(block, &phy_block_bbt);
|
|
|
|
//printk("mt6573_nand_write_page enter: page addr 0x%x\n", page_in_block + mapped_block * page_per_block);
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
// write bad index into oob
|
|
#if 0
|
|
if (mapped_block != block)
|
|
{
|
|
set_bad_index_to_oob(chip->oob_poi, block);
|
|
}
|
|
else
|
|
{
|
|
set_bad_index_to_oob(chip->oob_poi, FAKE_INDEX);
|
|
}
|
|
#endif
|
|
if(block_is_in_bmt_region(mapped_block))
|
|
{
|
|
memcpy(chip->oob_poi + OOB_INDEX_OFFSET, &phy_block_bbt, OOB_INDEX_SIZE);
|
|
}
|
|
#endif
|
|
if (mt6573_nand_exec_write_page(mtd, page_in_block + mapped_block * page_per_block, mtd->writesize, (u8 *)buf, chip->oob_poi))
|
|
{
|
|
MSG(INIT, "write fail at block: 0x%x, page: 0x%x\n", mapped_block, page_in_block);
|
|
if (update_bmt((page_in_block + mapped_block * page_per_block) << chip->page_shift,
|
|
UPDATE_WRITE_FAIL, (u8 *)buf, chip->oob_poi))
|
|
{
|
|
MSG(INIT, "Update BMT success\n");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
MSG(INIT, "Update BMT fail\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------
|
|
/*
|
|
static void mt6573_nand_command_sp(
|
|
struct mtd_info *mtd, unsigned int command, int column, int page_addr)
|
|
{
|
|
g_u4ColAddr = column;
|
|
g_u4RowAddr = page_addr;
|
|
|
|
switch(command)
|
|
{
|
|
case NAND_CMD_STATUS:
|
|
break;
|
|
|
|
case NAND_CMD_READID:
|
|
break;
|
|
|
|
case NAND_CMD_RESET:
|
|
break;
|
|
|
|
case NAND_CMD_RNDOUT:
|
|
case NAND_CMD_RNDOUTSTART:
|
|
case NAND_CMD_RNDIN:
|
|
case NAND_CMD_CACHEDPROG:
|
|
case NAND_CMD_STATUS_MULTI:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
*/
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
static int mt6573_nand_erase_mapping_page(struct mtd_info *mtd, int page)
|
|
{
|
|
// get mapping
|
|
struct nand_chip *chip = mtd->priv;
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int page_in_block = page % page_per_block;
|
|
int block = page / page_per_block;
|
|
u16 phy_block_bbt;
|
|
|
|
int mapped_block = get_mapping_block_index(block, &phy_block_bbt);
|
|
|
|
return (page_in_block + page_per_block * mapped_block);
|
|
}
|
|
|
|
|
|
static int mt6573_nand_erase_status(struct mtd_info *mtd, int page)
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
|
|
if (update_bmt( page << chip->page_shift,
|
|
UPDATE_ERASE_FAIL, NULL, NULL))
|
|
{
|
|
MSG(INIT, "Erase fail at block page addr: 0x%x, update BMT success\n", page);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
MSG(INIT, "Erase fail at block page addr: 0x%x, update BMT fail\n", page);
|
|
return NAND_STATUS_FAIL;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_command_bp
|
|
*
|
|
* DESCRIPTION:
|
|
* Handle the commands from MTD !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, unsigned int command, int column, int page_addr
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_command_bp(struct mtd_info *mtd, unsigned int command,
|
|
int column, int page_addr)
|
|
{
|
|
struct nand_chip* nand = mtd->priv;
|
|
#ifdef NAND_PFM
|
|
struct timeval pfm_time_erase;
|
|
#endif
|
|
int timeout;
|
|
int page_address = page_addr;
|
|
|
|
|
|
//printk("mt6573_nand_command_bp:0x%x, 0x%x, 0x%x\n", command, column, page_addr);
|
|
switch (command)
|
|
{
|
|
case NAND_CMD_SEQIN:
|
|
/* Reset g_kCMD */
|
|
//if (g_kCMD.u4RowAddr != page_addr) {
|
|
memset(g_kCMD.au1OOB, 0xFF, sizeof(g_kCMD.au1OOB));
|
|
g_kCMD.pDataBuf = NULL;
|
|
//}
|
|
g_kCMD.u4RowAddr = page_addr;
|
|
g_kCMD.u4ColAddr = column;
|
|
break;
|
|
|
|
case NAND_CMD_PAGEPROG:
|
|
if (g_kCMD.pDataBuf || (0xFF != g_kCMD.au1OOB[0]))
|
|
{
|
|
u8* pDataBuf = g_kCMD.pDataBuf ? g_kCMD.pDataBuf : nand->buffers->databuf;
|
|
mt6573_nand_exec_write_page(mtd, g_kCMD.u4RowAddr, mtd->writesize, pDataBuf, g_kCMD.au1OOB);
|
|
g_kCMD.u4RowAddr = (u32)-1;
|
|
g_kCMD.u4OOBRowAddr = (u32)-1;
|
|
}
|
|
break;
|
|
|
|
case NAND_CMD_READOOB:
|
|
g_kCMD.u4RowAddr = page_addr;
|
|
g_kCMD.u4ColAddr = column + mtd->writesize;
|
|
#ifdef NAND_PFM
|
|
g_kCMD.pureReadOOB = 1;
|
|
g_kCMD.pureReadOOBNum += 1;
|
|
#endif
|
|
break;
|
|
|
|
case NAND_CMD_READ0:
|
|
g_kCMD.u4RowAddr = page_addr;
|
|
g_kCMD.u4ColAddr = column;
|
|
#ifdef NAND_PFM
|
|
g_kCMD.pureReadOOB = 0;
|
|
#endif
|
|
break;
|
|
|
|
case NAND_CMD_ERASE1:
|
|
PFM_BEGIN(pfm_time_erase);
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
page_address = mt6573_nand_erase_mapping_page(mtd, page_addr);
|
|
#endif
|
|
(void)mt6573_nand_reset();
|
|
mt6573_nand_set_mode(CNFG_OP_ERASE);
|
|
(void)mt6573_nand_set_command(NAND_CMD_ERASE1);
|
|
|
|
if(devinfo.pagesize == 512){
|
|
(void)mt6573_nand_set_address(0,page_address,0,devinfo.addr_cycle-1);
|
|
}
|
|
else{
|
|
(void)mt6573_nand_set_address(0,page_address,0,devinfo.addr_cycle-2);
|
|
}
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
g_kCMD.u4RowAddr = page_address;
|
|
#endif
|
|
break;
|
|
|
|
case NAND_CMD_ERASE2:
|
|
(void)mt6573_nand_set_command(NAND_CMD_ERASE2);
|
|
while(DRV_Reg32(NFI_STA_REG32) & STA_NAND_BUSY);
|
|
PFM_END_E(pfm_time_erase);
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
g_bReadEraseStatus = true;
|
|
#endif
|
|
break;
|
|
|
|
case NAND_CMD_STATUS:
|
|
(void)mt6573_nand_reset();
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_BYTE_RW);
|
|
mt6573_nand_set_mode(CNFG_OP_SRD);
|
|
mt6573_nand_set_mode(CNFG_READ_EN);
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
(void)mt6573_nand_set_command(NAND_CMD_STATUS);
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_NOB_MASK);
|
|
mb();
|
|
DRV_WriteReg16(NFI_CON_REG16, CON_NFI_SRD|(1 << CON_NFI_NOB_SHIFT));
|
|
g_bcmdstatus = true;
|
|
break;
|
|
|
|
case NAND_CMD_RESET:
|
|
(void)mt6573_nand_reset();
|
|
//mt6573_nand_exec_reset_device();
|
|
break;
|
|
|
|
case NAND_CMD_READID:
|
|
mt6573_nand_reset();
|
|
/* Disable HW ECC */
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
|
|
#if 1
|
|
/* Reset NFI state machine */
|
|
mt6573_nand_reset();
|
|
|
|
/* Issue NAND chip reset command for Micron's MCP */
|
|
NFI_ISSUE_COMMAND(NAND_CMD_RESET, 0, 0, 0, 0);
|
|
|
|
timeout = TIMEOUT_4;
|
|
|
|
while(timeout)
|
|
timeout--;
|
|
#endif
|
|
|
|
|
|
/* Disable 16-bit I/O */
|
|
//NFI_CLN_REG16(NFI_PAGEFMT_REG16, PAGEFMT_DBYTE_EN);
|
|
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_READ_EN|CNFG_BYTE_RW);
|
|
(void)mt6573_nand_reset();
|
|
mt6573_nand_set_mode(CNFG_OP_SRD);
|
|
(void)mt6573_nand_set_command(NAND_CMD_READID);
|
|
(void)mt6573_nand_set_address(0,0,1,0);
|
|
mb();
|
|
DRV_WriteReg16(NFI_CON_REG16, CON_NFI_SRD);
|
|
while(DRV_Reg32(NFI_STA_REG32) & STA_DATAR_STATE);
|
|
break;
|
|
|
|
default:
|
|
BUG();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_select_chip
|
|
*
|
|
* DESCRIPTION:
|
|
* Select a chip !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, int chip
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_select_chip(struct mtd_info *mtd, int chip)
|
|
{
|
|
|
|
if (chip == -1 && false == g_bInitDone)
|
|
{
|
|
struct nand_chip *nand = mtd->priv;
|
|
/* Setup PageFormat */
|
|
if (4096 == mtd->writesize) {
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT) | PAGEFMT_4K);
|
|
nand->cmdfunc = mt6573_nand_command_bp;
|
|
} else if (2048 == mtd->writesize) {
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT) | PAGEFMT_2K);
|
|
nand->cmdfunc = mt6573_nand_command_bp;
|
|
} else if (512 == mtd->writesize) {
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT) | PAGEFMT_512);
|
|
//nand->cmdfunc = mt6573_nand_command_sp;
|
|
nand->cmdfunc = mt6573_nand_command_bp;
|
|
}
|
|
g_bInitDone = true;
|
|
}
|
|
switch(chip)
|
|
{
|
|
case -1:
|
|
break;
|
|
case 0:
|
|
case 1:
|
|
DRV_WriteReg16(NFI_CSEL_REG16, chip);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_byte
|
|
*
|
|
* DESCRIPTION:
|
|
* Read a byte of data !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static uint8_t mt6573_nand_read_byte(struct mtd_info *mtd)
|
|
{
|
|
#if 0
|
|
//while(0 == FIFO_RD_REMAIN(DRV_Reg16(NFI_FIFOSTA_REG16)));
|
|
/* Check the PIO bit is ready or not */
|
|
u32 timeout = TIMEOUT_4;
|
|
uint8_t retval = 0;
|
|
WAIT_NFI_PIO_READY(timeout);
|
|
|
|
retval = DRV_Reg8(NFI_DATAR_REG32);
|
|
MSG(INIT, "mt6573_nand_read_byte (0x%x)\n", retval);
|
|
|
|
if(g_bcmdstatus)
|
|
{
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
g_bcmdstatus = false;
|
|
}
|
|
|
|
return retval;
|
|
#endif
|
|
uint8_t retval = 0;
|
|
int value = 0;
|
|
|
|
if (!mt6573_nand_pio_ready())
|
|
{
|
|
printk("pio ready timeout\n");
|
|
retval = false;
|
|
}
|
|
|
|
if(g_bcmdstatus)
|
|
{
|
|
if(PIO_BIG_ENDIAN){
|
|
value = DRV_Reg8(NFI_DATAR_REG32);
|
|
retval = (uint8_t)(value >> 24);
|
|
}
|
|
else{
|
|
retval = DRV_Reg8(NFI_DATAR_REG32);
|
|
}
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_NOB_MASK);
|
|
mt6573_nand_reset();
|
|
if(g_bUseAHBMode){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
}
|
|
|
|
if(g_bHwEcc){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
g_bcmdstatus = false;
|
|
}
|
|
else{
|
|
//printk("retval=%x\n",DRV_Reg8(NFI_DATAR_REG32));
|
|
if(PIO_BIG_ENDIAN){
|
|
value = DRV_Reg8(NFI_DATAR_REG32);
|
|
retval = (uint8_t)(value >> 24);
|
|
}
|
|
else{
|
|
retval = DRV_Reg8(NFI_DATAR_REG32);
|
|
}
|
|
}
|
|
|
|
//printk("retval=%x\n",retval);
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
if(g_bReadEraseStatus){
|
|
//printk("mt6573_nand_read_byte:%x\n", g_kCMD.u4RowAddr);
|
|
if (retval & NAND_STATUS_FAIL){
|
|
mt6573_nand_erase_status(mtd, g_kCMD.u4RowAddr);
|
|
}
|
|
g_bReadEraseStatus = false;
|
|
}
|
|
#endif
|
|
return retval;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_buf
|
|
*
|
|
* DESCRIPTION:
|
|
* Read NAND data !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, uint8_t *buf, int len
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
{
|
|
struct nand_chip* nand = (struct nand_chip*)mtd->priv;
|
|
struct mt6573_CMD* pkCMD = &g_kCMD;
|
|
u32 u4ColAddr = pkCMD->u4ColAddr;
|
|
u32 u4PageSize = mtd->writesize;
|
|
|
|
|
|
if (u4ColAddr < u4PageSize)
|
|
{
|
|
if ((u4ColAddr == 0) && (len >= u4PageSize))
|
|
{
|
|
mt6573_nand_exec_read_page(mtd, pkCMD->u4RowAddr, u4PageSize,
|
|
buf, pkCMD->au1OOB);
|
|
if (len > u4PageSize)
|
|
{
|
|
u32 u4Size = min(len - u4PageSize, sizeof(pkCMD->au1OOB));
|
|
memcpy(buf + u4PageSize, pkCMD->au1OOB, u4Size);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mt6573_nand_exec_read_page(mtd, pkCMD->u4RowAddr, u4PageSize,
|
|
nand->buffers->databuf, pkCMD->au1OOB);
|
|
memcpy(buf, nand->buffers->databuf + u4ColAddr, len);
|
|
}
|
|
pkCMD->u4OOBRowAddr = pkCMD->u4RowAddr;
|
|
}
|
|
else
|
|
{
|
|
u32 u4Offset = u4ColAddr - u4PageSize;
|
|
u32 u4Size = min(len - u4Offset, sizeof(pkCMD->au1OOB));
|
|
if (pkCMD->u4OOBRowAddr != pkCMD->u4RowAddr)
|
|
{
|
|
mt6573_nand_exec_read_page(mtd, pkCMD->u4RowAddr, u4PageSize,
|
|
nand->buffers->databuf, pkCMD->au1OOB);
|
|
pkCMD->u4OOBRowAddr = pkCMD->u4RowAddr;
|
|
}
|
|
memcpy(buf, pkCMD->au1OOB + u4Offset, u4Size);
|
|
}
|
|
//dump_buf(buf, len);
|
|
pkCMD->u4ColAddr += len;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_write_buf
|
|
*
|
|
* DESCRIPTION:
|
|
* Write NAND data !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, const uint8_t *buf, int len
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
|
{
|
|
struct mt6573_CMD* pkCMD = &g_kCMD;
|
|
u32 u4ColAddr = pkCMD->u4ColAddr;
|
|
u32 u4PageSize = mtd->writesize;
|
|
int i4Size, i;
|
|
|
|
//printk("mt6573_nand_write_buf enter\n");
|
|
|
|
if (u4ColAddr >= u4PageSize)
|
|
{
|
|
u32 u4Offset = u4ColAddr - u4PageSize;
|
|
u8* pOOB = pkCMD->au1OOB + u4Offset;
|
|
i4Size = min(len, (int)(sizeof(pkCMD->au1OOB) - u4Offset));
|
|
|
|
for (i = 0; i < i4Size; i++)
|
|
{
|
|
pOOB[i] &= buf[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pkCMD->pDataBuf = (u8*)buf;
|
|
}
|
|
|
|
pkCMD->u4ColAddr += len;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_write_page_hwecc
|
|
*
|
|
* DESCRIPTION:
|
|
* Write NAND data with hardware ecc !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void mt6573_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf)
|
|
{
|
|
//printk("mt6573_nand_write_page_hwecc enter\n");
|
|
mt6573_nand_write_buf(mtd, buf, mtd->writesize);
|
|
mt6573_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_page_hwecc
|
|
*
|
|
* DESCRIPTION:
|
|
* Read NAND data with hardware ecc !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
//static int mt6573_nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page)
|
|
static int mt6573_nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf)
|
|
{
|
|
#if 0
|
|
mt6573_nand_read_buf(mtd, buf, mtd->writesize);
|
|
mt6573_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
#else
|
|
struct mt6573_CMD* pkCMD = &g_kCMD;
|
|
u32 u4ColAddr = pkCMD->u4ColAddr;
|
|
u32 u4PageSize = mtd->writesize;
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
u32 u4RowAddr = pkCMD->u4RowAddr;
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int block = u4RowAddr / page_per_block;
|
|
u16 page_in_block = u4RowAddr % page_per_block;
|
|
u16 phy_block_bbt;
|
|
int mapped_block = get_mapping_block_index(block, &phy_block_bbt);
|
|
|
|
//printk("mt6573_nand_read_page_hwecc:page=%x,u4ColAddr=%x,block=%x,mapped_block=%x\n", u4RowAddr, u4ColAddr,block, mapped_block);
|
|
//if(mapped_block != block)
|
|
// printk("detected bad block:%d,mapped_block=%d\n",block, mapped_block);
|
|
|
|
if (u4ColAddr == 0)
|
|
{
|
|
mt6573_nand_exec_read_page(mtd, page_in_block + mapped_block * page_per_block, u4PageSize, buf, chip->oob_poi);
|
|
pkCMD->u4ColAddr += u4PageSize + mtd->oobsize;
|
|
}
|
|
#else
|
|
|
|
if (u4ColAddr == 0)
|
|
{
|
|
mt6573_nand_exec_read_page(mtd, pkCMD->u4RowAddr, u4PageSize, buf, chip->oob_poi);
|
|
pkCMD->u4ColAddr += u4PageSize + mtd->oobsize;
|
|
}
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
#if 0
|
|
/******************************************************************************
|
|
*
|
|
* Read a page to a logical address
|
|
*
|
|
*****************************************************************************/
|
|
static int mt6573_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, u8 *buf, int page)
|
|
{
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int block = page / page_per_block;
|
|
u16 page_in_block = page % page_per_block;
|
|
int mapped_block = get_mapping_block_index(block);
|
|
|
|
if (mt6573_nand_exec_read_page(mtd, page_in_block + mapped_block * page_per_block,
|
|
mtd->writesize, buf, chip->oob_poi))
|
|
return 0;
|
|
/* else
|
|
return -EIO;*/
|
|
return 0;
|
|
}
|
|
#endif
|
|
/******************************************************************************
|
|
*
|
|
* Erase a block at a logical address
|
|
*
|
|
*****************************************************************************/
|
|
int mt6573_nand_erase_hw(struct mtd_info *mtd, int page)
|
|
{
|
|
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
|
|
|
#ifdef _MTK_NAND_DUMMY_DRIVER_
|
|
if (dummy_driver_debug)
|
|
{
|
|
unsigned long long time = sched_clock();
|
|
if (!((time * 123 + 59 ) % 1024))
|
|
{
|
|
printk(KERN_INFO "[NAND_DUMMY_DRIVER] Simulate erase error at page: 0x%x\n", page);
|
|
return NAND_STATUS_FAIL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
chip->erase(mtd, page);
|
|
|
|
return chip->waitfunc(mtd, chip);
|
|
}
|
|
|
|
static int mt6573_nand_erase(struct mtd_info *mtd, int page)
|
|
{
|
|
// get mapping
|
|
struct nand_chip *chip = mtd->priv;
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int page_in_block = page % page_per_block;
|
|
int block = page / page_per_block;
|
|
u16 phy_block_bbt;
|
|
|
|
int mapped_block = get_mapping_block_index(block, &phy_block_bbt);
|
|
|
|
int status = mt6573_nand_erase_hw(mtd, page_in_block + page_per_block * mapped_block);
|
|
|
|
if (status & NAND_STATUS_FAIL)
|
|
{
|
|
if (update_bmt( (page_in_block + mapped_block * page_per_block) << chip->page_shift,
|
|
UPDATE_ERASE_FAIL, NULL, NULL))
|
|
{
|
|
MSG(INIT, "Erase fail at block: 0x%x, update BMT success\n", mapped_block);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
MSG(INIT, "Erase fail at block: 0x%x, update BMT fail\n", mapped_block);
|
|
return NAND_STATUS_FAIL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_multi_page_cache
|
|
*
|
|
* description:
|
|
* read multi page data using cache read
|
|
*
|
|
* parameters:
|
|
* struct mtd_info *mtd, struct nand_chip *chip, int page, struct mtd_oob_ops *ops
|
|
*
|
|
* returns:
|
|
* none
|
|
*
|
|
* notes:
|
|
* only available for nand flash support cache read.
|
|
* read main data only.
|
|
*
|
|
*****************************************************************************/
|
|
#if 0
|
|
static int mt6573_nand_read_multi_page_cache(struct mtd_info *mtd, struct nand_chip *chip,
|
|
int page, struct mtd_oob_ops *ops)
|
|
{
|
|
int res = -EIO;
|
|
int len = ops->len;
|
|
struct mtd_ecc_stats stat = mtd->ecc_stats;
|
|
uint8_t *buf = ops->datbuf;
|
|
|
|
if (!mt6573_nand_ready_for_read(chip, page, 0, true, buf))
|
|
return -EIO;
|
|
|
|
while (len > 0)
|
|
{
|
|
mt6573_nand_set_mode(CNFG_OP_CUST);
|
|
DRV_WriteReg16(NFI_CON_REG16, 8 << CON_NFI_SEC_SHIFT);
|
|
|
|
if (len > mtd->writesize) // remained more than one page
|
|
{
|
|
if (!mt6573_nand_set_command(0x31)) // todo: add cache read command
|
|
goto ret;
|
|
}
|
|
else
|
|
{
|
|
if (!mt6573_nand_set_command(0x3f)) // last page remained
|
|
goto ret;
|
|
}
|
|
|
|
mt6573_nand_status_ready(STA_NAND_BUSY);
|
|
|
|
#ifdef USE_AHB_MODE
|
|
//if (!mt6573_nand_dma_read_data(buf, mtd->writesize))
|
|
if (!mt6573_nand_read_page_data(mtd, buf, mtd->writesize))
|
|
goto ret;
|
|
#else
|
|
if (!mt6573_nand_mcu_read_data(buf, mtd->writesize))
|
|
goto ret;
|
|
#endif
|
|
|
|
// get ecc error info
|
|
mt6573_nand_check_bch_error(mtd, buf, 3, page);
|
|
ECC_Decode_End();
|
|
|
|
page++;
|
|
len -= mtd->writesize;
|
|
buf += mtd->writesize;
|
|
ops->retlen += mtd->writesize;
|
|
|
|
if (len > 0)
|
|
{
|
|
ECC_Decode_Start();
|
|
mt6573_nand_reset();
|
|
}
|
|
|
|
}
|
|
|
|
res = 0;
|
|
|
|
ret:
|
|
mt6573_nand_stop_read();
|
|
|
|
if (res)
|
|
return res;
|
|
|
|
if (mtd->ecc_stats.failed > stat.failed)
|
|
{
|
|
printk(KERN_INFO "ecc fail happened\n");
|
|
return -EBADMSG;
|
|
}
|
|
|
|
return mtd->ecc_stats.corrected - stat.corrected ? -EUCLEAN: 0;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_oob_raw
|
|
*
|
|
* DESCRIPTION:
|
|
* Read oob data
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, const uint8_t *buf, int addr, int len
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* this function read raw oob data out of flash, so need to re-organise
|
|
* data format before using.
|
|
* len should be times of 8, call this after nand_get_device.
|
|
* Should notice, this function read data without ECC protection.
|
|
*
|
|
*****************************************************************************/
|
|
static int mt6573_nand_read_oob_raw(struct mtd_info *mtd, uint8_t *buf, int page_addr, int len)
|
|
{
|
|
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
|
u32 col_addr = 0;
|
|
u32 sector = 0;
|
|
int res = 0;
|
|
u32 colnob=2, rawnob;
|
|
int randomread =0;
|
|
int read_len = 0;
|
|
|
|
if (len > 128 || len % OOB_AVAI_PER_SECTOR || !buf)
|
|
{
|
|
printk(KERN_WARNING "[%s] invalid parameter, len: %d, buf: %p\n",
|
|
__FUNCTION__, len, buf);
|
|
return -EINVAL;
|
|
}
|
|
if(len>16)
|
|
{
|
|
randomread=1;
|
|
}
|
|
if(!randomread||!(devinfo.advancedmode & RAMDOM_READ))
|
|
{
|
|
//Always read from here
|
|
while (len > 0)
|
|
{
|
|
read_len = min(len, OOB_PER_SECTOR);
|
|
col_addr = NAND_SECTOR_SIZE + sector * (NAND_SECTOR_SIZE + OOB_PER_SECTOR); // TODO: Fix this hard-code 16
|
|
|
|
if (!mt6573_nand_ready_for_read(chip, page_addr, col_addr, false, NULL))
|
|
{
|
|
printk("mt6573_nand_ready_for_read return failed\n");
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
if (!mt6573_nand_mcu_read_data(buf + OOB_PER_SECTOR * sector, read_len)) // TODO: and this 8
|
|
{
|
|
printk("mt6573_nand_mcu_read_data return failed\n");
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
mt6573_nand_stop_read();
|
|
//dump_data(buf + 16 * sector,16);
|
|
sector++;
|
|
len -= read_len;
|
|
|
|
}
|
|
}
|
|
else //should be 64
|
|
{
|
|
col_addr = NAND_SECTOR_SIZE;
|
|
if (chip->options & NAND_BUSWIDTH_16)
|
|
{
|
|
col_addr /= 2;
|
|
}
|
|
|
|
if (!mt6573_nand_reset())
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
mt6573_nand_set_mode(0x6000);
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_READ_EN);
|
|
DRV_WriteReg16(NFI_CON_REG16, 4 << CON_NFI_SEC_SHIFT);
|
|
|
|
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
|
|
|
|
mt6573_nand_set_autoformat(false);
|
|
|
|
|
|
if (!mt6573_nand_set_command(NAND_CMD_READ0))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if(devinfo.pagesize == 512)
|
|
colnob = 1;
|
|
else
|
|
colnob = 2;
|
|
|
|
rawnob=devinfo.addr_cycle - colnob;
|
|
|
|
//1 FIXED ME: For Any Kind of AddrCycle
|
|
if (!mt6573_nand_set_address(col_addr, page_addr, colnob, rawnob))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if (!mt6573_nand_set_command(NAND_CMD_READSTART))
|
|
{
|
|
goto error;
|
|
}
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
|
|
read_len = min(len, OOB_PER_SECTOR);
|
|
if (!mt6573_nand_mcu_read_data(buf + OOB_PER_SECTOR * sector, read_len)) // TODO: and this 8
|
|
{
|
|
printk(KERN_WARNING "mt6573_nand_mcu_read_data return failed first 16\n");
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
sector++;
|
|
len -= read_len;
|
|
mt6573_nand_stop_read();
|
|
while(len>0)
|
|
{
|
|
read_len = min(len, 16);
|
|
if (!mt6573_nand_set_command(0x05))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
col_addr = NAND_SECTOR_SIZE + sector * (NAND_SECTOR_SIZE + 16);
|
|
if (chip->options & NAND_BUSWIDTH_16)
|
|
{
|
|
col_addr /= 2;
|
|
}
|
|
DRV_WriteReg32(NFI_COLADDR_REG32, col_addr);
|
|
DRV_WriteReg16(NFI_ADDRNOB_REG16, 2);
|
|
DRV_WriteReg16(NFI_CON_REG16, 4 << CON_NFI_SEC_SHIFT);
|
|
|
|
if( !mt6573_nand_status_ready(STA_ADDR_STATE))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if (!mt6573_nand_set_command(0xE0))
|
|
{
|
|
goto error;
|
|
}
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY))
|
|
{
|
|
goto error;
|
|
}
|
|
if (!mt6573_nand_mcu_read_data(buf + OOB_PER_SECTOR * sector, read_len)) // TODO: and this 8
|
|
{
|
|
printk(KERN_WARNING "mt6573_nand_mcu_read_data return failed first 16\n");
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
mt6573_nand_stop_read();
|
|
sector++;
|
|
len -= read_len;
|
|
}
|
|
//dump_data(&testbuf[16],16);
|
|
//printk(KERN_ERR "\n");
|
|
}
|
|
error:
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_BRD);
|
|
return res;
|
|
}
|
|
|
|
static int mt6573_nand_write_oob_raw(struct mtd_info *mtd, const uint8_t *buf, int page_addr, int len)
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
// int i;
|
|
u32 col_addr = 0;
|
|
u32 sector = 0;
|
|
// int res = 0;
|
|
// u32 colnob=2, rawnob=devinfo.addr_cycle-2;
|
|
// int randomread =0;
|
|
int write_len = 0;
|
|
int status;
|
|
|
|
#if defined(CONFIG_JFFS2_FS) && defined(TCSUPPORT_NAND_FLASH)
|
|
return 0;
|
|
#endif
|
|
//printk("mt6573_nand_write_oob_raw enter\n");
|
|
if (len > 128 || len % OOB_AVAI_PER_SECTOR || !buf)
|
|
{
|
|
printk(KERN_WARNING "[%s] invalid parameter, len: %d, buf: %p\n",
|
|
__FUNCTION__, len, buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (len > 0)
|
|
{
|
|
write_len = min(len, OOB_PER_SECTOR);
|
|
col_addr = sector * (NAND_SECTOR_SIZE + OOB_PER_SECTOR) + NAND_SECTOR_SIZE;
|
|
if (!mt6573_nand_ready_for_write(chip, page_addr, col_addr, false, NULL))
|
|
{
|
|
return -EIO;
|
|
}
|
|
|
|
if (!mt6573_nand_mcu_write_data(mtd, buf + sector * OOB_PER_SECTOR, write_len))
|
|
{
|
|
return -EIO;
|
|
}
|
|
|
|
(void)mt6573_nand_check_RW_count(write_len);
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_BWR);
|
|
(void)mt6573_nand_set_command(NAND_CMD_PAGEPROG);
|
|
|
|
while(DRV_Reg32(NFI_STA_REG32) & STA_NAND_BUSY);
|
|
|
|
status = chip->waitfunc(mtd, chip);
|
|
if (status & NAND_STATUS_FAIL)
|
|
{
|
|
printk(KERN_INFO "status: %d\n", status);
|
|
return -EIO;
|
|
}
|
|
|
|
len -= write_len;
|
|
sector++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mt6573_nand_write_oob_hw(struct mtd_info *mtd, struct nand_chip *chip, int page)
|
|
{
|
|
// u8 *buf = chip->oob_poi;
|
|
int i, iter;
|
|
|
|
memcpy(local_oob_buf, chip->oob_poi, mtd->oobsize);
|
|
//printk("mt6573_nand_write_oob_hw enter\n");
|
|
// copy ecc data
|
|
if(mtd->oobsize > 16){
|
|
for (i = 0; i < chip->ecc.layout->eccbytes; i++)
|
|
{
|
|
iter = (i / OOB_AVAI_PER_SECTOR) * OOB_PER_SECTOR + OOB_AVAI_PER_SECTOR + i % OOB_AVAI_PER_SECTOR;
|
|
local_oob_buf[iter] = chip->oob_poi[chip->ecc.layout->eccpos[i]];
|
|
// chip->oob_poi[chip->ecc.layout->eccpos[i]] = local_oob_buf[iter];
|
|
}
|
|
|
|
// copy FDM data
|
|
for (i = 0; i < (chip->ecc.layout->eccbytes / OOB_AVAI_PER_SECTOR); i++)
|
|
{
|
|
memcpy(&local_oob_buf[i * OOB_PER_SECTOR], &chip->oob_poi[i * OOB_AVAI_PER_SECTOR], OOB_AVAI_PER_SECTOR);
|
|
}
|
|
}
|
|
|
|
return mt6573_nand_write_oob_raw(mtd, local_oob_buf, page, mtd->oobsize);
|
|
}
|
|
|
|
static int mt6573_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
|
|
{
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int block = page / page_per_block;
|
|
u16 page_in_block = page % page_per_block;
|
|
u16 phy_block_bbt;
|
|
int mapped_block = get_mapping_block_index(block,&phy_block_bbt);
|
|
int i, iter;
|
|
|
|
|
|
if(g_bOOB_Test){
|
|
memcpy(local_oob_buf, chip->oob_poi, mtd->oobsize);
|
|
if(mtd->oobsize > 16){
|
|
// copy ecc data
|
|
for (i = 0; i < chip->ecc.layout->eccbytes; i++)
|
|
{
|
|
iter = (i / OOB_AVAI_PER_SECTOR) * OOB_PER_SECTOR + OOB_AVAI_PER_SECTOR + i % OOB_AVAI_PER_SECTOR;
|
|
local_oob_buf[iter] = chip->oob_poi[chip->ecc.layout->eccpos[i]];
|
|
// chip->oob_poi[chip->ecc.layout->eccpos[i]] = local_oob_buf[iter];
|
|
}
|
|
|
|
// copy FDM data
|
|
for (i = 0; i < (chip->ecc.layout->eccbytes / OOB_AVAI_PER_SECTOR); i++)
|
|
{
|
|
memcpy(&local_oob_buf[i * OOB_PER_SECTOR], &chip->oob_poi[i * OOB_AVAI_PER_SECTOR], OOB_AVAI_PER_SECTOR);
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
// write bad index into oob
|
|
#if 0
|
|
if (mapped_block != block)
|
|
{
|
|
set_bad_index_to_oob(chip->oob_poi, block);
|
|
}
|
|
else
|
|
{
|
|
set_bad_index_to_oob(chip->oob_poi, FAKE_INDEX);
|
|
}
|
|
#endif
|
|
if(block_is_in_bmt_region(mapped_block))
|
|
{
|
|
memcpy(chip->oob_poi + OOB_INDEX_OFFSET, &phy_block_bbt, OOB_INDEX_SIZE);
|
|
}
|
|
#endif
|
|
|
|
if (mt6573_nand_write_oob_hw(mtd, chip, page_in_block + mapped_block * page_per_block /* page */))
|
|
{
|
|
MSG(INIT, "write oob fail at block: 0x%x, page: 0x%x\n", mapped_block, page_in_block);
|
|
if (update_bmt((page_in_block + mapped_block * page_per_block) << chip->page_shift,
|
|
UPDATE_WRITE_FAIL, NULL, chip->oob_poi))
|
|
{
|
|
MSG(INIT, "Update BMT success\n");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
MSG(INIT, "Update BMT fail\n");
|
|
return -EIO;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int mt6573_nand_block_markbad_hw(struct mtd_info *mtd, loff_t offset, u32 bmt_block)
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
int block = (int)offset >> chip->phys_erase_shift;
|
|
int page = block * (1 << (chip->phys_erase_shift - chip->page_shift));
|
|
int ret;
|
|
unsigned int bad_flag_offset = 0;
|
|
|
|
u8 buf[8];
|
|
memset(buf, 0xFF, 8);
|
|
if(bmt_block)
|
|
bad_flag_offset = BMT_BAD_BLOCK_INDEX_OFFSET;
|
|
else{
|
|
if(chip->page_shift == 9) //512B page size
|
|
bad_flag_offset = 6;
|
|
else
|
|
bad_flag_offset = 0;
|
|
}
|
|
buf[bad_flag_offset] = 0;
|
|
|
|
ret = mt6573_nand_write_oob_raw(mtd, buf, page, 8);
|
|
return ret;
|
|
}
|
|
|
|
static int mt6573_nand_block_markbad(struct mtd_info *mtd, loff_t offset)
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
int block = (int)offset >> chip->phys_erase_shift;
|
|
int mapped_block;
|
|
int ret;
|
|
u16 phy_block_bbt;
|
|
|
|
nand_get_device(mtd, FL_WRITING);
|
|
|
|
mapped_block = get_mapping_block_index(block, &phy_block_bbt);
|
|
ret = mt6573_nand_block_markbad_hw(mtd, mapped_block << chip->phys_erase_shift, 0);
|
|
|
|
nand_release_device(mtd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int mt6573_nand_read_oob_hw(struct mtd_info *mtd,struct nand_chip *chip, int page)
|
|
{
|
|
int i;
|
|
u8 iter = 0;
|
|
#ifdef TESTTIME
|
|
unsigned long long time1,time2;
|
|
|
|
time1 = sched_clock();
|
|
#endif
|
|
|
|
if (mt6573_nand_read_oob_raw(mtd, chip->oob_poi, page, mtd->oobsize))
|
|
{
|
|
printk( "[%s]mt6573_nand_read_oob_raw return failed\n", __FUNCTION__);
|
|
return -EIO;
|
|
}
|
|
//dump_buf(chip->oob_poi, mtd->oobsize);
|
|
#ifdef TESTTIME
|
|
time2= sched_clock()-time1;
|
|
if(!readoobflag)
|
|
{
|
|
readoobflag=1;
|
|
printk(KERN_ERR "[%s] time is %llu",__FUNCTION__,time2);
|
|
}
|
|
#endif
|
|
if(mtd->oobsize== 16) //Page size is 512 bytes
|
|
return 0;
|
|
|
|
// adjust to ecc physical layout to memory layout
|
|
/*********************************************************/
|
|
/* FDM0 | ECC0 | FDM1 | ECC1 | FDM2 | ECC2 | FDM3 | ECC3 */
|
|
/* 8B | 8B | 8B | 8B | 8B | 8B | 8B | 8B */
|
|
/*********************************************************/
|
|
|
|
|
|
memcpy(local_oob_buf, chip->oob_poi, mtd->oobsize);
|
|
|
|
// copy ecc data
|
|
for (i = 0; i < chip->ecc.layout->eccbytes; i++)
|
|
{
|
|
iter = (i / OOB_AVAI_PER_SECTOR) * OOB_PER_SECTOR + OOB_AVAI_PER_SECTOR + i % OOB_AVAI_PER_SECTOR;
|
|
chip->oob_poi[chip->ecc.layout->eccpos[i]] = local_oob_buf[iter];
|
|
}
|
|
|
|
// copy FDM data
|
|
for (i = 0; i < (chip->ecc.layout->eccbytes / OOB_AVAI_PER_SECTOR); i++)
|
|
{
|
|
memcpy(&chip->oob_poi[i * OOB_AVAI_PER_SECTOR], &local_oob_buf[i * OOB_PER_SECTOR], OOB_AVAI_PER_SECTOR);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mt6573_nand_read_oob(struct mtd_info *mtd,struct nand_chip *chip, int page, int sndcmd)
|
|
{
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int block = page / page_per_block;
|
|
u16 page_in_block = page % page_per_block;
|
|
u16 phy_block_bbt;
|
|
int mapped_block = get_mapping_block_index(block, &phy_block_bbt);
|
|
|
|
mt6573_nand_read_oob_hw(mtd, chip, page_in_block + mapped_block * page_per_block);
|
|
|
|
return 0; // the return value is sndcmd
|
|
}
|
|
|
|
|
|
int mt6573_nand_block_bad_hw(struct mtd_info *mtd, loff_t ofs, u32 bmt_block)
|
|
{
|
|
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
|
int page_addr = (int)(ofs >> chip->page_shift);
|
|
unsigned int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
|
|
|
|
unsigned char oob_buf[8];
|
|
page_addr &= ~(page_per_block - 1);
|
|
unsigned int bad_flag_offset = 0;
|
|
|
|
if (mt6573_nand_read_oob_raw(mtd, oob_buf, page_addr, sizeof(oob_buf)))
|
|
{
|
|
printk(KERN_WARNING "mt6573_nand_read_oob_raw return error\n");
|
|
return 1;
|
|
}
|
|
|
|
if(bmt_block)
|
|
bad_flag_offset = BMT_BAD_BLOCK_INDEX_OFFSET;
|
|
else{
|
|
if(chip->page_shift == 9) //512B page size
|
|
bad_flag_offset = 6;
|
|
else
|
|
bad_flag_offset = 0;
|
|
}
|
|
|
|
|
|
if( oob_buf[bad_flag_offset] != 0xff){
|
|
printk(KERN_WARNING "Bad block detected at 0x%x, oob_buf[%d] is 0x%x\n", page_addr,bad_flag_offset, oob_buf[bad_flag_offset]);
|
|
// dump_nfi();
|
|
return 1;
|
|
}
|
|
|
|
return 0; // everything is OK, good block
|
|
}
|
|
|
|
|
|
static int mt6573_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
|
{
|
|
int chipnr = 0;
|
|
|
|
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
|
int block = (int)ofs >> chip->phys_erase_shift;
|
|
int mapped_block;
|
|
u16 phy_block_bbt;
|
|
|
|
int ret;
|
|
|
|
if (getchip) {
|
|
chipnr = (int)(ofs >> chip->chip_shift);
|
|
nand_get_device(mtd, FL_READING);
|
|
/* Select the NAND device */
|
|
chip->select_chip(mtd, chipnr);
|
|
}
|
|
|
|
mapped_block = get_mapping_block_index(block, &phy_block_bbt);
|
|
|
|
ret = mt6573_nand_block_bad_hw(mtd, mapped_block << chip->phys_erase_shift, 0);
|
|
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
if (ret)
|
|
{
|
|
//MSG(INIT, "Unmapped bad block: 0x%x\n", mapped_block);
|
|
if (update_bmt(mapped_block << chip->phys_erase_shift, UPDATE_UNMAPPED_BLOCK, NULL, NULL))
|
|
{
|
|
//MSG(INIT, "Update BMT success\n");
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
//MSG(INIT, "Update BMT fail\n");
|
|
ret = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (getchip)
|
|
{
|
|
nand_release_device(mtd);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_verify_buf
|
|
*
|
|
* DESCRIPTION:
|
|
* Verify the NAND write data is correct or not !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, const uint8_t *buf, int len
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
|
|
|
|
char gacBuf[4096 + 128];
|
|
|
|
static int mt6573_nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
|
{
|
|
#if 1
|
|
struct nand_chip* chip = (struct nand_chip*)mtd->priv;
|
|
struct mt6573_CMD* pkCMD = &g_kCMD;
|
|
u32 u4PageSize = mtd->writesize;
|
|
u32 *pSrc, *pDst;
|
|
int i;
|
|
|
|
mt6573_nand_exec_read_page(mtd, pkCMD->u4RowAddr, u4PageSize, gacBuf, gacBuf + u4PageSize);
|
|
|
|
pSrc = (u32*)buf;
|
|
pDst = (u32*)gacBuf;
|
|
len = len/sizeof(u32);
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
if (*pSrc != *pDst)
|
|
{
|
|
MSG(VERIFY, "mt6573_nand_verify_buf page fail at page %d\n", pkCMD->u4RowAddr);
|
|
return -1;
|
|
}
|
|
pSrc++;
|
|
pDst++;
|
|
}
|
|
|
|
pSrc = (u32*)chip->oob_poi;
|
|
pDst = (u32*)(gacBuf + u4PageSize);
|
|
|
|
if ((pSrc[0] != pDst[0]) || (pSrc[1] != pDst[1]) ||
|
|
(pSrc[2] != pDst[2]) || (pSrc[3] != pDst[3]) ||
|
|
(pSrc[4] != pDst[4]) || (pSrc[5] != pDst[5]))
|
|
// TODO: Ask Designer Why?
|
|
//(pSrc[6] != pDst[6]) || (pSrc[7] != pDst[7]))
|
|
{
|
|
MSG(VERIFY, "mt6573_nand_verify_buf oob fail at page %d\n", pkCMD->u4RowAddr);
|
|
MSG(VERIFY, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
|
pSrc[0], pSrc[1], pSrc[2], pSrc[3], pSrc[4], pSrc[5], pSrc[6], pSrc[7]);
|
|
MSG(VERIFY, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
|
pDst[0], pDst[1], pDst[2], pDst[3], pDst[4], pDst[5], pDst[6], pDst[7]);
|
|
return -1;
|
|
}
|
|
/*
|
|
for (i = 0; i < len; ++i) {
|
|
if (*pSrc != *pDst) {
|
|
printk(KERN_ERR"mt6573_nand_verify_buf oob fail at page %d\n", g_kCMD.u4RowAddr);
|
|
return -1;
|
|
}
|
|
pSrc++;
|
|
pDst++;
|
|
}
|
|
*/
|
|
//printk(KERN_INFO"mt6573_nand_verify_buf OK at page %d\n", g_kCMD.u4RowAddr);
|
|
|
|
return 0;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_init_hw
|
|
*
|
|
* DESCRIPTION:
|
|
* Initial NAND device hardware component !
|
|
*
|
|
* PARAMETERS:
|
|
* struct mt6573_nand_host *host (Initial setting data)
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_init_hw(struct mt6573_nand_host *host)
|
|
{
|
|
struct mt6573_nand_host_hw *hw = host->hw;
|
|
|
|
|
|
MSG(INIT, "Enable NFI Clock\n");
|
|
nand_enable_clock();
|
|
|
|
g_bInitDone = false;
|
|
/* Get the HW_VER */
|
|
//g_u4ChipVer = DRV_Reg32(CONFIG_BASE);
|
|
g_kCMD.u4OOBRowAddr = (u32)-1;
|
|
|
|
/* Set default NFI access timing control */
|
|
DRV_WriteReg32(NFI_ACCCON_REG32, hw->nfi_access_timing);
|
|
DRV_WriteReg16(NFI_CNFG_REG16, 0);
|
|
DRV_WriteReg16(NFI_PAGEFMT_REG16, 0);
|
|
|
|
/* Reset the state machine and data FIFO, because flushing FIFO */
|
|
(void)mt6573_nand_reset();
|
|
|
|
/* Set the ECC engine */
|
|
if(hw->nand_ecc_mode == NAND_ECC_HW)
|
|
{
|
|
MSG(INIT, "%s : Use HW ECC\n", MODULE_NAME);
|
|
if(g_bHwEcc){
|
|
NFI_SET_REG32(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
if(ECC_Config(host->hw))
|
|
return -1;
|
|
mt6573_nand_configure_fdm(8);
|
|
mt6573_nand_configure_lock();
|
|
}
|
|
|
|
/*set pio mode as big endian, sycn as dma mode*/
|
|
//NFI_SET_REG32(NFI_CNFG_REG16, CNFG_PIO_BIG_ENDIAN);
|
|
/* Initilize interrupt. Clear interrupt, read clear. */
|
|
DRV_Reg16(NFI_INTR_REG16);
|
|
|
|
/* Interrupt arise when read data or program data to/from AHB is done. */
|
|
DRV_WriteReg16(NFI_INTR_EN_REG16, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------
|
|
static int mt6573_nand_dev_ready(struct mtd_info *mtd)
|
|
{
|
|
return !(DRV_Reg32(NFI_STA_REG32) & STA_NAND_BUSY);
|
|
}
|
|
|
|
static void mt6573_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
|
{
|
|
}
|
|
|
|
|
|
static int mt6573_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
|
u_char *read_ecc, u_char *calc_ecc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int mt6573_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
unsigned int ra_nand_read_byte(unsigned long long from)
|
|
{
|
|
unsigned char ch;
|
|
size_t retlen;
|
|
//ramtd_nand_read(ranfc_mtd, (loff_t)from, 1, &retlen, &ch);
|
|
host->mtd._read(&(host->mtd), (loff_t)from, 1, &retlen, &ch);
|
|
return ch;
|
|
}
|
|
|
|
|
|
unsigned int ra_nand_read_dword(unsigned long long from)
|
|
{
|
|
unsigned char data[5];
|
|
unsigned long dword;
|
|
size_t retlen;
|
|
int ret = 0, i;
|
|
|
|
host->mtd._read(&(host->mtd), (loff_t)from, 4, &retlen, data);
|
|
if (ret != 0)
|
|
return -1;
|
|
|
|
dword = 0;
|
|
for (i=0; i<4; i++) {
|
|
dword += (unsigned long)data[i];
|
|
if (i<3)
|
|
dword <<= 8;
|
|
}
|
|
return dword;
|
|
}
|
|
|
|
#ifdef NAND_ECC_TEST
|
|
/******************************************************************************
|
|
* mt6573_nand_proc_read
|
|
*
|
|
* DESCRIPTION:
|
|
* Read the proc file to get the interrupt scheme setting !
|
|
*
|
|
* PARAMETERS:
|
|
* char *page, char **start, off_t off, int count, int *eof, void *data
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_ecc_proc_read(char *page, char **start, off_t off,
|
|
int count, int *eof, void *data)
|
|
{
|
|
int len;
|
|
if (off > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
// return sprintf(page, "Interrupt-Scheme is %d\n", g_i4Interrupt);
|
|
len = sprintf(page, "hw ecc is %d bit,spare size is %d\n", g_hw_ecc_bit, g_spare_size);
|
|
|
|
return len;
|
|
}
|
|
|
|
int mt6573_nand_ecc_test(int max_errors);
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_proc_write
|
|
*
|
|
* DESCRIPTION:
|
|
* Write the proc file to set the interrupt scheme !
|
|
*
|
|
* PARAMETERS:
|
|
* struct file* file, const char* buffer, unsigned long count, void *data
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_ecc_proc_write(struct file* file, const char* buffer,
|
|
unsigned long count, void *data)
|
|
{
|
|
char buf[16];
|
|
int len = count, n;
|
|
|
|
if (len >= sizeof(buf))
|
|
{
|
|
len = sizeof(buf) - 1;
|
|
}
|
|
|
|
if (copy_from_user(buf, buffer, len))
|
|
{
|
|
return -EFAULT;
|
|
}
|
|
|
|
buf[len] = '\0';
|
|
|
|
if(buf[0] == 'T'){
|
|
n = simple_strtol(buf+1, NULL, 10);
|
|
mt6573_nand_ecc_test(n);
|
|
}
|
|
else if(buf[0] == 'S'){
|
|
n = simple_strtol(buf+1, NULL, 10);
|
|
if(n == 16 || n == 26 || n == 27 || n == 28){
|
|
g_spare_size = n;
|
|
//ECC_Config_All_Bits(g_hw_ecc_bit);
|
|
}
|
|
else{
|
|
printk("spare size must be 16/26/27/28\n");
|
|
}
|
|
}
|
|
else{
|
|
|
|
n = simple_strtol(buf, NULL, 10);
|
|
|
|
ECC_Config_All_Bits(n);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
#if 1
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_read_oob_raw
|
|
*
|
|
* DESCRIPTION:
|
|
* Read oob data
|
|
*
|
|
* PARAMETERS:
|
|
* struct mtd_info *mtd, const uint8_t *buf, int addr, int len
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* this function read raw oob data out of flash, so need to re-organise
|
|
* data format before using.
|
|
* len should be times of 8, call this after nand_get_device.
|
|
* Should notice, this function read data without ECC protection.
|
|
*
|
|
*****************************************************************************/
|
|
static int mt6573_nand_read_oob_raw_more(struct mtd_info *mtd, uint8_t *buf, int page_addr, int len)
|
|
{
|
|
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
|
u32 col_addr = 0;
|
|
u32 sector = 0;
|
|
int res = 0;
|
|
int randomread =0;
|
|
int read_len = 0;
|
|
int oob_per_sector = g_spare_size;
|
|
|
|
if (len > 232 || len % OOB_AVAI_PER_SECTOR || !buf)
|
|
{
|
|
printk(KERN_WARNING "[%s] invalid parameter, len: %d, buf: %p\n",
|
|
__FUNCTION__, len, buf);
|
|
return -EINVAL;
|
|
}
|
|
if(len>16)
|
|
{
|
|
randomread=1;
|
|
}
|
|
/*TOSHIBA TC58NVG3S0F 4K Page size*/
|
|
/*Spare erea is 232 Bytes, oob per sector is 29=232/8*/
|
|
if(!randomread||!(devinfo.advancedmode & RAMDOM_READ))
|
|
{
|
|
//Always read from here
|
|
while (len > 0)
|
|
{
|
|
read_len = min(len, oob_per_sector);
|
|
col_addr = NAND_SECTOR_SIZE + sector * (NAND_SECTOR_SIZE + oob_per_sector); // TODO: Fix this hard-code 16
|
|
//printk("mt6573_nand_read_oob_raw_more: page_addr=%d,col_addr=%d, readlen=%d\n", page_addr,col_addr, read_len);
|
|
if (!mt6573_nand_ready_for_read(chip, page_addr, col_addr, false, NULL))
|
|
{
|
|
printk("mt6573_nand_ready_for_read return failed\n");
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
if (!mt6573_nand_mcu_read_data(buf + oob_per_sector * sector, read_len)) // TODO: and this 8
|
|
{
|
|
printk("mt6573_nand_mcu_read_data return failed\n");
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
mt6573_nand_stop_read();
|
|
//dump_data(buf + 16 * sector,16);
|
|
sector++;
|
|
len -= read_len;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
error:
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_BRD);
|
|
return res;
|
|
}
|
|
#if 0
|
|
static int mt6573_nand_write_oob_raw_more(struct mtd_info *mtd, const uint8_t *buf, int page_addr, int len)
|
|
{
|
|
struct nand_chip *chip = mtd->priv;
|
|
// int i;
|
|
u32 col_addr = 0;
|
|
u32 sector = 0;
|
|
// int res = 0;
|
|
// u32 colnob=2, rawnob=devinfo.addr_cycle-2;
|
|
// int randomread =0;
|
|
int write_len = 0;
|
|
int status;
|
|
int oob_per_sector = g_spare_size;
|
|
|
|
//printk("mt6573_nand_write_oob_raw enter\n");
|
|
if (len > 232 || len % OOB_AVAI_PER_SECTOR || !buf)
|
|
{
|
|
printk(KERN_WARNING "[%s] invalid parameter, len: %d, buf: %p\n",
|
|
__FUNCTION__, len, buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (len > 0)
|
|
{
|
|
write_len = min(len, oob_per_sector);
|
|
col_addr = sector * (NAND_SECTOR_SIZE + oob_per_sector) + NAND_SECTOR_SIZE;
|
|
if (!mt6573_nand_ready_for_write(chip, page_addr, col_addr, false, NULL))
|
|
{
|
|
return -EIO;
|
|
}
|
|
|
|
if (!mt6573_nand_mcu_write_data(mtd, buf + sector * oob_per_sector, write_len))
|
|
{
|
|
return -EIO;
|
|
}
|
|
(void)mt6573_nand_check_RW_count(write_len);
|
|
NFI_CLN_REG16(NFI_CON_REG16, CON_NFI_BWR);
|
|
(void)mt6573_nand_set_command(NAND_CMD_PAGEPROG);
|
|
|
|
while(DRV_Reg32(NFI_STA_REG32) & STA_NAND_BUSY);
|
|
|
|
status = chip->waitfunc(mtd, chip);
|
|
if (status & NAND_STATUS_FAIL)
|
|
{
|
|
printk(KERN_INFO "status: %d\n", status);
|
|
return -EIO;
|
|
}
|
|
|
|
len -= write_len;
|
|
sector++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
/**********************************************************
|
|
Description : CM_ECC_Invert_Bits
|
|
Input :
|
|
Output : 0
|
|
***********************************************************/
|
|
void CM_ECC_Invert_Bits(unsigned char* buff_ptr, unsigned int bit_pos)
|
|
{
|
|
unsigned int byte_pos = 0;
|
|
unsigned char byte_val = 0;
|
|
unsigned char temp_val = 0;
|
|
unsigned int invert_bit = 0;
|
|
|
|
byte_pos = bit_pos >> 3;
|
|
invert_bit = bit_pos & ((1<<3)-1);
|
|
byte_val = buff_ptr[byte_pos];
|
|
temp_val = byte_val & (1 << invert_bit);
|
|
|
|
if(temp_val > 0)
|
|
byte_val &= ~temp_val;
|
|
else
|
|
byte_val |= (1 << invert_bit);
|
|
buff_ptr[byte_pos] = byte_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//char oob_buf[234] = {0};
|
|
//char read_buf[4097] = {0};
|
|
|
|
#define DATA_BUF_SIZE (4096+234+1)
|
|
|
|
int mt6573_nand_ecc_test(int max_errors)
|
|
{
|
|
int block_index, page_addr, i, oob_size, error=0;
|
|
struct nand_chip *chip = (struct nand_chip *)host->mtd.priv;
|
|
int page_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
|
|
int page_size = 1 << chip->page_shift;
|
|
int block_size = 1 << chip->phys_erase_shift;
|
|
|
|
int ecc_error_bit, hw_ecc_bits = g_hw_ecc_bit;
|
|
int result = 0, sector_num = 0;
|
|
int max_error_bits = g_hw_ecc_bit;
|
|
|
|
unsigned long long offset, len;
|
|
char *data_buffer = NULL;
|
|
char *read_buf = NULL;
|
|
|
|
char *size_4K_page_data = NULL;
|
|
|
|
size_4K_page_data = kmalloc(4097, GFP_KERNEL);
|
|
if(!size_4K_page_data){
|
|
printk("alloc read_buf buffer failed!\n");
|
|
return -1;
|
|
}
|
|
memset(size_4K_page_data,0x5a, 4097);
|
|
read_buf = kmalloc(4097, GFP_KERNEL);
|
|
if(!read_buf){
|
|
printk("alloc read_buf buffer failed!\n");
|
|
return -1;
|
|
}
|
|
|
|
//g_spare_size = 26;
|
|
if(max_errors){
|
|
max_error_bits = max_errors;
|
|
}
|
|
|
|
|
|
printk("page_per_block=%d, page_size=%d, block_size=%d\n", page_per_block, page_size, block_size);
|
|
//1.Select a random block and erase it
|
|
//get_random_bytes??
|
|
//block_index = random() % (chip->chipsize/(1 << chip->phys_erase_shift));
|
|
block_index = 0;
|
|
|
|
printk("block_index=%d\n", block_index);
|
|
|
|
mt6573_nand_erase(&(host->mtd), block_index*page_per_block);
|
|
|
|
|
|
|
|
//2.Write a page data with HW_ECC on
|
|
g_bAutoFMT = true;
|
|
g_bHwEcc = true;
|
|
g_bUseAHBMode = true;
|
|
g_bOOB_Test = false;
|
|
|
|
/*Write to 2nd Page*/
|
|
page_addr = block_index*page_per_block+1;
|
|
offset = (unsigned long long)(page_addr*page_size);
|
|
|
|
printk("write offset=%llu\n", offset);
|
|
|
|
host->mtd.write(&(host->mtd), (loff_t)offset, (size_t)page_size, (size_t *)&len, size_4K_page_data);
|
|
|
|
|
|
//3.Read the page main data and oob data
|
|
data_buffer = kmalloc(DATA_BUF_SIZE, GFP_KERNEL);
|
|
if(!data_buffer){
|
|
printk("alloc data buffer failed!\n");
|
|
return -1;
|
|
}
|
|
memset(data_buffer, 0, DATA_BUF_SIZE);
|
|
host->mtd.read(&(host->mtd), (loff_t)offset, (size_t)page_size, (size_t *)&len, data_buffer);
|
|
|
|
for (i=0; i<page_size; i++) {
|
|
if (data_buffer[i] != size_4K_page_data[i]) {
|
|
printk("basic write read test fail, i=%d\n", i);
|
|
//err++;
|
|
|
|
#if 0
|
|
printk("*************RBuf***************\n");
|
|
dump_buf(data_buffer, page_size);
|
|
//printk("**************WBuf**************\n");
|
|
//dump_buf(size_4K_page_data, (int)oob_size);
|
|
//printk("****************************\n");
|
|
#endif
|
|
result = -1;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
//read oob...
|
|
oob_size = (1 << (chip->page_shift - 9)) * g_spare_size;
|
|
|
|
mt6573_nand_read_oob_raw_more(&(host->mtd), data_buffer+page_size, page_addr, oob_size);
|
|
|
|
|
|
for(ecc_error_bit = 1; ecc_error_bit <= max_errors; ecc_error_bit++){
|
|
|
|
//4.Create the test data with 4~16 bit error for every sector
|
|
for(sector_num = 0; sector_num < (page_size/NAND_SECTOR_SIZE); sector_num++){
|
|
CM_ECC_Invert_Bits(data_buffer, sector_num*NAND_SECTOR_SIZE*8+ecc_error_bit*200);
|
|
}
|
|
#if 1
|
|
error =0;
|
|
|
|
for (i=0; i<page_size; i++) {
|
|
if (data_buffer[i] != size_4K_page_data[i]) {
|
|
error++;
|
|
}
|
|
}
|
|
printk("%d bit error test\n", error);
|
|
#endif
|
|
|
|
//5.Write the test data to the next page in PIO mode and HW_ECC is off
|
|
g_bAutoFMT = false;
|
|
g_bHwEcc = false;
|
|
g_bUseAHBMode = false;
|
|
g_bOOB_Test = true;
|
|
|
|
page_addr++;//Select the next page
|
|
offset = (unsigned long long)(page_addr*page_size);
|
|
//mt6573_nand_write_oob_raw_more(&(host->mtd), data_buffer+page_size, page_addr, oob_size);
|
|
|
|
|
|
memcpy(local_oob_buf, data_buffer+page_size, oob_size);
|
|
|
|
host->mtd.write(&(host->mtd), (loff_t)offset, (size_t)page_size, (size_t *)&len, data_buffer);
|
|
|
|
//6.Read the data from the next page when HW_ECC is on
|
|
g_bAutoFMT = true;
|
|
g_bHwEcc = true;
|
|
g_bUseAHBMode = true;
|
|
g_bOOB_Test = false;
|
|
|
|
memset(read_buf, 0, 4097);
|
|
|
|
host->mtd.read(&(host->mtd), (loff_t)offset, (size_t)page_size, (size_t *)&len, read_buf);
|
|
|
|
if(ecc_error_bit <= hw_ecc_bits){
|
|
|
|
//7.Check whether the error bit be corrected
|
|
for (i=0; i<page_size; i++) {
|
|
if (read_buf[i] != size_4K_page_data[i]) {
|
|
printk("%d bit ecc correct test fail, i=%d\n", ecc_error_bit, i);
|
|
//err++;
|
|
result = -1;
|
|
goto end;
|
|
}
|
|
}
|
|
printk("%d bit error test PASS\n", error);
|
|
}
|
|
else{
|
|
#if 0
|
|
printk("%d bit error, more than hw ecc bits,ignore\n", error);
|
|
uncorrect_num = 0;
|
|
for (i=0; i<page_size; i++) {
|
|
if (read_buf[i] != size_4K_page_data[i]) {
|
|
//printk("%d bit ecc correct test fail, i=%d\n", ecc_error_bit, i);
|
|
uncorrect_num++;
|
|
}
|
|
}
|
|
if(uncorrect_num)
|
|
printk("%d bit error test: uncorrect_num=%d\n", error, uncorrect_num);
|
|
else
|
|
printk("%d bit error test PASS\n", error);
|
|
#endif
|
|
}
|
|
printk("**********************************************\n");
|
|
|
|
}
|
|
|
|
|
|
end:
|
|
if(size_4K_page_data){
|
|
kfree(size_4K_page_data);
|
|
}
|
|
|
|
if(read_buf){
|
|
kfree(read_buf);
|
|
}
|
|
if(data_buffer){
|
|
kfree(data_buffer);
|
|
}
|
|
if(result){
|
|
printk("NAND ECC Test Fail!!\n");
|
|
}
|
|
else{
|
|
printk("NAND ECC Test PASS!!\n");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_proc_read
|
|
*
|
|
* DESCRIPTION:
|
|
* Read the proc file to get the interrupt scheme setting !
|
|
*
|
|
* PARAMETERS:
|
|
* char *page, char **start, off_t off, int count, int *eof, void *data
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_proc_read(char *page, char **start, off_t off,
|
|
int count, int *eof, void *data)
|
|
{
|
|
int len;
|
|
int nand_mode = 0;
|
|
if (off > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
nand_mode = (g_bAutoFMT << 3) | (g_bHwEcc << 2) | (g_i4Interrupt << 1) | g_bUseAHBMode;
|
|
// return sprintf(page, "Interrupt-Scheme is %d\n", g_i4Interrupt);
|
|
len = sprintf(page, "%d\n", nand_mode);
|
|
len += sprintf(page + len, "Name: %s, ID: 0x%x, total size: %dMiB, page size: %d B\n", devinfo.devciename, devinfo.id,
|
|
devinfo.totalsize, devinfo.pagesize);
|
|
len += sprintf(page + len, "Current Interrupt is %s, working in %s mode \nHW_ECC is %s, AUTO_FMT is %s \nOOB_Test is %s\n",
|
|
g_i4Interrupt ? "ON" : "OFF",
|
|
g_bUseAHBMode? "DMA" : "PIO", g_bHwEcc? "ON" : "OFF", g_bAutoFMT? "ON" : "OFF", g_bOOB_Test? "ON" : "OFF");
|
|
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_proc_write
|
|
*
|
|
* DESCRIPTION:
|
|
* Write the proc file to set the interrupt scheme !
|
|
*
|
|
* PARAMETERS:
|
|
* struct file* file, const char* buffer, unsigned long count, void *data
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_proc_write(struct file* file, const char* buffer,
|
|
unsigned long count, void *data)
|
|
{
|
|
struct mtd_info *mtd = &host->mtd;
|
|
char buf[16];
|
|
int len = count, n;
|
|
|
|
if (len >= sizeof(buf))
|
|
{
|
|
len = sizeof(buf) - 1;
|
|
}
|
|
|
|
if (copy_from_user(buf, buffer, len))
|
|
{
|
|
return -EFAULT;
|
|
}
|
|
|
|
buf[len] = '\0';
|
|
if (buf[0] == 'I')
|
|
{
|
|
// sync before switching between polling and interrupt,
|
|
n = simple_strtol(buf+1, NULL, 10);
|
|
|
|
if ((n > 0 && !g_i4Interrupt) ||
|
|
(n == 0 && g_i4Interrupt))
|
|
{
|
|
nand_get_device(mtd, FL_READING);
|
|
|
|
g_i4Interrupt = n;
|
|
|
|
if (g_i4Interrupt)
|
|
{
|
|
DRV_Reg16(NFI_INTR_REG16);
|
|
enable_irq(MT6573_NFI_IRQ_LINE);
|
|
}
|
|
else{
|
|
disable_irq(MT6573_NFI_IRQ_LINE);
|
|
}
|
|
|
|
nand_release_device(mtd);
|
|
}
|
|
}
|
|
|
|
if (buf[0] == 'D')
|
|
{
|
|
#ifdef _MTK_NAND_DUMMY_DRIVER_
|
|
printk(KERN_INFO "Enable dummy driver\n");
|
|
dummy_driver_debug = 1;
|
|
#endif
|
|
}
|
|
|
|
#ifdef NAND_PFM
|
|
if (buf[0] == 'P')
|
|
{
|
|
/* Reset values */
|
|
g_PFM_R = 0;
|
|
g_PFM_W = 0;
|
|
g_PFM_E = 0;
|
|
g_PFM_RD = 0;
|
|
g_PFM_WD = 0;
|
|
g_kCMD.pureReadOOBNum = 0;
|
|
}
|
|
#endif
|
|
|
|
//AHB/PIO mode Switch
|
|
if(buf[0] == 'A'){
|
|
// sync before switching between polling and interrupt,
|
|
n = simple_strtol(buf+1, NULL, 10);
|
|
|
|
if ((n == 1 && !g_bUseAHBMode) ||
|
|
(n == 0 && g_bUseAHBMode))
|
|
{
|
|
nand_get_device(mtd, FL_READING);
|
|
|
|
if(n){
|
|
g_bUseAHBMode = true;
|
|
}
|
|
else{
|
|
g_bUseAHBMode = false;
|
|
}
|
|
|
|
|
|
nand_release_device(mtd);
|
|
}
|
|
}
|
|
|
|
//HW_ECC Switch
|
|
if(buf[0] == 'E'){
|
|
// sync before switching between polling and interrupt,
|
|
n = simple_strtol(buf+1, NULL, 10);
|
|
|
|
if ((n == 1 && !g_bHwEcc) ||
|
|
(n == 0 && g_bHwEcc))
|
|
{
|
|
nand_get_device(mtd, FL_READING);
|
|
|
|
if(n){
|
|
g_bHwEcc = true;
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
else{
|
|
g_bHwEcc = false;
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
|
|
|
|
nand_release_device(mtd);
|
|
}
|
|
}
|
|
|
|
//AUTO_FMT Switch
|
|
if(buf[0] == 'F'){
|
|
// sync before switching between polling and interrupt,
|
|
n = simple_strtol(buf+1, NULL, 10);
|
|
|
|
if ((n == 1 && !g_bAutoFMT) ||
|
|
(n == 0 && g_bAutoFMT))
|
|
{
|
|
nand_get_device(mtd, FL_READING);
|
|
|
|
if(n){
|
|
g_bAutoFMT = true;
|
|
mt6573_nand_set_autoformat(true);
|
|
}
|
|
else{
|
|
g_bAutoFMT = false;
|
|
mt6573_nand_set_autoformat(false);
|
|
}
|
|
|
|
|
|
nand_release_device(mtd);
|
|
}
|
|
}
|
|
|
|
//OOB Test Switch
|
|
if(buf[0] == 'O'){
|
|
// sync before switching between polling and interrupt,
|
|
n = simple_strtol(buf+1, NULL, 10);
|
|
|
|
if ((n == 1 && !g_bOOB_Test) ||
|
|
(n == 0 && g_bOOB_Test))
|
|
{
|
|
nand_get_device(mtd, FL_READING);
|
|
|
|
if(n){
|
|
g_bOOB_Test = true;
|
|
}
|
|
else{
|
|
g_bOOB_Test = false;
|
|
}
|
|
|
|
|
|
nand_release_device(mtd);
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
int calc_bmt_pool_size(struct mtd_info *mtd)
|
|
{
|
|
struct nand_chip *nand = mtd->priv;
|
|
int chip_size = nand->chipsize;
|
|
int block_size = 1 << nand->phys_erase_shift;
|
|
int total_block = chip_size / block_size;
|
|
int last_block = total_block - 1;
|
|
|
|
u16 valid_block_num = 0;
|
|
u16 need_valid_block_num = total_block * POOL_GOOD_BLOCK_PERCENT;
|
|
#if 0
|
|
printk("need_valid_block_num:%d \n", need_valid_block_num);
|
|
printk("total block:%d \n", total_block);
|
|
#endif
|
|
for(;last_block > 0; --last_block)
|
|
{
|
|
if(mt6573_nand_block_bad_hw(mtd, last_block * block_size, BAD_BLOCK_RAW))
|
|
{
|
|
continue;
|
|
|
|
}
|
|
else
|
|
{
|
|
valid_block_num++;
|
|
if(valid_block_num == need_valid_block_num)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (total_block - last_block);
|
|
|
|
}
|
|
#endif
|
|
|
|
static int mt6573_nand_setup(struct mt6573_nand_host_hw *hw)
|
|
{
|
|
//struct mt6573_nand_host_hw *hw;
|
|
struct mtd_info *mtd;
|
|
struct nand_chip *nand_chip;
|
|
int err = 0;
|
|
int id;
|
|
u32 ext_id;
|
|
u8 ext_id1, ext_id2, ext_id3;
|
|
int num;
|
|
|
|
/* OSBNB00043636: to Fix NAND DualImage , tclinux related MTD partition can't be programmed issue 20130313 Byron */
|
|
#if defined(TCSUPPORT_DUAL_IMAGE_ENHANCE)
|
|
u32 erase_unit;
|
|
#endif
|
|
|
|
/***********************************/
|
|
//return -1;
|
|
/********************************/
|
|
/* Allocate memory for the device structure (and zero it) */
|
|
host = kzalloc(sizeof(struct mt6573_nand_host), GFP_KERNEL);
|
|
if (!host)
|
|
{
|
|
MSG(INIT, "mt6573_nand: failed to allocate device structure.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Allocate memory for 16 byte aligned buffer */
|
|
local_buffer_16_align = local_buffer + 16 - ((u32)local_buffer % 16);
|
|
printk( "Allocate 16 byte aligned buffer: %p\n", local_buffer_16_align);
|
|
|
|
host->hw = hw;
|
|
|
|
/* init mtd data structure */
|
|
nand_chip = &host->nand_chip;
|
|
nand_chip->priv = host; /* link the private data structures */
|
|
|
|
mtd = &host->mtd;
|
|
mtd->priv = nand_chip;
|
|
mtd->owner = THIS_MODULE;
|
|
mtd->name = "MT6573-Nand";
|
|
|
|
hw->nand_ecc_mode = NAND_ECC_HW;
|
|
|
|
/* Set address of NAND IO lines */
|
|
nand_chip->IO_ADDR_R = (void __iomem*)NFI_DATAR_REG32;
|
|
nand_chip->IO_ADDR_W = (void __iomem*)NFI_DATAW_REG32;
|
|
nand_chip->chip_delay = 20; /* 20us command delay time */
|
|
nand_chip->ecc.mode = hw->nand_ecc_mode; /* enable ECC */
|
|
|
|
nand_chip->read_byte = mt6573_nand_read_byte;
|
|
nand_chip->read_buf = mt6573_nand_read_buf;
|
|
nand_chip->write_buf = mt6573_nand_write_buf;
|
|
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
|
|
nand_chip->verify_buf = mt6573_nand_verify_buf;
|
|
#endif
|
|
nand_chip->select_chip = mt6573_nand_select_chip;
|
|
nand_chip->dev_ready = mt6573_nand_dev_ready;
|
|
nand_chip->cmdfunc = mt6573_nand_command_bp;
|
|
nand_chip->ecc.read_page = mt6573_nand_read_page_hwecc;
|
|
nand_chip->ecc.write_page = mt6573_nand_write_page_hwecc;
|
|
|
|
|
|
nand_chip->ecc.layout = &nand_oob_64;
|
|
nand_chip->ecc.size = hw->nand_ecc_size; //2048
|
|
nand_chip->ecc.bytes = hw->nand_ecc_bytes; //32
|
|
//nand_chip->options = NAND_USE_FLASH_BBT;
|
|
nand_chip->options = NAND_SKIP_BBTSCAN;
|
|
//nand_chip->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
|
|
/*
|
|
BBT_AUTO_REFRESH |
|
|
NAND_NO_SUBPAGE_WRITE |
|
|
NAND_NO_AUTOINCR;
|
|
*/
|
|
|
|
// For BMT, we need to revise driver architecture
|
|
nand_chip->write_page = mt6573_nand_write_page;
|
|
nand_chip->ecc.write_oob = mt6573_nand_write_oob;
|
|
//nand_chip->read_page = mt6573_nand_read_page;
|
|
nand_chip->ecc.read_oob = mt6573_nand_read_oob;
|
|
nand_chip->block_markbad = mt6573_nand_block_markbad; // need to add nand_get_device()/nand_release_device().
|
|
//nand_chip->erase = mt6573_nand_erase;
|
|
nand_chip->block_bad = mt6573_nand_block_bad;
|
|
|
|
nand_chip->ecc.calculate = mt6573_nand_calculate_ecc;
|
|
nand_chip->ecc.correct = mt6573_nand_correct_data;
|
|
nand_chip->ecc.hwctl = mt6573_nand_enable_hwecc;
|
|
|
|
|
|
err = mt6573_nand_init_hw(host);
|
|
if(err != 0){
|
|
MSG(INIT, "Init HW fail! \r\n");
|
|
goto out;
|
|
}
|
|
/* Select the device */
|
|
nand_chip->select_chip(mtd, 0);
|
|
|
|
/*
|
|
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
|
|
* after power-up
|
|
*/
|
|
nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
|
|
|
|
|
/* Send the command for reading device ID */
|
|
nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
|
|
|
|
|
/* Read manufacturer and device IDs */
|
|
manu_id = nand_chip->read_byte(mtd);
|
|
dev_id = nand_chip->read_byte(mtd);
|
|
printk("manu_id=%x, dev_id=%x\n", manu_id , dev_id);
|
|
|
|
ext_id1 = nand_chip->read_byte(mtd);
|
|
ext_id2 = nand_chip->read_byte(mtd);
|
|
ext_id3 = nand_chip->read_byte(mtd);
|
|
ext_id = ext_id1 << 16 | ext_id2 << 8 | ext_id3;
|
|
|
|
printk("ext_id=%x,\n", ext_id );
|
|
|
|
//Check NAND Info
|
|
// id = (dev_id<<8)|manu_id;
|
|
id = dev_id | (manu_id << 8);
|
|
if(!get_device_info(id, ext_id, &devinfo))
|
|
{
|
|
MSG(INIT, "Not Support this Device! \r\n");
|
|
}
|
|
|
|
if (devinfo.pagesize == 4096) {
|
|
nand_chip->ecc.layout = &nand_oob_128;
|
|
nand_chip->ecc.size = 4096;
|
|
nand_chip->ecc.bytes = 64;
|
|
} else if (devinfo.pagesize == 2048) {
|
|
nand_chip->ecc.layout = &nand_oob_64;
|
|
nand_chip->ecc.size = 2048;
|
|
nand_chip->ecc.bytes = 32;
|
|
} else if (devinfo.pagesize == 512) {
|
|
nand_chip->ecc.layout = &nand_oob_16;
|
|
nand_chip->ecc.size = 512;
|
|
nand_chip->ecc.bytes = 8;
|
|
}
|
|
MSG(INIT, "[NAND] pagesz:%d eccsz: %d, oobsz: %d\n",
|
|
nand_chip->ecc.size, nand_chip->ecc.bytes, sizeof(g_kCMD.au1OOB));
|
|
|
|
MSG(INIT, "Support this Device in MTK table! %x \r\n",id);
|
|
hw->nfi_bus_width = devinfo.iowidth;
|
|
DRV_WriteReg32(NFI_ACCCON_REG32, devinfo.timmingsetting);
|
|
|
|
/* 16-bit bus width */
|
|
if (hw->nfi_bus_width == 16)
|
|
{
|
|
MSG(INIT, "%s : Set the 16-bit I/O settings!\n", MODULE_NAME);
|
|
nand_chip->options |= NAND_BUSWIDTH_16;
|
|
}
|
|
/* register NFI IRQ handler. */
|
|
err = request_irq(MT6573_NFI_IRQ_LINE, (irq_handler_t)mt6573_nand_irq_handler, 0,
|
|
"mt6573-nand", NULL);
|
|
if (0 != err)
|
|
{
|
|
MSG(INIT, "%s : Request IRQ fail: err = %d\n", MODULE_NAME, err);
|
|
goto out;
|
|
}
|
|
|
|
if (g_i4Interrupt)
|
|
enable_irq(MT6573_NFI_IRQ_LINE);
|
|
else
|
|
disable_irq(MT6573_NFI_IRQ_LINE);
|
|
#if 0
|
|
if (devinfo.advancedmode & CACHE_READ)
|
|
{
|
|
nand_chip->ecc.read_multi_page_cache = NULL;
|
|
// nand_chip->ecc.read_multi_page_cache = mt6573_nand_read_multi_page_cache;
|
|
// MSG(INIT, "Device %x support cache read \r\n",id);
|
|
}
|
|
else
|
|
nand_chip->ecc.read_multi_page_cache = NULL;
|
|
#endif
|
|
/* Scan to find existance of the device */
|
|
if (nand_scan(mtd, hw->nfi_cs_num))
|
|
{
|
|
MSG(INIT, "%s : nand_scan fail.\n", MODULE_NAME);
|
|
err = -ENXIO;
|
|
goto out;
|
|
}
|
|
g_page_size = mtd->writesize;
|
|
//platform_set_drvdata(pdev, host);
|
|
|
|
if (hw->nfi_bus_width == 16)
|
|
{
|
|
NFI_SET_REG16(NFI_PAGEFMT_REG16, PAGEFMT_DBYTE_EN);
|
|
}
|
|
nand_chip->select_chip(mtd, 0);
|
|
|
|
//printk("phys_erase_shift=%d,page_shift=%d,oob size=%d, mtd size=%x, chip size=%x, chip->numchips = %d\n",nand_chip->phys_erase_shift,nand_chip->page_shift, mtd->oobsize,
|
|
// mtd->size,nand_chip->chipsize, nand_chip->numchips);
|
|
|
|
//nand_chip->chipsize -= (BMT_POOL_SIZE) << nand_chip->phys_erase_shift;
|
|
mtd->size = nand_chip->chipsize;
|
|
|
|
#ifdef TCSUPPORT_CT_PON
|
|
nand_flash_avalable_size = nand_chip->chipsize - MAX_BMT_SIZE * 0x20000;
|
|
#endif
|
|
|
|
if (IS_NANDFLASH) {
|
|
//map->fldrv_priv = ra;
|
|
ranand_read_byte = ra_nand_read_byte;
|
|
ranand_read_dword = ra_nand_read_dword;
|
|
}
|
|
|
|
|
|
#ifdef TCSUPPORT_NAND_BMT
|
|
bmt_pool_size = calc_bmt_pool_size(mtd);
|
|
printk("bmt pool size: %d \n", bmt_pool_size);
|
|
nand_chip->chipsize -= (bmt_pool_size) << nand_chip->phys_erase_shift;
|
|
if (!g_bmt)
|
|
{
|
|
if ( !(g_bmt = init_bmt(nand_chip, bmt_pool_size)) )
|
|
{
|
|
printk("Error: init bmt failed \n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!g_bbt)
|
|
{
|
|
if ( !(g_bbt = start_init_bbt()) )
|
|
{
|
|
printk("Error: init bbt failed \n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if(write_bbt_or_bmt_to_flash() != 0)
|
|
{
|
|
printk("Error: save bbt or bmt to nand failed \n");
|
|
return -1;
|
|
}
|
|
|
|
if(create_badblock_table_by_bbt())
|
|
{
|
|
printk("Error: create bad block table failed \n");
|
|
return -1;
|
|
}
|
|
|
|
mtd->size = nand_logic_size;
|
|
#endif
|
|
|
|
#if 1//def CONFIG_MTD_PARTITIONS
|
|
|
|
|
|
/* Register the partitions */
|
|
if (IS_SPIFLASH) {
|
|
num = ARRAY_SIZE(mt6573_partitions);
|
|
mt6573_partitions[ num-1 ].size = mtd->size;
|
|
add_mtd_partitions(mtd, mt6573_partitions, num);
|
|
}
|
|
|
|
//err = add_mtd_partitions(mtd, g_pasStatic_Partition, part_num);
|
|
#else
|
|
|
|
err = add_mtd_device(mtd);
|
|
|
|
#endif
|
|
|
|
#ifdef _MTK_NAND_DUMMY_DRIVER_
|
|
dummy_driver_debug = 0;
|
|
#endif
|
|
|
|
if (IS_NANDFLASH){
|
|
#ifdef TCSUPPORT_DUAL_IMAGE_ENHANCE
|
|
|
|
offset = mtd->size/2;
|
|
#if 1
|
|
/* OSBNB00043636: to Fix NAND DualImage , tclinux related MTD partition can't be programmed issue 20130313 Byron */
|
|
erase_unit=devinfo.blocksize;
|
|
erase_unit=erase_unit<<10;
|
|
offset=(offset/(erase_unit)*(erase_unit));
|
|
#else
|
|
offset = (offset/NAND_FLASH_BLOCK_SIZE)*NAND_FLASH_BLOCK_SIZE;
|
|
#endif
|
|
printk("nandinit = offset:%x\n", offset);
|
|
|
|
#endif
|
|
}
|
|
|
|
/* Successfully!! */
|
|
if (!err)
|
|
{
|
|
MSG(INIT, "[mt6573_nand] probe successfully!\n");
|
|
nand_disable_clock();
|
|
return err;
|
|
}
|
|
|
|
/* Fail!! */
|
|
out:
|
|
MSG(INIT, "[NFI] mt6573_nand_probe fail, err = %d!\n", err);
|
|
|
|
nand_release(mtd);
|
|
|
|
//platform_set_drvdata(pdev, NULL);
|
|
|
|
kfree(host);
|
|
|
|
nand_disable_clock();
|
|
return err;
|
|
}
|
|
|
|
|
|
/*NAND Dev*/
|
|
/*=======================================================================*/
|
|
/* MT6573 NAND */
|
|
/*=======================================================================*/
|
|
|
|
#define NFI_base 0x1FBE0000
|
|
#define NFIECC_base 0x1FBE1000
|
|
static struct resource mt6573_resource_nand[] = {
|
|
{
|
|
.start = NFI_base,
|
|
.end = NFI_base + 0x220,
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
{
|
|
.start = NFIECC_base,
|
|
.end = NFIECC_base + 0x150,
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
{
|
|
.start = MT6573_NFI_IRQ_LINE,
|
|
.flags = IORESOURCE_IRQ,
|
|
},
|
|
{
|
|
.start = MT6573_NFIECC_IRQ_LINE,
|
|
.flags = IORESOURCE_IRQ,
|
|
},
|
|
};
|
|
|
|
static struct platform_device mt6573_nand_dev = {
|
|
.name = "mt6573-nand",
|
|
.id = 0,
|
|
.num_resources = ARRAY_SIZE(mt6573_resource_nand),
|
|
.resource = mt6573_resource_nand,
|
|
.dev = {
|
|
.platform_data = &mt6573_nand_hw,
|
|
},
|
|
};
|
|
|
|
|
|
int mt6573_nand_dev_register(void)
|
|
{
|
|
int retval = 0;
|
|
|
|
retval = platform_device_register(&mt6573_nand_dev);
|
|
if (retval != 0) {
|
|
printk(KERN_ERR "register nand device fail\n");
|
|
}
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_probe
|
|
*
|
|
* DESCRIPTION:
|
|
* register the nand device file operations !
|
|
*
|
|
* PARAMETERS:
|
|
* struct platform_device *pdev : device structure
|
|
*
|
|
* RETURNS:
|
|
* 0 : Success
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_probe(struct platform_device *pdev)
|
|
{
|
|
struct mt6573_nand_host_hw *hw;
|
|
struct resource *res = pdev->resource;
|
|
int err = 0;
|
|
|
|
|
|
|
|
hw = (struct mt6573_nand_host_hw*)pdev->dev.platform_data;
|
|
BUG_ON(!hw);
|
|
|
|
if (pdev->num_resources != 4 ||
|
|
res[0].flags != IORESOURCE_MEM ||
|
|
res[1].flags != IORESOURCE_MEM ||
|
|
res[2].flags != IORESOURCE_IRQ ||
|
|
res[3].flags != IORESOURCE_IRQ)
|
|
{
|
|
MSG(INIT, "%s: invalid resource type\n", __FUNCTION__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Request IO memory */
|
|
if (!request_mem_region(res[0].start,
|
|
res[0].end - res[0].start + 1,
|
|
pdev->name))
|
|
{
|
|
return -EBUSY;
|
|
}
|
|
if (!request_mem_region(res[1].start,
|
|
res[1].end - res[1].start + 1,
|
|
pdev->name))
|
|
{
|
|
return -EBUSY;
|
|
}
|
|
err = mt6573_nand_setup(hw);
|
|
if(!err){
|
|
platform_set_drvdata(pdev, host);
|
|
|
|
}
|
|
else{
|
|
platform_set_drvdata(pdev, NULL);
|
|
}
|
|
return err;
|
|
}
|
|
/******************************************************************************
|
|
* mt6573_nand_suspend
|
|
*
|
|
* DESCRIPTION:
|
|
* Suspend the nand device!
|
|
*
|
|
* PARAMETERS:
|
|
* struct platform_device *pdev : device structure
|
|
*
|
|
* RETURNS:
|
|
* 0 : Success
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_suspend(struct platform_device *pdev, pm_message_t state)
|
|
{
|
|
if (DRV_Reg32(NFI_STA_REG32) & STA_NAND_BUSY)
|
|
{
|
|
MSG(POWERCTL, "[NFI] Busy, Suspend Fail !\n");
|
|
return 1; // BUSY
|
|
}
|
|
|
|
MSG(POWERCTL, "[NFI] Suspend !\n");
|
|
return 0;
|
|
}
|
|
/******************************************************************************
|
|
* mt6573_nand_resume
|
|
*
|
|
* DESCRIPTION:
|
|
* Resume the nand device!
|
|
*
|
|
* PARAMETERS:
|
|
* struct platform_device *pdev : device structure
|
|
*
|
|
* RETURNS:
|
|
* 0 : Success
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int mt6573_nand_resume(struct platform_device *pdev)
|
|
{
|
|
MSG(POWERCTL, "[NFI] Resume !\n");
|
|
return 0;
|
|
}
|
|
/******************************************************************************
|
|
* mt6573_nand_remove
|
|
*
|
|
* DESCRIPTION:
|
|
* unregister the nand device file operations !
|
|
*
|
|
* PARAMETERS:
|
|
* struct platform_device *pdev : device structure
|
|
*
|
|
* RETURNS:
|
|
* 0 : Success
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 36)
|
|
static int __devexit mt6573_nand_remove(struct platform_device *pdev)
|
|
#else
|
|
static int mt6573_nand_remove(struct platform_device *pdev)
|
|
#endif
|
|
{
|
|
struct mt6573_nand_host *host = platform_get_drvdata(pdev);
|
|
struct mtd_info *mtd = &host->mtd;
|
|
|
|
nand_release(mtd);
|
|
|
|
kfree(host);
|
|
|
|
nand_disable_clock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NAND OTP operations
|
|
* ***************************************************************************/
|
|
#if (NAND_OTP_SUPPORT && SAMSUNG_OTP_SUPPORT)
|
|
unsigned int samsung_OTPQueryLength(unsigned int *QLength)
|
|
{
|
|
*QLength = SAMSUNG_OTP_PAGE_NUM * g_page_size;
|
|
return 0;
|
|
}
|
|
|
|
unsigned int samsung_OTPRead(unsigned int PageAddr, void *BufferPtr, void *SparePtr)
|
|
{
|
|
struct mtd_info *mtd = &host->mtd;
|
|
unsigned int rowaddr, coladdr;
|
|
unsigned int u4Size = g_page_size;
|
|
unsigned int timeout = 0xFFFF;
|
|
unsigned int bRet;
|
|
unsigned int sec_num = mtd->writesize >> 9;
|
|
|
|
if(PageAddr >= SAMSUNG_OTP_PAGE_NUM)
|
|
{
|
|
return OTP_ERROR_OVERSCOPE;
|
|
}
|
|
|
|
/* Col -> Row; LSB first */
|
|
coladdr = 0x00000000;
|
|
rowaddr = Samsung_OTP_Page[PageAddr];
|
|
|
|
MSG(OTP, "[%s]:(COLADDR) [0x%08x]/(ROWADDR)[0x%08x]\n", __func__, coladdr, rowaddr);
|
|
nand_get_device(mtd, FL_READING);
|
|
/* Power on NFI HW component. */
|
|
// nand_enable_clock();
|
|
|
|
mt6573_nand_reset();
|
|
(void)mt6573_nand_set_command(0x30);
|
|
mt6573_nand_reset();
|
|
(void)mt6573_nand_set_command(0x65);
|
|
|
|
MSG(OTP, "[%s]: Start to read data from OTP area\n", __func__);
|
|
|
|
if (!mt6573_nand_reset())
|
|
{
|
|
bRet = OTP_ERROR_RESET;
|
|
goto cleanup;
|
|
}
|
|
|
|
mt6573_nand_set_mode(CNFG_OP_READ);
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_READ_EN);
|
|
DRV_WriteReg16(NFI_CON_REG16, sec_num << CON_NFI_SEC_SHIFT);
|
|
|
|
DRV_WriteReg32(NFI_STRADDR_REG32, __virt_to_phys(BufferPtr));
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
|
|
if(g_bHwEcc){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
mt6573_nand_set_autoformat(true);
|
|
if(g_bHwEcc){
|
|
ECC_Decode_Start();
|
|
}
|
|
|
|
if (!mt6573_nand_set_command(NAND_CMD_READ0))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_set_address(coladdr, rowaddr, 2, 3))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_set_command(NAND_CMD_READSTART))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_read_page_data(mtd, BufferPtr, u4Size))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
mt6573_nand_read_fdm_data(SparePtr, sec_num);
|
|
|
|
mt6573_nand_stop_read();
|
|
|
|
MSG(OTP, "[%s]: End to read data from OTP area\n", __func__);
|
|
|
|
bRet = OTP_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
mt6573_nand_reset();
|
|
(void)mt6573_nand_set_command(0xFF);
|
|
nand_release_device(mtd);
|
|
return bRet;
|
|
}
|
|
|
|
unsigned int samsung_OTPWrite(unsigned int PageAddr, void *BufferPtr, void *SparePtr)
|
|
{
|
|
struct mtd_info *mtd = &host->mtd;
|
|
unsigned int rowaddr, coladdr;
|
|
unsigned int u4Size = g_page_size;
|
|
unsigned int timeout = 0xFFFF;
|
|
unsigned int bRet;
|
|
unsigned int sec_num = mtd->writesize >> 9;
|
|
|
|
if(PageAddr >= SAMSUNG_OTP_PAGE_NUM)
|
|
{
|
|
return OTP_ERROR_OVERSCOPE;
|
|
}
|
|
|
|
/* Col -> Row; LSB first */
|
|
coladdr = 0x00000000;
|
|
rowaddr = Samsung_OTP_Page[PageAddr];
|
|
|
|
MSG(OTP, "[%s]:(COLADDR) [0x%08x]/(ROWADDR)[0x%08x]\n", __func__, coladdr, rowaddr);
|
|
nand_get_device(mtd, FL_READING);
|
|
mt6573_nand_reset();
|
|
(void)mt6573_nand_set_command(0x30);
|
|
mt6573_nand_reset();
|
|
(void)mt6573_nand_set_command(0x65);
|
|
|
|
MSG(OTP, "[%s]: Start to write data to OTP area\n", __func__);
|
|
|
|
if (!mt6573_nand_reset())
|
|
{
|
|
bRet = OTP_ERROR_RESET;
|
|
goto cleanup;
|
|
}
|
|
|
|
mt6573_nand_set_mode(CNFG_OP_PRGM);
|
|
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_READ_EN);
|
|
|
|
DRV_WriteReg16(NFI_CON_REG16, sec_num << CON_NFI_SEC_SHIFT);
|
|
|
|
DRV_WriteReg32(NFI_STRADDR_REG32, __virt_to_phys(BufferPtr));
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_AHB);
|
|
|
|
if(g_bHwEcc){
|
|
NFI_SET_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}else{
|
|
NFI_CLN_REG16(NFI_CNFG_REG16, CNFG_HW_ECC_EN);
|
|
}
|
|
|
|
mt6573_nand_set_autoformat(true);
|
|
|
|
ECC_Encode_Start();
|
|
|
|
if (!mt6573_nand_set_command(NAND_CMD_SEQIN))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_set_address(coladdr, rowaddr, 2, 3))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!mt6573_nand_status_ready(STA_NAND_BUSY))
|
|
{
|
|
bRet = OTP_ERROR_BUSY;
|
|
goto cleanup;
|
|
}
|
|
|
|
mt6573_nand_write_fdm_data((struct nand_chip *)mtd->priv, BufferPtr, sec_num);
|
|
(void)mt6573_nand_write_page_data(mtd, BufferPtr, u4Size);
|
|
if(!mt6573_nand_check_RW_count(u4Size))
|
|
{
|
|
MSG(OTP, "[%s]: Check RW count timeout !\n", __func__);
|
|
bRet = OTP_ERROR_TIMEOUT;
|
|
goto cleanup;
|
|
}
|
|
|
|
mt6573_nand_stop_write();
|
|
(void)mt6573_nand_set_command(NAND_CMD_PAGEPROG);
|
|
while(DRV_Reg32(NFI_STA_REG32) & STA_NAND_BUSY);
|
|
|
|
bRet = OTP_SUCCESS;
|
|
|
|
MSG(OTP, "[%s]: End to write data to OTP area\n", __func__);
|
|
|
|
cleanup:
|
|
mt6573_nand_reset();
|
|
(void)mt6573_nand_set_command(0xFF);
|
|
nand_release_device(mtd);
|
|
return bRet;
|
|
}
|
|
#endif
|
|
|
|
#if NAND_OTP_SUPPORT
|
|
static int mt_otp_open(struct inode *inode, struct file *filp)
|
|
{
|
|
MSG(OTP, "[%s]:(MAJOR)%d:(MINOR)%d\n", __func__, MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
|
|
filp->private_data = (int*)OTP_MAGIC_NUM;
|
|
return 0;
|
|
}
|
|
|
|
static int mt_otp_release(struct inode *inode, struct file *filp)
|
|
{
|
|
MSG(OTP, "[%s]:(MAJOR)%d:(MINOR)%d\n", __func__, MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
|
|
return 0;
|
|
}
|
|
|
|
static int mt_otp_access(unsigned int access_type, unsigned int offset, void *buff_ptr, unsigned int length, unsigned int *status)
|
|
{
|
|
unsigned int i = 0, ret = 0;
|
|
char *BufAddr = (char *)buff_ptr;
|
|
unsigned int PageAddr, AccessLength=0;
|
|
int Status = 0;
|
|
|
|
static char *p_D_Buff = NULL;
|
|
char S_Buff[64];
|
|
|
|
if (!(p_D_Buff = kmalloc(g_page_size, GFP_KERNEL)))
|
|
{
|
|
ret = -ENOMEM;
|
|
*status = OTP_ERROR_NOMEM;
|
|
goto exit;
|
|
}
|
|
|
|
MSG(OTP, "[%s]: %s (0x%x) length:(%d bytes) !\n", __func__, access_type?"WRITE":"READ", offset, length);
|
|
|
|
while(1)
|
|
{
|
|
PageAddr = offset/g_page_size;
|
|
if(FS_OTP_READ == access_type)
|
|
{
|
|
memset(p_D_Buff, 0xff, g_page_size);
|
|
memset(S_Buff, 0xff, (sizeof(char)*64));
|
|
|
|
MSG(OTP, "[%s]: Read Access of page (%d)\n",__func__, PageAddr);
|
|
|
|
Status = g_mt6573_otp_fuc.OTPRead(PageAddr, p_D_Buff, &S_Buff);
|
|
*status = Status;
|
|
|
|
if( OTP_SUCCESS != Status)
|
|
{
|
|
MSG(OTP, "[%s]: Read status (%d)\n", __func__, Status);
|
|
break;
|
|
}
|
|
|
|
AccessLength = g_page_size - (offset % g_page_size);
|
|
|
|
if(length >= AccessLength)
|
|
{
|
|
memcpy(BufAddr, (p_D_Buff+(offset % g_page_size)), AccessLength);
|
|
}
|
|
else
|
|
{
|
|
//last time
|
|
memcpy(BufAddr, (p_D_Buff+(offset % g_page_size)), length);
|
|
}
|
|
}
|
|
else if(FS_OTP_WRITE == access_type)
|
|
{
|
|
AccessLength = g_page_size - (offset % g_page_size);
|
|
memset(p_D_Buff, 0xff, g_page_size);
|
|
memset(S_Buff, 0xff, (sizeof(char)*64));
|
|
|
|
if(length >= AccessLength)
|
|
{
|
|
memcpy((p_D_Buff+(offset % g_page_size)), BufAddr, AccessLength);
|
|
}
|
|
else
|
|
{
|
|
//last time
|
|
memcpy((p_D_Buff+(offset % g_page_size)), BufAddr, length);
|
|
}
|
|
|
|
Status = g_mt6573_otp_fuc.OTPWrite(PageAddr, p_D_Buff, &S_Buff);
|
|
*status = Status;
|
|
|
|
if( OTP_SUCCESS != Status)
|
|
{
|
|
MSG(OTP, "[%s]: Write status (%d)\n",__func__, Status);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MSG(OTP, "[%s]: Error, not either read nor write operations !\n",__func__);
|
|
break;
|
|
}
|
|
|
|
offset += AccessLength;
|
|
BufAddr += AccessLength;
|
|
if(length <= AccessLength)
|
|
{
|
|
length = 0;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
length -= AccessLength;
|
|
MSG(OTP, "[%s]: Remaining %s (%d) !\n",__func__, access_type?"WRITE":"READ", length);
|
|
}
|
|
}
|
|
error:
|
|
kfree(p_D_Buff);
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
static long mt_otp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = 0, i=0;
|
|
static char *pbuf = NULL;
|
|
|
|
void __user *uarg = (void __user *)arg;
|
|
struct otp_ctl otpctl;
|
|
|
|
/* Lock */
|
|
spin_lock(&g_OTPLock);
|
|
|
|
if (copy_from_user(&otpctl, uarg, sizeof(struct otp_ctl)))
|
|
{
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
if(false == g_bInitDone)
|
|
{
|
|
MSG(OTP, "ERROR: NAND Flash Not initialized !!\n");
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
if (!(pbuf = kmalloc(sizeof(char)*otpctl.Length, GFP_KERNEL)))
|
|
{
|
|
ret = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
|
|
switch (cmd)
|
|
{
|
|
case OTP_GET_LENGTH:
|
|
MSG(OTP, "OTP IOCTL: OTP_GET_LENGTH\n");
|
|
g_mt6573_otp_fuc.OTPQueryLength(&otpctl.QLength);
|
|
otpctl.status = OTP_SUCCESS;
|
|
MSG(OTP, "OTP IOCTL: The Length is %d\n", otpctl.QLength);
|
|
break;
|
|
case OTP_READ:
|
|
MSG(OTP, "OTP IOCTL: OTP_READ Offset(0x%x), Length(0x%x) \n", otpctl.Offset, otpctl.Length);
|
|
memset(pbuf, 0xff, sizeof(char)*otpctl.Length);
|
|
|
|
mt_otp_access(FS_OTP_READ, otpctl.Offset, pbuf, otpctl.Length, &otpctl.status);
|
|
|
|
if (copy_to_user(otpctl.BufferPtr, pbuf, (sizeof(char)*otpctl.Length)))
|
|
{
|
|
MSG(OTP, "OTP IOCTL: Copy to user buffer Error !\n");
|
|
goto error;
|
|
}
|
|
break;
|
|
case OTP_WRITE:
|
|
MSG(OTP, "OTP IOCTL: OTP_WRITE Offset(0x%x), Length(0x%x) \n", otpctl.Offset, otpctl.Length);
|
|
if (copy_from_user(pbuf, otpctl.BufferPtr, (sizeof(char)*otpctl.Length)))
|
|
{
|
|
MSG(OTP, "OTP IOCTL: Copy from user buffer Error !\n");
|
|
goto error;
|
|
}
|
|
mt_otp_access(FS_OTP_WRITE, otpctl.Offset , pbuf, otpctl.Length, &otpctl.status);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
ret = copy_to_user(uarg, &otpctl, sizeof(struct otp_ctl));
|
|
|
|
error:
|
|
kfree(pbuf);
|
|
exit:
|
|
spin_unlock(&g_OTPLock);
|
|
return ret;
|
|
}
|
|
|
|
static struct file_operations nand_otp_fops = {
|
|
.owner= THIS_MODULE,
|
|
.ioctl= mt_otp_ioctl,
|
|
.open= mt_otp_open,
|
|
.release= mt_otp_release,
|
|
};
|
|
|
|
static struct miscdevice nand_otp_dev = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = "otp",
|
|
.fops = &nand_otp_fops,
|
|
};
|
|
#endif
|
|
|
|
static struct mtd_info *nandflash_probe(struct map_info *map)
|
|
//int __devinit ra_nand_init(void)
|
|
{
|
|
int err = 0;
|
|
|
|
printk("tc3162 mtd init: nandflash_probe enter\n");
|
|
|
|
err = mt6573_nand_setup(&mt6573_nand_hw);
|
|
if(!err){
|
|
return &(host->mtd);
|
|
|
|
}
|
|
else{
|
|
return NULL;
|
|
|
|
}
|
|
//#ifdef TCSUPPORT_DUAL_IMAGE_ENHANCE
|
|
// offset = (1 << ra->flash->chip_shift)/2;
|
|
//#endif
|
|
|
|
//return &host->mtd;
|
|
}
|
|
|
|
static void nandflash_destroy(struct mtd_info *mtd)
|
|
//static void __devexit ra_nand_remove(void)
|
|
{
|
|
//struct mt6573_nand_host *host = platform_get_drvdata(pdev);
|
|
if(host){
|
|
struct mtd_info *mtd = &host->mtd;
|
|
|
|
nand_release(mtd);
|
|
|
|
kfree(host);
|
|
}
|
|
|
|
nand_disable_clock();
|
|
|
|
|
|
}
|
|
|
|
|
|
static struct mtd_chip_driver nandflash_chipdrv = {
|
|
.probe = nandflash_probe,
|
|
.destroy = nandflash_destroy,
|
|
.name = "nandflash_probe",
|
|
.module = THIS_MODULE
|
|
};
|
|
|
|
|
|
/******************************************************************************
|
|
Device driver structure
|
|
******************************************************************************/
|
|
static struct platform_driver mt6573_nand_driver = {
|
|
.probe = mt6573_nand_probe,
|
|
.remove = mt6573_nand_remove,
|
|
.suspend = mt6573_nand_suspend,
|
|
.resume = mt6573_nand_resume,
|
|
.driver = {
|
|
.name = "mt6573-nand",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 36)
|
|
int __devinit ra_nand_init(void)
|
|
#else
|
|
int ra_nand_init(void)
|
|
#endif
|
|
{
|
|
return platform_driver_register(&mt6573_nand_driver);
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 36)
|
|
void __devexit ra_nand_remove(void)
|
|
#else
|
|
void ra_nand_remove(void)
|
|
#endif
|
|
{
|
|
platform_driver_unregister(&mt6573_nand_driver);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_init
|
|
*
|
|
* DESCRIPTION:
|
|
* Init the device driver !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static int __init mt6573_nand_init(void)
|
|
{
|
|
struct proc_dir_entry *entry;
|
|
|
|
//#ifdef CONFIG_MTK_MTD_NAND_INTERRUPT_SCHEME
|
|
// boot up using polling mode
|
|
g_i4Interrupt = 0;
|
|
//#else
|
|
// g_i4Interrupt = 0;
|
|
//#endif
|
|
|
|
printk("tc3162 mtd init: mt6573_nand_init enter\n");
|
|
|
|
#if NAND_OTP_SUPPORT
|
|
int err = 0;
|
|
MSG(OTP, "OTP: register NAND OTP device ...\n");
|
|
err = misc_register(&nand_otp_dev);
|
|
if (unlikely(err))
|
|
{
|
|
MSG(OTP, "OTP: failed to register NAND OTP device!\n");
|
|
return err;
|
|
}
|
|
spin_lock_init(&g_OTPLock);
|
|
#endif
|
|
|
|
#if (NAND_OTP_SUPPORT && SAMSUNG_OTP_SUPPORT)
|
|
g_mt6573_otp_fuc.OTPQueryLength = samsung_OTPQueryLength;
|
|
g_mt6573_otp_fuc.OTPRead = samsung_OTPRead;
|
|
g_mt6573_otp_fuc.OTPWrite = samsung_OTPWrite;
|
|
#endif
|
|
|
|
|
|
entry = create_proc_entry(PROCNAME, 0666, NULL);
|
|
if (entry == NULL)
|
|
{
|
|
MSG(INIT, "MT6573 Nand : unable to create /proc entry\n");
|
|
return -ENOMEM;
|
|
}
|
|
entry->read_proc = mt6573_nand_proc_read;
|
|
entry->write_proc = mt6573_nand_proc_write;
|
|
|
|
#ifdef NAND_ECC_TEST
|
|
entry = create_proc_entry(PROCNAME_ECC, 0666, NULL);
|
|
if (entry == NULL)
|
|
{
|
|
MSG(INIT, "MT6573 Nand : unable to create /proc entry\n");
|
|
return -ENOMEM;
|
|
}
|
|
entry->read_proc = mt6573_nand_ecc_proc_read;
|
|
entry->write_proc = mt6573_nand_ecc_proc_write;
|
|
//entry->owner = THIS_MODULE;
|
|
#endif
|
|
MSG(INIT, "MediaTek MT6573 Nand driver init, version %s\n", VERSION);
|
|
if(IS_SPIFLASH){ //SPI Flash boot
|
|
//return platform_driver_register(&mt6573_nand_driver);
|
|
return mt6573_nand_dev_register();
|
|
}
|
|
else{
|
|
register_mtd_chip_driver(&nandflash_chipdrv);
|
|
return 0;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
* mt6573_nand_exit
|
|
*
|
|
* DESCRIPTION:
|
|
* Free the device driver !
|
|
*
|
|
* PARAMETERS:
|
|
* None
|
|
*
|
|
* RETURNS:
|
|
* None
|
|
*
|
|
* NOTES:
|
|
* None
|
|
*
|
|
******************************************************************************/
|
|
static void __exit mt6573_nand_exit(void)
|
|
{
|
|
MSG(INIT, "MediaTek MT6573 Nand driver exit, version %s\n", VERSION);
|
|
|
|
#if NAND_OTP_SUPPORT
|
|
misc_deregister(&nand_otp_dev);
|
|
#endif
|
|
|
|
#ifdef SAMSUNG_OTP_SUPPORT
|
|
g_mt6573_otp_fuc.OTPQueryLength = NULL;
|
|
g_mt6573_otp_fuc.OTPRead = NULL;
|
|
g_mt6573_otp_fuc.OTPWrite = NULL;
|
|
#endif
|
|
if(IS_SPIFLASH){ //SPI FLASH
|
|
//platform_driver_unregister(&mt6573_nand_driver);
|
|
}
|
|
else{
|
|
unregister_mtd_chip_driver(&nandflash_chipdrv);
|
|
}
|
|
remove_proc_entry(PROCNAME, NULL);
|
|
#ifdef NAND_ECC_TEST
|
|
remove_proc_entry(PROCNAME_ECC, NULL);
|
|
#endif
|
|
}
|
|
|
|
|
|
module_init(mt6573_nand_init);
|
|
module_exit(mt6573_nand_exit);
|
|
MODULE_LICENSE("GPL");
|