嵌入式Linux驱动学习【8】—— Nand Flash
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相关推荐
- IMX6ULL嵌入式Linux驱动学习笔记(二)
IMX6ULL嵌入式Linux驱动学习 一.字符设备驱动 二.驱动模块的加载与卸载 三.字符设备的注册与注销 四.设备号 五.file_operations的具体实现 六.字符设备驱动框架 七.编写应 ...
- linux驱动编写(nand flash驱动)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 很长一段时间,nand flash都是嵌入式的标配产品.nand flash价格便宜,存储量大, ...
- 嵌入式Linux驱动学习【9】—— Nor Flash
1 Nor Flash 原理图 与Nand Flash不同,Nor Flash有地址线.数据线,能直接读取数据,但是不能直接写入数据,需要有命令才行.当进行写时,一般要解锁->命令-&g ...
- 嵌入式Linux驱动学习之路(十五)按键驱动-定时器防抖
在之前的定时器驱动程序中,我们发现在连续按下按键的时候,正常情况下应该是一次按下对应一次松开.而程序有时候会显示是两次按下,一次松开.这个问题是因为在按下的时候,因为是机械按键,所以电压信号会产生一定 ...
- 嵌入式Linux驱动学习之路(二十六)DM9000C网卡驱动程序
基于DM9000C的原厂代码修改dm9000c的驱动程序. 首先确认内存的基地址 iobase. 确定中断号码. 打开模块的初始化函数定义. 配置内存控制器的相应时序(结合DM9000C.C的手册). ...
- 嵌入式Linux驱动笔记(五)------学习platform设备驱动
你好!这里是风筝的博客, 欢迎和我一起交流. 设备是设备,驱动是驱动. 如果把两个糅合写一起,当设备发生变化时,势必要改写整个文件,这是非常愚蠢的做法.如果把他们分开来,当设备发生变化时,只要改写设备 ...
- 嵌入式linux应用层中断函数,嵌入式LINUX驱动开发(中断处理函数)
嵌入式LINUX驱动开发(中断处理函数) 2020年08月11日 | 萬仟网网络运营 | 我要评论 嵌入式LINUX驱动学习之7中断相关(一)中断处理函数一.函数.头文件及说明二.编译举例:一.函数. ...
- 嵌入式Linux驱动笔记(十六)------设备驱动模型(kobject、kset、ktype)
###你好!这里是风筝的博客, ###欢迎和我一起交流. 前几天去面试,被问到Linux设备驱动模型这个问题,没答好,回来后恶补知识,找了些资料,希望下次能答出个满意答案. Linux早期时候,一个驱 ...
- 嵌入式Linux驱动难?到底难在哪?
驱动入门难在:如何通过自己的学习能力搭建起环境,并理解一个LED驱动. 深入驱动难在:对内核的理解,对特定协议的认识. 最近看到论坛和群里一些人在说驱动难,个别人提出提供的入门资料还是难以入门.作为嵌 ...
最新文章
- 漫画 | 小公司卧薪尝胆三年,意外拿到美团offer
- 第八周实践项目9 算法库——广义表
- mysql开启查看慢查询日志[转]
- Java IOUtils.copy方法代码示例(亲测)
- python爬取的信息条数比页面显示多_Python爬取分析北京二手房数据?数据结果真的太吓人了...
- 关于计算机英语素材,计算机专业英语相关素材.doc
- 端口映射工具linux,Linux下端口映射工具rinetd(示例代码)
- Win11如何开启旧版组件 Win11开启旧版组件的方法
- 【Kafka】Kafka No serviceName defined in either JAAS or Kafka config
- HTML+CSS实现淘宝首页
- 网上购物系统c语言代码,网上购物系统源代码要怎么搭建才是最好的?
- 大数据相关精品资料包分享
- 【Jenkins】在Pipeline和Ant中使用环境变量
- 绘制网络组建拓扑图方法分享
- nca算法_NCA告诉英国公民,立即寻找有史以来最恶劣的网络攻击的保护
- leetcode 58. 最后一个单词的长度(Length of Last Word)
- windows命令行工具连接mysql数据库报ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost:3306‘ (10061)
- l2高斯分布_L1正则先验是Laplace分布,L2正则先验分布是高斯分布
- Oracle- imp/impdp导入dmp文件
- 先行一步,7大技术创新和突破,阿里云把 Serverless 领域的这些难题都给解了
热门文章
- java日历记事本_用java写的日历记事本代码?
- 坐标下降法(Coordinate descent)
- 江苏计算机报名时间2021年上半年,关于2021年春季江苏省计算机等级考试报名的通知...
- Android 10调用相机拍照
- 眼动数据分析(基于EyeLink眼动仪数据)
- 机器视觉问题:工业普通定焦镜头如何计算景深?景深计算
- c++_-nan(ind) NAN
- RT_thread 独立看门狗 watchdog 不断自动复位的解决方法
- 企鹅龙drbl+再生龙clonezilla 自动化实践剖析
- 吐血整理,20个计算机保研常见问题及回答模板