Nandflash 驱动

1. 基本知识

jz2440 中使用的nandflash的型号是 K9F2G08U0M

Nandflash 驱动_第1张图片

图中可以看到这个nandflash的结构是:

1页 = (2k + 64) 字节

1块 = 64个页 = ( 2k + 64) x 64 = 128k + 4k 字节

1个设备 = 2048个块 = ...

Nandflash 驱动_第2张图片

对于某个字节的访问,地址的组成如上图,是分5个周期发送的,其中 Column是列,Row 是 [行/页]

 

操作nandflash的命令如下

Nandflash 驱动_第3张图片

 

2. nandflash 控制器介绍

2440 中带有 nandflash 控制器,如果想要读 ID,只需要以下几步

1)将寄存器NFCONT(0x4E000004)的bit1=0,来使能片选

2)写入寄存器NFCMMD(0x4E000008)=0X90,发送命令

3)写入寄存器NFADDR(0x4E00000C)=0X00,发送地址

4)while判断nRE(读使能)是否为低电平

5)读取寄存器NFDATA(0x4E000010),来读取数据

 

3. 在uboot阶段测试nandflash

md 读 (md.l 读4字节 md.b 读1字节 )

mw 写(md.l 读4字节 md.b 读1字节 )

3.1)读 ID 的例子:

选中 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

3.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

 

4. 驱动需要做的

在内核中的一个有关2410的nandflash的例子是 drivers/mtd/nand/s3c2410.c,可以作为我们编写驱动的参考。

https://www.cnblogs.com/lifexy/p/7701181.html

内核mtd层提供了字符设备的操作接口和块设备的操作接口,在 /dev 下会有两个字符设备的节点,一个块设备的节点。内核把整个框架搭建好了,我们关心 nandflash 驱动我们需要做什么?

1)设置mtd_info结构体成员

2)设置nand_chip结构体成员

3)设置硬件相关(设置nand控制器时序等)

4)通过nand_scan()来扫描nandflash

5)通过add_mtd_partitions()来添加分区,创建MTD字符/块设备

 

一些函数:

int nand_scan(struct mtd_info *mtd, int maxchips); //扫描nandflash,扫描成功返回0


/* 将nandflash分成nbparts个分区,会创建多个MTD字符/块设备,成功返回0
 * master:就是要创建的mtd设备
 * parts:分区信息的数组,它的结构体是mtd_partition
 * nbparts:要创建分区的个数,比如上图,那么就等于4
 */

int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts); // linux 2.xxx

int mtd_device_parse_register(struct mtd_info *mtd, const char **types,
                  struct mtd_part_parser_data *parser_data,
                  const struct mtd_partition *parts,
                  int nr_parts) // linux3.4.xxx


/* 卸载分区,并卸载MTD字符/块设备 */
int del_mtd_partitions(struct mtd_info *master);   // linux 2.xxx
int mtd_device_unregister(struct mtd_info *master) // linux 3.4.xxx

 

5. 例子

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct  mynand_regs {
    unsigned long nfconf  ;             //0x4E000000
    unsigned long nfcont  ;
    unsigned long nfcmd   ;
    unsigned long nfaddr  ;
    unsigned long nfdata  ;
    unsigned long nfeccd0 ;
    unsigned long nfeccd1 ;
    unsigned long nfeccd  ;
    unsigned long nfstat  ;
    unsigned long nfestat0;
    unsigned long nfestat1;
    unsigned long nfmecc0 ;
    unsigned long nfmecc1 ;
    unsigned long nfsecc  ;
    unsigned long nfsblk  ;
    unsigned long nfeblk  ;
};
static struct mynand_regs *my_regs;		//nand寄存器
static struct mtd_info *my_mtd;
static struct nand_chip *mynand_chip;

static struct mtd_partition mynand_part[] = {
    [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,
    }
};

  /*nand flash  :CE */
static void mynand_select_chip(struct mtd_info *mtd, int chipnr)
{
        if(chipnr==-1)          //CE Disable
       {
        my_regs->nfcont|=(0x01<<1);	//bit1置1
       }
        else					//CE Enable
       {
        my_regs->nfcont&=~(0x01<<1);	//bit1置0
       }
}
   /*命令/地址控制函数 */
static void mynand__cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
    if (ctrl & NAND_CLE)		//当前为command状态 ,
          my_regs->nfcmd=dat;
    else						//当前为地址状态 ,  if  (ctrl & NAND_ALE)
         my_regs->nfaddr=dat;
}

/* nand flash 设备就绪函数(获取RnB引脚状态 */
static int mynand__device_ready(struct mtd_info *mtd)
{
    return (my_regs->nfstat&0x01);                //获取RnB状态,0:busy       1:ready
}


    /*init入口函数*/
static int mynand_init(void)
{
    struct clk *nand_clk;
    int res;
   /*1.分配结构体: mtd_info和nand_chip */
   my_mtd=kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
   mynand_chip=kzalloc(sizeof(struct nand_chip), GFP_KERNEL);

    /*2.获取nand flash 寄存器虚拟地址*/
     my_regs=ioremap(0x4E000000, sizeof(struct mynand_regs));

    /*3.设置mtd_info*/
    my_mtd->owner=THIS_MODULE;
    my_mtd->priv=mynand_chip;                    //私有数据

    /*4.设置nand_chip*/
    mynand_chip->IO_ADDR_R=&my_regs->nfdata;                 //设置读data
    mynand_chip->IO_ADDR_W=&my_regs->nfdata;                 //设置写data
    mynand_chip->select_chip=mynand_select_chip;             //设置CE
    mynand_chip->cmd_ctrl = mynand__cmd_ctrl;                //设置写command/address
    mynand_chip->dev_ready = mynand__device_ready;           //设置RnB
    mynand_chip->ecc.mode = NAND_ECC_SOFT;                   //设置软件ECC


    /*5.设置硬件相关*/
        /*5.1使能nand flash 时钟*/
        nand_clk=clk_get(NULL,"nand");
        clk_enable(nand_clk);

        /*5.2设置时序*/
        #define TACLS	0                    //0nS
        #define TWRPH0	1                    //15nS
        #define TWRPH1	0                    //5nS
        my_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

        /*5.3       bit1:关闭片选,       bit0:开启nand flash 控制器*/
        my_regs->nfcont=(1<<1)|(1<<0);

    /*6.扫描NAND*/
    if (nand_scan(my_mtd, 1)) {	// 1:表示只扫描一个nand flash 设备
        res = -ENXIO;
        goto out;
    }

    /*7.添加分区,创建字符/块设备*/
    //res = add_mtd_partitions(my_mtd, mynand_part, 4);
	//if(res)
        //return 0;
	res = mtd_device_parse_register(my_mtd, NULL, NULL, mynand_part, 4);
    if(!res)
        return 0;

out:
    //del_mtd_partitions(my_mtd);	//卸载分区,卸载字符/块设备
	mtd_device_unregister(my_mtd);
    kfree(my_mtd);              //释放mtd
    iounmap(my_regs);           //释放nand flash寄存器
    kfree(mynand_chip);         //释放nand_chip
    return 0;

}

/*exit出口函数*/
static void mynand_exit(void)
{
    //del_mtd_partitions(my_mtd);	//卸载分区,卸载字符/块设备
	mtd_device_unregister(my_mtd);
    kfree(my_mtd);				//释放mtd
    iounmap(my_regs);			//释放nand flash寄存器
    kfree(mynand_chip);			//释放nand_chip
}

module_init(mynand_init);
module_exit(mynand_exit);
MODULE_LICENSE("GPL");

 

代码解释

Nandflash 驱动_第4张图片

Nandflash 驱动_第5张图片

Nandflash 驱动_第6张图片

其中 5 中的时间:

2440 上的控制器

Nandflash 驱动_第7张图片

Nandflash 驱动_第8张图片


nandflash芯片中描述的时间

Nandflash 驱动_第9张图片

Nandflash 驱动_第10张图片

Nandflash 驱动_第11张图片

则 tcls = 10ns

控制器 nandflash芯片

TACL = TCLS - TWP = 15ns - 15ns = 0ns

TWRPH0 = TWP = 15ns

TWRPH1 = TCLH = 5ns

 

我板子设置的时钟:

CPUID: 32440001

FCLK: 400 MHz

HCLK: 100 MHz

PCLK: 50 MHz

 

100MHZ ---> 10ns

寄存器中

TACLS

        Duration = HCLK x TACLS

                ---> 0 = HCLK x [TACLS]

                ---> [TACLS] = 0

                ---> 实际的值是 10ns

TWRPH0

        Duration = HCLK x ( TWRPH0 + 1 )

                ---> 15ns = HCLK x ( [TWRPH0] + 1 )

                ---> 15ns = 10(x+1) // 等号后边要大于等于前边的时间

                ---> x = 1

                ---> 实际的值是 20ns

TWRPH1

        Duration = HCLK x ( TWRPH1 + 1 )

                ---> 5ns = HCLK x ( [TWRPH1] + 1 )

                ---> 5ns = 10(x+1)

                ---> x = 0

                ---> 实际的值是 10ns

······

 

6. 测试方法

1)先将内核中默认 nandflash 相关驱动去掉

make menuconfig-> Device Drivers-> Memory Technology Device (MTD) support-> NAND Device Support-> NAND Flash support for Samsung S3C SoCs

 

2)重新编译内核,并用新内核启动

3)将新编译的驱动拷贝到开发板中,加载

root@lip:~$ insmod nandflash_mtd.ko

NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)

Scanning device for bad blocks

Bad eraseblock 94 at 0x000000bc0000

...

Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":

0x000000000000-0x000000040000 : "bootloader"

0x000000040000-0x000000060000 : "params"

0x000000060000-0x000000260000 : "kernel"

0x000000260000-0x000010000000 : "root"

root@lip:~$ cat /proc/mtd

dev: size erasesize name

mtd0: 00040000 00020000 "bootloader"

mtd1: 00020000 00020000 "params"

mtd2: 00200000 00020000 "kernel"

mtd3: 0fda0000 00020000 "root"

上边的 erasesize = 0x00020000 = 128k

size = 00040000 = 256k

因此下边的 #blocks 单位是 1k

root@lip:~$ cat /proc/partitions

major minor #blocks name

31 0 256 mtdblock0

31 1 128 mtdblock1

31 2 2048 mtdblock2

31 3 259712 mtdblock3

4)格式化

> flash_eraseall /dev/mtd3

 

5) 挂载

> mount -t yaffs /dev/mtdblock3 /mnt

 

6)新建立文件,并卸载,重启后重新挂载,仍然后此文件

 

你可能感兴趣的:(linux驱动,nandflash,驱动,mtd)