NAND FLASH 原理以及操作详见:https://blog.csdn.net/qq_16933601/article/details/100001443
一、基本的问题
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的数据
二、NAND FLASH驱动程序层次分析
1.S3c2410.c
从init函数开始看起
代码语言:javascript复制static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronicsn");
platform_driver_register(&s3c2412_nand_driver);//注册平台
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver);
}
看下s3c2440_nand_driver这个平台有哪些内容
代码语言:javascript复制static struct platform_driver s3c2440_nand_driver = {
.probe = s3c2440_nand_probe,//一如既往的probe函数。是:调用probe函数
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2440-nand",//是否同名设备
.owner = THIS_MODULE,
},
};
当与nand flash设备匹配时,就会调用s3c2440_nand_driver->probe()来初始化
2.s3c2440_nand_probe
代码语言:javascript复制static int s3c24xx_nand_probe(struct platform_device *pdev,
enum s3c_cpu_type cpu_type)
{
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
struct s3c2410_nand_info *info;//s3c2410 nand flash状态结构体 包括 nand 时钟 nand控制器等
struct s3c2410_nand_mtd *nmtd;//控制器结构体和mtd的信息
struct s3c2410_nand_set *sets;//不同的mtd注册的nand 芯片
struct resource *res;
int err = 0;
int size;
int nr_sets;
int setno;
.................................
err = s3c2410_nand_inithw(info, pdev); //初始化硬件hardware,设置TACLS、TWRPH0、TWRPH1通信时序等
s3c2410_nand_init_chip(info, nmtd, sets);//初始化芯片
nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);//扫描nand flash
if (nmtd->scan_res == 0) {
s3c2410_nand_add_partition(info, nmtd, sets);//添加分区mtd分区
}
通过上面代码和注释,得出:驱动主要调用内核的nand_scan()函数,add_mtd_partitions()函数,来完成注册nand flash 关于MTD设备的解释https://blog.csdn.net/u013562393/article/details/50868033
3.mtd_info也可以看下
代码语言:javascript复制struct mtd_info {
u_char type; /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */
uint32_t flags; /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */
uint64_t size; /* mtd设备的大小 */
uint32_t erasesize; /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
uint32_t writesize; /* 写大小, 对于norFlash是字节,对nandFlash为一页 */
uint32_t oobsize; /* OOB字节数 */
uint32_t oobavail; /* 可用的OOB字节数 */
unsigned int erasesize_shift; /* 默认为0,不重要 */
unsigned int writesize_shift; /* 默认为0,不重要 */
unsigned int erasesize_mask; /* 默认为1,不重要 */
unsigned int writesize_mask; /* 默认为1,不重要 */
const char *name; /* 名字, 不重要*/
int index; /* 索引号,不重要 */
int numeraseregions; /* 通常为1 */
struct mtd_erase_region_info *eraseregions; /* 可变擦除区域 */
void *priv; /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
struct module *owner; /* 一般设置为THIS_MODULE */
/* 擦除函数 */
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
/* 读写flash函数 */
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);
/* 带oob读写Flash函数 */
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);
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);
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
/* 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);
/* 电源管理函数 */
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);
/* 坏块管理函数 */
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags);
struct backing_dev_info *backing_dev_info;
struct notifier_block reboot_notifier; /* default mode before reboot */
/* ECC status information */
struct mtd_ecc_stats ecc_stats;
int subpage_sft;
struct device dev;
int usecount;
int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
};
4.s3c2410_nand_add_partition
代码语言:javascript复制static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *mtd,
struct s3c2410_nand_set *set)
{
if (set == NULL)
return add_mtd_device(&mtd->mtd);//添加分区
if (set->nr_partitions > 0 && set->partitions != NULL) {
return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
}
5.nand_scan
代码语言:javascript复制int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
/* Many callers got this wrong, so check for it for a while... */
if (!mtd->owner && caller_is_module()) {
printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!n");
BUG();
}
ret = nand_scan_ident(mtd, maxchips);//识别
if (!ret)
ret = nand_scan_tail(mtd);//尾部
return ret;
}
nand_scan会调用nand_scan()->nand_scan_ident()->nand_get_flash_type()来获取flash存储器的类型 以及nand_scan()->nand_scan_ident()->nand_tail()来构造mtd设备的成员(实现对nand flash的读,写,擦除等)
6.nand_scan_ident
代码语言:javascript复制int nand_scan_ident(struct mtd_info *mtd, int maxchips)
{
int i, busw, nand_maf_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
nand_set_defaults(chip, busw);//设置默认函数
/* Read the flash type */
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);//获得flash类型
7.nand_get_flash_type
代码语言:javascript复制static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int busw, int *maf_id)
{
struct nand_flash_dev *type = NULL;
int i, dev_id, maf_idx;
/* Select the device */
chip->select_chip(mtd, 0);调用nand_chip结构体的成员select_chip使能flash片选
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//发出命令读ID NAND_CMD_READID 90 最后数据保存在mtd结构体里
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);//读厂家ID
dev_id = chip->read_byte(mtd);//读设备ID
/* Lookup the flash id */
//for循环匹配nand_flash_ids[]数组,找到对应的nandflash设备ID信息
for (i = 0; nand_flash_ids[i].name != NULL; i ) {
if (dev_id == nand_flash_ids[i].id) {
type = &nand_flash_ids[i];
break;
}
}
从上面代码和注释得出,nand_chip结构体就是保存与硬件相关的函数(后面会讲这个结构体) nand_flash_ids数组 如下图所示,在芯片手册中,看到nand flash的设备ID=0xDA
代码语言:javascript复制struct nand_flash_dev nand_flash_ids[] = {
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
{"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
{"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
{"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
{"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
{"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
{"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
{"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
{"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
................................
/* 2 Gigabit */
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},//与手册信息相同
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
8.add_mtd_partitions
代码语言:javascript复制//函数成员介绍:
//master:就是要创建的mtd设备
//parts:分区信息的数组,它的结构体是mtd_partition,该结构体如下所示:
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
u_int32_t cur_offset = 0;
int i;
/* register our partition */
add_mtd_device(&slave->mtd);
/*
struct mtd_partition {
char *name; //分区名
u_int32_t size; //分区大小
u_int32_t offset; //分区所在的偏移值
u_int32_t mask_flags; //掩码标志
struct nand_ecclayout *ecclayout; //OOB布局
struct mtd_info **mtdp; //MTD的指针,不常用
};
}
*/
9.add_mtd_device
代码语言:javascript复制//创建一个mtd设备
int add_mtd_device(struct mtd_info *mtd)
{
int i;
BUG_ON(mtd->writesize == 0);
mutex_lock(&mtd_table_mutex);
for (i=0; i < MAX_MTD_DEVICES; i )
if (!mtd_table[i]) {
struct list_head *this;
.......................................
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
//找mtd_notifiers链表里的list_head结构体
list_for_each(this, &mtd_notifiers) {
通过list_head找到struct mtd_notifier *not
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
//最后调用mtd_notifier的add()函数
not->add(mtd);
}
10.mtd_notifiers链表
代码语言:javascript复制void register_mtd_user (struct mtd_notifier *new)
{
int i;
mutex_lock(&mtd_table_mutex);
list_add(&new->list, &mtd_notifiers);//注册mtd_notifiers
__module_get(THIS_MODULE);
for (i=0; i< MAX_MTD_DEVICES; i )
if (mtd_table[i])
new->add(mtd_table[i]);
mutex_unlock(&mtd_table_mutex);
}
11.register_mtd_user
list_head在register_mtd_user()里放到mtd_notifiers链表中
代码语言:javascript复制static int __init init_mtdchar(void)
{
//创建字符设备mtd,主设备号为90,cat /proc/devices 可以看到
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
mtd_class = class_create(THIS_MODULE, "mtd");//创建类
if (IS_ERR(mtd_class)) {
printk(KERN_ERR "Error creating mtd class.n");
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
return PTR_ERR(mtd_class);
}
register_mtd_user(¬ifier);//将notifier添加到mtd_notifiers链表中
return 0;
}
之所以上面没有创建设备节点,是因为此时没有nand flash驱动
12.notifier结构体
代码语言:javascript复制static struct mtd_notifier notifier = {
.add = mtd_notify_add,
.remove = mtd_notify_remove,
};
13.mtd_nofify_add
代码语言:javascript复制static void mtd_notify_add(struct mtd_info* mtd)
{
if (!mtd)
return;
/* 其中MTD_CHAR_MAJOR主设备定义为90 */
class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
NULL, "mtd%d", mtd->index);//创建mtd%d字符设备节点
class_device_create(mtd_class, NULL,
MKDEV(MTD_CHAR_MAJOR, mtd->index*2 1),
NULL, "mtd%dro", mtd->index);//创建mtd%dro字符设备节点
}
mtdchar.c的入口函数将notifie添加到mtd_notifiers链表中。
然后在add_mtd_device()函数中,当查找到mtd字符设备的list_head时,就调用mtd_notifiers->add()来创建两个字符设备(mtd%d,mtd%dro)
14.先看字符设备的mtd_notify_add
代码语言:javascript复制static void mtd_notify_add(struct mtd_info* mtd)
{
if (!mtd)
return;
class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
NULL, "mtd%d", mtd->index);
class_device_create(mtd_class, NULL,
MKDEV(MTD_CHAR_MAJOR, mtd->index*2 1),
NULL, "mtd%dro", mtd->index);
}
15.再看块设备的blktrans_notify_add
代码语言:javascript复制static void blktrans_notify_add(struct mtd_info *mtd)
{
struct list_head *this;
if (mtd->type == MTD_ABSENT)
return;
//链表里面的每个成员调用 add
list_for_each(this, &blktrans_majors) {
struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
tr->add_mtd(tr, mtd);
}
}
15.搜索blktrans_majors链表,看看mtd_blktrans_ops结构体在哪里添加进去的找到该链表在register_mtd_blktrans()函数中:
代码语言:javascript复制int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
... ...
ret = register_blkdev(tr->major, tr->name);//注册块设备
... ...
tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);//分配一个请求队列
... ...
list_add(&tr->list, &blktrans_majors);//将tr->list添加到blktrans_majors链表
... ...
}
调用register_mtd_blktrans
16.mtdblock.c
代码语言:javascript复制static struct mtd_blktrans_ops mtdblock_tr = {
.name = "mtdblock",
.major = 31,
.part_bits = 0,
.blksize = 512,
.open = mtdblock_open,
.flush = mtdblock_flush,
.release = mtdblock_release,
.readsect = mtdblock_readsect,
.writesect = mtdblock_writesect,
.add_mtd = mtdblock_add_mtd,
.remove_dev = mtdblock_remove_dev,
.owner = THIS_MODULE,
};
找到执行mtd_blktrans_ops结构体的add_mtd()函数,就是上图的mtdblock_add_mtd()函数 在mtdblock_add_mtd()函数中最终会调用add_blktrans_dev()
17.add_mtd_blktrans_dev()函数如下所示:
代码语言:javascript复制int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{
... ...
gd = alloc_disk(1 << tr->part_bits);//分配一个gendisk结构体
... ...
gd->major = tr->major;//设置gendisk的主设备号
gd->first_minor = (new->devnum) << tr->part_bits;//设置gendisk的起始此设备号
gd->fops = &mtd_blktrans_ops;//设置操作函数
... ...
gd->queue = tr->blkcore_priv->rq;//设置请求队列
... ...
add_disk(gd);//向内核注册gendisk结构体
return 0;
}
18.mtd_blkdevs.c
代码语言:javascript复制 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
mtd_blkdevs()块设备的入口函数 将 blktrans_notifier添加到mtd_notifiers链表中,并创建设备,请求队列。
然后在add_mtd_device()函数中,当查找到有blktrans_notifier时,就调用blktrans_notifier->add()来分配设置注册结构体
19.总结
显然在内核中,mtd已经帮我们做了整个框架,而我们的nand flash驱动只需要以下几步即可:
1)设置mtd_info结构体成员
2)设置nand_chip结构体成员
3)设置硬件相关(设置nand控制器时序等)
4)通过nand_scan()来扫描nand flash
5)通过add_mtd_partitions()来添加分区,创建MTD字符/块设备
三、写程序
1.参考内核的部分函数
2.参考手册内容
2440手册
nand手册
3.完整驱动源码
代码语言:javascript复制/* 参考
* driversmtdnands3c2410.c
* driversmtdnandat91_nand.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/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>
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 ;
};
static struct nand_chip *s3c_nand;
static struct mtd_info *s3c_mtd;
static struct s3c_nand_regs *s3c_nand_regs;
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,//剩下的所有
}
};
static void (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 s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
if (ctrl & NAND_CLE)
{
/* 发命令: NFCMMD=dat */
s3c_nand_regs->nfcmd = dat;
}
else
{
/* 发地址: NFADDR=dat */
s3c_nand_regs->nfaddr = dat;
}
}
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
return (s3c_nand_regs->nfstat & (1<<0));
}
static int s3c_nand_init(void)
{
struct clk *clk;
/* 1. 分配一个nand_chip结构体 */
s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
/* 2. 设置nand_chip */
/* 设置nand_chip是给nand_scan函数使用的, 如果不知道怎么设置, 先看nand_scan怎么使用
* 它应该提供:选中,发命令,发地址,发数据,读数据,判断状态的功能
*/
s3c_nand->select_chip = s3c2440_select_chip;
s3c_nand->cmd_ctrl = s3c2440_cmd_ctrl;
s3c_nand->IO_ADDR_R = &s3c_nand_regs->nfdata;
s3c_nand->IO_ADDR_W = &s3c_nand_regs->nfdata;
s3c_nand->dev_ready = s3c2440_dev_ready;
s3c_nand->ecc.mode = NAND_ECC_SOFT;
/* 3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 */
/* 使能NAND FLASH控制器的时钟 */
clk = clk_get(NULL, "nand");
clk_enable(clk); /* CLKCON'bit[4] */
/* HCLK=100MHz
* TACLS: 发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
* TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1
* TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0
*/
#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);
/* 4. 使用: nand_scan */
s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
s3c_mtd->owner = THIS_MODULE;
s3c_mtd->priv = s3c_nand;
nand_scan(s3c_mtd, 1); /* 识别NAND FLASH, 构造mtd_info */
/* 5. add_mtd_partitions */
add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
//add_mtd_device(s3c_mtd);
return 0;
}
static void 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");