嵌入式Linux驱动学习之路(二十三)NAND FLASH驱动程序

时间:2022-09-04 21:24:29

NAND FLASH是一个存储芯片。

在芯片上的DATA0~DATA7上既能传输数据也能传输地址。

  当ALE为高电平时传输的是地址。

  当CLE为高电平时传输的是命令。

  当ALE和CLE都为低电平时传输的是数据。

将数据发给nand Flash后,在发送第二次数据之前还要判断芯片是否处于空闲状态。一般是通过引脚RnB来判断,一般是高电平代表就绪,低电平代表正忙。

操作Nand Flash的一般步骤是:

  1. 发命令

    选中芯片

    CLE设置为高电平

    在DATA0~DATA7上输出命令值

    发出一个写脉冲

  2. 发地址

    选中芯片

    ALE为高电平

    在DATA0~DATA7上传输数据

    发出一个写脉冲

  3. 发数据

    选中芯片

    发出读脉冲

    读取DATA0~DATA7上的数据。

使用UBOOT来体验NAND FLASH的操作:

读ID

    选中        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

    退出读ID的状态      NFCMMD=0XFF      mw.b  0x4e000008  0xff

NAND FLASH驱动程序层次

看内核启动信息

S3C24XX NAND Driver, (c)  Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=, 30ns Twrph0= 70ns, Twrph1= 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB ,3V -bit)
Scanning device for bad blocks
Bad eraseblock at 0x02000000
Bad eraseblock at 0x02020000
Bad eraseblock at 0x027e0000
Bad eraseblock at 0x04bc0000
Bad eraseblock at 0x04c00000
Creating 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 (drivers\mtd\nand) s3c2410_nand_inithw
s3c2410_nand_init_chip
nand_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, );
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -);
*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在哪设置
// 答. drivers\mtd\mdblock.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 (drivers\mtd\mdblock.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

驱动程序代码:

/*
* drivers\mtd\nand\s3c2410.c
* drivers\mtd\nand\at91_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 nand_chip *s3c_nand;
struct mtd_info *s3c_mtd;
struct clk *clk;
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 nfeecc0;
unsigned long nfeecc1;
unsigned long nfsecc;
unsigned long nfsblk;
unsigned long nfeblk;
}; static struct mtd_partition s3c_nand_parts[] = {
[] = {
.name = "bootloader",
.size = 0x00040000,
.offset= ,
},
[] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = 0x00020000,
},
[] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
} }; static volatile struct s3c_nand_regs *nand_regs; static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr )
{
if(chipnr == - )
{
/* 取消选中 */
nand_regs->nfcont |= (<<);
}
else
{
/* 选中 NFCONT^1 设置为1 */
nand_regs->nfcont &= ~(<<);
}
} static void s3c2440_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ if (cmd == NAND_CMD_NONE)
return; if (ctrl & NAND_CLE)
{
/* 发命令 */
nand_regs->nfcmd = cmd;
}
else
{
/* 发地址 */
nand_regs->nfaddr = cmd;
}
} static int s3c2440_dev_ready(struct mtd_info *mtd)
{
/* */
return (nand_regs->nfstat & (<<));
} static int s3c_nand_init(void)
{
/* 分配一个nand_chip结构体 */
s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); nand_regs = ioremap(0x4e000000,sizeof(struct s3c_nand_regs)); clk = clk_get(NULL,"nand");
clk_enable(clk);
/* 设置 */
s3c_nand->select_chip = s3c2440_select_chip;
s3c_nand->cmd_ctrl = s3c2440_nand_cmd_ctrl;
s3c_nand->IO_ADDR_R = (void *)&nand_regs->nfdata;
s3c_nand->IO_ADDR_W = (void *)&nand_regs->nfdata;
s3c_nand->dev_ready = s3c2440_dev_ready;
s3c_nand->ecc.mode = NAND_ECC_SOFT; /* 硬件相关的操作:根据nand Flash的手册设置时间参数 */
/* HCLK = 100MHz */
/* TACLS: 发出cle/ale之后多长时间才发出nWE信号 */
/* TWRPHO: nWE的脉冲宽度 */
nand_regs->nfconf = (<<);
nand_regs->nfcont = 0x03; /* 使用: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,); /* 扫描识别 */ /* add_mtd_partitions */
add_mtd_partitions(s3c_mtd,s3c_nand_parts,); //add_mtd_device(s3c_mtd); //整个flash只有一个分区的话可以用这个
return ;
} static void s3c_nand_exit(void)
{
del_mtd_partitions(s3c_mtd);
kfree(s3c_mtd);
iounmap(nand_regs);
kfree(s3c_nand); } module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");

在添加这个内核模块的时候,首先卸载内核中的nand Flash驱动。

->Device Drivers

  ->Memory Technology Device (MTD) support

    ->NAND Device Support

sd

嵌入式Linux驱动学习之路(二十三)NAND FLASH驱动程序的更多相关文章

  1. 嵌入式Linux驱动学习之路&lpar;二十一&rpar;字符设备驱动程序总结和块设备驱动程序的引入

    字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申 ...

  2. 嵌入式Linux驱动学习之路&lpar;二十四&rpar;Nor Flash驱动程序

    Nor Flash和Nand Flash的不同: 类型 NOR Flash  Nand Flash  接口 RAM-like,引脚多 引脚少 容量 小(1M.2M...) 大(512M.1G) 读 简 ...

  3. 嵌入式Linux驱动学习之路&lpar;二十五&rpar;虚拟网卡驱动程序

    一.协议栈层次对比 设备无关层到驱动层的体系结构 1).网络协议接口层向网络层协议提供提供统一的数据包收发接口,不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过 ...

  4. 嵌入式Linux驱动学习之路&lpar;二十二&rpar;用内存模拟磁盘

    安装驱动后,可在/dev/目录下发现已经生成了相应的设备文件. 格式化设备:mkdosfs /dev/ramblock. 挂载设备. 读写设备 . 驱动程序代码: /***************** ...

  5. 嵌入式Linux驱动学习之路&lpar;二十&rpar;USB设备驱动

    USB在接入系统的时候,以0的设备ID和主机通信,然后由主机为其分配新的ID. 在主机端,D+和D-都是下拉接地的.而设备端的D-接上拉时,表明此设备为高速设备:12M/s. D+接上拉时则是全速设备 ...

  6. 嵌入式Linux驱动学习之路&lpar;二十七&rpar;字符设备驱动的另一种写法

    之前讲的字符设备驱动程序,只要有一个主设备号,那么次设备号无论是什么都会和同一个 struct file_operations 结构体对应. 而本节课讲的是如何在设备号相同的情况下,让不同的次设备号对 ...

  7. 嵌入式Linux驱动学习之路&lpar;二十六&rpar;DM9000C网卡驱动程序

    基于DM9000C的原厂代码修改dm9000c的驱动程序. 首先确认内存的基地址 iobase. 确定中断号码. 打开模块的初始化函数定义. 配置内存控制器的相应时序(结合DM9000C.C的手册). ...

  8. 嵌入式Linux驱动学习之路&lpar;二&rpar;u-boot体验

    u-boot工程简介 现在的u-boot支持PowerPC.ARM.X86.MIPS体系结构的上百种开发板,已经称为功能最多.灵活性最强,并且开发最积极的开源Bootloader.目前由DENX的Wo ...

  9. 嵌入式Linux驱动学习之路&lpar;一&rpar;嵌入式系统的软硬件架构

    硬件资料: 操作系统:(非虚拟机) zws@z-pc:~$ lsb_release -aNo LSB modules are available.Distributor ID: Ubuntu Desc ...

随机推荐

  1. Python之路【第七篇续】:进程、线程、协程

    Socket Server模块 SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端.即:每个客户端请求连接到服务器时 ...

  2. Linux(CentOS、Ububtu)一键安装Openstack及其它参考文档汇总

    原文链接  http://www.aboutyun.com/thread-10920-1-1.html openstack相关资料 CentOS下一键安装Openstack  http://blog. ...

  3. &lbrack;翻译&rsqb; java NIO Channel

    原文地址:http://tutorials.jenkov.com/java-nio/channels.html JAVA NIO channels和流的概念很像,下面是他们的一些区别: 你可以对cha ...

  4. Effective Java 阅读笔记——枚举和注解

    30:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 31:用实例域代替序数 应该给enum添加i ...

  5. javascript小白学习指南0---1

    引言: 做为一名程序猿.都是真心的想把自己的东西分享出来,供大家一起学习探讨.一起提高技能.一起涨工资,呵   这一系列的文章都是关于Javascript 基础的 当然文章其中穿插了些我自己的理解.希 ...

  6. VC学习笔记&colon; 1&period; Window程序内部运行机制

    0. 内容结构 API与SDK 窗口与句柄 消息与消息队列 WinMain函数 1. API与SDK 这里 API是指由Windows操作系统提供给应用程序的编程接口: Windows系统提供的API ...

  7. Log4Net &period;NET log处理

    1.NuGet 安装Log4Net. 2.新建一个Common的project,并且添加一个LogWriter的类: public class LogWriter { //Error log publ ...

  8. Power BI免费版&lpar;Free&rpar;,专业版&lpar;Pro&rpar;以及增值版&lpar;Premium&rpar;授权功能对比, Server

    Features of Power BI Report Server and the Power BI service Features Power BI Report Server Power BI ...

  9. JS合并数组的几种方法及优劣比较

    本文属于JavaScript的基础技能. 我们将学习结合/合并两个JS数组的各种常用方法,并比较各种方法的优缺点. 我们先来看看具体的场景: var q = [ 5, 5, 1, 9, 9, 6, 4 ...

  10. Effective Java 第三版——48&period; 谨慎使用流并行

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...