Linux 4.x MTD源碼分析-核心數據結構

mtd_info

mtd_info從物理性狀和閃存操做(讀,寫,擦除等)的角度描述了閃存板塊,其內容來自對具體閃存芯片的查詢,結構中有些內容來自cfi_private。mtd_info有一個指針priv指向map_info結構,而map_info有一個指針fldrv_priv指向cfi_private。mtd_info結構還包含了在物理上實際操做閃存板塊,其實是板塊上的芯片的函數集合。後面會結合代碼詳細講到如何初始化該結構。node

struct mtd_info {
	u_char type;
	// 存儲器類型,MTD_RAM/_ROM/_NORFLASH/_NANDFLASH/。。。
	uint32_t flags;
	uint64_t size;	 // Total size of the 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;
	/* 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;

	/* * Size of the write buffer used by the MTD. MTD devices having a write * buffer can write multiple writesize chunks at a time. E.g. while * writing 4 * writesize bytes to a device with 2 * writesize bytes * buffer the MTD driver can (but doesn't have to) do 2 writesize * operations, but not 4. Currently, all NANDs have writebufsize * equivalent to writesize (NAND page size). Some NOR flashes do have * writebufsize greater than writesize. */
	uint32_t writebufsize;

	uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
	uint32_t oobavail;  // Available OOB bytes per block

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

	/* * read ops return -EUCLEAN if max number of bitflips corrected on any * one region comprising an ecc step equals or exceeds this value. * Settable by driver, else defaults to ecc_strength. User can override * in sysfs. N.B. The meaning of the -EUCLEAN return code has changed; * see Documentation/ABI/testing/sysfs-class-mtd for more detail. */
	unsigned int bitflip_threshold;

	// Kernel-only stuff starts here.
	const char *name;
	int index;

	/* ECC layout structure pointer - read only! */
	struct nand_ecclayout *ecclayout;

	/* the ecc step size. */
	unsigned int ecc_step_size;

	/* max number of correctible bit errors per ecc step */
	unsigned int ecc_strength;

	/* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. */
	int numeraseregions;
	struct mtd_erase_region_info *eraseregions;

	/* * Do not call via these pointers, use corresponding mtd_*() * wrappers instead. */
	int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
	int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
		       size_t *retlen, void **virt, resource_size_t *phys);
	int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
	unsigned long (*_get_unmapped_area) (struct mtd_info *mtd, unsigned long len, unsigned long offset, unsigned long flags);
	int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
		      size_t *retlen, u_char *buf);
	int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
		       size_t *retlen, const u_char *buf);
	int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
			     size_t *retlen, const u_char *buf);
	int (*_read_oob) (struct mtd_info *mtd, loff_t from,
			  struct mtd_oob_ops *ops);
	int (*_write_oob) (struct mtd_info *mtd, loff_t to,
			   struct mtd_oob_ops *ops);
	int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len,
				    size_t *retlen, struct otp_info *buf);
	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, size_t len,
				    size_t *retlen, struct otp_info *buf);
	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 to,
				     size_t len, size_t *retlen, u_char *buf);
	int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
				    size_t len);
	int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
			unsigned long count, loff_t to, size_t *retlen);
	void (*_sync) (struct mtd_info *mtd);
	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);
	int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);
	int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
	int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
	int (*_suspend) (struct mtd_info *mtd);
	void (*_resume) (struct mtd_info *mtd);
	void (*_reboot) (struct mtd_info *mtd);
	/* * If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. */
	int (*_get_device) (struct mtd_info *mtd);
	void (*_put_device) (struct mtd_info *mtd);

	/* Backing device capabilities for this device * - provides mmap capabilities */
	struct backing_dev_info *backing_dev_info;

	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;
};
複製代碼

map_info

map_info從內存訪問的角度描述了一種閃存芯片。它保存着閃存的物理開始地址,存儲空間大小,總線帶寬等信息。map_info最主要的任務是把閃存的物理地址空間映射到內核的線性地址空間,這樣訪問閃存空間的上層user modules就不用關心具體硬件的差別了。算法

struct map_info {
	const char *name;
	//閃存的大小
	unsigned long size;
	//閃存的物理地址
	resource_size_t phys;
#define NO_XIP (-1UL)

    //閃存的物理地址被映射到內核的線性地址,內核其餘模塊的代碼要訪問閃存都是使用虛擬地址
	void __iomem *virt;
	void *cached;

	int swap; /* this mapping's byte-swapping requirement */
	//總線位寬,1=8bit, 2=16bit...
	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. */
//定義CONFIG_MTD_COMPLEX_MAPPINGS表示使用非線性的地址映射,若是有定義該宏chip driver須要實現
//下面4個api,實現從虛擬地址到物理地址的轉化,同時讀寫對應物理地址中的內容
//內核通常調用map_read/map_write等宏訪問閃存,當定義了CONFIG_MTD_COMPLEX_MAPPINGS,
//該宏會調用這4個API
//使用1對1的映射比較常見,因此通常不須要定義該宏
#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;
	struct device_node *device_node;
	void *fldrv_priv;
	struct mtd_chip_driver *fldrv;
};
複製代碼

cfi_private

cfi_private是對閃存板塊的綜合描述。該結構是不定長度的,取決於板塊實際上有多少塊芯片,numchips域指示了芯片的數量,根據該值便可知道chips數組的大小。api

struct cfi_private {
	uint16_t cmdset;
	void *cmdset_priv;
	int interleave;
	int device_type;
	int cfi_mode;		/* Are we a JEDEC device pretending to be CFI? */
	int addr_unlock1;
	int addr_unlock2;
	struct mtd_info *(*cmdset_setup)(struct map_info *);
	struct cfi_ident *cfiq; /* For now only one. We insist that all devs must be of the same type. */
	// 在AutoSelect mode下,從flash讀出來的 Device ID信息
	int mfr, id;
	int numchips;
	map_word sector_erase_cmd;
	unsigned long chipshift; /* Because they're of the same type */
	const char *im_name;	 /* inter_module name for cmdset_setup */
	struct flchip chips[0];  /* per-chip data structure for each chip */
};

複製代碼

flchip

用於對大CHIP(若是2片並列或者4片並列)的操做。數組

struct flchip {
    // 記錄該芯片在內存中的偏移
	unsigned long start; /* Offset within the map */
	// unsigned long len;
	/* We omit len for now, because when we group them together we insist that they're all of the same size, and the chip size is held in the next level up. If we get more versatile later, it'll make it a damn sight harder to find which chip we want from a given offset, and we'll want to add the per-chip length field back in. */
	int ref_point_counter;
	// 記錄芯片當前的狀態,probe時初始化爲FL_READY
	flstate_t state;
	flstate_t oldstate;

	unsigned int write_suspended:1;
	unsigned int erase_suspended:1;
	unsigned long in_progress_block_addr;

	struct mutex mutex;
	wait_queue_head_t wq; /* Wait on here when we're waiting for the chip to be ready */
	int word_write_time;
	int buffer_write_time;
	int erase_time;

	int word_write_time_max;
	int buffer_write_time_max;
	int erase_time_max;

	void *priv;
};
複製代碼

cfi_pri_intelext

Intel/Sharp擴展命令集0x0001提供的產商特定的關於芯片的信息。app

/* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */

struct cfi_pri_intelext {
    // "PRI"
	uint8_t  pri[3];
	// Major/Minor version number, ASCII
	uint8_t  MajorVersion;
	uint8_t  MinorVersion;
	
	//P+5 chip支持的功能的bitmask, bit X = 1/0 某功能支持/不支持,具體功能看intel spec
	//bit 0 Chip erase supported bit 0 = 0 No
    //bit 1 Suspend erase supported bit 1 = 
    //bit 2 Suspend program supported bit 2 = 
    //bit 3 Legacy lock/unlock supported bit 3 = 
    //bit 4 Queued erase supported bit 4 = 
    //bit 5 Instant Individual block locking supported bit 5 = 
    //bit 6 Protection bits supported bit 6 = 
    //bit 7 Page-mode read supported bit 7 = 
    //bit 8 Synchronous read supported bit 8 = 
	uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature block follows - FIXME - not currently supported */
	//P+9 suspended erase/program 狀態下,chip支持的功能的bitmask
	//read Array, Status,Query老是支持的,其它支持特性包括:
	//bit 0 Program supported after erase suspend bit 0 = 1 Yes
    //bits 1–7 reserved; undefined bits are 「0」
	uint8_t  SuspendCmdSupport;
	
	// P+A Block status register mask
    //bit 0 Block Lock-Bit Status register active bit 0 = 1 Yes
    //bit 1 Block Lock-Down Bit Status active
    //bits 2–15 are Reserved; undefined bits are 「0」 3C: --00
	uint16_t BlkStatusRegMask;
	
	// P+C 地址
	uint8_t  VccOptimal;
	// P+D 地址
	uint8_t  VppOptimal;
	// P+E 在JEDEC ID地址空間中的Protection register fields的個數
	uint8_t  NumProtectionFields;
	// 一個保護寄存器域對應4B的配置
	uint16_t ProtRegAddr;
	uint8_t  FactProtRegSize;
	uint8_t  UserProtRegSize;
	
	uint8_t  extra[0];
} __packed;
複製代碼

cfi_ident

在nor進入QRY模式時,從 Query Struct中查詢出的關於芯片的基本查詢信息。具體看CFI規範紀要 查詢信息在nor中以小端方式存儲,讀出來後要轉爲cpu字節序。 在QRY mode下一次數據只有低8bit有效,因此一次只能讀1B. 這個結構體是個變長的,在讀出nor具備的擦除區數量後,會在這個結構體尾部給每一個擦除區分配4B空間放擦除區信息。ide

/* Basic Query Structure */
struct cfi_ident {
	uint8_t  qry[3];
	// chip首選算法命令集
	uint16_t P_ID;
	// chip首選算法相關的查詢表,這是可選的表,存放好比主/次版本等各類產商定義的特性
	uint16_t P_ADR;
	
	// 和上面兩個字段相似,差異是這是備選的算法
	uint16_t A_ID;
	uint16_t A_ADR;
	uint8_t  VccMin;
	uint8_t  VccMax;
	uint8_t  VppMin;
	uint8_t  VppMax;
	// 寫一個word的timeout, 若是WordWriteTimeoutTyp=n, 則timeout爲2^n us
	// =0表不支持,會hardcode一個默認值
	uint8_t  WordWriteTimeoutTyp;
	// 多字節寫的timeout,不支持時,設置爲0,表不使用
	uint8_t  BufWriteTimeoutTyp;
	uint8_t  BlockEraseTimeoutTyp;
	uint8_t  ChipEraseTimeoutTyp;
	uint8_t  WordWriteTimeoutMax;
	uint8_t  BufWriteTimeoutMax;
	uint8_t  BlockEraseTimeoutMax;
	uint8_t  ChipEraseTimeoutMax;
	//表示該chip的大小,若是DevSize=n, 則chip容量爲2^n 字節
	uint8_t  DevSize;
	uint16_t InterfaceDesc;
	// 0x2A地址,多字節寫時的buffer大小,若是該值爲n, 則buffer大小爲2^n字節
	uint16_t MaxBufWriteSize;
	//norflash有多少個擦除區(erase region)
	//根據手冊,擦除區是指具備一樣大小的連續的擦除塊( Erase Block),
	//注意必定要是連續的擦除塊,不連續的就算兩個區了
	// num_erase_regions=0表沒有擦除區,或者只能整個device都擦除
	uint8_t  NumEraseRegions;
	//bits 15-0=y 表示該擦除區所包含的擦除塊的塊數=y+1
	//bits 31-16=z 表示擦除塊的大小=256*Z
	uint32_t EraseRegionInfo[0]; /* Not host ordered */
} __packed;
複製代碼
相關文章
相關標籤/搜索