1.3.2.1. Linux MTD中检测不同类型Nand Flash的ID部分的代码

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