Chinaunix首页 | 论坛 | 博客
  • 博客访问: 845360
  • 博文数量: 321
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 936
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-23 11:25
文章分类

全部博文(321)

文章存档

2017年(1)

2016年(10)

2015年(61)

2014年(187)

2013年(62)

分类: LINUX

2015-07-11 18:06:33

原文地址:Linux驱动学习笔记之NAND 作者:jiaweijing

先从原理图上看,NAND地址线与数据线复用,那么是怎么判断传输的是数据还是地址呢,是通过ALE来控制的,当ALE为高电平时,传输的是地址,反之为数据。
在DATA0~DATA7上既传输数据,又传输地址,也传输命令
     当ALE为高电平时传输的是地址,
     当CLE为高电平时传输的是命令
     当ALE和CLE都为低电平时传输的是数据
nFCE,为片选信号,只有通过nFCE才能决定是否让nandflash工作。
RnB,状态引脚,它为高电平表示就绪,它为低电平表示正忙
nFWE,写
nFRE,读。

根据nandflash数据手册,其读写操作过程如下
     发出命令
     发出地址
     发出数据/读数据
                                        //2440上对应的操作相应要简单些。
          NAND FLASH                      S3C2440
发命令    选中芯片                   
          CLE设为高电平                   NFCMMD=命令值     
          在DATA0~DATA7上输出命令值
          发出一个写脉冲
            
发地址    选中芯片                        NFADDR=地址值
          ALE设为高电平
          在DATA0~DATA7上输出地址值
          发出一个写脉冲

发数据    选中芯片                        NFDATA=数据值
          ALE,CLE设为低电平
          在DATA0~DATA7上输出数据值
          发出一个写脉冲

读数据    选中芯片                        val=NFDATA
          发出读脉冲
          读DATA0~DATA7的数据

NAND FLASH驱动程序层次

看内核启动信息
S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 256 at 0x02000000
Bad eraseblock 257 at 0x02020000
Bad eraseblock 319 at 0x027e0000
Bad eraseblock 606 at 0x04bc0000
Bad eraseblock 608 at 0x04c00000
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x00000000-0x00040000 : "bootloader"
0x00040000-0x00060000 : "params"
0x00060000-0x00260000 : "kernel"
0x00260000-0x10000000 : "root"

搜"S3C24XX NAND Driver"
S3c2410.c (drivers\mtd\nand)

s3c2410_nand_inithw
s3c2410_nand_init_chip
nand_scan  // drivers/mtd/nand/nand_base.c 根据nand_chip的底层操作函数识别NAND FLASH,构造mtd_info
    nand_scan_ident
        nand_set_defaults
if (!chip->select_chip)
chip->select_chip = nand_select_chip; // 默认值不适用

if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command;
chip->cmd_ctrl(mtd, command, ctrl);
if (!chip->read_byte)
chip->read_byte = nand_read_byte;
readb(chip->IO_ADDR_R);
if (chip->waitfunc == NULL)
chip->waitfunc = nand_wait;
chip->dev_ready
        
        
        nand_get_flash_type
            chip->select_chip(mtd, 0);
            chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
            *maf_id = chip->read_byte(mtd);
            dev_id = chip->read_byte(mtd);
    nand_scan_tail
    mtd->erase = nand_erase;
    mtd->read = nand_read;
    mtd->write = nand_write;
s3c2410_nand_add_partition
    add_mtd_partitions
        add_mtd_device
            list_for_each(this, &mtd_notifiers) { // 问. mtd_notifiers在哪设置
                                                  // 答. drivers/mtd/mtdchar.c,mtd_blkdev.c调用register_mtd_user
                struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
                not->add(mtd);
                // mtd_notify_add  和 blktrans_notify_add
                先看字符设备的mtd_notify_add
                        class_device_create
                        class_device_create
                再看块设备的blktrans_notify_add
                    list_for_each(this, &blktrans_majors) { // 问. blktrans_majors在哪设置
                                                            // 答. drivers\mtd\mdblock.c或mtdblock_ro.c   register_mtd_blktrans
                        struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);              
                        tr->add_mtd(tr, mtd);
                                mtdblock_add_mtd (drivers\mtd\mdblock.c)
                                    add_mtd_blktrans_dev
                                        alloc_disk
                                        gd->queue = tr->blkcore_priv->rq; // tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
                                        add_disk           



点击(此处)折叠或打开

  1. /* 参考
  2.  * drivers\mtd\nand\s3c2410.c
  3.  * drivers\mtd\nand\at91_nand.c
  4.  */


  5. #include <linux/module.h>
  6. #include <linux/types.h>
  7. #include <linux/init.h>
  8. #include <linux/kernel.h>
  9. #include <linux/string.h>
  10. #include <linux/ioport.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/delay.h>
  13. #include <linux/err.h>
  14. #include <linux/slab.h>
  15. #include <linux/clk.h>
  16.  
  17. #include <linux/mtd/mtd.h>
  18. #include <linux/mtd/nand.h>
  19. #include <linux/mtd/nand_ecc.h>
  20. #include <linux/mtd/partitions.h>
  21.  
  22. #include <asm/io.h>
  23.  
  24. #include <asm/arch/regs-nand.h>
  25. #include <asm/arch/nand.h>

  26. struct s3c_nand_regs { /* 定义类型不需要加static */
  27.     unsigned long nfconf;
  28.     unsigned long nfcont;
  29.     unsigned long nfcmd;
  30.     unsigned long nfaddr;
  31.     unsigned long nfdata;
  32.     unsigned long nfeccd0;
  33.     unsigned long nfeccd1;
  34.     unsigned long nfeccd;
  35.     unsigned long nfstat;
  36.     unsigned long nfstat0;
  37.     unsigned long nfstat1;
  38.     unsigned long nfmecc0;
  39.     unsigned long nfmecc1;
  40.     unsigned long nfsecc;
  41.     unsigned long nfsblk;
  42.     unsigned long nfeblk;
  43. };

  44. static struct nand_chip *s3c_nand;
  45. static struct mtd_info *s3c_mtd;
  46. static struct s3c_nand_regs *s3c_nand_regs;

  47. static struct mtd_partition s3c_nand_parts[] = {
  48.     [0] = {
  49.         .name = "bootloader",
  50.         .size = 0x00040000,
  51.         .offset = 0,
  52.     },
  53.     [1] = {
  54.         .name = "params",
  55.         .offset = MTDPART_OFS_APPEND,
  56.         .size = 0x00020000,
  57.     },
  58.     [2] = {
  59.         .name = "kernel",
  60.         .offset = MTDPART_OFS_APPEND, /* 紧跟上一分区 */
  61.         .size = 0x00200000,
  62.     },
  63.     [3] = {
  64.         .name = "root",
  65.         .offset = MTDPART_OFS_APPEND,
  66.         .size = MTDPART_SIZ_FULL, /* 剩下的所有分区 */
  67.     },
  68. };

  69. static void s3c2440_select_chip(s3c_mtd, int chipnr)
  70. {
  71.     if (chipnr == -1)
  72.     {
  73.         /* 取消选中: NFCONT[1]设为1 */
  74.         
  75.         s3c_nand_regs->nfcont |= (1<<1);
  76.     }
  77.     else
  78.     {
  79.         /* 选中: NFCONT[1]设为0 */
  80.         
  81.         s3c_nand_regs->nfcont &= ~(1<<1);
  82.     }
  83. }

  84. static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
  85. {
  86.     if (ctrl & NAND_CLE)
  87.     {
  88.         /* 发命令: NFCMMD = dat */
  89.         s3c_nand_regs->nfcmd = dat;
  90.     }
  91.     else
  92.     {
  93.         /* 发地址: NFADDR = dat */
  94.         s3c_nand_regs->nfaddr = dat;
  95.     }
  96. }

  97. static int s3c2440_dev_ready(struct mtd_info *mtd)
  98. {
  99.     //return "NFSTAT的寄存器0";
  100.     return (s3c_nand_regs->nfstat & (1<<0));
  101. }

  102. static int s3c_nand_init(void)
  103. {
  104.     struct clk *clk;
  105.     
  106.     /* 1.分配一个nand_chip结构体 */
  107.     s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);

  108.     s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
  109.     
  110.     /* 2.设置nand_chip */
  111.     /* 2.1 设置nand_chip是给nand_scan函数用,可以通过看nand_scan的使用来设置nand_chip
  112.      * 它应该提供:选中、发命令、发地址、发数据、读数据、判断状态等功能
  113.      */
  114.     s3c_nand->select_chip = s3c2440_select_chip;
  115.     s3c_nand->cmd_ctrl = s3c2440_cmd_ctrl;
  116.     //s3c_nand->IO_ADDR_R = "NFDATA的虚拟地址";
  117.     s3c_nand->IO_ADDR_R = &s3c_nand_regs->nfdata;
  118.     //s3c_nand->IO_ADDR_W = "NFDATA的虚拟地址";
  119.     s3c_nand->IO_ADDR_W = &s3c_nand_regs->nfdata;
  120.     s3c_nand->dev_ready = s3c2440_dev_ready;
  121.     s3c_nand->ecc.mode = NAND_ECC_SOFT; /* ECC 能过软件的方法校验 */
  122.     /* 3.硬件相关的操作 :根据NAND_FLASH的手册设置时间参数 */
  123.     /* 使能NAND FLASH控制器时钟 */
  124.     clk = clk_get(NULL, "nand");
  125.     clk_enable(clk); /* 实际上就是将CLKCON的bit4设为1 */
  126.     
  127.     /* HCLK=100MHz
  128.      * TACLS: 发出CLE/ALE之后多长时间才发出nWE信号,
  129.      * 从NAND手册可知CLE/ALE与nWE可以同时发出,
  130.      * 所以TACLS=0;
  131.      * TWRPH0:nWE信号的脉冲宽度,HCLK x (TWRPH0 + 1),
  132.      * 从NAND手册可知它要>=12ns,所以TWRPH0>=1;
  133.      * TWRPH1:nWE变为高电平后多长时间CLE/ALE才能变为低电平,
  134.      * 从NAND手册可知它要>=5ns,所以TWRPH1>=0;
  135.      */
  136. #define TACLS 0
  137. #define TWRPH0 1
  138. #define TWRPH1 0
  139.     s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

  140.     /* NFCONT:
  141.      * BIT1--1,取消片选
  142.      * BIT0--1,使能NAND FLASH控制器
  143.      */
  144.     s3c_nand_regs->nfcont = (1<<1) | (1<<0);
  145.     
  146.     /* 4. 使用: nand_scan */
  147.     s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
  148.     s3c_mtd->owner = THIS_MODULE;
  149.     s3c_mtd->priv = s3c_nand;
  150.     nand_scan(s3c_mtd, 1); /* 扫描、识别NAND 并构造mtd_info结构体 */
  151.     /* 5. add_mtd_partitions */
  152.     //add_mtd_device(struct mtd_info * mtd) /* 如果不用构造分区,用这个函数就行 */
  153.     add_mtd_partitions(s3c_mtd ,s3c_nand_parts, 4);
  154.     return 0;
  155. }

  156. static void s3c_nand_exit(void)
  157. {
  158.     del_mtd_partitions(s3c_mtd);
  159.     kfree(s3c_mtd);
  160.     iounmap(s3c_nand_regs);
  161.     kfree(s3c_nand);
  162. }

  163. module_init(s3c_nand_init);
  164. module_exit(s3c_nand_exit);


  165. MODULE_DESCRIPTION("nand driver test for the s3c2440");
  166. MODULE_LICENSE("GPL");
测试的时候,需要将内核带的nand驱动去掉,然后通过nfs起文件系统,再加载编译好的nand模块进行相关的测试。
阅读(702) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~