| /* |
| * 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"); |
| |
| |
| |
| |
| |
| |
| |