关键词:
目录
title: MTD下的Nand驱动
tags: linux
date: 2018/12/26/ 17:07:22
toc: true
---
MTD下的Nand驱动
引入
我们从启动信息的打印入口
S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 609 at 0x04c20000
Creating 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
可以看到如下代码driversmtd
ands3c2410.c
,可以看到这个是platform
平台设备驱动了,进入probe
开始分析
static int __init s3c2410_nand_init(void)
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics
");
platform_driver_register(&s3c2412_nand_driver);
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver);
平台设备资源文件
搜索名字可以找到平台的资源文件,包含了
- 寄存器
- 时序参数
- 分区表
设备平台的platform_device赋值
struct platform_device s3c_device_nand =
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource, //分配了1M的寄存器地址
;
static struct resource s3c_nand_resource[] =
[0] =
.start = S3C2410_PA_NAND,
.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
.flags = IORESOURCE_MEM,
;
s3c244x_map_io()
>s3c_device_nand.name = "s3c2440-nand";
archarmplat-s3c24xxs3c244x.csmdk_machine_init
>s3c_device_nand.dev.platform_data = &smdk_nand_info;
static struct s3c2410_platform_nand smdk_nand_info =
.tacls = 20,
.twrph0 = 60,
.twrph1 = 20,
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
.sets = smdk_nand_sets,
;
static struct s3c2410_nand_set smdk_nand_sets[] =
[0] =
.name = "NAND",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
.partitions = smdk_default_nand_part,
,
;
static struct mtd_partition smdk_default_nand_part[] =
[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,;
关键数据结构
2440的程序使用s3c2410_nand_info
包含了mtd
驱动程序中必备的两个结构体
- nand_chip 硬件操作层
- mtd_info 协议层
- s3c2410_nand_set 包含了分区表
平台框架
我们从probe
入手开始分析
s3c24xx_nand_probe
驱动主要调用内核的nand_scan()函数,add_mtd_partitions()函数,来完成注册nandflash
static int s3c24xx_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)
... ...
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); //3.扫描nandflash
... ...
s3c2410_nand_add_partition(info, nmtd, sets); //4.调用add_mtd_partitions()来添加mtd分区
... ...
更详细的流程如下
s3c24xx_nand_probe
// 获取平台设备 platform_device 的 dev.platform_data
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
//设置平台设备中的 driver_data 具体的结构为 s3c2410_nand_info 设置为空
struct s3c2410_nand_info *info;
info = kmalloc(sizeof(*info), GFP_KERNEL);
memzero(info, sizeof(*info));
platform_set_drvdata(pdev, info);
//设置info 中的时钟,并使能
info->clk = clk_get(&pdev->dev, "nand");
clk_enable(info->clk);
//资源文件也就是寄存器空间
res = pdev->resource;
size = res->end - res->start + 1;
info->area = request_mem_region(res->start, size, pdev->name);
//设置info的具体结构数据
info->device = &pdev->dev;
info->platform = plat;
info->regs = ioremap(res->start, size);
info->cpu_type = cpu_type;
//根据nand的时钟和tacls、twrph0、twrph1设置寄存器
err = s3c2410_nand_inithw(info, pdev);
// 一个nr_set 中就有一张分区表,这里应该是指有几个分区表 我们这里就只有一个分区表
size = nr_sets * sizeof(*info->mtds);
info->mtds = kmalloc(size, GFP_KERNEL);
memzero(info->mtds, size);
nmtd = info->mtds;
//设置chip中的一些函数指针 以及ecc 以及 info->sel_reg和info->sel_bit
s3c2410_nand_init_chip(info, nmtd, sets);
nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
//flash识别等
>nand_scan_ident(mtd, maxchips);
>nand_set_defaults 设置chip的读写函数等 nand_command、nand_select_chip。。。
//调用一些chip函数操作nand 读取id
>nand_get_flash_type
>chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//NAND_CMD_READID=0x90
>在数组中nand_flash_ids 寻找具体信息
>"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS,
>获取页大小,擦出大小,位宽等
//打印flash信息
printk(KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)
", *maf_id,
dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
//获取mtd的一些信息,ecc等
>nand_scan_tail(mtd);
//sets 就是平台设备dev->platform_data->sets
//根据分区表数目添加分区
s3c2410_nand_add_partition(info, nmtd, sets);
//nr_partitions 分区表的数目
if (set->nr_partitions > 0 && set->partitions != NULL)
add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
//nbparts=set->nr_partitions=分区表数目
for (i = 0; i < nbparts; i++)
//创建一个mtd设备,一个分区表一个
add_mtd_device(&slave->mtd)
> //找mtd_notifiers链表里的list_head结构体
list_for_each(this, &mtd_notifiers)
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
// 通过list_head找到struct mtd_notifier *not,调用它的add函数
not->add(mtd);
//字符设备 创建字符设备文件
//块设备 调用gendisk 添加分区
nand_scan
- mtd_info 是协议相关
- nand_chip 硬件相关
这里主要用nand_chip
提供的最基本的函数,来识别FALSH,再添加一些nand_chip
中其他的函数以及参数,再调用nand_scan_tail
添加mtd_info
的成员
nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
//flash识别等
>nand_scan_ident(mtd, maxchips);
>nand_set_defaults 设置chip的读写函数等 nand_command、nand_select_chip。。。
//调用一些chip函数操作nand 读取id
>nand_get_flash_type
>chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//NAND_CMD_READID=0x90
>在数组中nand_flash_ids 寻找具体信息
>"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS,
>获取页大小,擦出大小,位宽等
//打印flash信息
printk(KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)
", *maf_id,
dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
//获取mtd的一些信息,ecc等
>nand_scan_tail(mtd);
s3c2410_nand_add_partition
这里是根据分区表添加分区,调用系统提供的add_mtd_partitions
来操作
add_mtd_partitions
这里会最终调用mtd_notifiers
中的not->add(mtd)
来添加分区表
//创建一个mtd设备,一个分区表一个
add_mtd_device(&slave->mtd)
> //找mtd_notifiers链表里的list_head结构体
list_for_each(this, &mtd_notifiers)
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
// 通过list_head找到struct mtd_notifier *not,调用它的add函数
not->add(mtd);
那么如何去添加到分区表呢?这里是调用了 mtd_notifiers 中的 add函数成员,搜索全局变量mtd_notifiers来查看是在哪里初始化的,继续搜索register_mtd_user
可以看到有字符设备和块设备调用
void register_mtd_user (struct mtd_notifier *new)
list_add(&new->list, &mtd_notifiers);
for (i=0; i< MAX_MTD_DEVICES; i++)
if (mtd_table[i])
new->add(mtd_table[i]);
字符设备add
会创建字符设备驱动mtd%d mtd%dro
init_mtdchar
>register_mtd_user
#define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31
init_mtdchar
register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)
mtd_class = class_create(THIS_MODULE, "mtd");
register_mtd_user(¬ifier);
static struct mtd_notifier notifier =
.add = mtd_notify_add,
.remove = mtd_notify_remove,
;
块设备add
结论
mtd_notifiers.add== blktrans_notify_add ->>>blktrans_majors.add_mtd == mtdblock_add_mtd
在这里面会调用之前块设备驱动的gendisk
的操作
代码分析如下
初始化在drivers/mtd/mtd_blkdevs.c
中的register_mtd_blktrans
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
if (!blktrans_notifier.list.next)
register_mtd_user(&blktrans_notifier);
static struct mtd_notifier blktrans_notifier =
.add = blktrans_notify_add,
.remove = blktrans_notify_remove,
;
也就是最终调用了blktrans_notify_add来添加分区表
not->add == blktrans_notify_add
这里blktrans_notify_add
又是通过一个链表来实现add的操作,也就是通过blktrans_majors
中的add,
继续搜索这个链表的初始化添加
static void blktrans_notify_add(struct mtd_info *mtd)
struct list_head *this;
list_for_each(this, &blktrans_majors)
struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
tr->add_mtd(tr, mtd);
可以看到依然是在register_mtd_blktrans添加
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
//这里初始化 mtd_notifiers 这个链表,这个链表的add会在添加分区表操作
if (!blktrans_notifier.list.next)
register_mtd_user(&blktrans_notifier);
// 这个是gendisk 的队列函数
tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
"%sd", tr->name);
//mtd_notifiers 中的add会调用blktrans_majors链表中的add_mtd,初始化blktrans_majors这个链表
INIT_LIST_HEAD(&tr->devs);
list_add(&tr->list, &blktrans_majors);
搜索register_mtd_blktrans被mtdblock_ro.c
和mtdblock.c
调用,一个只读,我们分析可读写的
static int __init init_mtdblock(void)
return register_mtd_blktrans(&mtdblock_tr);
也就是说最终的add指向如下
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_notifiers.add== blktrans_notify_add ->>>blktrans_majors.add_mtd == mtdblock_add_mtd
在这里面会调用之前块设备驱动的gendisk
的操作
static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
dev->mtd = mtd;
dev->devnum = mtd->index;
dev->size = mtd->size >> 9;
dev->tr = tr;
add_mtd_blktrans_dev(dev);
int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
//分配一个gendisk
gd = alloc_disk(1 << tr->part_bits);
gd->major = tr->major;
gd->first_minor = (new->devnum) << tr->part_bits;
gd->fops = &mtd_blktrans_ops;
gd->private_data = new;
new->blkcore_priv = gd;
//设置队列
gd->queue = tr->blkcore_priv->rq;
//添加gendisk
add_disk(gd);
队列
在register_mtd_blktrans
中有设置队列处理的函数
// 这个是gendisk 的队列函数
tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
"%sd", tr->name);
处理函数如下,搜索blkcore_priv->thread
查看到mtd_blktrans_thread
mtd_blktrans_request
wake_up_process(tr->blkcore_priv->thread);
static int mtd_blktrans_thread(void *arg)
while (!kthread_should_stop())
//电梯调度算法
req = elv_next_request(rq);
//操作
res = do_blktrans_request(tr, dev, req);
//一次获取结束
end_request(req, res);
进入这个处理req的函数
do_blktrans_request
case READ
tr->readsect
case WRITE
tr->writesect
搜索发现就是刚才注册的mtdblock_tr
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,
;
程序设计
参考
2440
官方驱动driversmtd ands3c2410.c
- 简单的
driversmtd andat91_nand.c
或者应该是通用的driversmtd andplat_nand.c
平台设备文件
可以看到,linux在这里使用platform
平台总线来构造这个驱动,在2440中如下:
- 寄存器
- 时序参数
- 分区表
struct platform_device s3c_device_nand =
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource, //分配了1M的寄存器地址
;
static struct resource s3c_nand_resource[] =
[0] =
.start = S3C2410_PA_NAND,
.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
.flags = IORESOURCE_MEM,
;
static struct s3c2410_platform_nand smdk_nand_info =
.tacls = 20,
.twrph0 = 60,
.twrph1 = 20,
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
.sets = smdk_nand_sets,
;
static struct s3c2410_nand_set smdk_nand_sets[] =
[0] =
.name = "NAND",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
.partitions = smdk_default_nand_part,
,
;
static struct mtd_partition smdk_default_nand_part[] =
[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,;
必备的构造结构
查看下官方的driversmtd
andplat_nand.c
,可以看出来必备的结构
struct plat_nand_data
struct nand_chip chip;
struct mtd_info mtd;
void __iomem *io_base;
#ifdef CONFIG_MTD_PARTITIONS
int nr_parts;
struct mtd_partition *parts;
#endif
;
- nand_chip 硬件操作相关
- mtd_info 协议相关,在调用
nand_scan_tail
后会将里面的读写绑定到nand_chip 的读写
xxx_nand_probe
>nand_scan
>nand_scan_ident //设置nand——chip
>nand_set_defaults
>nand_get_flash_type
>nand_scan_tail //设置mtd
步骤简述
设置nand_chip和mtd_info
nand_chip
提供了硬件操作的接口,这里必备的一些需要分析nand_scan
来分析,nand_set_defaults
里面有一些默认的函数,需要看下是否适用
查看driversmtd
andplat_nand.c
的probe
怎么设置的 mtd_info
的priv
需要指向nand_chip
data->chip.priv = &data;
//mtd必须的设置
data->mtd.priv = &data->chip;
data->mtd.owner = THIS_MODULE;
//chip的一些设置
data->chip.IO_ADDR_R = data->io_base;
data->chip.IO_ADDR_W = data->io_base;
data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
data->chip.dev_ready = pdata->ctrl.dev_ready;
data->chip.select_chip = pdata->ctrl.select_chip;
data->chip.chip_delay = pdata->chip.chip_delay;
data->chip.options |= pdata->chip.options;
//ecc的设置,我们只需要设置 NAND_ECC_SOFT即可
data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
data->chip.ecc.layout = pdata->chip.ecclayout;
data->chip.ecc.mode = NAND_ECC_SOFT;
设置时钟
/* 3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 */
/* 使能NAND FLASH控制器的时钟 */
clk = clk_get(NULL, "nand");
clk_enable(clk); /* CLKCON‘bit[4] */
设置分区表
函数原型如下
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
//parts 为分区表
//nbparts 表示这个分区表里面有几个分区
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,
;
测试
内核配置去除NAND
去除掉内核的nand
驱动
-> Device Drivers │
│ -> Memory Technology Device (MTD) support (MTD [=y]) │
│ -> NAND Device Support (MTD_NAND [=y])
烧入内核后启动可以发现无法找到文件系统,疑问nand驱动已经被去除了
Root-NFS: No NFS server available, giving up.
VFS: Unable to mount root fs via NFS, trying floppy.
VFS: Cannot open root device "mtdblock3" or unknown-block(2,0)
Please append a correct "root=" boot option; here are the available partitions:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(2,0)
使用nfs文件系统启动
设置uboot参数
set bootargs noinitrd root=/dev/nfs nfsroot=172.16.45.222:/home/book/stu/fs ip=172.16.45.200:172.16.45.222:172.16.45.222:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
# 可以不save 也就是本次生效
save
mount -t nfs -o nolock,vers=2 172.16.45.222:/home/book/stu /mnt
没有开启ECC
的会提示ecc错误
# insmod s3c_nand.ko
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 609 at 0x04c20000
加载驱动
# mkdir nfs_stu
# mount -t nfs -o nolock,vers=2 172.16.45.222:/home/book/stu /nfs_stu/
# cd /nfs_stu/code/
# insmod s3c_nand.ko
分区表打印
挂载驱动后可以打印分区表了
# insmod s3c_nand.ko
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 609 at 0x04c20000
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x00000000-0x00040000 : "bootloader"
0x00040000-0x00060000 : "params"
0x00060000-0x00260000 : "kernel"
0x00260000-0x10000000 : "root"
# ls /dev/mtd* -l
crw-rw---- 1 0 0 90, 0 Jan 1 00:06 /dev/mtd0
crw-rw---- 1 0 0 90, 1 Jan 1 00:06 /dev/mtd0ro
crw-rw---- 1 0 0 90, 2 Jan 1 00:06 /dev/mtd1
crw-rw---- 1 0 0 90, 3 Jan 1 00:06 /dev/mtd1ro
crw-rw---- 1 0 0 90, 4 Jan 1 00:06 /dev/mtd2
crw-rw---- 1 0 0 90, 5 Jan 1 00:06 /dev/mtd2ro
crw-rw---- 1 0 0 90, 6 Jan 1 00:06 /dev/mtd3
crw-rw---- 1 0 0 90, 7 Jan 1 00:06 /dev/mtd3ro
brw-rw---- 1 0 0 31, 0 Jan 1 00:06 /dev/mtdblock0
brw-rw---- 1 0 0 31, 1 Jan 1 00:06 /dev/mtdblock1
brw-rw---- 1 0 0 31, 2 Jan 1 00:06 /dev/mtdblock2
brw-rw---- 1 0 0 31, 3 Jan 1 00:06 /dev/mtdblock3
查看下分区表的信息
# cat /proc/partitions
# 其中blocks表示分区的容量,每个blocks是1KB
major minor #blocks name
31 0 256 mtdblock0
31 1 128 mtdblock1
31 2 2048 mtdblock2
31 3 259712 mtdblock3
挂载这个分区
mount /dev/mtdblock3 /mnt/
mtd-utils
编译安装,这里util
依赖zlib
,还要安装这个,zlib
要安装到交叉编译器中,使用which
查看
# tar xzvf zlib-1.2.3.tar.gz
# which arm-linux-gcc
/opt/gcc-3.4.5-glibc-2.3.6/bin/arm-linux-gcc
#其中-prefix指定zlib的安装路径,需要指定到交叉编译器所在路径
#CC=arm-linux-gcc ./configure --shared --prefix=/opt/gcc-3.4.5-glibc-2.3.6/arm-linux
#make
#make install
#------------------------------------------------------------------
#mkdir tmp
#tar xjf mtd-utils-05.07.23.tar.bz2 -C tmp/
#cd tmp
#cd util/
#make
复制到nfs的文件系统提供给单板使用
cp flash_erase flash_eraseall flashcp /stu/fs
使用工具格式化后挂载
FAQ : 格式化使用字符设备
因为每个分区的字符设备,其实就是对应着每个分区块设备。即/dev/mtd3对应/dev/mtdblock3
flash_eraseall, flash_erase那些命令是以ioctl等基础而实现, 而块设备不支持ioctl, 只有字符设备支持
#擦除分区3,也就是文件系统的分区
./flash_eraseall /dev/mtd3
#挂载刚才格式化 的分区3 也就是 root
# mount -t yaffs /dev/mtdblock3 /mnt
yaffs: dev is 32505859 name is "mtdblock3"
yaffs: passed flags ""
yaffs: Attempting MTD mount on 31.3, "mtdblock3"
yaffs: auto selecting yaffs2
block 591 is bad
# ls /mnt
lost+found
#卸载
umount /mnt
复制文件系统回来
卸载这个mnt
可能发现无法卸载,查看使用的pid后杀掉,发现是sh占用了
umount: cannot umount /mnt: Device or resource busy
fuser /mnt
kill -9 pid
挂载后复制文件,这个/nfs_stu/fs/*
是在nfs上的文件系统
cp /nfs_stu/fs/* /mnt -r
# 提示一下可以忽略,因为nand驱动不全 https://blog.csdn.net/grublyboy/article/details/9664169
yaffs chunk 773 was not erased
重启系统,设置启动参数,设置内核加载原来的NAND的驱动
set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0 user_debug=0xff
系统可以正常启动
完整程序
/* 参考
* driversmtd
ands3c2410.c
* driversmtd
andat91_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 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 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");
u-boot-2014.10移植支持nand(代码片段)
在smdk2440.h里面加上NAND的配置选项 #defineCONFIG_CMD_NAND编译出错drivers/mtd/nand/s3c2410_nand.c:Infunction‘s3c2410_hwcontrol‘:drivers/mtd/nand/s3c2410_nand.c:44:warning:implicitdeclarationoffunction‘s3c2410_g 查看详情
uboot设置uboot下的bootargs参数(代码片段)
...a;1.通过代码中设置setenv("bootargs",boot_args);2.通过uboot下的命令行设置setenvbootargs'xxx...'bootargs参数解析root: 目前很多新的开发板都是使用FLASH作为存储。因为很多都直接使用MTD驱动程序。 MTD驱动程序的主要优点... 查看详情
nandflash基本知识
...何的软件支持,在NAND器件上进行同样操作时,通常需要驱动程序,也就是内存技术驱动程序(MTD),NAND和NOR器件在进行写入和擦除操作时都需要MTD。---------摘抄自网上流传很广的《NAND和NORflash的区别》Secondpart:NANDFlash结构与驱动... 查看详情
linux内核编译
...么原因,该如何才能解决呢?/arm2410/kernel/include/linux/mtd/nand_ids.h:55:warning:`nand_smc_info'definedbutnotusedmake[4]:***[nand.o]Error1make[4]:Leavingdirectory`/arm2410/kernel/drivers/mtd/nand'make[3]:***[first_rule]Error2make[3]:Leavingdirectory`/arm2410/kernel/drivers/mtd/... 查看详情
什么是mtd分区和nandflash
...的优越之处,因为大多数情况下闪存只是用来存储少量的代码,这时NOR闪存更适合一些。而NAND则是高数据存储密度的理想解决方案。NOR的特点是芯片内执行(XIP,eXecuteInPlace),这样应用程序可以直接在flash闪存内运行,不必再把代... 查看详情
ltp-ddtnand_mtd_dd_rw_jffs2
error:由于在uboot下没有发现坏块,将核心代码剥离出来调试:flash_eraseall-q-j/dev/mtd1mkdir-p/mnt/partition_nand_1419mount-tjffs2-oasync/dev/mtdblock1/mnt/partition_nand_1419timeddif=/dev/urandomof=/tmp/srctestbs=131072count= 查看详情
锐捷ccna系列交换机配置模式切换(代码片段)
实训目的熟悉配置模式切换。实训拓扑实验所需设备:设备类型设备型号数量二层交换机S37601实训步骤步骤1:打开交换机的命令行界面。DecompilingorReverseEngineeringisNotAllowed.*Oct1503:01:15:%MTD_DRIVER-5-MTD_NAND_FOUND:1NANDchips(chipsize:33554432)de... 查看详情
格式化并挂载ubi文件系统过程详解(代码片段)
...可读写的文件系统;2、内核中使能UBI2.1、使能UBI设备驱动Location:││->DeviceDrivers││->MemoryTechnologyDevice(MTD)support(MTD[=y])││->EnableUBI-Unsortedblockimages2.1.1、UBIwear-levelingthresholdUBI系统记录每个擦除块发生擦除操作的次... 查看详情
痞子衡嵌入式:并行接口nand标准(onfi)及slcrawnand简介(代码片段)
...大类:SerialNAND、RawNAND,这两类NAND的差异是很大的(软件驱动开发角度而言),即使你掌握其中一种,也不代表你能了解另一种。 RawNAND是相对于SerialNAND而言的,Serial 查看详情
裸机——nand(代码片段)
1.首先需要知道Nand的基础知识 从Nand的芯片手册可以获得 我使用的芯片手册是K9F2G08 首先从芯片手册的名称可以获得信息: K9F:三星 2G :2Gb(256MB) 08 :8位数据线 下面列出芯片手册... 查看详情
hdu5929basicdatastructure(模拟+乱搞)题解(代码片段)
题意:给定一种二进制操作nand,为0nand0=10nand1=1 1nand0=1 1nand1=0现在要你模拟一个队列,实现PUSHx往队头塞入x,POP队尾退出,REVERSE翻转,QUERY询问队头到队尾的nand和。思路:其他都可以模拟,但是n为2e5,如果直接求nand和必... 查看详情
norflash驱动编写笔记
...log.csdn.net/ziyiyunmen/article/details/9744901 一、Linux Flash驱动结构 1、Linux MTD系统层次在Linux系统中,提供了MTD(内存技术设备)系统来建立Flash针对Linux的统一、抽象的接口。在引入MTD后,Linux系统中Flash设备驱动及接口... 查看详情
sylixos下的io系统调用(代码片段)
SylixOS标准I/O基本介绍1.1简介I/O系统又称作输入输出系统,SylixOS兼容POSIX标准输入输出系统,SylixOS的I/O概念继承了UNIX操作系统的I/O概念,认为一切皆为文件。与UNIX操作系统相同,SylixOS中的文件也分为不同的类型。1.2两种I/O类型I... 查看详情
单片机开发ov2640在没有dcmi接口的情况下的stm32驱动(代码片段)
文章目录(一)背景介绍(二)接线(三)软件实现(一)背景介绍在之前刚学STM32的时候完成了一个ov7670的驱动ov7670驱动已经快要两年过去了,最近抽了一点时间又将之前搞得ov2640的驱动完善了一下看一下效果吧... 查看详情
单片机开发ov2640在没有dcmi接口的情况下的stm32驱动(代码片段)
文章目录(一)背景介绍(二)接线(三)软件实现(一)背景介绍在之前刚学STM32的时候完成了一个ov7670的驱动ov7670驱动已经快要两年过去了,最近抽了一点时间又将之前搞得ov2640的驱动完善了一下看一下效果吧... 查看详情
u-boot之nand启动与nor启动的区别(代码片段)
nand启动与nor启动的区别主要分为以下几部分说明:1、nandflash与norflash的最主要区别2、s3c2440的nand启动与nor启动原理3、nand启动与nor启动的时候uboot做了什么 1、在JZ2440开发板上有两种Flash,分别为nandflash和norflash。这两种flash的... 查看详情
windows操作系统如何支持yaffs文件系统
1、修改内核源码根目录下的Makefile文件#ARCH?=arm#CROSS_COMPILE?=arm-linux-2、修改arch/arm/plat-s3c24xx/common-smdk.c文件,修改NandFlash的分区信息和NandFlash的硬件信息。这里的分区一定要和你的bootloader的一致,你可以参考smdk_default_nand_part[]这... 查看详情
i.mx6ull驱动开发|23-linux下的驱动分离与分层——platform平台驱动模型(代码片段)
一、Linux驱动的分离1.为什么需要驱动分离?在嵌入式开发中,无论处理器如何更换,外设模块的操作都是一致的,比如有三个不同的平台都要驱动MPU6050传感器,最简单的方法是针对每个平台都写一份驱动... 查看详情