Linux 4.x MTD源码分析-核心数据结构

mtd_info

mtd_info从物理性状和闪存操作(读,写,擦除等)的角度描述了闪存板块,其内容来自对具体闪存芯片的查询,结构中有些内容来自cfi_private。mtd_info有一个指针priv指向map_info结构,而map_info有一个指针fldrv_priv指向cfi_private。mtd_info结构还包含了在物理上实际操作闪存板块,实际上是板块上的芯片的函数集合。后面会结合代码详细讲到如何初始化该结构。

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数组的大小。

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提供的产商特定的关于芯片的信息。

/* 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空间放擦除区信息。

/* 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;
复制代码

转载于:https://juejin.im/post/5c4414bd6fb9a04a0164a22b

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值