MTD下的Nand驱动

目录

  • MTD下的Nand驱动

    • 引入
    • 平台设备资源文件
    • 关键数据结构
    • 平台框架
      • s3c24xx_nand_probe
      • nand_scan
      • s3c2410_nand_add_partition
      • add_mtd_partitions
      • 字符设备add
      • 块设备add
      • 队列
    • 程序设计
      • 参考
      • 平台设备文件
      • 必备的构造结构
      • 步骤简述
    • 测试
      • 内核配置去除NAND
      • 使用nfs文件系统启动
      • 加载驱动
      • 分区表打印
      • mtd-utils
      • 复制文件系统回来
    • 完整程序


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可以看到如下代码drivers\mtd\nand\s3c2410.c,可以看到这个是platform平台设备驱动了,进入probe开始分析

static int __init s3c2410_nand_init(void)
{
    printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");

    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";
arch\arm\plat-s3c24xx\s3c244x.c\smdk_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)\n", *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)\n", *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(&notifier);
        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.cmtdblock.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官方驱动drivers\mtd\nand\s3c2410.c
  • 简单的drivers\mtd\nand\at91_nand.c或者应该是通用的drivers\mtd\nand\plat_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,}};

必备的构造结构

查看下官方的drivers\mtd\nand\plat_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里面有一些默认的函数,需要看下是否适用

查看drivers\mtd\nand\plat_nand.cprobe怎么设置的 mtd_infopriv需要指向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

系统可以正常启动

完整程序


/* 参考
 * 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 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");

原文地址:https://www.cnblogs.com/zongzi10010/p/10178914.html

时间: 2024-10-16 09:37:09

MTD下的Nand驱动的相关文章

SylixOS下基于NUC970的NAND驱动

开发环境 开发环境 宿主机: Windows7 64bits 系统 开发板: 安米MDK972 软件环境: RealEvo-IDE3.0 NAND Flash: S34ML02G100TF100 S34ML02G100TF100芯片参数 Density:2 Gbit Input / Output Bus Width: 8-bits Page Size:2112 (2048 + 64) bytes; 64 bytes is spare area Block Size: 64 Pages;128k

SylixOS nand驱动框架理解

1.适用范围 该应用笔记适用于帮助用户在SylixOS下使用nandflash器件. 2.原理概述 2.1nandflash存储结构 nandflash存储结构如图2-1所示,下面以mini2440开发板上的k9f1g08系列nandflash为例,简单介绍nandflash的存储结构. 图2-1 nandflash存储结构 nandflash的存储结构主要有两个单位:页(page),块(block).每页大小为2K+64字节,2K用来存储数据,64字节用来存储控制信息(OOB区,例如坏块信息.

linux-2.6.32在mini2440开发板上移植(1)之移植Nand驱动并修改分区信息

编者:linux中的nand的移植由于使用了MTD技术,所以就没有想象的那么复杂了. 1 Linux-2.6.32.2 内核所支持的Nand Flash 类型 Linux2.6.32.2 已经自带了大部分Nand Flash 驱动, 在linux-2.6.32.2/drivers/mtd/nand/nand_ids.c 文件中,定义了所支持的各种Nand Flash 类型. 2 修改Nand Flash 分区表 但是系统默认的分区不是我们所需的,所以要自已修改,除此之外,还有Nand Flash

linux下USB设备驱动

本文以USB鼠标讲解USB设备驱动. 驱动程序中调用usb_register(struct usb_driver *)函数时,先判断USB总线驱动程序是否支持usb_driver里定义的id_table,若支持,调用usb_driver的probe函数. 所以首先要定义并初始化usb_driver结构体: static struct usb_driver usbmouse_as_key_driver = { .name = "usbmouse_as_key", .probe = usb

Linux下hp打印机驱动hplip分析

Hplip分析 版本是2.14,源码位置:http://hplipopensource.com.图的来源:http://hplipopensource.com/node/128. 1.D-Bus初始化流程 D-Bus的初始化同样是在ui4/devmgr5.py开始的. ui4/devmgr5.py 01 class DevMgr5(QMainWindow,  Ui_MainWindow): 02     ...... 03     # TODO: Make sbus init mandatory

20150503 imx257下实现I2C驱动的四种方法

20150503 imx257下实现I2C驱动的四种方法 2015-05-3 Lover雪儿 时间过得好快,转眼间五一假期就即将结束了,假期期间,大家都潇洒的去玩了,徒留辛辛苦苦的程序员还是窝在宿舍乖乖的敲着代码... 好啦,开开玩笑,辛酸史每家都有一大本,还是要积极的面对生活!!! 今天我们的任务是简单的入门linux内核下i2c设备驱动分离的四种写法. 一.一个简单的i2c驱动 和以前的驱动程序不同,i2c驱动分为drv驱动和dev设备驱动两个文件,不懂的可以参考我以前写的<20150313

【L】小米随身WiFi,Linux下AP热点驱动(开源)

前言 这是作者修改后的Linux下的小米随身WiFi驱动,支持Ap模式,开源,同时支持360随身WiFi二代 注意: 1.这个是Linux下的小米随身WiFi驱动,不是Window和MAC下 2.这个不是STA模式下的可当作无线网卡的驱动,是可当Ap热点模式的驱动 3.这个理论上也是支持360随身WiFi二代..(rtusb_dev_id.c 里面同样有360的id) 原由: 小米随身WiFi很好用,有Widnow和Mac的系统的驱动, 但是可怜不少Linux下的苦逼开发者,没有驱动就是没有..

MongoDB在MFC下使用C++驱动编译错误的解决

.post p{text-indent: 2em;} 今天使用MongoDB的C++驱动,在编译连接的时候一直出现错误,显示的string_data.h下93行max宏的问题,可视其本身并不是调用max宏,而是调用 std::numeric_limits<size_t>::max 这样就是产生错误,通过搜索发现解决方法(参考网址:http://blog.chinaunix.net/uid-17102734-id-2830143.html),将该函数用括号括起来,避免windows定义的混淆,具

在两种情况下设备与驱动会发生匹配

在两种情况下设备与驱动会发生匹配:(基于linux内核3.0) 当设备插入系统时,设备挂接到总线上,与总线上的所有驱动进行匹配(bus_type.match进行匹配), 如果匹配成功,则调用bus_type.probe或者driver.probe初始化该设备,挂接到总线上,如果匹配失败,则只是将该设备挂接到总线上. 当驱动注册到系统时,驱动挂接到总线上,与总线上的所有设备进行匹配(用bus_type.match进行匹配),如果匹配成功,则调用bus_type.probe或者driver.prob