nand flash 驱动编写笔记

介绍nand flash 很详细的网页地址http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html#idp1398800

最近学了学NAND FLASH 和NOR FLASH 驱动,挺简单的其实,学驱动程序主要我觉得分两步,第一步是了解这部分驱动的系统框架,第二步是弄懂要搞的硬件,然后根据框架把硬件加进去,以后学习的时候就按这个思路学。。。

在linux系统中,系统提供了MTD(memory technology device)系统,,对FLASH的 硬件进行了隔离,所以程序员在了解了MTD的框架之后,只需要把硬件相关的那部分驱动程序做了就行,

MTD原始设备的数据结构是mtd_info,

struct mtd_info
{
	u_char type;
	uint32_t flags;
	uint64_t size;	 // Total size of the MTD

	/* "Major" erase size for the device. Na茂ve users may take this
	 * to be the only erase size available, or may use the more detailed
	 * information below if they desire
	 */
	uint32_t erasesize;
	/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
	 * though individual bits can be cleared), in case of NAND flash it is
	 * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
	 * it is of ECC block size, etc. It is illegal to have writesize = 0.
	 * Any driver registering a struct mtd_info must ensure a writesize of
	 * 1 or larger.
	 */
	uint32_t writesize;

	uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
	uint32_t oobavail;  // Available OOB bytes per block

	/*
	 * If erasesize is a power of 2 then the shift is stored in
	 * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
	 */
	unsigned int erasesize_shift;
	unsigned int writesize_shift;
	/* Masks based on erasesize_shift and writesize_shift */
	unsigned int erasesize_mask;
	unsigned int writesize_mask;

	// Kernel-only stuff starts here.
	const char *name;
	int index;

	/* ecc layout structure pointer - read only ! */
	struct nand_ecclayout *ecclayout;

	/* Data for variable erase regions. If numeraseregions is zero,
	 * it means that the whole device has erasesize as given above.
	 */
	int numeraseregions;
	struct mtd_erase_region_info *eraseregions;

	/*
	 * Erase is an asynchronous operation.  Device drivers are supposed
	 * to call instr->callback() whenever the operation completes, even
	 * if it completes with a failure.
	 * Callers are supposed to pass a callback function and wait for it
	 * to be called before writing to the block.
	 */
	int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

	/* This stuff for eXecute-In-Place */
	/* phys is optional and may be set to NULL */
	int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
			size_t *retlen, void **virt, resource_size_t *phys);

	/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
	void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);

	/* Allow NOMMU mmap() to directly map the device (if not NULL)
	 * - return the address to which the offset maps
	 * - return -ENOSYS to indicate refusal to do the mapping
	 */
	unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
					    unsigned long len,
					    unsigned long offset,
					    unsigned long flags);

	/* Backing device capabilities for this device
	 * - provides mmap capabilities
	 */
	struct backing_dev_info *backing_dev_info;


	int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

	/* In blackbox flight recorder like scenarios we want to make successful
	   writes in interrupt context. panic_write() is only intended to be
	   called when its known the kernel is about to panic and we need the
	   write to succeed. Since the kernel is not going to be running for much
	   longer, this function can break locks and delay to ensure the write
	   succeeds (but not sleep). */

	int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

	int (*read_oob) (struct mtd_info *mtd, loff_t from,
			 struct mtd_oob_ops *ops);
	int (*write_oob) (struct mtd_info *mtd, loff_t to,
			 struct mtd_oob_ops *ops);

	/*
	 * Methods to access the protection register area, present in some
	 * flash devices. The user data is one time programmable but the
	 * factory data is read only.
	 */
	int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
	int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
	int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

	/* kvec-based read/write methods.
	   NB: The 'count' parameter is the number of _vectors_, each of
	   which contains an (ofs, len) tuple.
	*/
	int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);

	/* Sync */
	void (*sync) (struct mtd_info *mtd);

	/* Chip-supported device locking */
	int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
	int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);

	/* Power Management functions */
	int (*suspend) (struct mtd_info *mtd);
	void (*resume) (struct mtd_info *mtd);

	/* Bad block management functions */
	int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

	struct notifier_block reboot_notifier;  /* default mode before reboot */

	/* ECC status information */
	struct mtd_ecc_stats ecc_stats;
	/* Subpage shift (NAND) */
	int subpage_sft;

	void *priv;

	struct module *owner;
	struct device dev;
	int usecount;

	/* If the driver is something smart, like UBI, it may need to maintain
	 * its own reference counting. The below functions are only for driver.
	 * The driver may register its callbacks. These callbacks are not
	 * supposed to be called by MTD users */
	int (*get_device) (struct mtd_info *mtd);
	void (*put_device) (struct mtd_info *mtd);
}


每一个分区有一个mtd_info,编写nand flash主要做几步

/*1.分配一个nand_chip结构体
*2.设置
3.硬件相关的设置
4.使用: nand_scan扫描flash 信息,并初始化mtd_info结构体
5.add_mtd_partitions
*/

/*参考/drivers/mtd/nand/s3c2410.c*/

#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>


#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>

#include <asm/io.h>

#include <plat/regs-nand.h>
#include <plat/nand.h>

#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
#else
static int hardware_ecc = 0;
#endif

#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
static int clock_stop = 1;
#else
static const int clock_stop = 0;
#endif
struct 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   ;

};

 struct mtd_partition s3c_nand_part[] = {
	[0] = {
		.name	= "my_Board_uboot",
		.offset	= 0x00000000,
		.size	= 0x00040000,
	},
	[1] = {
		.name	= "my_Board_kernel",
		.offset	= 0x00200000,
		.size	= 0x00300000,
	},
	[2] = {
		.name	= "my_Board_yaffs2",
		.offset	= 0x00500000,
		.size	= MTDPART_SIZ_FULL,
	}
	
};

static struct nand_chip *s3c_nand;
static struct mtd_info *s3c_mtd;
static struct s3c_nand_regs *s3c_nand_regs;

static void s3c_select_chip(struct mtd_info *mtd, int chipnr)
{
if(chipnr==-1){
/*取消选中: NFCONT[1]设为1*/
s3c_nand_regs->nfcont |= (1<<1);

}
else{
/*选中: NFCONT[1]设为0*/
s3c_nand_regs->nfcont &= ~(1<<1);
}
}

static void s3c_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
	if(ctrl&NAND_CLE)
	{/*发命令:NFCMMD=dat*/
     s3c_nand_regs->nfcmd=dat;
    
	}
    else{
     /*发地址:NFCMMD=dat*/
     s3c_nand_regs->nfaddr=dat;

	}


}
static int s3c_nand_device_ready(struct mtd_info *mtd)
{
	/*0busy 1ready*/
	return  (s3c_nand_regs->nfstat&(1<<0));/*NFSTAT[0]的 值;*/
}
static int s3c_nand_init(void)
{
struct clock *clk;
/*1.分配一个nand_chip结构体
*2.设置
3.硬件相关的设置
4.使用: nand_scan
5.add_mtd_partitions
*/
s3c_nand=kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
/*设置nand_chip是给nand_scan函数使用的
如果不知道怎么使用,先看nand_scan怎么使用
它应该提供;选中,发命令,发地址,发数据
*/
s3c_nand_regs = ioremap(0x4E000000,sizeof(s3c_nand_regs));

s3c_mtd=kzalloc(sizeof(struct mtd_info), GFP_KERNEL);

s3c_mtd->priv = s3c_nand;
s3c_mtd->owner = THIS_MODULE;

s3c_nand->select_chip = s3c_select_chip;
s3c_nand->cmd_ctrl = s3c_nand_cmd_ctrl;
s3c_nand->IO_ADDR_R= &s3c_nand_regs->nfdata;
s3c_nand->IO_ADDR_W= &s3c_nand_regs->nfdata;
s3c_nand->dev_ready=s3c_nand_device_ready;
s3c_nand->ecc.mode = NAND_ECC_SOFT;
/*硬件设置之前先使能时钟*/
clk=clk_get(NULL,"nand");
clk_enable(clk); /*CLKCON bit[4]*/


/*
硬件相关设置:根据NAND FLASH的手册设置时间参数
HCLK=100MHZ
TACLS:发出CLE/ALE之后多长时间才发出nWE信号,从
      NAND 手册可知CLE/ALE与nWE可以同时发出,所以
      TACLS=0,
TWRPH0:nWE的脉冲宽度,HCLK*(TWRPH0+1),从nand 手册
可知它要>=12ns,所以TWRPH0>=1
TWRPH1:nWE变为高电平后多长时间cle/ale才能变为低电平
从nand 手册可知它要>=5ns,所以TWRPH1>=1



*/	
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0

s3c_nand_regs->nfconf=(TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/*
NFCONT:
bit1设为1,取消片选
bit0设为1,使能nand flash控制器
*/
s3c_nand_regs->nfcont=(1<<1)|(1<<0);


/*The flash ID is read and the mtd/chip structures are
 * filled with the appropriate values.
 * The mtd->owner field must be set to the module of the caller*/
nand_scan(s3c_mtd,1);/*识别nand flash 构造mtd_info*/
/*这个函数的作用是添加分区,添加到分区数组中*/
 add_mtd_partitions(s3c_mtd, s3c_nand_part, 3);
// add_mtd_device(s3c_mtd);
return 0;
}
static int s3c_nand_exit(void)
{
del_mtd_partitions(s3c_mtd);
kfree(s3c_mtd);
iounmap(s3c_nand_regs);
kfree(s3c_nand);

}



module_init(s3c_nand_init);
module_exit(s3c_nand_exit);

MODULE_LICENSE("GPL");



这是我看着韦东山的视频抄的程序,看源码把,我水平有限,也说不清


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值