title: MTD下的Nand驱动

tags: linux

date: 2018/12/26/ 17:07:22

toc: true

MTD下的Nand驱动

引入

我们从启动信息的打印入口

  1. S3C24XX NAND Driver, (c) 2004 Simtec Electronics
  2. s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
  3. NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
  4. Scanning device for bad blocks
  5. Bad eraseblock 609 at 0x04c20000
  6. Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
  7. 0x00000000-0x00040000 : "bootloader"
  8. 0x00040000-0x00060000 : "params"
  9. 0x00060000-0x00260000 : "kernel"
  10. 0x00260000-0x10000000 : "root"

搜索S3C24XX NAND Driver可以看到如下代码drivers\mtd\nand\s3c2410.c,可以看到这个是platform平台设备驱动了,进入probe开始分析

  1. static int __init s3c2410_nand_init(void)
  2. {
  3. printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
  4. platform_driver_register(&s3c2412_nand_driver);
  5. platform_driver_register(&s3c2440_nand_driver);
  6. return platform_driver_register(&s3c2410_nand_driver);
  7. }

平台设备资源文件

搜索名字可以找到平台的资源文件,包含了

  • 寄存器
  • 时序参数
  • 分区表
  1. 设备平台的platform_device赋值
  2. struct platform_device s3c_device_nand = {
  3. .name = "s3c2410-nand",
  4. .id = -1,
  5. .num_resources = ARRAY_SIZE(s3c_nand_resource),
  6. .resource = s3c_nand_resource, //分配了1M的寄存器地址
  7. };
  8. static struct resource s3c_nand_resource[] = {
  9. [0] = {
  10. .start = S3C2410_PA_NAND,
  11. .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
  12. .flags = IORESOURCE_MEM,
  13. }};
  14. s3c244x_map_io()
  15. >s3c_device_nand.name = "s3c2440-nand";
  16. arch\arm\plat-s3c24xx\s3c244x.c\smdk_machine_init
  17. >s3c_device_nand.dev.platform_data = &smdk_nand_info;
  18. static struct s3c2410_platform_nand smdk_nand_info = {
  19. .tacls = 20,
  20. .twrph0 = 60,
  21. .twrph1 = 20,
  22. .nr_sets = ARRAY_SIZE(smdk_nand_sets),
  23. .sets = smdk_nand_sets,
  24. };
  25. static struct s3c2410_nand_set smdk_nand_sets[] = {
  26. [0] = {
  27. .name = "NAND",
  28. .nr_chips = 1,
  29. .nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
  30. .partitions = smdk_default_nand_part,
  31. },
  32. };
  33. static struct mtd_partition smdk_default_nand_part[] = {
  34. [0] = {
  35. .name = "bootloader",
  36. .size = 0x00040000,
  37. .offset = 0,},
  38. [1] = {
  39. .name = "params",
  40. .offset = MTDPART_OFS_APPEND,
  41. .size = 0x00020000,},
  42. [2] = {
  43. .name = "kernel",
  44. .offset = MTDPART_OFS_APPEND,
  45. .size = 0x00200000,},
  46. [3] = {
  47. .name = "root",
  48. .offset = MTDPART_OFS_APPEND,
  49. .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

  1. static int s3c24xx_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)
  2. {
  3. ... ...
  4. err = s3c2410_nand_inithw(info, pdev); //初始化硬件hardware,设置TACLS 、TWRPH0、TWRPH1通信时序等
  5. s3c2410_nand_init_chip(info, nmtd, sets); //初始化芯片
  6. nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); //3.扫描nandflash
  7. ... ...
  8. s3c2410_nand_add_partition(info, nmtd, sets); //4.调用add_mtd_partitions()来添加mtd分区
  9. ... ...
  10. }

更详细的流程如下

  1. s3c24xx_nand_probe
  2. // 获取平台设备 platform_device 的 dev.platform_data
  3. struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
  4. //设置平台设备中的 driver_data 具体的结构为 s3c2410_nand_info 设置为空
  5. struct s3c2410_nand_info *info;
  6. info = kmalloc(sizeof(*info), GFP_KERNEL);
  7. memzero(info, sizeof(*info));
  8. platform_set_drvdata(pdev, info);
  9. //设置info 中的时钟,并使能
  10. info->clk = clk_get(&pdev->dev, "nand");
  11. clk_enable(info->clk);
  12. //资源文件也就是寄存器空间
  13. res = pdev->resource;
  14. size = res->end - res->start + 1;
  15. info->area = request_mem_region(res->start, size, pdev->name);
  16. //设置info的具体结构数据
  17. info->device = &pdev->dev;
  18. info->platform = plat;
  19. info->regs = ioremap(res->start, size);
  20. info->cpu_type = cpu_type;
  21. //根据nand的时钟和tacls、twrph0、twrph1设置寄存器
  22. err = s3c2410_nand_inithw(info, pdev);
  23. // 一个nr_set 中就有一张分区表,这里应该是指有几个分区表 我们这里就只有一个分区表
  24. size = nr_sets * sizeof(*info->mtds);
  25. info->mtds = kmalloc(size, GFP_KERNEL);
  26. memzero(info->mtds, size);
  27. nmtd = info->mtds;
  28. //设置chip中的一些函数指针 以及ecc 以及 info->sel_reg和info->sel_bit
  29. s3c2410_nand_init_chip(info, nmtd, sets);
  30. nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
  31. //flash识别等
  32. >nand_scan_ident(mtd, maxchips);
  33. >nand_set_defaults 设置chip的读写函数等 nand_commandnand_select_chip。。。
  34. //调用一些chip函数操作nand 读取id
  35. >nand_get_flash_type
  36. >chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//NAND_CMD_READID=0x90
  37. >在数组中nand_flash_ids 寻找具体信息
  38. >{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
  39. >获取页大小,擦出大小,位宽等
  40. //打印flash信息
  41. printk(KERN_INFO "NAND device: Manufacturer ID:"
  42. " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
  43. dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
  44. //获取mtd的一些信息,ecc等
  45. >nand_scan_tail(mtd);
  46. //sets 就是平台设备dev->platform_data->sets
  47. //根据分区表数目添加分区
  48. s3c2410_nand_add_partition(info, nmtd, sets);
  49. //nr_partitions 分区表的数目
  50. if (set->nr_partitions > 0 && set->partitions != NULL)
  51. add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
  52. //nbparts=set->nr_partitions=分区表数目
  53. for (i = 0; i < nbparts; i++)
  54. //创建一个mtd设备,一个分区表一个
  55. add_mtd_device(&slave->mtd)
  56. > //找mtd_notifiers链表里的list_head结构体
  57. list_for_each(this, &mtd_notifiers) {
  58. struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
  59. // 通过list_head找到struct mtd_notifier *not,调用它的add函数
  60. not->add(mtd);
  61. //字符设备 创建字符设备文件
  62. //块设备 调用gendisk 添加分区
  63. }

nand_scan

  • mtd_info 是协议相关
  • nand_chip 硬件相关

这里主要用nand_chip提供的最基本的函数,来识别FALSH,再添加一些nand_chip中其他的函数以及参数,再调用nand_scan_tail添加mtd_info的成员

  1. nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
  2. //flash识别等
  3. >nand_scan_ident(mtd, maxchips);
  4. >nand_set_defaults 设置chip的读写函数等 nand_commandnand_select_chip。。。
  5. //调用一些chip函数操作nand 读取id
  6. >nand_get_flash_type
  7. >chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//NAND_CMD_READID=0x90
  8. >在数组中nand_flash_ids 寻找具体信息
  9. >{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
  10. >获取页大小,擦出大小,位宽等
  11. //打印flash信息
  12. printk(KERN_INFO "NAND device: Manufacturer ID:"
  13. " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
  14. dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
  15. //获取mtd的一些信息,ecc等
  16. >nand_scan_tail(mtd);

s3c2410_nand_add_partition

这里是根据分区表添加分区,调用系统提供的add_mtd_partitions来操作

add_mtd_partitions

这里会最终调用mtd_notifiers中的not->add(mtd)来添加分区表

  1. //创建一个mtd设备,一个分区表一个
  2. add_mtd_device(&slave->mtd)
  3. > //找mtd_notifiers链表里的list_head结构体
  4. list_for_each(this, &mtd_notifiers) {
  5. struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
  6. // 通过list_head找到struct mtd_notifier *not,调用它的add函数
  7. not->add(mtd);

那么如何去添加到分区表呢?这里是调用了 mtd_notifiers 中的 add函数成员,搜索全局变量mtd_notifiers来查看是在哪里初始化的,继续搜索register_mtd_user 可以看到有字符设备和块设备调用

  1. void register_mtd_user (struct mtd_notifier *new)
  2. {
  3.         list_add(&new->list, &mtd_notifiers);
  4.         for (i=0; i< MAX_MTD_DEVICES; i++)
  5.                 if (mtd_table[i])
  6.                         new->add(mtd_table[i]);
  7. }

字符设备add

会创建字符设备驱动mtd%d mtd%dro

  1. init_mtdchar
  2. >register_mtd_user
  3. #define MTD_CHAR_MAJOR 90
  4. #define MTD_BLOCK_MAJOR 31
  5. init_mtdchar
  6. register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)
  7. mtd_class = class_create(THIS_MODULE, "mtd");
  8. register_mtd_user(&notifier);
  9. static struct mtd_notifier notifier = {
  10. .add = mtd_notify_add,
  11. .remove = mtd_notify_remove,
  12. };

块设备add

结论

mtd_notifiers.add== blktrans_notify_add ->>>blktrans_majors.add_mtd == mtdblock_add_mtd

在这里面会调用之前块设备驱动的gendisk的操作

代码分析如下

初始化在drivers/mtd/mtd_blkdevs.c中的register_mtd_blktrans

  1. int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
  2. {
  3. if (!blktrans_notifier.list.next)
  4. register_mtd_user(&blktrans_notifier);
  5. }
  6. static struct mtd_notifier blktrans_notifier = {
  7. .add = blktrans_notify_add,
  8. .remove = blktrans_notify_remove,
  9. };

也就是最终调用了blktrans_notify_add来添加分区表

not->add == blktrans_notify_add

这里blktrans_notify_add 又是通过一个链表来实现add的操作,也就是通过blktrans_majors中的add,继续搜索这个链表的初始化添加

  1. static void blktrans_notify_add(struct mtd_info *mtd)
  2. {
  3. struct list_head *this;
  4. list_for_each(this, &blktrans_majors) {
  5. struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
  6. tr->add_mtd(tr, mtd);
  7. }
  8. }

可以看到依然是在register_mtd_blktrans添加

  1. int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
  2. {
  3. //这里初始化 mtd_notifiers 这个链表,这个链表的add会在添加分区表操作
  4. if (!blktrans_notifier.list.next)
  5. register_mtd_user(&blktrans_notifier);
  6. // 这个是gendisk 的队列函数
  7. tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
  8. tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
  9. "%sd", tr->name);
  10. //mtd_notifiers 中的add会调用blktrans_majors链表中的add_mtd,初始化blktrans_majors这个链表
  11. INIT_LIST_HEAD(&tr->devs);
  12. list_add(&tr->list, &blktrans_majors);
  13. }

搜索register_mtd_blktrans被mtdblock_ro.cmtdblock.c调用,一个只读,我们分析可读写的

  1. static int __init init_mtdblock(void)
  2. {
  3. return register_mtd_blktrans(&mtdblock_tr);
  4. }

也就是说最终的add指向如下

  1. static struct mtd_blktrans_ops mtdblock_tr = {
  2. .name = "mtdblock",
  3. .major = 31,
  4. .part_bits = 0,
  5. .blksize = 512,
  6. .open = mtdblock_open,
  7. .flush = mtdblock_flush,
  8. .release = mtdblock_release,
  9. .readsect = mtdblock_readsect,
  10. .writesect = mtdblock_writesect,
  11. .add_mtd = mtdblock_add_mtd,
  12. .remove_dev = mtdblock_remove_dev,
  13. .owner = THIS_MODULE,
  14. };

也就是说

mtd_notifiers.add== blktrans_notify_add ->>>blktrans_majors.add_mtd == mtdblock_add_mtd

在这里面会调用之前块设备驱动的gendisk的操作

  1. static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  2. {
  3. struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
  4. dev->mtd = mtd;
  5. dev->devnum = mtd->index;
  6. dev->size = mtd->size >> 9;
  7. dev->tr = tr;
  8. add_mtd_blktrans_dev(dev);
  9. }
  10. int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
  11. {
  12. //分配一个gendisk
  13. gd = alloc_disk(1 << tr->part_bits);
  14. gd->major = tr->major;
  15. gd->first_minor = (new->devnum) << tr->part_bits;
  16. gd->fops = &mtd_blktrans_ops;
  17. gd->private_data = new;
  18. new->blkcore_priv = gd;
  19. //设置队列
  20. gd->queue = tr->blkcore_priv->rq;
  21. //添加gendisk
  22. add_disk(gd);
  23. }

队列

register_mtd_blktrans中有设置队列处理的函数

  1. // 这个是gendisk 的队列函数
  2. tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
  3. tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
  4. "%sd", tr->name);

处理函数如下,搜索blkcore_priv->thread 查看到mtd_blktrans_thread

  1. mtd_blktrans_request
  2. wake_up_process(tr->blkcore_priv->thread);
  3. static int mtd_blktrans_thread(void *arg)
  4. {
  5. while (!kthread_should_stop()) {
  6. //电梯调度算法
  7. req = elv_next_request(rq);
  8. //操作
  9. res = do_blktrans_request(tr, dev, req);
  10. //一次获取结束
  11. end_request(req, res);
  12. }

进入这个处理req的函数

  1. do_blktrans_request
  2. case READ
  3. tr->readsect
  4. case WRITE
  5. tr->writesect

搜索发现就是刚才注册的mtdblock_tr

  1. static struct mtd_blktrans_ops mtdblock_tr = {
  2. .name = "mtdblock",
  3. .major = 31,
  4. .part_bits = 0,
  5. .blksize = 512,
  6. .open = mtdblock_open,
  7. .flush = mtdblock_flush,
  8. .release = mtdblock_release,
  9. .readsect = mtdblock_readsect,
  10. .writesect = mtdblock_writesect,
  11. .add_mtd = mtdblock_add_mtd,
  12. .remove_dev = mtdblock_remove_dev,
  13. .owner = THIS_MODULE,
  14. };

程序设计

参考

  • 2440官方驱动drivers\mtd\nand\s3c2410.c
  • 简单的drivers\mtd\nand\at91_nand.c或者应该是通用的drivers\mtd\nand\plat_nand.c

平台设备文件

可以看到,linux在这里使用platform平台总线来构造这个驱动,在2440中如下:

  • 寄存器
  • 时序参数
  • 分区表
  1. struct platform_device s3c_device_nand = {
  2. .name = "s3c2410-nand",
  3. .id = -1,
  4. .num_resources = ARRAY_SIZE(s3c_nand_resource),
  5. .resource = s3c_nand_resource, //分配了1M的寄存器地址
  6. };
  7. static struct resource s3c_nand_resource[] = {
  8. [0] = {
  9. .start = S3C2410_PA_NAND,
  10. .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
  11. .flags = IORESOURCE_MEM,
  12. }};
  13. static struct s3c2410_platform_nand smdk_nand_info = {
  14. .tacls = 20,
  15. .twrph0 = 60,
  16. .twrph1 = 20,
  17. .nr_sets = ARRAY_SIZE(smdk_nand_sets),
  18. .sets = smdk_nand_sets,
  19. };
  20. static struct s3c2410_nand_set smdk_nand_sets[] = {
  21. [0] = {
  22. .name = "NAND",
  23. .nr_chips = 1,
  24. .nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
  25. .partitions = smdk_default_nand_part,
  26. },
  27. };
  28. static struct mtd_partition smdk_default_nand_part[] = {
  29. [0] = {
  30. .name = "bootloader",
  31. .size = 0x00040000,
  32. .offset = 0,},
  33. [1] = {
  34. .name = "params",
  35. .offset = MTDPART_OFS_APPEND,
  36. .size = 0x00020000,},
  37. [2] = {
  38. .name = "kernel",
  39. .offset = MTDPART_OFS_APPEND,
  40. .size = 0x00200000,},
  41. [3] = {
  42. .name = "root",
  43. .offset = MTDPART_OFS_APPEND,
  44. .size = MTDPART_SIZ_FULL,}};

必备的构造结构

查看下官方的drivers\mtd\nand\plat_nand.c,可以看出来必备的结构

  1. struct plat_nand_data {
  2. struct nand_chip chip;
  3. struct mtd_info mtd;
  4. void __iomem *io_base;
  5. #ifdef CONFIG_MTD_PARTITIONS
  6. int nr_parts;
  7. struct mtd_partition *parts;
  8. #endif
  9. };
  • nand_chip 硬件操作相关
  • mtd_info 协议相关,在调用nand_scan_tail后会将里面的读写绑定到nand_chip 的读写
  1. xxx_nand_probe
  2. >nand_scan
  3. >nand_scan_ident //设置nand——chip
  4. >nand_set_defaults
  5. >nand_get_flash_type
  6. >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

  1. data->chip.priv = &data;
  2. //mtd必须的设置
  3. data->mtd.priv = &data->chip;
  4. data->mtd.owner = THIS_MODULE;
  5. //chip的一些设置
  6. data->chip.IO_ADDR_R = data->io_base;
  7. data->chip.IO_ADDR_W = data->io_base;
  8. data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
  9. data->chip.dev_ready = pdata->ctrl.dev_ready;
  10. data->chip.select_chip = pdata->ctrl.select_chip;
  11. data->chip.chip_delay = pdata->chip.chip_delay;
  12. data->chip.options |= pdata->chip.options;
  13. //ecc的设置,我们只需要设置 NAND_ECC_SOFT即可
  14. data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
  15. data->chip.ecc.layout = pdata->chip.ecclayout;
  16. data->chip.ecc.mode = NAND_ECC_SOFT;

设置时钟

  1. /* 3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 */
  2. /* 使能NAND FLASH控制器的时钟 */
  3. clk = clk_get(NULL, "nand");
  4. clk_enable(clk); /* CLKCON'bit[4] */

设置分区表

函数原型如下

  1. int add_mtd_partitions(struct mtd_info *master,
  2. const struct mtd_partition *parts,
  3. int nbparts)
  4. //parts 为分区表
  5. //nbparts 表示这个分区表里面有几个分区
  6. static struct mtd_partition s3c_nand_parts[] = {
  7. [0] = {
  8. .name = "bootloader",
  9. .size = 0x00040000,
  10. .offset = 0,
  11. },
  12. [1] = {
  13. .name = "params",
  14. .offset = MTDPART_OFS_APPEND,
  15. .size = 0x00020000,
  16. },
  17. [2] = {
  18. .name = "kernel",
  19. .offset = MTDPART_OFS_APPEND,
  20. .size = 0x00200000,
  21. },
  22. [3] = {
  23. .name = "root",
  24. .offset = MTDPART_OFS_APPEND,
  25. .size = MTDPART_SIZ_FULL,
  26. }
  27. };

测试

内核配置去除NAND

去除掉内核的nand驱动

  1. -> Device Drivers
  2. -> Memory Technology Device (MTD) support (MTD [=y])
  3. -> NAND Device Support (MTD_NAND [=y])

烧入内核后启动可以发现无法找到文件系统,疑问nand驱动已经被去除了

  1. Root-NFS: No NFS server available, giving up.
  2. VFS: Unable to mount root fs via NFS, trying floppy.
  3. VFS: Cannot open root device "mtdblock3" or unknown-block(2,0)
  4. Please append a correct "root=" boot option; here are the available partitions:
  5. Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(2,0)

使用nfs文件系统启动

设置uboot参数

  1. 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
  2. # 可以不save 也就是本次生效
  3. save
  4. mount -t nfs -o nolock,vers=2 172.16.45.222:/home/book/stu /mnt

没有开启ECC的会提示ecc错误

  1. # insmod s3c_nand.ko
  2. NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
  3. Scanning device for bad blocks
  4. Bad eraseblock 609 at 0x04c20000

加载驱动

  1. # mkdir nfs_stu
  2. # mount -t nfs -o nolock,vers=2 172.16.45.222:/home/book/stu /nfs_stu/
  3. # cd /nfs_stu/code/
  4. # insmod s3c_nand.ko

分区表打印

挂载驱动后可以打印分区表了

  1. # insmod s3c_nand.ko
  2. NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
  3. Scanning device for bad blocks
  4. Bad eraseblock 609 at 0x04c20000
  5. Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
  6. 0x00000000-0x00040000 : "bootloader"
  7. 0x00040000-0x00060000 : "params"
  8. 0x00060000-0x00260000 : "kernel"
  9. 0x00260000-0x10000000 : "root"
  10. # ls /dev/mtd* -l
  11. crw-rw---- 1 0 0 90, 0 Jan 1 00:06 /dev/mtd0
  12. crw-rw---- 1 0 0 90, 1 Jan 1 00:06 /dev/mtd0ro
  13. crw-rw---- 1 0 0 90, 2 Jan 1 00:06 /dev/mtd1
  14. crw-rw---- 1 0 0 90, 3 Jan 1 00:06 /dev/mtd1ro
  15. crw-rw---- 1 0 0 90, 4 Jan 1 00:06 /dev/mtd2
  16. crw-rw---- 1 0 0 90, 5 Jan 1 00:06 /dev/mtd2ro
  17. crw-rw---- 1 0 0 90, 6 Jan 1 00:06 /dev/mtd3
  18. crw-rw---- 1 0 0 90, 7 Jan 1 00:06 /dev/mtd3ro
  19. brw-rw---- 1 0 0 31, 0 Jan 1 00:06 /dev/mtdblock0
  20. brw-rw---- 1 0 0 31, 1 Jan 1 00:06 /dev/mtdblock1
  21. brw-rw---- 1 0 0 31, 2 Jan 1 00:06 /dev/mtdblock2
  22. brw-rw---- 1 0 0 31, 3 Jan 1 00:06 /dev/mtdblock3

查看下分区表的信息

  1. # cat /proc/partitions
  2. # 其中blocks表示分区的容量,每个blocks是1KB
  3. major minor #blocks name
  4. 31 0 256 mtdblock0
  5. 31 1 128 mtdblock1
  6. 31 2 2048 mtdblock2
  7. 31 3 259712 mtdblock3

挂载这个分区

  1. mount /dev/mtdblock3 /mnt/

mtd-utils

编译安装,这里util依赖zlib,还要安装这个,zlib要安装到交叉编译器中,使用which查看

  1. # tar xzvf zlib-1.2.3.tar.gz
  2. # which arm-linux-gcc
  3. /opt/gcc-3.4.5-glibc-2.3.6/bin/arm-linux-gcc
  4. #其中-prefix指定zlib的安装路径,需要指定到交叉编译器所在路径
  5. #CC=arm-linux-gcc ./configure --shared --prefix=/opt/gcc-3.4.5-glibc-2.3.6/arm-linux
  6. #make
  7. #make install
  8. #------------------------------------------------------------------
  9. #mkdir tmp
  10. #tar xjf mtd-utils-05.07.23.tar.bz2 -C tmp/
  11. #cd tmp
  12. #cd util/
  13. #make

复制到nfs的文件系统提供给单板使用

  1. cp flash_erase flash_eraseall flashcp /stu/fs

使用工具格式化后挂载

FAQ : 格式化使用字符设备

因为每个分区的字符设备,其实就是对应着每个分区块设备。即/dev/mtd3对应/dev/mtdblock3

flash_eraseall, flash_erase那些命令是以ioctl等基础而实现, 而块设备不支持ioctl, 只有字符设备支持

  1. #擦除分区3,也就是文件系统的分区
  2. ./flash_eraseall /dev/mtd3
  3. #挂载刚才格式化 的分区3 也就是 root
  4. # mount -t yaffs /dev/mtdblock3 /mnt
  5. yaffs: dev is 32505859 name is "mtdblock3"
  6. yaffs: passed flags ""
  7. yaffs: Attempting MTD mount on 31.3, "mtdblock3"
  8. yaffs: auto selecting yaffs2
  9. block 591 is bad
  10. # ls /mnt
  11. lost+found
  12. #卸载
  13. umount /mnt

复制文件系统回来

卸载这个mnt可能发现无法卸载,查看使用的pid后杀掉,发现是sh占用了

  1. umount: cannot umount /mnt: Device or resource busy
  2. fuser /mnt
  3. kill -9 pid

挂载后复制文件,这个/nfs_stu/fs/*是在nfs上的文件系统

  1. cp /nfs_stu/fs/* /mnt -r
  2. # 提示一下可以忽略,因为nand驱动不全 https://blog.csdn.net/grublyboy/article/details/9664169
  3. yaffs chunk 773 was not erased

重启系统,设置启动参数,设置内核加载原来的NAND的驱动

  1. set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0 user_debug=0xff

系统可以正常启动

完整程序


  1. /* 参考
  2. * drivers\mtd\nand\s3c2410.c
  3. * drivers\mtd\nand\at91_nand.c
  4. */
  5. #include <linux/module.h>
  6. #include <linux/types.h>
  7. #include <linux/init.h>
  8. #include <linux/kernel.h>
  9. #include <linux/string.h>
  10. #include <linux/ioport.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/delay.h>
  13. #include <linux/err.h>
  14. #include <linux/slab.h>
  15. #include <linux/clk.h>
  16. #include <linux/mtd/mtd.h>
  17. #include <linux/mtd/nand.h>
  18. #include <linux/mtd/nand_ecc.h>
  19. #include <linux/mtd/partitions.h>
  20. #include <asm/io.h>
  21. #include <asm/arch/regs-nand.h>
  22. #include <asm/arch/nand.h>
  23. struct s3c_nand_regs {
  24. unsigned long nfconf ;
  25. unsigned long nfcont ;
  26. unsigned long nfcmd ;
  27. unsigned long nfaddr ;
  28. unsigned long nfdata ;
  29. unsigned long nfeccd0 ;
  30. unsigned long nfeccd1 ;
  31. unsigned long nfeccd ;
  32. unsigned long nfstat ;
  33. unsigned long nfestat0;
  34. unsigned long nfestat1;
  35. unsigned long nfmecc0 ;
  36. unsigned long nfmecc1 ;
  37. unsigned long nfsecc ;
  38. unsigned long nfsblk ;
  39. unsigned long nfeblk ;
  40. };
  41. static struct nand_chip *s3c_nand;
  42. static struct mtd_info *s3c_mtd;
  43. static struct s3c_nand_regs *s3c_nand_regs;
  44. static struct mtd_partition s3c_nand_parts[] = {
  45. [0] = {
  46. .name = "bootloader",
  47. .size = 0x00040000,
  48. .offset = 0,
  49. },
  50. [1] = {
  51. .name = "params",
  52. .offset = MTDPART_OFS_APPEND,
  53. .size = 0x00020000,
  54. },
  55. [2] = {
  56. .name = "kernel",
  57. .offset = MTDPART_OFS_APPEND,
  58. .size = 0x00200000,
  59. },
  60. [3] = {
  61. .name = "root",
  62. .offset = MTDPART_OFS_APPEND,
  63. .size = MTDPART_SIZ_FULL,
  64. }
  65. };
  66. static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
  67. {
  68. if (chipnr == -1)
  69. {
  70. /* 取消选中: NFCONT[1]设为1 */
  71. s3c_nand_regs->nfcont |= (1<<1);
  72. }
  73. else
  74. {
  75. /* 选中: NFCONT[1]设为0 */
  76. s3c_nand_regs->nfcont &= ~(1<<1);
  77. }
  78. }
  79. static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
  80. {
  81. if (ctrl & NAND_CLE)
  82. {
  83. /* 发命令: NFCMMD=dat */
  84. s3c_nand_regs->nfcmd = dat;
  85. }
  86. else
  87. {
  88. /* 发地址: NFADDR=dat */
  89. s3c_nand_regs->nfaddr = dat;
  90. }
  91. }
  92. static int s3c2440_dev_ready(struct mtd_info *mtd)
  93. {
  94. return (s3c_nand_regs->nfstat & (1<<0));
  95. }
  96. static int s3c_nand_init(void)
  97. {
  98. struct clk *clk;
  99. /* 1. 分配一个nand_chip结构体 */
  100. s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
  101. s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
  102. /* 2. 设置nand_chip */
  103. /* 设置nand_chip是给nand_scan函数使用的, 如果不知道怎么设置, 先看nand_scan怎么使用
  104. * 它应该提供:选中,发命令,发地址,发数据,读数据,判断状态的功能
  105. */
  106. s3c_nand->select_chip = s3c2440_select_chip;
  107. s3c_nand->cmd_ctrl = s3c2440_cmd_ctrl;
  108. s3c_nand->IO_ADDR_R = &s3c_nand_regs->nfdata;
  109. s3c_nand->IO_ADDR_W = &s3c_nand_regs->nfdata;
  110. s3c_nand->dev_ready = s3c2440_dev_ready;
  111. s3c_nand->ecc.mode = NAND_ECC_SOFT;
  112. /* 3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 */
  113. /* 使能NAND FLASH控制器的时钟 */
  114. clk = clk_get(NULL, "nand");
  115. clk_enable(clk); /* CLKCON'bit[4] */
  116. /* HCLK=100MHz
  117. * TACLS: 发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
  118. * TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1
  119. * TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0
  120. */
  121. #define TACLS 0
  122. #define TWRPH0 1
  123. #define TWRPH1 0
  124. s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
  125. /* NFCONT:
  126. * BIT1-设为1, 取消片选
  127. * BIT0-设为1, 使能NAND FLASH控制器
  128. */
  129. s3c_nand_regs->nfcont = (1<<1) | (1<<0);
  130. /* 4. 使用: nand_scan */
  131. s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
  132. s3c_mtd->owner = THIS_MODULE;
  133. s3c_mtd->priv = s3c_nand;
  134. nand_scan(s3c_mtd, 1); /* 识别NAND FLASH, 构造mtd_info */
  135. /* 5. add_mtd_partitions */
  136. add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
  137. //add_mtd_device(s3c_mtd);
  138. return 0;
  139. }
  140. static void s3c_nand_exit(void)
  141. {
  142. del_mtd_partitions(s3c_mtd);
  143. kfree(s3c_mtd);
  144. iounmap(s3c_nand_regs);
  145. kfree(s3c_nand);
  146. }
  147. module_init(s3c_nand_init);
  148. module_exit(s3c_nand_exit);
  149. MODULE_LICENSE("GPL");

MTD下的Nand驱动的更多相关文章

  1. MTD中的nand驱动初步分析---面向u-boot

    之前提到nand驱动的初始化分析,有一个结构体 struct mtd_info始终贯穿这些代码 再来分析一下这个结构体的基本功能,如何初始化,如何使用 一.分析过程 看看结构体的出现和使用方式 第一次 ...

  2. Linux MTD下获取Nand flash各个参数的过程的详细解析【转】

    本文转载自:https://www.crifan.com/files/doc/docbook/nand_get_type/release/html/nand_get_type.html 文章不错可以看 ...

  3. 基于MTD的NAND驱动开发、K9F1G08 2K page、Yaffs2 Files System

    转载:http://hi.baidu.com/cui1206/item/1d4119e376132513585dd886 基于MTD的NAND驱动(linux-2.6.22.10内核),目前已可以在该 ...

  4. Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  5. 24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  6. NAND驱动

    NAND FLASH是一个存储芯片 那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A" 问1. 原理图上NAND FLASH和S3C2440之间只有数据线,      ...

  7. u-boot的nand驱动写过程分析

    从命令说起,在u-boot输入下列命令: nand write 40008000 0 20000 命令的意思是将内存0x40008000开始的部分写入nand,从nand地址0开始写,写入长度是0x2 ...

  8. u-boot下的DM驱动模型 阶梯状 (转)

    U-boot 下DM驱动模型的相关笔记要注意的关键两点: DM驱动模型的一般流程bind->ofdata_to_platdata(可选)->probe    启动,bind操作时单独完成的 ...

  9. linux下的声卡驱动架构

    1.linux下的声卡驱动架构主要分为OSS架构和ALSA架构. 2.OSS架构 OSS全称是Open Sound System,叫做开放式音频系统,这种早期的音频系统这种基于文件系统的访问方式,这意 ...

随机推荐

  1. SQL学习笔记---常用命令

    常用命令 变量 1.声明 declare @变量名 类型,… 2.赋值 1.同时赋值多个变量(可以结合查询) select @变量名=表达式1,表达式2 2.单个赋值(推荐) set @变量名=表达式 ...

  2. PowerDesigner导出SQL,注释为空时以name代替

    版本 操作步骤 打开Edit Current DBMS 选中Script->Objects->Column->Add 将Value中的内容全部替换为如下 %20:COLUMN% [% ...

  3. ThinkPad E470笔记本电脑无声音、无线wifi功能(灰色)

    最近有同事找我看他的笔记本没有wifi,型号是ThinkPadE470 ,上网搜了下提问的挺多,写一个看看有什么帮助没 看了下笔记本wifi标志是灰色显示只有飞行模式,启用了一下热键 fn+F3 没什 ...

  4. .net 获取远程访问的ip

    这两天一直做获取远程访问的ip和自己的ip相关的问题. 在解决获取ip相关问题的时候,主要使用了上下文对象,httpcontext对象.原理很简单,内部有两大对象,request和response.里 ...

  5. [LeetCode] 7. 整数反转

    题目链接:https://leetcode-cn.com/problems/reverse-integer/ 题目描述: 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 ...

  6. day23--面向对象之封装、继承、多态

    面向对象的三大特性: 封装: 在类的内部(class内部)可以由属性和方法,外部代码可以通过直接调用实例变量的方法来操作数据,这样就隐藏了内部的逻辑,但是外部还是可以直接修改实例的属性,因此当需求中存 ...

  7. SpringBoot实战(八)之RabbitMQ

    什么是RabbitMQ? RabbitMQ 是一个消息代理.它的核心原理非常简单:接收和发送消息.你可以把它想像成一个邮局:你把信件放入邮箱,邮递员就会把信件投递到你的收件人处.在这个比喻中,Rabb ...

  8. 学号 20175329 2018-2019-3《Java程序设计》第六周学习总结

    学号 20175329 2018-2019-3<Java程序设计>第六周学习总结 教材学习内容 第七章 内部类与异常类 内部类与外嵌类之间的重要关系如下: 内部类的外嵌类的成员变量在内部类 ...

  9. App遍历探讨(含源代码)

    好像好久没有更新博客了,之前写的几篇博客关于自动化的框架的居多,其中好多博友向我提了好多问题,我没有回复.这里给博友道个歉~ ~ 总结几点原因如下: 1.我一般很少上博客,看到了都是好几天之前的问题 ...

  10. js字符串转时间

    function StrToDateTime(value) { if (value) { return (new Date(Date.parse(value.replace(/-/g, "/ ...