博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于JZ2440的NAND FLASH的驱动程序的实现
阅读量:4195 次
发布时间:2019-05-26

本文共 9479 字,大约阅读时间需要 31 分钟。

本文的主要任务是实现JZ2440开发板板载的型号为K9F2G08U0A的容量为256MB、位宽为8位的NAND FLASH驱动程序的开发。

一、前期准备

开发板:JZ2440(ARM9)

内核版本:Linux-3.4.10

  NAND FLASH : K9F2G08U0A( 256MB   8bit   3.3V)

重新配置内核,去掉已有的NAND FLASH的驱动程序。

二、K9F2G08U0A大简单介绍

K9F2G08U0A三星电子设计的NAND FLASH类型的存储半导体,它的大小为256MB,位宽为8bit,支持ECC坏块检测机制。

1、原理图布局和基本引脚定义

各个引脚的具体定义:

        LDATA0~LDATA7:数据、地址、命令传输引脚,具体传输什么类型数据由ALE、CLE决定

        RnB :状态判断引脚,用它来判断flash是处于忙碌还是就绪状态

        CLE :命令锁存信号,为高电平时数据线上发送命令

        nFCE:片选信号,为低电平表示选中

        ALE:地址锁存引脚,为高电平时数据线上发送地址

        nFWE:写信号,为低电平时表示在数据线上写入

        nFRE:读信号,为低电平时表示从数据线上读取数据

2、存储单元基本布局

从上图可以看出,该NAND flash是按页来存储数据的,每一页的大小是2KB + 64Bytes,其中前面2KB是用来存放真正的数据的,后面的64Bytes数据作为ECC用来检测该页数据是否有效;每一个块包括64页数据。

三、驱动程序的实现

1、编写一个nand flash的驱动程序大致有如下步骤:

a、分配一个nand_chip的结构体变量

b、设置这个结构体的变量

c、执行硬件相关的操作

d、使用这个结构体变量,并添加设备分区

2、具体实现

2.1 基本工作,实现如下:

/* 定义一个结构体来描述s3c2440 nand 控制器的寄存器 */struct yl_s3c_nand_regs {	unsigned long nfconf  ;	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  ;};/* 定义一个结构体方便对nand驱动的操作 */struct yl_nand_mtd{	struct nand_chip *nand_chip;				// 定义一个nand_chip结构体的指针变量	struct mtd_info *mtd_info;					// 定义一个mtd_info结构体的指针变量	struct yl_s3c_nand_regs *s3c_nand_regs;		// 定义一个用来操作寄存器的结构体指针变量	struct clk *clk;							// 定义一个操作时钟的结构体指针变量};/* 定义一个 yl_nand_mtd 结构体的全局变量 */static struct yl_nand_mtd nand_mtd;
这部分的主要工作是为了方便整个驱动程序的开发,实现了硬件操作相关的前期准备工作,也定义了一个全局的结构体变量,方便驱动程序的编写。

2.2、硬件相关的操作

/* 0、硬件相关的操作 */	/* 0.1 映射寄存器的地址 */	nand_mtd.s3c_nand_regs = ioremap(0x4E000000, sizeof(struct yl_s3c_nand_regs));	if (!nand_mtd.s3c_nand_regs) {		printk("ioremap error!\n");		ret = -ENOMEM;		goto out1;	}	/* 0.2 打开nand控制器的时钟 */	nand_mtd.clk = clk_get(NULL, "nand");	if (IS_ERR(nand_mtd.clk)) {		printk("clk_get error!\n");		ret = -ENODEV;		goto out2;	}	clk_enable(nand_mtd.clk);	/* 0.3 设置nand控制器和nand芯片之间通信的基本时序要求 */	/* 设置时序要求 */#define tacls    0#define twrph0   1#define twrph1   0	nand_mtd.s3c_nand_regs->nfconf = (tacls<<12) | (twrph0<<8) | (twrph1<<4);	/* 设置nand控制器的状态: 取消片选,使能nand控制器 */	nand_mtd.s3c_nand_regs->nfcont = (1<<1) | (1<<0);
这部分主要是映射寄存器的地址、打开nand控制器的时钟,设置nand控制器的操作时序。

2.3、分配一个nand_chip的结构体指针变量

/* 1、分配一个nand_chip结构体变量 */	nand_mtd.nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);	if (!nand_mtd.nand_chip) {		printk("kzalloc for nand_chip error!\n");		ret = -ENOMEM;		goto out3;	}
2.4 设置上面分配的nand_chip结构体指针变量的成员
/* 2、设置这个结构体变量 */	nand_mtd.nand_chip->select_chip = yl_s3c_select_chip;		// 设置片选信号的函数	nand_mtd.nand_chip->cmd_ctrl    = yl_s3c_cmd_ctrl;			// 控制函数,来确定发送的数据是命令还是地址	nand_mtd.nand_chip->dev_ready   = yl_s3c_dev_ready;			// 判断芯片是否准备好	nand_mtd.nand_chip->IO_ADDR_R   = &nand_mtd.s3c_nand_regs->nfdata;	// 设置读数据的寄存器的地址	nand_mtd.nand_chip->IO_ADDR_W   = &nand_mtd.s3c_nand_regs->nfdata;	// 设置写数据的寄存器的地址	nand_mtd.nand_chip->ecc.mode	= NAND_ECC_SOFT;			// 设置ecc为软件校验模式
这部分主要实现了三个函数:yl_s3c_select_chip()设置片选,yl_s3c_cmd_ctrl()设置发送命令还是地址、yl_s3c_dev_ready()判断nand flash是否处于就绪状态,它们的具体实现如下:

设置片选:

/* 设置nand的片选函数 */static void yl_s3c_select_chip(struct mtd_info *mtd, int chip){	if (-1 == chip)	// 取消片选, nfcont寄存器bit[1] = 1	{		nand_mtd.s3c_nand_regs->nfcont |= (1<<1);			}	else	// 选中,nfcont寄存器bit[1] = 0	{		nand_mtd.s3c_nand_regs->nfcont &= ~(1<<1);	}}
设置发送命令还是地址:

/* 命令设置函数,确定发送的数据是命令还是地址 */static void yl_s3c_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl){	if (ctrl & NAND_CLE)	// 发命令	{		nand_mtd.s3c_nand_regs->nfcmd = dat;	}	else	// 发地址	{		nand_mtd.s3c_nand_regs->nfaddr = dat;	}}
判断nand flash是否处于就绪状态:
/* 判断nand flash所处的状态 */static int yl_s3c_dev_ready(struct mtd_info *mtd){	// nfstat寄存器bit[0] = 0 忙碌,bit[0] = 1 就绪	return (nand_mtd.s3c_nand_regs->nfstat & (1<<0));}
2.5 使用上面实现的nand_chip结构体变量

/* 3、使用这个结构体变量 */	/* 分配mtd_info,并把mtd_info与nand_chip挂钩起来 */	nand_mtd.mtd_info = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);	if (!nand_mtd.mtd_info) {		printk("kzalloc for mtd_info error!\n");		ret = -ENOMEM;		goto out4;	}	nand_mtd.mtd_info->owner = THIS_MODULE;	nand_mtd.mtd_info->priv  = nand_mtd.nand_chip;	/* 识别nand flash,构造mtd_info */	nand_scan(nand_mtd.mtd_info, 1);
2.6 添加分区

/* 4、添加分区 */	ret = mtd_device_parse_register(nand_mtd.mtd_info, NULL, NULL,		yl_s3c_nand_partitions, ARRAY_SIZE(yl_s3c_nand_partitions));
为这块nand flash添加分区,这需要实现一个分区表yl_s3c_nand_partitions,它的具体实现如下:
/* 定义nand flash的分区表 */static struct mtd_partition yl_s3c_nand_partitions[] = {	[0] = {		.name	= "bootloader",		.size	= SZ_256K,		.offset	= 0,	},	[1] = {		.name	= "params",		.offset = MTDPART_OFS_APPEND,		.size	= SZ_128K,	},	[2] = {		.name	= "kernel",		.offset = MTDPART_OFS_APPEND,		.size	= SZ_4M,	},	[3] = {		.name	= "rootfs",		.offset	= MTDPART_OFS_APPEND,		.size	= MTDPART_SIZ_FULL,	}};
至此,整个驱动的大致实现过程就说完了。

整个驱动的完整实现如下所示:

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 定义一个结构体来描述s3c2440 nand 控制器的寄存器 */struct yl_s3c_nand_regs { unsigned long nfconf ; 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 ;};/* 定义一个结构体方便对nand驱动的操作 */struct yl_nand_mtd{ struct nand_chip *nand_chip; // 定义一个nand_chip结构体的指针变量 struct mtd_info *mtd_info; // 定义一个mtd_info结构体的指针变量 struct yl_s3c_nand_regs *s3c_nand_regs; // 定义一个用来操作寄存器的结构体指针变量 struct clk *clk; // 定义一个操作时钟的结构体指针变量};/* 定义一个 yl_nand_mtd 结构体的全局变量 */static struct yl_nand_mtd nand_mtd;/* 定义nand flash的分区表 */static struct mtd_partition yl_s3c_nand_partitions[] = { [0] = { .name = "bootloader", .size = SZ_256K, .offset = 0, }, [1] = { .name = "params", .offset = MTDPART_OFS_APPEND, .size = SZ_128K, }, [2] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = SZ_4M, }, [3] = { .name = "rootfs", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, }};/* 设置nand的片选函数 */static void yl_s3c_select_chip(struct mtd_info *mtd, int chip){ if (-1 == chip) // 取消片选, nfcont寄存器bit[1] = 1 { nand_mtd.s3c_nand_regs->nfcont |= (1<<1); } else // 选中,nfcont寄存器bit[1] = 0 { nand_mtd.s3c_nand_regs->nfcont &= ~(1<<1); }}/* 命令设置函数,确定发送的数据是命令还是地址 */static void yl_s3c_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl){ if (ctrl & NAND_CLE) // 发命令 { nand_mtd.s3c_nand_regs->nfcmd = dat; } else // 发地址 { nand_mtd.s3c_nand_regs->nfaddr = dat; }}/* 判断nand flash所处的状态 */static int yl_s3c_dev_ready(struct mtd_info *mtd){ // nfstat寄存器bit[0] = 0 忙碌,bit[0] = 1 就绪 return (nand_mtd.s3c_nand_regs->nfstat & (1<<0));}/* 定义入口函数 */static int __init yl_s3c_nand_init(void){ int ret = 0; /* 0、硬件相关的操作 */ /* 0.1 映射寄存器的地址 */ nand_mtd.s3c_nand_regs = ioremap(0x4E000000, sizeof(struct yl_s3c_nand_regs)); if (!nand_mtd.s3c_nand_regs) { printk("ioremap error!\n"); ret = -ENOMEM; goto out1; } /* 0.2 打开nand控制器的时钟 */ nand_mtd.clk = clk_get(NULL, "nand"); if (IS_ERR(nand_mtd.clk)) { printk("clk_get error!\n"); ret = -ENODEV; goto out2; } clk_enable(nand_mtd.clk); /* 0.3 设置nand控制器和nand芯片之间通信的基本时序要求 */ /* 设置时序要求 */#define tacls 0#define twrph0 1#define twrph1 0 nand_mtd.s3c_nand_regs->nfconf = (tacls<<12) | (twrph0<<8) | (twrph1<<4); /* 设置nand控制器的状态: 取消片选,使能nand控制器 */ nand_mtd.s3c_nand_regs->nfcont = (1<<1) | (1<<0); /* 1、分配一个nand_chip结构体变量 */ nand_mtd.nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); if (!nand_mtd.nand_chip) { printk("kzalloc for nand_chip error!\n"); ret = -ENOMEM; goto out3; } /* 2、设置这个结构体变量 */ nand_mtd.nand_chip->select_chip = yl_s3c_select_chip; // 设置片选信号的函数 nand_mtd.nand_chip->cmd_ctrl = yl_s3c_cmd_ctrl; // 控制函数,来确定发送的数据是命令还是地址 nand_mtd.nand_chip->dev_ready = yl_s3c_dev_ready; // 判断芯片是否准备好 nand_mtd.nand_chip->IO_ADDR_R = &nand_mtd.s3c_nand_regs->nfdata; // 设置读数据的寄存器的地址 nand_mtd.nand_chip->IO_ADDR_W = &nand_mtd.s3c_nand_regs->nfdata; // 设置写数据的寄存器的地址 nand_mtd.nand_chip->ecc.mode = NAND_ECC_SOFT; // 设置ecc为软件校验模式 /* 3、使用这个结构体变量 */ /* 分配mtd_info,并把mtd_info与nand_chip挂钩起来 */ nand_mtd.mtd_info = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!nand_mtd.mtd_info) { printk("kzalloc for mtd_info error!\n"); ret = -ENOMEM; goto out4; } nand_mtd.mtd_info->owner = THIS_MODULE; nand_mtd.mtd_info->priv = nand_mtd.nand_chip; /* 识别nand flash,构造mtd_info */ nand_scan(nand_mtd.mtd_info, 1); /* 4、添加分区 */ ret = mtd_device_parse_register(nand_mtd.mtd_info, NULL, NULL, yl_s3c_nand_partitions, ARRAY_SIZE(yl_s3c_nand_partitions)); if(ret) // 失败 { printk("mtd_device_parse_register error!\n"); goto out5; } else // 成功 { return 0; }out5: kfree(nand_mtd.mtd_info); // 释放mtd_info分配的资源out4: kfree(nand_mtd.nand_chip); // 释放nand_chip分配的资源out3: clk_disable(nand_mtd.clk); // 失能时钟 clk_put(nand_mtd.clk); // 将时钟从内核时钟链表中移除out2: iounmap(nand_mtd.s3c_nand_regs); // 释放io映射的资源out1: return ret;}/* 定义出口函数 */static void __exit yl_s3c_nand_exit(void){ /* 进行出口的一些操作,释放资源,解除绑定,取消注册 */ mtd_device_unregister(nand_mtd.mtd_info); kfree(nand_mtd.mtd_info); kfree(nand_mtd.nand_chip); clk_disable(nand_mtd.clk); clk_put(nand_mtd.clk); iounmap(nand_mtd.s3c_nand_regs);}module_init(yl_s3c_nand_init);module_exit(yl_s3c_nand_exit);MODULE_LICENSE("GPL");
四、编译测试
对上面这个驱动进行编译,得到模块文件,在内核中加载这个模块:

在加载时会输出一下信息:

在设备目录下产生如下设备节点:

你可能感兴趣的文章
迈向工程硕士
查看>>
Flex国际化(I18N )
查看>>
软件需求变更管理
查看>>
CruiseControl持续集成
查看>>
Flex自定义事件
查看>>
Flex基础控件--Button
查看>>
Flex基础控件--ComboBox
查看>>
Flex基础控件--DateField
查看>>
大学计算机课程复习--计算机组成原理
查看>>
大学计算机课程复习--汇编语言
查看>>
大学计算机课程复习--操作系统
查看>>
大学计算机课程复习--数据库原理
查看>>
大学计算机课程复习--软件工程
查看>>
P2P畅想
查看>>
我的常州之旅
查看>>
QTP Flex测试
查看>>
RIATest 安装与破解
查看>>
Flex 字体使用
查看>>
RIATest Flex测试
查看>>
数据仓库术语
查看>>