前几天匆忙间发了一篇关于S5PV210的8位HWECC驱动的文章,但是后来发现存在严重的Bug,就将原来那篇文章删除了,这里先说声抱歉,但是,HWECC能有效的节省CPU占用量,我仔细调试了S5PV210的HWECC部分,现在刚调好1位的HWECC,为了表示误发原来那篇文章的歉意,现在将代码放在这里,与大家分享:
#include <linux/module.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #define NFCONT_MECCLOCK (1<<7) #define NFCONT_SECCLOCK (1<<6) #define NFCONT_INITMECC (1<<5) #define NFCONT_INITSECC (1<<4) #define NFCONT_INITECC (NFCONT_INITMECC | NFCONT_INITSECC) struct s5p_nand_regs{ unsigned long nfconf; unsigned long nfcont; unsigned long nfcmmd; unsigned long nfaddr; unsigned long nfdata; unsigned long nfmeccd0; unsigned long nfmeccd1; unsigned long nfseccd; unsigned long nfsblk; unsigned long nfeblk; unsigned long nfstat; unsigned long nfeccerr0; unsigned long nfeccerr1; unsigned long nfmecc0; unsigned long nfmecc1; unsigned long nfsecc; unsigned long nfmlcbitpt; }; static volatile struct s5p_nand_regs *s5p_nand_regs; static struct nand_chip *nand_chip; static struct mtd_info *s5p_mtd_info; static struct clk *s5p_nand_clk; static int eccmode; static struct nand_ecclayout s5p_nand_oob_64 = { .eccbytes = 16, .eccpos = { 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 }, .oobfree = { { .offset = 2, .length = 38 } } }; static struct mtd_partition s5p_nand_partions[] = { [0] = { .name = "bootloader", .offset = 0, .size = SZ_1M, }, [1] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = 5*SZ_1M, }, [2] = { .name = "rootfs", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, }, }; static void s5p_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { if (ctrl & NAND_CTRL_CHANGE) { if (ctrl & NAND_NCE) { if (cmd != NAND_CMD_NONE) { s5p_nand_regs->nfcont &= ~(1<<1); } } else { s5p_nand_regs->nfcont |= (1<<1); } } if (cmd != NAND_CMD_NONE) { if (ctrl & NAND_CLE) s5p_nand_regs->nfcmmd = cmd; else if (ctrl & NAND_ALE) s5p_nand_regs->nfaddr = cmd; } } static int s5p_nand_ready(struct mtd_info *mtd){ return (s5p_nand_regs->nfstat & 0x1); } static void s5p_ecc_hwctl(struct mtd_info *mtd, int mode){ eccmode = mode; s5p_nand_regs->nfconf &= ~(0x3 << 23); /* Init main ECC & unlock */ s5p_nand_regs->nfcont |= NFCONT_INITMECC; s5p_nand_regs->nfcont &= ~NFCONT_MECCLOCK; } static int s5p_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code){ unsigned long nfmecc0 = s5p_nand_regs->nfmecc0; /* Lock */ s5p_nand_regs->nfcont |= NFCONT_MECCLOCK; ecc_code[0] = (nfmecc0)&0xff; ecc_code[1] = (nfmecc0>>8)&0xff; ecc_code[2] = (nfmecc0>>16)&0xff; ecc_code[3] = (nfmecc0>>24)&0xff; return 0; } static int s5p_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc){ unsigned nfmeccd0, nfmeccd1; unsigned long nfeccerr0; nfmeccd0 = (read_ecc[1]<<16)|read_ecc[0]; nfmeccd1 = (read_ecc[3]<<16)|read_ecc[2]; s5p_nand_regs->nfmeccd0 = nfmeccd0; s5p_nand_regs->nfmeccd1 = nfmeccd1; nfeccerr0 = s5p_nand_regs->nfeccerr0; switch(nfeccerr0&0x3){ case 0: return 0; case 1: printk("s5p-nand: detected one bit error\n"); dat[(nfeccerr0>>7)&0x7ff] ^= 1<<((nfeccerr0>>4)&0x3); return 1; case 2: case 3: printk("s5p-nand: detected uncorrected error\n"); return 3; default: return -EIO; } } static int s5p_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int i, stat, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; int secc_start = mtd->oobsize - eccbytes; int col = 0; uint8_t *p = buf; uint32_t *mecc_pos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; col = mtd->writesize; chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1); /* spare area */ chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, chip->oob_poi, secc_start); chip->ecc.calculate(mtd, p, &ecc_calc[chip->ecc.total]); chip->read_buf(mtd, chip->oob_poi + secc_start, eccbytes); col = 0; /* main area */ 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->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, chip->oob_poi + mecc_pos[0] + ((chip->ecc.steps - eccsteps) * eccbytes), 0); if (stat == -1) mtd->ecc_stats.failed++; col = eccsize * (chip->ecc.steps + 1 - eccsteps); } return 0; } static int s5p_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; int secc_start = mtd->oobsize - eccbytes; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; uint32_t *eccpos = chip->ecc.layout->eccpos; /* main area */ 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]); } for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; /* spare area */ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); chip->write_buf(mtd, chip->oob_poi, secc_start); chip->ecc.calculate(mtd, p, &ecc_calc[chip->ecc.total]); for (i = 0; i < eccbytes; i++) chip->oob_poi[secc_start + i] = ecc_calc[chip->ecc.total + i]; chip->write_buf(mtd, chip->oob_poi + secc_start, eccbytes); return 0; } static int s5p_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { uint8_t *ecc_calc = chip->buffers->ecccalc; int eccbytes = chip->ecc.bytes; int secc_start = mtd->oobsize - eccbytes; chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, chip->oob_poi, secc_start); chip->ecc.calculate(mtd, 0, &ecc_calc[chip->ecc.total]); chip->read_buf(mtd, chip->oob_poi + secc_start, eccbytes); return 0; } static int s5p_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { int status = 0; int eccbytes = chip->ecc.bytes; int secc_start = mtd->oobsize - eccbytes; uint8_t *ecc_calc = chip->buffers->ecccalc; int i; 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); chip->ecc.calculate(mtd, 0, &ecc_calc[chip->ecc.total]); for (i = 0; i < eccbytes; i++) chip->oob_poi[secc_start + i] = ecc_calc[chip->ecc.total + i]; chip->write_buf(mtd, chip->oob_poi + secc_start, eccbytes); /* 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; } static int s5p_nand_probe(struct platform_device *pdev){ int ret = 0; struct resource *mem; //硬件部分初始化 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { dev_err(&pdev->dev, "can't get I/O resource mem\n"); return -ENXIO; } s5p_nand_regs = (volatile struct s5p_nand_regs *)ioremap(mem->start, resource_size(mem)); if (s5p_nand_regs == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -EIO; goto err_exit; } s5p_nand_clk = clk_get(&pdev->dev, "nand"); if(s5p_nand_clk == NULL){ dev_dbg(&pdev->dev, "get clk failed\n"); ret = -ENODEV; goto err_iounmap; } clk_enable(s5p_nand_clk); //s5p_nand_regs->nfconf &= ~(0xfff<<4); //s5p_nand_regs->nfconf |= (3<<12)|(5<<8)|(3<<4); //s5p_nand_regs->nfcont |= 3; //分配驱动相关结构体 nand_chip = (struct nand_chip *)kzalloc(sizeof(struct nand_chip), GFP_KERNEL); if(nand_chip == NULL){ dev_err(&pdev->dev, "failed to allocate nand_chip structure\n"); ret = -ENOMEM; goto err_clk_put; } s5p_mtd_info = (struct mtd_info *)kzalloc(sizeof(struct mtd_info), GFP_KERNEL); if(s5p_mtd_info == NULL){ dev_err(&pdev->dev, "failed to allocate mtd_info structure\n"); ret = -ENOMEM; goto err_free_chip; } //设置驱动相关结构体 nand_chip->IO_ADDR_R = (unsigned char*)&s5p_nand_regs->nfdata; nand_chip->IO_ADDR_W = (unsigned char*)&s5p_nand_regs->nfdata; nand_chip->cmd_ctrl = s5p_nand_cmd_ctrl; nand_chip->dev_ready = s5p_nand_ready; nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.hwctl = s5p_ecc_hwctl; nand_chip->ecc.calculate = s5p_ecc_calculate; nand_chip->ecc.correct = s5p_ecc_correct; nand_chip->ecc.read_oob = s5p_nand_read_oob; nand_chip->ecc.write_oob = s5p_nand_write_oob; nand_chip->ecc.read_page = s5p_nand_read_page; nand_chip->ecc.write_page = s5p_nand_write_page; nand_chip->ecc.size = 512; nand_chip->ecc.bytes = 4; nand_chip->ecc.strength = 1; nand_chip->ecc.layout = &s5p_nand_oob_64; s5p_mtd_info->priv = nand_chip; s5p_mtd_info->owner = THIS_MODULE; //扫描Nand flash 设备 if(nand_scan(s5p_mtd_info, 1)){ dev_dbg(&pdev->dev, "nand scan error\n"); goto err_free_info; } //添加分区信息 ret = mtd_device_parse_register(s5p_mtd_info, NULL, NULL, s5p_nand_partions, ARRAY_SIZE(s5p_nand_partions)); if(!ret) return 0; err_free_info: kfree(s5p_mtd_info); err_free_chip: kfree(nand_chip); err_clk_put: clk_disable(s5p_nand_clk); clk_put(s5p_nand_clk); err_iounmap: iounmap(s5p_nand_regs); err_exit: return ret; } static int s5p_nand_remove(struct platform_device *pdev){ nand_release(s5p_mtd_info); kfree(s5p_mtd_info); kfree(nand_chip); clk_disable(s5p_nand_clk); clk_put(s5p_nand_clk); iounmap(s5p_nand_regs); return 0; } static struct platform_driver s5p_nand_drv = { .driver = { .owner = THIS_MODULE, .name = "s5p-nand", }, .probe = s5p_nand_probe, .remove = s5p_nand_remove, }; module_platform_driver(s5p_nand_drv); MODULE_LICENSE("GPL");
接下来的几天我会继续调试一下8位HWECC,不知道能不能调好,从天嵌技术支持那里获悉,天嵌技术人员当时也调试过8位HWECC,他们从三星的某个资料中发现S5PV210的HWECC模块只能使用1位HWECC,不知道是不是真的,我要自己来验证一下。
如果有什么问题欢迎留言讨论,转载原来那篇HWECC文章的朋友请自己修正一下吧。
本文链接:http://blog.csdn.net/girlkoo/article/details/8794993
本文作者:girlkoo