关于nand flash,由于各个厂家的read id读出的内容的定义,都不同,导致,对于读出的id,分别要用不同的解析方法,下面这段代码,是我之前写的,本来打算自己写信去推荐到Linux MTD内核源码的,不过后来由于没搞懂具体申请流程,就放弃了。不过,后来,看到Linux的MTD部分更新了,加了和下面类似的做法。
此处只是为了记录下来,也算给感兴趣的人一个参考吧。
文件:\linux-2.6.28.4\drivers\mtd\nand\nand_base.c
/*
* Get the flash and manufacturer id and lookup if the type is supported
*/
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int busw, int *maf_id)
{
... ...
chip->chipsize = (uint64_t)type->chipsize << 20;
/* 针对不同的MLC和SLC的nand flash,添加了不同的解析其ID的方法 */
/* Newer devices have all the information in additional id bytes */
if (!type->pagesize) {
int erase_bits, page_base, block_base, old_50nm, new_40nm;
uint8_t id3rd, id4th, id5th, id6th, id7th;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = id3rd = chip->read_byte(mtd);
/* The 4th id byte is the important one */
id4th = chip->read_byte(mtd);
id5th = chip->read_byte(mtd);
id6th = chip->read_byte(mtd);
id7th = chip->read_byte(mtd);
/* printk(KERN_INFO " (ID:%02x %02x %02x %02x %02x %02x %02x) ",
id1st, id2nd, id3rd, id4th, id5th, id6th, id7th); */
if (nand_is_mlc(chip->cellinfo)) {
/*
* MLC:
* 50nm has 5 bytes ID, further read ID will periodically output
* 40nm has 6 bytes ID
*/
/*
* the 4th byte is not the same meaning for different manufature
*/
if (NAND_MFR_SAMSUNG == *maf_id) {
/* samsung MLC chip has several type ID meanings:
(1)50nm serials, such as K9GAG08U0M
(2)40nm serials, such as K9LBG08UXD
*/
/* old 50nm chip will periodically output if read further ID */
old_50nm = (id1st == id6th) && (id2nd == id7th);
/* is 40nm or newer */
new_40nm = id6th & 0x07;
if ((!old_50nm) && new_40nm) {
/*
* Samsang
* follow algorithm accordding to datasheets of:
* K9LBG08UXD_1.3 (40nm),
* ID(hex): EC D7 D5 29 38 41
* this algorithm is suitable for new chip than 50nm
* such as K9GAG08u0D,
* ID(hex): EC D5 94 29 B4 41
*/
int bit236;
/* Calc pagesize, bit0,bit1: page size */
page_base = (1 << 11); /* 2KB */
mtd->writesize = page_base * (1 << (id4th & BIT01));
block_base = (1 << 17); /* 128 KB */
/* Calc block size, bit4,bit5,bit7: block size */
erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
erase_bits |= (id4th >> 5) & BIT(2); /* get bit7 and combine them */
mtd->erasesize = block_base * (1 << erase_bits);
/* Calc oobsize, bit2,bit3,bit6: oob size */
bit236 = (id4th >> 2) & BIT01; /* get bit2,bit3 */
bit236 |= (id4th >> 4) & BIT(2); /* get bit6 and combine them */
switch (bit236) {
case 0x01:
mtd->oobsize = 128;
break;
case 0x02:
mtd->oobsize = 218;
break;
default:
/* others reserved */
break;
}
}
else {
/*
* Samsang
* follow algorithm accordding to datasheets of:
* K9GAG08U0M (50nm)
* this algorithm is suitable for old 50nm chip
*/
goto slc_algorithm;
}
}
else if (NAND_MFR_TOSHIBA == *maf_id) {
/*
* Toshiba
* follow algorithm guess from ID of TC58NVG3D1DTG00:
* Toshiba MLC TC58NVG3D1DTG00 1GB 8bit 1chip
* 4K+218 512K+27K 3.3V, (ID:98 D3 94 BA 64 13 42)
*/
int bit23;
/* Calc pagesize, bit0,bit1: page size */
page_base = (1 << 10); /* 1KB */
mtd->writesize = page_base * (1 << (id4th & BIT01));
block_base = (1 << 16); /* 64 KB */
/* Calc block size, bit4,bit5: block size */
erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
mtd->erasesize = block_base * (1 << erase_bits);
/* Calc oobsize, use spare/redundant area bit */
bit23 = (id4th >> 2) & BIT01; /* get bit2,bit3 */
switch (bit23) {
case 0x01:
mtd->oobsize = 128;
break;
case 0x02:
mtd->oobsize = 218;
break;
default:
/* others reserved */
break;
}
/* Get buswidth information: x8 or x16 */
busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
}
else if (NAND_MFR_MICRON == *maf_id) {
/*
* Micron
* follow algorithm accordding to datasheets of:
* 29F32G08CBAAA
*/
int spare_area_size_bit;
/* Calc pagesize, bit0,bit1: page size */
page_base = (1 << 10); /* 1KB */
mtd->writesize = page_base * (1 << (id4th & 0x03));
block_base = (1 << 16); /* 64 KB */
/* Calc block size, bit4,bit5: block size */
erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
mtd->erasesize = block_base * (1 << erase_bits);
/* Calc oobsize, use spare/redundant area bit */
spare_area_size_bit = (id4th >> 2) & BIT(0);
if (spare_area_size_bit) /* special oob */
mtd->oobsize = 218;
else /* normal */
mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
/* Get buswidth information: x8 or x16 */
busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
}
else {
/*
* Others
* FIXME: update follow algrithm,
* according to different manufacture's chip's datasheet
*/
goto slc_algorithm;
}
}
else {
/*
* SLC, only has 4 bytes ID, further read will output periodically, such as:
* Hynix : HY27UG084G2M, only has 4 byte ID,
* following read ID is periodically same as the 1st ~ 4th byte,
* for HY27UG084G2M is : 0xAD 0xDC 0x80 0x15 0xAD 0xDC 0x80 0x15 .....
*/
slc_algorithm:
/* Calc pagesize, bit0,bit1: page size */
page_base = (1 << 10); /* 1KB */
mtd->writesize = page_base * (1 << (id4th & BIT01));
block_base = (1 << 16); /* 64 KB */
/* Calc block size, bit4,bit5: block size */
erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
mtd->erasesize = block_base * (1 << erase_bits);
/* Calc oobsize, use fixed ratio */
mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
/* Get buswidth information: x8 or x16 */
busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
}
} else {
/*
* Old devices have chip data hardcoded in the device id table
*/
mtd->erasesize = type->erasesize;
mtd->writesize = type->pagesize;
mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
busw = type->options & NAND_BUSWIDTH_16;
}
/*
* 以上内容,主要是更加不同厂家的nand flash的datasheet,一点点总结出来的算法。
* 最新的Linux的MTD部分,已经添加了类似如上部分的代码。此处贴出来,仅供参考。
*/
/*
* Check, if buswidth is correct. Hardware drivers should set
* chip correct !
*/
if (busw != (chip->options & NAND_BUSWIDTH_16)) {
printk(KERN_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);
printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8);
return ERR_PTR(-EINVAL);
}
... ...
}





