天嵌 ARM开发社区

 找回密码
 注册
查看: 2351|回复: 0

S5PV210(TQ210)学习笔记——Nand驱动之HWECC(转)

[复制链接]
freewing 发表于 2014-11-14 15:59:43 | 显示全部楼层 |阅读模式
HWECC能有效的节省CPU占用量,我仔细调试了S5PV210的HWECC部分,现在刚调好1位的HWECC,为了表示误发原来那篇文章的歉意,现在将代码放在这里,与大家分享:




[plain] view plaincopy


  • #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);  
  •   
  •         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);  
  •     }  
  •   
  •     for (i = 0; i < chip->ecc.total; i++)  
  •         chip->oob_poi[eccpos] = ecc_calc;  
  •   
  •     /* 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");  




您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

i.MX8系列ARM cortex A53 M4 工控板上一条 /1 下一条

Archiver|手机版|小黑屋|天嵌 嵌入式开发社区 ( 粤ICP备11094220号 )

GMT+8, 2024-5-1 07:31 , Processed in 1.046875 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表