【驱动】第8课、NAND驱动之学习笔记

主   机:VMWare--Ubuntu-16.04.2-x64-100ask

开发板:Mini2440--256M NandFlash,   2M NorFlash,   64M SDRAM,   LCD-TD35;
    bootlorder:u-boot1.16,        Kernel:2.6.22.6;
编译器:arm-linux-gcc-3.4.5


 

【课堂笔记】

NAND FLASH是一个存储芯片
那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A"

问1. 原理图上NAND FLASH和S3C2440之间只有数据线,
怎么传输地址?
答1.在DATA0~DATA7上既传输数据,又传输地址
当ALE为高电平时传输的是地址,

问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令
怎么传入命令?
答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址,
当CLE为高电平时传输的是命令
当ALE和CLE都为低电平时传输的是数据

问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等
那么怎么避免干扰?
答3. 这些设备,要访问之必须"选中",
没有选中的芯片不会工作,相当于没接一样

问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,
NAND FLASH肯定不可能瞬间完成烧写的,
怎么判断烧写完成?
答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙

问5. 怎么操作NAND FLASH呢?
答5. 根据NAND FLASH的芯片手册,一般的过程是:
发出命令
发出地址
发出数据/读数据

      NAND FLASH                  S3C2440
发命令     选中芯片
      CLE设为高电平                NFCMMD=命令值
      在DATA0~DATA7上输出命令值
      发出一个写脉冲

发地址     选中芯片                  NFADDR=地址值
      ALE设为高电平
      在DATA0~DATA7上输出地址值
      发出一个写脉冲

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

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

用 u-boot 来体验NAND FLASH的操作:

1. 读ID
        S3C2440             u-boot
选中         NFCONT的bit1设为0                    md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x90    NFCMMD=0x90                            mw.b 0x4E000008 0x90
发出地址0x00    NFADDR=0x00                             mw.b 0x4E00000C 0x00
读数据得到0xEC  val=NFDATA                                  md.b 0x4E000010 1
读数据得到device  code val=NFDATA                             md.b 0x4E000010 1
      0xda
退出读ID的状态     NFCMMD=0xff                               mw.b 0x4E000008 0xff

2. 读内容: 读0地址的数据
使用UBOOT命令:
nand dump 0
Page 00000000 dump:
17 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5

                                   S3C2440                                 u-boot
选中                                 NFCONT的bit1设为0         md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x00                  NFCMMD=0x00                 mw.b 0x4E000008 0x00
发出地址0x00                  NFADDR=0x00                   mw.b 0x4E00000C 0x00
发出地址0x00                  NFADDR=0x00                   mw.b 0x4E00000C 0x00
发出地址0x00                  NFADDR=0x00                   mw.b 0x4E00000C 0x00
发出地址0x00                  NFADDR=0x00                   mw.b 0x4E00000C 0x00
发出地址0x00                  NFADDR=0x00                   mw.b 0x4E00000C 0x00
发出命令0x30                  NFCMMD=0x30                  mw.b 0x4E000008 0x30
读数据得到0x17              val=NFDATA                        md.b 0x4E000010 1
读数据得到0x00              val=NFDATA                        md.b 0x4E000010 1
读数据得到0x00              val=NFDATA                        md.b 0x4E000010 1
读数据得到0xea              val=NFDATA                        md.b 0x4E000010 1
退出读状态                     NFCMMD=0xff                     mw.b 0x4E000008 0xff


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. make menuconfig去掉内核自带的NAND FLASH驱动
    -> Device Drivers
  -> Memory Technology Device (MTD) support
    -> NAND Device Support
    < > NAND Flash support for S3C2410/S3C2440 SoC
2. make uImage
使用新内核启动, 并且使用NFS作为根文件系统
3. insmod s3c_nand.ko
4. 格式化 (参考下面编译工具)
 # flash_eraseall  /dev/mtd3   // 回归到yaffs文件系统格式;
5. 挂接
mount -t yaffs /dev/mtdblock3 /mnt
6. 在/mnt目录下建文件

 

【编译工具】
1. tar xjf mtd-utils-05.07.23.tar.bz2
2. cd mtd-utils-05.07.23/util
修改Makefile:
#CROSS=arm-linux-
改为
CROSS=arm-linux-
3. make
4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/


 

【个人笔记】

把块驱动所有结构体、函数掰开揉碎了塞进脑子里!
多练习!背下来!


【编程】
目的:编写Nandflash的驱动程序;
步骤:
一、驱动框架
1、分配一个nand_chip结构体s3c_nand;
2、设置nand_chip;
说明:设置nand_chip是给nand_scan函数使用的,如果不知道怎么设置,先看nand_scan怎么使用:选中,发命令,发地址,发数据,读数据,判断状态等功能。
s3c_nand->selec_chip; s3c_nand->cmd_ctrl; s3c_nand->IO_ADDR_R;
s3c_nand->IO_ADDR_W; s3c_nand->dev_ready; s3c_nand->ecc.mode;

3、硬件相关的设置:根据nand_flash的手册设置时间参数,使能nand_flash控制器的时钟;
3.1使能nand控制器时钟;
3.2Nandflash控制器初始化(和裸机初始化程序几乎一样);
4、使用: nand_scan;
4.1分配一个 mtd_info 结构体;
4.2配置该结构体;
4.3用该结构体作为参数使用nand_scan();
5、注册(mtd结构体);
add_mtd_partitions;

二、硬件相关的配置
1、映射寄存器虚拟内存。
2、填充s3c_nand的操作函数;
3、3.1使能nand控制器时钟;
3.2Nandflash控制器初始化(和裸机初始化程序几乎一样);

三、构建并注册分区信息
3.1构建Nand分区表
/* NAND parititon from 2.4.18-swl5 */
static struct mtd_partition s3c_nand_parts[] = {
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
3.2add_mtd_partitions();

【测试】
# umount /mnt/
# insmod s3c_nand5.ko
# ls -l /dev/mtd*
crw-rw---- 1 0 0 90, 0 Jan 1 00:00 /dev/mtd0
crw-rw---- 1 0 0 90, 1 Jan 1 00:00 /dev/mtd0ro
crw-rw---- 1 0 0 90, 2 Jan 1 00:00 /dev/mtd1
crw-rw---- 1 0 0 90, 3 Jan 1 00:00 /dev/mtd1ro
crw-rw---- 1 0 0 90, 4 Jan 1 00:00 /dev/mtd2
crw-rw---- 1 0 0 90, 5 Jan 1 00:00 /dev/mtd2ro
crw-rw---- 1 0 0 90, 6 Jan 1 00:00 /dev/mtd3
crw-rw---- 1 0 0 90, 7 Jan 1 00:00 /dev/mtd3ro
brw-rw---- 1 0 0 31, 0 Jan 1 00:00 /dev/mtdblock0
brw-rw---- 1 0 0 31, 1 Jan 1 00:00 /dev/mtdblock1
brw-rw---- 1 0 0 31, 2 Jan 1 00:00 /dev/mtdblock2
brw-rw---- 1 0 0 31, 3 Jan 1 00:00 /dev/mtdblock3
# mount /dev/mtdblock3 /mnt
# ls /mnt/
lost+found test2
# cat /mnt/test2/test_2.txt
myboke:
https://www.cnblogs.com/xiaohujian/
# umount /mnt/
# ls /mnt/
# rmmod s3c_nand5

# insmod s3c_nand6.ko
# mount /dev/mtdblock3 /mnt/
# ls /mnt/
lost+found test2
# cat /mnt/test2/test_2.txt
打印信息:
myboke:
https://www.cnblogs.com/xiaohujian/

【问题】
问1:对于同一Nand块设备,加载不同Nand驱动程序产生的分区变化是否会破坏存储在Nand中的文件?
答1:不知,但猜测如果文件存储的位置不在分割区中线位置,则应该不会破坏。

问2:对于同一Nand块设备,如果不执行擦除操作,则在Nand块设备上创建的文件在不同Nand驱动程序间(假如文件没有被分割破坏)都是可见的吗?
答2:据上面的例子,应该是的。

问3:须在配置s3c2440某功能模块寄存器前(通过设置寄存器CLKCON的相关位)使能相关模块的时钟,然后才可对该功能模块的寄存器进行操作,
其间的内部原理是什么?方法?
答3:模块的寄存器在读/写前需先使能时钟,是因为...
方法1:直接用 ioremap() 映射寄存器 CLKCON 的地址,然后对寄存器映射的虚拟内存进行操作,进而操作寄存器相关位;
方法2:用clk_get()和clk_enable()函数,使能相关功能模块的时钟。

问4:设置nand_chip是给nand_scan函数使用的,如果不知道怎么设置,先看nand_scan怎么使用:选中,发命令,发地址,发数据,读数据,判断状态等功能。
s3c_nand->selec_chip; s3c_nand->cmd_ctrl; s3c_nand->IO_ADDR_R;
s3c_nand->IO_ADDR_W; s3c_nand->dev_ready; s3c_nand->ecc.mode;
其中,没有 NFDATA 寄存器的专用操作函数,是只用默认函数即可还是只配置s3c_nand->IO_ADDR_R;s3c_nand->IO_ADDR_W;即可?
答4:不知。

 【源码】

s3c_nand.c

  1 /*
  2  * 2019-01-13
  3  * 目的: Nandflash驱动程序;
  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 MODULE_LICENSE("GPL");
 27 
 28 struct s3c_nand_regs{
 29     unsigned long nfconf;          /* nand flash 配制             */
 30     unsigned long nfcont;          /* nand flash 控制             */
 31     unsigned long nfcmd;           /* nand flash 指令             */
 32     unsigned long nfaddr;         /* nand flash 地址             */
 33     unsigned long nfdata;         /* nand flash 数据             */
 34     unsigned long nfmeccd0;      /* nand flash 主区域 ecc0/1    */
 35     unsigned long nfmeccd1;      /* nand flash 主区域 ecc2/3    */
 36     unsigned long nfseccd;      /* nand flash 空闲区域 ecc     */
 37     unsigned long nfstat;          /* nand flash 运行状态         */
 38     unsigned long nfestat0;      /* nand flash i/o[7:0]ecc 状态 */
 39     unsigned long nfestat1;        /* nand flash i/o[15:8]ecc 状态*/
 40     unsigned long nfmecc0;      /* nand flash 主区域 ecc0 状态 */
 41     unsigned long nfmecc1;      /* nand flash 主区域 ecc1 状态 */
 42     unsigned long nfsecc;        /* nand flash 空闲区域 ecc 状态*/
 43     unsigned long nfsblk;          /* nand flash 开始块地址       */
 44     unsigned long nfeblk;        /* nand flash 结束块地址       */
 45 };
 46 static struct s3c_nand_regs *s3c_nand_regs;
 47 static struct nand_chip *s3c_nand;
 48 static struct mtd_info *s3c_mtd;
 49 static struct clk *clk;
 50 
 51 /* NAND parititon from 2.4.18-swl5 */
 52 static struct mtd_partition s3c_nand_parts[] = {
 53     [0] = {
 54         .name   = "bootloader",
 55         .size   = 0x00040000,
 56         .offset    = 0,
 57     },
 58     [1] = {
 59         .name   = "params",
 60         .offset = MTDPART_OFS_APPEND,
 61         .size   = 0x00020000,
 62     },
 63     [2] = {
 64         .name   = "kernel",
 65         .offset = MTDPART_OFS_APPEND,
 66         .size   = 0x00200000,
 67     },
 68     [3] = {
 69         .name   = "root",
 70         .offset = MTDPART_OFS_APPEND,
 71         .size   = MTDPART_SIZ_FULL,
 72     }
 73 };
 74 
 75 /* Nand片选, nfcont'[1]: 0=使能,1=禁止 */
 76 static void s3c2440_select_chip(struct mtd_info *mtd, int chip)
 77 {
 78     if (chip == -1)     /* 禁止片选 */
 79     {
 80         s3c_nand_regs->nfcont |= (1<<1);
 81     } else                /* 使能片选 */
 82     {
 83         s3c_nand_regs->nfcont &= ~(1<<1);
 84     }
 85         
 86 }
 87 /* 向Nand存储器发命令/地址周期 */
 88 static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
 89 {
 90     if (ctrl & NAND_CLE)        /* 发命令 */
 91         s3c_nand_regs->nfcmd = dat;
 92     else                        /* 发地址 */
 93         s3c_nand_regs->nfaddr = dat;
 94 }
 95 /* Nand存储器状态, returns: 0=busy, 1=ready */
 96 static int s3c2440_dev_ready(struct mtd_info *mtd)
 97 {    
 98     /* nfstat'[0]: 0=busy, 1=ready */
 99     return s3c_nand_regs->nfstat & (1<<0);
100 }
101 
102 static int s3c_nand_init(void)
103 {
104     int err;
105     /* 1.分配一个nand_chip结构体 */
106     s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
107     if(!s3c_nand)
108     {
109         printk(KERN_ERR "Failed to allocate s3c_nand nand_chip structure!\n");
110         err = -ENOMEM;
111         goto err_fail1;
112     }
113     /* 1.2映射寄存器虚拟内存 */
114     s3c_nand_regs = ioremap(0X4E000000, sizeof(struct s3c_nand_regs));
115     if(!s3c_nand_regs)
116     {
117         printk(KERN_ERR "Failed to map s3c_nand_regs resources!\n");
118         err = -ENOMEM;
119         goto err_fail2;
120     }
121     /* 2.配置该结构体: 给nand_scan()函数使用 */
122     s3c_nand->IO_ADDR_R     = &s3c_nand_regs->nfdata;
123     s3c_nand->IO_ADDR_W     = &s3c_nand_regs->nfdata;
124     s3c_nand->select_chip     = s3c2440_select_chip;
125     s3c_nand->cmd_ctrl         = s3c2440_cmd_ctrl;
126     s3c_nand->dev_ready     = s3c2440_dev_ready;
127     s3c_nand->ecc.mode         = NAND_ECC_SOFT;
128     /* 3.硬件相关的配置 */
129     /* 3.1使能Nandflash控制器时钟 */
130     clk = clk_get(NULL, "nand");
131     if(!clk)
132     {
133         printk(KERN_ERR "Failed to get s3c2440 nand controller clock!\n");
134         err = -ENOENT;
135         goto err_fail3;
136     }
137     clk_enable(clk);
138     /* 3.2初始化Nandflash控制器 */
139 #define  TACLS   0
140 #define  TWRPH0  1
141 #define  TWRPH1  0
142     /*设置NAND FLASH的时序*/
143     s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
144     /*使能NAND FLASH控制器,初始化ECC,禁止片选*/
145     s3c_nand_regs->nfcont = (1<<1) | (1<<0);
146     /* 4.调用nand_scan()函数 */
147     /* 4.1分配一个mtd_info结构体 */
148     s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
149     if(!s3c_mtd)
150     {
151         printk(KERN_ERR "Failed to allocate s3c_mtd mtd_info structure!\n");
152         err = -ENOMEM;
153         goto err_fail4;
154     }
155     /* 4.2配置mtd */
156     s3c_mtd->owner = THIS_MODULE;
157     s3c_mtd->priv = s3c_nand;
158     /* 4.3以mtd作参数,调用nand_scan()函数: 识别Nandflash, 构造mtd_info */
159     err = nand_scan(s3c_mtd, 1);
160     if(err < 0)
161     {
162         printk(KERN_ERR "Unable to scan for the NAND device!\n");
163         goto err_fail5;
164     }
165     /* 5.注册mtd分割表 */
166     add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
167     return 0;
168  err_fail5:
169      nand_release(s3c_mtd);
170  err_fail4:
171      kfree(s3c_mtd);
172  err_fail3:
173      if(clk)
174     {
175         clk_put(clk);
176         clk_disable(clk);
177         clk = NULL;
178     }
179  err_fail2:
180      iounmap(s3c_nand_regs);
181  err_fail1:
182      kfree(s3c_nand);
183     return err;
184 }
185 static void s3c_nand_exit(void)
186 {
187     del_mtd_partitions(s3c_mtd);
188     nand_release(s3c_mtd);
189      kfree(s3c_mtd);
190      if(clk)
191     {
192         clk_put(clk);
193         clk_disable(clk);
194         clk = NULL;
195     }
196      iounmap(s3c_nand_regs);
197      kfree(s3c_nand);
198 }
199 
200 module_init(s3c_nand_init);
201 module_exit(s3c_nand_exit);

 

posted @ 2019-01-15 16:29  大秦长剑  阅读(665)  评论(0编辑  收藏  举报