zte's code,first commit
Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/mtd/zftl_v3.c b/ap/os/linux/linux-3.4.x/drivers/mtd/zftl_v3.c
new file mode 100755
index 0000000..b09118c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/mtd/zftl_v3.c
@@ -0,0 +1,1225 @@
+/*
+ * 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/mtd/zftl_ecc.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 */
+
+/* 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);
+int zftl_unpack_tags(struct zftl_packed_tags *pt);
+
+
+#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 nvrwo_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;
+ struct zftl_packed_tags pt;
+
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooboffs = offs & mask;
+
+ #ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+ ops.ooblen = sizeof(struct zftl_packed_tags);
+ ops.oobbuf = (uint8_t*)&pt;
+ #else
+ ops.ooblen = sizeof(struct zftl_oob);
+ ops.oobbuf = buf;
+ #endif
+ ops.datbuf = NULL;
+
+ res = nand->_read_oob(nand, (offs & ~mask), &ops);
+
+ #ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+ zftl_unpack_tags(&pt);
+ memcpy(buf,(uint8_t *)&(pt.t),sizeof(struct zftl_oob ));
+ #endif
+
+ 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;
+ struct zftl_oob *oob = (struct zftl_oob *)zftl->oobbuf;
+ uint32_t index = 0;
+ uint32_t addr = 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;
+ }
+
+ if(memcmpb(oob, 0xff, oobsize)) /* µÚÒ»Ò³oob != 0xff */
+ {
+ //if( oob.used == 1 ) /* block is used */
+ if( !memcmp(oob->head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE) ) /* °üº¬ÓзÖÇøÍ· */
+ {
+ *is_writed += 1;
+ if( oob->version > zftl->versionTable[oob->logicBlockID])
+ {
+ /* version is newer */
+ if(zftl->versionTable[oob->logicBlockID] != 0) /* ÒѾÓжÔÓ¦µÄused¿éÁË */
+ zftl->blockTable[zftl->blockRepTable[oob->logicBlockID]] = BLOCK_DIRTY;
+
+ zftl->blockRepTable[oob->logicBlockID] = index;
+ zftl->versionTable[oob->logicBlockID] = 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;
+ }
+
+
+void zftl_pack_tags(struct zftl_packed_tags *pt,
+ struct zftl_oob *t)
+{
+ memcpy((uint8_t *)&(pt->t),(uint8_t *)t,sizeof(struct zftl_oob));
+ zftl_ecc_calc_other((uint8_t *)&pt->t,
+ sizeof(struct zftl_oob),
+ &pt->ecc);
+
+}
+
+static uint32_t is_erased(uint8_t *buf, int len)
+{
+ int i = 0;
+ for (i = 0; i < len; i++)
+ if (buf[i] != 0xFF)
+ return 0;
+ return 1;
+}
+
+int zftl_unpack_tags(struct zftl_packed_tags *pt)
+{
+ enum zftl_ecc_result ecc_result = ZFTL_ECC_RESULT_NO_ERROR;
+ struct zftl_ecc_other ecc;
+ int result;
+
+
+ zftl_ecc_calc_other((unsigned char *)&pt->t,
+ sizeof(struct zftl_oob),
+ &ecc);
+ result = zftl_ecc_correct_other((unsigned char *)&pt->t,\
+ sizeof(struct zftl_oob),\
+ &pt->ecc, &ecc);
+ switch (result) {
+ case 0:
+ ecc_result = ZFTL_ECC_RESULT_NO_ERROR;
+ //printf("ZFTL_ECC_RESULT_NO_ERROR\n");
+ break;
+ case 1:
+
+ ecc_result = ZFTL_ECC_RESULT_FIXED;
+ printk("ZFTL_ECC_RESULT_FIXED\n");
+
+ break;
+ case -1:
+ if(!is_erased((unsigned char *)pt,sizeof(struct zftl_packed_tags)))
+ {
+ ecc_result = ZFTL_ECC_RESULT_UNFIXED;
+
+ //printk("ZFTL_ECC_RESULT_UNFIXED\n");
+ }
+
+ break;
+ default:
+ ecc_result = ZFTL_ECC_RESULT_UNKNOWN;
+ printk("ZFTL_ECC_RESULT_UNKNOWN\n");
+ }
+ if(ecc_result==ZFTL_ECC_RESULT_UNFIXED)
+ {
+
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+static int zftl_write_block(nand_info_t *nand, uint32_t offs, size_t *retlen,
+ struct zftl_oob *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;
+
+
+ #ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+ struct zftl_packed_tags pt;
+ zftl_pack_tags(&pt,oob);
+ #endif
+
+ /* 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_AUTO_OOB;
+ ops.ooboffs = 0;
+
+ #ifdef CONFIG_ZFTL_ENABLE_OOB_ECC
+ ops.ooblen = sizeof(struct zftl_packed_tags);
+ ops.oobbuf = (uint8_t *)&pt;
+ #else
+ ops.ooblen = sizeof(struct zftl_oob);
+ ops.oobbuf = (uint8_t *)oob;
+ #endif
+ 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;
+ struct zftl_oob *oob = (struct zftl_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;
+
+ if((zftl == NULL) || (buf == NULL))
+ {
+ BUG();
+ }
+
+ while(left_to_write > 0)
+ {
+ /* ¿Õ¼ä£¬³õʼ»¯Îª 0xFF */
+ memset(blockbuf, 0xff, erasesize);
+ memset(oob, 0xff, oob_size);
+
+ /* »ñÈ¡Âß¼¿é£¬¼°¿éÄÚÆ«ÒÆ */
+ 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;
+ }
+ if( 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);
+ oob->version = 1;
+ }
+ else
+ {
+
+ oob->version += 1;
+ zftl_debug(" [zftl_write] oob->version = %d,oob->logicBlockID = %d\n", oob->version,oob->logicBlockID);
+ }
+
+ memcpy(oob->head, CONFIG_ZFLT_HEAD, CONFIG_ZFLT_HEAD_BYTE); /* ¿½±´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);
+
+ if( zftl_write_block(nand, (loff_t)((firstBlock + block_new) << erasesize_shift), &retlen,
+ 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[oob->logicBlockID] = block_new;
+
+ zftl_debug(" [zftl_write] zftl->blockRepTable[oob->logicBlockID] = %d,oob->logicBlockID = %d\n", zftl->blockRepTable[oob->logicBlockID],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 */
+ oob->logicBlockID = block_log;
+ oob->used = 0x1;
+ oob->version = 0x1;
+ memcpy(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);
+
+ if( zftl_write_block(nand, (loff_t)((firstBlock + block_new) << erasesize_shift), &retlen,
+ 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[oob->logicBlockID] = block_new;
+
+ zftl_debug(" [zftl_write2] zftl->blockRepTable[oob->logicBlockID] = %d,oob->logicBlockID = %d\n", zftl->blockRepTable[oob->logicBlockID],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);
+ }
+
+ if ( strcmp(part_name,"nvrwo") == 0 )
+ {
+ nvrwo_partsize = partition_size;
+ printk("nvrwo_partsize=0x%0x\n",nvrwo_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_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 if( dwStart < nvro_partsize + nvrwo_partsize)
+ {
+ zftl = zftl_get_ZFTLrecord_byname("nvrwo"); /*NVR,0~1M*/
+ dwStart -= nvro_partsize;
+ }
+ else
+ {
+ zftl = zftl_get_ZFTLrecord_byname("nvrw"); /* NVRW, 1~5M*/
+ dwStart = dwStart - nvro_partsize - nvrwo_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);
+
+ 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 if( dwStart < nvro_partsize + nvrwo_partsize)
+ {
+ zftl = zftl_get_ZFTLrecord_byname("nvrwo"); /*NVR,0~1M*/
+ dwStart -= nvro_partsize;
+ }
+ else
+ {
+ zftl = zftl_get_ZFTLrecord_byname("nvrw"); /* NVRW, 1~5M*/
+ dwStart = dwStart - nvro_partsize - nvrwo_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);
+ 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");
+
+
+
+
+
+
+