MTD(Memory Technology Device)即常說的Flash等使用存儲(chǔ)芯片的存儲(chǔ)設(shè)備,MTD子系統(tǒng)對(duì)應(yīng)的是塊設(shè)備驅(qū)動(dòng)框架中的設(shè)備驅(qū)動(dòng)層,可以說,MTD就是針對(duì)Flash設(shè)備設(shè)計(jì)的標(biāo)準(zhǔn)化硬件驅(qū)動(dòng)框架。本文基于3.14內(nèi)核,討論MTD驅(qū)動(dòng)框架。
MTD子系統(tǒng)框架
設(shè)備節(jié)點(diǎn)層:MTD框架可以在/dev下創(chuàng)建字符設(shè)備節(jié)點(diǎn)(主設(shè)備號(hào)90)以及塊設(shè)備節(jié)點(diǎn)(主設(shè)備號(hào)31), 用戶通過訪問此設(shè)備節(jié)點(diǎn)即可訪問MTD字符設(shè)備或塊設(shè)備。
MTD設(shè)備層: 基于MTD原始設(shè)備, Linux在這一層次定義出了MTD字符設(shè)備和塊設(shè)備, 字符設(shè)備在mtdchar.c中實(shí)現(xiàn), 塊設(shè)備則是通過結(jié)構(gòu)mtdblk_dev來(lái)描述,"/drivers/mtd/mtdchar.c"文件實(shí)現(xiàn)了MTD字符設(shè)備接口;?"/drivers/mtd/mtdblock.c"文件實(shí)現(xiàn)了MTD塊設(shè)備接口
MTD原始設(shè)備層: 由MTD原始設(shè)備的通用代碼+特定的Flash數(shù)據(jù)組成。mtd_info、mtd_part、mtd_partition以及mtd_partitions等對(duì)象及其操作方法就屬于這一層,對(duì)應(yīng)的文件是"drivers/mtd/mtdcore.c"。類似于i2c驅(qū)動(dòng)框架中的核心層。
硬件驅(qū)動(dòng)層: 內(nèi)核將常用的flash操作都已經(jīng)在這個(gè)層次實(shí)現(xiàn), 驅(qū)動(dòng)開發(fā)只需要將相應(yīng)的設(shè)備信息添加進(jìn)去即可, 比如,NOR flash的芯片驅(qū)動(dòng)位于"drivers/mtd/chips/", Nand flash位于"drivers/mtd/nand/"(eg s3c2410.c)
核心結(jié)構(gòu)和方法簡(jiǎn)述
為了實(shí)現(xiàn)上述的框架, 內(nèi)核中使用了如下類和API, 這些幾乎是開發(fā)一個(gè)MTD驅(qū)動(dòng)必須的
核心結(jié)構(gòu)
mtd_info描述原始設(shè)備層的一個(gè)分區(qū)的結(jié)構(gòu), 描述一個(gè)設(shè)備或一個(gè)多分區(qū)設(shè)備中的一個(gè)分區(qū)
mtd_table管理原始設(shè)備層的mtd_info的數(shù)組
mtd_part表示一個(gè)分區(qū), 其中的struct mtd_info mtd描述該分區(qū)的信息, 一個(gè)物理Flash設(shè)備可以有多于1個(gè)mtd_part,每個(gè)mtd_part都對(duì)應(yīng)一個(gè)mtd_info。
mtd_partition描述一個(gè)分區(qū)表, 通過管理mtd_part以及每一個(gè)mtd_part中的mtd_info來(lái)描述所有的分區(qū),一個(gè)物理Flash設(shè)備只有一個(gè)mtd_partition
mtd_partitions是一個(gè)list_head對(duì)象,用于管理mtd_partition們
map_info描述一個(gè)NOR Flash設(shè)備
nand_chip描述一個(gè)NAND Flash設(shè)備
核心方法
add_mtd_device()/del_mtd_device()注冊(cè)/注銷一個(gè)MTD設(shè)備
add_mtd_partitions()/del_mtd_partitions()注冊(cè)注銷一個(gè)或多個(gè)分區(qū)表,
do_map_probe()用來(lái)根據(jù)傳入的參數(shù)匹配一個(gè)map_info對(duì)象的驅(qū)動(dòng),比如CFI接口或JEDEC接口的NOR Flash,并返回一個(gè)mtd_info以便注冊(cè)分區(qū)信息。
nand_scan():NAND flash使用這個(gè)API來(lái)匹配驅(qū)動(dòng)。
核心結(jié)構(gòu)與方法詳述
mtd_info
本身是沒有l(wèi)ist_head來(lái)供內(nèi)核管理,對(duì)mtd_info對(duì)象的管理是通過mtd_part來(lái)實(shí)現(xiàn)的。mtd_info對(duì)象屬于原始設(shè)備層,里面的很多函數(shù)接口內(nèi)核已經(jīng)實(shí)現(xiàn)了。mtd_info中的read()/write()等操作是MTD設(shè)備驅(qū)動(dòng)要實(shí)現(xiàn)的主要函數(shù),在NORFlash或NANDFlash中的驅(qū)動(dòng)代碼中幾乎看不到mtd_info的成員函數(shù),即這些函數(shù)對(duì)于Flash芯片是透明的,因?yàn)長(zhǎng)inux在MTD的下層實(shí)現(xiàn)了針對(duì)NORFlash和NANDFlash的通用的mtd_info函數(shù)。
114 struct mtd_info {115 u_char type;116 uint32_t flags;117 uint64_t size; // Total size of the MTD118 123 uint32_t erasesize;131 uint32_t writesize;132 142 uint32_t writebufsize;143 144 uint32_t oobsize; // Amount of OOB data per block (e.g. 16)145 uint32_t oobavail; // Available OOB bytes per block146 151 unsigned int erasesize_shift;152 unsigned int writesize_shift;153 /* Masks based on erasesize_shift and writesize_shift */154 unsigned int erasesize_mask;155 unsigned int writesize_mask;156 164 unsigned int bitflip_threshold;165 166 // Kernel-only stuff starts here.167 const char *name;168 int index;169 170 /* ECC layout structure pointer - read only! */ 171 struct nand_ecclayout *ecclayout;172 173 /* the ecc step size. */174 unsigned int ecc_step_size;175 176 /* max number of correctible bit errors per ecc step */177 unsigned int ecc_strength;178 179 /* Data for variable erase regions. If numeraseregions is zero,180 * it means that the whole device has erasesize as given above.181 */182 int numeraseregions;183 struct mtd_erase_region_info *eraseregions;184 185 /*186 * Do not call via these pointers, use corresponding mtd_*()187 * wrappers instead.188 */189 int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);190 int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,191 size_t *retlen, void **virt, resource_size_t *phys);192 int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);193 unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,194 unsigned long len,195 unsigned long offset,196 unsigned long flags);197 int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,198 size_t *retlen, u_char *buf);199 int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,200 size_t *retlen, const u_char *buf);201 int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,202 size_t *retlen, const u_char *buf);203 int (*_read_oob) (struct mtd_info *mtd, loff_t from,204 struct mtd_oob_ops *ops);205 int (*_write_oob) (struct mtd_info *mtd, loff_t to,206 struct mtd_oob_ops *ops);207 int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,208 size_t len);209 int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,210 size_t len, size_t *retlen, u_char *buf);211 int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,212 size_t len);213 int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,214 size_t len, size_t *retlen, u_char *buf);215 int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,216 size_t len, size_t *retlen, u_char *buf);217 int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,218 size_t len);219 int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,220 unsigned long count, loff_t to, size_t *retlen);221 void (*_sync) (struct mtd_info *mtd);222 int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);223 int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);224 int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);225 int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);226 int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);227 int (*_suspend) (struct mtd_info *mtd);228 void (*_resume) (struct mtd_info *mtd);229 /*230 * If the driver is something smart, like UBI, it may need to maintain231 * its own reference counting. The below functions are only for driver.232 */233 int (*_get_device) (struct mtd_info *mtd);234 void (*_put_device) (struct mtd_info *mtd);235 236 /* Backing device capabilities for this device237 * - provides mmap capabilities238 */239 struct backing_dev_info *backing_dev_info;240 241 struct notifier_block reboot_notifier; /* default mode before reboot */242 243 /* ECC status information */244 struct mtd_ecc_stats ecc_stats;245 /* Subpage shift (NAND) */246 int subpage_sft;247 248 void *priv;249 250 struct module *owner;251 struct device dev;252 int usecount;253 };
struct mtd_info
--115-->MTD設(shè)備類型,有MTD_RAM,MTD_ROM、MTD_NORFLASH、MTD_NAND_FLASH
--116-->讀寫及權(quán)限標(biāo)志位,有MTD_WRITEABLE、MTD_BIT_WRITEABLE、MTD_NO_ERASE、MTD_UP_LOCK
--117-->MTD設(shè)備的大小
--123-->主要的擦除塊大小,NandFlash就是"塊"的大小
--131-->最小可寫字節(jié)數(shù),NandFlash一般對(duì)應(yīng)"頁(yè)"的大小
--144-->一個(gè)block中的OOB字節(jié)數(shù)
--145-->一個(gè)block中可用oob的字節(jié)數(shù)
--171-->ECC布局結(jié)構(gòu)體指針
--190-->針對(duì)eXecute-In-Place,即XIP
--192-->如果這個(gè)指針為空,不允許XIP
--197-->讀函數(shù)指針
--199-->寫函數(shù)指針
--248-->私有數(shù)據(jù)
mtd_part
內(nèi)核管理分區(qū)的鏈表節(jié)點(diǎn),通過它來(lái)實(shí)現(xiàn)對(duì)mtd_info對(duì)象的管理。
41 struct mtd_part { 42 struct mtd_info mtd; 43 struct mtd_info *master; 44 uint64_t offset; 45 struct list_head list; 46 };
struct mtd_part
--42-->對(duì)應(yīng)的mtd_info對(duì)象
--43-->父對(duì)象指針
--44-->偏移量
--45-->鏈表節(jié)點(diǎn)
mtd_partition
描述一個(gè)分區(qū)
39 struct mtd_partition { 40 const char *name; /* identifier string */ 41 uint64_t size; /* partition size */ 42 uint64_t offset; /* offset within the master MTD space */ 43 uint32_t mask_flags; /* master MTD flags to mask out for this partition */ 44 struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */ 45 };
mtd_partition
--40-->分區(qū)名
--41-->分區(qū)大小,使用MTDPART_SIZ_FULL表示使用全部空間
--42-->分區(qū)在master設(shè)備中的偏移量。MTDPART_OFS_APPEND表示從上一個(gè)分區(qū)結(jié)束的地方開始,MTDPART_OFS_NXTBLK表示從下一個(gè)擦除塊開始; MTDPART_OFS_RETAIN表示盡可能向后偏,把size大小的空間留下即可
--43-->權(quán)限掩碼,MTD_WRITEABLE表示將父設(shè)備的只讀選項(xiàng)變成可寫(可寫分區(qū)要求size和offset要erasesize對(duì)齊,eg MTDPART_OFS_NEXTBLK)
--44-->NANDFlash的OOB布局,OOB是NANDFlash中很有用空間,比如yaffs2就需要將壞塊信息存儲(chǔ)在OOB區(qū)域
mtd_partitions
鏈表頭,將所有的mtd_partition連接起來(lái)。
36 /* Our partition linked list */ 37 static LIST_HEAD(mtd_partitions);
下圖是關(guān)鍵API的調(diào)用關(guān)系。
mtd_add_partition()
?? └── add_mtd_device()
add_mtd_partitions()
?? └── add_mtd_device()
add_mtd_device()
分配并初始化一個(gè)mtd對(duì)象。
367 334 int add_mtd_device(struct mtd_info *mtd) 335 { 336 struct mtd_notifier *not; 337 int i, error; 338 339 if (!mtd->backing_dev_info) { 340 switch (mtd->type) { 341 case MTD_RAM: 342 mtd->backing_dev_info = &mtd_bdi_rw_mappable; 343 break; 344 case MTD_ROM: 345 mtd->backing_dev_info = &mtd_bdi_ro_mappable; 346 break; 347 default: 348 mtd->backing_dev_info = &mtd_bdi_unmappable; 349 break; 350 } 351 } 355 356 i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL); 357 if (i < 0) 358 goto fail_locked; 359 360 mtd->index = i; 361 mtd->usecount = 0; 362 363 /* default value if not set by driver */ 364 if (mtd->bitflip_threshold == 0) 365 mtd->bitflip_threshold = mtd->ecc_strength; 366 367 if (is_power_of_2(mtd->erasesize)) 368 mtd->erasesize_shift = ffs(mtd->erasesize) - 1; 369 else 370 mtd->erasesize_shift = 0; 371 372 if (is_power_of_2(mtd->writesize)) 373 mtd->writesize_shift = ffs(mtd->writesize) - 1; 374 else 375 mtd->writesize_shift = 0; 376 377 mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; 378 mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; 379 380 /* Some chips always power up locked. Unlock them now */ 381 if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) { 382 error = mtd_unlock(mtd, 0, mtd->size); 387 } 388 392 mtd->dev.type = &mtd_devtype; 393 mtd->dev.class = &mtd_class; 394 mtd->dev.devt = MTD_DEVT(i); 395 dev_set_name(&mtd->dev, "mtd%d", i); 396 dev_set_drvdata(&mtd->dev, mtd); 397 if (device_register(&mtd->dev) != 0) 399 400 if (MTD_DEVT(i)) 401 device_create(&mtd_class, mtd->dev.parent, 402 MTD_DEVT(i) + 1, 403 NULL, "mtd%dro", i); 408 list_for_each_entry(not, &mtd_notifiers, list) 409 not->add(mtd); 417 return 0; 424 }
add_mtd_device()
--395-->設(shè)置MTD設(shè)備的名字
--396-->設(shè)置私有數(shù)據(jù),將mtd地址藏到device->device_private->void* driver_data
--408-->遍歷所有的mtd_notifier,將其添加到通知鏈
mtd_add_partition()
通過將一個(gè)mtd_part對(duì)象注冊(cè)到內(nèi)核,將mtd_info對(duì)象注冊(cè)到內(nèi)核,即為一個(gè)設(shè)備添加一個(gè)分區(qū)。
537 int mtd_add_partition(struct mtd_info *master, const char *name,538 long long offset, long long length)539 {540 struct mtd_partition part;541 struct mtd_part *p, *new;542 uint64_t start, end;543 int ret = 0;545 /* the direct offset is expected */546 if (offset == MTDPART_OFS_APPEND || 547 offset == MTDPART_OFS_NXTBLK)548 return -EINVAL;549 550 if (length == MTDPART_SIZ_FULL)551 length = master->size - offset;552 553 if (length <= 0)554 return -EINVAL;555 556 part.name = name;557 part.size = length;558 part.offset = offset;559 part.mask_flags = 0;560 part.ecclayout = NULL;561 562 new = allocate_partition(master, &part, -1, offset);563 if (IS_ERR(new))564 return PTR_ERR(new);565 566 start = offset;567 end = offset + length;568 569 mutex_lock(&mtd_partitions_mutex);570 list_for_each_entry(p, &mtd_partitions, list)571 if (p->master == master) {572 if ((start >= p->offset) &&573 (start < (p->offset + p->mtd.size)))574 goto err_inv;575 576 if ((end >= p->offset) &&577 (end < (p->offset + p->mtd.size)))578 goto err_inv;579 }580 581 list_add(&new->list, &mtd_partitions);582 mutex_unlock(&mtd_partitions_mutex);583 584 add_mtd_device(&new->mtd);585 586 return ret;591 }
add_mtd_partitions()
添加一個(gè)分區(qū)表到內(nèi)核,一個(gè)MTD設(shè)備一個(gè)分區(qū)表
626 int add_mtd_partitions(struct mtd_info *master,627 const struct mtd_partition *parts,628 int nbparts)629 {630 struct mtd_part *slave;631 uint64_t cur_offset = 0;632 int i;636 for (i = 0; i < nbparts; i++) {637 slave = allocate_partition(master, parts + i, i, cur_offset);642 list_add(&slave->list, &mtd_partitions);645 add_mtd_device(&slave->mtd);647 cur_offset = slave->offset + slave->mtd.size;648 }649 650 return 0; 651 }
用戶空間編程
MTD設(shè)備提供了字符設(shè)備和塊設(shè)備兩種接口,對(duì)于字符設(shè)備接口,在"drivers/mtd/mtdchar.c"中實(shí)現(xiàn)了,比如,用戶程序可以直接通過ioctl()回調(diào)相應(yīng)的驅(qū)動(dòng)實(shí)現(xiàn)。其中下面的幾個(gè)是這些操作中常用的結(jié)構(gòu),這些結(jié)構(gòu)是對(duì)用戶空間開放的,類似于輸入子系統(tǒng)中的input_event結(jié)構(gòu)。
mtd_info_user
//include/uapi/mtd/mtd-abi.h125 struct mtd_info_user { 126 __u8 type;127 __u32 flags;128 __u32 size; /* Total size of the MTD */129 __u32 erasesize; 130 __u32 writesize; 131 __u32 oobsize; /* Amount of OOB data per block (e.g. 16) */132 __u64 padding; /* Old obsolete field; do not use */133 };
mtd_oob_buf
描述NandFlash的OOB(Out Of Band)信息。
35 struct mtd_oob_buf { 36 __u32 start; 37 __u32 length; 38 unsigned char __user *ptr; 39 };
erase_info_user
25 struct erase_info_user { 26 __u32 start; 27 __u32 length; 28 };
實(shí)例
mtd_oob_buf oob;erase_info_user erase;mtd_info_user meminfo;/* 獲得設(shè)備信息 */if(0 != ioctl(fd, MEMGETINFO, &meminfo)) perror("MEMGETINFO"); /* 擦除塊 */if(0 != ioctl(fd, MEMERASE, &erase)) perror("MEMERASE");/* 讀OOB */if(0 != ioctl(fd, MEMREADOOB, &oob)) perror("MEMREADOOB");/* 寫OOB??? */ if(0 != ioctl(fd, MEMWRITEOOB, &oob)) perror("MEMWRITEOOB"); /* 檢查壞塊 */if(blockstart != (ofs & (~meminfo.erase + 1))){ blockstart = ofs & (~meminfo.erasesize + 1); if((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) perror("MEMGETBADBLOCK"); else if(badblock) /* 壞塊代碼 */ else /* 好塊代碼 */}
NANDFlash和NORFlash都是基于MTD框架編寫的,由于MTD框架中通用代碼已經(jīng)在內(nèi)核中實(shí)現(xiàn)了,所以驅(qū)動(dòng)開發(fā)主要是進(jìn)行MTD框架中的的開發(fā)。
NOR Flash驅(qū)動(dòng)
下圖就是NORFlash驅(qū)動(dòng)在MTD驅(qū)動(dòng)框架中的位置
基于上述的MTD框架, Flash驅(qū)動(dòng)都變的十分的簡(jiǎn)單, 因?yàn)楫?dāng)下Flash的操作接口已經(jīng)很統(tǒng)一, a, 相應(yīng)的代碼在"drivers/mtd/chips"中文件實(shí)現(xiàn),所以在設(shè)備驅(qū)動(dòng)層, 留給驅(qū)動(dòng)工程師的工作就大大的減少了。
基于MTD子系統(tǒng)開發(fā)NOR FLash驅(qū)動(dòng),只需要構(gòu)造一個(gè)map_info類型的對(duì)象并調(diào)用do_map_probe()來(lái)匹配內(nèi)核中已經(jīng)寫好的驅(qū)動(dòng),比如CFI接口的驅(qū)動(dòng)或JEDEC接口的驅(qū)動(dòng)。當(dāng)下編寫一個(gè)NorFlash驅(qū)動(dòng)的工作流程如下
map_info
208 struct map_info {209 const char *name;210 unsigned long size;211 resource_size_t phys;212 #define NO_XIP (-1UL)214 void __iomem *virt;215 void *cached;217 int swap; /* this mapping's byte-swapping requirement */218 int bankwidth; 243 void (*set_vpp)(struct map_info *, int);245 unsigned long pfow_base;246 unsigned long map_priv_1;247 unsigned long map_priv_2;248 struct device_node *device_node;249 void *fldrv_priv;250 struct mtd_chip_driver *fldrv; 251 };
struct map_info
--210-->NOR Flash設(shè)備的容量
--211-->NOR Flash在物理地址空間中的地址
--214-->由物理地址映射的虛擬地址
--218-->總線寬度,NOR Flash是有地址總線的,所以才能片上執(zhí)行,一般都是8位或16位寬
構(gòu)造好一個(gè)map_info對(duì)象之后,接下來(lái)的工作就是匹配驅(qū)動(dòng)+注冊(cè)分區(qū)表
do_map_probe()
這個(gè)API用來(lái)根據(jù)傳入的參數(shù)匹配一個(gè)map_info對(duì)象的驅(qū)動(dòng),比如CFI接口或JEDEC接口的NOR Flash。這個(gè)函數(shù)的接口如下:
struct mtd_info *do_map_probe(const char *name, struct map_info *map)
對(duì)于常用的NorFlash標(biāo)準(zhǔn),這個(gè)函數(shù)的調(diào)用方式如下:
do_map_probe("cfi_probe", &xxx_map_info);do_map_probe("jedec_probe",&xxx_map_info);do_map_probe("map_rom",&xxx_map_info);
匹配了設(shè)備驅(qū)動(dòng),可以發(fā)現(xiàn)一個(gè)map_info對(duì)象中沒有mtd_partitions相關(guān)的信息,對(duì)于一個(gè)NOR Flash的分區(qū)信息,需要通過do_map_probe返回的mtd_info對(duì)象來(lái)注冊(cè)到內(nèi)核。這里我們可以先調(diào)用parse_mtd_partitions()查看Flash上已有的分區(qū)信息,獲取了分區(qū)信息之后再調(diào)用add_mtd_partitions()將分區(qū)信息寫入內(nèi)核
NOR Flash驅(qū)動(dòng)模板
#define WINDOW_SIZE ...#define WINDOW_ADDR ...static struct map_info xxx_map = { .name = "xxx flash", .size = WINDOW_SIZE, .bankwidth = 1, .phys = WINDOW_ADDR,};static struct mtd_partition xxx_partitions[] = { .name = "Drive A", .offset = 0, .size = 0x0e000,};#define NUM_PARTITIONS ARRAY_SIZE(xxx_partitions)static struct mtd_info *mymtd;static int __init init_xxx_map(void){ int rc = 0; xxx_map.virt = ioremap_nocache(xxx_map.phys, xxx_map.size); if(!xxx_map.virt){ printk(KERN_ERR"Failed to ioremap_nocache\n"); rc = -EIO; goto err2; } simple_map_init(&xxx_map); mymtd = do_map_probe("jedec_probe", &xxx_map); if(!mymtd){ rc = -ENXIO; goto err1; } mymtd->owner = THIS_MODULE; add_mtd_partitions(mymtd, xxx_partitions, NUM_PARTITIONS); return 0;err1: map_destroy(mymtd); iounmap(xxx_map.virt);err2: return rc;}static void __exit cleanup_xxx_map(void){ if(mymtd){ del_mtd_partitions(mymtd); map_destroy(mymtd); } if(xxx_map.virt){ iounmap(xxx_map.virt); xxx_map.virt = NULL; }}
Nand Flash驅(qū)動(dòng)
下圖就是基于MTD框架的NandFlash驅(qū)動(dòng)的位置。
Nand Flash和NOR Flash類似,內(nèi)核中已經(jīng)在"drivers/mtd/nand/nand_base.c"中實(shí)現(xiàn)了通用的驅(qū)動(dòng)程序,驅(qū)動(dòng)開發(fā)中不需要再實(shí)現(xiàn)mtd_info中的read, write, read_oob, write_oob等接口,只需要構(gòu)造并注冊(cè)一個(gè)nand_chip對(duì)象, 這個(gè)對(duì)象主要描述了一片flash芯片的相關(guān)信息,包括地址信息,讀寫方法,ECC模式,硬件控制等一系列底層機(jī)制。當(dāng)下,編寫一個(gè)NandFlash驅(qū)動(dòng)的工作流程如下:
nand_chip
這個(gè)結(jié)構(gòu)描述一個(gè)NAND Flash設(shè)備,通常藏在mtd_info->priv中,以便在回調(diào)其中的接口的時(shí)候可以找到nand_chip對(duì)象。
547 struct nand_chip {548 void __iomem *IO_ADDR_R;549 void __iomem *IO_ADDR_W;550 551 uint8_t (*read_byte)(struct mtd_info *mtd);578 579 int chip_delay;580 unsigned int options;581 unsigned int bbt_options;582 583 int page_shift;584 int phys_erase_shift;585 int bbt_erase_shift;586 int chip_shift;587 int numchips;588 uint64_t chipsize;589 int pagemask;590 int pagebuf;591 unsigned int pagebuf_bitflips;592 int subpagesize;593 uint8_t bits_per_cell;594 uint16_t ecc_strength_ds;595 uint16_t ecc_step_ds;596 int badblockpos;597 int badblockbits;598 599 int onfi_version;600 struct nand_onfi_params onfi_params;601 602 int read_retries;603 604 flstate_t state;605 606 uint8_t *oob_poi;607 struct nand_hw_control *controller;608 609 struct nand_ecc_ctrl ecc;610 struct nand_buffers *buffers;611 struct nand_hw_control hwcontrol;612 613 uint8_t *bbt;614 struct nand_bbt_descr *bbt_td;615 struct nand_bbt_descr *bbt_md;616 617 struct nand_bbt_descr *badblock_pattern;618 619 void *priv;620 };
struct nand_chip
--609-->NAND芯片的OOB分布和模式,如果不賦值,則會(huì)使用內(nèi)核默認(rèn)的OOB
--580-->與具體的NAND 芯片相關(guān)的一些選項(xiàng),如NAND_BUSWIDTH_16 等,可以參考
--583-->用位表示的NAND 芯片的page 大小,如某片NAND 芯片的一個(gè)page 有512 個(gè)字節(jié),那么page_shift 就是9 ;
--584-->用位表示的NAND 芯片的每次可擦除的大小,如某片NAND 芯片每次可擦除16K 字節(jié)( 通常就是一個(gè)block 的大小) ,那么phys_erase_shift 就是14 ;
--585-->用位表示的bad block table 的大小,通常一個(gè)bbt 占用一個(gè)block ,所以bbt_erase_shift 通常與phys_erase_shift 相等;
--587-->表示系統(tǒng)中有多少片NAND 芯片;
--588-->NAND 芯片的大小;
--589-->計(jì)算page number 時(shí)的掩碼,總是等于chipsize/page 大小 - 1 ;
--590-->用來(lái)保存當(dāng)前讀取的NAND 芯片的page number ,這樣一來(lái),下次讀取的數(shù)據(jù)若還是屬于同一個(gè)page ,就不必再?gòu)腘AND 芯片讀取了,而是從data_buf 中直接得到;
--596-->表示壞塊信息保存在oob 中的第幾個(gè)字節(jié)。對(duì)于絕大多數(shù)的NAND 芯片,若page size> 512,那么壞塊信息從Byte 0 開始存儲(chǔ),否則就存儲(chǔ)在Byte 5 ,即第六個(gè)字節(jié)。
--619-->私有數(shù)據(jù)
nand_scan()
準(zhǔn)備好了一個(gè)nand_chip,接下來(lái)的工作就是匹配驅(qū)動(dòng)+注冊(cè)分區(qū)表。
NAND flash使用nand_scan()來(lái)匹配驅(qū)動(dòng),這個(gè)函數(shù)會(huì)讀取NAND芯片的ID,并根據(jù)mtd->priv即nand_chip中的成員初始化mtd_info。如果要分區(qū),則以mtd_info和mtd_partition為參數(shù)調(diào)用add_mtd_partitions來(lái)添加分區(qū)信息。
int nand_scan(struct mtd_info *mtd, int maxchips)
NAND Flash驅(qū)動(dòng)模板
#define CHIP_PHYSICAL_ADDRESS ... #define NUM_PARTITIONS 2 static struct mtd_partition partition_info[] = { { .name = "Flash partition 1", .info = 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設(shè)備對(duì)象和nand_chip分配內(nèi)存 */ 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; } /* 初始化結(jié)構(gòu)體 */ memset((char *)board_mtd, 0 ,sizeof(struct mtd_info) + sizeof(struct nand_chip)); /* 映射物理地址 */ baseaddr = (unsigned long) ioremap(CHIP_PHYSICAL_ADDRESS,1024); if(!baseaddr){ printk("Ioremap to access NAND Chip failed\n"); err = -EIO; goto out_mtd; } /* 獲取私有數(shù)據(jù)(nand_chip)指針 */ this = (struct nand_chip *)(&board_mtd[1]); /* 將nand_chip賦予mtd_info私有指針 */ board_mtd->priv = this; /* 設(shè)置NAND Flash的IO基地址 */ this->IO_ADDR_R = baseaddr; this->IO_ADDR_W = baseaddr; /* 硬件控制函數(shù) */ this->cmd_ctrl = board_hwcontrol; /* 初始化設(shè)備ready函數(shù) */ this->dev_ready = board_dev_ready; /* 掃描以確定設(shè)備的存在 */ if(nand_scan(board_mtd, 1)){ err = -ENXIO; goto = out_ior; } /* 添加分區(qū) */ 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) { /* 釋放資源,注銷設(shè)備 */ nand_release(board_mtd); /* unmap物理地址 */ iounmap((void *)baseaddr); /* 釋放MTD設(shè)備結(jié)構(gòu)體 */ kfree(board_mtd); } /* 硬件控制 */ static void board_hwcontrol(struct mtd_info *mtd, int dat,unsigned int ctrl) { ... if(ctrl & NAND_CTRL_CHANGE){ if(ctrl & NAND_NCE){ } } ... } /*返回ready狀態(tài)*/ static int board_dev_ready(struct mtd_info *mtd) { return xxx_read_ready_bit(); }
?
評(píng)論
查看更多