关闭

s5pv210 S34ML04G2 SLC nandflash驱动调试笔记

1148人阅读 评论(0) 收藏 举报
分类:

最近调试S34ML04G2与之前的K9F4G08区别主要是oob和ecc校验位数不一样 发现直接用之前的驱动就可以启动 为了确保准确性还是来重新调试下驱动

S34ML04G2 页大小是2K+128B 4bit ecc/528B(512+16)

K9F4G08页大小2K+64B 1bit ecc/528

这里需要注意的是528B是512+16B网上看的说这16B是oob现在还没搞清楚是什么情况

还有后面的4bit ecc不是说512B会产生4bit ecc这里不要搞混了 这个是与控制端对应的 210支持1bit 4bit 8,12,16bit ecc最后寄存器设置都是不一样的 这个4bit是指每512B可以纠正4bit的错误

下面我们结合代码整体来看下区别

首先是u-boot里主要涉及的文件有cpu/s5pc11x/nand.c drivers/mtd/nand/nand_base.cdrivers/mtd/nand/nand_util.c

drivers/mtd/nand/nand_ids.c include/linux/mtd/nand.h

首先在include/linux/mtd/nand.h增加一个宏

#define NAND_MFR_SPANSION	0x01
#define NAND_MFR_TOSHIBA	0x98
#define NAND_MFR_SAMSUNG	0xec
#define NAND_MFR_FUJITSU	0x04
#define NAND_MFR_NATIONAL	0x8f
#define NAND_MFR_RENESAS	0x07
#define NAND_MFR_STMICRO	0x20
#define NAND_MFR_HYNIX		0xad
spansion的id是1

然后是drivers/mtd/nand/nand_ids.c

struct nand_manufacturers nand_manuf_ids[] = {
	{NAND_MFR_SPANSION, "Spansion"},
	{NAND_MFR_TOSHIBA, "Toshiba"},
	{NAND_MFR_SAMSUNG, "Samsung"},
	{NAND_MFR_FUJITSU, "Fujitsu"},
	{NAND_MFR_NATIONAL, "National"},
	{NAND_MFR_RENESAS, "Renesas"},
	{NAND_MFR_STMICRO, "ST Micro"},
	{NAND_MFR_HYNIX, "Hynix"},
	{0x0, "Unknown"}
};
增加spansion

在nand_flash_ids中由于samsung有spansion这两个芯片读出来的dev id都是0xdc所以里面就不用加了

然后就是cpu/s5pc11x/nand.c

增加

static struct nand_ecclayout gzsd_oob_128 = {//add by hclydao 
	.useecc = MTD_NANDECC_AUTOPLACE,	/* Only for U-Boot */
	.eccbytes = 32,
	.eccpos = {
                96, 97, 98, 99, 100, 101, 102, 103,
                104, 105, 106, 107, 108, 109, 110, 111,
                112, 113, 114, 115, 116, 117, 118, 119,
                120, 121, 122, 123, 124, 125, 126, 127},
	.oobfree = {
		{.offset = 2,
		 .length = 94}}
};
这里对这个做一个简单的说明  我在网上看到一些感觉说的不好理解 首先eccbytes为什么是32B

在210芯片手册中有说明 对于4bit ecc校验每512B会生成8个字节的ecc parity code其实文档上也没有明确的文字说明

210 datasheet相关说明如下


第3点 当你写512B后,校验值会在NFMECC0与NFMECC1里更新 而这两个寄存器里存的刚好就是8个字节(最后一个字节是保留的) 就是说每512B会生成8B校验码(这里我也设置成7B过 似乎是不行) 而一页是4x512B=2K就会生成4x8B=32B检验码 所以eccbytes是32B

而oobfree里的offset = 2这个一般是固定的 网上说oob前两个字节是用来设置标记坏块用的,length就是oob里可用的空间的长度 lenght之所以是94是因为我看代码里的这几个结构体ecc大部分都是保存在oob的最后空间 因为ecc是32B所以 存放ecc的eccpos就是从96开始到最后的这32个字节 那前面的2-95就是自由的空间这个自由空间的长度就是length(94 这个我应该没算错)这里所说的oob就是nandflash手册上说的spare size也有的手册上叫redundant area

对于S34ML04G2来说oob总大小就是128B
然后修改下面这个函数

void board_nand_init(struct nand_chip *nand)
{
#if defined(CFG_NAND_HWECC)
    int i;
    u_char tmp;
    struct nand_flash_dev *type = NULL;
#endif
    int maf_id;//add by hclydao 20150707
    NFCONT_REG         &= ~NFCONT_WP;
    nand->IO_ADDR_R        = (void __iomem *)(NFDATA);
    nand->IO_ADDR_W        = (void __iomem *)(NFDATA);
    nand->cmd_ctrl        = s3c_nand_hwcontrol;
    nand->dev_ready        = s3c_nand_device_ready;
    nand->scan_bbt        = s3c_nand_scan_bbt;
    nand->options        = 0;

#if defined(CFG_NAND_FLASH_BBT)
        nand->options         |= NAND_USE_FLASH_BBT;
#else
        nand->options        |= NAND_SKIP_BBTSCAN;
#endif

#if defined(CFG_NAND_HWECC)
    nand->ecc.mode        = NAND_ECC_HW;
    nand->ecc.hwctl        = s3c_nand_enable_hwecc;
    nand->ecc.calculate    = s3c_nand_calculate_ecc;
    nand->ecc.correct    = s3c_nand_correct_data;

    s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
    s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
    s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
    s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
    s3c_nand_device_ready(0);

    tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
    maf_id = tmp;
    tmp = readb(nand->IO_ADDR_R); /* Device ID */
    for (i = 0; nand_flash_ids[i].name != NULL; i++) {
        if (tmp == nand_flash_ids[i].id) {
            type = &nand_flash_ids[i];
            break;
        }
    }

    nand->cellinfo = readb(nand->IO_ADDR_R);    /* 3rd byte */
    tmp = readb(nand->IO_ADDR_R);            /* 4th byte */
    if (!type->pagesize) {
        if (((nand->cellinfo >> 2) & 0x3) == 0) {
            nand_type = S3C_NAND_TYPE_SLC;
            nand->ecc.size = 512;
            nand->ecc.bytes    = 4;
            if ((1024 << (tmp & 0x3)) > 512) {
                if(maf_id == NAND_MFR_SPANSION) {
                    nand->ecc.read_page = gzsd_read_page_4bit;
                    nand->ecc.write_page = gzsd_write_page_4bit;
                    nand->ecc.read_oob = gzsd_read_oob_4bit;
                    nand->ecc.write_oob = gzsd_write_oob_4bit;
                    nand->ecc.layout = &gzsd_oob_128;
                    nand->ecc.hwctl = gzsd_enable_hwecc_4bit;
                    nand->ecc.calculate = gzsd_calculate_ecc_4bit;
                    nand->ecc.correct = gzsd_correct_data_4bit;
                    nand->ecc.size = 512;
                    nand->ecc.bytes = 8;
                    nand->options |= NAND_NO_SUBPAGE_WRITE;
                } else {
                    nand->ecc.read_page = s3c_nand_read_page_1bit;
                    nand->ecc.write_page = s3c_nand_write_page_1bit;
                    nand->ecc.read_oob = s3c_nand_read_oob_1bit;
                    nand->ecc.write_oob = s3c_nand_write_oob_1bit;
                    nand->ecc.layout = &s3c_nand_oob_64;
                    nand->ecc.hwctl = s3c_nand_enable_hwecc;
                    nand->ecc.calculate = s3c_nand_calculate_ecc;
                    nand->ecc.correct = s3c_nand_correct_data;
                    nand->options |= NAND_NO_SUBPAGE_WRITE;
                }
            } else {
                nand->ecc.layout = &s3c_nand_oob_16;
            }
        } else {
            nand_type = S3C_NAND_TYPE_MLC;
            nand->options |= NAND_NO_SUBPAGE_WRITE;    /* NOP = 1 if MLC */
            nand->ecc.read_page = s3c_nand_read_page_4bit;
            nand->ecc.write_page = s3c_nand_write_page_4bit;
            nand->ecc.size = 512;
            nand->ecc.bytes = 8;    /* really 7 bytes */
            nand->ecc.layout = &s3c_nand_oob_mlc_64;
        }
    } else {
        nand_type = S3C_NAND_TYPE_SLC;
        nand->ecc.size = 512;
        nand->cellinfo = 0;
        nand->ecc.bytes = 4;
        nand->ecc.layout = &s3c_nand_oob_16;
    }
#else
    nand->ecc.mode = NAND_ECC_SOFT;
#endif
}
这里的

                    nand->ecc.size = 512;
                    nand->ecc.bytes = 8;

就是对应的多少字节生成多少个字节的ecc校验值 我们这里是每512字节生成8字节的ecc校验值.

然后增加如下这些函数

static void gzsd_wait_4bitenc(void)
{
	while (!(readl(NFSTAT) & (1 << 7))) {}
}

static void gzsd_wait_4bitdec(void)
{
	while (!(readl(NFSTAT) & (1 << 6))) {}
}

void gzsd_enable_hwecc_4bit(struct mtd_info *mtd, int mode)
{
	u_long nfreg;
	u_long nfcont;
	cur_ecc_mode = mode;
	/* set msglenght 512byte and 4 bit selection */
	nfreg = readl(NFCONF);
	nfreg &= ~(0x7 << 23);
	nfreg |= (0x2 << 23);
	writel(nfreg, NFCONF);

	/* Initialize & unlock */
	nfcont = readl(NFCONT);
	nfcont |= NFCONT_INITMECC;
	nfcont &= ~NFCONT_MECCLOCK;

	if (mode == NAND_ECC_WRITE)
		nfcont |= NFCONT_ECC_ENC;
	else if (mode == NAND_ECC_READ)
		nfcont &= ~NFCONT_ECC_ENC;

	writel(nfcont, NFCONT);
}

int gzsd_calculate_ecc_4bit(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
	u_long nfcont, nfmecc0, nfmecc1;
	int i;
	/* Lock */
	nfcont = readl(NFCONT);
	nfcont |= NFCONT_MECCLOCK;
	writel(nfcont, NFCONT);
	if (cur_ecc_mode == NAND_ECC_READ) {
		gzsd_wait_4bitdec();
	}else {
		gzsd_wait_4bitenc();
		nfmecc0 = readl(NFMECC0);
		nfmecc1 = readl(NFMECC1);
		ecc_code[0] = nfmecc0 & 0xff;
		ecc_code[1] = (nfmecc0 >> 8) & 0xff;
		ecc_code[2] = (nfmecc0 >> 16) & 0xff;
		ecc_code[3] = (nfmecc0 >> 24) & 0xff;
		ecc_code[4] = nfmecc1 & 0xff;
		ecc_code[5] = (nfmecc1 >> 8) & 0xff;
		ecc_code[6] = (nfmecc1 >> 16) & 0xff;
		ecc_code[7] = (nfmecc1 >> 24) & 0xff;
	}
}

int gzsd_correct_data_4bit(struct mtd_info *mtd, u_char *dat)
{
	int ret = -1;
	u_long nfestat0, nfestat1, nfmeccdata0, nfmeccdata1, nfmlcbitpt;
	u_char err_type;

	s3c_nand_wait_ecc_busy();

	nfestat0 = readl(NFESTAT0);
	nfestat1 = readl(NFESTAT1);
	nfmlcbitpt = readl(NFMLCBITPT);
	err_type = (nfestat0 >> 26) & 0x7;

	/* No error, If free page (all 0xff) */
	if ((nfestat0 >> 29) & 0x1) {
		err_type = 0;
	} else {
		/* No error, If all 0xff from 17th byte in oob (in case of JFFS2 format) */
		if (dat) {
			if (dat[17] == 0xff && dat[26] == 0xff && dat[35] == 0xff && dat[44] == 0xff && dat[54] == 0xff)
				err_type = 0;
		}
	}

	switch (err_type) {
		case 5: /* Uncorrectable */
			printk("s3c-nand: ECC uncorrectable error detected\n");
			ret = -1;
			break;

		case 4: /* 4 bit error (Correctable) */
			dat[(nfestat1 >> 16) & 0x3ff] ^= ((nfmlcbitpt >> 24) & 0xff);

		case 3: /* 3 bit error (Correctable) */
			dat[nfestat1 & 0x3ff] ^= ((nfmlcbitpt >> 16) & 0xff);

		case 2: /* 2 bit error (Correctable) */
			dat[(nfestat0 >> 16) & 0x3ff] ^= ((nfmlcbitpt >> 8) & 0xff);

		case 1: /* 1 bit error (Correctable) */
			printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
			dat[nfestat0 & 0x3ff] ^= (nfmlcbitpt & 0xff);
			ret = err_type;
			break;

		case 0: /* No error */
			ret = 0;
			break;
	}
	return ret;
}

int gzsd_read_page_4bit(struct mtd_info *mtd, struct nand_chip *chip,
				uint8_t *buf)
{
	int i, stat, eccsize = chip->ecc.size;
	int eccbytes = chip->ecc.bytes;
	int eccsteps = chip->ecc.steps;
	int col = 0;
	uint8_t *p = buf;
	uint32_t *mecc_pos = chip->ecc.layout->eccpos;
	/* Step1: read whole oob */
	col = mtd->writesize;
	chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
	col = 0;
	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
		chip->ecc.hwctl(mtd, NAND_ECC_READ);
		chip->read_buf(mtd, p, eccsize);
		chip->write_buf(mtd, chip->oob_poi + mecc_pos[0] + ((chip->ecc.steps - eccsteps) * eccbytes), eccbytes);
		chip->ecc.calculate(mtd, 0, 0);
		stat = chip->ecc.correct(mtd, p, 0, 0);

		if (stat == -1)
			mtd->ecc_stats.failed++;

		col = eccsize * (chip->ecc.steps + 1 - eccsteps);
	}
	return 0;
}

void gzsd_write_page_4bit(struct mtd_info *mtd, struct nand_chip *chip,
				  const uint8_t *buf)
{
	int i, eccsize = chip->ecc.size;
	int eccbytes = chip->ecc.bytes;
	int eccsteps = chip->ecc.steps;
	const uint8_t *p = buf;
	uint8_t *ecc_calc = chip->buffers->ecccalc;
	uint32_t *mecc_pos = chip->ecc.layout->eccpos;
	/* Step1: write main data and encode mecc */
	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
		chip->write_buf(mtd, p, eccsize);
		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
	}
	/* Step2: save encoded mecc */
	for (i = 0; i < chip->ecc.total; i++)
		chip->oob_poi[mecc_pos[i]] = ecc_calc[i];

	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
}

int gzsd_read_oob_4bit(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd)
{
	int eccbytes = chip->ecc.bytes;
	int secc_start = mtd->oobsize - eccbytes;
	if (sndcmd) {
		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
		sndcmd = 0;
	}
	chip->ecc.hwctl(mtd, NAND_ECC_READ);
	chip->read_buf(mtd, chip->oob_poi, secc_start);
	return sndcmd;
}

int gzsd_write_oob_4bit(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
	int status = 0;
	int eccbytes = chip->ecc.bytes;
	int secc_start = mtd->oobsize - eccbytes;

	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
	/* spare area */
	chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
	chip->write_buf(mtd, chip->oob_poi,secc_start);

	/* Send command to program the OOB data */
	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
	status = chip->waitfunc(mtd, chip);
	return status & NAND_STATUS_FAIL ? -EIO : 0;
}
上面大部分函数都是在原有的函数的基础上copy来的

gzsd_wait_4bitenc与gzsd_wait_4bitdec是我自己加上的 不能使用之前的s3c_nand_wait_dec和s3c_nand_wait_enc这两个函数是给MLC类型的用的 其中需要注意的是210手册里有很多寄存器的位是以MLC开头的不要弄混的 这些位有的也适用于SLC

gzsd_enable_hwecc_4bit是基于s3c_nand_enable_hwecc进行的修改 主要设置为4bit ecc模式以及4bit ecc模式编解码的设置还有一些流程在手册上有说明

gzsd_calculate_ecc_4bit是基于s3c_nand_calculate_ecc_1bit基础上进行的修改 在原有的驱动里已经有了MLC 4bit ecc的驱动 所以这里改一下就可以用了 这里面主要就是不能使用s3c_nand_wait_decs3c_nand_wait_enc

gzsd_correct_data_4bit是基于s3c_nand_correct_data进行的修改 这个改动不大

gzsd_read_page_4bit与gzsd_write_page_4bit实际上就是s3c_nand_read_page_4bits3c_nand_write_page_4bit

gzsd_read_oob_4bit与gzsd_write_oob_4bit是s3c_nand_read_oob_8bits3c_nand_write_oob_8bit

很多操作基本上跟1bit与8bit的类似 所以直接可以抄过来

u-boot里实际上并没有用到read_oob与write_oob这两个函数 而read oob与write oob是在read_page和write_page里完成的

接着是drivers/mtd/nand/nand_base.c里函数nand_get_flash_type

加上

			/* Calc oobsize */
			if(maf_id == NAND_MFR_SPANSION) {//add by hclydao 20150707 for spansion
				mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 8);
			}
			else {
				mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
			}
oobsize改为128字节 默认的是64

接着是drivers/mtd/nand/nand_util.c

在有meminfo->oobsize与meminfo->writesize的判断里加上自己的

&& !(meminfo->oobsize == 128 && meminfo->writesize == 2048)//add by hclydao
 u-boot基本上就只改动这些 然后就是内核里发动基本和u-boot一样 内核里只需要修改s3c_nand.c就可以了

然后就是文件系统制作下载一个mkyaffs2image工具源码加上对2k+128的支持 生成镜像后就可以下载启动了.

============================================
作者:hclydao
http://blog.csdn.net/hclydao
版权没有,但是转载请保留此段声明

============================================


参考资料:

http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html

http://blog.csdn.net/brantyou/article/details/8185547

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:226673次
    • 积分:2906
    • 等级:
    • 排名:第12940名
    • 原创:64篇
    • 转载:5篇
    • 译文:0篇
    • 评论:215条
    浏览支持
    联系方式
    邮箱:hclydao@163.com

    博客专栏
    文章分类