linux設備驅動(32)MTD子系統詳解


MTD(Memory Technology Device)即常說的Flash等使用存儲芯片的存儲設備,MTD子系統對應的是塊設備驅動框架中的設備驅動層,可以說,MTD就是針對Flash設備設計的標准化硬件驅動框架。

1 MTD子系統框架

(1)設備節點層:MTD框架可以在/dev下創建字符設備節點(主設備號90)以及塊設備節點(主設備號31), 用戶通過訪問此設備節點即可訪問MTD字符設備或塊設備。

(2)MTD設備層: 基於MTD原始設備, Linux在這一層次定義出了MTD字符設備和塊設備, 字符設備在mtdchar.c中實現, 塊設備則是通過結構mtdblk_dev來描述,"/drivers/mtd/mtdchar.c"文件實現了MTD字符設備接口; "/drivers/mtd/mtdblock.c"文件實現了MTD塊設備接口

(3)MTD原始設備層: 由MTD原始設備的通用代碼+特定的Flash數據組成。mtd_info、mtd_part、mtd_partition以及mtd_partitions等對象及其操作方法就屬於這一層,對應的文件是"drivers/mtd/mtdcore.c"。類似於i2c驅動框架中的核心層。

(4)硬件驅動層: 內核將常用的flash操作都已經在這個層次實現, 驅動開發只需要將相應的設備信息添加進去即可, 比如,NOR flash的芯片驅動位於"drivers/mtd/chips/", Nand flash位於"drivers/mtd/nand/"(eg s3c2410.c)

2 主要數據結構

2.1 mtd_info 結構體

描述原始設備層的一個分區的結構, 描述一個設備或一個多分區設備中的一個分區。定義位於:include\linux\mtd\mtd.h

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

  1 struct mtd_info {
  2     u_char type;//MTD設備類型,有MTD_RAM,MTD_ROM、MTD_NORFLASH、MTD_NAND_FLASH
  3     uint32_t flags;//讀寫及權限標志位,有MTD_WRITEABLE、MTD_BIT_WRITEABLE、MTD_NO_ERASE、MTD_UP_LOCK
  4     uint64_t size;     // Total size of the MTD MTD設備的大小
  5     uint32_t erasesize;//主要的擦除塊大小,NandFlash就是"塊"的大小
  6     uint32_t writesize;//最小可寫字節數,NandFlash一般對應"頁"的大小
  7     /*
  8      * Size of the write buffer used by the MTD. MTD devices having a write
  9      * buffer can write multiple writesize chunks at a time. E.g. while
 10      * writing 4 * writesize bytes to a device with 2 * writesize bytes
 11      * buffer the MTD driver can (but doesn't have to) do 2 writesize
 12      * operations, but not 4. Currently, all NANDs have writebufsize
 13      * equivalent to writesize (NAND page size). Some NOR flashes do have
 14      * writebufsize greater than writesize.
 15      */
 16     uint32_t writebufsize;
 17 
 18     uint32_t oobsize;   //一個block中的OOB字節數
 19     uint32_t oobavail;  //一個block中可用oob的字節數
 20 
 21     unsigned int erasesize_shift;
 22     unsigned int writesize_shift;
 23     /* Masks based on erasesize_shift and writesize_shift */
 24     unsigned int erasesize_mask;
 25     unsigned int writesize_mask;
 26 
 27     unsigned int bitflip_threshold;
 28 
 29     // Kernel-only stuff starts here.
 30     const char *name;
 31     int index;
 32 
 33     /* ECC layout structure pointer - read only! */
 34     struct nand_ecclayout *ecclayout;//ECC布局結構體指針
 35 
 36     /* max number of correctible bit errors per ecc step */
 37     unsigned int ecc_strength;
 38 
 39     /* Data for variable erase regions. If numeraseregions is zero,
 40      * it means that the whole device has erasesize as given above.
 41      */
 42     int numeraseregions;
 43     struct mtd_erase_region_info *eraseregions;
 44 
 45     /*
 46      * Do not call via these pointers, use corresponding mtd_*()
 47      * wrappers instead.
 48      */
 49     int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
 50     int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,//針對eXecute-In-Place,即XIP
 51                size_t *retlen, void **virt, resource_size_t *phys);
 52     int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);//如果這個指針為空,不允許XIP
 53     unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
 54                          unsigned long len,
 55                          unsigned long offset,
 56                          unsigned long flags);
 57     int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,//讀函數指針
 58               size_t *retlen, u_char *buf);
 59     int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,//寫函數指針
 60                size_t *retlen, const u_char *buf);
 61     int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
 62                  size_t *retlen, const u_char *buf);
 63     int (*_read_oob) (struct mtd_info *mtd, loff_t from,
 64               struct mtd_oob_ops *ops);
 65     int (*_write_oob) (struct mtd_info *mtd, loff_t to,
 66                struct mtd_oob_ops *ops);
 67     int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
 68                     size_t len);
 69     int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
 70                     size_t len, size_t *retlen, u_char *buf);
 71     int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
 72                     size_t len);
 73     int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
 74                     size_t len, size_t *retlen, u_char *buf);
 75     int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
 76                      size_t len, size_t *retlen, u_char *buf);
 77     int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
 78                     size_t len);
 79     int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
 80             unsigned long count, loff_t to, size_t *retlen);
 81     void (*_sync) (struct mtd_info *mtd);
 82     int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
 83     int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
 84     int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
 85     int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
 86     int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
 87     int (*_suspend) (struct mtd_info *mtd);
 88     void (*_resume) (struct mtd_info *mtd);
 89     /*
 90      * If the driver is something smart, like UBI, it may need to maintain
 91      * its own reference counting. The below functions are only for driver.
 92      */
 93     int (*_get_device) (struct mtd_info *mtd);
 94     void (*_put_device) (struct mtd_info *mtd);
 95 
 96     /* Backing device capabilities for this device
 97      * - provides mmap capabilities
 98      */
 99     struct backing_dev_info *backing_dev_info;
100 
101     struct notifier_block reboot_notifier;  /* default mode before reboot */
102 
103     /* ECC status information */
104     struct mtd_ecc_stats ecc_stats;
105     /* Subpage shift (NAND) */
106     int subpage_sft;
107 
108     void *priv;//私有數據
109 
110     struct module *owner;
111     struct device dev;
112     int usecount;
113 }

2.2 mtd_part結構體

內核管理分區的鏈表節點,通過它來實現對mtd_info對象的管理。定義 位於:\linux-3.10.73\drivers\mtd\mtdpart.c

1 struct mtd_part {
2           struct mtd_info mtd;//對應的mtd_info對象
3           struct mtd_info *master; //父對象指針                                        
4           uint64_t offset;//偏移量
5           struct list_head list;//鏈表節點
6   };

2.3 mtd_partition 

描述一個分區,定義位於:\include\linux\mtd\partitions.h

1 struct mtd_partition {
2     char *name;            /*分區名 */
3     uint64_t size;            /*分區大小,使用MTDPART_SIZ_FULL表示使用全部空間 */
4     uint64_t offset;        /*分區在master設備中的偏移量。MTDPART_OFS_APPEND表示從上一個分區結束的地方開始,MTDPART_OFS_NXTBLK表示從下一個擦除塊開始; MTDPART_OFS_RETAIN表示盡可能向后偏,把size大小的空間留下即可*/
5     uint32_t mask_flags;        /* 權限掩碼,MTD_WRITEABLE表示將父設備的只讀選項變成可寫(可寫分區要求size和offset要erasesize對齊,eg MTDPART_OFS_NEXTBLK) */
6     struct nand_ecclayout *ecclayout;    /* NANDFlash的OOB布局,OOB是NANDFlash中很有用空間,比如yaffs2就需要將壞塊信息存儲在OOB區域 */
7 };

2.4 鏈表mtd_partitions

鏈表頭,將所有的mtd_partition連接起來。

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

3 主要api

主要API的調用關系如下:

1 mtd_add_partition()
2    └── add_mtd_device()
3 add_mtd_partitions()
4    └── add_mtd_device()

3.1 函數mtd_add_partition

定義位於:drivers\mtd\mtdpart.c

通過將一個mtd_part對象注冊到內核,將mtd_info對象注冊到內核,即為一個設備添加一個分區。

 1 int mtd_add_partition(struct mtd_info *master, char *name,
 2               long long offset, long long length)
 3 {
 4     struct mtd_partition part;
 5     struct mtd_part *p, *new;
 6     uint64_t start, end;
 7     int ret = 0;
 8 
 9     /* the direct offset is expected */
10     if (offset == MTDPART_OFS_APPEND ||
11         offset == MTDPART_OFS_NXTBLK)
12         return -EINVAL;
13 
14     if (length == MTDPART_SIZ_FULL)
15         length = master->size - offset;
16 
17     if (length <= 0)
18         return -EINVAL;
19 
20     part.name = name;
21     part.size = length;
22     part.offset = offset;
23     part.mask_flags = 0;
24     part.ecclayout = NULL;
25 
26     new = allocate_partition(master, &part, -1, offset);
27     if (IS_ERR(new))
28         return PTR_ERR(new);
29 
30     start = offset;
31     end = offset + length;
32 
33     mutex_lock(&mtd_partitions_mutex);
34     list_for_each_entry(p, &mtd_partitions, list)
35         if (p->master == master) {
36             if ((start >= p->offset) &&
37                 (start < (p->offset + p->mtd.size)))
38                 goto err_inv;
39 
40             if ((end >= p->offset) &&
41                 (end < (p->offset + p->mtd.size)))
42                 goto err_inv;
43         }
44 
45     list_add(&new->list, &mtd_partitions);
46     mutex_unlock(&mtd_partitions_mutex);
47 
48     add_mtd_device(&new->mtd);
49 
50     return ret;
51 err_inv:
52     mutex_unlock(&mtd_partitions_mutex);
53     free_partition(new);
54     return -EINVAL;
55 }

3.2 函數add_mtd_partitions

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

 1 int add_mtd_partitions(struct mtd_info *master,
 2                const struct mtd_partition *parts,
 3                int nbparts)
 4 {
 5     struct mtd_part *slave;
 6     uint64_t cur_offset = 0;
 7     int i;
 8 
 9     printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
10 
11     for (i = 0; i < nbparts; i++) {
12         slave = allocate_partition(master, parts + i, i, cur_offset);
13         if (IS_ERR(slave))
14             return PTR_ERR(slave);
15 
16         mutex_lock(&mtd_partitions_mutex);
17         list_add(&slave->list, &mtd_partitions);
18         mutex_unlock(&mtd_partitions_mutex);
19 
20         add_mtd_device(&slave->mtd);
21 
22         cur_offset = slave->offset + slave->mtd.size;
23     }
24 
25     return 0;
26 }

3.3 函數add_mtd_device

分配並初始化一個mtd對象。 定位於:drivers\mtd\mtdcore.c

 1 int add_mtd_device(struct mtd_info *mtd)
 2 {
 3     struct mtd_notifier *not;
 4     int i, error;
 5 
 6     if (!mtd->backing_dev_info) {
 7         switch (mtd->type) {
 8         case MTD_RAM:
 9             mtd->backing_dev_info = &mtd_bdi_rw_mappable;
10             break;
11         case MTD_ROM:
12             mtd->backing_dev_info = &mtd_bdi_ro_mappable;
13             break;
14         default:
15             mtd->backing_dev_info = &mtd_bdi_unmappable;
16             break;
17         }
18     }
19 
20     BUG_ON(mtd->writesize == 0);
21     mutex_lock(&mtd_table_mutex);
22 
23     i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
24     if (i < 0)
25         goto fail_locked;
26 
27     mtd->index = i;
28     mtd->usecount = 0;
29 
30     /* default value if not set by driver */
31     if (mtd->bitflip_threshold == 0)
32         mtd->bitflip_threshold = mtd->ecc_strength;
33 
34     if (is_power_of_2(mtd->erasesize))
35         mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
36     else
37         mtd->erasesize_shift = 0;
38 
39     if (is_power_of_2(mtd->writesize))
40         mtd->writesize_shift = ffs(mtd->writesize) - 1;
41     else
42         mtd->writesize_shift = 0;
43 
44     mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
45     mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
46 
47     /* Some chips always power up locked. Unlock them now */
48     if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
49         error = mtd_unlock(mtd, 0, mtd->size);
50         if (error && error != -EOPNOTSUPP)
51             printk(KERN_WARNING
52                    "%s: unlock failed, writes may not work\n",
53                    mtd->name);
54     }
55 
56     /* Caller should have set dev.parent to match the
57      * physical device.
58      */
59     mtd->dev.type = &mtd_devtype;
60     mtd->dev.class = &mtd_class;
61     mtd->dev.devt = MTD_DEVT(i);
62     dev_set_name(&mtd->dev, "mtd%d", i);//設置MTD設備的名字
63     dev_set_drvdata(&mtd->dev, mtd);//設置私有數據,將mtd地址藏到device->device_private->void* driver_data
64     if (device_register(&mtd->dev) != 0)
65         goto fail_added;
66 
67     if (MTD_DEVT(i))
68         device_create(&mtd_class, mtd->dev.parent,
69                   MTD_DEVT(i) + 1,
70                   NULL, "mtd%dro", i);
71 
72     pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
73     /* No need to get a refcount on the module containing
74        the notifier, since we hold the mtd_table_mutex */
75     list_for_each_entry(not, &mtd_notifiers, list)//遍歷所有的mtd_notifier,將其添加到通知鏈
76         not->add(mtd);
77 
78     mutex_unlock(&mtd_table_mutex);
79     /* We _know_ we aren't being removed, because
80        our caller is still holding us here. So none
81        of this try_ nonsense, and no bitching about it
82        either. :) */
83     __module_get(THIS_MODULE);
84     return 0;
85 
86 fail_added:
87     idr_remove(&mtd_idr, i);
88 fail_locked:
89     mutex_unlock(&mtd_table_mutex);
90     return 1;
91 }

4 用戶空間編程

MTD設備提供了字符設備和塊設備兩種接口,對於字符設備接口,在"drivers/mtd/mtdchar.c"中實現了,比如,用戶程序可以直接通過ioctl()回調相應的驅動實現。其中下面的幾個是這些操作中常用的結構,這些結構是對用戶空間開放的,類似於輸入子系統中的input_event結構。

4.1 相關結構體

mtd_info_user,定義位於:include\uapi\mtd\mtd-abi.h

1 struct mtd_info_user {
2     __u8 type;
3     __u32 flags;
4     __u32 size;    /* Total size of the MTD */
5     __u32 erasesize;
6     __u32 writesize;
7     __u32 oobsize;    /* Amount of OOB data per block (e.g. 16) */
8     __u64 padding;    /* Old obsolete field; do not use */
9 };

mtd_oob_buf描述NandFlash的OOB(Out Of Band)信息。

1 struct mtd_oob_buf {
2     __u32 start;
3     __u32 length;
4     unsigned char __user *ptr;
5 };

結構體erase_info_user 

1 struct erase_info_user {
2     __u32 start;
3     __u32 length;
4 };

實例

 1 mtd_oob_buf oob;
 2 erase_info_user erase;
 3 mtd_info_user meminfo;
 4 
 5 /* 獲得設備信息 */
 6 if(0 != ioctl(fd, MEMGETINFO, &meminfo))
 7     perror("MEMGETINFO");
 8     
 9 /* 擦除塊 */
10 if(0 != ioctl(fd, MEMERASE, &erase))
11     perror("MEMERASE");
12 
13 /* 讀OOB */
14 if(0 != ioctl(fd, MEMREADOOB, &oob))
15     perror("MEMREADOOB");
16 
17 /* 寫OOB??? */    
18 if(0 != ioctl(fd, MEMWRITEOOB, &oob))
19     perror("MEMWRITEOOB");
20     
21 /* 檢查壞塊 */
22 if(blockstart != (ofs & (~meminfo.erase + 1))){
23     blockstart = ofs & (~meminfo.erasesize + 1);
24     if((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0)
25         perror("MEMGETBADBLOCK");
26     else if(badblock)
27         /* 壞塊代碼 */
28     else
29         /* 好塊代碼 */
30 }

NANDFlash和NORFlash都是基於MTD框架編寫的,由於MTD框架中通用代碼已經在內核中實現了,所以驅動開發主要是進行MTD框架中的的開發。

5 nor flash

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

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

結構體map_info

定義位於:include\linux\mtd\map.h

 1 struct map_info {
 2     const char *name;
 3     unsigned long size;//NOR Flash設備的容量
 4     resource_size_t phys;//NOR Flash在物理地址空間中的地址
 5 #define NO_XIP (-1UL)
 6 
 7     void __iomem *virt;//由物理地址映射的虛擬地址
 8     void *cached;
 9 
10     int swap; /* this mapping's byte-swapping requirement */
11     int bankwidth; /* in octets. This isn't necessarily the width//總線寬度,NOR Flash是有地址總線的,所以才能片上執行,一般都是8位或16位寬
12                of actual bus cycles -- it's the repeat interval
13               in bytes, before you are talking to the first chip again.
14               */
15 
16 #ifdef CONFIG_MTD_COMPLEX_MAPPINGS
17     map_word (*read)(struct map_info *, unsigned long);
18     void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
19 
20     void (*write)(struct map_info *, const map_word, unsigned long);
21     void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
22 
23     /* We can perhaps put in 'point' and 'unpoint' methods, if we really
24        want to enable XIP for non-linear mappings. Not yet though. */
25 #endif
26     /* It's possible for the map driver to use cached memory in its
27        copy_from implementation (and _only_ with copy_from).  However,
28        when the chip driver knows some flash area has changed contents,
29        it will signal it to the map driver through this routine to let
30        the map driver invalidate the corresponding cache as needed.
31        If there is no cache to care about this can be set to NULL. */
32     void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
33 
34     /* set_vpp() must handle being reentered -- enable, enable, disable
35        must leave it enabled. */
36     void (*set_vpp)(struct map_info *, int);
37 
38     unsigned long pfow_base;
39     unsigned long map_priv_1;
40     unsigned long map_priv_2;
41     struct device_node *device_node;
42     void *fldrv_priv;
43     struct mtd_chip_driver *fldrv;
44 }

構造好一個map_info對象之后,接下來的工作就是匹配驅動+注冊分區表。

函數do_map_probe用來根據傳入的參數匹配一個map_info對象的驅動,比如CFI接口或JEDEC接口的NOR Flash,其定義位於:drivers\mtd\chips\chipreg.c 如下:

 1 struct mtd_info *do_map_probe(const char *name, struct map_info *map)
 2 {
 3     struct mtd_chip_driver *drv;
 4     struct mtd_info *ret;
 5 
 6     drv = get_mtd_chip_driver(name);
 7 
 8     if (!drv && !request_module("%s", name))
 9         drv = get_mtd_chip_driver(name);
10 
11     if (!drv)
12         return NULL;
13 
14     ret = drv->probe(map);
15 
16     /* We decrease the use count here. It may have been a
17        probe-only module, which is no longer required from this
18        point, having given us a handle on (and increased the use
19        count of) the actual driver code.
20     */
21     module_put(drv->module);
22 
23     return ret;
24 }

對於常用的NorFlash標准,這個函數的調用方式如下:

1 do_map_probe("cfi_probe", &xxx_map_info);
2 do_map_probe("jedec_probe",&xxx_map_info);
3 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的例子:

 1 #define WINDOW_SIZE ...
 2 #define WINDOW_ADDR ...
 3 static struct map_info xxx_map = {
 4     .name = "xxx flash",
 5     .size = WINDOW_SIZE,
 6     .bankwidth = 1,
 7     .phys = WINDOW_ADDR,
 8 };
 9 
10 static struct mtd_partition xxx_partitions[] = {
11     .name = "Drive A",
12     .offset = 0,
13     .size = 0x0e000,
14 };
15 
16 #define NUM_PARTITIONS ARRAY_SIZE(xxx_partitions)
17 
18 static struct mtd_info *mymtd;
19 
20 static int __init init_xxx_map(void)
21 {
22     int rc = 0;
23     xxx_map.virt = ioremap_nocache(xxx_map.phys, xxx_map.size);
24     if(!xxx_map.virt){
25         printk(KERN_ERR"Failed to ioremap_nocache\n");
26         rc = -EIO;
27         goto err2;
28     }
29     simple_map_init(&xxx_map);
30     mymtd = do_map_probe("jedec_probe", &xxx_map);
31     if(!mymtd){
32         rc = -ENXIO;
33         goto err1;
34     }
35     mymtd->owner = THIS_MODULE;
36     add_mtd_partitions(mymtd, xxx_partitions, NUM_PARTITIONS);
37     return 0;
38 err1:
39     map_destroy(mymtd);
40     iounmap(xxx_map.virt);
41 err2:
42     return rc;
43 }
44 static void __exit cleanup_xxx_map(void)
45 {
46     if(mymtd){
47         del_mtd_partitions(mymtd);
48         map_destroy(mymtd);
49     }
50     
51     if(xxx_map.virt){
52         iounmap(xxx_map.virt);
53         xxx_map.virt = NULL;
54     }
55 }

6 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對象。定義位於:include\linux\mtd\nand.h

 1 struct nand_chip {
 2     void __iomem *IO_ADDR_R;
 3     void __iomem *IO_ADDR_W;
 4 
 5     uint8_t (*read_byte)(struct mtd_info *mtd);
 6     u16 (*read_word)(struct mtd_info *mtd);
 7     void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
 8     void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
 9     void (*select_chip)(struct mtd_info *mtd, int chip);
10     int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
11     int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
12     void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
13     int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
14             u8 *id_data);
15     int (*dev_ready)(struct mtd_info *mtd);
16     void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
17             int page_addr);
18     int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
19     void (*erase_cmd)(struct mtd_info *mtd, int page);
20     int (*scan_bbt)(struct mtd_info *mtd);
21     int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
22             int status, int page);
23     int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
24             uint32_t offset, int data_len, const uint8_t *buf,
25             int oob_required, int page, int cached, int raw);
26     int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
27             int feature_addr, uint8_t *subfeature_para);
28     int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
29             int feature_addr, uint8_t *subfeature_para);
30 
31     int chip_delay;
32     unsigned int options;//與具體的NAND 芯片相關的一些選項,如NAND_BUSWIDTH_16 等,可以參考<Linux/mtd/nand.h>
33     unsigned int bbt_options;
34 
35     int page_shift;//用位表示的NAND 芯片的page 大小,如某片NAND 芯片的一個page 有512 個字節,那么page_shift 就是9
36     int phys_erase_shift;//用位表示的NAND 芯片的每次可擦除的大小,如某片NAND 芯片每次可擦除16K 字節( 通常就是一個block 的大小) ,那么phys_erase_shift 就是14
37     int bbt_erase_shift;//用位表示的bad block table 的大小,通常一個bbt 占用一個block ,所以bbt_erase_shift 通常與phys_erase_shift 相等
38     int chip_shift;
39     int numchips;//表示系統中有多少片NAND 芯片
40     uint64_t chipsize;//NAND 芯片的大小
41     int pagemask;//計算page number 時的掩碼,總是等於chipsize/page 大小 - 1
42     int pagebuf;//用來保存當前讀取的NAND 芯片的page number ,這樣一來,下次讀取的數據若還是屬於同一個page ,就不必再從NAND 芯片讀取了,而是從data_buf 中直接得到
43     unsigned int pagebuf_bitflips;
44     int subpagesize;
45     uint8_t cellinfo;
46     int badblockpos;//表示壞塊信息保存在oob 中的第幾個字節。對於絕大多數的NAND 芯片,若page size> 512,那么壞塊信息從Byte 0 開始存儲,否則就存儲在Byte 5 ,即第六個字節
47     int badblockbits;
48 
49     int onfi_version;
50     struct nand_onfi_params    onfi_params;
51 
52     flstate_t state;
53 
54     uint8_t *oob_poi;
55     struct nand_hw_control *controller;
56     struct nand_ecclayout *ecclayout;
57 
58     struct nand_ecc_ctrl ecc;//NAND芯片的OOB分布和模式,如果不賦值,則會使用內核默認的OOB
59     struct nand_buffers *buffers;
60     struct nand_hw_control hwcontrol;
61 
62     uint8_t *bbt;
63     struct nand_bbt_descr *bbt_td;
64     struct nand_bbt_descr *bbt_md;
65 
66     struct nand_bbt_descr *badblock_pattern;
67 
68     void *priv;//私有數據
69 }

函數nand_scan

定義位於:drivers\mtd\nand\nand_base.c

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

 1 int nand_scan(struct mtd_info *mtd, int maxchips)
 2 {
 3     int ret;
 4 
 5     /* Many callers got this wrong, so check for it for a while... */
 6     if (!mtd->owner && caller_is_module()) {
 7         pr_crit("%s called with NULL mtd->owner!\n", __func__);
 8         BUG();
 9     }
10 
11     ret = nand_scan_ident(mtd, maxchips, NULL);
12     if (!ret)
13         ret = nand_scan_tail(mtd);
14     return ret;
15 }

nand flash驅動例子:

  1 #define CHIP_PHYSICAL_ADDRESS ...  
  2 #define NUM_PARTITIONS 2 
  3 static struct mtd_partition partition_info[] = { 
  4         { 
  5                 .name = "Flash partition 1", 
  6                 .info = 0, 
  7                 .size = 8 * 1024 * 1024, 
  8         }, 
  9         { 
 10                 .name = "Flash partition 2", 
 11                 offset = MTDPART_OFS_NEXT, 
 12                 size = MTDPART_SIZ_FULL, 
 13         }, 
 14 }; 
 15  
 16  
 17 int __init board_init(void) 
 18 { 
 19         struct nand_chip *this; 
 20         int err = 0; 
 21         /* 為MTD設備對象和nand_chip分配內存 */ 
 22         board_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),GFP_KERNEL); 
 23         if(!board_mtd){ 
 24                 printk("Unable to allocate NAND MTD device structure\n"); 
 25                 err = -ENOMEM; 
 26                 goto out; 
 27         } 
 28  
 29         /* 初始化結構體 */ 
 30         memset((char *)board_mtd, 0 ,sizeof(struct mtd_info) + sizeof(struct nand_chip)); 
 31  
 32         /* 映射物理地址 */ 
 33         baseaddr = (unsigned long) ioremap(CHIP_PHYSICAL_ADDRESS,1024); 
 34         if(!baseaddr){ 
 35                 printk("Ioremap to access NAND Chip failed\n"); 
 36                 err = -EIO; 
 37                 goto out_mtd; 
 38         } 
 39  
 40         /* 獲取私有數據(nand_chip)指針 */ 
 41         this = (struct nand_chip *)(&board_mtd[1]); 
 42  
 43         /* 將nand_chip賦予mtd_info私有指針 */ 
 44         board_mtd->priv = this; 
 45  
 46         /* 設置NAND Flash的IO基地址 */ 
 47         this->IO_ADDR_R = baseaddr; 
 48         this->IO_ADDR_W = baseaddr; 
 49  
 50         /* 硬件控制函數 */ 
 51         this->cmd_ctrl = board_hwcontrol; 
 52  
 53         /* 初始化設備ready函數 */ 
 54         this->dev_ready = board_dev_ready; 
 55          
 56         /* 掃描以確定設備的存在 */ 
 57         if(nand_scan(board_mtd, 1)){ 
 58                 err = -ENXIO; 
 59                 goto = out_ior; 
 60         } 
 61  
 62         /* 添加分區 */ 
 63         add_mtd_partitions(board_mtd,partition_info,NUM_PARTITIONS); 
 64         goto out; 
 65 out_ior: 
 66         iounmap((void *)baseaddr); 
 67 out_mtd: 
 68         kfree(board_mtd); 
 69 out: 
 70         return err; 
 71 } 
 72  
 73 static void __exit board_cleanup(void) 
 74 { 
 75         /* 釋放資源,注銷設備 */ 
 76         nand_release(board_mtd); 
 77  
 78         /* unmap物理地址 */ 
 79         iounmap((void *)baseaddr); 
 80  
 81         /* 釋放MTD設備結構體 */ 
 82         kfree(board_mtd); 
 83 } 
 84  
 85 /* 硬件控制 */ 
 86 static void board_hwcontrol(struct mtd_info *mtd, int dat,unsigned int ctrl) 
 87 { 
 88         ... 
 89         if(ctrl & NAND_CTRL_CHANGE){ 
 90                 if(ctrl & NAND_NCE){ 
 91  
 92                 } 
 93         } 
 94         ... 
 95 } 
 96  
 97 /*返回ready狀態*/ 
 98 static int board_dev_ready(struct mtd_info *mtd) 
 99 { 
100         return xxx_read_ready_bit(); 
101 } 

 

參考博文:https://www.cnblogs.com/xiaojiang1025/p/6615898.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM