实验描述:
nand flash驱动程序
内核版本:
Linux 2.6.38
开发板:
Mini 6410
如何确定参数时间的大小:
s3c6410手册:
nand flash手册:
程序实现:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#define PRINTK printk
//#define PRINTK(...)
struct s3c6410_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 nfsblk ;
unsigned long nfeblk ;
unsigned long nfstat ;
unsigned long nfeccrr0 ;
unsigned long nfeccrr1 ;
unsigned long nfmecc0 ;
unsigned long nfmecc1 ;
unsigned long nfsecc ;
unsigned long nfmlcbitpt;
unsigned long nf8eccerr0;
unsigned long nf8eccerr1;
unsigned long nf8eccerr2;
unsigned long nfm8ecc0 ;
unsigned long nfm8ecc1 ;
unsigned long nfm8ecc2 ;
unsigned long nfm8ecc3 ;
unsigned long nfmlc8bitpt0;
unsigned long nfmlc8bitpt1;
};
static struct nand_chip *s3c6410_nand_chips;
static struct s3c6410_nand_regs *s3c6410_nand_regs;
static struct clk *nand_clk;
static struct mtd_info *s3c6410_mtd_info;
static struct mtd_partition s3c6410_nand_parts[] = {
[0] = {
.name = "bootloader",
.size = 0x000000080000,
.offset = 0,
},
[1] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x000000500000,
},
[2] = {
.name = "file system",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
void nand_select_chip(struct mtd_info *mtd, int chip)
{
if (chip == -1)
{
s3c6410_nand_regs->nfcont |= (1<<1); //cancle select
PRINTK(KERN_ALERT"cancle select!\n");
}
else
{
s3c6410_nand_regs->nfcont &= ~(1<<1); //enable select
PRINTK(KERN_ALERT"enable select!\n");
}
}
void nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
if (ctrl & NAND_CLE)
{
s3c6410_nand_regs->nfcmd = dat; //send command
PRINTK(KERN_ALERT"send command\n");
}
else
{
s3c6410_nand_regs->nfaddr = dat; //send address
PRINTK(KERN_ALERT"send address\n");
}
}
int nand_dev_ready(struct mtd_info *mtd)
{
PRINTK(KERN_ALERT"read:%d\n", s3c6410_nand_regs->nfstat & (1<<0));
return (s3c6410_nand_regs->nfstat & (1<<0));
}
static int nand_driver_init(void)
{
int err;
s3c6410_nand_chips = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
if(!s3c6410_nand_chips){
err = -ENOMEM;
PRINTK(KERN_ALERT"s3c6410_nand_chips kzalloc busy!\n");
goto out_err;
}
s3c6410_nand_regs = ioremap(0x70200000, sizeof(struct s3c6410_nand_regs));
if(!s3c6410_nand_regs){
err = -EIO;
PRINTK(KERN_ALERT"ioreamp to s3c6410_nand_regs fail!\n");
goto out_err;
}
s3c6410_nand_chips->select_chip = nand_select_chip;
s3c6410_nand_chips->cmd_ctrl = nand_cmd_ctrl;
s3c6410_nand_chips->IO_ADDR_R = &s3c6410_nand_regs->nfdata;
s3c6410_nand_chips->IO_ADDR_W = &s3c6410_nand_regs->nfdata;
s3c6410_nand_chips->dev_ready = nand_dev_ready;
s3c6410_nand_chips->ecc.mode = NAND_ECC_SOFT;
nand_clk = clk_get(NULL, "nand"); //we can also use 6410 guide to set clk
clk_enable(nand_clk);
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
//3.3V, As tclh>=5ns,twp>=12ns,tcls>=12ns, HCLK=10ns, so TACLS=0, TWRPH0=3(1/400)
s3c6410_nand_regs->nfconf = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);
s3c6410_nand_regs->nfcont = (1 << 0) | (1 << 1); //select and enable
s3c6410_mtd_info = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
if(!s3c6410_mtd_info){
err = -ENOMEM;
PRINTK(KERN_ALERT"s3c6410_mtd_info kzalloc busy!\n");
goto out_err;
}
s3c6410_mtd_info->owner = THIS_MODULE;
s3c6410_mtd_info->priv =s3c6410_nand_chips;
if(nand_scan(s3c6410_mtd_info, 1)){//scan nand flash to produce mtd_info
err = -ENXIO;
goto out_err;
}
add_mtd_partitions(s3c6410_mtd_info, s3c6410_nand_parts, 3);
PRINTK(KERN_ALERT"init!\n");
return 0;
out_err:
if(s3c6410_mtd_info)
kfree(s3c6410_mtd_info);
if(s3c6410_nand_regs)
iounmap(s3c6410_nand_regs);
if(s3c6410_nand_chips)
kfree(s3c6410_nand_chips);
return err;
}
static int nand_driver_exit(void)
{
del_mtd_partitions(s3c6410_mtd_info);
if(s3c6410_mtd_info)
kfree(s3c6410_mtd_info);
if(s3c6410_nand_regs)
iounmap(s3c6410_nand_regs);
if(s3c6410_nand_chips)
kfree(s3c6410_nand_chips);
PRINTK(KERN_ALERT"exit!\n");
return 0;
}
module_init(nand_driver_init);
module_exit(nand_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("S3C6410 nand flash driver");
MODULE_AUTHOR("Books, <uppour@sina.cn>");