TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。% L3 \! t$ }: G1 ?; O
8 @- J( l3 _' O1 S8 \9 ?8 ^
; l' J# D4 J0 ?* {: u+ y1 n7 f
+ K, l6 N# e# Z) B8 s( l0 l
如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。' H) r8 f7 K1 O. N
5 N! D+ A$ k6 \
Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。
. H! G6 H7 K |/ P# [
* ]3 G2 u o- R8 UMTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c: MTD原始设备接口相关实现,mtdpart.c : MTD分区接口相关实现。
8 @8 A5 y8 F/ E- D
$ Y; e9 s" K; w; H9 S+ @5 A4 aMTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c : MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。; J7 J5 m# l" J% m& q. }9 t+ d
9 ]9 Y6 h8 \( b0 D. H6 P
设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备 X8 k3 a' K" T& U8 p0 Z
8 G" D; t- Z: b: Y- \) MMTD数据结构:
3 m2 J- N" c& \ D6 l ?3 u/ P; F7 k" B8 v
1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:
_& V1 I. S. X3 j/ c% c1 h/ h6 y |) `( |$ B
H1 p2 \1 l8 y' O' K
struct mtd_info *mtd_table[MAX_MTD_DEVICES];' g& F& Y* Y- L# T* Q0 b
2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。
, G- p$ p$ C7 ~2 H8 T/ ^
, v+ W& H2 C# Z: A* ^struct mtd_part {$ ^9 T8 r* f9 f5 C! w) ~
struct mtd_info mtd; /* 分区信息, 大部分由master决定 */, `9 h" R3 e, u2 v0 N, t: `
struct mtd_info *master; /* 分区的主分区 */
5 H' M& h+ T0 A9 ^ uint64_t offset; /* 分区的偏移地址 */+ U* D1 L( s: h0 L7 E
int index; /* 分区号 (Linux3.0后不存在该字段) */
* P) {9 n2 F; L, L struct list_head list; /* 将mtd_part链成一个链表mtd_partitons */
/ Z2 k) F% W; f7 U1 T int registered;
3 C& @9 R7 _$ d+ V' `; t0 j7 e};' A$ D4 ?, x1 R8 G z- @* r
mtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。
* [& @: M! D o$ W- y" v u
2 ^: p/ |! O" R5 Qstruct mtd_info {4 t$ k% l# i% t, {& C- ^
u_char type; /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */" `" a/ E2 f6 {; s$ c) Y5 Z5 i# {9 }
uint32_t flags; /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */6 z* }6 r% x4 ^' Q+ L( o0 l/ T
uint64_t size; /* mtd设备的大小 */; w6 T0 ]5 V8 @/ {: B$ Q. B
uint32_t erasesize; /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */2 |: m, @8 J2 @+ n3 B
uint32_t writesize; /* 写大小, 对于norFlash是字节,对nandFlash为一页 */. t' V9 t+ Z. h5 b: G' R& O8 C. g
uint32_t oobsize; /* OOB字节数 */
% n6 u6 e7 g* F uint32_t oobavail; /* 可用的OOB字节数 */' h2 ~, m) W# q/ R/ Z* K
unsigned int erasesize_shift; /* 默认为0,不重要 */% f( u- s: H$ T& [# }( N' T0 m
unsigned int writesize_shift; /* 默认为0,不重要 */
+ ~! D5 e0 C7 g& H% y% t unsigned int erasesize_mask; /* 默认为1,不重要 */
, Y3 D4 A1 O9 j5 C% t unsigned int writesize_mask; /* 默认为1,不重要 */
& x: g! d5 O9 @4 { const char *name; /* 名字, 不重要*/
+ Y* ^& [' }8 Z5 x/ k. Q& f4 l int index; /* 索引号,不重要 */" @" Z0 B! C* a* s) c% s
int numeraseregions; /* 通常为1 */
4 B9 h5 m9 f9 t }/ r' ], c; ` struct mtd_erase_region_info *eraseregions; /* 可变擦除区域 */
# w. G# s; v& o4 W
" Z* ?4 |7 C9 J void *priv; /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
6 Q+ Y) q+ A# K% d! W struct module *owner; /* 一般设置为THIS_MODULE */
) B4 I, H8 D. k1 [ - [' U% o# ~/ J1 i4 A
/* 擦除函数 */
% U) z. O: F& P, V3 v) s& G( ~7 K* \ int (*erase) (struct mtd_info *mtd, struct erase_info *instr);5 L7 w6 b0 q+ J# [, e. J9 o
* f+ A8 N7 L5 O; M: u /* 读写flash函数 */ g, q Q; H" ~( i! h
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
! X: c8 |4 t' p5 O2 z7 H1 Y int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);! _9 y- z$ o8 u" c
% F/ M! l7 d; _ /* 带oob读写Flash函数 */
" C" T- R. ~2 Y8 L" U int (*read_oob) (struct mtd_info *mtd, loff_t from,( u6 {0 X: H+ S0 v
struct mtd_oob_ops *ops);2 f& ^, o. o1 [7 K+ f, Z# V
int (*write_oob) (struct mtd_info *mtd, loff_t to,
( m: S- x8 g& z struct mtd_oob_ops *ops);
- i) y l& R% F( n4 A" G+ B9 f" s& {0 T8 |* q
int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);. g+ l$ I$ @0 S: w( L# N
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
% r; y- @6 H( R/ p int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);' r, E& }2 x9 m I) o2 Q9 W( Q, s
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);$ W& B: ^$ r7 k' ^/ ]+ F! p8 K* Z
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);/ @; B1 w, J2 a% P+ J r7 [
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);, {, K: ?7 i" C
& \* c r2 r- z7 K1 \& u2 a! X int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
$ U, R1 S, M: S; L6 V6 Q! c6 s int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);! x4 o9 Z3 R p9 T: B# E) A1 L
/* Sync */
) Q( r0 Q# c- C/ K9 Y0 g4 e void (*sync) (struct mtd_info *mtd); a) C- K- ^5 j0 U( ] k0 ~
- @; P5 w. b u6 c' z /* Chip-supported device locking */
; T" W2 X0 q$ ]: b3 O! t2 a int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);/ ?$ G1 y0 O; G$ b, `
int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
8 g) k- w* s4 N j6 _" d E( Y M' v
/* 电源管理函数 */
& \+ ^& U6 H7 k( E int (*suspend) (struct mtd_info *mtd);
+ v7 V6 {' b, B# G2 g! ?" B void (*resume) (struct mtd_info *mtd);
/ ? H% F' q0 o9 @- g" F) ]% S% m* n, k2 K8 C6 ~
/* 坏块管理函数 */
1 F' ~7 N, w% F* ^, j5 G int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
" n7 U4 [& M( u int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
: r2 F7 R$ M5 j/ J7 N" [4 @
- p9 x7 O+ @5 z9 w( Y7 @0 A3 V& F void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);+ {9 |. `( b* \, M0 I
unsigned long (*get_unmapped_area) (struct mtd_info *mtd," L. A! {1 H9 ]0 m
unsigned long len,+ V. h& |$ X3 i& i7 {4 C
unsigned long offset,
* ] ?; d# t4 x* ]) G unsigned long flags);
" y4 Z* n7 A2 e2 @+ T# Q) D/ t struct backing_dev_info *backing_dev_info;0 f% r: ^, i+ @! K% l
struct notifier_block reboot_notifier; /* default mode before reboot */2 x# F# |& n2 c; `. Q# H
: Z+ ^1 X5 F) a3 ]) i
/* ECC status information */
) L/ j- u; x, ~; \1 y struct mtd_ecc_stats ecc_stats;
) R, U. K" G3 A int subpage_sft;
: L6 c/ n7 V3 e) _6 h; D# y struct device dev;$ c9 b L: k# @( S$ f
int usecount;
6 Y, p1 K0 P7 d3 p p, y; v' ? int (*get_device) (struct mtd_info *mtd);% K0 t h$ a% c' Y3 V; k
void (*put_device) (struct mtd_info *mtd);/ B# T5 ]! S t) u; _' a) X
};9 m* d5 O D$ O+ L5 b" ]2 T
mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。
) @, ]; \& C- {/ @0 A% P9 c' N2 ]如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。
0 L; D( G4 ^# d) ~& K% c$ a7 V5 ] \7 Q$ w
$ T! f) B: T) J- P6 F) f0 a" ~# G/ m
int add_mtd_device(struct mtd_info *mtd)" |+ r- L s! b* M$ z# o$ r
int del_mtd_device (struct mtd_info *mtd)
* r+ a6 X' @6 n1 E0 e) H如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。 C" i' P& i3 H* `6 F( _$ R2 f# l
int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)" K' Y" O3 g7 g* X. U
int del_mtd_partitions(struct mtd_info *master); g* p4 j& {8 T! t( ^$ B
其中mtd_partition结构体表示分区的信息
, w1 ?4 t: i6 p, g3 X; r4 vstruct mtd_partition {: |$ b% T( \8 `. L; B. G: P
char *name; /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
# q) c. i! T: }3 }/ U! h uint64_t size; /* 分区大小 */
, R9 I7 N. G! G- T& P3 f! d uint64_t offset; /* 分区偏移值 */$ b% r1 ]: J- F2 ]7 X
uint32_t mask_flags; /* 掩码标识,不重要 */3 M, t' z( K5 y9 l
struct nand_ecclayout *ecclayout; /* OOB布局 */
) d% b8 h5 @ J& U struct mtd_info **mtdp; /* pointer to store the MTD object */7 Z( z; i4 N! e& M! H1 g
};3 C: p1 P$ r/ q! k: ?
其中nand_ecclayout结构体:1 t9 k. ?$ J8 \ d2 ]) |- f
struct nand_ecclayout {
3 Y1 e" Q6 n( Z3 Z __u32 eccbytes; /* ECC字节数 */
9 W, p( `! w8 p: _4 ] __u32 eccpos[64]; /* ECC校验码在OOB区域存放位置 */
. M; v2 P) x, H2 i __u32 oobavail; ) K. W9 |' i7 Q V% P
/* 除了ECC校验码之外可用的OOB字节数 */- s( y9 A) I( K* q
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];( d8 y2 ?: k9 S8 g. `5 m' i7 o% k2 i
};
% A4 j; P4 r9 m, B% d/ R关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。5 p" L4 _! I- U8 D) G
MTD设备层:1 \) |/ P# R* Y& o8 C/ @' z
mtd字符设备接口:& E2 ]5 Y( J( c9 r' @# M; _7 I4 j: {
! g' V4 q/ d/ f; x6 m
/drivers/mtd/mtdchar.c文件实现了MTD字符设备接口,通过它,可以直接访问Flash设备,与前面的字符驱动一样,通过file_operations结构体里面的open()、read()、write()、ioctl()可以读写Flash,通过一系列IOCTL 命令可以获取Flash 设备信息、擦除Flash、读写NAND 的OOB、获取OOB layout 及检查NAND 坏块等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL) . O; F3 \' I4 j& p5 t+ H1 q
3 _8 f, n$ I d1 Nmtd块设备接口:/ y: ?# t s- z% S" b5 S3 a }
6 V5 G: O/ ^+ H, S- g8 `4 l/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。
4 Z0 `) ]" Y( VMTD硬件驱动层:
$ `! l; C2 x; n9 x; \& a3 r# ^2 V5 J J: S' b1 j
Linux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。
9 F" u5 q: ~ s' V p% k- q5 z, l) ]! C5 ]
MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。
9 ^8 ~2 C6 f8 l% h: k& @7 \! I/ \. T6 ~9 ~6 {( R, Y+ i3 L. H$ X
& i# S& h) o" M8 ]6 L
struct nand_chip {
% t' ^7 J# ? \3 ~4 B. t0 n- u void __iomem *IO_ADDR_R; /* 读8位I/O线地址 */
/ t; F% `. ?2 ?% H void __iomem *IO_ADDR_W; /* 写8位I/O线地址 */
$ l+ K3 F7 ]6 d" k! z, k) O- J+ U* Q
/* 从芯片中读一个字节 *// g! `; ]& j8 l7 I
uint8_t (*read_byte)(struct mtd_info *mtd); ( a! G" j" }+ p
/* 从芯片中读一个字 */) w5 C5 O7 M- b. m. q
u16 (*read_word)(struct mtd_info *mtd);
0 G1 `: q& ~6 i$ a# o /* 将缓冲区内容写入芯片 */
& [" Q$ M# q9 u1 R void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
5 y' I8 y; g) ~" l7 { /* 读芯片读取内容至缓冲区/ */
2 h! i" A5 R' w0 f7 i4 s7 E& ^ void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
/ x0 P O* c" F9 R3 c /* 验证芯片和写入缓冲区中的数据 */& D' f# F+ p- i6 k
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
9 Q: o7 O/ U0 w7 D+ A' g6 M; O' C /* 选中芯片 *// C0 Y9 N# P, C; L5 M9 |
void (*select_chip)(struct mtd_info *mtd, int chip);
, d1 ~+ o: R4 T! g- g7 b5 C0 B /* 检测是否有坏块 */. R8 c" @5 V' r/ L4 Y. O6 A- X
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);5 ]" o0 ~& H6 x# {8 ^
/* 标记坏块 */
7 a; f. @% x9 @% _$ Y& p/ c* B int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);. B6 K2 D) C" ?
/* 命令、地址、数据控制函数 */5 [3 V5 P# K5 m. z8 m
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);; S- s% N* ]! y1 y H
/* 设备是否就绪 */) G& _. |) n- B# X$ b/ {
int (*dev_ready)(struct mtd_info *mtd);0 j' Y% p# V+ U" ^3 p8 B4 w
/* 实现命令发送 */8 I2 v$ ~4 |2 X
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);5 c! _+ c. ?/ c. E! Q0 t; `
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
- X, f8 n) I, t% R0 @ K /* 擦除命令的处理 */" b- f' q, X! }. B
void (*erase_cmd)(struct mtd_info *mtd, int page);# ` i' ]8 l. d1 T+ F
/* 扫描坏块 */. d+ Q3 x4 ?3 ]* { U+ l
int (*scan_bbt)(struct mtd_info *mtd);0 i: {+ i! }' _' {0 q
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);# L6 v5 k- \0 s
/* 写一页 */) P& W! ]' H! t: S
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,/ L4 R9 N7 e. p9 E5 P) \8 y3 f' l
const uint8_t *buf, int page, int cached, int raw);
' A2 w! P3 O, x+ m
1 }% V( g. G8 c/ ~- N/ z int chip_delay; /* 由板决定的延迟时间 */- {" }) l1 c3 f; h1 A* t; h6 h
/* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */& X! `( l8 m; Z- B1 ]. ^# }0 V2 O
unsigned int options; 6 D9 o9 t) q V/ E# v! g
' e* ^2 `) v' y- @- c/ J+ ~ /* 用位表示的NAND芯片的page大小,如某片NAND芯片2 w# M& d2 n2 ?2 y; e# C) Y
* 的一个page有512个字节,那么page_shift就是9
$ W1 P2 x/ y+ q! j, {* d% ` */
2 @+ h0 |8 A# m" T3 g; n int page_shift;4 f7 M& A# W5 h6 O* S
/* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
5 m3 p+ K! r5 c! J * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14* F6 N5 p. {. m4 ?( U* i
*/% e% Q+ v- i* ?6 O) ]
int phys_erase_shift;
6 r% F1 d2 f9 X6 `0 D$ A /* 用位表示的bad block table的大小,通常一个bbt占用一个block,' G+ N7 J- |& l) [! k
* 所以bbt_erase_shift通常与phys_erase_shift相等
6 E' O) d9 f( ^4 \ */2 u# l9 n' x: a8 C2 K
int bbt_erase_shift;
% P7 s/ D# r2 z% O, a+ Q3 o3 M; g /* 用位表示的NAND芯片的容量 */
2 z- W- Z9 V# r% v8 J: h' B int chip_shift;2 N( c4 w9 c& U: _7 v0 F1 c
/* NADN FLASH芯片的数量 */$ g+ O$ V8 j1 {$ q
int numchips;+ i! S: F/ \0 }
/* NAND芯片的大小 */
8 ]7 @- l/ h# F5 ? uint64_t chipsize;
- W& S- g# r1 G4 N int pagemask;% E% @+ G* e3 G
int pagebuf;
% X6 x) Y$ y& w0 {8 W6 I& s int subpagesize;
3 {: e5 Q: ?& H: u uint8_t cellinfo;
5 B: I. \1 ` n6 Z1 ` int badblockpos;
. N" q3 `9 h/ Z' E& ~: ]# F nand_state_t state;" [4 Q* b; s+ U1 j' `# g
uint8_t *oob_poi;
& t0 y6 @5 ^2 A# ?5 C- M struct nand_hw_control *controller;
& ^% F+ B6 p x struct nand_ecclayout *ecclayout; /* ECC布局 */
( D: U( i+ R: s+ R
$ n" k# u. C6 ~# Y' ^, z struct nand_ecc_ctrl ecc; /* ECC校验结构体,里面有大量的函数进行ECC校验 */
% m- m9 N5 k5 ?/ J6 G7 y struct nand_buffers *buffers;2 S3 Q# C! R( t6 A
struct nand_hw_control hwcontrol;
8 e ]) [( N0 Q: i" O struct mtd_oob_ops ops;
( `$ e0 H* c- r5 {: D uint8_t *bbt;
, [ j) \! |3 h1 `8 M8 S struct nand_bbt_descr *bbt_td;. h. l2 \! N- K! _" u( y) D
struct nand_bbt_descr *bbt_md;
3 x& U5 q$ `0 ~; T struct nand_bbt_descr *badblock_pattern;9 A3 H/ _. v- S$ f0 M
void *priv;9 S. @1 Y5 J5 n' |8 L* J' ?+ t# C
};+ j" f7 d1 z5 D; S4 q
- r' L0 B# h6 H9 ]# m [1 I
最后,我们来用图表的形式来总结一下,MTD设备层、MTD原始设备层、FLASH硬件驱动层之间的联系。
- x: H( K' z) @$ W7 @( B+ }2 j9 _* H; H
) z" I, l0 e. {9 g W5 }/ I" B' m. g) x" x1 [6 z5 E
0 o9 q1 \5 z* x* K2 s# o+ l |
|