int mtd_init(void) {
int ret;
#ifdef CONFIG_MTD_CFI ret = cfi_init(); #endif
#ifdef CONFIG_MTD_SMC ret = smc_init(); #endif
#ifdef CONFIG_S3C2410_AMD_BOOT ret = amd_init(); #endif
可见,vivi现在支持三种类型的存储接口,一种是CFI,也就是Intel发起的一个flash的接口标准,主要就是intel的nor flash系列;一种是smc,智能卡系列接口,nand flash就是通过这个接口实现读写的;一种是AMD的flash系列。选择什么启动方式,就要选择相应的配置项。
核心部分根据配置应该调用smc_init函数。-->【drivers/mtd/maps/s3c2410_flash.c】。这里最为核心的就是两个数据结构,一个是mtd_info,位于【include/mtd/mtd.h】,如下: struct mtd_info { u_char type; u_int32_t flags;
u_int32_t size; // Total size of the MTD
/* \飗e users may take this * to be the only erase size available, or may use the more detailed * information below if they desire */
u_int32_t erasesize;
u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) u_int32_t ecctype; u_int32_t eccsize;
// Kernel-only stuff starts here. char *name; int index;
/* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. */
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
/* This really shouldn't be here. It can go away in 2.5 */ u_int32_t bank_size; struct module *module;
36
int (*erase) (struct mtd_info *mtd, struct erase_info *instr); /* This stuff for 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 */ void (*unpoint) (struct mtd_info *mtd, u_char * addr);
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 (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf);
int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf);
int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); /*
* 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. */
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
/* This function is not yet implemented */
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
/* Chip-supported device locking */
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); void *priv; };
mtd_info是表示MTD设备的结构,每个分区也被表示为一个mtd_info,如果有两个MTD设备,每个设备有三个分区,那么在系统中就一共有6个mtd_info结构。关于mtd_info,在《Linux MTD源代码分析》中讲解非常透彻,不过需要注意的是,在vivi的实现中没有使用mtd_table,另外priv指向的是nand_chip,这些都是与Linux下不同的地方,主要是为了简化。另一个是nand_chip,这个结构则包含了nand flash的所有信息。 struct nand_chip {
#ifdef CONFIG_MTD_NANDY void (*hwcontrol)(int cmd); void (*write_cmd)(u_char val); void (*write_addr)(u_char val); u_char (*read_data)(void); void (*write_data)(u_char val); void (*wait_for_ready)(void);
37
/*spinlock_t chip_lock;*/ /*wait_queue_head_t wq;*/ /*nand_state_t state;*/ int page_shift; u_char *data_buf; u_char *data_cache; int cache_page;
struct nand_smc_dev *dev;
u_char spare[SMC_OOB_SIZE]; #else /* CONFIG_MTD_NANDY */ unsigned long IO_ADDR_R; unsigned long IO_ADDR_W; void (*hwcontrol)(int cmd); int (*dev_ready)(void); int chip_delay;
/*spinlock_t chip_lock;*/ /*wait_queue_head_t wq;*/ /*nand_state_t state;*/ int page_shift; u_char *data_buf; u_char *data_cache; int cache_page;
#ifdef CONFIG_MTD_NAND_ECC u_char ecc_code_buf[6]; u_char reserved[2]; #endif
#endif /* CONFIG_MTD_NANDY */ };
所谓的初始化,其实就是填充处理上述两个数据结构的过程。填充完毕之后,后续的工作都会基于此展开。下面开始看smc_init的代码。
mymtd = mmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)); this = (struct nand_chip *)(&mymtd[1]);
在这里,第一句参考前面heap的实现代码,重点看第二句代码。这句代码是有一定的技巧性,但是也存在着很大的风险。其中,mymtd是指向struct mtd_info的指针,那么mymtd[1]实际上是等效于*(mymtd + 1)的数学计算模式,注意mymtd并非数组,这里仅仅利用了编译器翻译的特点。对于指针而言,加1实际上增加的指针对应类型的值,在这里地址实际上增加了sizeof(struct mtd_info),因为前面分配了两块连续的地址空间,所以&(*(mymtd + 1))实际上就是mtd_info数据结构结束的下一个地址,然后实现强制转换,于是this就成为了nand_chip的入口指针了。但是,这里必须要把握好,因为这个地方是不会进行内存的检查的,也就是说,如果你使用了mymtd[2],那么仍然按照上述公式解析,虽然可以运算,可是就是明显的指针泄漏了,可能会出现意料不到的结果。写了一个测试程序,对这点进行了探讨,要小心内存问题。
38
了解清楚了,mymtd指向mtd_info的入口,this指向nand_chip的入口。
memset((char *)mymtd, 0, sizeof(struct mtd_info)); memset((char *)this, 0, sizeof(struct nand_chip));
mymtd->priv = this;
上述代码首先初始化这两个结构体,即均为0.然后利用priv把二者联系起来,也就是mymtd通过其成员priv指向this,那么mymtd中的抽闲操作函数,比如read、write等,真正的是通过this来实现的。很明显,this的实现部分属于flash硬件驱动层,而mymtd部分则属于MTD设备层,二者的联系就是通过成员priv实现的。
接下来首先是初始化nand flash设备,这跟前面的基础实验一致。
/* set NAND Flash controller */ nfconf = NFCONF;
/* NAND Flash controller enable */ nfconf |= NFCONF_FCTRL_EN;
/* Set flash memory timing */
nfconf &= ~NFCONF_TWRPH1; /* 0x0 */ nfconf |= NFCONF_TWRPH0_3; /* 0x3 */ nfconf &= ~NFCONF_TACLS; /* 0x0 */
NFCONF = nfconf;
然后填充nand flash的数据结构的一个实例this,分成了两个部分,nand flash基本操作函数成员的初始化、其余信息的填写。
/* Set address of NAND IO lines */ this->hwcontrol = smc_hwcontrol; this->write_cmd = write_cmd; this->write_addr = write_addr; this->read_data = read_data; this->write_data = write_data;
this->wait_for_ready = wait_for_ready;
/* Chip Enable -> RESET -> Wait for Ready -> Chip Disable */ this->hwcontrol(NAND_CTL_SETNCE); this->write_cmd(NAND_CMD_RESET); this->wait_for_ready();
this->hwcontrol(NAND_CTL_CLRNCE);
smc_insert(this);
上面这些都不难理解,感觉在结构体设计上还是比较出色的,把成员和相应的操作封
39
装起来,面向对象的一种方法。下面看smc_insert,根据刚才填充的nand_flash结构,构造mtd_info结构无非还是按照结构体填写相应的信息,细节部分就不深入探讨了。
inline int
smc_insert(struct nand_chip *this) {
/* Scan to find existance of the device */ if (smc_scan(mymtd)) { return -ENXIO; }
/* Allocate memory for internal data buffer */ this->data_buf = mmalloc(sizeof(u_char) *
(mymtd->oobblock + mymtd->oobsize));
if (!this->data_buf) {
printk(\ this->data_buf = NULL; return -ENOMEM; }
return 0; }
第一部分扫描填充mymtd数据结构。后面主要用于nand flash的oob缓冲处理。具体部分可以参考《s3c2410完全开发》。我们先来看看smc_scan函数的执行(drivers/mtd/nand/smc_core.c这个文件中包含的是nand flash中绝大多数真正进行操作的函数):
int smc_scan(struct mtd_info *mtd) {
int i, nand_maf_id, nand_dev_id; //定义flash的厂家ID和设备ID
struct nand_chip *this = mtd->priv; //获得与mtd设备相联系的真正的flash设备结构 /* Select the device */ nand_select(); //#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); nand_command(mtd, NAND_CMD_RESET, -1, -1); udelay(10);这三句代码和我们之前在nand章节中讲解的片选flash是一个意思,先将使能nand的那位置1也就是片选nand,然后想nand发送reset命令,然后等待一段时间。 /* Send the command for reading device ID */
nand_command(mtd, NAND_CMD_READID, 0x00, -1);//向nand发送读ID的命令 this->wait_for_ready(); //等待nand结果数据准备好 /* Read manufacturer and device IDs */
nand_maf_id = this->read_data();//nand数据准备好后,通过read_data可以相继的读出厂家ID和设备ID
nand_dev_id = this->read_data();
/* Print and sotre flash device information */
for (i = 0; nand_flash_ids[i].name != NULL; i++) { //在数组nand_flash_ids中查找与ID相符合的项,可以看到下面对数组说明
40