nandflash特点:
- nand存在位反转的现象,所以需要ecc校验数据,ecc校验可以矫正一位的位反转,发现两位的位反转;
- nand存在坏块的问题,所谓坏块,就是不能擦除的块,nandflash只能从1写为0;而不能从0写为1,也就是write时,只能将某一位拉低;而擦除操作是将0拉高位1。擦除是按块擦的,写的话按页写。写的话按页写是为了提高效率,而擦除只能按块擦除。
- nandflash的接口有并口和spi接口的。
硬件结构
原理图
并口的nandflash,8bit flash,地址和数据线共用
ALE和CLE用于地址和数据选择。
内部结构
页结构
每一页2048 data+64OOB;OOB(out of band);OOB主要有2byte坏快标识 + ECC数据;
坏块标识0xff为好块,其他为坏。坏块标识每一块的第一页的OOB区内。
以上页结构信息,不同的芯片厂商结构也不一样。
nandflash命令集
#define NAND_CMD_READ0 0
#define NAND_CMD_READ1 1
#define NAND_CMD_RNDOUT 5
#define NAND_CMD_PAGEPROG 0x10
#define NAND_CMD_READOOB 0x50
#define NAND_CMD_ERASE1 0x60
#define NAND_CMD_STATUS 0x70
#define NAND_CMD_SEQIN 0x80
#define NAND_CMD_RNDIN 0x85
#define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_PARAM 0xec
#define NAND_CMD_GET_FEATURES 0xee
#define NAND_CMD_SET_FEATURES 0xef
#define NAND_CMD_RESET 0xff
/* Extended commands for large page devices */
#define NAND_CMD_READSTART 0x30
#define NAND_CMD_RNDOUTSTART 0xE0
#define NAND_CMD_CACHEDPROG 0x15
#define NAND_CMD_NONE -1
mtd nandflash驱动
mtd框架分层:
设备层: mtdchar.c/mtdblock.c; 字符设备和块设备接口。
原始设备层 mtdcore.c 提供mtd_info;mtd_partion;mtd_part结构。
硬件驱动层 /mtd/nand/下
nand_base.c nand通用接口;
分rawnand;onenand,spinand;
rawnand: 地址数据总线为并口。
spinand:通过spi总线控制,相当与rawnand封装成了spi接口。多了一个spi解释单元。
onenand:可以理解为nor+nand。
mtd框架能做什么不能做什么?
-
nand flash有坏块,mtd驱动框架能帮我们跳坏块吗?
答:不能,mtd只提供读写page接口,和ioctl接口来测试该块是否为坏块,但是不会在读写的时候帮我们自动跳过坏块,跳坏块操作需要我们应用来自己实现。
-
nandflash会存在位反转现象,mtd驱动框架会帮我们做ecc校验吗? 纠正一位的bitflip,发现2bit的bitflip?
答:这个mtd驱动框架会帮做,但是ecc校验可选软件ecc和硬件ecc,软件ecc算法mtd框架提供,硬件ecc则是硬件结构提供,读数据的时候会校验。
-
nandflash擦除是按块擦除的,写是按页写的,如果我们要写一个块中的某一页,需要将整个块读出来然后,修改然后擦除写入。即read-update-erase-write;这个操作mtd会帮我们做吗?
答:不会。mtd只提供页写入接口,这一系列操作需要用户自己做。至于提高写入速率的操作,也需要用户自己实现。
rawnand驱动字符设备接口
字符设备写流程图:
1. 写数据不跳坏块
2. oob中ecc数据可以是
1> 原始数据中已经带有的
2> 软件算法生成的 3中算法;
3> 硬件实现ecc算法;需要驱动实现ecc…
3. 不提供block写buffer;即read-update-erase-write流程。
读数据流
1.同样不跳坏块
2.ecc校验 nand_chip.ecc.correct
见代码硬件ecc分支nand_read_page_hwecc:
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size, ret;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->ecc.calc_buf;
uint8_t *ecc_code = chip->ecc.code_buf;
unsigned int max_bitflips = 0;
ret = nand_read_page_op(chip, page, 0, NULL, 0);
if (ret)
return ret;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(mtd, NAND_ECC_READ);
ret = nand_read_data_op(chip, p, eccsize, false);
if (ret)
return ret;
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
}
ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false);
if (ret)
return ret;
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
eccsteps = chip->ecc.steps;
p = buf;
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat == -EBADMSG &&
(chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
/* check for empty pages with bitflips */
stat = nand_check_erased_ecc_chunk(p, eccsize,
&ecc_code[i], eccbytes,
NULL, 0,
chip->ecc.strength);
}
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
mtd->ecc_stats.corrected += stat;
max_bitflips = max_t(unsigned int, max_bitflips, stat);
}
}
return max_bitflips;
}
坏快表BBT
有的厂商将坏块表存放在flash第一块的第一页中,因为厂商出厂时第一页会确保为好块的。但是如果flash用来存放代码,则不能第一页不能用来存放坏快表。有的厂商将坏块表放在最后一块,但是要确保最后一块为好块。
mtdchar iotcl判断该块是否为坏块。
case MEMGETBADBLOCK:
{
loff_t offs;
if (copy_from_user(&offs, argp, sizeof(loff_t)))
return -EFAULT;
return mtd_block_isbad(mtd, offs);
break;
}
调用链
如果内存中没有bbt(chip->bbt);则去读oob区域;
如果有bbt,则从bbt[]中读取数据;
标记坏块时,标记bbt并更新oob区域。
nor flash
cfi接口;jedec(老的接口)