关于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); } ... ... }