📄 samsung_nand.c
字号:
//----------------------------------------------------
//Copyright (C), 2004-2009, lst.
//版权所有 (C), 2004-2009, lst.
//所属模块: 文件系统芯片驱动
//作者:lst
//版本:V1.1.0
//文件描述: 用于三星nand flash的文件系统驱动模块
//其他说明:
//修订历史:
// 2. 日期:20090131
// 作者:lst
// 新版本号:v1.1.0
// 修改说明: 添加用定时器中断来等待操作完成的代码。
// 1. 日期:20090104
// 作者:lst
// 新版本号:v1.0.0
// 修改说明:原始版本
//------------------------------------------------------
//特注: 用int_sync函数修改flash操作函数,使之不死等。
#include "inc_os.h"
#include "samsung_nand.h"
#include "flashfile.h"
#include "gpio.h"
#include <string.h>
static struct flash_chip tg_samsung_nand; //芯片
static volatile uint8_t *pg_nand_address = (uint8_t *)nand_rw_address;
//扇区缓冲区指针,扇区读写函数内部专用,djyffs中,扇区不是所有flash的共性,故不
//出现在struct flash_chip 结构中。动态分配内存而不定义成静态数组,是因为chip作
//为一个资源,是可以删除的,删除时可以回收资源。
static uint8_t *pg_sector_buf; //扇区缓冲区指针
static uint32_t u32g_sectors_per_block; //每块包含的扇区数
//----读扇区(含oob)-------------------------------------------------------------
//功能: 读一个扇区,含oob中的内容一起读
//参数: sector,被读的扇区号
// data,读出数据的缓冲区
//返回: 无
//-----------------------------------------------------------------------------
void __read_sector_and_oob(uint32_t sector,uint8_t *data)
{
uint32_t i;
uint32_t address;
address =cn_sector_size*sector; //计算实际地址
ce_active(); //激活片选
__write_command_2808(cn_2808_select_page0); //写入读模式命令
address_start(); //开始写入地址
*pg_nand_address = (uint8_t)address; // A0 ~ A7
*pg_nand_address = (uint8_t)(address >> 9); // A9 ~ A16
*pg_nand_address = (uint8_t)(address >> 17); // A17 ~ A22
address_end(); //完成写入地址
__wait_ready_2808( ); //等待芯片内部操作完成
for(i=0; i < 528; i++)
{
data[i] = *pg_nand_address; //读取数据
}
ce_inactive(); //关闭片选
return ;
}
//----写掉电恢复块-------------------------------------------------------------
//功能: 当芯片正在写入数据时掉电或者复位,将使正在写入的块数据丢失,为在这种情况
// 下保护数据,采用的策略是:
// 1.如果写入的是新块,则直接写入,因为写入新块失败和写入掉电恢复块失败的
// 后果是一样的。
// 2.如果是修改一块或者从一块后面添加数据,则把数据先写入到掉电恢复块,并
// 在掉电恢复块的适当位置标明掉电恢复块保护的是哪一块数据。
// 3.写入掉电恢复块后,接着把数据写入到目标块中,完成后,擦除掉电恢复块。
// 4.重新启动后,如果检测到掉电恢复块有有效数据,则恢复到目标块
// 本函数的职责:第三种条件下,调用本函数把数据写入掉电恢复块。
//参数: PCRB_block,用于保存掉电恢复数据的块号,芯片的绝对块号
// protected_block,被保护的目标块号,芯片的绝对块号。
//返回: true = 成功写入,false = 写入失败,可能第PCRB_block块是坏块。
//-----------------------------------------------------------------------------
bool_t write_PCRB_2808(uint32_t PCRB_block,
uint32_t protected_block,uint8_t *buf)
{
uint32_t cur_sector; //当前正在写的扇区
uint32_t completed = 0;
uint32_t verify;
uint32_t address;
uint32_t loop;
uint8_t obuf[528];
if(PCRB_block >= tg_samsung_nand.block_sum)
return false;
cur_sector = PCRB_block * u32g_sectors_per_block;
for(loop = 0; loop < u32g_sectors_per_block; loop++)
{
verify = __write_sector_2808_with_ecc(cur_sector,0,
buf+completed,cn_sector_size);
if((verify == cn_all_right_verify) || (verify == cn_ecc_right_verify))
{
completed += cn_sector_size;
}else
return false;
cur_sector++;
}
//第一页的最后4字节保存受保护的目标块号。
address =cn_block_size*PCRB_block + 12;
ce_active(); //激活片选
__write_command_2808(cn_2808_select_oob); //操作oob页
__write_command_2808(cn_2808_page_program); //启动编程命令
address_start(); //启动写入地址时序
*pg_nand_address = (uint8_t)address; // A0 ~ A7
*pg_nand_address = (uint8_t)(address >> 9); // A9 ~ A16
*pg_nand_address = (uint8_t)(address >> 17); // A17 ~ A22
address_end(); //结束写入地址时序
__wait_ready_2808( ); //等待芯片内部操作完成
//受保护的目标块号写入flash,注意:没有做ECC校验,以后补上---db
*pg_nand_address = (uint8_t)protected_block;
*pg_nand_address = (uint8_t)(protected_block>>8);
*pg_nand_address = (uint8_t)(protected_block>>16);
*pg_nand_address = (uint8_t)(protected_block>>24);
__write_command_2808(cn_2808_startup_write); //启动芯片内部写入过程
__wait_ready_2808_slow(cn_wait_page_write); //等待芯片内部操作完成
if(__read_status_2808() & cn_2808_failure)
{
ce_inactive();
return false;
}
ce_inactive();
return true;
}
//----从掉电恢复块恢复数据-----------------------------------------------------
//功能: 从掉电恢复块恢复数据,如果掉电恢复块里有有效数据,比较其与目标块的数据是
// 否一致,如果不一致则用掉电恢复块的数据覆盖目标块。无论是否需要恢复,最后
// 均擦除掉电恢复块。
//参数: PCRB_block,掉电恢复块块号,芯片的绝对块号
// restored,本指针返回被恢复的目标块
//返回: true = 无需恢复或者正确恢复,false = 发生错误,一般是因为目标块是坏块。
//-----------------------------------------------------------------------------
bool_t restore_PCRB_2808(uint32_t PCRB_block,uint32_t *restored)
{
uint32_t verify;
uint32_t address;
uint32_t loop;
uint32_t protected_block;
uint32_t sector_from,sector_to;
uint8_t *sector_buf; //扇区缓冲区,动态分配,不在栈中分配
bool_t result = true;
if(PCRB_block >= tg_samsung_nand.block_sum)
return false;
//第一页的最后4字节保存受保护的目标块号。
address =cn_block_size*PCRB_block + 12;
ce_active(); //激活片选
__write_command_2808(cn_2808_select_oob); //操作oob页
address_start(); //启动写入地址时序
*pg_nand_address = (uint8_t)address; // A0 ~ A7
*pg_nand_address = (uint8_t)(address >> 9); // A9 ~ A16
*pg_nand_address = (uint8_t)(address >> 17); // A17 ~ A22
address_end(); //结束写入地址时序
__wait_ready_2808( ); //等待芯片内部操作完成
//受保护的目标块号写入flash,注意:没有做ECC校验,以后补上---db
protected_block = *pg_nand_address;
protected_block += (uint32_t)(*pg_nand_address)<<8;
protected_block += (uint32_t)(*pg_nand_address)<<16;
protected_block += (uint32_t)(*pg_nand_address)<<24;
*restored = protected_block;
ce_inactive();
if(protected_block > tg_samsung_nand.block_sum)
{
erase_block_2808(PCRB_block);
return true;
}
sector_buf = m_malloc(cn_sector_size,0);
if(sector_buf == NULL)
return false;
erase_block_2808(protected_block);
sector_from = PCRB_block * u32g_sectors_per_block;
sector_to = protected_block * u32g_sectors_per_block;
for(loop = 0; loop < u32g_sectors_per_block; loop++)
{
verify = __read_sector_2808_with_ecc(
sector_from,0,sector_buf,cn_sector_size);
if((verify != cn_all_right_verify) && (verify != cn_ecc_right_verify))
{
result = false;
break;
}
verify = __write_sector_2808_with_ecc(
sector_to,0,sector_buf,cn_sector_size);
if((verify != cn_all_right_verify) && (verify != cn_ecc_right_verify))
{
result = false;
break;
}
sector_from++;
sector_to++;
}
m_free(sector_buf);
erase_block_2808(PCRB_block);
return result;
}
//----等待芯片内部完成操作----------------------------------------------------
//功能: 对芯片执行写操作后,要效用本函数等待操作完成才能进一步操作。
//参数: 无
//返回: true = 正确完成操作,false = 发生错误
//-----------------------------------------------------------------------------
bool_t __wait_ready_2808(void)
{
volatile uint32_t val;
do
{
val=nand_busy_port;
val &= nand_busy_bit;
} while(val == 0);
return true;
}
//----等待芯片内部慢速操作完成-------------------------------------------------
//功能: 对芯片执行写操作后,要调用本函数等待操作完成才能进一步操作。由于一连串的
// 同步操作将浪费很多时间,对于一些很快就绪的操作,宜使用__wait_ready_2808
// 函数。
//参数: wait_time,估计等待时间,微秒数
//返回: true = 正确完成操作,false = 发生错误
//-----------------------------------------------------------------------------
bool_t __wait_ready_2808_slow(uint16_t wait_time)
{
volatile uint32_t val;
timer_set_counter(4,wait_time); //计数值设为wait_time
timer_reload(4); //重载定时值
timer_start(4); //启动定时器
int_asyn_signal_sync(cn_irq_line_timer4);
timer_stop(4);
do
{
val=nand_busy_port;
val &= nand_busy_bit;
} while(val == 0);
return true;
}
//----写入命令----------------------------------------------------
//功能: 写入芯片写命令字
//参数: val,写命令字,
//返回: 无
//-----------------------------------------------------------------------------
void __write_command_2808(uint8_t val)
{
command_start();
*pg_nand_address = val;
command_end();
}
//----读扇区(无ecc校验)----------------------------------------------------
//功能: 不带ecc校验从一扇区内读取数据,地址不能跨扇区边界
//参数: sector,扇区号
// offset,扇区内偏移地址
// data,保存读取数据的缓冲区
// size,读取的尺寸
//返回: 正确读取
//-----------------------------------------------------------------------------
uint32_t __read_sector_2808_no_ecc(uint32_t sector,uint32_t offset,
uint8_t *data,uint32_t size)
{
uint32_t i;
uint32_t address;
address =cn_sector_size*sector + offset; //计算实际地址
ce_active(); //激活片选
if(address & 0x100)
__write_command_2808(cn_2808_select_page1); //写入读模式命令
else
__write_command_2808(cn_2808_select_page0); //写入读模式命令
address_start(); //开始写入地址
*pg_nand_address = (uint8_t)address; // A0 ~ A7
*pg_nand_address = (uint8_t)(address >> 9); // A9 ~ A16
*pg_nand_address = (uint8_t)(address >> 17); // A17 ~ A22
address_end(); //完成写入地址
__wait_ready_2808( ); //等待芯片内部操作完成
for(i=0; i < size; i++)
{
data[i] = *pg_nand_address; //读取数据
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -