blob: a7ed7032dfdb983955121e0ff31eb4f2546332bc [file] [log] [blame]
/*
* Linux driver for NAND NV Flash Translation Layer
* Copyright (C) 2013, ZTE Corporation.
*/
#include <linux/mtd/mtd.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/blktrans.h>
#include <linux/mtd/zftl.h>
#include <linux/types.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#ifdef CONFIG_MTD_NVRESUME
#include <linux/mfd/zx234290.h>
#endif
#include <linux/mtd/partition_guard.h>
#define ZFTL_DEBUG 0
/* DEBUG */
#if ZFTL_DEBUG
#define zftl_debug(fmt,args...) printk (fmt ,##args)
#define zftl_debugX(level,fmt,args...) if (DEBUG>=level) printk(fmt,##args);
#else
#define zftl_debug(fmt,args...)
#define zftl_debugX(level,fmt,args...)
#endif /* DEBUG */
int g_oob_zftl_offset = 48;
/* defined in cmd_nand.c */
int arg_off_size(int argc, char *const argv[], int *idx,
loff_t *off, loff_t *size);
extern int nand_block_isbad(struct mtd_info *mtd, loff_t offs);
#ifdef CONFIG_MTD_NVRESUME
int zftl_res = 0;
extern int hal_Comm_Soft_Reset(T_ZDrvSys_RESET_TYPE reset_type);
extern int zOss_NvInResume(void);
extern void zOss_NvSetErrorAddr(int dwAddr);
#endif
struct ZFTLrecord *zftl_info[CONFIG_ZFTL_MAX_PARTITIONS] = {NULL};
extern struct mtd_info *mtd_fota;
extern struct cmdline_mtd_partition *partitions;
struct cmdline_mtd_partition {
struct cmdline_mtd_partition *next;
char *mtd_id;
int num_parts;
struct mtd_partition *parts;
};
int partnum = 0;
static struct mutex zftl_mutex;
static int mutex_inited = 0;
int cdrom_partsize = 0;
/* nvro partition size */
int nvro_partsize = 0;
int nand_NvProgram(int dwStart, int dwLen, char* from);
int nand_NvRead(int dwStart, int dwLen, char* to);
static int memcmpb(void *a, int c, int n)
{
int i;
for (i = 0; i < n; i++) {
if (c != ((unsigned char *)a)[i])
return 1;
}
return 0;
}
/* Read oob data from flash */
static int zftl_read_oob(struct ZFTLrecord *zftl, uint32_t offs, uint8_t *buf)
{
nand_info_t *nand = zftl->nand;
uint32_t mask = zftl->writesize - 1;
struct mtd_oob_ops ops;
int32_t res;
ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = offs & mask;
ops.ooblen = zftl->oobsize;
ops.oobbuf = buf;
ops.datbuf = NULL;
res = nand->_read_oob(nand, (offs & ~mask), &ops);
return res;
}
static int zftl_setup_table(struct ZFTLrecord *zftl, uint32_t *is_writed)
{
nand_info_t *nand = zftl->nand;
uint32_t block_num = zftl->numBlocks;
uint32_t first_block = zftl->firstBlock;
uint32_t erasesize = zftl->erasesize;
uint32_t oobsize = zftl->oobsize;
unsigned char *oob = zftl->oobbuf;
uint32_t index = 0;
uint32_t addr = 0;
unsigned char zftl_buffer[256] = {0};
struct zftl_oob zftl_oob ={0};
memset(oob, 0x0, oobsize);
for( index = 0; index < block_num; index++ )
{
addr = (first_block + index) * erasesize;
if(nand->_block_isbad(nand, (loff_t)addr))
{
zftl->blockTable[index] = BLOCK_BAD;
continue;
}
/* д¿éµÄ˳ÐòΪ ÏÈдµÚ¶þÒ³¿ªÊ¼µ½×îºóÒ»Ò³£¬ÔÙдµÚÒ»Ò³ */
/* µÚÒ»Ò³oob µÚ¶þÒ³oob ¿é״̬ ˵Ã÷ */
/* 0xff 0xff BLOCK_FREE ¶¼Ã»ÓпªÊ¼Ð´ */
/* 0xff ---- BLOCK_DIRTY ¿ªÊ¼Ð´£¬µ«Ã»ÓÐдÍêµÚÒ»Ò³£¬Öм䱻ÖÐ¶Ï */
/* ---- 0xff BLOCK_USED/DIRTY дÍêµÚÒ»Ò³£¬Íê³É¿éдÈë */
/* ---- ---- BLOCK_USED/DIRTY дÍêµÚÒ»Ò³£¬Íê³É¿éдÈë */
if(zftl_read_oob(zftl, addr, (uint8_t *)oob ))/* ¶ÁÈ¡µÚÒ»Ò³ */
{
printk("[zftl_setup_table] read oob error at = 0x%08x\n", addr);
return NAND_READ_WRITE_ERROR;
}
memcpy(zftl_buffer,oob + g_oob_zftl_offset,oobsize -g_oob_zftl_offset);
memcpy((unsigned char *)&zftl_oob,zftl_buffer,sizeof(struct zftl_oob));
if(memcmpb(oob, 0xff, oobsize)) /* µÚÒ»Ò³oob != 0xff */
{
//if( oob.used == 1 ) /* block is used */
if( !memcmp(zftl_oob.head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE) ) /* °üº¬ÓзÖÇøÍ· */
{
*is_writed += 1;
if( zftl_oob.version > zftl->versionTable[zftl_oob.logicBlockID])
{
/* version is newer */
if(zftl->versionTable[zftl_oob.logicBlockID] != 0) /* ÒѾ­ÓжÔÓ¦µÄused¿éÁË */
zftl->blockTable[zftl->blockRepTable[zftl_oob.logicBlockID]] = BLOCK_DIRTY;
zftl->blockRepTable[zftl_oob.logicBlockID] = index;
zftl->versionTable[zftl_oob.logicBlockID] = zftl_oob.version;
zftl->blockTable[index] = BLOCK_USED;
/* Added by zhouqi for xxx, 2014/04/23 */
zftl->lastFreeBlock = index + 1;
}
else
zftl->blockTable[index] = BLOCK_DIRTY;
}
else
zftl->blockTable[index] = BLOCK_DIRTY;
}
else /* 0xff£¬block is free */
{
if(zftl_read_oob(zftl, addr + zftl->writesize, (uint8_t *)oob ))/* ¶ÁÈ¡µÚ¶þÒ³ */
{
printk("[zftl_setup_table] read oob error at = 0x%08x\n", addr);
return NAND_READ_WRITE_ERROR;
}
if(memcmpb(oob, 0xff, oobsize)) /* µÚ¶þÒ³oob != 0xff */
{
zftl->blockTable[index] = BLOCK_DIRTY;
}
else /* µÚ¶þÒ³oob = 0xff */
zftl->blockTable[index] = BLOCK_FREE;
}
}
return 0;
}
/* ²Á³ýÒ»¿é£¬ebnumΪ¿éºÅ */
static int zftl_erase_block(nand_info_t *nand, uint32_t ebnum)
{
int err = -1;
struct erase_info ei;
uint32_t addr = ebnum * nand->erasesize;
memset(&ei, 0, sizeof(struct erase_info));
ei.mtd = nand;
ei.addr = (uint64_t)addr;
ei.len = (uint64_t)nand->erasesize;
err = nand->_erase(nand, &ei);
if (err)
{
printk("[zftl_erase_block]: erasing %d error %d\n", ebnum, err);
return err;
}
if (ei.state == MTD_ERASE_FAILED)
{
printk("[zftl_erase_block]: erasing %d\n",ebnum);
return -1;
}
return 0;
}
void zftl_garbage_collect(struct ZFTLrecord *zftl)
{
uint32_t block_num = zftl->numBlocks;
uint32_t firstBlock = zftl->firstBlock;
uint32_t index = 0;
nand_info_t *nand = zftl->nand;
zftl_debug("\n");
for( index = 0; index < block_num; index++ )
{
if( zftl->blockTable[index] == BLOCK_DIRTY )
{
zftl_debug(" [zftl_garbage_collect] eraseblock zftl->blockTable[-%d-]\n", index);
if( zftl_erase_block(nand, firstBlock + index) )
{
/* ²Á³ýʧ°Ü£¬±ê¼ÇΪ»µ¿ì */
zftl_debug(" [zftl_garbage_collect] eraseblock zftl->blockTable[-%d-] ERROR\n", index);
zftl->blockTable[index] = BLOCK_BAD;
/* ±ê¼Ç»µ¿é£¬¸üлµ¿é±í mtd->erase ÖУ¬ÒÔ°üº¬²Á³ýʧ°Ü¸üÐÂBBT*/
nand->_block_markbad(nand, (loff_t)((firstBlock + index) << zftl->erasesize_shift));
}
else
{
/* ²Á³ý³É¹¦ */
zftl->blockTable[index] = BLOCK_FREE;;
}
}
}
zftl_debug("\n");
}
/* Added by zhouqi for xxx, 2014/04/23 */
static int zftl_find_free_block(struct ZFTLrecord *zftl, uint32_t *freeBlock)
{
uint32_t i = 2;
uint32_t index = 0;
uint32_t max = 0;
do
{
index = zftl->lastFreeBlock;
max = zftl->numBlocks;
for( ; index < max; index++ )
{
if( zftl->blockTable[index] == BLOCK_FREE )
{
*freeBlock = index ;
zftl->lastFreeBlock = index + 1;
zftl_debug(" [zftl_find_free_block++] zftl->blockTable[ -%d- ] -BLOCK_FREE-\n", index - 1);
return 0;
}
}
max = zftl->lastFreeBlock;
index = 1;
for( ; index < max; index++ )
{
if( zftl->blockTable[index - 1] == BLOCK_FREE )
{
*freeBlock = index - 1;
zftl->lastFreeBlock = index;
zftl_debug(" [zftl_find_free_block--] zftl->blockTable[ -%d- ] -BLOCK_FREE-\n", index - 1);
return 0;
}
}
/* ûÓÐÕÒµ½¿ÕÏеĿ飬Ôò½øÐÐÀ¬»ø»ØÊÕ */
zftl_debug(" [zftl_find_free_block] no free block --> zftl_garbage_collect()\n");
zftl_garbage_collect(zftl);
} while( --i !=0 );
return 1;
}
#if 0
static int zftl_check_offset_and_size(struct ZFTLrecord *zftl,
loff_t off, loff_t size)
{
if( off > (zftl->numBlocks << zftl->erasesize_shift) ||
( off + size) > (zftl->numBlocks << zftl->erasesize_shift))
return 1;
return 0;
}
#endif
static int zftl_write_block(nand_info_t *nand, uint32_t offs, size_t *retlen,
uint8_t *oob, u_char *buf)
{
struct mtd_oob_ops ops;
int32_t res = 0;
uint32_t offset = offs;
size_t ret_len = 0;
size_t pagesize = nand->writesize;
/* write the remain */
res = nand->_write(nand, (loff_t)(offset + nand->writesize), nand->erasesize - pagesize,
&ret_len, (u_char *)(buf + pagesize) );
/* write the first page of the block write the oob */
ops.mode = MTD_OPS_PLACE_OOB;
ops.ooboffs = 0;
ops.ooblen = nand->oobsize;
ops.oobbuf = oob;
ops.datbuf = buf;
ops.len = pagesize;
res += nand->_write_oob(nand, (loff_t)offset, &ops);
*retlen = ops.retlen + ret_len;
return res;
}
int zftl_write(struct ZFTLrecord *zftl, uint32_t to, uint32_t len, u_char *buf)
{
int ret = 0;
uint32_t offset_block_phy = 0;
uint32_t block_new = 0;
size_t retlen = 0;
u_char *buffer = buf;
unsigned char *oob = zftl->oobbuf;
uint32_t block_offset = 0;
uint32_t erasesize_shift = zftl->erasesize_shift;
uint32_t block_log = 0;
uint32_t erasesize = zftl->erasesize;
uint32_t is_upthreshold = 0;
uint32_t oob_size = zftl->oobsize;
uint32_t firstBlock = zftl->firstBlock;
u_char *blockbuf = zftl->blockbuf;
uint32_t left_to_write = len;
uint32_t write_length = 0;
uint32_t offset = to;
nand_info_t *nand = zftl->nand;
unsigned char zftl_buffer[256] = {0};
struct zftl_oob zftl_oob ={0};
if((zftl == NULL) || (buf == NULL))
{
BUG();
}
while(left_to_write > 0)
{
/* ¿Õ¼ä£¬³õʼ»¯Îª 0xFF */
memset(blockbuf, 0xff, erasesize);
memset(oob, 0xff, oob_size);
memset(zftl_buffer,0xff,256);
/* »ñÈ¡Âß¼­¿é£¬¼°¿éÄÚÆ«ÒÆ */
block_log = offset >> (zftl->erasesize_shift);
block_offset = offset & (zftl->erasesize - 1); /* ¿éÄ򵀮«ÒƵØÖ· */
/* ÅжÏÂß¼­¿éÊÇ·ñÓÐÎïÀí¶ÔÓ¦¿é£¬Èç¹ûÓУ¬ÔòΪ¸ÄдÊý¾Ý£»Èç¹ûûÓУ¬ÔòΪµÚÒ»´Îд´Ë¿é */
if(zftl->blockRepTable[block_log] != BLOCK_NIL)
{
/* »ñÈ¡Âß¼­¿é¶ÔÓ¦µÄÎïÀí¿é */
offset_block_phy = zftl->blockRepTable[block_log];
zftl_debug(" [zftl_write] zftl->blockRepTable[%d] = %d\n", block_log, offset_block_phy);
/* Õû¸ö¶Áȡһ¿éÊý¾Ý */
zftl_debug(" [zftl_read] nand->read( -0x%0x-, -0x%0x-)\n",
((firstBlock + offset_block_phy) << erasesize_shift),erasesize);
ret = nand->_read(nand, ((firstBlock + offset_block_phy) << erasesize_shift), erasesize, &retlen, blockbuf);
if (ret || retlen != erasesize)
{
printk(KERN_ERR"[zfltl]: -zftl_read- error\n");
#ifdef CONFIG_MTD_NVRESUME
if((!zOss_NvInResume())&&(zftl==zftl_info[1])&&(zftl_res != 1))
{
return -1;
}
#endif
}
/* ¶ÁÈ¡ÕâÒ»¿éµÄOOBÐÅÏ¢£¬²¢¸üРVERSION, Èç¹û´ïµ½°æ±¾ºÅµÄãÐÖµ£¬Ôò½øÐлØÊÕ */
if(zftl_read_oob(zftl, ((firstBlock + offset_block_phy) << erasesize_shift), (uint8_t *)oob ))
{
printk(" [zftl_write] read oob error at = 0x%x\n", (offset_block_phy << erasesize_shift));
//return -1;
}
memcpy(zftl_buffer,oob + g_oob_zftl_offset,oob_size-g_oob_zftl_offset);
memcpy((unsigned char *)&zftl_oob,zftl_buffer,sizeof(struct zftl_oob));
if( zftl_oob.version == CONFIG_ZFLT_VERSION_THRESHOLD )
{
zftl_debug(" [zftl_write] up to the -CONFIG_ZFLT_VERSION_THRESHOLD-\n");
is_upthreshold = 1;
zftl_garbage_collect(zftl);
zftl_oob.version = 1;
}
else
{
zftl_oob.version += 1;
zftl_debug(" [zftl_write] oob->version = %d,oob->logicBlockID = %d\n", zftl_oob.version, zftl_oob.logicBlockID);
}
memcpy(zftl_oob.head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE); /* ¿½±´zftlÍ· */
zftl_oob.logicBlockID = block_log;
/* Ìî³ä¸üРdata */
if (left_to_write < (erasesize - block_offset))
write_length = left_to_write;
else
write_length = nand->erasesize - block_offset;
memcpy(blockbuf + block_offset, buffer, write_length);
zftl_debug(" [zftl_write] update the date ( -0x%0x-, -0x%0x- )\n",
block_offset, write_length);
/* ѰÕÒÒ»¸ö FREE µÄÎïÀí¿é */
find_next_free_block_1:
if( zftl_find_free_block(zftl, &block_new) )/* BLOCK_USED */
{
/* ûÓÐÕÒµ½FREE¿é */
printk(" [zftl_write] there is enough block, please enlarge the partition\n");
return CAN_NOT_FIND_FREE_BLOCK;
}
/* дÈëÒ»¿é */
zftl_debug(" [zftl_write_block] nand->write( -0x%0x-, -0x%0x-)\n",
((firstBlock + block_new) << erasesize_shift), erasesize);
memcpy(oob + g_oob_zftl_offset,(uint8_t *)&zftl_oob,sizeof(struct zftl_oob));
if( zftl_write_block(nand, (loff_t)((firstBlock + block_new) << erasesize_shift), &retlen,
(uint8_t *)oob, blockbuf) )
{
/* дÊý¾Ýʧ°Üʱ£¬±ê¼ÇBBT£¬¸üРTABLE£¬ÖØÐ²éÕÒеĿéд */
printk(" -ERROR-\n");
zftl->blockTable[block_new] = BLOCK_BAD;
nand->_block_markbad(nand, (loff_t)((firstBlock + block_new) << erasesize_shift));
goto find_next_free_block_1;
}
/* ¸üРblockRepTable¡¢blockTable */
zftl->blockRepTable[zftl_oob.logicBlockID] = block_new;
zftl_debug(" [zftl_write] zftl->blockRepTable[oob->logicBlockID] = %d,oob->logicBlockID = %d\n", zftl->blockRepTable[zftl_oob.logicBlockID], zftl_oob.logicBlockID);
zftl->blockTable[block_new] = BLOCK_USED;
zftl->blockTable[offset_block_phy] = BLOCK_DIRTY;
/* ¸üРblockRepTable¡¢blockTable */
if( is_upthreshold == 1 )
{
zftl_debug(" [zftl_write] ( is_upthreshold == 1 )\n");
zftl_debug(" [zftl_write] eraseblock zftl->blockTable[-%d-]\n", offset_block_phy);
if( zftl_erase_block(nand, firstBlock + offset_block_phy) )
{
/* ²Á³ýʧ°Ü£¬±ê¼ÇΪ»µ¿ì */
zftl_debug(" [zftl_write] eraseblock zftl->blockTable[-%d-] ERROR!\n",
(firstBlock + offset_block_phy) << erasesize);
zftl->blockTable[offset_block_phy] = BLOCK_BAD;
nand->_block_markbad(nand, (loff_t)((firstBlock + offset_block_phy) << erasesize_shift)); /* ±ê¼Ç»µ¿é£¬¸üлµ¿é±í */
}
else
zftl->blockTable[offset_block_phy] = BLOCK_FREE;
}
zftl_debug("\n");
left_to_write -= write_length;
offset += write_length;
buffer += write_length;
}
else /* zftl->blockRepTable[block_log] == BLOCK_NIL */
{
/* ´ËÂß¼­µØÖ·µÚÒ»´Î±»Ð´ÈëÊý¾Ý */
zftl_debug(" [zftl_write] zftl->blockRepTable[%d] == BLOCK_NIL\n", block_log);
/* ²¢¸üÐÂOOB */
zftl_oob.logicBlockID = block_log;
zftl_oob.used = 0x1;
zftl_oob.version = 0x1;
memcpy(zftl_oob.head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE); /* ¿½±´zftlÍ· */
/* Ìî³ä¸üРdata */
if (left_to_write < (erasesize - block_offset))
write_length = left_to_write;
else
write_length = nand->erasesize - block_offset;
memcpy(blockbuf + block_offset, buffer, write_length);
zftl_debug(" [zftl_write] update the date ( -0x%0x-, -0x%0x- )\n",
block_offset, write_length);
/* ѰÕÒÒ»¸ö FREE µÄÎïÀí¿é */
find_next_free_block_2:
if( zftl_find_free_block(zftl, &block_new) )
{
/* ûÓÐÕÒµ½FREE¿é */
printk(" [zftl_write] there is enough block, please enlarge the partition\n");
return CAN_NOT_FIND_FREE_BLOCK;
}
/* дÈëÒ»¿é */
zftl_debug(" [zftl_write_block] nand->write( -0x%0x-, -0x%0x-)\n",
((firstBlock + block_new) << erasesize_shift), erasesize);
memcpy(oob + g_oob_zftl_offset,(uint8_t *)&zftl_oob,sizeof(struct zftl_oob));
if( zftl_write_block(nand, (loff_t)((firstBlock + block_new) << erasesize_shift), &retlen,
(uint8_t *)oob, blockbuf) )
{
/* дÊý¾Ýʧ°Üʱ£¬±ê¼ÇBBT£¬¸üРTABLE£¬ÖØÐ²éÕÒеĿéд */
printk(" -ERROR-\n");
zftl->blockTable[block_new] = BLOCK_BAD;
nand->_block_markbad(nand, (loff_t)((firstBlock + block_new) << erasesize_shift));
goto find_next_free_block_2;
}
/* ¸üРblockRepTable¡¢blockTable */
zftl->blockRepTable[zftl_oob.logicBlockID] = block_new;
zftl_debug(" [zftl_write2] zftl->blockRepTable[oob->logicBlockID] = %d,oob->logicBlockID = %d\n", zftl->blockRepTable[zftl_oob.logicBlockID], zftl_oob.logicBlockID);
zftl->blockTable[block_new] = BLOCK_USED;
zftl_debug("\n");
left_to_write -= write_length;
offset += write_length;
buffer += write_length;
}
}
/* Added by zhouqi for xxx, 2014/04/23 */
zftl_garbage_collect(zftl);
zftl_debug("\n");
return 0;
}
int zftl_read(struct ZFTLrecord *zftl, uint32_t from, uint32_t len, u_char *buffer)
{
int ret = 0;
uint32_t offset_block_phy;
uint32_t left_to_read = len;
uint32_t read_length = 0;
uint32_t retlen = 0;
u_char *buf = buffer;
uint32_t offset = from;
uint32_t block_offset = 0;
uint32_t block_log = 0;
uint32_t erasesize = zftl->erasesize;
uint32_t erasesize_shift = zftl->erasesize_shift;
nand_info_t *nand = zftl->nand;
uint32_t firstBlock = zftl->firstBlock;
if((zftl == NULL) || (buf == NULL))
{
BUG();
}
while( left_to_read > 0 )
{
block_offset = offset & (zftl->erasesize - 1);
block_log = offset >> zftl->erasesize_shift;
/* »ñÈ¡Âß¼­¿é¶ÔÓ¦µÄÎïÀí¿é */
if( zftl->blockRepTable[block_log] != BLOCK_NIL )
offset_block_phy = zftl->blockRepTable[block_log];
else
{
/* µ±Ã»ÓÐдÈëÊý¾Ýʱ£¬Ã»ÓÐÓ³Éä¹ØÏµ£¬¶ÁÈ¡Êý¾ÝȫΪ 0XFF */
if (left_to_read < (erasesize - block_offset))
read_length = left_to_read;
else
read_length = nand->erasesize - block_offset;
memset(buf, 0xff, read_length);
left_to_read -= read_length;
offset += read_length;
buf += read_length;
continue;
}
if (left_to_read < (erasesize - block_offset))
read_length = left_to_read;
else
read_length = nand->erasesize - block_offset;
zftl_debug(" [zftl_read] nand->read( -0x%0x-, -0x%0x-)\n",
((firstBlock + offset_block_phy) << erasesize_shift) + block_offset,
read_length);
ret = nand->_read(nand, ((firstBlock + offset_block_phy) << erasesize_shift) + block_offset,
read_length, &retlen, buf);
if (ret || retlen != read_length)
{
printk(KERN_ERR"[zfltl]: -zftl_read- error\n");
//return -1;
#ifdef CONFIG_MTD_NVRESUME
if((!zOss_NvInResume())&&(zftl==zftl_info[1])&&(zftl_res != 1))
{
return -1;
}
#endif
}
left_to_read -= read_length;
offset += read_length;
buf += read_length;
}
zftl_debug("\n");
return 0;
}
struct ZFTLrecord * zftl_get_ZFTLrecord_byname(char* part_name)
{
uint32_t i;
for(i = 0; zftl_partition_name[i] != NULL; i++ )
{
if(zftl_info[i]!=NULL)
{
if(strcmp(zftl_info[i]->mbd.mtd->name, part_name) == 0)
{
return zftl_info[i];
}
}
}
return NULL;
}
int zftl_init(unsigned char *part_name,struct ZFTLrecord *zftlinfo )
{
nand_info_t *nand = mtd_fota;
//uint32_t i, j,k, block;
uint32_t k = 0;
struct ZFTLrecord *zftl = NULL;
uint32_t partition_size = 0;
uint32_t partition_offest = 0;
struct cmdline_mtd_partition *part;
printk( "zftl[%d] is %s\n",partnum,part_name);
/* ZFTLrecord½á¹¹ÌåÄÚ´æÉêÇë */
zftl_info[partnum] = zftlinfo;
if (!zftl_info[partnum])
{
printk( "zftl: out of memory for data structure ZFTLrecord\n");
return 1;
}
zftl = zftl_info[partnum];
zftl->nand = nand;
zftl->erasesize = nand->erasesize;
zftl->writesize = nand->writesize;
zftl->erasesize_shift = ffs(nand->erasesize) - 1;
zftl->writesize_shift = ffs(nand->writesize) - 1;
zftl->oobsize = nand->oobsize;
part = partitions;
for(k=0;k<part->num_parts;k++)
{
if ( strcmp( (char *)part->parts[k].name, part_name ) == 0 )
break;
} /* nvr */
partition_offest = part->parts[k].offset;
partition_size = part->parts[k].size;
if ( strcmp(part_name,"cdrom") == 0 )
{
cdrom_partsize = partition_size;
}
if ( strcmp(part_name,"nvro") == 0 )
{
nvro_partsize = partition_size;
printk("nvro_partsize=0x%0x\n",nvro_partsize);
}
zftl->firstBlock= partition_offest >> zftl->erasesize_shift; /* ¾ßÌåµÄÊýÖµ´Ó·ÖÇøÖлñµÃ */
zftl->numBlocks = partition_size / zftl->erasesize; /* ÔõôȷÈÏÇøÓò·¶Î§ zhouqi,ʵ¼ÊʹÓÃʱ¿éºÅÒª -1 ?*/
/* ¿é»º´æÉêÇë */
zftl->blockbuf = kmalloc(zftl->erasesize, GFP_KERNEL);
if (!zftl->blockbuf)
{
printk( "zftl: out of memory for data structure blockbuf\n");
goto failed_alloc_blockbuf;
}
/* OOB»º´æÉêÇë */
zftl->oobbuf = kmalloc(zftl->oobsize, GFP_KERNEL);
if (!zftl->oobbuf)
{
printk( "zftl: out of memory for data oobbuf\n");
goto failed_alloc_oobbuf;
}
/* blockRepTable±íÄÚ´æÉêÇë, ³õʼ»¯Îª 0xFFFF*/
zftl->blockRepTable = kmalloc(zftl->numBlocks * sizeof(zftl->blockRepTable), GFP_KERNEL);
if (!zftl->blockRepTable)
{
printk( "zftl: out of memory for data structure blockRepTable\n");
goto failed_alloc_blockreptable;
}
memset(zftl->blockRepTable, 0xff, zftl->numBlocks * sizeof(zftl->blockRepTable));
/* versionTable±íÄÚ´æÉêÇë */
/* ³õʼ»¯Îª¶¼Îª0£¬NANDÖеÄÊý¾Ýversion×îСΪ 1 */
zftl->versionTable = kzalloc(zftl->numBlocks * sizeof(zftl->versionTable), GFP_KERNEL);
if (!zftl->versionTable)
{
printk( "zftl: out of memory for data structure versionTable\n");
goto failed_alloc_versionTable;
}
/* blockTable±íÄÚ´æÉêÇë */
zftl->blockTable = kzalloc(zftl->numBlocks * sizeof(zftl->blockTable), GFP_KERNEL);
if (!zftl->blockTable)
{
printk( "zftl: out of memory for data structure blockTable\n");
goto failed_alloc_blocktable;
}
/* ·ÖÎö¹ÒÔØzftl·ÖÇø£¬½¨Á¢ÄÚ´æ±í */
if ( zftl_setup_table((zftl), &zftl->is_writed))
{
printk( "zftl: could not mount device\n");
goto failed_setup_table;
}
zftl_debug(" [zftl_init] is_writed[%d] = %d\n", partnum, zftl->is_writed);
partnum++;
if(!mutex_inited)
{
mutex_init(&zftl_mutex);
mutex_inited = 1;
}
return 0;
failed_setup_table:
kfree(zftl->blockTable);
failed_alloc_blocktable:
kfree(zftl->versionTable);
failed_alloc_versionTable:
kfree(zftl->blockRepTable);
failed_alloc_blockreptable:
kfree(zftl->oobbuf);
failed_alloc_oobbuf:
kfree(zftl->blockbuf);
failed_alloc_blockbuf:
kfree(zftl);
return 1;
}
static void zftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
uint32_t i;
struct ZFTLrecord *zftl = NULL;
for(i=0; zftl_partition_name[i] != NULL; i++)
{
if(strcmp(mtd->name, zftl_partition_name[i]) == 0)
{
break;
}
}
if(zftl_partition_name[i] == NULL)
{
return;
}
zftl = kzalloc(sizeof(struct ZFTLrecord), GFP_KERNEL);
if (!zftl)
{
kfree(zftl);
return;
}
zftl->mbd.mtd = mtd;
zftl->mbd.devnum = mtd->index;
zftl->mbd.tr = tr;
zftl->mbd.size = mtd->size >> 12;
zftl_init(mtd->name,zftl);
if (add_mtd_blktrans_dev(&zftl->mbd))
{
kfree(zftl);
}
return;
}
static void zftl_remove_dev(struct mtd_blktrans_dev *dev)
{
del_mtd_blktrans_dev(dev);
}
static int zftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
char *buffer)
{
struct ZFTLrecord *zftl = NULL;
int ret = 0;
mutex_lock(&zftl_mutex);
zftl = zftl_get_ZFTLrecord_byname(mbd->mtd->name);
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_read(zftl, block<<12, (uint32_t)4096, (u_char *)buffer);
mutex_unlock(&zftl_mutex);
return ret;
}
static int zftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
char *buffer)
{
struct ZFTLrecord *zftl = NULL;
int ret = 0;
mutex_lock(&zftl_mutex);
zftl = zftl_get_ZFTLrecord_byname(mbd->mtd->name);
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_write(zftl, block<<12, (uint32_t)4096, (u_char *)buffer);
mutex_unlock(&zftl_mutex);
return ret;
}
int zftl_nvread(uint32_t from, uint32_t len, u_char *buffer)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
//zftl = zftl_info[4]; /* ramdisk */
zftl = zftl_get_ZFTLrecord_byname("ramdisk");
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_read(zftl, from, len, buffer);
mutex_unlock(&zftl_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(zftl_nvread);
int zftl_nvwrite(uint32_t from, uint32_t len, u_char *buffer)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
//zftl = zftl_info[4]; /* ramdisk */
zftl = zftl_get_ZFTLrecord_byname("ramdisk");
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_write(zftl, from, len, buffer);
mutex_unlock(&zftl_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(zftl_nvwrite);
int zftl_wrapper_write(char *part_name, int dwStart, int dwLen, char* from)
{
int ret = 0;
struct ZFTLrecord *zftl = NULL;
mutex_lock(&zftl_mutex);
if((part_name == NULL) || (from == NULL))
{
mutex_unlock(&zftl_mutex);
return -5;
}
zftl = zftl_get_ZFTLrecord_byname(part_name);
if(zftl == NULL)
{
BUG();
mutex_unlock(&zftl_mutex);
return -1; //×ö¶ÏÑÔ
}
ret = zftl_write(zftl, dwStart, dwLen, from);
if(ret != 0)
{
mutex_unlock(&zftl_mutex);
return ret;
}
mutex_unlock(&zftl_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(zftl_wrapper_write);
int zftl_wrapper_read(char *part_name, int dwStart, int dwLen, char* to)
{
int ret = 0;
struct ZFTLrecord *zftl = NULL;
mutex_lock(&zftl_mutex);
if((part_name == NULL) || (to == NULL))
{
mutex_unlock(&zftl_mutex);
return -5;
}
zftl = zftl_get_ZFTLrecord_byname(part_name);
if(zftl == NULL)
{
BUG();
mutex_unlock(&zftl_mutex);
return -1; //×ö¶ÏÑÔ
}
ret = zftl_read(zftl, dwStart, dwLen, to);
if(ret != 0)
{
mutex_unlock(&zftl_mutex);
return ret;
}
mutex_unlock(&zftl_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(zftl_wrapper_read);
int nand_NvRead(int dwStart, int dwLen, char* to)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
if( dwStart < nvro_partsize )
{
zftl = zftl_get_ZFTLrecord_byname("nvro"); /*NVR,0~1M*/
}
else
{
zftl = zftl_get_ZFTLrecord_byname("nvrw"); /* NVRW, 1~5M*/
dwStart -= nvro_partsize;
}
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_read(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)to);
mutex_unlock(&zftl_mutex);
#ifdef CONFIG_MTD_NVRESUME
if(ret == -1)
{
zftl_res = 1;
zOss_NvSetErrorAddr(dwStart + nvro_partsize);
hal_Comm_Soft_Reset(RESET_TO_NORMAL);
}
#endif
return ret;
}
EXPORT_SYMBOL_GPL(nand_NvRead);
int nand_NvProgram(int dwStart, int dwLen, char* from)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
if( dwStart < nvro_partsize )
{
zftl = zftl_get_ZFTLrecord_byname("nvro"); /*NVR,0~1M*/
}
else
{
zftl = zftl_get_ZFTLrecord_byname("nvrw"); /* NVRW, 1~5M*/
dwStart -= nvro_partsize;
}
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_write(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)from);
mutex_unlock(&zftl_mutex);
#ifdef CONFIG_MTD_NVRESUME
if(ret == -1)
{
zftl_res = 1;
zOss_NvSetErrorAddr(dwStart + nvro_partsize);
hal_Comm_Soft_Reset(RESET_TO_NORMAL);
}
#endif
return ret;
}
EXPORT_SYMBOL_GPL(nand_NvProgram);
int nand_CdromRead(void *devData,void *buffer, uint32_t blkcnt, uint32_t pos )
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
zftl = zftl_get_ZFTLrecord_byname("cdrom");
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_read(zftl, (pos<<9), (uint32_t)(blkcnt<<9), (u_char *)buffer);
if(ret == 0)
{
ret = blkcnt;
}
mutex_unlock(&zftl_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(nand_CdromRead);
int nand_CdromProgram(void *devData,void *buffer, uint32_t blkcnt, uint32_t pos)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
zftl = zftl_get_ZFTLrecord_byname("cdrom");
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_write(zftl, (pos<<9), (uint32_t)(blkcnt<<9), (u_char *)buffer);
if(ret == 0)
{
ret = blkcnt;
}
mutex_unlock(&zftl_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(nand_CdromProgram);
int nand_SmsRead(int dwStart, int dwLen, char* to)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
zftl = zftl_get_ZFTLrecord_byname("sms");
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_read(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)to);
mutex_unlock(&zftl_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(nand_SmsRead);
int nand_SmsProgram(int dwStart, int dwLen, char* from)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
zftl = zftl_get_ZFTLrecord_byname("sms");
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_write(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)from);
mutex_unlock(&zftl_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(nand_SmsProgram);
int nand_ResetFlagProgram(int dwStart, int dwLen, char* from)
{
mutex_lock(&zftl_mutex);
struct ZFTLrecord *zftl = NULL;
int ret = 0;
//zftl = zftl_info[2]; /* fotaflag */
zftl = zftl_get_ZFTLrecord_byname("fotaflag");
if(!zftl)
{
mutex_unlock(&zftl_mutex);
return -2;
}
ret = zftl_write(zftl, (uint32_t)dwStart, (uint32_t)dwLen, (u_char*)from);
mutex_unlock(&zftl_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(nand_ResetFlagProgram);
static struct mtd_blktrans_ops zftl_tr = {
.name = "zftl",
.major = 35,
.part_bits = 0,
.blksize = 4096,
.readsect = zftl_readblock,
.writesect = zftl_writeblock,
.add_mtd = zftl_add_mtd,
.remove_dev = zftl_remove_dev,
.owner = THIS_MODULE,
};
static int __init init_zftl(void)
{
return register_mtd_blktrans(&zftl_tr);
}
static void __exit cleanup_zftl(void)
{
deregister_mtd_blktrans(&zftl_tr);
}
module_init(init_zftl);
module_exit(cleanup_zftl);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");