linux內核中mtd架構分析


一. 引言

MTD(memory technology device內存技術設備)是用於訪問memory設備(RAM、ROM、flash)的Linux的子系統。MTD的主要目的是為了使新的memory設備的驅動更加簡單,為此它在硬件和上層之間提供了一個抽象的接口。MTD的所有源代碼在/drivers/mtd子目錄下。(參考 百度百科)

We're working on a generic Linux subsystem for memory devices, especially Flash devices.

The aim of the system is to make it simple to provide a driver for new hardware, by providing a generic interface between the hardware drivers and the upper layers of the system.

Hardware drivers need to know nothing about the storage formats used, such as FTL, FFS2, etc., but will only need to provide simple routines for readwrite and erase. Presentation of the device's contents to the user in an appropriate form will be handled by the upper layers of the system.

 

傳統上, UNIX 只認識塊設備和字符設備。字符設備是類似鍵盤或者鼠標的這類設備,你必須從它讀取當前數據,但是不可以定位也沒有大小。塊設備有固定的大小並且可以定位, 它們恰好組織成許多字節的塊,通常為512字節。

閃存既不滿足塊設備描述也不滿足字符設備的描述。它們表現的類似塊設備,但又有所不同。比如,塊設備不區分寫和擦除操作。因此,一種符合閃存特性的特殊設備類型誕生了, 就是 MTD 設備。所以 MTD 既不是塊設備,也不是字符設備。

注:MTD主要就是為NorFlash和NandFlash設計的,其余像接口映射、RAM、ROM等都是輔助功能。

二. 設計結構

 

 

注:文件系統不基於MTD塊設備設計

mtd提供了操作flash的框架,供上層以統一的接口調用,而具體的flash都是通過硬件控制器操作的,硬件控制器的驅動程序來成mtd具體接口的實現。而同種flash(nandflash或norflash)操作流程相同(flash的操作命令及時序都符合國際標准),從而同種類的flash又進一步提取出一個驅動框架(完成了大部分通用讀寫、擦除),如map_info、nand_chip。最終,flash驅動程序僅實現與本驅動器和flash密切相關的屬性,從而填充map_info或nand_chip。

MTD設備通常可分為四層:設備節點、MTD設備層、MTD原始設備層和硬件驅動層,這四層的作用如下。
》》硬件驅動層:Flash硬件驅動層負責flash硬件設備的讀寫、擦除,主要針對NorFlash(mtd/chips)和NandFlash(mtd/nand)。
》》MTD原始設備層:MTD原始設備層有兩部分組成,一部分是MTD原始設備的通用代碼,另一部分是各個特定的Flash數據,例如分區。
》》MTD設備層:基於MTD原始設備,linux系統可以定義出MTD的塊設備(主設備號31)和字符設備(設備號90),構成MTD設備層。MTD塊設備定義了一個描述MTD塊設備的結構mtdblk_dev,並聲明了一個名為mtdblks的指針數組,這個數組中每一個mtdblk_dev和mtd_table中每一個mtd_info—對應。
》》設備節點:通過mknod在/dev子目錄下建立MTD字符設備節點(/dev/mtd#)和MTD塊設備節點(/dev/mtdblock#),用戶通過訪問此設備節點即可訪問MTD字符設備和塊設備。

內核啟動后,通過mount 命令可以將flash中的其余分區作為文件系統掛載到mountpoint上。
注:文件系統基於mtd原始設備層,直接調用mtd相關接口讀寫。向上提供接口給VFS,VFS調用設備節點或直接文件系統自身完成。

 

 

三. 應用

僅供參考,摘自mtd website。

The MTD system is divided into two types of module: "users" and "drivers".

Drivers are the modules which provide raw read/write/erase access to physical memory devices.

Users are the modules which use MTD drivers and provide a higher-level interface to user-space.

We currently have four 'user' modules available: FTL, NFTL, JFFS and MTDBLOCK. FTL and NFTL both provide a pseudo-block device on which a 'normal' filesystem is placed. JFFS is a filesystem which runs directly on the flash, and MTDBLOCK performs no translation - just provides a block device interface directly to the underlying MTD driver.

Just because I use the word 'module', it doesn't mean that these have to be loadable modules. You can link them statically into your kernel.

Writing a driver module

Instructions for writing a driver are very simple:

  • Allocate and populate a struct mtd_info with information about your device, and pointers to your access routines.
  • Register it by calling add_mtd_device

Oh yes - you have to actually write the access routines too, which have to conform to the rules.

Writing a user module

This is only slightly more complex:

  • Write a pair of notifier add and remove functions, which will be called whenever a driver is added to, or removed from, the system, respectively.
  • Register them by calling register_mtd_notifier

This ought to call your notifier function immediately for all drivers which are already present in the system. But it doesn't yet. Currently, drivers scan through callingget_mtd_device() to find previously-loaded drivers. This is bad and will be fixed soon.

  1. 1.  mtd user modules

These are the modules which provide interfaces that can be used directly from userspace. The user modules currently planned include:

  • Raw character access:
    A character device which allows direct access to the underlying memory. Useful for creating filesystems on the devices, before using some of the translation drivers below, or for raw storage on infrequently-changed flash, or RAM devices.
  • Raw block access
    A block device driver which allows you to pretend that the flash is a normal device with sensible sector size. It actually works by caching a whole flash erase block in RAM, modifying it as requested, then erasing the whole block and writing back the modified data.
    This allows you to use normal filesystems on flash parts. Obviously it's not particularly robust when you are writing to it - you lose a whole erase block's worth of data if your read/modify/erase/rewrite cycle actually goes read/modify/erase/poweroff. But for development, and for setting up filesystems which are actually going to be mounted read-only in production units, it should be fine. 
    There is also a read-only version of this driver which doesn't have the capacity to do the caching and erase/writeback, mainly for use with uCLinux where the extra RAM requirement was considered too large.
  • Flash Translation Layer (FTL)
  • NFTL
    Block device drivers which implement an FTL/NFTL filesystem on the underlying memory device. FTL is fully functional. NFTL is currently working for both reading and writing, but could probably do with some more field testing before being used on production systems.
  • Journalling Flash File System, v2
    This provides a filesystem directly on the flash, rather than emulating a block device. For more information, see sources.redhat.com.
  1. 2.  mtd hardware device drivers

These provide physical access to memory devices, and are not used directly - they are accessed through the user modules above.

  • On-board memory
    Many PC chipsets are incapable of correctly caching system memory above 64M or 512M. A driver exists which allows you to use this memory with the linux-mtd system.
  • PCMCIA devices
    PCMCIA flash (not CompactFlash but real flash) cards are now supported by the pcmciamtd driver in CVS.
  • Common Flash Interface (CFI) onboard NOR flash
    This is a common solution and is well-tested and supported, most often using JFFS2 or cramfs file systems.
  • Onboard NAND flash
    NAND flash is rapidly overtaking NOR flash due to its larger size and lower cost; JFFS2 support for NAND flash is approaching production quality.
  • M-Systems' DiskOnChip 2000 and Millennium
    The DiskOnChip 2000, Millennium and Millennium Plus devices should be fully supported, using their native NFTL and INFTL 'translation layers'. Support for JFFS2 on DiskOnChip 2000 and Millennium is also operational although lacking proper support for bad block handling.
  • CompactFlash - http://www.compactflash.org/
    CompactFlash emulates an IDE disk, either through the PCMCIA-ATA standard, or by connecting directly to an IDE interface. 
    As such, it has no business being on this page, as to the best of my knowledge it doesn't have any alternative method of accessing the flash - you have to use the IDE emulation - I mention it here for completeness.

四. 目錄介紹

mtd所有源碼都在driver/mtd下,下截圖基於2.6.30內核,3.2內核稍有不同。

 

通過查閱Kconfig和Makefile可大致了解相關內容:

#

# Makefile for the memory technology device drivers.

#

# Core functionality.

obj-$(CONFIG_MTD)       += mtd.o

mtd-y               := mtdcore.o mtdsuper.o mtdbdi.o

mtd-$(CONFIG_MTD_PARTITIONS)    += mtdpart.o

obj-$(CONFIG_MTD_CONCAT)    += mtdconcat.o

obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o

obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o

obj-$(CONFIG_MTD_AFS_PARTS) += afs.o

obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o

obj-$(CONFIG_MTD_OF_PARTS)      += ofpart.o

# 'Users' - code which presents functionality to userspace.

obj-$(CONFIG_MTD_CHAR)      += mtdchar.o

obj-$(CONFIG_MTD_BLKDEVS)   += mtd_blkdevs.o

obj-$(CONFIG_MTD_BLOCK)     += mtdblock.o

obj-$(CONFIG_MTD_BLOCK_RO)  += mtdblock_ro.o

obj-$(CONFIG_FTL)       += ftl.o

obj-$(CONFIG_NFTL)      += nftl.o

obj-$(CONFIG_INFTL)     += inftl.o

obj-$(CONFIG_RFD_FTL)       += rfd_ftl.o

obj-$(CONFIG_SSFDC)     += ssfdc.o

obj-$(CONFIG_MTD_OOPS)      += mtdoops.o

nftl-objs       := nftlcore.o nftlmount.o

inftl-objs      := inftlcore.o inftlmount.o

obj-y       += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/

obj-$(CONFIG_MTD_UBI)       += ubi/

mtd包括核心文件mtdcore.c、mtdsuper.c、mtdbdi.c,分區文件mtdpart.c,設備層文件mtdchar.c、mtd_blkdevs.c、mtdblock.c。

mtd_blkdevs:common interface to block layer for MTD ‘translation layers’

mtd_block:Caching block device access to MTD devices, select MTD_BLKDEVS

目錄包括:

chips:Norflash相關支持,cfi/jedec接口通用驅動。

lpddr:低功耗ddr設備,若不是ddr采用mtd管理,不用理會。

maps:存放對各種使用NorFlash開發板的flash地址划分。這個目錄不包含NandFlash的信息。

devices:自包含mtd設備接口,設備自己向系統注冊mtd設備(add_mtd_device())。具體可參考其中一類設備如dataflash,代碼at91_dataflash.c。此類文件直接包含硬件驅動程序和原始設備接口,設備層(char/block)直接使用即可。

nand:Nandflash相關支持。

onenand:onenand flash相關支持

test:測試相關文件

ubi:ubifs文件系統支持

五. 數據結構

1. mtd_info

include/linux/mtd/mtd.h

mtd_info是表示MTD原始設備的結構體,每個分區也被認為是一個mtd_info,例如,如果有兩個MTD原始設備,而每個上有3個分區,在系統中就將共有6個mtd_info結構體,這些mtd_info的指針被存放在名為mtd_table的數組里。

mtd_info中的read()/write()/read_oob()/write_oob()是MTD設備驅動要實現的主要函數。

struct mtd_info {  

    u_char type;     // 內存技術的類型  

    uint32_t flags;  // 標志位  

    uint64_t size;   // Total size of the MTD 、mtd 設備的大小  

    /* "Major" erase size for the device. Na茂ve users may take this 

     * to be the only erase size available, or may use the more detailed 

     * information below if they desire 

     */  

    uint32_t erasesize;    // 主要的擦除塊大小 erase size of main block  

    /* 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. 

     */  

    uint32_t writesize;           // 最小的可寫單元的字節數  

    uint32_t oobsize;   // Amount of OOB data per block (e.g. 16) OOB 字節數  

    uint32_t oobavail;  // Available OOB bytes per block   可用OBB 字節數  

    /* 

     * If erasesize is a power of 2 then the shift is stored in 

     * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize. 

     */  

    unsigned int erasesize_shift;  

    unsigned int writesize_shift;  

    /* Masks based on erasesize_shift and writesize_shift */  

    unsigned int erasesize_mask;  

    unsigned int writesize_mask;  

    // Kernel-only stuff starts here.  

constchar *name;  

int index;  

    /* ecc layout structure pointer - read only ! */  

struct nand_ecclayout *ecclayout;  // ECC 布局結構體指針  

    /* 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;  

    /* 

     * Erase is an asynchronous operation.  Device drivers are supposed 

     * to call instr->callback() whenever the operation completes, even 

     * if it completes with a failure. 

     * Callers are supposed to pass a callback function and wait for it 

     * to be called before writing to the block. 

     */  

int (*erase) (struct mtd_info *mtd, struct erase_info *instr);  

    /* This stuff for eXecute-In-Place */  

    /* phys is optional and may be set to NULL */  

int (*point) (struct mtd_info *mtd, loff_t from, size_t len,            // 針對 eXecute-In- Place  

size_t *retlen, void **virt, resource_size_t *phys);  

    /* We probably shouldn't allow XIP if the unpoint isn't a NULL */  

void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);        // 如果unpoint 為空,不允許 XIP  

    /* Allow NOMMU mmap() to directly map the device (if not NULL) 

     * - return the address to which the offset maps 

     * - return -ENOSYS to indicate refusal to do the mapping 

     */  

    unsigned long (*get_unmapped_area) (struct mtd_info *mtd,  

                        unsigned long len,  

                        unsigned long offset,  

                        unsigned long flags);  

    /* Backing device capabilities for this device 

     * - provides mmap capabilities 

     */  

struct backing_dev_info *backing_dev_info;  

int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);        // 讀 flash  

int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);   // 寫 flash  

    /* In blackbox flight recorder like scenarios we want to make successful 

       writes in interrupt context. panic_write() is only intended to be 

       called when its known the kernel is about to panic and we need the 

       write to succeed. Since the kernel is not going to be running for much 

       longer, this function can break locks and delay to ensure the write 

       succeeds (but not sleep). */  

int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);   // Kernel panic 時序讀寫  

int (*read_oob) (struct mtd_info *mtd, loff_t from,           // 讀 out-of-band  

struct mtd_oob_ops *ops);  

int (*write_oob) (struct mtd_info *mtd, loff_t to,            // 寫 out-of-band  

struct mtd_oob_ops *ops);  

    /* 

     * 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 (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);  

int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);  

int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);  

int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);  

int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);  

int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, 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. 

*/  

int (*writev) (struct mtd_info *mtd, conststruct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); // iovec-based 讀寫函數  

/* Sync */    

void (*sync) (struct mtd_info *mtd);                             // Sync   

/* Chip-supported device locking */  

int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);    // 設備鎖  

int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);  

int (*is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_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;  

struct device dev;  

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

};  

flash驅動中使用如下兩個函數注冊和注銷MTD設備:

int add_mtd_device(struct mtd_info *mtd);

int del_mtd_device(struct mtd_info *mtd);

在Nor和Nand的驅動代碼中幾乎看不到mtd_info的成員函數(也即這些成員函數對於flash驅動是透明的),這是因為linux在MTD的下層實現了針對Nor和nand的通用mtd_info成員函數。

2. mtd_table

存儲設備目前擁有的mtd_info。

driver/mtd/mtdcore.c

  struct mtd_info *mtd_table[MAX_MTD_DEVICES];

3. mtd_part mtd分區結構體

drivers/mtd/mtdpart.c。

mtd_part用於描述分區,其mtd_info成員用於描述本分區,它會被加入到mtd_tabe中,其大部分成員由其主分區mtd_part->master決定,各種函數也指向主分區的相應函數,而主分區(其大小涵蓋所有分區)則不作為一個MTD原始設備加入mtd_table。

struct mtd_part {  

struct mtd_info mtd;      // 分區的信息(大部分由其 master 決定)  

struct mtd_info *master;  // 該分區的主分區  

    uint64_t offset;          // 分區的偏移地址  

struct list_head list;    // 分區號  

};  

/*

 * Given a pointer to the MTD object in the mtd_part structure, we can retrieve

 * the pointer to that structure with this macro.

 */

#define PART(x)  ((struct mtd_part *)(x))

4. mtd_partition

mtd_partition會在MTD原始設備層調用add_mtd_partitions()時傳遞分區信息用。

/*

 * Partition definition structure:

 *

 * An array of struct partition is passed along with a MTD object to

 * mtd_device_register() to create them.

 *

 * For each partition, these fields are available:

 * name: string that will be used to label the partition's MTD device.

 * size: the partition size; if defined as MTDPART_SIZ_FULL, the partition

 *  will extend to the end of the master MTD device.

 * offset: absolute starting position within the master MTD device; if

 *  defined as MTDPART_OFS_APPEND, the partition will start where the

 *  previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block;

 *  if MTDPART_OFS_RETAIN, consume as much as possible, leaving size

 *  after the end of partition.

 * mask_flags: contains flags that have to be masked (removed) from the

 *  master MTD flag set for the corresponding MTD partition.

 *  For example, to force a read-only partition, simply adding

 *  MTD_WRITEABLE to the mask_flags will do the trick.

 *

 * Note: writeable partitions require their size and offset be

 * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK).

 */

struct mtd_partition {

    char *name;         /* identifier string */

    uint64_t size;          /* partition size */

    uint64_t offset;        /* offset within the master MTD space */

    uint32_t mask_flags;        /* master MTD flags to mask out for this partition */

    struct nand_ecclayout *ecclayout;   /* out of band layout for this partition (NAND only) */

  };

flash驅動中使用如下兩個函數注冊和注銷分區:

int add_mtd_partitions(struct mtd_info *master, struct mtd_partition *parts, int nbparts);

int del_mtd_partitions(struct mtd_info *master);

5. mtd_notifier

struct mtd_notifier {

void (*add)(struct mtd_info *mtd);

void (*remove)(struct mtd_info *mtd);

struct list_head list;

};

 

MTD通知器,加入/刪除MTD設備和原始設備時調用的函數,在設備層,當MTD字符設備或塊設備注冊時,如果定義了CONFIG_DEVFS_FS,則會將一個mtd_notifier加入MTD原始設備層的mtd_notifiers鏈表。其中的函數會在兩種情況下被調用,一是加入/刪除新的MTD字符/塊設備時,此時調用該MTD字符/塊設備的notifier對下層所有的MTD原始設備操作一遍,二是加入/刪除新的MTD原始設備時,此時調用所有的notifier對該原始設備執行一遍。

六. 函數

1. mtd設備層-字符設備mtdchar.c

主要實現對字符設備的支持,完成struct file_operations mtd_fops注冊。

    static const struct file_operations mtd_fops = {

    .owner      = THIS_MODULE,

    .llseek     = mtd_lseek,

    .read       = mtd_read,

    .write      = mtd_write,

    .ioctl      = mtd_ioctl,

    .open       = mtd_open,

    .release    = mtd_close,

    .mmap       = mtd_mmap,

#ifndef CONFIG_MMU

    .get_unmapped_area = mtd_get_unmapped_area,

#endif

};

函數操作的對象為mtd_info,底層調用了mtd_info的讀寫擦除操作。

2. mtd設備層-塊設備mtd_blkdevs.c、mtdblock.c

一個提供對傳輸層支持,一個提供對緩沖(cache)支持。

static struct mtd_blktrans_ops mtdblock_tr = {

    .name       = "mtdblock",

    .major      = 31,

    .part_bits  = 0,

    .blksize    = 512,

    .open       = mtdblock_open,

    .flush      = mtdblock_flush,

    .release    = mtdblock_release,

    .readsect   = mtdblock_readsect,

    .writesect  = mtdblock_writesect,

    .add_mtd    = mtdblock_add_mtd,

    .remove_dev = mtdblock_remove_dev,

    .owner      = THIS_MODULE,

};

static struct block_device_operations mtd_blktrans_ops = {

    .owner      = THIS_MODULE,

    .open       = blktrans_open,

    .release    = blktrans_release,

    .locked_ioctl   = blktrans_ioctl,

    .getgeo     = blktrans_getgeo,

};

函數操作的對象為mtd_info,底層調用了mtd_info的讀寫擦除操作。

主要原理是將Flash的erase block 中的數據在內存中建立映射,然后對其進行修改,最后擦除Flash 上的block,將內存中的映射塊寫入Flash 塊。整個過程被稱為read/modify/erase/rewrite 周期。 但是,這樣做是不安全的,當下列操作序列發生時,read/modify/erase/poweroff,就會丟失這個block 塊的數據。 

塊設備模擬驅動按照block 號和偏移量來定位文件,因此在Flash 上除了文件數據,基本沒有額外的控制數據。

 3. mtd原始設備層-mtdcore.c

core registration and callback routines for MTD drivers and users.

EXPORT_SYMBOL_GPL(add_mtd_device);

EXPORT_SYMBOL_GPL(del_mtd_device);

EXPORT_SYMBOL_GPL(get_mtd_device);

EXPORT_SYMBOL_GPL(get_mtd_device_nm);

EXPORT_SYMBOL_GPL(put_mtd_device);

EXPORT_SYMBOL_GPL(register_mtd_user);

EXPORT_SYMBOL_GPL(unregister_mtd_user);

EXPORT_SYMBOL_GPL(default_mtd_writev);

extern int add_mtd_device(struct mtd_info *mtd);

extern int del_mtd_device (struct mtd_info *mtd);

extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);

extern struct mtd_info *get_mtd_device_nm(const char *name);

extern void put_mtd_device(struct mtd_info *mtd);

struct mtd_notifier {

    void (*add)(struct mtd_info *mtd);

    void (*remove)(struct mtd_info *mtd);

    struct list_head list;

};

extern void register_mtd_user (struct mtd_notifier *new);

extern int unregister_mtd_user (struct mtd_notifier *old);

int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,

               unsigned long count, loff_t to, size_t *retlen);

int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,

              unsigned long count, loff_t from, size_t *retlen);

 

struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
--獲取一個可用的mtd設備句柄。
@mtd: last known address of the required MTD device
@num: internal device number of the required MTD device
Given a number and NULL address, return the num’th entry in the device table, if any.
Given an address and num==-1, search the device table for a device with that address and return if it’s still present.
Given both, return the num’th driver only if its address matches.

 

4. mtd原始設備層-mtdsuper.c

對mtd superblock管理。

extern int get_sb_mtd(struct file_system_type *fs_type, int flags,

              const char *dev_name, void *data,

              int (*fill_super)(struct super_block *, void *, int),

              struct vfsmount *mnt);

extern void kill_mtd_super(struct super_block *sb);

5. mtd原始設備層-mtdbdi.c

mtd backing device capabilities。

/*

 * backing device capabilities for non-mappable devices (such as NAND flash)

 * - permits private mappings, copies are taken of the data

 */

struct backing_dev_info mtd_bdi_unmappable = {

    .capabilities   = BDI_CAP_MAP_COPY,

};

6. mtd原始設備層-mtdpart.c

對分區支持。

int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);

int del_mtd_partitions(struct mtd_info *);

/*

 * Functions dealing with the various ways of partitioning the space

 */

struct mtd_part_parser {

    struct list_head list;

    struct module *owner;

    const char *name;

    int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);

};

extern int register_mtd_parser(struct mtd_part_parser *parser);

extern int deregister_mtd_parser(struct mtd_part_parser *parser);

extern int parse_mtd_partitions(struct mtd_info *master, const char **types,

                struct mtd_partition **pparts, unsigned long origin);

用master分區初始化其余所有分區。

7. mtd原始設備層-mtdcmdline.c

Read flash partition table from command line

struct cmdline_mtd_partition {

    struct cmdline_mtd_partition *next;

    char *mtd_id;

    int num_parts;

    struct mtd_partition *parts;

};

static struct mtd_part_parser cmdline_parser = {

    .owner = THIS_MODULE,

    .parse_fn = parse_cmdline_partitions,

    .name = "cmdlinepart",

};

static int __init cmdline_parser_init(void)

{

    return register_mtd_parser(&cmdline_parser);

}

 8. 重要函數重復

flash驅動中使用如下兩個函數來注冊和注銷MTD設備:

int add_mtd_device(struct mtd_info *mtd);

int del_mtd_device(struct mtd_info *mtd);

flash驅動中使用如下兩個函數注冊和注銷分區:

int add_mtd_partitions(struct mtd_info *master, struct mtd_partition *parts, int nbparts);

int del_mtd_partitions(struct mtd_info *master);

七. NorFlash相關

NorFlash相關位於drivers/mtd/chips下。

探測flash可通過CFI接口(cfi_probe.c)或JEDEC接口(jedec_probe.c),兩種設備都要用到gen_probe.c文件。

不同的制造商使用不同的命令集,目前Linux的MTD實現的命令集有AMD/Fujitsu的標准命令集和Intel/Sharp的擴展命令集(兼容Intel/Sharp標准命令集)兩個,這兩個命令集分別在cfi_cmdset_0002.c和cfi_cmdset_0001.c中實現。此外還有一些非CFI標准的Flash,其中“jedec”類型的Flash的探測程序在jedec.c中,“sharp”類型的Flash的探測程序在sharp.c中,“amd_flash”類型的Flash的探測程序在amd_flash.c中。最后,還有一些非Flash的MTD,比如ROM或absent(無)設備。這些設備的探測程序在map_rom.c、map_ram.c和map_absent.c中。所有類型的芯片都通過chipreg.c中的do_map_probe()程序驅動。

NorFlash驅動的核心是定義map_info結構體,它指定了NorFlash的基址、位寬、大小等信息以及Flash的讀寫函數。可認為NorFlash驅動就是根據map_info探測芯片的過程。

/* The map stuff is very simple. You fill in your struct map_info with

   a handful of routines for accessing the device, making sure they handle

   paging etc. correctly if your device needs it. Then you pass it off

   to a chip probe routine -- either JEDEC or CFI probe or both -- via

   do_map_probe(). If a chip is recognised, the probe code will invoke the

   appropriate chip driver (if present) and return a struct mtd_info.

   At which point, you fill in the mtd->module with your own module

   address, and register it with the MTD core code. Or you could partition

   it and register the partitions instead, or keep it for your own private

   use; whatever.

   The mtd->priv field will point to the struct map_info, and any further

   private data required by the chip driver is linked from the

   mtd->priv->fldrv_priv field. This allows the map driver to get at

   the destructor function map->fldrv_destroy() when it's tired

   of living.

*/

struct map_info {

    const char *name;

    unsigned long size;

    resource_size_t phys;

#define NO_XIP (-1UL)

    void __iomem *virt;

    void *cached;

    int bankwidth; /* in octets. This isn't necessarily the width

               of actual bus cycles -- it's the repeat interval

              in bytes, before you are talking to the first chip again.

              */

#ifdef CONFIG_MTD_COMPLEX_MAPPINGS

    map_word (*read)(struct map_info *, unsigned long);

    void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);

    void (*write)(struct map_info *, const map_word, unsigned long);

    void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);

    /* We can perhaps put in 'point' and 'unpoint' methods, if we really

       want to enable XIP for non-linear mappings. Not yet though. */

#endif

    /* It's possible for the map driver to use cached memory in its

       copy_from implementation (and _only_ with copy_from).  However,

       when the chip driver knows some flash area has changed contents,

       it will signal it to the map driver through this routine to let

       the map driver invalidate the corresponding cache as needed.

       If there is no cache to care about this can be set to NULL. */

    void (*inval_cache)(struct map_info *, unsigned long, ssize_t);

    /* set_vpp() must handle being reentered -- enable, enable, disable

       must leave it enabled. */

    void (*set_vpp)(struct map_info *, int);

    unsigned long pfow_base;

    unsigned long map_priv_1;

    unsigned long map_priv_2;

    void *fldrv_priv;

    struct mtd_chip_driver *fldrv;

};

struct mtd_chip_driver {

    struct mtd_info *(*probe)(struct map_info *map);

    void (*destroy)(struct mtd_info *);

    struct module *module;

    char *name;

    struct list_head list;

};

void register_mtd_chip_driver(struct mtd_chip_driver *);

void unregister_mtd_chip_driver(struct mtd_chip_driver *);

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

void map_destroy(struct mtd_info *mtd);

 

Norflash芯片驅動的具體實現在mtd/maps下,如9261支持的flash驅動實現為mtd/maps/at91sam9261.c。

一個MTD原始設備可以由一塊或者數塊相同的Flash芯片組成。假設由4塊devicetype為x8的Flash,每塊大小為8M,interleave為2,起始地址為0x01000000,地址相連,則構成一個MTD原始設備(0x01000000-0x03000000),其中兩塊interleave成一個chip,其地址從0x01000000到0x02000000,另兩塊interleave成一個chip,其地址從0x02000000到0x03000000。

請注意,所有組成一個MTD原始設備的Flash芯片必須是同類型的(無論是interleave還是地址相連),在描述MTD原始設備的數據結構中也只是采用了同一個結構來描述組成它的Flash芯片。

八. NandFlash相關

NandFlash相關位於drivers/mtd/nand下,nandflash編程接口可參考

http://www.linux-mtd.infradead.org/tech/mtdnand/index.html

因為此目錄實現了通用的NAND驅動(mtd/nand/nand_base.c),因此芯片級的NAND驅動不在需要實現mtd_info結構體中的read()、write()、read_oob()、write_oob()等成員函數,而主體轉移到了nand_chip數據結構。

MTD使用nand_chip來表示一個NAND Flash芯片,該結構體包含了關於Nand Flash的地址信息、讀寫方法、ECC模式、硬件控制等一系列底層機制。

通過Kconfig和Makefile可簡單了解該目錄下內容,使用mtd nand必須包含文件nand_base.c、nand_bbt.c、nand_ecc.c、nand_ids.c;此外就是Nandflash控制芯片驅動,atmel的是atmel_nand.c,ti的omap2.c。

nand_bbt.c: Bad block table support for the NAND driver

nand_ecc.c: an ECC algorithm that detects and corrects 1 bit errors in a 256 byte block of data.

nand_ids.c: chip id list. Name, ID code, pagesize, chipsize in MegaByte, eraseblock size, options. Manufacturer ID list.

nand_base.c: the generic mtd driver for Nand flash devices.實現nand_chip到mtd_info的轉換。

atmel_nand.c: 實現芯片級nand_chip支持。

/**

 * struct nand_chip - NAND Private Flash Chip Data

 * @IO_ADDR_R:      [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device

 * @IO_ADDR_W:      [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device

 * @read_byte:      [REPLACEABLE] read one byte from the chip

 * @read_word:      [REPLACEABLE] read one word from the chip

 * @write_buf:      [REPLACEABLE] write data from the buffer to the chip

 * @read_buf:       [REPLACEABLE] read data from the chip into the buffer

 * @verify_buf:     [REPLACEABLE] verify buffer contents against the chip data

 * @select_chip:    [REPLACEABLE] select chip nr

 * @block_bad:      [REPLACEABLE] check, if the block is bad

 * @block_markbad:  [REPLACEABLE] mark the block bad

 * @cmd_ctrl:       [BOARDSPECIFIC] hardwarespecific funtion for controlling

 *          ALE/CLE/nCE. Also used to write command and address

 * @dev_ready:      [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line

 *          If set to NULL no access to ready/busy is available and the ready/busy information

 *          is read from the chip status register

 * @cmdfunc:        [REPLACEABLE] hardwarespecific function for writing commands to the chip

 * @waitfunc:       [REPLACEABLE] hardwarespecific function for wait on ready

 * @ecc:        [BOARDSPECIFIC] ecc control ctructure

 * @buffers:        buffer structure for read/write

 * @hwcontrol:      platform-specific hardware control structure

 * @ops:        oob operation operands

 * @erase_cmd:      [INTERN] erase command write function, selectable due to AND support

 * @scan_bbt:       [REPLACEABLE] function to scan bad block table

 * @chip_delay:     [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)

 * @state:      [INTERN] the current state of the NAND device

 * @oob_poi:        poison value buffer

 * @page_shift:     [INTERN] number of address bits in a page (column address bits)

* @phys_erase_shift:   [INTERN] number of address bits in a physical eraseblock

 * @bbt_erase_shift:    [INTERN] number of address bits in a bbt entry

 * @chip_shift:     [INTERN] number of address bits in one chip

 * @options:        [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about

 *          special functionality. See the defines for further explanation

 * @badblockpos:    [INTERN] position of the bad block marker in the oob area

 * @cellinfo:       [INTERN] MLC/multichip data from chip ident

 * @numchips:       [INTERN] number of physical chips

 * @chipsize:       [INTERN] the size of one chip for multichip arrays

 * @pagemask:       [INTERN] page number mask = number of (pages / chip) - 1

 * @pagebuf:        [INTERN] holds the pagenumber which is currently in data_buf

 * @subpagesize:    [INTERN] holds the subpagesize

 * @ecclayout:      [REPLACEABLE] the default ecc placement scheme

 * @bbt:        [INTERN] bad block table pointer

 * @bbt_td:     [REPLACEABLE] bad block table descriptor for flash lookup

 * @bbt_md:     [REPLACEABLE] bad block table mirror descriptor

 * @badblock_pattern:   [REPLACEABLE] bad block scan pattern used for initial bad block scan

 * @controller:     [REPLACEABLE] a pointer to a hardware controller structure

 *          which is shared among multiple independend devices

 * @priv:       [OPTIONAL] pointer to private chip date

 * @errstat:        [OPTIONAL] hardware specific function to perform additional error status checks

 *          (determine if errors are correctable)

 * @write_page:     [REPLACEABLE] High-level page write function

 */

struct nand_chip {

    void  __iomem   *IO_ADDR_R;

    void  __iomem   *IO_ADDR_W;

    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, const uint8_t *buf, int len);

    void        (*select_chip)(struct mtd_info *mtd, int chip);

    int     (*block_bad)(struct mtd_info *mtd, loff_t ofs, 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,

                      const uint8_t *buf, 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;

    uint64_t    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;

};

 

九. Flash測試

mtd flash測試程序分為內核層和應用層。

內核層:drivers/mtd/tests,包含mtd_oobtest, mtd_pagetest, mtd_readtest, mtd_speedtest, mtd_subpagetest,

              mtd_torturetest, mtd_nandecctest, mtd_nandbiterrs,通過模塊參數傳參完成測試。

應用層:http://www.linux-mtd.infradead.org/提供的mtd user-space tools。

十. 附錄

MTDMemory Technology Device,內存技術設備

JEDECJoint Electron Device Engineering Council,電子電器設備聯合會

CFICommon Flash Interface,通用Flash接口,Intel發起的一個Flash的接口標准

OOB out of band,某些內存技術支持out-of-band數據——例如,NAND flash每512字節的塊有16個字節的extra data,用於糾錯或元數據。

ECC error correction,某些硬件不僅允許對flash的訪問,也有ecc功能,所有flash器件都受位交換現象的困擾。在某些情況下,一個比特位會發生反轉或被報告反轉了,如果此位真的反轉了,就要采用ECC算法。

erasesize 一個erase命令可以擦除的最小塊的尺寸

buswidthMTD設備的接口總線寬度

interleave交錯數,幾塊芯片平行連接成一塊芯片,使buswidth變大

OTP:one-time programmable一次可編程,僅可編程一次。程序燒入IC后,將不可再次更改。

BBT:Bad block table

 

參考:

  1. http://www.linux-mtd.infradead.org/
  2. http://www.linux-mtd.infradead.org/archive/index.html
  3. linux設備驅動開發詳解  宋寶華
  4. http://blog.csdn.net/bugouyonggan/article/details/9167213
  5. http://blog.csdn.net/lizhiguo0532/article/details/6007636
  6. http://www.linux-mtd.infradead.org/tech/mtdnand/index.html


免責聲明!

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



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