Flash驱动
Flash 在嵌入式系统中是必不可少的,它是 BootLoader、Linux 内核和文件系统的最佳载体。在 Linux 内核中,引入了 MTD 层为 NOR Flash 和 NAND Flash 设备提供统一的接口,从而使得 Flash 驱动的设计工作大为简化。 Linux MTD 系统层次 下面列出了linux MTD系统的架构: 如上图所示,在引入 MTD后,Linux 系统中的 Flash 设备驱动及接口可分为 4 层,从上到下依次是:设备节点、MTD 设备层、MTD原始设备层和硬件驱动层,这 4 层的作用如下: 1.硬件驱动层: Flash 硬件驱动层负责 Flash 硬件设备的读,擦除,写、Linux MTD设备的 NOR Flash 芯片驱动位于 drivers/mtd/chips 子目录下,NAND 型 Flash的驱动程序则位于/drivers/mtd/nand子目录下。 MTD 原始设备层由两部分组成,一部分是 MTD 原始设备的通用代码,另一部分是各个特定的 Flash 的数据,例如分区。 基于 MTD 原始设备,Linux 系统可以定义出 MTD 的块设备(主设备号 31)和字符设备(设备号 90),构成 MTD 设备层。MTD 字符设备的定义在 mtdchar.c 中实现,通过注册一系列 file_operation 函数(lseek、open、close、read、write、ioctl)可实现对 MTD 设备的读写和控制。MTD块设备则是定义了一个描述 MTD 块设备的结构 mtdblk_dev,并声明了一个名为 mtdblks 的指针数组,这数组中的每一个 mtdblk_dev 和 mtd_table 中的每一个 mtd_info 一一对应。 Linux MTD 系统接口 如上图所示,在引入 MTD 后,底层 Flash 驱动直接与 MTD 原始设备层交互,利用其提供的接口注册设备和分区。用于描述 MTD 原始设备的数据结构是mtd_info,这其中定义了大量关于 MTD 的数据和操作函数,这个结构体的定义如下代码所示。mtd_info 是表示MTD 原始设备的结构体,每个分区也被认为是一个 mtd_info,例如,如果有两个 MTD 原始设备,而每个上有 3 个分区,在系统中就将共有 6 个 mtd_info 结构体,这些 mtd_info的指针被存放在名为 mtd_table 的数组里。 mtd_info的定义: struct mtd_info { u_char type;// 内存技术的类型 u_int32_t flags;//标志位 u_int32_t size; //mtd 设备的大小 u_int32_t erasesize;//主要的擦除块大小(同一个 //mtd 设备可能有数种不同的 erasesize) /* Minimal writable flash unit size. In case of NOR flash it is 1 (even * though individual bits can be cleared),in case of NAND flash it is * one NAND page (or half,or one-fourths of it),in case of ECC-ed NOR * it is of ECC block size,etc. It is illegal to have writesize = 0. * Any driver registering a struct mtd_info must ensure a writesize of * 1 or larger. */ u_int32_t writesize;//写的大小? u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)// oob 数据大小 u_int32_t oobavail; // Available OOB bytes per block 每个块中可用的oob的大小 // Kernel-only stuff starts here. char *name; int index;//索引 /* ecc layout structure pointer - read only ! */ struct nand_ecclayout *ecclayout; /* Data for variable erase regions. If numeraseregions is zero,* it means that the whole device has erasesize as given above. */ int numeraseregions;//不同 erasesize 的区域的数目(通常是 1) struct mtd_erase_region_info *eraseregions; //此 routine 用于将一个 erase_info 加入 erase queue int (*erase) (struct mtd_info *mtd,struct erase_info *instr); /* This stuff for eXecute-In-Place *//*针对 eXecute-In-Place */ int (*point) (struct mtd_info *mtd,loff_t from,size_t len,size_t *retlen,u_char **mtdbuf); /* We probably shouldn't allow XIP if the unpoint isn't a NULL *//* 如果 unpoint 为空,不允许 XIP */ void (*unpoint) (struct mtd_info *mtd,u_char * addr,size_t len); //读 Flash int (*read) (struct mtd_info *mtd,u_char *buf); int (*write) (struct mtd_info *mtd,loff_t to,const u_char *buf);//写 Flash int (*read_oob) (struct mtd_info *mtd,struct mtd_oob_ops *ops);//读 out-of-band int (*write_oob) (struct mtd_info *mtd,struct mtd_oob_ops *ops);//写 out-of-band /* * Methods to access the protection register area,present in some * flash devices. The user data is one time programmable but the * factory data is read only. */ //下面是读取flash中的寄存器,只读 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); /* kvec-based read/write methods. NB: The 'count' parameter is the number of _vectors_,each of which contains an (ofs,len) tuple. *//* iovec-based 读写函数,对于 NAND Flash 需要针对它定义 */ int (*writev) (struct mtd_info *mtd,const struct kvec *vecs,unsigned long count,size_t *retlen); /* Sync */ void (*sync) (struct mtd_info *mtd); /* Chip-supported device locking *//* 设备锁 */ int (*lock) (struct mtd_info *mtd,loff_t ofs,size_t len); int (*unlock) (struct mtd_info *mtd,size_t len); /* Power Management functions *//* 电源管理函数*/ int (*suspend) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd); /* Bad block management functions */ 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; /* default mode before reboot */ /* ECC status information */ struct mtd_ecc_stats ecc_stats; /* Subpage shift (NAND) */ int subpage_sft; void *priv;//私有数据 struct module *owner; int usecount; /* If the driver is something smart,like UBI,it may need to maintain * its own reference counting. The below functions are only for driver. * The driver may register its callbacks. These callbacks are not * supposed to be called by MTD users */ int (*get_device) (struct mtd_info *mtd); void (*put_device) (struct mtd_info *mtd); };mtd_info 的 type 字段 给出底层物理设备的类型,包括 MTD_RAM(内存)、MTD_ROM(只读内存)、MTD_ NORFlash(nor)、MTD_NANDFlash(nand)、MTD_PEROM 等,用于表示底层的设备是个什么设备。 flags 字段 包括 MTD_ERASEABLE(可擦除)、MTD_WRITEB_WRITEABLE(可编程)、MTD_XIP(可片内执行)、MTD_OOB(NAND 带外数据)、MTD_ECC(支持自动 ECC)等。某些内存技术支持带外数据(OOB),例如,NAND Flash 每 512字节就会有 16 个字节的 表明了 ECC 的类型,包括 MTD_ECC_NONE(不支持自动 ECC)、MTD_ECC_ RS_DiskOnChip(DiskOnChip 上自动 ECC)、MTD_ECC_SW(Toshiba &?Samsung 设备 SW ECC)。 mtdcore.c (路径driversmtd)中定义了 MTD 设备数组: struct mtd_info *mtd_table[MAX_MTD_DEVICES]; EXPORT_SYMBOL_GPL(mtd_table);最多可以有 MAX_MTD_DEVICES(默认定义为 32)个设备,每个 MTD 分区也算一个 MTD 设备。 mtd_info 中的 read()、write()、read_ecc()、write_ecc()、read_oob()、write_oob()是 MTD 设备驱动要实现的主要函数,后面我们将看到,在 NOR 和 NAND 的驱动代码中几乎看不到 mtd_info 的成员函数(也即这些成员函数对于 Flash 芯片驱动是透明的),这是因为 Linux 在 MTD 的下层实现了针对 NOR Hash 和 NAND Hash 的通用的mtd_info 成员函数。 Flash 驱动中使用如下两个函数注册和注销 MTD 设备(路径:driversmtdMtdcore.c?): int add_mtd_device(struct mtd_info *mtd); int del_mtd_device (struct mtd_info *mtd);下面代码所示的 mtd_part 结构体用于描述分区,其 mtd_info 结构体成员用于描述本分区,它会被加入到 mtd_table 中,其大部分成员由其主分区 mtd_part->master 决定,各种函数也指向主分区的相应函数,而主分区(其大小涵盖所有分区)则不作为一个 MTD原始设备加入 mtd_table。 struct mtd_part { struct mtd_info mtd;//分区的信息(大部分由其 master 决定) struct mtd_info *master;//该分区的主分区 u_int32_t offset;//该分区的偏移地址 int index;//分区号 struct list_head list; int registered; };mtd_partition 会在 MTD 原始设备层调用 add_mtd_partions()时传递分区信息用,这个结构体的定义如下所示。 struct mtd_partition { char *name; /* 标识字符串 */ u_int32_t size; /* 分区大小 */ u_int32_t offset; /* 主 MTD 空间内的偏移*/ u_int32_t mask_flags; /* 掩码标志 */ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ struct mtd_info **mtdp; /* pointer to store the MTD object */ };Flash 驱动中使用如下两个函数注册和注销分区: int add_mtd_partitions(struct mtd_info *,const struct mtd_partition *,int); int del_mtd_partitions(struct mtd_info *);add_mtd_partitions()会对每一个新建分区建立一个新的 mtd_part 结构体,将其加入 mtd_ partitions 中,并调用 add_mtd_device()将此分区作为 MTD 设备加入 mtd_table。成功时返回 0,如果分配 mtd_part 时内存不足,则返回-ENOMEM。 del_mtd_partitions()的作用是对于 mtd_partitions 上的每一个分区,如果它的主分区是 master(参数 master 是被删除分区的主分区)则将它从 mtd_partitions 和 mtd_table,中删除并释放掉,这个函数会调用 del_mtd_device()。 add_mtd_partitions()中新建的 mtd_part 需要依赖传入的 mtd_partition 参数对其进行初始化,代码如下所示: int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts) { struct mtd_part *slave; u_int32_t cur_offset = 0; int i; printk (KERN_NOTICE "Creating %d MTD partitions on "%s":n",nbparts,master->name); for (i = 0; i < nbparts; i++) { /* allocate the partition structure */ slave = kzalloc (sizeof(*slave),GFP_KERNEL); if (!slave) { printk ("memory allocation error while creating partitions for "%s"n",master->name); del_mtd_partitions(master); return -ENOMEM; } list_add(&slave->list,&mtd_partitions); /* 设置该分区的 MTD 对象 mtd_info*/ slave->mtd.type = master->type; slave->mtd.flags = master->flags & ~parts[i].mask_flags; slave->mtd.size = parts[i].size; slave->mtd.writesize = master->writesize; slave->mtd.oobsize = master->oobsize; slave->mtd.oobavail = master->oobavail; slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.name = parts[i].name; slave->mtd.owner = master->owner; slave->mtd.read = part_read; slave->mtd.write = part_write; if(master->point && master->unpoint){ slave->mtd.point = part_point; slave->mtd.unpoint = part_unpoint; } if (master->read_oob) slave->mtd.read_oob = part_read_oob; if (master->write_oob) slave->mtd.write_oob = part_write_oob; if(master->read_user_prot_reg) slave->mtd.read_user_prot_reg = part_read_user_prot_reg; if(master->read_fact_prot_reg) slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; if(master->write_user_prot_reg) slave->mtd.write_user_prot_reg = part_write_user_prot_reg; if(master->lock_user_prot_reg) slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; if(master->get_user_prot_info) slave->mtd.get_user_prot_info = part_get_user_prot_info; if(master->get_fact_prot_info) slave->mtd.get_fact_prot_info = part_get_fact_prot_info; if (master->sync) slave->mtd.sync = part_sync; if (!i && master->suspend && master->resume) { slave->mtd.suspend = part_suspend; slave->mtd.resume = part_resume; } if (master->writev) slave->mtd.writev = part_writev; if (master->lock) slave->mtd.lock = part_lock; if (master->unlock) slave->mtd.unlock = part_unlock; if (master->block_isbad) slave->mtd.block_isbad = part_block_isbad; if (master->block_markbad) slave->mtd.block_markbad = part_block_markbad; slave->mtd.erase = part_erase; slave->master = master; slave->offset = parts[i].offset; slave->index = i; if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; if (slave->offset == MTDPART_OFS_NXTBLK) { slave->offset = cur_offset; if ((cur_offset % master->erasesize) != 0) { /* Round up to next erasesize */ slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; printk(KERN_NOTICE "Moving partition %d: " "0x%08x -> 0x%08xn",i,cur_offset,slave->offset); } } if (slave->mtd.size == MTDPART_SIZ_FULL) slave->mtd.size = master->size - slave->offset; cur_offset = slave->offset + slave->mtd.size; printk (KERN_NOTICE "0x%08x-0x%08x : "%s"n",slave->offset,slave->offset + slave->mtd.size,slave->mtd.name); /* let's do some sanity checks */ if (slave->offset >= master->size) { /* let's register it anyway to preserve ordering */ slave->offset = 0; slave->mtd.size = 0; printk ("mtd: partition "%s" is out of reach -- disabledn",parts[i].name); } if (slave->offset + slave->mtd.size > master->size) { slave->mtd.size = master->size - slave->offset; printk ("mtd: partition "%s" extends beyond the end of device "%s" -- size truncated to %#xn",parts[i].name,master->name,slave->mtd.size); } if (master->numeraseregions>1) { /* Deal with variable erase size stuff */ int i; struct mtd_erase_region_info *regions = master->eraseregions; /* Find the first erase regions which is part of this partition. */ for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) ; for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { if (slave->mtd.erasesize < regions[i].erasesize) { slave->mtd.erasesize = regions[i].erasesize; } } } else { /* Single erase size */ slave->mtd.erasesize = master->erasesize; } if ((slave->mtd.flags & MTD_WRITEABLE) && (slave->offset % slave->mtd.erasesize)) { /* Doesn't start on a boundary of major erase size */ /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ slave->mtd.flags &= ~MTD_WRITEABLE; printk ("mtd: partition "%s" doesn't start on an erase block boundary -- force read-onlyn",parts[i].name); } if ((slave->mtd.flags & MTD_WRITEABLE) && (slave->mtd.size % slave->mtd.erasesize)) { slave->mtd.flags &= ~MTD_WRITEABLE; printk ("mtd: partition "%s" doesn't end on an erase block -- force read-onlyn",parts[i].name); } slave->mtd.ecclayout = master->ecclayout; if (master->block_isbad) { uint32_t offs = 0; while(offs < slave->mtd.size) { if (master->block_isbad(master,offs + slave->offset)) slave->mtd.ecc_stats.badblocks++; offs += slave->mtd.erasesize; } } if(parts[i].mtdp) { /* store the object pointer (caller may or may not register it */ *parts[i].mtdp = &slave->mtd; slave->registered = 0; } else { /* register our partition */ add_mtd_device(&slave->mtd); slave->registered = 1; } } return 0; }最后,为了使系统能支持 MTD 字符设备与块设备及 MTD 分区,在编译内核时应该包括相应的配置选项,如下所示。 NAND Flash 驱动 如上图所示,Linux 内核在 MTD的 下 层 实 现 了 通 用 的 NAND 驱 动 ( 主 要 通 过drivers/mtd/nand/nand_base.c文件实现),因此芯片级的 NAND 驱动不再需要实现 mtd_info 中的 read()、write()、read_oob()、write_oob()等成员函数,而主体转移到了nand_chip 数据结构。 struct nand_chip {
void __iomem *IO_ADDR_R;//读 8 根 I/O 线的地址
void __iomem *IO_ADDR_W;//写 8 根 I/O 线的地址
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);//控制 CE 信号
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,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;
}; 先附上一副图:
与 NOR Flash 类似,由于有了 MTD 层,完成一个 NAND?Flash 驱动在 Linux 中的工作量也很小,如上图所示,主要的工作如下。 (3) mtd_info 为参数调用 nand_scan()函数探测 NAND Flash是否存在,该函数的原型为: int nand_scan (struct mtd_info *mtd,int maxchips);nand_scan()函数会读取 NAND 芯片 ID,并根据 mtd->priv 即 nand_chip 中的成员初始化 mtd_info。 (4)如果要分区,则以 mtd_info 和 mtd_partition 为参数调用 add_mtd_partitions(),添加分区信息。 下面所示为一个简单的 NAND Flash 设备驱动模板: #define CHIP_PHYSICAL_ADDRESS ... #define NUM_PARTITIONS 2 static struct mtd_partition partition_info[] = { { .name = "Flash partition 1",? .offset = 0,? .size = 8 * 1024 * 1024 },{ .name = "Flash partition 2",? .offset = MTDPART_OFS_NEXT,? .size = MTDPART_SIZ_FULL },}; int __init board_init(void) { struct nand_chip *this; int err = 0; /* 为 MTD 设备结构体和 nand_chip 分配内存 */ board_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),GFP_KERNEL); if (!board_mtd) { printk("Unable to allocate NAND MTD device structure.n"); err = - ENOMEM; goto out; } /* 初始化结构体 */ memset((char*)board_mtd,sizeof(struct mtd_info) + sizeof(struct nand_chip));
/* 映射物理地址 */
baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS,1024);
if (!baseaddr)
{
printk("Ioremap to access NAND chip failedn");
err = - EIO;
goto out_mtd;
}
/* 获得私有数据(nand_chip)指针 */
this = (struct nand_chip*)(&board_mtd[1]);
/* 将 nand_chip 赋予 mtd_info 私有指针 */
board_mtd->priv = this;
/* 设置 NAND Flash 的 I/O 基地址 */
this->IO_ADDR_R = baseaddr;
this->IO_ADDR_W = baseaddr;
/* 硬件控制函数 */
this->hwcontrol = board_hwcontrol;
/* 从数据手册获知命令延迟时间 */
this->chip_delay = CHIP_DEPENDEND_COMMAND_DELAY;
/* 初始化设备 ready 函数 */
this->dev_ready = board_dev_ready;
this->eccmode = NAND_ECC_SOFT;
/* 扫描以确定设备的存在 */
if (nand_scan(board_mtd,1))
{
err = - ENXIO;
goto out_ior;
}
//添加分区
add_mtd_partitions(board_mtd,partition_info,NUM_PARTITIONS);
goto out;
out_ior: iounmap((void*)baseaddr);
out_mtd: kfree(board_mtd);
out: return err;
}
static void _ _exit board_cleanup(void) { /* 释放资源,注销设备 */ nand_release(board_mtd); /* unmap 物理地址 */ iounmap((void*)baseaddr); /* 释放 MTD 设备结构体 */ kfree(board_mtd); } /* GPIO 方式的硬件控制 */ static void board_hwcontrol(struct mtd_info *mtd,int cmd) { switch (cmd) { case NAND_CTL_SETCLE: /* Set CLE pin high */ break; case NAND_CTL_CLRCLE: /* Set CLE pin low */ break; case NAND_CTL_SETALE: /* Set ALE pin high */ break; case NAND_CTL_CLRALE: /* Set ALE pin low */ break; case NAND_CTL_SETNCE: /* Set nCE pin low */ break; case NAND_CTL_CLRNCE: /* Set nCE pin high */ break; } } /* 返回设备 ready 状态 */ static int board_dev_ready(struct mtd_info *mtd) { return xxx_read_ready_bit(); } S3C2410 NAND 控制器硬件描述 S3C2410 处理器集成了一个 NAND 控制器,它提供如下引脚。1.D[7:0]:数据/命令/地址 I/O 端口(数据、命令、地址复用)。 2.CLE:命令锁存使能(数据线上 NAND Flash 命令有效,输出)。 3.ALE:地址锁存使能(数据线上 NAND Flash 地址有效,输出)。 4.nFCE:NAND Flash 片选(输出)。 5.nFRE:NAND Flash 读使能(输出)。 6.nFWE:NAND Flash 写使能(输出)。 7.R/nB:NAND Flash 准备好/忙(输入)。 8.NCON:输入,NAND Flash 内存地址步长选择,0:表示 3 步长地址,1:表示 4 步长地址(NAND Flash 中地址要通过 d[7:0]送多次,每送一次就为一步长)。 S3C2410 对 NAND Flash 的操作通过 NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT 和 NFECC 这 6 个寄存器来完成。 1.NFCONF:NAND Flash 配置寄存器,用于使能 NAND Flash 控制器、初始化ECC、并设置 NAND Flash 片选信号 nFCE 为 1(即不选中)。 通过 S3C2410 的 NAND 控制器访问 NAND Flash 的一般流程如下。 nand_chip 初始化和成员函数 下面分析一下nandchip的驱动实现: 路径:?driversmtdnandS3c2410.c 首先是初始化和卸载函数: 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); } static void __exit s3c2410_nand_exit(void) { platform_driver_unregister(&s3c2412_nand_driver); platform_driver_unregister(&s3c2440_nand_driver); platform_driver_unregister(&s3c2410_nand_driver); } module_init(s3c2410_nand_init); module_exit(s3c2410_nand_exit);这里支持很多的2410,2440,2412各个CPU,选2440看下: 定义如下: static struct platform_driver s3c2440_nand_driver = { .probe = s3c2440_nand_probe,//当和设备匹配后调用 .remove = s3c2410_nand_remove,.suspend = s3c24xx_nand_suspend,//电源管理相关 .resume = s3c24xx_nand_resume,.driver = { .name = "s3c2440-nand",.owner = THIS_MODULE,},};看下probe是如何对nand初始化的: static int s3c2440_nand_probe(struct platform_device *dev) { return s3c24xx_nand_probe(dev,TYPE_S3C2440); }继续: 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)); platform_set_drvdata(pdev,info);//将s3c2410_nand_info放到platform dev上 spin_lock_init(&info->controller.lock);//初始化锁 init_waitqueue_head(&info->controller.wq);//初始化等待队列 /* get the clock source and enable it */ info->clk = clk_get(&pdev->dev,"nand");//获取nandflash的时钟 if (IS_ERR(info->clk)) { dev_err(&pdev->dev,"failed to get clock"); err = -ENOENT; goto exit_error; } clk_enable(info->clk);//使能它 /* allocate and map the resource */ /* currently we assume we have the one resource */ 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); 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); /* initialise the hardware */ err = s3c2410_nand_inithw(info,pdev);//硬件时序的初始化 if (err != 0) goto exit_error; sets = (plat != NULL) ? plat->sets : NULL; nr_sets = (plat != NULL) ? plat->nr_sets : 1; info->mtd_count = nr_sets; /* allocate our information */ 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); /* initialise all possible chips */ 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);//读写函数和硬件ecc的初始化 nmtd->scan_res = nand_scan(&nmtd->mtd,(sets) ? sets->nr_chips : 1);//扫描是否有这个nand设备,就用到了上面初始化读写函数中的指针了 if (nmtd->scan_res == 0) { s3c2410_nand_add_partition(info,sets);//最终调用到了driversmtdMtdcore.c中的add_mtd_device来添加分区}if (sets != NULL)sets++;}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;} (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |