首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Flash >

S5PV210(TQ210)学习札记——Nand flash驱动添加8位HWECC

2013-04-12 
S5PV210(TQ210)学习笔记——Nand flash驱动添加8位HWECC经过一天的努力,已经成功驱动了S5PV210的8位HWECC模

S5PV210(TQ210)学习笔记——Nand flash驱动添加8位HWECC

经过一天的努力,已经成功驱动了S5PV210的8位HWECC模块,这样,Nand flash访问效率应该会高于软件ECC方式。ECC校验的应用范围比较广,在Nand、SD等存储器中广泛应用,因此,学习ECC校验算法还是必要的,虽然S5PV210的Nand控制器已经完美的支持8/12/16位ECC校验,但是了解ECC校验原理还是非常重要的,我没有找到8bitECC校验原理相关的文章,但是找到一片非常不错的1位ECC校验原理的文章,非常经典,推荐大家静下心来阅读一下,下面是这篇文章的链接地址:

http://www.360doc.com/content/11/0523/17/496343_118837985.shtml

一 Nand flash的ECC校验原理

对于我们的S5PV210,Nand flash的ECC校验原理是这样的:

(1)写操作ECC生成

我们的S5PV210每次计算ECC的最大长度为512字节,当我们向Nand中写入数据时,每写满512字节,硬件ECC模块就会生成相应长度的ECC校验码,如果您的Nand flash的页大小刚好为512,则可以立刻将生成的ECC校验码写入OOB;而如果您的页大小超过了512字节,比如我们使用的TQ210的Nand flash页大小为2K,这时,每当我们写入512个字节后,需要先将生成的ECC校验码拷贝到内存,当我们写完一整页后,再将四次生成的全部ECC校验码一次性写入OOB。当然了,还可以为写入OOB的ECC校验码计算ECC校验码,操作过程如上,这里就不作说明了。

小结:写操作只需要计算ECC校验码并写入OOB,无需进行校正操作。

(2)读操作ECC生成

跟写操作一样,读的时候也是每512字节计算一次ECC校验码,如果您的Nand flash页大小超过512字节,就要分多次读取。这样,程序在读取Nand的时候会计算得到一套ECC校验码,还能在OOB中读出一套写入数据时计算的ECC校验码,如果两者相同,则说明Nand读操作没有发生错误或者是说发生了8位ECC校验检测不出来的错误,其他情况下表明发生了错误,这样,就可以根据错误的情况分别处理,来校正发生错误的数据。

小结:读操作既需要计算ECC校验码,也需要参考OOB中读出的ECC校验码进行数据校正操作。

更具体的操作过程可以参考芯片手册中Nand部分,手册对Nand存储器ECC方式的写入和读取操作分别作了详细的介绍,仔细阅读手册,然后编写这部分的驱动会事半功倍。


二 Nand flash驱动源码(含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>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;};struct s5p_nand_ecc{unsigned long nfeccconf;unsigned long nfecccont;unsigned long nfeccstat;unsigned long nfeccsecstat;unsigned long nfeccprgecc0;unsigned long nfeccprgecc1;unsigned long nfeccprgecc2;unsigned long nfeccprgecc3;unsigned long nfeccprgecc4;unsigned long nfeccprgecc5;unsigned long nfeccprgecc6;unsigned long nfeccerl0;unsigned long nfeccerl1;unsigned long nfeccerl2;unsigned long nfeccerl3;unsigned long nfeccerl4;unsigned long nfeccerl5;unsigned long nfeccerl6;unsigned long nfeccerl7;unsigned long nfeccerp0;unsigned long nfeccerp1;unsigned long nfeccerp2;unsigned long nfeccerp3;unsigned long nfeccconecc0;unsigned long nfeccconecc1;unsigned long nfeccconecc2;unsigned long nfeccconecc3;unsigned long nfeccconecc4;unsigned long nfeccconecc5;unsigned long nfeccconecc6;};static struct nand_chip *nand_chip;static struct mtd_info *s5p_mtd_info;static struct s5p_nand_regs *s5p_nand_regs;static struct s5p_nand_ecc  *s5p_nand_ecc;static struct clk *s5p_nand_clk;static volatile unsigned long* mp0_1con;static volatile unsigned long* mp0_3con;static volatile unsigned long* mp0_6con;static int eccmode;static struct nand_ecclayout s5p_nand_oob_64_8bit = {    .eccbytes = 52,    .eccpos = {        12, 13, 14, 15,        16, 17, 18, 19, 20, 21, 22, 23,        24, 25, 26, 27, 28, 29, 30, 31,        32, 33, 34, 35, 36, 37, 38, 39,        40, 41, 42, 43, 44, 45, 46, 47,        48, 49, 50, 51, 52, 53, 54, 55,        56, 57, 58, 59, 60, 61, 62, 63},     .oobfree = {         {.offset = 2,            .length = 10}}};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_select_chip(struct mtd_info *mtd, int chipnr){if(chipnr == -1){s5p_nand_regs->nfcont |= (1<<1);}else{s5p_nand_regs->nfcont &= ~(1<<1);}}static void s5p_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl){if (ctrl & NAND_CLE){s5p_nand_regs->nfcmmd = cmd;}else{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){/* Save mode */eccmode = mode;/* 8 bit selection */s5p_nand_regs->nfconf &= ~(0x3 << 23);s5p_nand_regs->nfconf |= (0x1 << 23);/* Set MsgLength & EccType */s5p_nand_ecc->nfeccconf = (511<<16)|(3);/* Set direction */if (mode == NAND_ECC_WRITE){s5p_nand_ecc->nfecccont |= 1<<16;s5p_nand_ecc->nfeccsecstat |= 1<<25;}else if (mode == NAND_ECC_READ){s5p_nand_ecc->nfecccont &= ~(1<<16);s5p_nand_ecc->nfeccsecstat |= 1<<24;}/* Initialize & unlock */s5p_nand_ecc->nfecccont |= 1<<2;s5p_nand_regs->nfcont &= ~(1<<7);}static int s5p_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,uint8_t *ecc_code){unsigned long ecccode0, ecccode1, ecccode2, ecccode3;/* Lock */s5p_nand_regs->nfcont |= 1<<7;/* Wait busy */while(s5p_nand_ecc->nfeccstat&(1<<31));ecccode0 = s5p_nand_ecc->nfeccprgecc0;ecccode1 = s5p_nand_ecc->nfeccprgecc1;ecccode2 = s5p_nand_ecc->nfeccprgecc2;ecccode3 = s5p_nand_ecc->nfeccprgecc3;ecc_code[0]  = ecccode0&0xff;ecc_code[1]  = (ecccode0>>8)&0xff;ecc_code[2]  = (ecccode0>>16)&0xff;ecc_code[3]  = (ecccode0>>24)&0xff;ecc_code[4]  = ecccode1&0xff;ecc_code[5]  = (ecccode1>>8)&0xff;ecc_code[6]  = (ecccode1>>16)&0xff;ecc_code[7]  = (ecccode1>>24)&0xff;ecc_code[8]  = ecccode2&0xff;ecc_code[9]  = (ecccode2>>8)&0xff;ecc_code[10] = (ecccode2>>16)&0xff;ecc_code[11] = (ecccode2>>24)&0xff;ecc_code[12] = ecccode3&0xff;return 0;}static int s5p_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,uint8_t *calc_ecc){unsigned char err_bits_cnt;unsigned long nf8eccerr0, nf8eccerr1, nf8eccerr2, nf8eccerr3, nfmlc8bitpt0, nfmlc8bitpt1;while(s5p_nand_ecc->nfeccstat&(1<<31));nf8eccerr0 = s5p_nand_ecc->nfeccerl0;nf8eccerr1 = s5p_nand_ecc->nfeccerl1;nf8eccerr2 = s5p_nand_ecc->nfeccerl2;nf8eccerr3 = s5p_nand_ecc->nfeccerl3;nfmlc8bitpt0 = s5p_nand_ecc->nfeccerp0;nfmlc8bitpt1 = s5p_nand_ecc->nfeccerp1;if(s5p_nand_ecc->nfeccstat&(1<<8)){err_bits_cnt = 0;}else{err_bits_cnt = s5p_nand_ecc->nfeccsecstat&0x1f;err_bits_cnt = err_bits_cnt>8?9:err_bits_cnt;}switch(err_bits_cnt){case 9:printk("s5p-nand: ecc uncorrectable error detected\n");return -EIO;case 8:dat[(nf8eccerr3>>16)&0x3ff] ^= ((nfmlc8bitpt1>>24)&0xff);case 7:dat[nf8eccerr3&0x3ff]       ^= ((nfmlc8bitpt1>>16)&0xff);case 6:dat[(nf8eccerr2>>16)&0x3ff] ^= ((nfmlc8bitpt1>>8)&0xff);case 5:dat[nf8eccerr2&0x3ff]       ^= (nfmlc8bitpt1&0xff);case 4:dat[(nf8eccerr1>>16)&0x3ff] ^= (nfmlc8bitpt0>>24)&0xff;case 3:dat[nf8eccerr1&0x3ff]       ^= (nfmlc8bitpt0>>16)&0xff;case 2:dat[(nf8eccerr0>>16)&0x3ff] ^= (nfmlc8bitpt0>>8)&0xff;case 1:dat[nf8eccerr0&0x3ff] ^= nfmlc8bitpt0&0xff;printk("s5p-nand: %d bit(s) error detected, corrected successful", err_bits_cnt);return err_bits_cnt;case 0:return 0;default:return -EIO;}return 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 = (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_ecc = (struct s5p_nand_ecc *)ioremap(0xB0E20000, sizeof(struct s5p_nand_ecc));if(s5p_nand_ecc == NULL){dev_err(&pdev->dev, "ioremap failed\n");ret = -EIO;goto err_iounmap;}//配置Nand flash 相关GPIOmp0_1con = ioremap(0xE02002E0, 4);mp0_3con = ioremap(0xE0200320, 4);mp0_6con = ioremap(0xE0200380, 4);*mp0_1con &= ~(0xffff<<8);*mp0_1con |= (0x3333<<8);*mp0_3con = 0x22222222;*mp0_6con = 0x22222222;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 = (3<<23)|(3<<12)|(5<<8)|(3<<4)|(1<<1);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->select_chip = s5p_nand_select_chip;nand_chip->cmd_ctrl    = s5p_nand_cmd_ctrl;nand_chip->IO_ADDR_R   = &s5p_nand_regs->nfdata;nand_chip->IO_ADDR_W   = &s5p_nand_regs->nfdata;nand_chip->dev_ready   = s5p_nand_ready;nand_chip->ecc.mode    = NAND_ECC_HW;nand_chip->ecc.layout      = &s5p_nand_oob_64_8bit;nand_chip->ecc.hwctl   = s5p_ecc_hwctl;nand_chip->ecc.calculate = s5p_ecc_calculate;nand_chip->ecc.correct   = s5p_ecc_correct;nand_chip->ecc.size    = 512;nand_chip->ecc.bytes   = 13;nand_chip->options     |= NAND_NO_SUBPAGE_WRITE;nand_chip->ecc.strength = 1;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:if(s5p_nand_ecc == NULL)iounmap(s5p_nand_ecc);if(s5p_nand_regs == NULL)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(mp0_6con);iounmap(mp0_3con);iounmap(mp0_1con);if(s5p_nand_ecc == NULL)iounmap(s5p_nand_ecc);if(s5p_nand_regs == NULL)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");

上面是源码,没有作过多的测试,如果发现什么问题请留言相告或讨论。


本文链接:http://blog.csdn.net/girlkoo/article/details/8785851

本文作者:girlkoo

热点排行