mtd
?
1.Linux-MTD Subsystem FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。 在引入MTD后Linux系统中FLASH设备驱动可分为四层,如图:
1. 硬件驱动层 FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。 2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.c、mtdpart.c),另一部分是各个特定的FLASH的数据,例如分区。 3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)www.linuxidc.com和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。 4. 设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。 也可通过下图理解: ? 从上图可以看出,MTD设备层与原始设备层打交道。通过分析源代码我们可以知道当上层要求对FLASH进行读写时,它会像设备层发出请求,设备层的读写函数会调用原始设备层中的读写函数,即mtd_info结构体(mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数会调用nand_chip(nand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以当我们写一个flash硬件驱动程序时,有以下步骤: 1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。 2. 在模块加载时为每一个chip(主分区)分配mtd_info和nand_chip的内存,根据目标板nand 控制器的特殊情况初始化nand_chip中的实现对FLASH操作的成员函数,如hwcontrol()、dev_ready()、read_byte()、write_byte()等。填充mtd_info,并将其priv成员指向nand_chip。 3. 以mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。nand_scan()函数会从FLASH芯片中读取其参数,填充相应nand_chip成员。 4. 如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区(不包含主分区)分配一个mtd_info结构体,填充,并注册。 ? ? ? ? 2.nand flash驱动程序实例分析 我们以2.6.26内核中s3c2410的nand flash驱动程序为例来分析一下这个过程,这里的flash驱动被写成了platform驱动的形式。我们下面分析其过程: 1. 注册nand flash设备 nand flash分区: linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:
[0] = { name: "bootloader", size: 0x00100000, offset: 0x0, }, [1] = { name: "kernel", size: 0x00300000, offset: 0x00100000, [2] = { name: "root", size: 0x02800000, offset: 0x00400000, }; static struct s3c2410_nand_set smdk_nand_sets[] = {? //该数组为chip集合,这里我们只有一片chip [0] = { .name = "NAND", .nr_chips = 1, .nr_partitions = ARRAY_SIZE(smdk_default_nand_part), .partitions = smdk_default_nand_part, }; static struct s3c2410_platform_nand smdk_nand_info = {? //这里将许多数据作为platform_data传入包括chip数组 .tacls = 20, .twrph0 = 60, .twrph1 = 20, .nr_sets = ARRAY_SIZE(smdk_nand_sets), .sets = smdk_nand_sets, }; nand控制器资源: linux2.6.26.8/arch/arm/plat-s3c24xx/devs.c static struct resource s3c_nand_resource[] = { [0] = { .start = S3C2410_PA_NAND, .end?? = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1, .flags = IORESOURCE_MEM, } }; struct platform_device s3c_device_nand = { .name?? = "s3c2410-nand", .id?? =? -1, .num_resources?? = ARRAY_SIZE(s3c_nand_resource), .resource?? = s3c_nand_resource, }; 注册nand flash作为platform device: linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c: static struct platform_device __initdata *smdk_devs[] = { &s3c_device_nand, … }; void __init smdk_machine_init(void) { … s3c_device_nand.dev.platform_data = &smdk_nand_info;?? //注意这里的赋值,在nand? flash驱动程序的probe函数里面利用了这里赋值的数据 platform_add_devices(smdk_devs,ARRAY_SIZE(smdk_devs)); s3c2410_pm_init(); }
2. 注册nand flash driver static struct platform_driver s3c2410_nand_driver = { .probe = s3c2410_nand_probe, .remove = s3c2410_nand_remove,
.resume = s3c24xx_nand_resume, .driver = { .name = "s3c2410-nand", .owner = THIS_MODULE, }; static int __init s3c2410_nand_init(void) { printk("S3C24XX NAND Driver,(c) 2004 Simtec Electronicsn"); ? platform_driver_register(&s3c2412_nand_driver); platform_driver_register(&s3c2440_nand_driver); return platform_driver_register(&s3c2410_nand_driver); } module_init(s3c2410_nand_init); 当platform_driver驱动被加载时或者是当platform_device被注册时,总线驱动程序 会查找与设备匹配的驱动程序,找到时设备驱动程序的probe函数会被调用,下面我们来分析一下在我们驱动程序中的probe函数: static int s3c2410_nand_probe(struct platform_device *dev) { return s3c24xx_nand_probe(dev,TYPE_S3C2410); } static int s3c24xx_nand_probe(struct platform_device *pdev, ????? enum s3c_cpu_type cpu_type) { struct s3c2410_platform_nand *plat = to_nand_plat(pdev); struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; struct resource *res; int err = 0; int size; int nr_sets; int setno; ? pr_debug("s3c2410_nand_probe(%p)n",pdev); ? info = kmalloc(sizeof(*info),GFP_KERNEL);? //分配s3c2410_nand_info内存 if (info == NULL) { dev_err(&pdev->dev,"no memory for flash infon"); err = -ENOMEM; goto exit_error; } ? memzero(info,sizeof(*info));???????? //将s3c2410_nand_info清零 platform_set_drvdata(pdev,info); //pdev->dev->driver_data = info ? spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); ? ? ? info->clk = clk_get(&pdev->dev,"nand"); if (IS_ERR(info->clk)) { dev_err(&pdev->dev,"failed to get clockn"); err = -ENOENT; goto exit_error; } ? clk_enable(info->clk); ? ? ? ? res? = pdev->resource; size = res->end - res->start + 1; ? info->area = request_mem_region(res->start,size,pdev->name); ? if (info->area == NULL) { dev_err(&pdev->dev,"cannot reserve register regionn"); err = -ENOENT; goto exit_error; } ? info->device???? = &pdev->dev; info->platform?? = plat; info->regs?????? = ioremap(res->start,size);? //存储nand控制器寄存器虚拟地 址 info->cpu_type?? = cpu_type; ? if (info->regs == NULL) { dev_err(&pdev->dev,"cannot reserve register regionn"); err = -EIO; goto exit_error; } ? dev_dbg(&pdev->dev,"mapped registers at %pn",info->regs); ? ? ? err = s3c2410_nand_inithw(info,pdev);? //设置TACLS TWRPH0 TWRPH1 if (err != 0) goto exit_error; ? sets = (plat != NULL) ? plat->sets : NULL;??? //sets指向plat->sets数组的首地址 nr_sets = (plat != NULL) ? plat->nr_sets : 1;?? //plat->sets中的chips数目 ? info->mtd_count = nr_sets; size = nr_sets * sizeof(*info->mtds);????? info->mtds = kmalloc(size,GFP_KERNEL); if (info->mtds == NULL) { dev_err(&pdev->dev,"failed to allocate mtd storagen"); err = -ENOMEM; goto exit_error; } memzero(info->mtds,size); //将申请的s3c2410_nand_mtd结构体数组清零 nmtd = info->mtds; for (setno = 0; setno < nr_sets; setno++,nmtd++) { pr_debug("initialising set %d (%p,info %p)n",setno,nmtd,info); ? s3c2410_nand_init_chip(info,sets); //初始化s3c2410_nand_mtd结构 体中的chip成员和mtd成员,且mtd.priv = chip ? nmtd->scan_res = nand_scan_ident(&nmtd->mtd, ?(sets) ? sets->nr_chips : 1); //设置nand_chip一些成员 的默认值并探测FLASH,并读出FLASH参数,填入nand_chip ? if (nmtd->scan_res == 0) { s3c2410_nand_update_chip(info,nmtd); // nand_scan_tail(&nmtd->mtd);?? //设置nand_chip中所有未被设置的 函数指针的值,并填充相关mtd_info成员,若需要建立bad block table s3c2410_nand_add_partition(info,sets);? //添加分区 } ? if (sets != NULL) sets++; //注意这里sets++,指向下一个plat->sets里的set } ? if (allow_clk_stop(info)) { dev_info(&pdev->dev,"clock idle support enabledn"); clk_disable(info->clk); } ? pr_debug("initialised okn"); return 0; ? ?exit_error: s3c2410_nand_remove(pdev); ? if (err == 0) err = -EINVAL; return err; } ? ? 附:几个机构体 ? ? struct mtd_info { u_char type; u_int32_t flags; u_int32_t size;? // Total size of the MTD ? ? u_int32_t erasesize; ? ? u_int32_t writesize; ? u_int32_t oobsize;?? // Amount of OOB data per block (e.g. 16) u_int32_t oobavail;? // Available OOB bytes per block ? // Kernel-only stuff starts here. char *name; int index; ? ? struct nand_ecclayout *ecclayout; ? ? int numeraseregions; struct mtd_erase_region_info *eraseregions; ? ? int (*erase) (struct mtd_info *mtd,struct erase_info *instr); ? ? ? int (*point) (struct mtd_info *mtd,loff_t from,size_t len, size_t *retlen,void **virt,resource_size_t *phys); ? ? void (*unpoint) (struct mtd_info *mtd,size_t len); ? ? int (*read) (struct mtd_info *mtd,size_t *retlen,u_char *buf); int (*write) (struct mtd_info *mtd,loff_t to,const u_char *buf); ? ? ? int (*panic_write) (struct mtd_info *mtd,const u_char *buf); ? int (*read_oob) (struct mtd_info *mtd, ?struct mtd_oob_ops *ops); int (*write_oob) (struct mtd_info *mtd, ?struct mtd_oob_ops *ops); ? ? int (*get_fact_prot_info) (struct mtd_info *mtd,struct otp_info *buf,size_t len); int (*read_fact_prot_reg) (struct mtd_info *mtd,u_char *buf); int (*get_user_prot_info) (struct mtd_info *mtd,size_t len); int (*read_user_prot_reg) (struct mtd_info *mtd,u_char *buf); int (*write_user_prot_reg) (struct mtd_info *mtd,u_char *buf); int (*lock_user_prot_reg) (struct mtd_info *mtd,size_t len); ? ? int (*writev) (struct mtd_info *mtd,const struct kvec *vecs,unsigned long count,size_t *retlen); ? ? void (*sync) (struct mtd_info *mtd); ? ? int (*lock) (struct mtd_info *mtd,loff_t ofs,size_t len); int (*unlock) (struct mtd_info *mtd,size_t len); ? ? int (*suspend) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd); ? ? int (*block_isbad) (struct mtd_info *mtd,loff_t ofs); int (*block_markbad) (struct mtd_info *mtd,loff_t ofs); ? struct notifier_block reboot_notifier;? ? ? struct mtd_ecc_stats ecc_stats; ? int subpage_sft; ? void *priv; ? struct module *owner; int usecount; ? ? int (*get_device) (struct mtd_info *mtd); void (*put_device) (struct mtd_info *mtd); }; ? struct nand_chip { void? __iomem *IO_ADDR_R; void? __iomem *IO_ADDR_W; ? uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); void (*write_buf)(struct mtd_info *mtd,const uint8_t *buf,int len); void (*read_buf)(struct mtd_info *mtd,uint8_t *buf,int len); int (*verify_buf)(struct mtd_info *mtd,int len); void (*select_chip)(struct mtd_info *mtd,int chip); int (*block_bad)(struct mtd_info *mtd,int getchip); int (*block_markbad)(struct mtd_info *mtd,loff_t ofs); void (*cmd_ctrl)(struct mtd_info *mtd,int dat, ??? unsigned int ctrl); int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd,unsigned command,int column,int page_addr); int (*waitfunc)(struct mtd_info *mtd,struct nand_chip *this); void (*erase_cmd)(struct mtd_info *mtd,int page); int (*scan_bbt)(struct mtd_info *mtd); int (*errstat)(struct mtd_info *mtd,struct nand_chip *this,int state,int status,int page); int (*write_page)(struct mtd_info *mtd,struct nand_chip *chip, ????? const uint8_t *buf,int page,int cached,int raw); ? int chip_delay; unsigned int options; ? int page_shift; int phys_erase_shift; int bbt_erase_shift; int chip_shift; int numchips; unsigned long chipsize; int pagemask; int pagebuf; int subpagesize; uint8_t cellinfo; int badblockpos; ? nand_state_t state; ? uint8_t *oob_poi; struct nand_hw_control? *controller; struct nand_ecclayout *ecclayout; ? struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; struct nand_hw_control hwcontrol; ? struct mtd_oob_ops ops; ? uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; ? struct nand_bbt_descr *badblock_pattern; ? void *priv; };
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |