Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3548185
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类: LINUX

2014-10-02 18:36:57

1.Linux-MTD Subsystem

    FLASH在嵌入式系统中是必不可少的,它是bootloaderlinux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR FLASHNAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。

在引入MTDLinux系统中FLASH设备驱动可分为四层,如图:


1. 硬件驱动层

FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。

2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.cmtdpart.c),另一部分是各个特定的FLASH的数据,例如分区。

3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。

4. 设备节点:通过mknod/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。

也可通过下图理解:



从上图可以看出,MTD设备层与原始设备层打交道。通过分析源代码我们可以知道当上层要求对FLASH进行读写时,它会像设备层发出请求,设备层的读写函数会调用原始设备层中的读写函数,即mtd_info结构体(mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数会调用nand_chipnand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以当我们写一个flash硬件驱动程序时,有以下步骤:

1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。

2. 在模块加载时为每一个chip(主分区)分配mtd_infonand_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_infomtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区(不包含主分区)分配一个mtd_info结构体,填充,并注册。

 

2.nand flash驱动程序实例分析

我们以2.6.26内核中s3c2410nand flash驱动程序为例来分析一下这个过程,这里的flash驱动被写成了platform驱动的形式。我们下面分析其过程:

1. 注册nand flash设备

nand flash分区:

 

linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c: 

 

static struct mtd_partition smdk_default_nand_part[] {

[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

linux/drivers/mtd/nand/s3c2410.c:

 

static struct platform_driver s3c2410_nand_driver {

.probe s3c2410_nand_probe,

.remove s3c2410_nand_remove,

.suspend s3c24xx_nand_suspend,

.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 Electronics\n");

 

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 info\n");

err -ENOMEM;

goto exit_error;

}

 

memzero(info, sizeof(*info));         //s3c2410_nand_info清零

platform_set_drvdata(pdev, info); //pdev->dev->p->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 clock\n");

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 region\n");

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 region\n");

err -EIO;

goto exit_error;

}

 

dev_dbg(&pdev->dev, "mapped registers at %p\n", 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 storage\n");

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, nmtd, 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, nmtd, sets);  //添加分区

}

 

if (sets != NULL)

sets++; //注意这里sets++,指向下一个plat->sets里的set

}

 

if (allow_clk_stop(info)) {

dev_info(&pdev->dev, "clock idle support enabled\n");

clk_disable(info->clk);

}

 

pr_debug("initialised ok\n");

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, loff_t from, size_t len);

 

 

int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

 

 

int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

int (*read_oob) (struct mtd_info *mtd, loff_t from,

 struct mtd_oob_ops *ops);

int (*write_oob) (struct mtd_info *mtd, loff_t to,

 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, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);

int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

 

 

int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, 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, loff_t ofs, 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, const uint8_t *buf, int len);

void (*select_chip)(struct mtd_info *mtd, int chip);

int (*block_bad)(struct mtd_info *mtd, loff_t ofs, 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;

}; 


阅读(559) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~