Linux MTD子系統 _從模型分析到Flash驅動模板

MTD(Memory Technology Device)即常說的Flash等使用存儲芯片的存儲設備,MTD子系統對應的是塊設備驅動框架中的設備驅動層,能夠說,MTD就是針對Flash設備設計的標準化硬件驅動框架。本文基於3.14內核,討論MTD驅動框架。node

MTD子系統框架

  • 設備節點層:MTD框架能夠在/dev下建立字符設備節點(主設備號90)以及塊設備節點(主設備號31), 用戶經過訪問此設備節點便可訪問MTD字符設備或塊設備。
  • MTD設備層: 基於MTD原始設備, Linux在這一層次定義出了MTD字符設備和塊設備, 字符設備在mtdchar.c中實現, 塊設備則是經過結構mtdblk_dev來描述,"/drivers/mtd/mtdchar.c"文件實現了MTD字符設備接口; "/drivers/mtd/mtdblock.c"文件實現了MTD塊設備接口
  • MTD原始設備層: 由MTD原始設備的通用代碼+特定的Flash數據組成。mtd_info、mtd_part、mtd_partition以及mtd_partitions等對象及其操做方法就屬於這一層,對應的文件是"drivers/mtd/mtdcore.c"。相似於i2c驅動框架中的核心層。
  • 硬件驅動層: 內核將經常使用的flash操做都已經在這個層次實現, 驅動開發只須要將相應的設備信息添加進去便可, 好比,NOR flash的芯片驅動位於"drivers/mtd/chips/", Nand flash位於"drivers/mtd/nand/"(eg s3c2410.c)

核心結構和方法簡述

爲了實現上述的框架, 內核中使用了以下類和API, 這些幾乎是開發一個MTD驅動必須的編程

核心結構

  • mtd_info描述原始設備層的一個分區的結構, 描述一個設備或一個多分區設備中的一個分區
  • mtd_table管理原始設備層的mtd_info的數組
  • mtd_part表示一個分區, 其中的struct mtd_info mtd描述該分區的信息, 一個物理Flash設備能夠有多於1個mtd_part,每一個mtd_part都對應一個mtd_info。
  • mtd_partition描述一個分區表, 經過管理mtd_part以及每個mtd_part中的mtd_info來描述全部的分區,一個物理Flash設備只有一個mtd_partition
  • mtd_partitions是一個list_head對象,用於管理mtd_partition們
  • map_info描述一個NOR Flash設備
  • nand_chip描述一個NAND Flash設備

核心方法

  • add_mtd_device()/del_mtd_device()註冊/註銷一個MTD設備
  • add_mtd_partitions()/del_mtd_partitions()註冊註銷一個或多個分區表,
  • do_map_probe()用來根據傳入的參數匹配一個map_info對象的驅動,好比CFI接口或JEDEC接口的NOR Flash,並返回一個mtd_info以便註冊分區信息。
  • nand_scan():NAND flash使用這個API來匹配驅動。

核心結構與方法詳述

mtd_info

自己是沒有list_head來供內核管理,對mtd_info對象的管理是經過mtd_part來實現的。mtd_info對象屬於原始設備層,裏面的不少函數接口內核已經實現了。mtd_info中的read()/write()等操做是MTD設備驅動要實現的主要函數,在NORFlash或NANDFlash中的驅動代碼中幾乎看不到mtd_info的成員函數,即這些函數對於Flash芯片是透明的,由於Linux在MTD的下層實現了針對NORFlash和NANDFlash的通用的mtd_info函數。api

114 struct mtd_info {
115         u_char type;
116         uint32_t flags;
117         uint64_t size;   // Total size of the MTD
118 
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 block
146 
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 maintain
231          * 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 device
237          * - provides mmap capabilities
238          */
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設備類型,有MTD_RAM,MTD_ROM、MTD_NORFLASH、MTD_NAND_FLASH
--116-->讀寫及權限標誌位,有MTD_WRITEABLE、MTD_BIT_WRITEABLE、MTD_NO_ERASE、MTD_UP_LOCK
--117-->MTD設備的大小
--123-->主要的擦除塊大小,NandFlash就是"塊"的大小
--131-->最小可寫字節數,NandFlash通常對應"頁"的大小
--144-->一個block中的OOB字節數
--145-->一個block中可用oob的字節數
--171-->ECC佈局結構體指針
--190-->針對eXecute-In-Place,即XIP
--192-->若是這個指針爲空,不容許XIP
--197-->讀函數指針
--199-->寫函數指針
--248-->私有數據數組

mtd_part

內核管理分區的鏈表節點,經過它來實現對mtd_info對象的管理。app

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-->對應的mtd_info對象
--43-->父對象指針
--44-->偏移量
--45-->鏈表節點框架

mtd_partition

描述一個分區ide

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-->分區名
--41-->分區大小,使用MTDPART_SIZ_FULL表示使用所有空間
--42-->分區在master設備中的偏移量。MTDPART_OFS_APPEND表示從上一個分區結束的地方開始,MTDPART_OFS_NXTBLK表示從下一個擦除塊開始; MTDPART_OFS_RETAIN表示儘量向後偏,把size大小的空間留下便可
--43-->權限掩碼,MTD_WRITEABLE表示將父設備的只讀選項變成可寫(可寫分區要求size和offset要erasesize對齊,eg MTDPART_OFS_NEXTBLK)
--44-->NANDFlash的OOB佈局,OOB是NANDFlash中頗有用空間,好比yaffs2就須要將壞塊信息存儲在OOB區域函數

mtd_partitions

鏈表頭,將全部的mtd_partition鏈接起來。佈局

36 /* Our partition linked list */
 37 static LIST_HEAD(mtd_partitions);

下圖是關鍵API的調用關係。ui

mtd_add_partition()
   └── add_mtd_device()
add_mtd_partitions()
   └── add_mtd_device()

add_mtd_device()

分配並初始化一個mtd對象。

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-->設置MTD設備的名字
--396-->設置私有數據,將mtd地址藏到device->device_private->void* driver_data
--408-->遍歷全部的mtd_notifier,將其添加到通知鏈

mtd_add_partition()

經過將一個mtd_part對象註冊到內核,將mtd_info對象註冊到內核,即爲一個設備添加一個分區。

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()

添加一個分區表到內核,一個MTD設備一個分區表

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設備提供了字符設備和塊設備兩種接口,對於字符設備接口,在"drivers/mtd/mtdchar.c"中實現了,好比,用戶程序能夠直接經過ioctl()回調相應的驅動實現。其中下面的幾個是這些操做中經常使用的結構,這些結構是對用戶空間開放的,相似於輸入子系統中的input_event結構。

mtd_info_user

//include/uapi/mtd/mtd-abi.h
125 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 };

實例

mtd_oob_buf oob;
erase_info_user erase;
mtd_info_user meminfo;

/* 得到設備信息 */
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框架中通用代碼已經在內核中實現了,因此驅動開發主要是進行MTD框架中的的開發。

NOR Flash驅動

下圖就是NORFlash驅動在MTD驅動框架中的位置

基於上述的MTD框架, Flash驅動都變的十分的簡單, 由於當下Flash的操做接口已經很統一, a, 相應的代碼在"drivers/mtd/chips"中文件實現,因此在設備驅動層, 留給驅動工程師的工做就大大的減小了。
基於MTD子系統開發NOR FLash驅動,只須要構造一個map_info類型的對象並調用do_map_probe()來匹配內核中已經寫好的驅動,好比CFI接口的驅動或JEDEC接口的驅動。當下編寫一個NorFlash驅動的工做流程以下

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設備的容量
--211-->NOR Flash在物理地址空間中的地址
--214-->由物理地址映射的虛擬地址
--218-->總線寬度,NOR Flash是有地址總線的,因此才能片上執行,通常都是8位或16位寬

構造好一個map_info對象以後,接下來的工做就是匹配驅動+註冊分區表

do_map_probe()

這個API用來根據傳入的參數匹配一個map_info對象的驅動,好比CFI接口或JEDEC接口的NOR Flash。這個函數的接口以下:

struct mtd_info *do_map_probe(const char *name, struct map_info *map)

對於經常使用的NorFlash標準,這個函數的調用方式以下:

do_map_probe("cfi_probe", &xxx_map_info);
do_map_probe("jedec_probe",&xxx_map_info);
do_map_probe("map_rom",&xxx_map_info);

匹配了設備驅動,能夠發現一個map_info對象中沒有mtd_partitions相關的信息,對於一個NOR Flash的分區信息,須要經過do_map_probe返回的mtd_info對象來註冊到內核。這裏咱們能夠先調用parse_mtd_partitions()查看Flash上已有的分區信息,獲取了分區信息以後再調用add_mtd_partitions()將分區信息寫入內核

NOR Flash驅動模板

#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驅動

下圖就是基於MTD框架的NandFlash驅動的位置。

Nand Flash和NOR Flash相似,內核中已經在"drivers/mtd/nand/nand_base.c"中實現了通用的驅動程序,驅動開發中不須要再實現mtd_info中的read, write, read_oob, write_oob等接口,只須要構造並註冊一個nand_chip對象, 這個對象主要描述了一片flash芯片的相關信息,包括地址信息,讀寫方法,ECC模式,硬件控制等一系列底層機制。當下,編寫一個NandFlash驅動的工做流程以下:

nand_chip

這個結構描述一個NAND Flash設備,一般藏在mtd_info->priv中,以便在回調其中的接口的時候能夠找到nand_chip對象。

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分佈和模式,若是不賦值,則會使用內核默認的OOB
--580-->與具體的NAND 芯片相關的一些選項,如NAND_BUSWIDTH_16 等,能夠參考<Linux/mtd/nand.h>
--583-->用位表示的NAND 芯片的page 大小,如某片NAND 芯片的一個page 有512 個字節,那麼page_shift 就是9 ;
--584-->用位表示的NAND 芯片的每次可擦除的大小,如某片NAND 芯片每次可擦除16K 字節( 一般就是一個block 的大小) ,那麼phys_erase_shift 就是14 ;
--585-->用位表示的bad block table 的大小,一般一個bbt 佔用一個block ,因此bbt_erase_shift 一般與phys_erase_shift 相等;
--587-->表示系統中有多少片NAND 芯片;
--588-->NAND 芯片的大小;
--589-->計算page number 時的掩碼,老是等於chipsize/page 大小 - 1 ;
--590-->用來保存當前讀取的NAND 芯片的page number ,這樣一來,下次讀取的數據若仍是屬於同一個page ,就沒必要再從NAND 芯片讀取了,而是從data_buf 中直接獲得;
--596-->表示壞塊信息保存在oob 中的第幾個字節。對於絕大多數的NAND 芯片,若page size> 512,那麼壞塊信息從Byte 0 開始存儲,不然就存儲在Byte 5 ,即第六個字節。
--619-->私有數據

nand_scan()

準備好了一個nand_chip,接下來的工做就是匹配驅動+註冊分區表
NAND flash使用nand_scan()來匹配驅動,這個函數會讀取NAND芯片的ID,並根據mtd->priv即nand_chip中的成員初始化mtd_info。若是要分區,則以mtd_info和mtd_partition爲參數調用add_mtd_partitions來添加分區信息。

int nand_scan(struct mtd_info *mtd, int maxchips)

NAND Flash驅動模板

#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設備對象和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, 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; 
        } 
 
        /* 獲取私有數據(nand_chip)指針 */ 
        this = (struct nand_chip *)(&board_mtd[1]); 
 
        /* 將nand_chip賦予mtd_info私有指針 */ 
        board_mtd->priv = this; 
 
        /* 設置NAND Flash的IO基地址 */ 
        this->IO_ADDR_R = baseaddr; 
        this->IO_ADDR_W = baseaddr; 
 
        /* 硬件控制函數 */ 
        this->cmd_ctrl = board_hwcontrol; 
 
        /* 初始化設備ready函數 */ 
        this->dev_ready = board_dev_ready; 
         
        /* 掃描以肯定設備的存在 */ 
        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); 
} 
 
/* 硬件控制 */ 
static void board_hwcontrol(struct mtd_info *mtd, int dat,unsigned int ctrl) 
{ 
        ... 
        if(ctrl & NAND_CTRL_CHANGE){ 
                if(ctrl & NAND_NCE){ 
 
                } 
        } 
        ... 
} 
 
/*返回ready狀態*/ 
static int board_dev_ready(struct mtd_info *mtd) 
{ 
        return xxx_read_ready_bit(); 
}
相關文章
相關標籤/搜索