📄 nand_flash.c
字号:
/*****************************************************************************
** nand_flash.c
**
** Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
**
** This file contains copyrighted material. Use of this file is
** restricted by the provisions of a Freescale Software License
** Agreement, which has either been electronically accepted by
** you or has been expressly executed between the parties.
**
** Description: Explanation for the usage of this file.
**
** Revision History:
** -----------------
*****************************************************************************/
#include "nand_flash.h"
#include "protocol.h"
#include "flash_lib.h"
#include "nfc_base.h"
/* global nand device
* handle
*/
nand_t* nand = NULL;
/* indication for bbt enable
* or not, define in the main.c
* host will send command to
* set this flag
*/
extern FL_BBT_FLAG flag_fl_bbt;
/* extern global nand
* device handle, define
* in the nand_ids.c
*/
extern nand_t nand_type[];
/* indication for the follow
* up operation or not.defined
* in the main.c file.
* host will send command to
* set this flag
*/
extern u8 go_on;
/* store the number of bad
* block met during operaton.
* will use this value to adjust
* the following up opeartion(read/prg)
* start end addr
*/
static u32 bb_skip;
/* store the number of bad
* block met during operaton.
* will use this value to adjust
* the following up opeartion(compare)
* start end addr
*/
static u32 cmp_block;
/*!
* Function to erase all blocks
* @param [out] addr - address
*/
static void adjust_addr(u32 *block)
{
/*reset the bb_skip */
if(go_on == 0)
bb_skip = 0;
/* FIXME recount the start block
* May meet bad block during last
* programing, should ajust the
* following up programing start addr
*/
*block += bb_skip;
}
/*!
* Function to read nand flash id
* @param [out] man_id - manufacture id
* @param [out] dev_id - device id
*/
void nand_read_id(u8 *man_id, u8 *dev_id)
{
u32 tmp;
nfc_send_read_id(&tmp);
/* read the id */
*man_id = (u8)tmp;
*dev_id = (u8)(tmp>>8);
}
/*!
* Function to initialize nand
* @param none
*/
s16 nand_init(void)
{
u8 man_id,dev_id;
u32 i;
if(!nand) {
nfc_base_init();
nand_read_id(&man_id, &dev_id);
for(i=0;nand_type[i].man_id !=0;i++) {
if( nand_type[i].man_id == man_id && nand_type[i].dev_id == dev_id ) {
nand = nand_type + i;
nfc_set_fms(nand->bus_width, nand->page_size, nand->oob_size);
break;
}
}
}
if(nand) {
if(flag_fl_bbt != FL_BBT_ENABLE) {
return FLASH_ERROR_NO;
} else {
if(nand->bbt) {
return FLASH_ERROR_NO;
} else {
// for 4G nand same layout with redboot and linux driver
if((nand->page_size == 4096) && (nand->blk_count == 8192))
nand->blk_count >>= 1;
nand->bbt = malloc(nand->blk_count >> 2);
if (nand->bbt) {
memset(nand->bbt,0,nand->blk_count >> 2);
if (nand_scan_bbt(nand) == 0 ) {
return FLASH_ERROR_NO;
}
}
}
}
}
return FLASH_ERROR_INIT;
}
/*!
* Function to erase all blocks
* @param [in] addr - address
* @param [in] size - size
*/
s32 check_param(u32 addr, u32 size)
{
u32 page_size = nand->page_size;
u32 ppb = nand->ppb;
u32 blk_size = page_size * ppb;
u32 blk_count = nand->blk_count;
if ( (addr / blk_size) > blk_count || size == 0 || ((addr + size) / blk_size) > blk_count )
return FLASH_ERROR_OVER_ADDR;
return 0;
}
//u32 atk_channel_send(const u8 *buf, u32 count);
/*!
* Function to read data from nand flash
* @param [in] src - nand flash address
* @param [in] dest - destination address
* @param [in] len - byte size to be read
* @param [in] callback - call back function to send data & progress
*/
s16 nand_read(u32 src, u32 dest, u32 len,dump_callback callback)
{
s16 ret;
u16 csum;
u32 page_size = nand->page_size;
u32 ppb = nand->ppb;
u32 blk_count = nand->blk_count;
u32 blk_size = page_size * ppb;
u32 block = src / blk_size;
u32 page = (src % blk_size) / page_size;
u32 offset = src % page_size;
u32 readlen = 0;
/* FIXME,adjust the start
* block address due to
* the bad block meet in
* pre-read phase
*/
adjust_addr(&block);
/* param check */
ret = check_param(src,len);
if (0 != ret)
return ret;
do {
if (page >= ppb) {
block++;
page = 0;
}
/* check bad block first*/
if ( page == 0) {
if(!is_good_block(nand,block,0)){
block++;
bb_skip++;
continue;
}
}
/* check the offset */
if(offset == 0)
readlen = min(len, page_size);
else
readlen = ((len + offset) > page_size) ? page_size - offset : len;
/* read the page */
ret = nand_read_page(block, page, dest, readlen, offset);
if (FLASH_ERROR_NO != ret)
return ret;
/* send the dump status and data to host */
if (callback != NULL) {
csum = calculate_checksum((u8 *)dest, readlen);
callback((u8 *)dest, FLASH_PARTLY, csum, readlen);
}
/* update the value */
len -= readlen;
dest += readlen;
page++;
offset = 0;
} while (len && block < blk_count);
/* check the blknum */
if(block >= blk_count )
ret = FLASH_ERROR_EOF;
return ret;
}
/*!
* Function to Write data to nand flash
* @param [in] dest - nand flash address
* @param [in] src - data buffer address
* @param [in] len - byte to be written
* @param [in] callback the call back function to notify the progress
*/
s16 nand_write(u32 dest, u32 src, u32 len, u8 file_format, response_callback callback)
{
s16 ret;
u32 page_size = nand->page_size;
u32 ppb = nand->ppb;
u32 blk_count = nand->blk_count;
u32 blk_size = page_size * ppb;
u32 block = dest/blk_size;
u32 page = 0;
u32 writelen = 0;
/* FIXME,adjust the start
* block address due to
* the bad block meet in
* pre-prog phase
*/
adjust_addr(&block);
cmp_block = block;
/* param check */
ret = check_param(dest,len);
if (0 != ret)
return ret;
do {
if (page >= ppb) {
block++;
page = 0;
}
if (0 == page) {
/* check bad block first*/
if(!is_good_block(nand, block,0)) {
block++;
bb_skip++;
continue;
}
/* erase the block */
if (nand_erase_block(block) != FLASH_ERROR_NO) {
mark_bad_block(nand,block);
block++;
bb_skip++;
continue;
}
}
/* write the page */
writelen = min(len, page_size);
ret = nand_write_page(block, page, src, writelen,file_format);
/* if ECC error */
if (FLASH_ERROR_ECC == ret ) {
mark_bad_block(nand, block);
block++;
bb_skip++;
page = 0;
continue;
}
/* if PROG error */
if (FLASH_ERROR_PROG == ret)
return ret;
/* send the prg status to host */
if(callback != NULL)
callback(FLASH_PARTLY, block, writelen);
/* update the value */
len -= writelen;
src += writelen;
page++;
} while (len && block < blk_count);
/* check the blknum */
if(block >= blk_count )
ret = FLASH_ERROR_EOF;
return ret;
}
/*!
* Function to compare the content between source and dest
* @param [in] src - nand flash address
* @param [in] dest - target address
* @param [in] len - length to be compared
* @param [in] callback - the call back function to notify the progress
*/
s16 nand_compare(u32 src, u32 dest, u32 len,response_callback callback)
{
u32 page_size = nand->page_size;
u32 ppb = nand->ppb;
u32 blk_count = nand->blk_count;
u32 blk_size = page_size * ppb;
u32 block = src/blk_size;
u32 page = 0;
u32 readlen;
s16 ret;
/* FIXME,adjust the start
* block address of compare
* to the most recently prog
* start block
*/
block = cmp_block;
do {
if (page >= ppb) {
block++;
page = 0;
}
if (page == 0) {
if(!is_good_block(nand, block, 0)) {
block++;
continue;
}
}
/* set the read size */
readlen = min(len, page_size);
/* compare the page */
ret = nand_compare_page(block, page, dest, readlen);
if (FLASH_ERROR_NO != ret)
return ret;
/* send the verify status to host */
if(callback != NULL)
callback(FLASH_VERIFY, block, readlen);
/* update the value */
len -= readlen;
dest += readlen;
page++;
} while (len && block < blk_count);
/* check the blknum */
if(block >= blk_count )
ret = FLASH_ERROR_EOF;
return ret;
}
/*!
* Function to erase flash content between start address and endadress
* @param [in] start - start address to be erased
* @param [in] end - end address to be erased
*/
s16 nand_erase_conditional(u32 start, u32 end,response_callback callback)
{
s16 ret;
u32 i,j,k;
u32 page_size = nand->page_size;
u32 ppb = nand->ppb;
u32 blk_count = nand->blk_count;
u32 blk_size = page_size * ppb;
/* param check */
ret = check_param(start,end - start);
if (0 != ret)
return ret;
i = start / blk_size;
k = (end - 1) / blk_size;
j = 0;
for(; i<= k; i++) {
//if (is_good_block(nand, i, 0))
{
ret = nand_erase_block(i);
if(FLASH_ERROR_NO != ret) {
j++;
} else {
if(callback)
callback(FLASH_ERASE, i, blk_size);
}
}
}
/*FIX ME ,This is for the Linux bbt upate*/
if ((k >= blk_count -4) && (nand->bbt)) {
free(nand->bbt);
nand->bbt = NULL;
nand_init();
}
/*partial erease*/
if (j > 0)
return FLASH_ERROR_PART_ERASE;
return ret;
}
/*!
* Function to check bad block
* @param [in] nd - nand structure
* @param [in] nrblk - number of blocks
* @param [in] allowbbt - allow write bbt or not
*/
u8 is_good_block(nand_t* nd, u32 nrblk,int allowbbt)
{
if(flag_fl_bbt == FL_BBT_ENABLE) {
return (!nand_isbad_bbt(nd,nrblk,allowbbt));
} else {
return (nand_block_is_good(nrblk));
}
}
/*!
* Function to mark bad block
* @param [in] nd - nand structure
* @param [in] nrblk - number of blocks
*/
s16 mark_bad_block(nand_t *nd,u32 nrblk)
{
if(flag_fl_bbt == FL_BBT_ENABLE) {
return (nand_bbt_markbad(nd,nrblk));
} else {
return (nand_mark_bad_block(nrblk));
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -