1. 前言:
1.1 先分析下nandflash的布局、操作结构
由图可以看出一片Nand flash为一个设备(device),其数据存储分层为:
a. 1个设备(device)=1024个块(Blocks),块也是Nand flash擦除操作的最小单位。
b. 1个块(block) = 64页(Pages),页是Nand flash写入的最小单位,对于每一个页,由数据块区域和空闲区域。数据区,也容易理解,就是存储一些数据,而对于空闲区,一般也叫做OOB(out of Band),这个区域,是基于Nand flash的硬件特性设计的,Nand flash在数据读写的时候很容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错的机制,此机制就被叫做ECC,所以设计了该多余的区域,用来放置数据的校验值。
c. 1个页(page) = 数据块大小(2K)+OOB块大小(64Bytes)那么通过上面我们就可以计算出怎么访问一个物理地址:块大小*块号+页大小*页号+页内地址。
从硬件的图来看,对于K9F1G08X0C这款芯片,其容量为132MB,那么就应该需要28条地址先,而现在只有8条地址线,对于Nand flash就导入了地址周期的概念,对于该款flash,所以需要4个周期:2个列地址(Column)周期和2个行地址(ROW)周期。从下面的功能框图来看,对于列地址A0--A11,就是页内地址,地址范围就是0--4094,与页内地址(2K+64)吻合,其实对于页内地址,其实只需要A0-A10,而对于多出来的A11,是用来表示页内的oob区域。那么对于A12-A27就是用来表示属于哪一个块和块里面的哪一个页号。
1.2 要对nandflash操作,比如读取一页数据、写入一页数据,都要发送对应的命令,而且要符合硬件的规定,如图:
从上图可以看出,在Read读取数据时需两个周期Cycle,即分两次发送对应的命令,第一次是0x00,第二次是0x30,而且两次中间要发送对应的地址行地址(页)+列地址(页内地址),这个要特别注意,在下面的源码读取数据时会分析到。
由于篇幅原因,关于nandflash中oob、bbt、ecc,详见:点击打开链接
2. nandflash相关结构体
a. struct nuc970_nand_info
struct nuc970_nand_info {
struct nand_hw_control controller; //nandflash驱动控制器操作结构体
struct mtd_info mtd; //nandflash分区操作结构体
struct nand_chip chip; //芯片操作结构体
struct mtd_partition *parts; //nandflash分区数组信息
int nr_parts; //nandflash分区数组个数
struct platform_device *pdev; //指向dev.c中的设备
struct clk *clk; //nandflash控制器时钟
struct clk *fmi_clk; //fmi控制器时钟
void __iomem *reg; //寄存器
int eBCHAlgo; //BCH ECC算法
int m_i32SMRASize; //见datasheet FMI__NANDRACTL 表示oob的字节数
int m_ePageSize; //页大小
unsigned char * pnand_vaddr; //nandflash虚拟地址
unsigned char * pnand_phyaddr; //nandflash物理地址
int m_i32MyShowTime; //???相当于信号量
spinlock_t lock;
};
b. struct nand_hw_control
struct nand_hw_control {
spinlock_t lock;
struct nand_chip *active; //芯片操作结构体
wait_queue_head_t wq;
};
c. struct nand_chip
struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
uint8_t (*read_byte)(struct mtd_info *mtd); //读一个byte
u16 (*read_word)(struct mtd_info *mtd); //读一个word
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); //写一个缓冲区数据
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); //读一个缓冲区数据
void (*select_chip)(struct mtd_info *mtd, int chip); //片选
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); //坏块
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); //坏块标记
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); //命令控制
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
u8 *id_data); //???
int (*dev_ready)(struct mtd_info *mtd); //设备忙状态?
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
int page_addr); //命令功能
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); //???
void (*erase_cmd)(struct mtd_info *mtd, int page); //擦除命令
int (*scan_bbt)(struct mtd_info *mtd); //bbt:bad blockta(即坏块表),扫描坏块表
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw);
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
int chip_delay; //芯片时序特性
unsigned int options;
unsigned int bbt_options; //坏块表选项
int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
uint64_t chipsize;
int pagemask;
int pagebuf;
unsigned int pagebuf_bitflips;
int subpagesize;
uint8_t cellinfo;
int badblockpos;
int badblockbits;
int onfi_version; //ONFI版本
struct nand_onfi_params onfi_params;
flstate_t state;
uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
uint8_t *bbt;
struct nand_bbt_descr *bbt_td; //坏块表
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
void *priv;
};
d. struct mtd_partition
struct mtd_partition {
char *name; /* identifier string */
uint64_t size; /* partition size */
uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */
};
e. struct nand_ecclayout
struct nand_ecclayout {
__u32 eccbytes; //表示使用几个ecc字节
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE]; //表示ecc占用的位置,因为现在一版大页面4kbyte也就128个,所以数组为128,以后有更大页面的,这里也要改了。
__u32 oobavail; //有几个oob可用,这个跟下面的成员有点像,一般用下面的
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; //定义oob有效个数,从哪开始等
};
d. struct nand_oobfree
struct nand_oobfree {
__u32 offset;
__u32 length;
};
e. struct mtd_info
struct mtd_info {
u_char type; //flash类型,如MTD_NORFLASH,MTD_NANDFLASH
uint32_t flags; //MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等
uint64_t size; //mtd设备的大小
/* "Major" erase size for the device. Naïve users may take this
* to be the only erase size available, or may use the more detailed
* information below if they desire
*/
uint32_t erasesize; //MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
* it is of ECC block size, etc. It is illegal to have writesize = 0.
* Any driver registering a struct mtd_info must ensure a writesize of
* 1 or larger.
*/
uint32_t writesize; //写大小, 对于norFlash是字节,对nandFlash为一页
/*
* Size of the write buffer used by the MTD. MTD devices having a write
* buffer can write multiple writesize chunks at a time. E.g. while
* writing 4 * writesize bytes to a device with 2 * writesize bytes
* buffer the MTD driver can (but doesn't have to) do 2 writesize
* operations, but not 4. Currently, all NANDs have writebufsize
* equivalent to writesize (NAND page size). Some NOR flashes do have
* writebufsize greater than writesize.
*/
uint32_t writebufsize; //
uint32_t oobsize; // Amount of OOB data per block (e.g. 16) OOB字节数
uint32_t oobavail; // Available OOB bytes per block 可用的OOB字节数
/*
* If erasesize is a power of 2 then the shift is stored in
* erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
*/
unsigned int erasesize_shift;
unsigned int writesize_shift;
/* Masks based on erasesize_shift and writesize_shift */
unsigned int erasesize_mask;
unsigned int writesize_mask;
/*
* read ops return -EUCLEAN if max number of bitflips corrected on any
* one region comprising an ecc step equals or exceeds this value.
* Settable by driver, else defaults to ecc_strength. User can override
* in sysfs. N.B. The meaning of the -EUCLEAN return code has changed;
* see Documentation/ABI/testing/sysfs-class-mtd for more detail.
*/
unsigned int bitflip_threshold;
// Kernel-only stuff starts here.
const char *name;
int index;
/* ECC layout structure pointer - read only! */
struct nand_ecclayout *ecclayout;
/* max number of correctible bit errors per ecc step */
unsigned int ecc_strength;
/* Data for variable erase regions. If numeraseregions is zero,
* it means that the whole device has erasesize as given above.
*/
int numeraseregions;
struct mtd_erase_region_info *eraseregions; //可变擦除区域
/*
* Do not call via these pointers, use corresponding mtd_*()
* wrappers instead.
*/
//擦除函数
int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);
int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags);
// 读写flash函数
int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
//带oob读写Flash函数
int (*_read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int (*_write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
size_t len);
int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf);
int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
size_t len);
int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf);
int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
size_t len, size_t *retlen, u_char *buf);
int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len);
int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen);
void (*_sync) (struct mtd_info *mtd);
int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
int (*_suspend) (struct mtd_info *mtd);
void (*_resume) (struct mtd_info *mtd);
/*
* If the driver is something smart, like UBI, it may need to maintain
* its own reference counting. The below functions are only for driver.
*/
int (*_get_device) (struct mtd_info *mtd);
void (*_put_device) (struct mtd_info *mtd);
/* Backing device capabilities for this device
* - provides mmap capabilities
*/
struct backing_dev_info *backing_dev_info;
struct notifier_block reboot_notifier; /* default mode before reboot */
/* ECC status information */
struct mtd_ecc_stats ecc_stats;
/* Subpage shift (NAND) */
int subpage_sft;
void *priv; //指向chip, struct nand_chip *chip
struct module *owner;
struct device dev;
int usecount;
};
3. struct platform_device
static u64 nuc970_device_fmi_dmamask = 0xffffffffUL;
struct platform_device nuc970_device_fmi = {
.name = "nuc970-fmi",
.id = -1,
.num_resources = ARRAY_SIZE(nuc970_fmi_resource),
.resource = nuc970_fmi_resource,
.dev = {
.dma_mask = &nuc970_device_fmi_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
static struct resource nuc970_fmi_resource[] = {
[0] = {
.start = NUC970_PA_FMI,
.end = NUC970_PA_FMI + NUC970_SZ_FMI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_FMI,
.end = IRQ_FMI,
.flags = IORESOURCE_IRQ,
}
};
static u64 nuc970_device_fmi_dmamask = 0xffffffffUL;
platform_add_devices(&nuc970_device_fmi, 1);
fmi设备注册。
4. static struct platform_driver
static struct platform_driver nuc970_nand_driver = {
.driver = {
.name = "nuc970-fmi",
.owner = THIS_MODULE,
},
.probe = nuc970_nand_probe,
.remove = nuc970_nand_remove,
};
设备和驱动通过名称“nuc970-fmi”匹配,调用.probe=nuc970_nand_probe;
5. probe(...)
在分析nuc970_nand_probe(...)函数之前,先看下驱动的几个API
nandflash硬件ECC初始化:
static void nuc970_nand_hwecc_init (struct mtd_info *mtd)
{
struct nuc970_nand_info *nand = container_of(mtd, struct nuc970_nand_info, mtd);
writel ( readl(REG_SMCSR)|0x1, REG_SMCSR); // reset SM controller
// Redundant area size
writel( nand->m_i32SMRASize , REG_SMREACTL );
// Protect redundant 3 bytes
// because we need to implement write_oob function to partial data to oob available area.
// Please note we skip 4 bytes
writel( readl(REG_SMCSR) | 0x100, REG_SMCSR);
// To read/write the ECC parity codes automatically from/to NAND Flash after data area field written.
writel( readl(REG_SMCSR) | 0x10, REG_SMCSR);
if ( nand->eBCHAlgo >= 0 ) {
// Set BCH algorithm 设置ECC算法
writel( (readl(REG_SMCSR) & (~0x007C0000)) | g_i32BCHAlgoIdx[nand->eBCHAlgo], REG_SMCSR);
// Enable H/W ECC, ECC parity check enable bit during read page
//使能ECC算法 | 使能读取页数据后,使用字段ecc检查
writel( readl(REG_SMCSR) | 0x00800080, REG_SMCSR);
} else {
// Disable H/W ECC / ECC parity check enable bit during read page
//禁止ECC | 读取数据后,禁止使用字段
writel( readl(REG_SMCSR) & (~0x00800000) &(~0x80), REG_SMCSR);
}
}
硬件ECC禁止:
static void nuc970_nand_hwecc_fini (struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
if ( chip->ecc.mode == NAND_ECC_HW_OOB_FIRST )
writel(readl(REG_SMCSR)&(~0x00800000), REG_SMCSR); // ECC disable
}
NandFlash初始化,包括nandflash选择使能,通讯时序设置,片选CS0,写保护寄存器开启,软复位nandflash:
static void nuc970_nand_initialize ( void )
{
ENTER() ;
// Enable SM_EN 该寄存器还包括eMMC,这里使能nandflash功能
writel( NAND_EN, REG_NAND_FMICSR );
// Timing control
// writel(0x3050b, REG_SMTCR);
// tCLS= (2+1)TAHB,
// tCLH= (2*2+2)TAHB,
// tALS= (2*1+1)TAHB,
// tALH= (2*2+2)TAHB,
writel(0x20305, REG_SMTCR); //时序控制
// Enable SM_CS0 片选CS0
writel((readl(REG_SMCSR)&(~0x06000000))|0x04000000, REG_SMCSR);
//nandflash 写保护寄存器, 1表示没有写保护,数据是可写的
writel(0x1, REG_NFECR); /* un-lock write protect */
// NAND Reset 软复位
writel(readl(REG_SMCSR) | 0x1, REG_SMCSR); // software reset
LEAVE();
}
页大小决定了ecc空间,具体如下:
static const int g_i32ParityNum[ePageSize_CNT][eBCH_CNT] = {
{ 8, 15, 23, 29, -1 }, // For 512
{ 32, 60, 92, 116, 90 }, // For 2K
{ 64, 120, 184, 232, 180 }, // For 4K
{ 128, 240, 368, 464, 360 }, // For 8K
};
硬件ecc选择bch算法:
static int nuc970_hwecc_choice_bch_algorithm ( E_PAGESIZE ePagesize, int oob_size, int oob_available )
{
int i;
/* Rule: select a BCH algorithm as the oob_size/2 large than parity number */
for ( i=eBCH_T24; i>=0; i-- )
if ( g_i32ParityNum[ePagesize][i] > 0 )
//oob_size-oob_available-DEF_RESERVER_OOB_SIZE_FOR_MARKER=存储ecc校验空间,
//如果g_i32ParityNum[ePagesize][i]值小于该空间表示可以存储ecc的校验值,即
//可选择该硬件bch算法.
if ( g_i32ParityNum[ePagesize][i] <= (oob_size-oob_available-DEF_RESERVER_OOB_SIZE_FOR_MARKER) ) // use half ecc size
break;
return i;
}
nuc970选择ECC算法:
static void nuc970_choice_bch_algo ( struct mtd_info *mtd, int page )
{
int i;
struct nuc970_nand_info *nand = container_of(mtd, struct nuc970_nand_info, mtd);
struct mtd_partition *part;
struct nand_ecclayout *part_ecclayout;
int BCHAlgoIdx=0;
int SMRASize=0;
u32 page_addr = page * mtd->writesize; //页地址*1页的大小
for ( i=0; i<nand->nr_parts; i++ )
{
part = &nand->parts[i];
part_ecclayout = part->ecclayout;
if ( part_ecclayout && part_ecclayout != mtd->ecclayout ) { //ecclayout是否匹配?
if ( part->offset <= page_addr && page_addr < part->offset+part->size ) //判定page_addr地址是否在当前分区范围内?
{
struct nuc970_nand_ecclayout * pSNUC970NandEccLayout=(struct nuc970_nand_ecclayout *)part_ecclayout;
BCHAlgoIdx = pSNUC970NandEccLayout->m_BCHAlgoidx;
SMRASize = pSNUC970NandEccLayout->m_SMRASize;
break;
}
}
}
if ( i == nand->nr_parts ) //相等表示未找到对应的分区
{
//chp BCHAlgoIdx = nuc970_hwecc_choice_bch_algorithm ( nand->m_ePageSize, mtd->oobsize, mtd->oobavail );
BCHAlgoIdx = nuc970_nand_SYSTEM_oob.m_BCHAlgoidx;
SMRASize = mtd->oobsize;
}
if ( BCHAlgoIdx != nand->eBCHAlgo ) //bch算法进行了更新?
{
if ( BCHAlgoIdx >= 0 )
printk("(0x%08X)BCH change to %s: %dB\n", page_addr, g_pcBCHAlgoIdx[BCHAlgoIdx], g_i32ParityNum[nand->m_ePageSize][BCHAlgoIdx] );
else
printk("(0x%08X)BCH change to No-ECC\n", page_addr );
}
nand->eBCHAlgo = BCHAlgoIdx;
if ( SMRASize != nand->m_i32SMRASize )
printk("(0x%08X)SMRA size change to %dB\n", page_addr, SMRASize );
nand->m_i32SMRASize = SMRASize;
}
nandflash ecc数据纠正:
void fmiSM_CorrectData_BCH(u8 ucFieidIndex, u8 ucErrorCnt, u8* pDAddr)
{
u32 uaData[24], uaAddr[24];
u32 uaErrorData[4];
u8 ii, jj;
u32 uPageSize;
u32 field_len, padding_len, parity_len;
u32 total_field_num;
u8 *smra_index;
ENTER();
//--- assign some parameters for different BCH and page size
switch (readl(REG_SMCSR) & 0x007C0000) //获取nandflash寄存器中BCH算法
{
case BCH_T24:
field_len = 1024;
padding_len = BCH_PADDING_LEN_1024;
parity_len = BCH_PARITY_LEN_T24;
break;
case BCH_T15:
field_len = 512;
padding_len = BCH_PADDING_LEN_512;
parity_len = BCH_PARITY_LEN_T15;
break;
case BCH_T12:
field_len = 512;
padding_len = BCH_PADDING_LEN_512;
parity_len = BCH_PARITY_LEN_T12;
break;
case BCH_T8:
field_len = 512;
padding_len = BCH_PADDING_LEN_512;
parity_len = BCH_PARITY_LEN_T8;
break;
case BCH_T4:
field_len = 512;
padding_len = BCH_PADDING_LEN_512;
parity_len = BCH_PARITY_LEN_T4;
break;
default:
printk("NAND ERROR: %s(): invalid SMCR_BCH_TSEL = 0x%08X\n", __FUNCTION__, (u32)(readl(REG_SMCSR) & 0x7C0000));
LEAVE();
return;
}
uPageSize = readl(REG_SMCSR) & 0x00030000; //获取nandflash中页大小
switch (uPageSize)
{
case 0x30000: total_field_num = 8192 / field_len; break;
case 0x20000: total_field_num = 4096 / field_len; break;
case 0x10000: total_field_num = 2048 / field_len; break;
case 0x00000: total_field_num = 512 / field_len; break;
default:
printk("NAND ERROR: %s(): invalid SMCR_PSIZE = 0x%08X\n", __FUNCTION__, uPageSize);
LEAVE();
return;
}
//--- got valid BCH_ECC_DATAx and parse them to uaData[]
// got the valid register number of BCH_ECC_DATAx since one register include 4 error bytes
jj = ucErrorCnt/4;
jj ++;
if (jj > 6)
jj = 6; // there are 6 BCH_ECC_DATAx registers to support BCH T24
for(ii=0; ii<jj; ii++)
{
uaErrorData[ii] = readl(REG_BCH_ECC_DATA0 + ii*4); //读取ECC数据
}
for(ii=0; ii<jj; ii++)
{
uaData[ii*4+0] = uaErrorData[ii] & 0xff;
uaData[ii*4+1] = (uaErrorData[ii]>>8) & 0xff;
uaData[ii*4+2] = (uaErrorData[ii]>>16) & 0xff;
uaData[ii*4+3] = (uaErrorData[ii]>>24) & 0xff;
}
//--- got valid REG_BCH_ECC_ADDRx and parse them to uaAddr[]
// got the valid register number of REG_BCH_ECC_ADDRx since one register include 2 error addresses
jj = ucErrorCnt/2;
jj ++;
if (jj > 12)
jj = 12; // there are 12 REG_BCH_ECC_ADDRx registers to support BCH T24
for(ii=0; ii<jj; ii++)
{
uaAddr[ii*2+0] = readl(REG_BCH_ECC_ADDR0 + ii*4) & 0x07ff; // 11 bits for error address nandflash ecc错误字节地址
uaAddr[ii*2+1] = (readl(REG_BCH_ECC_ADDR0 + ii*4)>>16) & 0x07ff;
}
//--- pointer to begin address of field that with data error
pDAddr += (ucFieidIndex-1) * field_len;
//--- correct each error bytes
for(ii=0; ii<ucErrorCnt; ii++)
{
// for wrong data in field
if (uaAddr[ii] < field_len)
{
#ifdef NUC970_NAND_DEBUG
printk("BCH error corrected for data: address 0x%08X, data [0x%02X] --> ",
(unsigned int)(pDAddr+uaAddr[ii]), (unsigned int)(*(pDAddr+uaAddr[ii])));
#endif
*(pDAddr+uaAddr[ii]) ^= uaData[ii]; //异或,计算ECC,即读取数据的ECC和nandflash中的ecc进行异或比较
#ifdef NUC970_NAND_DEBUG
printk("[0x%02X]\n", *(pDAddr+uaAddr[ii]));
#endif
}
// for wrong first-3-bytes in redundancy area
else if (uaAddr[ii] < (field_len+3))
{
uaAddr[ii] -= field_len;
uaAddr[ii] += (parity_len*(ucFieidIndex-1)); // field offset
#ifdef NUC970_NAND_DEBUG
printk("BCH error corrected for 3 bytes: address 0x%08X, data [0x%02X] --> ",
(unsigned int)((u8 *)REG_SMRA0 + uaAddr[ii]), (unsigned int)(*((u8 *)REG_SMRA0 + uaAddr[ii])));
#endif
*((u8 *)REG_SMRA0 + uaAddr[ii]) ^= uaData[ii];
#ifdef NUC970_NAND_DEBUG
printk("[0x%02X]\n", *((u8 *)REG_SMRA0+uaAddr[ii]));
#endif
}
//以下代码为纠错代码,老衲没有看明白.......!!!!!
// for wrong parity code in redundancy area
else
{
// BCH_ERR_ADDRx = [data in field] + [3 bytes] + [xx] + [parity code]
// |<-- padding bytes -->|
// The BCH_ERR_ADDRx for last parity code always = field size + padding size.
// So, the first parity code = field size + padding size - parity code length.
// For example, for BCH T12, the first parity code = 512 + 32 - 23 = 521.
// That is, error byte address offset within field is
uaAddr[ii] = uaAddr[ii] - (field_len + padding_len - parity_len);
// smra_index point to the first parity code of first field in register SMRA0~n
smra_index = (u8 *)
(REG_SMRA0 + (readl(REG_SMREACTL) & 0x1ff) - // bottom of all parity code -
(parity_len * total_field_num) // byte count of all parity code
);
// final address = first parity code of first field +
// offset of fields +
// offset within field
#ifdef NUC970_NAND_DEBUG
printk("BCH error corrected for parity: address 0x%08X, data [0x%02X] --> ",
(unsigned int)(smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii]),
(unsigned int)(*(smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii])));
#endif
*((u8 *)smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii]) ^= uaData[ii];
#ifdef NUC970_NAND_DEBUG
printk("[0x%02X]\n",
*((u8 *)smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii]));
#endif
}
} // end of for (ii<ucErrorCnt)
LEAVE();
}
int fmiSMCorrectData (struct mtd_info *mtd, unsigned long uDAddr )
{
int uStatus, ii, jj, i32FieldNum=0;
volatile int uErrorCnt = 0;
ENTER();
if ( readl ( REG_SMISR ) & 0x4 ) //只读,表明ECC校验值是否出错
{
if ( ( readl(REG_SMCSR) & 0x7C0000) == BCH_T24 ) //BCH_T24的页为1024字节,其他的为512字节
i32FieldNum = mtd->writesize / 1024; // Block=1024 for BCH
else
i32FieldNum = mtd->writesize / 512;
//每一页的大小,我使用的是每页=2K,而计算ecc是每512字节计算一个oob,所以这里有四个
if ( i32FieldNum < 4 ) //每一页会计算ECC校
i32FieldNum = 1;
else
i32FieldNum /= 4;
for ( jj=0; jj<i32FieldNum; jj++ )
{
//REG_SMECC_ST0+jj*4: 该寄存器每8个bit表示一个ECC状态,Bit0~1是否有ECC错误,Bit2~7 表示ECC错误的个数是多少?
uStatus = readl ( REG_SMECC_ST0+jj*4 );
if ( !uStatus )
continue;
for ( ii=1; ii<5; ii++ )
{
if ( !(uStatus & 0x03) ) { // No error Bit0~1
uStatus >>= 8;
continue;
} else if ( (uStatus & 0x03)==0x01 ) { // Correctable error 错误,数据纠正
uErrorCnt = (uStatus >> 2) & 0x1F;
#ifdef NUC970_NAND_DEBUG
printk("Field (%d, %d) have %d error!!\n", jj, ii, uErrorCnt);
#endif
fmiSM_CorrectData_BCH(jj*4+ii, uErrorCnt, (char*)uDAddr);
break;
} else // uncorrectable error or ECC error
{
#ifdef NUC970_NAND_DEBUG
printk("SM uncorrectable error is encountered, 0x%4x !!\n", uStatus);
#endif
LEAVE();
return -1;
}
uStatus >>= 8;
}
} //jj
}
LEAVE();
return uErrorCnt;
}
使能硬件ECC:
void nuc970_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
ENTER();
#ifdef NUC970_NAND_DEBUG
{
char * ptr=REG_SMRA0;
int i=0;
if( mode == NAND_ECC_READ )
printk("[R]=\n");
else
printk("[W]=\n");
for(i=0; i<mtd->oobsize; i++)
{
printk("%X ", *(ptr+i) );
if ( i % 32 == 31)
printk("\n");
}
printk("\n");
}
#endif
LEAVE();
}
DMA使能、禁止:
static void nuc970_nand_dmac_init( void )
{
// DMAC enable
writel( readl(REG_NAND_DMACCSR) | 0x3, REG_NAND_DMACCSR);
writel( readl(REG_NAND_DMACCSR) & (~0x2), REG_NAND_DMACCSR);
// Clear DMA finished flag
writel( readl(REG_SMISR) | 0x1, REG_SMISR);
// Disable Interrupt
writel(readl(REG_SMIER) & ~(0x1), REG_SMIER);
}
/*
* nuc970_nand_dmac_fini - Finalize dma controller
*/
static void nuc970_nand_dmac_fini(void)
{
// Clear DMA finished flag
writel(readl(REG_SMISR) | 0x1, REG_SMISR);
}
读nandflash字节:
static unsigned char nuc970_nand_read_byte(struct mtd_info *mtd)
{
unsigned char ret;
struct nuc970_nand_info *nand;
ENTER() ;
if ( nuc970_wait_sem(mtd) < 0 )
return -1;
nand = container_of(mtd, struct nuc970_nand_info, mtd);
ret = (unsigned char)read_data_reg(nand);
nuc970_release_sem(mtd);
LEAVE();
return ret;
}
批量读nandflash字节:
static void nuc970_nand_read_buf(struct mtd_info *mtd, unsigned char *buf, int len)
{
int i;
struct nuc970_nand_info *nand;
nand = container_of(mtd, struct nuc970_nand_info, mtd);
ENTER() ;
if ( nuc970_wait_sem(mtd) < 0 )
return;
for (i = 0; i < len; i++)
buf[i] = (unsigned char)read_data_reg(nand);
nuc970_release_sem(mtd);
LEAVE();
}
批量写nandflash字节:
static void nuc970_nand_write_buf(struct mtd_info *mtd, const unsigned char *buf, int len)
{
int i;
struct nuc970_nand_info *nand;
nand = container_of(mtd, struct nuc970_nand_info, mtd);
ENTER() ;
if ( nuc970_wait_sem(mtd) < 0 )
return;
for (i = 0; i < len; i++)
write_data_reg(nand, buf[i]);
nuc970_release_sem(mtd);
LEAVE();
}
DMA对nandflash的读、写操作:
static inline int _nuc970_nand_dma_transfer(struct mtd_info *mtd, const u_char *addr, unsigned int len, int is_write)
{
struct nuc970_nand_info *nand = container_of(mtd, struct nuc970_nand_info, mtd);
dma_addr_t dma_addr = (dma_addr_t)nand->pnand_phyaddr;
ENTER() ;
// For save, wait DMAC to ready
while ( readl(REG_NAND_DMACCSR) & 0x200 );
// Reinitial dmac
nuc970_nand_dmac_init();
// Fill dma_addr
writel((unsigned long)dma_addr, REG_NAND_DMACSAR);
// Enable target abort interrupt generation during DMA transfer.
writel( 0x1, REG_NAND_DMACIER);
// Clear Ready/Busy 0 Rising edge detect flag
writel(0x400, REG_SMISR);
// Set which BCH algorithm
if ( nand->eBCHAlgo >= 0 ) {
// Set BCH algorithm
writel( (readl(REG_SMCSR) & (~0x7C0000)) | g_i32BCHAlgoIdx[nand->eBCHAlgo], REG_SMCSR);
// Enable H/W ECC, ECC parity check enable bit during read page
writel( readl(REG_SMCSR) | 0x00800000 | 0x80, REG_SMCSR);
} else {
// Disable H/W ECC / ECC parity check enable bit during read page
writel( readl(REG_SMCSR) & (~0x00800080), REG_SMCSR);
}
writel( nand->m_i32SMRASize , REG_SMREACTL );
writel( readl(REG_SMIER) & (~0x4), REG_SMIER );
writel ( 0x4, REG_SMISR );
// Enable SM_CS0
writel((readl(REG_SMCSR)&(~0x06000000))|0x04000000, REG_SMCSR);
/* setup and start DMA using dma_addr */
if ( is_write ) {
register char * ptr=REG_SMRA0; //register 是提示编译器将这个变量不放在栈内,放在寄存器中进行操作
// To mark this page as dirty.
if ( ptr[3] == 0xFF )
ptr[3] = 0;
if ( ptr[2] == 0xFF )
ptr[2] = 0;
if ( addr )
memcpy( (void*)nand->pnand_vaddr, (void*)addr, len);
writel ( readl(REG_SMCSR) | 0x4, REG_SMCSR ); //DMA写使能
while ( !(readl(REG_SMISR) & 0x1) ); //DMA读、写数据完成中断标识
} else {
// Blocking for reading
// Enable DMA Read
writel ( readl(REG_SMCSR) | 0x2, REG_SMCSR); //DMA读使能
if ( readl(REG_SMCSR) & 0x80 ) {
do {
int stat=0;
if ( (stat=fmiSMCorrectData ( mtd, (unsigned long)nand->pnand_vaddr)) < 0 ) //重要,待分析!!!
{
mtd->ecc_stats.failed++;
writel ( 0x4, REG_SMISR ); //清除ECC校验出错中断标识
writel ( 0x3, REG_NAND_DMACCSR); // reset DMAC
writel ( readl(REG_SMCSR)|0x1, REG_SMCSR); // reset SM controller
break;
}
else if ( stat > 0 ) {
//mtd->ecc_stats.corrected += stat; //Occure: MLC UBIFS mount error
writel ( 0x4, REG_SMISR );
}
} while ( !(readl(REG_SMISR) & 0x1) || (readl(REG_SMISR) & 0x4) );
} else
while ( !(readl(REG_SMISR) & 0x1) );
if ( addr )
memcpy( (void*)addr, (void*)nand->pnand_vaddr, len );
}
nuc970_nand_dmac_fini();
LEAVE();
return 0;
}
从DMA中nandflash批量读:
static void nuc970_read_buf_dma(struct mtd_info *mtd, u_char *buf, int len)
{
ENTER();
if ( nuc970_wait_sem(mtd) < 0 )
return;
if ( len == mtd->writesize ) /* start transfer in DMA mode */
_nuc970_nand_dma_transfer ( mtd, buf, len, 0x0);
else {
nuc970_nand_read_buf(mtd, buf, len);
#ifdef NUC970_NAND_DEBUG
{
int i;
printk("R OOB %d\n", len );
for ( i=0; i<len; i++ )
{
printk("%02X ", buf[i] );
if ( i%32 == 31 ) printk("\n");
}
printk("\n");
}
#endif
}
nuc970_release_sem(mtd);
LEAVE();
}
向DMA中nandflash批量写:
static void nuc970_write_buf_dma(struct mtd_info *mtd, const u_char *buf, int len)
{
ENTER();
if ( nuc970_wait_sem(mtd) < 0 )
return;
if ( len == mtd->writesize ) /* start transfer in DMA mode */
_nuc970_nand_dma_transfer(mtd, (u_char *)buf, len, 0x1);
else
{
#ifdef NUC970_NAND_DEBUG
int i;
printk("W OOB %d\n", len);
for ( i=0; i<len; i++ )
{
printk("%02X ", buf[i] );
if ( i%32 == 31 ) printk("\n");
}
#endif
nuc970_nand_write_buf(mtd, buf, len);
}
nuc970_release_sem(mtd);
LEAVE();
}
校验nandflash是否busy状态:
static int nuc970_check_rb(struct nuc970_nand_info *nand)
{
unsigned int val;
ENTER();
spin_lock(&nand->lock);
val = readl(REG_SMISR) & READYBUSY;
spin_unlock(&nand->lock);
LEAVE();
return val;
}
static int nuc970_nand_devready(struct mtd_info *mtd)
{
struct nuc970_nand_info *nand;
int ready;
ENTER() ;
if ( nuc970_wait_sem(mtd) < 0 )
return -1;
nand = container_of(mtd, struct nuc970_nand_info, mtd);
ready = (nuc970_check_rb(nand)) ? 1 : 0;
nuc970_release_sem(mtd);
LEAVE();
return ready;
}
命令操作接口,对应ONFI中的标准,这里特别强调下命令操作的步骤,例如读数据操作,内容如下:第一次是0x00h,第二次是0x30h,而两次命令中间,需要发送对应的你所要读取的页的地址,在发送地址截至,接下来调用读数据接口接收数据:
static void nuc970_nand_command_lp(struct mtd_info *mtd, unsigned int command, int column, int page_addr)
{
register struct nand_chip *chip = mtd->priv;
struct nuc970_nand_info *nand;
ENTER() ;
if ( nuc970_wait_sem(mtd) < 0 ) //获取mtd分区是否加锁?
{
LEAVE();
return;
}
nand = container_of(mtd, struct nuc970_nand_info, mtd);
//页地址有效 && 对应有效的命令 = 选择有效的bch算法
if ( page_addr != -1 && ( command == NAND_CMD_SEQIN || command == NAND_CMD_READ0 || command == NAND_CMD_READOOB ) )
nuc970_choice_bch_algo ( mtd, page_addr );
if (command == NAND_CMD_READOOB) { //如何理解if语句内部???解析如下
column += mtd->writesize; //oob位于每一页的后面,这里的column表示列地址(nuc970_nand_read_oob_hwecc(...)函数在
//读取oob时,column=0),要读取出对应的oob开始地址为“页+cloumn”
command = NAND_CMD_READ0;
}
//以下部分要理解,需看懂时序,详解《linux_nand_driver.pdf》第20页时序图
write_cmd_reg(nand, command & 0xff); //启动写命令到nandflash控制器
if (column != -1 || page_addr != -1) {
if (column != -1) {
write_addr_reg(nand, (column&0xFF) ); //列地址,即页内地址 低8bit
if ( page_addr != -1 )
write_addr_reg(nand, (column >> 8) ); //列地址,即页内地址 高8bit
else
write_addr_reg(nand, (column >> 8) | ENDADDR); //ENDADDR, FMI_NANDADDR的第31bit,表明本次的地址结束
}
if (page_addr != -1) {
write_addr_reg(nand, (page_addr&0xFF) ); //页地址,具体可以定位到哪一页
if ( chip->chipsize > (128 << 20) ) {
write_addr_reg(nand, (page_addr >> 8)&0xFF );
write_addr_reg(nand, ((page_addr >> 16)&0xFF)|ENDADDR );
} else {
write_addr_reg(nand, ((page_addr >> 8)&0xFF)|ENDADDR );
}
}
}
switch (command) {
case NAND_CMD_ERASE1: //case语句成立,就表示当前命令执行完,直接释放锁,退出即可!
case NAND_CMD_ERASE2:
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_SEQIN:
case NAND_CMD_RNDIN:
case NAND_CMD_STATUS:
LEAVE();
nuc970_release_sem(mtd);
return;
case NAND_CMD_RESET: //待分析工作原理???
if (chip->dev_ready)
break;
if ( chip->chip_delay )
udelay(chip->chip_delay);
write_cmd_reg(nand, NAND_CMD_STATUS);
write_cmd_reg(nand, command);
while (!nuc970_check_rb(nand)) ;
LEAVE();
nuc970_release_sem(mtd);
return;
case NAND_CMD_RNDOUT: //待分析工作原理???
write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); //启动随机读取,外面调用该nuc970_nand_command_lp(...)函数,待该函数返回会有相应的接口读取数据
nuc970_release_sem(mtd);
LEAVE();
return;
case NAND_CMD_READ0: //read0命令
write_cmd_reg(nand, NAND_CMD_READSTART); //见《linux_nand_driver.pdf》第18页,内容如下:“第一次是0x00h,第二次是0x30h,而两次命令中间,需要发送对应的你所要
//读取的页的地址...”,外面调用该nuc970_nand_command_lp(...)函数,待该函数返回会有相应的接口读取数据
break;
default:
if (!chip->dev_ready) {
if ( chip->chip_delay )
udelay(chip->chip_delay);
LEAVE();
nuc970_release_sem(mtd);
return;
}
}
while (!nuc970_check_rb(nand)) ;
nuc970_release_sem(mtd);
LEAVE();
}
nandflash硬件ECC写操作:
static void nuc970_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint32_t hweccbytes=chip->ecc.layout->eccbytes;
register char * ptr=REG_SMRA0;
ENTER();
if ( nuc970_wait_sem(mtd) < 0 )
{
LEAVE();
return ;
}
memset ( (void*)ptr, 0xFF, mtd->oobsize );
memcpy ( (void*)ptr, (void*)chip->oob_poi, mtd->oobsize - chip->ecc.total );
_nuc970_nand_dma_transfer( mtd, buf, mtd->writesize , 0x1);
// Copy parity code in SMRA to calc
memcpy ( (void*)ecc_calc, (void*)( REG_SMRA0 + ( mtd->oobsize - chip->ecc.total ) ), chip->ecc.total );
// Copy parity code in calc to oob_poi
memcpy ( (void*)(chip->oob_poi+hweccbytes), (void*)ecc_calc, chip->ecc.total);
nuc970_release_sem(mtd);
LEAVE();
}
硬件ecc,先读取oob,确定ecc是否出错,纠错正常才读取数据:
static int nuc970_nand_read_page_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
int eccsize = chip->ecc.size;
uint8_t *p = buf;
char * ptr=REG_SMRA0;
ENTER();
if ( nuc970_wait_sem(mtd) < 0 )
{
LEAVE();
return -1;
}
//printk("smcsr 0x%x\n", readl(REG_SMCSR));
//nuc970_choice_bch_algo ( mtd, page );
/* At first, read the OOB area */
nuc970_nand_command_lp(mtd, NAND_CMD_READOOB, 0, page);
nuc970_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
// Second, copy OOB data to SMRA for page read
memcpy ( (void*)ptr, (void*)chip->oob_poi, mtd->oobsize );
// Third, read data from nand
nuc970_nand_command_lp(mtd, NAND_CMD_READ0, 0, page);
_nuc970_nand_dma_transfer(mtd, p, eccsize, 0x0);
// Fouth, restore OOB data from SMRA
memcpy ( (void*)chip->oob_poi, (void*)ptr, mtd->oobsize );
nuc970_release_sem(mtd);
LEAVE();
return 0;
}
oob(out-of-band)参数配置:
static void nuc970_layout_oob_table ( struct nand_ecclayout* pNandOOBTbl, int oobsize , int eccbytes )
{
//ecc使用字节数
pNandOOBTbl->eccbytes = eccbytes;
//oob中多余的字节数 = oob总空间 - DEF_RESERVER_OOB_SIZE_FOR_MARKER - ecc占用的字节数
pNandOOBTbl->oobavail = oobsize - DEF_RESERVER_OOB_SIZE_FOR_MARKER - eccbytes ;
//下面我的理解是,坏块标记,至于如何使用,待分析????
pNandOOBTbl->oobfree[0].offset = DEF_RESERVER_OOB_SIZE_FOR_MARKER; // Bad block marker size
pNandOOBTbl->oobfree[0].length = oobsize - eccbytes - pNandOOBTbl->oobfree[0].offset ;
}
硬件ecc读取oob,这里多说一句,oob读取流程[命令 + 地址(列地址+行地址) + 等待nandflash忙 + 开始数据读取]
a. 发送读取命令NAND_CMD_READOOB,读取oob,实际上在ONFI协议中并没有NAND_CMD_READOOB命令,在函数nuc970_nand_command_lp(...)内部进行了转换为读命令NAND_CMD_READ0,oob的偏移位置为“每一页的尾部”,所以该函数的形参中column=0;
b. 读取数据, nuc970_nand_read_buf(...):
static int nuc970_nand_read_oob_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
char * ptr=REG_SMRA0;
ENTER();
if ( nuc970_wait_sem(mtd) < 0 )
{
LEAVE();
return -1;
}
nuc970_nand_command_lp(mtd, NAND_CMD_READOOB, 0, page);
// nuc970_choice_bch_algo ( mtd, page );
nuc970_nand_read_buf(mtd, &chip->oob_poi[0], mtd->oobsize);
// Second, copy OOB data to SMRA for page read
memcpy ( (void*)ptr, (void*)chip->oob_poi, mtd->oobsize );
// Third, read data from nand
nuc970_nand_command_lp(mtd, NAND_CMD_READ0, 0, page);
_nuc970_nand_dma_transfer(mtd, NULL, mtd->writesize, 0x0);
// Fouth, recovery OOB data for SMRA
memcpy ( (void*)chip->oob_poi, (void*)ptr, mtd->oobsize );
nuc970_release_sem(mtd);
return 0;
}
按顺序写数据到nandflash:
static int nuc970_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, int data_len,
const uint8_t *buf, int oob_required, int page, int cached, int raw)
{
int status;
if ( nuc970_wait_sem(mtd) < 0 )
{
LEAVE();
return -1;
}
nuc970_nand_command_lp(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
chip->ecc.write_page_raw(mtd, chip, buf, 0);
else
{
if ( page >= 0 && page < (1<<(chip->phys_erase_shift+2)) / mtd->writesize ) // four blocks
{
// Special pattern
char * ptr=REG_SMRA0;
memset ( (void*)ptr, 0xFF, mtd->oobsize );
ptr[3] = 0x00;
ptr[2] = 0x00;
ptr[1] = 0xFF;
_nuc970_nand_dma_transfer( mtd, buf, mtd->writesize , 0x1 );
}
else
{
nuc970_nand_write_page_hwecc ( mtd, chip, buf, oob_required );
}
}
/*
* Cached progamming disabled for now, Not sure if its worth the
* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
*/
cached = 0;
if (!cached || !(chip->options & NAND_CACHEPRG)) {
nuc970_nand_command_lp(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
/*
* See if operation failed and additional status checks are
* available
*/
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status, page);
if (status & NAND_STATUS_FAIL)
{
nuc970_release_sem(mtd);
return -EIO;
}
}
else {
nuc970_nand_command_lp(mtd, NAND_CMD_CACHEDPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
}
#if 0 // for verify
/* Send command to read back the data */
nuc970_nand_command_lp(mtd, NAND_CMD_READ0, 0, page);
if (nuc970_verify_buf(mtd, buf, mtd->writesize))
{
nuc970_release_sem(mtd);
return -EIO;
}
#endif
nuc970_release_sem(mtd);
return 0;
}
对底层硬件操作的函数调用分析完了,接下来正真开始驱动注册:
static int nuc970_nand_probe(struct platform_device *pdev)
{
struct nand_chip *chip;
struct nuc970_nand_info *nuc970_nand;
struct mtd_info *mtd;
struct pinctrl *p;
int retval=0;
E_PAGESIZE ePageSize;
ENTER() ;
nuc970_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc970_nand_info), GFP_KERNEL);
if (!nuc970_nand)
return -ENOMEM;
//分配虚拟地址
nuc970_nand->pnand_vaddr = (unsigned char *) dma_alloc_writecombine(NULL, 512*16, (dma_addr_t *)&nuc970_nand->pnand_phyaddr, GFP_KERNEL);
if(nuc970_nand->pnand_vaddr == NULL){
printk(KERN_ERR "nuc970_nand: failed to allocate ram for nand data.\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, nuc970_nand); //将nuc970_nand绑定为pdev的私有数据!
spin_lock_init(&nuc970_nand->controller.lock);
init_waitqueue_head(&nuc970_nand->controller.wq);
nuc970_nand->pdev = pdev;
//这里有个疑问是mtd包含chip???我猜mtd应该是对应上层(如nandflash_erase命令就是和MTD相关),chip是对底层硬件操作
mtd = &nuc970_nand->mtd; //分区操作结构体
chip = &(nuc970_nand->chip); //芯片操作结构体
nuc970_nand->mtd.priv = chip; //将chip绑定到mtd的私有成员
nuc970_nand->mtd.owner = THIS_MODULE;
spin_lock_init(&nuc970_nand->lock);
/*
* Get Clock
*/
nuc970_nand->fmi_clk = clk_get(NULL, "fmi_hclk");
if (IS_ERR(nuc970_nand->fmi_clk)) {
printk("no fmi_clk?\n");
retval = -ENXIO;
goto fail1;
}
nuc970_nand->clk = clk_get(NULL, "nand_hclk");
if (IS_ERR(nuc970_nand->clk)) {
printk("no nand_clk?\n");
goto fail2;
}
clk_prepare(nuc970_nand->fmi_clk);
clk_enable(nuc970_nand->fmi_clk);
clk_prepare(nuc970_nand->clk);
clk_enable(nuc970_nand->clk);
nuc970_nand->chip.controller = &nuc970_nand->controller;
//绑定chip的接口操作函数,即chip对应底层硬件的操作方式
chip->cmdfunc = nuc970_nand_command_lp; //nandflash命令操作
chip->read_byte = nuc970_nand_read_byte; //读字节
chip->select_chip = nuc970_nand_select_chip; //chip片选
chip->read_buf = nuc970_read_buf_dma; //从DMA中读取nandflash数据
chip->write_buf = nuc970_write_buf_dma; //通过DMA写数据到nandflash
// Check NAND device NBUSY0 pin
chip->dev_ready = nuc970_nand_devready; //读nandflash控制器是否busy
/* set up nand options */
chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
//chip->options |= NAND_SKIP_BBTSCAN;
nuc970_nand->reg = 0x00;
// Read OOB data first, then HW read page
chip->write_page = nuc970_nand_write_page; //页写操作
chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; //???
chip->ecc.hwctl = nuc970_nand_enable_hwecc; //使能硬件ECC
chip->ecc.calculate = nuc970_nand_calculate_ecc; //计算ECC
chip->ecc.correct = nuc970_nand_correct_data; //纠正数据
chip->ecc.write_page= nuc970_nand_write_page_hwecc; //硬件ECC页写操作
chip->ecc.read_page = nuc970_nand_read_page_hwecc_oob_first; //读页数据前先读取oob(out of bound)
chip->ecc.read_oob = nuc970_nand_read_oob_hwecc; //读取数据
chip->ecc.layout = &nuc970_nand_oob;
//配置gpio管脚为nandflash功能
#if defined (CONFIG_NUC970_NAND_PC)
p = devm_pinctrl_get_select(&pdev->dev, "nand-PC");
#elif defined (CONFIG_NUC970_NAND_PI)
p = devm_pinctrl_get_select(&pdev->dev, "nand-PI");
#endif
if (IS_ERR(p))
{
dev_err(&pdev->dev, "unable to reserve pin\n");
retval = PTR_ERR(p);
}
nuc970_nand_initialize( );
nuc970_nand->m_i32MyShowTime = 0;
/* first scan to find the device and get the page size */
if (nand_scan_ident(&(nuc970_nand->mtd), 1, NULL)) {
retval = -ENXIO;
goto fail3;
}
//Set PSize bits of SMCSR register to select NAND card page size
switch (mtd->writesize) { //每一页的大小,我使用的是2K,而计算ecc是每512字节计算一个oob,所以这里有四个
case 2048:
writel( (readl(REG_SMCSR)&(~0x30000)) + 0x10000, REG_SMCSR);
ePageSize = ePageSize_2048;
break;
case 4096:
writel( (readl(REG_SMCSR)&(~0x30000)) + 0x20000, REG_SMCSR);
ePageSize = ePageSize_4096;
break;
case 8192:
writel( (readl(REG_SMCSR)&(~0x30000)) + 0x30000, REG_SMCSR);
ePageSize = ePageSize_8192;
break;
/* Not support now. */
case 512:
//writel( (readl(REG_SMCSR)&(~0x30000)) + 0, REG_SMCSR);
//ePageSize = ePageSize_512;
//break;
default:
printk("NUC970 NAND CONTROLLER IS NOT SUPPORT THE PAGE SIZE. (%d, %d)\n", mtd->writesize, mtd->oobsize );
goto fail3;
}
nuc970_nand->m_ePageSize = ePageSize;
{
// System area
int i32eBCHAlgo = 0;
switch(ePageSize) {
case 0:
case 1: i32eBCHAlgo = eBCH_T4; break;
case 2: i32eBCHAlgo = eBCH_T8; break;
case 3: i32eBCHAlgo = eBCH_T12; break;
default:
break;
}
nuc970_nand_SYSTEM_oob.m_BCHAlgoidx = i32eBCHAlgo;
nuc970_nand_SYSTEM_oob.m_SMRASize = g_SYSAREA_NAND_EXTRA_SIZE[ePageSize];
nuc970_layout_oob_table ( (struct nand_ecclayout*)&nuc970_nand_SYSTEM_oob, nuc970_nand_SYSTEM_oob.m_SMRASize, g_i32ParityNum[ePageSize][i32eBCHAlgo] );
printk("SYSTEM: USE %s HWECC algorithm(SMRA size: %d, Parity number:%d bytes)\n",
g_pcBCHAlgoIdx[i32eBCHAlgo],nuc970_nand_SYSTEM_oob.m_SMRASize,g_i32ParityNum[ePageSize][i32eBCHAlgo] );
//chp i32eBCHAlgo = nuc970_hwecc_choice_bch_algorithm ( ePageSize, mtd->oobsize, 0 );
i32eBCHAlgo = nuc970_nand_SYSTEM_oob.m_BCHAlgoidx;
nuc970_nand_EXECUTE_oob.m_BCHAlgoidx = i32eBCHAlgo;
if ( ePageSize==3 && mtd->oobsize >= g_SYSAREA_NAND_EXTRA_SIZE[ePageSize] ) // 8K, oob >= 376Byte
{
nuc970_nand_EXECUTE_oob.m_SMRASize = g_SYSAREA_NAND_EXTRA_SIZE[ePageSize];
mtd->oobsize = nuc970_nand_EXECUTE_oob.m_SMRASize;
}
else if ( ePageSize==1 && mtd->oobsize >= g_SYSAREA_NAND_EXTRA_SIZE[ePageSize] ) // 2K, oob >= 64Byte
{
nuc970_nand_EXECUTE_oob.m_SMRASize = g_SYSAREA_NAND_EXTRA_SIZE[ePageSize];
mtd->oobsize = nuc970_nand_EXECUTE_oob.m_SMRASize;
}
else
nuc970_nand_EXECUTE_oob.m_SMRASize = mtd->oobsize;
if ( i32eBCHAlgo >= 0 ) {
nuc970_layout_oob_table ( (struct nand_ecclayout*)&nuc970_nand_EXECUTE_oob, nuc970_nand_EXECUTE_oob.m_SMRASize, g_i32ParityNum[ePageSize][i32eBCHAlgo] );
printk("EXECUTE: USE %s HWECC algorithm(SMRA size: %d, Parity number:%d bytes)\n",
g_pcBCHAlgoIdx[i32eBCHAlgo], nuc970_nand_EXECUTE_oob.m_SMRASize, g_i32ParityNum[ePageSize][i32eBCHAlgo] );
}
}
#ifndef CONFIG_MTD_CMDLINE_PARTS
nuc970_nand->parts = (struct mtd_partition*)partitions;
nuc970_nand->nr_parts = ARRAY_SIZE(partitions);
#endif
nuc970_nand->m_i32SMRASize = mtd->oobsize;
//chp nuc970_nand->eBCHAlgo = nuc970_hwecc_choice_bch_algorithm ( ePageSize, mtd->oobsize, 0 );
nuc970_nand->eBCHAlgo = nuc970_nand_SYSTEM_oob.m_BCHAlgoidx;
if ( nuc970_nand->eBCHAlgo < 0 ) {
nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, 0 );
chip->ecc.bytes = 0;
chip->ecc.size = mtd->writesize;
printk("Choice BCH algorithm: No match.\n");
} else {
nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[ePageSize][nuc970_nand->eBCHAlgo] );
chip->ecc.bytes = nuc970_nand_oob.eccbytes;
chip->ecc.size = mtd->writesize;
printk("USE %s HWECC algorithm(Parity number:%d bytes)\n", g_pcBCHAlgoIdx[nuc970_nand->eBCHAlgo], g_i32ParityNum[ePageSize][nuc970_nand->eBCHAlgo] );
}
/* set BCH Tn */
switch (nuc970_nand->eBCHAlgo)
{
case eBCH_T4:
chip->ecc.strength = 4;
break;
case eBCH_T8:
chip->ecc.strength = 8;
break;
case eBCH_T12:
chip->ecc.strength = 12;
break;
case eBCH_T15:
chip->ecc.strength = 15;
break;
case eBCH_T24:
chip->ecc.strength = 24;
break;
default:
;
}
/* add mtd-id. The string should same as uboot definition */
mtd->name = "nand0";
/* second phase scan */
if ( nand_scan_tail( &(nuc970_nand->mtd) ) ) {
retval = -ENXIO;
goto fail3;
}
if ( nuc970_nand->eBCHAlgo >= 0 )
nuc970_nand_hwecc_init (&(nuc970_nand->mtd));
else
nuc970_nand_hwecc_fini (&(nuc970_nand->mtd));
dump_chip_info( chip );
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
mtd_device_parse_register(&(nuc970_nand->mtd), NULL, NULL, nuc970_nand->parts, nuc970_nand->nr_parts);
LEAVE();
dump_regs(__LINE__);
printk("fmi-sm: registered successfully! mtdid=%s\n", mtd->name);
return retval;
fail3:
fail2:
fail1: kfree(nuc970_nand);
return retval;
}
probe(...)探测函数内部已基本有注释,现在主要分析几个重要的的函数,如下,
nandflash扫描识别,包括厂商ID,芯片ID,是否符合ONFI标准....
if (nand_scan_ident(&(nuc970_nand->mtd), 1, NULL)) {
retval = -ENXIO;
goto fail3;
}
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
struct nand_flash_dev *table)
{
int i, busw, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
nand_set_defaults(chip, busw); //设置默认函数接口,包括坏块扫描函数
/* Read the flash type */
//读取flash的类型,包括厂商ID,设备ID
type = nand_get_flash_type(mtd, chip, busw,
&nand_maf_id, &nand_dev_id, table);
if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
pr_warn("No NAND device found\n");
chip->select_chip(mtd, -1);
return PTR_ERR(type);
}
chip->select_chip(mtd, -1);
/* Check for a chip array */
for (i = 1; i < maxchips; i++) {
//片选使能
chip->select_chip(mtd, i);
//复位nandflash
/* See comment in nand_get_flash_type for reset */
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
//发送读ID命令
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
//读取厂商ID和设备ID
/* Read manufacturer and device IDs */
if (nand_maf_id != chip->read_byte(mtd) ||
nand_dev_id != chip->read_byte(mtd)) {
chip->select_chip(mtd, -1); //片选禁止
break;
}
chip->select_chip(mtd, -1);
}
if (i > 1)
pr_info("%d NAND chips detected\n", i);
//保存当前片选的nandflash
/* Store the number of chips and calc total size for mtd */
chip->numchips = i;
mtd->size = i * chip->chipsize;
return 0;
}
nandflash默认接口设置,函数形参 @busw决定了外围总线宽度是8位还是16位,我这里是8位,默认设置的函数我们在probe()探测函数内部绑定,这里主要分析坏块扫描接口绑定:
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
/* check for proper chip_delay setup, set 20us if not */
if (!chip->chip_delay)
chip->chip_delay = 20;
/* check, if a user supplied command function given */
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command;
/* check, if a user supplied wait function given */
if (chip->waitfunc == NULL)
chip->waitfunc = nand_wait;
if (!chip->select_chip)
chip->select_chip = nand_select_chip;
/* If called twice, pointers that depend on busw may need to be reset */
if (!chip->read_byte || chip->read_byte == nand_read_byte)
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!chip->read_word)
chip->read_word = nand_read_word;
if (!chip->block_bad)
chip->block_bad = nand_block_bad;
if (!chip->block_markbad)
chip->block_markbad = nand_default_block_markbad;
if (!chip->write_buf || chip->write_buf == nand_write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!chip->read_buf || chip->read_buf == nand_read_buf)
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!chip->scan_bbt)
chip->scan_bbt = nand_default_bbt; //绑定坏块扫描函数
if (!chip->controller) {
chip->controller = &chip->hwcontrol;
spin_lock_init(&chip->controller->lock);
init_waitqueue_head(&chip->controller->wq);
}
}
先看下坏块表的结构体:
struct nand_bbt_descr {
int options; //选项,具体见赋值
int pages[NAND_MAX_CHIPS]; //nandflash芯片个数,我们这里为1块
int offs; //坏块标记存放在oob区域,offs是相对oob的偏移位置
int veroffs;
uint8_t version[NAND_MAX_CHIPS];
int len; //坏块标记占用的字节数
int maxblocks;
int reserved_block_code;
uint8_t *pattern; //如果坏块标记的是0xff,则说明这个block是好的
};
nandflash默认坏块表:
int nand_default_bbt(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
/* Is a flash based bad block table requested? */
if (this->bbt_options & NAND_BBT_USE_FLASH) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
if (this->bbt_options & NAND_BBT_NO_OOB) {
this->bbt_td = &bbt_main_no_oob_descr;
this->bbt_md = &bbt_mirror_no_oob_descr;
} else {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
}
} else {
this->bbt_td = NULL;
this->bbt_md = NULL;
}
if (!this->badblock_pattern)
nand_create_badblock_pattern(this); //建立一个bad block坏块表模式
return nand_scan_bbt(mtd, this->badblock_pattern); //扫描坏块表
}
建立一个坏块表模版:
static int nand_create_badblock_pattern(struct nand_chip *this)
{
struct nand_bbt_descr *bd;
if (this->badblock_pattern) {
pr_warn("Bad block pattern already allocated; not replacing\n");
return -EINVAL;
}
bd = kzalloc(sizeof(*bd), GFP_KERNEL);
if (!bd)
return -ENOMEM;
bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; //坏块扫描
bd->offs = this->badblockpos; //坏块标记存放在每个page的oob里面
bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; //根据总线的宽度决定坏块标记在oob的字节数
bd->pattern = scan_ff_pattern; //如果坏块标记的是0xff,则说明这个block是好的
bd->options |= NAND_BBT_DYNAMICSTRUCT; //表明bbt是动态结构体
this->badblock_pattern = bd;
return 0;
}
扫描坏块,以下函数说明已经很详细了,就是如果flash已经存在bbt了,就读取到内存中,如果没有,就扫描全部flash芯片建立bbt;那最初的bbt从何而来?2种来源,一种是bootloader启动的时候,创建了bbt并保存到flash中;另外就是kernel第一次启动的时候如果没有找到合法的bbt就扫描flash并建立bbt
int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
int len, res = 0;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
len = mtd->size >> (this->bbt_erase_shift + 2);
/*
* Allocate memory (2bit per block) and clear the memory bad block
* table.
*/
this->bbt = kzalloc(len, GFP_KERNEL);
if (!this->bbt)
return -ENOMEM;
/*
* If no primary table decriptor is given, scan the device to build a
* memory based bad block table.
*/
if (!td) {
if ((res = nand_memory_bbt(mtd, bd))) {
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
kfree(this->bbt);
this->bbt = NULL;
}
return res;
}
verify_bbt_descr(mtd, td);
verify_bbt_descr(mtd, md);
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
len += (len >> this->page_shift) * mtd->oobsize;
buf = vmalloc(len);
if (!buf) {
kfree(this->bbt);
this->bbt = NULL;
return -ENOMEM;
}
/* Is the bbt at a given page? */
if (td->options & NAND_BBT_ABSPAGE) { //
read_abs_bbts(mtd, buf, td, md);
} else {
/* Search the bad block table using a pattern in oob */
search_read_bbts(mtd, buf, td, md);
}
res = check_create(mtd, buf, bd);
/* Prevent the bbt regions from erasing / writing */
mark_bbt_region(mtd, td);
if (md)
mark_bbt_region(mtd, md);
vfree(buf);
return res;
}
搜索坏块表:
static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td,
struct nand_bbt_descr *md)
{
/* Search the primary table */
search_bbt(mtd, buf, td); //扫描主要的表
/* Search the mirror table */
if (md)
search_bbt(mtd, buf, md); //扫描镜像表
}
static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
struct nand_chip *this = mtd->priv;
int i, chips;
int bits, startblock, block, dir;
int scanlen = mtd->writesize + mtd->oobsize; //数据区+oob区
int bbtblocks;
int blocktopage = this->bbt_erase_shift - this->page_shift;
/* Search direction top -> down? */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = (mtd->size >> this->bbt_erase_shift) - 1;
dir = -1;
} else {
startblock = 0;
dir = 1;
}
/* Do we have a bbt per chip? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
bbtblocks = this->chipsize >> this->bbt_erase_shift;
startblock &= bbtblocks - 1;
} else {
chips = 1;
bbtblocks = mtd->size >> this->bbt_erase_shift;
}
/* Number of bits for each erase block in the bbt */
bits = td->options & NAND_BBT_NRBITS_MSK;
for (i = 0; i < chips; i++) {
/* Reset version information */
td->version[i] = 0;
td->pages[i] = -1;
/* Scan the maximum number of blocks */
for (block = 0; block < td->maxblocks; block++) { //遍历chip的全部blocks
int actblock = startblock + dir * block; //计算哪个block
loff_t offs = (loff_t)actblock << this->bbt_erase_shift; //计算block在nandflash中的的偏移
/* Read first page */
scan_read(mtd, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) { //校验坏块
td->pages[i] = actblock << blocktopage; //计算实际有坏块的页
if (td->options & NAND_BBT_VERSION) {
offs = bbt_get_ver_offs(mtd, td);
td->version[i] = buf[offs];
}
break;
}
}
startblock += this->chipsize >> this->bbt_erase_shift;
}
/* Check, if we found a bbt for each requested chip */
for (i = 0; i < chips; i++) {
if (td->pages[i] == -1)
pr_warn("Bad block table not found for chip %d\n", i);
else
pr_info("Bad block table found at page %d, version "
"0x%02X\n", td->pages[i], td->version[i]);
}
return 0;
}
如果出现坏块,内核在启动时会打印如下的信息:
Bad block table found at page 65472, version 0x01
Bad block table found at page 65408, version 0x01
关于nandflash坏块表,后续专门写一篇文章,待续......
再重新回到nand_scan_ident(...)函数中的nand_get_flash_type(...)获取厂商ID、设备ID:
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int busw,
int *maf_id, int *dev_id,
struct nand_flash_dev *type)
{
int i, maf_idx;
u8 id_data[8];
/* Select the device */
chip->select_chip(mtd, 0);
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up.
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
/*
* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
*/
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
pr_info("%s: second ID read did not match "
"%02x,%02x against %02x,%02x\n", __func__,
*maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
}
if (!type)
type = nand_flash_ids;
for (; type->name != NULL; type++) {
if (is_full_id_nand(type)) {
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
goto ident_done;
} else if (*dev_id == type->dev_id) {
break;
}
}
chip->onfi_version = 0;
if (!type->name || !type->pagesize) {
//探测是否是ONFI标准,如果是就读取chip的大小、页擦除大小、页尺寸....
/* Check is chip is ONFI compliant */
if (nand_flash_detect_onfi(mtd, chip, &busw))
goto ident_done;
}
if (!type->name)
return ERR_PTR(-ENODEV);
if (!mtd->name)
mtd->name = type->name;
chip->chipsize = (uint64_t)type->chipsize << 20;
if (!type->pagesize && chip->init_size) {
/* Set the pagesize, oobsize, erasesize by the driver */
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
/* Decode parameters from extended ID */
nand_decode_ext_id(mtd, chip, id_data, &busw);
} else {
nand_decode_id(mtd, chip, type, id_data, &busw);
}
/* Get chip options */
chip->options |= type->options;
/*
* Check if chip is not a Samsung device. Do not clear the
* options for chips which do not have an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:
/* Try to identify manufacturer */
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
if (nand_manuf_ids[maf_idx].id == *maf_id)
break;
}
if (chip->options & NAND_BUSWIDTH_AUTO) {
WARN_ON(chip->options & NAND_BUSWIDTH_16);
chip->options |= busw;
nand_set_defaults(chip, busw);
} else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
/*
* Check, if buswidth is correct. Hardware drivers should set
* chip correct!
*/
pr_info("NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
*dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
pr_warn("NAND bus width %d instead %d bit\n",
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8);
return ERR_PTR(-EINVAL);
}
nand_decode_bbm_options(mtd, chip, id_data);
/* Calculate the address shift from the page size */
chip->page_shift = ffs(mtd->writesize) - 1;
/* Convert chipsize to number of pages per chip -1 */
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
chip->bbt_erase_shift = chip->phys_erase_shift =
ffs(mtd->erasesize) - 1;
if (chip->chipsize & 0xffffffff)
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
else {
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
chip->chip_shift += 32 - 1;
}
chip->badblockbits = 8;
chip->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function! */
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;
pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s),"
" %dMiB, page size: %d, OOB size: %d\n",
*maf_id, *dev_id, nand_manuf_ids[maf_idx].name,
chip->onfi_version ? chip->onfi_params.model : type->name,
(int)(chip->chipsize >> 20), mtd->writesize, mtd->oobsize);
return type;
}
输出的调试信息为:
NAND device: Manufacturer ID: 0x01, Chip ID: 0xf1 (AMD/Spansion S34ML01G2), 128MiB, page size: 2048, OOB size: 64S
nuc970_probe(...)-->nand_scan_tail(...),该函数主要完成一些未初始化的函数,并扫描整个chip的坏块:
int nand_scan_tail(struct mtd_info *mtd)
{
int i;
struct nand_chip *chip = mtd->priv;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH));
if (!(chip->options & NAND_OWN_BUFFERS))
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
if (!chip->buffers)
return -ENOMEM;
/* Set the internal oob buffer location, just after the page data */
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/*
* If no default placement scheme is given, select an appropriate one.
*/
if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
switch (mtd->oobsize) { //oob中的布局
case 8:
chip->ecc.layout = &nand_oob_8;
break;
case 16:
chip->ecc.layout = &nand_oob_16;
break;
case 64:
chip->ecc.layout = &nand_oob_64;
break;
case 128:
chip->ecc.layout = &nand_oob_128;
break;
default:
pr_warn("No oob scheme defined for oobsize %d\n",
mtd->oobsize);
BUG();
}
}
if (!chip->write_page)
chip->write_page = nand_write_page;
/* set for ONFI nand */
if (!chip->onfi_set_features)
chip->onfi_set_features = nand_onfi_set_features;
if (!chip->onfi_get_features)
chip->onfi_get_features = nand_onfi_get_features;
/*
* Check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
switch (chip->ecc.mode) {
case NAND_ECC_HW_OOB_FIRST:
/* Similar to NAND_ECC_HW, but a separate read_page handle */
if (!chip->ecc.calculate || !chip->ecc.correct ||
!chip->ecc.hwctl) {
pr_warn("No ECC functions supplied; "
"hardware ECC not possible\n");
BUG();
}
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
case NAND_ECC_HW:
/* Use standard hwecc read page function? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_hwecc;
if (!chip->ecc.write_page)
chip->ecc.write_page = nand_write_page_hwecc;
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw;
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw;
if (!chip->ecc.read_oob)
chip->ecc.read_oob = nand_read_oob_std;
if (!chip->ecc.write_oob)
chip->ecc.write_oob = nand_write_oob_std;
if (!chip->ecc.read_subpage)
chip->ecc.read_subpage = nand_read_subpage;
if (!chip->ecc.write_subpage)
chip->ecc.write_subpage = nand_write_subpage_hwecc;
case NAND_ECC_HW_SYNDROME:
if ((!chip->ecc.calculate || !chip->ecc.correct ||
!chip->ecc.hwctl) &&
(!chip->ecc.read_page ||
chip->ecc.read_page == nand_read_page_hwecc ||
!chip->ecc.write_page ||
chip->ecc.write_page == nand_write_page_hwecc)) {
pr_warn("No ECC functions supplied; "
"hardware ECC not possible\n");
BUG();
}
/* Use standard syndrome read/write page function? */
if (!chip->ecc.read_page)
chip->ecc.read_page = nand_read_page_syndrome;
if (!chip->ecc.write_page)
chip->ecc.write_page = nand_write_page_syndrome;
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
if (!chip->ecc.read_oob)
chip->ecc.read_oob = nand_read_oob_syndrome;
if (!chip->ecc.write_oob)
chip->ecc.write_oob = nand_write_oob_syndrome;
if (mtd->writesize >= chip->ecc.size) {
if (!chip->ecc.strength) {
pr_warn("Driver must set ecc.strength when using hardware ECC\n");
BUG();
}
break;
}
pr_warn("%d byte HW ECC not possible on "
"%d byte page size, fallback to SW ECC\n",
chip->ecc.size, mtd->writesize);
chip->ecc.mode = NAND_ECC_SOFT;
case NAND_ECC_SOFT:
chip->ecc.calculate = nand_calculate_ecc;
chip->ecc.correct = nand_correct_data;
chip->ecc.read_page = nand_read_page_swecc;
chip->ecc.read_subpage = nand_read_subpage;
chip->ecc.write_page = nand_write_page_swecc;
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
if (!chip->ecc.size)
chip->ecc.size = 256;
chip->ecc.bytes = 3;
chip->ecc.strength = 1;
break;
case NAND_ECC_SOFT_BCH:
if (!mtd_nand_has_bch()) {
pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
BUG();
}
chip->ecc.calculate = nand_bch_calculate_ecc;
chip->ecc.correct = nand_bch_correct_data;
chip->ecc.read_page = nand_read_page_swecc;
chip->ecc.read_subpage = nand_read_subpage;
chip->ecc.write_page = nand_write_page_swecc;
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
/*
* Board driver should supply ecc.size and ecc.bytes values to
* select how many bits are correctable; see nand_bch_init()
* for details. Otherwise, default to 4 bits for large page
* devices.
*/
if (!chip->ecc.size && (mtd->oobsize >= 64)) {
chip->ecc.size = 512;
chip->ecc.bytes = 7;
}
chip->ecc.priv = nand_bch_init(mtd,
chip->ecc.size,
chip->ecc.bytes,
&chip->ecc.layout);
if (!chip->ecc.priv) {
pr_warn("BCH ECC initialization failed!\n");
BUG();
}
chip->ecc.strength =
chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
break;
case NAND_ECC_NONE:
pr_warn("NAND_ECC_NONE selected by board driver. "
"This is not recommended!\n");
chip->ecc.read_page = nand_read_page_raw;
chip->ecc.write_page = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.write_oob = nand_write_oob_std;
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0;
chip->ecc.strength = 0;
break;
default:
pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode);
BUG();
}
/* For many systems, the standard OOB write also works for raw */
if (!chip->ecc.read_oob_raw)
chip->ecc.read_oob_raw = chip->ecc.read_oob;
if (!chip->ecc.write_oob_raw)
chip->ecc.write_oob_raw = chip->ecc.write_oob;
/*
* The number of bytes available for a client to place data into
* the out of band area.
*/
chip->ecc.layout->oobavail = 0;
for (i = 0; chip->ecc.layout->oobfree[i].length
&& i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
chip->ecc.layout->oobavail +=
chip->ecc.layout->oobfree[i].length;
mtd->oobavail = chip->ecc.layout->oobavail;
/*
* Set the number of read / write steps for one page depending on ECC
* mode.
*/
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
pr_warn("Invalid ECC parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
switch (chip->ecc.steps) {
case 2:
mtd->subpage_sft = 1;
break;
case 4:
case 8:
case 16:
mtd->subpage_sft = 2;
break;
}
}
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
/* Initialize state */
chip->state = FL_READY;
/* Invalidate the pagebuffer reference */
chip->pagebuf = -1;
/* Large page NAND with SOFT_ECC should support subpage reads */
if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
chip->options |= NAND_SUBPAGE_READ;
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
MTD_CAP_NANDFLASH;
mtd->_erase = nand_erase;
mtd->_point = NULL;
mtd->_unpoint = NULL;
mtd->_read = nand_read;
mtd->_write = nand_write;
mtd->_panic_write = panic_nand_write;
mtd->_read_oob = nand_read_oob;
mtd->_write_oob = nand_write_oob;
mtd->_sync = nand_sync;
mtd->_lock = NULL;
mtd->_unlock = NULL;
mtd->_suspend = nand_suspend;
mtd->_resume = nand_resume;
mtd->_block_isbad = nand_block_isbad;
mtd->_block_markbad = nand_block_markbad;
mtd->writebufsize = mtd->writesize;
/* propagate ecc info to mtd_info */
mtd->ecclayout = chip->ecc.layout;
mtd->ecc_strength = chip->ecc.strength;
/*
* Initialize bitflip_threshold to its default prior scan_bbt() call.
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
* properly set.
*/
if (!mtd->bitflip_threshold)
mtd->bitflip_threshold = mtd->ecc_strength;
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
/* Build bad block table */
return chip->scan_bbt(mtd);
}
EXPORT_SYMBOL(nand_scan_tail);
最后调用“chip->scan_bbt(mtd)”扫描坏块、并且建立坏块表,函数内部上面已经分析过,这里不在赘述!
6. 分区表创建
nandflash分区结构体定义:
static struct mtd_partition partitions[] = {
{
.name = "u-boot",
.offset = 0,
.size = 2 * 1024 * 1024,
.ecclayout = (struct nand_ecclayout*)&nuc970_nand_SYSTEM_oob
},
{
.name = "Kernel",
.size = 20 * 1024 * 1024,
.offset = MTDPART_OFS_APPEND,
.ecclayout = (struct nand_ecclayout*)&nuc970_nand_EXECUTE_oob
},
{
.name = "user",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
};
分区结构注册:
mtd_device_parse_register(&(nuc970_nand->mtd), NULL, NULL, nuc970_nand->parts, nuc970_nand->nr_parts);
int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
struct mtd_part_parser_data *parser_data,
const struct mtd_partition *parts,
int nr_parts)
{
int err;
struct mtd_partition *real_parts;
err = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
if (err <= 0 && nr_parts && parts) {
//复制parts分区到内存
real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
GFP_KERNEL);
if (!real_parts)
err = -ENOMEM;
else
err = nr_parts; //分区个数
}
if (err > 0) { //err > 0条件成立
err = add_mtd_partitions(mtd, real_parts, err); //增加MTD分区
kfree(real_parts);
} else if (err == 0) {
err = add_mtd_device(mtd); //增加MTD设备,见下面
if (err == 1)
err = -ENODEV;
}
return err;
}
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
uint64_t cur_offset = 0;
int i;
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = allocate_partition(master, parts + i, i, cur_offset); //分配一个分区,见下面
if (IS_ERR(slave))
return PTR_ERR(slave);
mutex_lock(&mtd_partitions_mutex);
list_add(&slave->list, &mtd_partitions); //将分区添加到mtd_partitions全局链表中
mutex_unlock(&mtd_partitions_mutex);
add_mtd_device(&slave->mtd); //将mtd添加到设备对象中,见下面
cur_offset = slave->offset + slave->mtd.size;
}
return 0;
}
动态分配一个分区allocate_partitions(...),包括函数的对mtd操作的函数初始化:
static struct mtd_part *allocate_partition(struct mtd_info *master,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
{
struct mtd_part *slave;
char *name;
/* allocate the partition structure */
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
name = kstrdup(part->name, GFP_KERNEL); //分区名称
if (!name || !slave) {
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
master->name);
kfree(name);
kfree(slave);
return ERR_PTR(-ENOMEM);
}
/* set up the MTD object for this partition */
slave->mtd.type = master->type; //flash类型,如MTD_NORFLASH
slave->mtd.flags = master->flags & ~part->mask_flags;
slave->mtd.size = part->size; //分区大小
slave->mtd.writesize = master->writesize; //分区最小写单元,NOR-FLASH为1个字节,NAND-FLASH为一页
slave->mtd.writebufsize = master->writebufsize;
slave->mtd.oobsize = master->oobsize; //OOB大小,包括ECC检验
slave->mtd.oobavail = master->oobavail;
slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = name; //分区名称,如内核“kernel”,文件系统“rootfs”
slave->mtd.owner = master->owner;
slave->mtd.backing_dev_info = master->backing_dev_info;
/* NOTE: we don't arrange MTDs as a tree; it'd be error-prone
* to have the same data be in two different partitions.
*/
slave->mtd.dev.parent = master->dev.parent;
slave->mtd._read = part_read; //分区读
slave->mtd._write = part_write; //分区写
if (master->_panic_write)
slave->mtd._panic_write = part_panic_write; //内核恐慌(崩溃)时写操作
if (master->_point && master->_unpoint) {
slave->mtd._point = part_point;
slave->mtd._unpoint = part_unpoint;
}
if (master->_get_unmapped_area)
slave->mtd._get_unmapped_area = part_get_unmapped_area;
if (master->_read_oob)
slave->mtd._read_oob = part_read_oob; //读oob ecc校验
if (master->_write_oob)
slave->mtd._write_oob = part_write_oob;
if (master->_read_user_prot_reg)
slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
if (master->_read_fact_prot_reg)
slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
if (master->_write_user_prot_reg)
slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
if (master->_lock_user_prot_reg)
slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
if (master->_get_user_prot_info)
slave->mtd._get_user_prot_info = part_get_user_prot_info;
if (master->_get_fact_prot_info)
slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
if (master->_sync)
slave->mtd._sync = part_sync;
if (!partno && !master->dev.class && master->_suspend &&
master->_resume) {
slave->mtd._suspend = part_suspend;
slave->mtd._resume = part_resume;
}
if (master->_writev)
slave->mtd._writev = part_writev;
if (master->_lock)
slave->mtd._lock = part_lock;
if (master->_unlock)
slave->mtd._unlock = part_unlock;
if (master->_is_locked)
slave->mtd._is_locked = part_is_locked;
if (master->_block_isbad)
slave->mtd._block_isbad = part_block_isbad;
if (master->_block_markbad)
slave->mtd._block_markbad = part_block_markbad;
slave->mtd._erase = part_erase; //分区擦除
slave->master = master;
slave->offset = part->offset;
if (slave->offset == MTDPART_OFS_APPEND)
slave->offset = cur_offset;
if (slave->offset == MTDPART_OFS_NXTBLK) {
slave->offset = cur_offset;
if (mtd_mod_by_eb(cur_offset, master) != 0) {
/* Round up to next erasesize */
slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
printk(KERN_NOTICE "Moving partition %d: "
"0x%012llx -> 0x%012llx\n", partno,
(unsigned long long)cur_offset, (unsigned long long)slave->offset);
}
}
if (slave->offset == MTDPART_OFS_RETAIN) {
slave->offset = cur_offset;
if (master->size - slave->offset >= slave->mtd.size) {
slave->mtd.size = master->size - slave->offset
- slave->mtd.size;
} else {
printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
part->name, master->size - slave->offset,
slave->mtd.size);
/* register to preserve ordering */
goto out_register;
}
}
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = master->size - slave->offset;
//注意这里内核会打印分区的地址范围
printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
/* let's do some sanity checks */
if (slave->offset >= master->size) {
/* let's register it anyway to preserve ordering */
slave->offset = 0;
slave->mtd.size = 0;
printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
part->name);
goto out_register;
}
if (slave->offset + slave->mtd.size > master->size) {
slave->mtd.size = master->size - slave->offset;
printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
part->name, master->name, (unsigned long long)slave->mtd.size);
}
if (master->numeraseregions > 1) {
/* Deal with variable erase size stuff */
int i, max = master->numeraseregions;
u64 end = slave->offset + slave->mtd.size;
struct mtd_erase_region_info *regions = master->eraseregions;
/* Find the first erase regions which is part of this
* partition. */
for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
;
/* The loop searched for the region _behind_ the first one */
if (i > 0)
i--;
/* Pick biggest erasesize */
for (; i < max && regions[i].offset < end; i++) {
if (slave->mtd.erasesize < regions[i].erasesize) {
slave->mtd.erasesize = regions[i].erasesize;
}
}
BUG_ON(slave->mtd.erasesize == 0);
} else {
/* Single erase size */
slave->mtd.erasesize = master->erasesize;
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
mtd_mod_by_eb(slave->offset, &slave->mtd)) {
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
part->name);
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
part->name);
}
slave->mtd.ecclayout = master->ecclayout;
slave->mtd.ecc_strength = master->ecc_strength;
slave->mtd.bitflip_threshold = master->bitflip_threshold;
if (master->_block_isbad) {
uint64_t offs = 0;
while (offs < slave->mtd.size) {
if (mtd_block_isbad(master, offs + slave->offset))
slave->mtd.ecc_stats.badblocks++;
offs += slave->mtd.erasesize;
}
}
out_register:
return slave;
}
上面函数执行完后,会输出如下报文:
Creating 3 MTD partitions on "nand0":
0x000000000000-0x000000200000 : "u-boot"
0x000000200000-0x000000700000 : "kernel"
0x000000700000-0x000008000000 : "rootfs"
增加mtd设备:
int add_mtd_device(struct mtd_info *mtd)
{
struct mtd_notifier *not;
int i, error;
if (!mtd->backing_dev_info) {
switch (mtd->type) {
case MTD_RAM:
mtd->backing_dev_info = &mtd_bdi_rw_mappable;
break;
case MTD_ROM:
mtd->backing_dev_info = &mtd_bdi_ro_mappable;
break;
default:
mtd->backing_dev_info = &mtd_bdi_unmappable;
break;
}
}
BUG_ON(mtd->writesize == 0);
mutex_lock(&mtd_table_mutex);
i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
if (i < 0)
goto fail_locked;
mtd->index = i;
mtd->usecount = 0;
/* default value if not set by driver */
if (mtd->bitflip_threshold == 0)
mtd->bitflip_threshold = mtd->ecc_strength;
if (is_power_of_2(mtd->erasesize))
mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
else
mtd->erasesize_shift = 0;
if (is_power_of_2(mtd->writesize))
mtd->writesize_shift = ffs(mtd->writesize) - 1;
else
mtd->writesize_shift = 0;
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
/* Some chips always power up locked. Unlock them now */
if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
error = mtd_unlock(mtd, 0, mtd->size);
if (error && error != -EOPNOTSUPP)
printk(KERN_WARNING
"%s: unlock failed, writes may not work\n",
mtd->name);
}
/* Caller should have set dev.parent to match the
* physical device.
*/
mtd->dev.type = &mtd_devtype;
mtd->dev.class = &mtd_class;
mtd->dev.devt = MTD_DEVT(i);
dev_set_name(&mtd->dev, "mtd%d", i);
dev_set_drvdata(&mtd->dev, mtd);
if (device_register(&mtd->dev) != 0)
goto fail_added;
if (MTD_DEVT(i))
device_create(&mtd_class, mtd->dev.parent,
MTD_DEVT(i) + 1,
NULL, "mtd%dro", i);
pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
list_for_each_entry(not, &mtd_notifiers, list)
not->add(mtd);
mutex_unlock(&mtd_table_mutex);
/* We _know_ we aren't being removed, because
our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it
either. :) */
__module_get(THIS_MODULE);
return 0;
fail_added:
idr_remove(&mtd_idr, i);
fail_locked:
mutex_unlock(&mtd_table_mutex);
return 1;
}
7. 未完成的问题:
1. nandflash bbt表的扫描分析;
2. 文件系统对nandflash访问的层次问题;
可以参考这篇文章:点击打开链接