1 Nand Flash

  原理图。

  引脚:

引脚 功能
IO0~IO7 数据输入输出
CLE 命令锁存
ALE 地址锁存
nCE 芯片使能
nRE 读使能
nWE 写使能
R/nB 就绪/忙

  操作nand flash基本流程,传输命令–>地址–>数据,以读操作为例,时序图:

  读操作主要有以下步骤:
  1)选中芯片(NFCONT寄存器第1位)
  2)清除RnB(NFSTAT寄存器第4位)
  3)发出命令0x00(NFCMMD寄存器)
  4)发出列地址(2次)(NFADDR)
  5)发出页(行)地址(3次)(NFADDR)
  6)发出命令0x30(NFCMMD寄存器)
  7)等待RnB(NFSTAT寄存器第0位)
  8)读数据(NFDATA寄存器)
  9)取消片选(NFCONT寄存器第1位)

2 框架

2.1 框架

2.2 设备

  在内核移植中,建立了mtd_partition结构体,描述了内核分区。在mini2440_machine_init中,将smdk2410_devices信息注册到platform。

static struct mtd_partition mini2440_default_nand_part[] = {[0] = {.name   = "supervivi",.size  = 0x00040000,.offset   = 0,},[1] = {.name    = "param",.offset = 0x00040000,.size    = 0x00020000,},[2] = {.name   = "Kernel",.offset = 0x00060000,.size   = 0x00500000,},[3] = {.name   = "root",.offset = 0x00560000,.size = 1024 * 1024 * 1024, //},[4] = {.name    = "nand",.offset = 0x00000000,.size = 1024 * 1024 * 1024, //}
};

2.3 驱动

  在drivers/mtd/nand/s3c2410.c中,s3c2410_nand_init注册platform驱动s3c24xx_nand_driver,所以,当与设备相匹配时,会调用probe函数。

static int s3c24xx_nand_probe(struct platform_device *pdev)
{...err = s3c2410_nand_inithw(info);//初始化硬件...for (setno = 0; setno < nr_sets; setno++, nmtd++) {s3c2410_nand_init_chip(info, nmtd, sets);//初始化芯片nmtd->scan_res = nand_scan_ident(&nmtd->mtd,(sets) ? sets->nr_chips : 1);//扫描nand flashif (nmtd->scan_res == 0) {s3c2410_nand_update_chip(info, nmtd);nand_scan_tail(&nmtd->mtd);s3c2410_nand_add_partition(info, nmtd, sets);//添加分区}if (sets != NULL)sets++;}...
}

  nand_scan_ident–>nand_get_flash_type,与nand_flash_ids[]进行匹配,获取芯片类型信息。struct nand_flash_dev nand_flash_ids[ ]是全局类型,定义了一些NAND芯片的类型。
  s3c2410_nand_add_partition()->add_mtd_partitions() -> add_one_partition()->add_mtd_device()创建分区。

int add_mtd_device(struct mtd_info *mtd)
{for (i=0; i < MAX_MTD_DEVICES; i++)if (!mtd_table[i]) {struct mtd_notifier *not;...list_for_each_entry(not, &mtd_notifiers, list)not->add(mtd);}
}

  这里的mtd_notifiers,通过查找,看到是在register_mtd_user添加的。

void register_mtd_user (struct mtd_notifier *new)
{...list_add(&new->list, &mtd_notifiers);...
}

  而register_mtd_user,有两处调用:

mtdoops_console_init in mtdoops.c (drivers\mtd) :    register_mtd_user(&mtdoops_notifier);
register_mtd_blktrans in mtd_blkdevs.c (drivers\mtd) :      register_mtd_user(&blktrans_notifier);

  以register_mtd_blktrans为例

static struct mtd_notifier blktrans_notifier = {.add = blktrans_notify_add,.remove = blktrans_notify_remove,
};int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{...if (!blktrans_notifier.list.next)register_mtd_user(&blktrans_notifier);...list_add(&tr->list, &blktrans_majors);...
}

  所以上述add_mtd_device最终调用了blktrans_notify_add。

static void blktrans_notify_add(struct mtd_info *mtd)
{struct mtd_blktrans_ops *tr;if (mtd->type == MTD_ABSENT)return;list_for_each_entry(tr, &blktrans_majors, list)tr->add_mtd(tr, mtd);
}

  blktrans_majors在register_mtd_blktrans添加。

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,
};static int __init init_mtdblock(void)
{mutex_init(&mtdblks_lock);return register_mtd_blktrans(&mtdblock_tr);
}

  所以上述not->add(mtd)的大致流程

not->add(mtd)  --->                //即blktrans_notify_addtr->add_mtd(tr, mtd);  --->   //即mtdblock_add_mtdadd_mtd_blktrans_dev --->add_disk(gd);       //向内核注册gendisk结构体

3 程序

  描述:编写nand flash驱动,建立分区,通过挂载,测试驱动功能。

#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>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   = "supervivi",.size  = 0x00040000,.offset   = 0,},[1] = {.name    = "param",.offset = 0x00040000,.size    = 0x00020000,},[2] = {.name   = "Kernel",.offset = 0x00060000,.size   = 0x00500000,},[3] = {.name   = "root",.offset = 0x00560000,.size = 1024 * 1024 * 1024, //},[4] = {.name    = "nand",.offset = 0x00000000,.size = 1024 * 1024 * 1024, //}
};static void s3c2440_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 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   0s3c_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");

4 测试

  1)make menuconfig去掉内核自带的NAND FLASH驱动

-> Device Drivers-> Memory Technology Device (MTD) support-> NAND Device Support< >   NAND Flash support for S3C2410/S3C2440 SoC

  2)make uImage
  使用新内核启动, 并且使用NFS作为根文件系统。
  3)insmod s3c_nand.ko
  查看:ls -l /dev/mtd*
  查看分区信息:cat /proc/partitions
  此时看到/dev/mtdblock*是字符设备,
  执行mdev -s,若不执行,直接挂接,出现问题:
  mount: mounting /dev/mtdblock3 on /mnt/ failed: Block device required
  4)格式化 (参考下面编译工具)
  flash_eraseall /dev/mtd3 // yaffs
  5)挂接
  mount -t yaffs /dev/mtdblock3 /mnt
  6)在/mnt目录下建文件
  卸载目录,重新启动,再挂接,还能看到建立的文件。

  编译工具:
  1)tar xjf mtd-utils-05.07.23.tar.bz2
  2)cd mtd-utils-05.07.23/util
  修改Makefile:
  #CROSS=arm-linux-
  改为
  CROSS=arm-linux-
  3)make
  4)cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/

嵌入式Linux驱动学习【8】—— Nand Flash相关推荐

  1. IMX6ULL嵌入式Linux驱动学习笔记(二)

    IMX6ULL嵌入式Linux驱动学习 一.字符设备驱动 二.驱动模块的加载与卸载 三.字符设备的注册与注销 四.设备号 五.file_operations的具体实现 六.字符设备驱动框架 七.编写应 ...

  2. linux驱动编写(nand flash驱动)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 很长一段时间,nand flash都是嵌入式的标配产品.nand flash价格便宜,存储量大, ...

  3. 嵌入式Linux驱动学习【9】—— Nor Flash

    1 Nor Flash   原理图   与Nand Flash不同,Nor Flash有地址线.数据线,能直接读取数据,但是不能直接写入数据,需要有命令才行.当进行写时,一般要解锁->命令-&g ...

  4. 嵌入式Linux驱动学习之路(十五)按键驱动-定时器防抖

    在之前的定时器驱动程序中,我们发现在连续按下按键的时候,正常情况下应该是一次按下对应一次松开.而程序有时候会显示是两次按下,一次松开.这个问题是因为在按下的时候,因为是机械按键,所以电压信号会产生一定 ...

  5. 嵌入式Linux驱动学习之路(二十六)DM9000C网卡驱动程序

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

  6. 嵌入式Linux驱动笔记(五)------学习platform设备驱动

    你好!这里是风筝的博客, 欢迎和我一起交流. 设备是设备,驱动是驱动. 如果把两个糅合写一起,当设备发生变化时,势必要改写整个文件,这是非常愚蠢的做法.如果把他们分开来,当设备发生变化时,只要改写设备 ...

  7. 嵌入式linux应用层中断函数,嵌入式LINUX驱动开发(中断处理函数)

    嵌入式LINUX驱动开发(中断处理函数) 2020年08月11日 | 萬仟网网络运营 | 我要评论 嵌入式LINUX驱动学习之7中断相关(一)中断处理函数一.函数.头文件及说明二.编译举例:一.函数. ...

  8. 嵌入式Linux驱动笔记(十六)------设备驱动模型(kobject、kset、ktype)

    ###你好!这里是风筝的博客, ###欢迎和我一起交流. 前几天去面试,被问到Linux设备驱动模型这个问题,没答好,回来后恶补知识,找了些资料,希望下次能答出个满意答案. Linux早期时候,一个驱 ...

  9. 嵌入式Linux驱动难?到底难在哪?

    驱动入门难在:如何通过自己的学习能力搭建起环境,并理解一个LED驱动. 深入驱动难在:对内核的理解,对特定协议的认识. 最近看到论坛和群里一些人在说驱动难,个别人提出提供的入门资料还是难以入门.作为嵌 ...

最新文章

  1. 漫画 | 小公司卧薪尝胆三年,意外拿到美团offer
  2. 第八周实践项目9 算法库——广义表
  3. mysql开启查看慢查询日志[转]
  4. Java IOUtils.copy方法代码示例(亲测)
  5. python爬取的信息条数比页面显示多_Python爬取分析北京二手房数据?数据结果真的太吓人了...
  6. 关于计算机英语素材,计算机专业英语相关素材.doc
  7. 端口映射工具linux,Linux下端口映射工具rinetd(示例代码)
  8. Win11如何开启旧版组件 Win11开启旧版组件的方法
  9. 【Kafka】Kafka No serviceName defined in either JAAS or Kafka config
  10. HTML+CSS实现淘宝首页
  11. 网上购物系统c语言代码,网上购物系统源代码要怎么搭建才是最好的?
  12. 大数据相关精品资料包分享
  13. 【Jenkins】在Pipeline和Ant中使用环境变量
  14. 绘制网络组建拓扑图方法分享
  15. nca算法_NCA告诉英国公民,立即寻找有史以来最恶劣的网络攻击的保护
  16. leetcode 58. 最后一个单词的长度(Length of Last Word)
  17. windows命令行工具连接mysql数据库报ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost:3306‘ (10061)
  18. l2高斯分布_L1正则先验是Laplace分布,L2正则先验分布是高斯分布
  19. Oracle- imp/impdp导入dmp文件
  20. 先行一步,7大技术创新和突破,阿里云把 Serverless 领域的这些难题都给解了

热门文章

  1. java日历记事本_用java写的日历记事本代码?
  2. 坐标下降法(Coordinate descent)
  3. 江苏计算机报名时间2021年上半年,关于2021年春季江苏省计算机等级考试报名的通知...
  4. Android 10调用相机拍照
  5. 眼动数据分析(基于EyeLink眼动仪数据)
  6. 机器视觉问题:工业普通定焦镜头如何计算景深?景深计算
  7. c++_-nan(ind) NAN
  8. RT_thread 独立看门狗 watchdog 不断自动复位的解决方法
  9. 企鹅龙drbl+再生龙clonezilla 自动化实践剖析
  10. 吐血整理,20个计算机保研常见问题及回答模板