首页 / 操作系统 / Linux / Nand Flash驱动程序编写
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的数据用UBOOT来体验NAND FLASH的操作:1. 读ID S3C2440 u-boot选中 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 2. 读内容: 读0地址的数据使用UBOOT命令:nand dump 0Page 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 0xffNAND FLASH驱动程序层次看内核启动信息S3C24XX NAND Driver, (c) 2004 Simtec Electronicss3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30nsNAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)Scanning device for bad blocksBad eraseblock 256 at 0x02000000Bad eraseblock 257 at 0x02020000Bad eraseblock 319 at 0x027e0000Bad eraseblock 606 at 0x04bc0000Bad eraseblock 608 at 0x04c00000Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":0x00000000-0x00040000 : "bootloader"0x00040000-0x00060000 : "params"0x00060000-0x00260000 : "kernel"0x00260000-0x10000000 : "root"搜"S3C24XX NAND Driver"S3c2410.c (driversmtd
and)s3c2410_nand_inithws3c2410_nand_init_chipnand_scan // drivers/mtd/nand/nand_base.c 根据nand_chip的底层操作函数识别NAND FLASH,构造mtd_info nand_scan_ident nand_set_defaults if (!chip->select_chip) chip->select_chip = nand_select_chip; // 默认值不适用 if (chip->cmdfunc == NULL) chip->cmdfunc = nand_command; chip->cmd_ctrl(mtd, command, ctrl); if (!chip->read_byte) chip->read_byte = nand_read_byte; readb(chip->IO_ADDR_R); if (chip->waitfunc == NULL) chip->waitfunc = nand_wait; chip->dev_ready nand_get_flash_type chip->select_chip(mtd, 0); chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); *maf_id = chip->read_byte(mtd); dev_id = chip->read_byte(mtd); nand_scan_tail mtd->erase = nand_erase; mtd->read = nand_read; mtd->write = nand_write; s3c2410_nand_add_partition add_mtd_partitions add_mtd_device list_for_each(this, &mtd_notifiers) { // 问. mtd_notifiers在哪设置 // 答. drivers/mtd/mtdchar.c,mtd_blkdev.c调用register_mtd_user struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); not->add(mtd); // mtd_notify_add 和 blktrans_notify_add 先看字符设备的mtd_notify_add class_device_create class_device_create 再看块设备的blktrans_notify_add list_for_each(this, &blktrans_majors) { // 问. blktrans_majors在哪设置 // 答. driversmtdmdblock.c或mtdblock_ro.c register_mtd_blktrans struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); tr->add_mtd(tr, mtd); mtdblock_add_mtd (driversmtdmdblock.c) add_mtd_blktrans_dev 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 测试4th:1. make menuconfig去掉内核自带的NAND FLASH驱动-> Device Drivers -> Memory Technology Device (MTD) support -> NAND Device Support < > NAND Flash support for S3C2410/S3C2440 SoC2. make uImage 使用新内核启动, 并且使用NFS作为根文件系统3. insmod s3c_nand.ko4. 格式化 (参考下面编译工具) flash_eraseall /dev/mtd3 // yaffs 5. 挂接 mount -t yaffs /dev/mtdblock3 /mnt6. 在/mnt目录下建文件 编译工具:1. tar xjf mtd-utils-05.07.23.tar.bz22. cd mtd-utils-05.07.23/util修改Makefile:#CROSS=arm-linux-改为CROSS=arm-linux-3. make4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/<br><br><br>
复制代码
struct nand_chip {
/*8 位NAND 芯片的读写地址*/
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
/*读取数据并验证*/
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
/*选定芯片*/
void (*select_chip)(struct mtd_info *mtd, int chip);
/*检查坏块*/
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
/*mark(标记)bad block*/
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
/*发送命令/地址*/
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
/*读取芯片状态*/
int (*dev_ready)(struct mtd_info *mtd);
/*发送命令*/
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
/*等待就绪*/
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
/*擦除命令函数*/
void (*erase_cmd)(struct mtd_info *mtd, int page);
/*扫描坏块表*/
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
/*高级页面写功能*/
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);int chip_delay;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
...................
};
struct nand_ecc_ctrl {
nand_ecc_modes_t mode;
int steps;
int size;
int bytes;
int total;
int prepad;
int postpad;
struct nand_ecclayout *layout;/*hwctl函数:
*这个函数用来控制硬件产生ecc
*其实它主要的工作就是控制NAND controller 向 NAND 芯片发出NAND_ECC_READ 、NAND_ECC_WRITE 和NAND_ECC_READSYN *等命令,与struct nand_chip 结构体中的cmdfunc 类似
*/
void (*hwctl)(struct mtd_info *mtd, int mode);/*根据data 计算ecc 值*/
int (*calculate)(struct mtd_info *mtd,const uint8_t *dat,uint8_t *ecc_code);
/*根据ecc 值,判断读写数据时是否有错误发生,若有错,则立即试着纠正,纠正失败则返回错误*/
int (*correct)(struct mtd_info *mtd, uint8_t *dat,uint8_t *read_ecc,uint8_t *calc_ecc);
/*read_page_raw write_page_raw函数
*从NAND 芯片中读取一个page 的原始数据和向NAND 芯片写入一个page 的原始数据,所谓的原始数据,即不对读写的数据做ecc处理 *该读写什么值就读写什么值。另外,这两个函数会读写整个page 中的所有内容,即不但会读写一个page 中MAIN部分,还会读写OOB 部分。
*/
int (*read_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);
void (*write_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);/*
*read_page 和write_page 在读写过程中会加入ecc 的计算,校验,和纠正等处理。
*/
int (*read_page)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);
void (*write_page)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);/*
*读写oob 中的内容,不包括MAIN 部分。
*/
int (*read_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page,int sndcmd);
int (*write_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page);
};
其实,以上提到的这几个read_xxx 和write_xxx 函数,最终都会调用struct nand_chip 中的read_buf 和write_buf 这两个函数,所以如果没有特殊需求的话,我认为不必自己实现,使用MTD 提供的default 的函数即可。--------------------------------------------------------#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_chip;
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 int s3c_dev_ready(struct mtd_info *mtd)
{
/*返回"NFSTAT的bit[0]";*/
return (s3c_nand_regs->nfstat & (1<<0));
}static void s3c_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl)
{ if (ctrl & NAND_CLE)
{
/* 发命令: NFCMMD=dat */
s3c_nand_regs->nfcmd = dat;
//writeb(cmd, host->io_base + (1 << host->board->cle));/*命令*/
}
else
{
/* 发地址: NFADDR=dat */
s3c_nand_regs->nfaddr = dat;
//writeb(cmd, host->io_base + (1 << host->board->ale));/*地址*/
}
}
static void s3c_select_chip(struct mtd_info *mtd, int chip)
{
if(chip ==-1)
{
/*表示取消选中 NFCONT[1]设为1 */
s3c_nand_regs->nfcont |=(1<<1);
}
else
{
s3c_nand_regs->nfcont &=~(1<<1);
/*选中芯片 NFCONT[1]设为0 */
}
}
static int s3c_nand_init(void)
{ struct clk *clk;
/*1.分配一个nand_chip结构体*/
s3c_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
/*2.设置nandc_chip结构体*/
s3c_nand_chip->IO_ADDR_R = &s3c_nand_regs->nfdata;
s3c_nand_chip->IO_ADDR_W = &s3c_nand_regs->nfdata;
s3c_nand_chip->cmd_ctrl = s3c_cmd_ctrl;
s3c_nand_chip->dev_ready = s3c_dev_ready;
s3c_nand_chip->select_chip = s3c_select_chip;
s3c_nand_chip->ecc.mode = NAND_ECC_SOFT;
//s3c_nand_chip->chip_delay = 20; /* 使能NAND FLASH控制器的时钟 */
clk = clk_get(NULL, "nand");
clk_enable(clk); /* CLKCON"bit[4] */
/*初始化nand控制器 设置寄存器*/
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
/* 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
*/
s3c_nand_regs->nfconf=(TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* NFCONT:
* BIT1-设为1, 取消片选
* BIT0-设为1, 使能NAND FLASH控制器
* BIT4-设为0, 未初始化hardware ECC
*/
s3c_nand_regs->nfcont = (1<<1) | (1<<0);
/*分配一个mtd_info结构体*/
s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); /*设置s3c_mtd结构体*/
s3c_mtd->owner = THIS_MODULE;
s3c_mtd->priv = s3c_nand_chip; /* 识别NAND FLASH, 构造mtd_info */
nand_scan(s3c_mtd, 1); /*设置分区表*/
add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
add_mtd_device(s3c_mtd);
return 0;
}static void s3c_nand_exit(void)
{
kfree(s3c_mtd);
kfree(s3c_nand_chip);
iounmap(s3c_nand_regs);
}module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-12/138152.htm