Linux MTD子系统学习(一)

1 Linux MTD基本概述
1.1 专有名词描述
1. MTD:Memory Technology Device,内存技术设备。
2. JEDEC:Joint Electron Device Engineering Council,电子电器设备联合会。
3. CFI:Common Flash Interface,通用Flash接口,Intel发起的一个Flash的接口标准。
4. OOB: out of band,某些内存技术支持out-of-band数据——例如,NAND flash每512字节的块有16个字节的extra
data,用于纠错或元数据。
5. ECC: error correction,某些硬件不仅允许对flash的访问,也有ecc功能,所有flash器件都受位交换现象的困扰。在某些情况下,一个比特位会发生反转或被报告反转了,如果此位真的反转了,就要采用ECC算法。
6. erasesize: 一个erase命令可以擦除的最小块的尺寸。
7. buswidth:MTD设备的接口总线宽度。
8.interleave:交错数,几块芯片平行连接成一块芯片,使buswidth变大。
9. devicetype:芯片类型,x8、x16或者x32。
10.NAND:一种Flash技术,参看NAND和NOR的比较。
11.NOR:一种Flash技术,参看NAND和NOR的比较。
1.2 MTD介绍
MTD(memory technology device):内存技术设备,是linux用于描述ROM,NAND,NOR等设备的子系统的抽象,MTD设备可以按块读写也可以按字节读写,也就是说MTD设备既可以是块设备也可以是字符设备,块设备(mtdblackx)操作针对文件系统,字符设备(mtdx)操作主要针对格式化等操作的测试用。
由于块设备的I/O性能与CPU相比很差,因此,块设备的数据流往往会引入文件系统的cache机制
注意:MTD设备既非块设备也不是字符设备,但可以同时提供字符设备和块设备接口来操作。
在这里插入图片描述

如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。

Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。

MTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c:MTD原始设备接口相关实现,mtdpart.c:MTD分区接口相关实现。

MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c:MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。

设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备。

2 Linux MTD相关数据结构
2.1 mtd_info
Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:
struct mtd_info *mtd_table[MAX_MTD_DEVICES];

//源码:include/linux/mtd/mtd.h

struct mtd_info {
u_char type; //mtd类型,如:MTD_NORFLASH(See:mtd-abi.h)
uint32_t flags;// 标志位, MTD_WRITEABLE、MTD_NO_ERASE等(See mtd-abi.h)
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;//擦除大小,即flash的块大小(mtd设备可能有多个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;// 写大小, 对于norFlash是字节,对nandFlash为一页

/*
 * 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;	//默认为0,不重要
unsigned int writesize_shift;	//默认为0,不重要
/* Masks based on erasesize_shift and writesize_shift */
unsigned int erasesize_mask;	//默认为1,不重要
unsigned int writesize_mask;	//默认为1,不重要

/*
 * 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;	//mtd设备名
int index;



/* OOB layout description */
//nand_ecclayout结构体指针, 表示的是ecc布局,可参考硬件手册的OOB中ecc布局
const struct mtd_ooblayout_ops *ooblayout;

/* NAND pairing scheme, only provided for MLC/TLC NANDs */
const struct mtd_pairing_scheme *pairing;

/* 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;//通常为1
struct mtd_erase_region_info *eraseregions;//可变擦除区域

/*
 * Do not call via these pointers, use corresponding mtd_*()
 * wrappers instead.
 */
/* flash擦除函数 */
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);
/* flash读写函数 */
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);

/* 带oob读写flash函数 */
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 (*_max_bad_blocks) (struct mtd_info *mtd, loff_t ofs, size_t len);

/* 电源管理函数 */
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);

struct notifier_block reboot_notifier;  /* default mode before reboot */

/* ECC status information */
struct mtd_ecc_stats ecc_stats;//ECC状态
/* Subpage shift (NAND) */
int subpage_sft;

//私有数据, cfi接口flash指向map_info结构, 或指向自定义flash相关结构体
void *priv; 
struct module *owner;
struct device dev;
int usecount;
struct mtd_debug_info dbg;

};

2.2 mtd_part
Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。

//源码:drivers/mtd/mtdpart.c

struct mtd_part {
struct mtd_info mtd; //分区信息,大部分来自parent
struct mtd_info *parent;//分区的主分区
uint64_t offset; //分区的偏移地址
struct list_head list; //链接到mtd_partitions链表中
};

2.3 mtd_partition
该结构体表示某一分区的分区信息,其将被添加到mtd_partitions链表中。
//源码:include/linux/mtd/patitions.h

struct mtd_partition {
const char name; / identifier string */
const char *const types; / names of parsers to use if any /
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 device_node *of_node;
};
2.4 mao_info
//源码:include/linux/mtd/map.h

struct map_info {
const char *name; //名称
unsigned long size; //大小
resource_size_t phys; //物理地址
#define NO_XIP (-1UL)
void __iomem *virt; //虚拟地址,通常通过ioremap将物理地址映射得到的
void *cached;

int swap; /* this mapping's byte-swapping requirement */
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);

/* This will be called with 1 as parameter when the first map user
 * needs VPP, and called with 0 when the last user exits. The map
 * core maintains a reference counter, and assumes that VPP is a
 * global resource applying to all mapped flash chips on the system.
 */
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;

};

2.5 nand_chip
源码:include/linux/mtd/nand.h

struct nand_chip {
void __iomem *IO_ADDR_R; //读写8根io线的地址
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 (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
		u8 *id_data);
//检查设备是否就绪
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;
unsigned int bbt_options;

int page_shift;	//表示page的大小,如Nand的页大小为512byte,则page_shift为9
int phys_erase_shift;	//擦除块的大小

/* 用位表示的bad block table的大小,通常一个bbt占用一个block,
所 以bbt_erase_shift通常与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;
int badblockbits;

int onfi_version;
struct nand_onfi_params	onfi_params;

flstate_t state;

uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;	//ECC布局

struct nand_ecc_ctrl ecc;			//ECC校验结构体,含有大量ECC校验的函数
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;

uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;

struct nand_bbt_descr *badblock_pattern;

void *priv;

};

2.6 spi_nor
用于表示一个spi-nor设备的相关信息。

struct spi_nor {
struct mtd_info mtd; //nor mtd信息
struct mutex lock;
struct device *dev;
u32 page_size; //flash的页大小
u8 addr_width; //地址宽度
u8 erase_opcode; //flash擦除指令
u8 read_opcode; //flash 读数据指令
u8 read_dummy; //读操作时的dummy数据
u8 program_opcode; //页操作指令
enum spi_nor_protocol read_proto;//多线读写时的相关操作
enum spi_nor_protocol write_proto;
enum spi_nor_protocol reg_proto;
bool sst_write_second;

//标志位,如:SNOR_F_USE_FSR(See:spi-nor.h,spi_nor_option_flags)
u32			flags;
u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];

//为读、写、擦除、lock、unlock的相关准备工作
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);

//读nor内部寄存器
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
//写nor内部寄存器
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);

//读nor内部数据
ssize_t (*read)(struct spi_nor *nor, loff_t from,
		size_t len, u_char *read_buf);
//往nor写入数据
ssize_t (*write)(struct spi_nor *nor, loff_t to,
		size_t len, const u_char *write_buf);
//擦除flash数据
int (*erase)(struct spi_nor *nor, loff_t offs);

//锁住flash的部分区域
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);

void *priv;//私有数据

};

注:以上源码基于Linux-4.14.14分析。
————————————————
版权声明:本文为CSDN博主「楓潇潇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013836909/article/details/93298658

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值