|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
6 f3 X8 v% r$ s! s在阅读本文之前,请先掌握以下基本知识,不然请略过本文。
! Y0 w, |# I5 U% l2 \6 T5 q3 Q4 D8 g3 z9 A. [& @# C- t1 Q7 ^
预备知识:# m/ Q, K9 g1 d J C& {
9 B6 l3 f# \# J) l熟读LDD3前十章节的内容。
6 |: P) @& q: W0 n1 W, U8 {, ]0 g! Y8 Y$ J$ a1 t+ {5 `5 R) m
熟悉内核驱动模型(sysfs)和platform总线。5 z. a7 c4 a9 | O. f; j. X9 V
) F4 w( ~* `7 G- H$ ~6 f5 S简要了解过SD卡规范。
! x- I! g. E- R8 J* G& f
7 {4 b( q" ?" d7 Y0 P+ \' ?# } D* ?! Z3 y
K8 D/ L2 p# A( Z/ p
本文的内容基于如下硬件和软件平台:
7 I( i1 P! G3 l1 S, E+ F" M+ g7 q9 O8 k; }3 _; x
目标平台:TQ2440! q; K. ]/ B5 j/ c! h
$ K% e, t% e% n; O+ R" q6 G% qCPU:s3c2440
5 O, S4 i& V+ y2 K
) M9 J2 u' Y, n) u+ W" L! V3 B7 N内核版本:3.12.5+ F8 W: U/ ?, [4 K% N( j( y
5 @ @" s0 C7 w- q9 n0 U基于SD规范4.10,即《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10》。; a' e3 }) E! b
6 B0 p. ^8 p- Q7 w+ o一、MMC子系统构架5 V8 w1 B2 q! p
待写。。。
% W) `& p9 u1 \4 p4 D9 K" x: K- s. @& K n. t
二、主要数据结构8 V5 @6 ^$ X: }7 q
待写。。。! k7 ~- q) u4 t1 }( L; f2 b
6 \! X: m0 }9 U/ n三、MMC子系统初始化
% `. |) D$ N8 }% W$ @# H首先看看子系统是如何初始化的,完成哪些工作。$ g) z" h+ O8 s, A- ~3 r8 {2 ~
# H' }: Z9 D/ N$ c5 J
代码位于linux/drivers/mmc/core/core.c。
9 Z$ f0 {8 S3 k5 P! l( Q% ?; o6 m/ D2 u5 [: q: I- e
& y6 l* g; ], dstatic int __init mmc_init(void), w# C$ c/ x5 z8 j3 G' ^( z' y
{
' o! _% O$ a# x7 f, u3 y int ret;: q. P) C& n' k9 @, Z1 ~; v
$ N/ A5 \ G1 Z6 F) F. w /* 创建一个工作队列*/
; g L- C3 Z0 X# s9 a workqueue = alloc_ordered_workqueue("kmmcd", 0);# o. x* |3 x3 a* M: I
if (!workqueue)
5 L4 Z/ t$ S$ L9 x# | return -ENOMEM;9 v, i' _& Z6 L6 ?9 T7 }2 w
8 r! W5 J( O# W: ~0 d /* 注册mmc总线,总线提供probe方法
( L/ N/ y) \: _; C& K: }( ^2 l 并直接在内部调用驱动probe方法*/
9 H9 `& h8 J0 X- H6 a* h' v ret = mmc_register_bus();
+ _8 A* O" n7 r( d" r if (ret)6 Q) K1 J6 |* E6 b* x7 B
goto destroy_workqueue;
% p) b) F; E" I 9 ], S+ Q* Z, u D
/* 注册名为mmc_host的类*/
: u* l6 V( z8 {2 h ret = mmc_register_host_class();
0 [6 @1 }! d5 w if (ret): s2 e3 b) E! ]) |. b
goto unregister_bus;6 v, o/ t; O5 Z; t2 e) q$ h7 b
# k% M$ B* z: g1 {- ?# c# g. B /* 注册sdio总线,总线提供probe方法! f7 u9 v l/ ~% P- S
并直接在内部调用驱动probe方法*/ z7 c) f5 T [, c
ret = sdio_register_bus();, P; c4 t! k) o" l# @! \
if (ret)
' z" W1 u0 Q8 k: o$ @6 x @& u goto unregister_host_class;& n7 I/ ]: P5 I6 n# n6 W: t; E
# d% W v% X/ z3 e return 0;7 j7 K; w( D6 g5 o# E; i
% p4 g8 D) h: i: ?) A% Y9 [unregister_host_class:; X4 k' [. i( _$ Z) [3 w) v5 X
mmc_unregister_host_class();
: V& }! T. E$ j# e4 @ Runregister_bus: n: X/ g% ]8 H8 L9 _
mmc_unregister_bus();
/ g# y$ z! F% }. C6 `$ P" Ndestroy_workqueue:
) P9 h2 ~: s3 ^% Y. ~ destroy_workqueue(workqueue);
! A, ?/ e3 g9 f |
5 G0 r7 i1 f# C* @- \ I" X% Q8 G6 o return ret;4 g: d; n7 n$ ]+ A
}; z) h# p, l3 ~5 d9 J7 ^. `- h
H/ d G: l1 f+ X4 W% @代码首先注册了一个工作队列,这个工作队列将用于扫描sd卡设备。我们会在后面进行说明。. T, C* R2 C( w5 ~ N7 c. r
2 V* g% L/ ?; B2 ~6 I
工作对类已内核线程的形式运行,可以用ps命令看到名为[kmmcd]的内核线程。: a" f- t; n0 U8 w. l
# N! l7 L. h5 } g9 X3 l* u
接着注册了两条名为mmc和sdio的总线,以及一个名为mmc_host的类。具体代码如下:
. o% u; |. o# e8 j; I$ d" S7 d( x7 ^% S
. N" Y4 _+ R7 K9 m2 q K
static struct bus_type mmc_bus_type = {
+ k; {4 d# p% ^' H .name = "mmc",* x5 d) u- T6 |5 n9 b q/ L
.dev_attrs = mmc_dev_attrs,& _" ]0 p4 S. \9 k" c% F! ?6 L
.match = mmc_bus_match,3 W2 P& E. ~2 p2 Q5 @# ?6 b% e
.uevent = mmc_bus_uevent,
& s# {" B$ v/ [3 B. [* A5 Z .probe = mmc_bus_probe,
9 l, j- K' u* X7 `, \ .remove = mmc_bus_remove,
1 i. v9 C3 E0 V" Z .shutdown = mmc_bus_shutdown,# f- ^+ b" L: d6 R; s$ h
.pm = &mmc_bus_pm_ops," j. d& _9 C# t# s! z( \
};1 M5 P) P: g0 T* N! _" ?! l! z
# a: H7 c( l! zint mmc_register_bus(void)
! H. r, B& O! ~{) }. L4 }6 P$ ^1 o9 v+ D
return bus_register(&mmc_bus_type);
* i8 w4 s9 H& l% _0 ^- l}
4 l7 M- J; X$ u9 y7 V6 Y( U! a4 r$ ]- i$ [
static struct class mmc_host_class = {
0 N5 z8 G. A: _* y$ o; { .name = "mmc_host",
; A7 S* d5 u, B- P# @* _ .dev_release = mmc_host_classdev_release,
4 ?- z: j" M; p/ [* Y# r};
. X1 J' X: P4 }& ^8 p : H! y; z6 V9 }
int mmc_register_host_class(void)/ ]) R9 Q" A& Y0 l0 E: g+ n. o" q
{
3 p8 x/ w" o# K0 Y4 X7 d& @( M. X return class_register(&mmc_host_class);+ B5 @' p% r: u
}# y5 Z a Z+ E% T8 j6 h0 y
, V7 N4 E$ k- z$ j* \/ v6 U
static struct bus_type sdio_bus_type = {
- H2 A, O4 O/ q/ \5 s .name = "sdio",: v' ]2 Q& t6 v$ {! S# i9 @
.dev_attrs = sdio_dev_attrs,- i" S2 {% a, ]/ o( Y+ K
.match = sdio_bus_match,
9 ^' Z% R/ ?0 y* f' M) ] .uevent = sdio_bus_uevent,* {% T2 Q& X0 P' j9 w+ u7 R
.probe = sdio_bus_probe,
& L; R N. q' r .remove = sdio_bus_remove,7 U9 t# Y9 m0 {/ s! J) e1 u8 |
.pm = SDIO_PM_OPS_PTR,
$ v: I% Q9 B) p$ x2 L};
5 ^2 X, P0 Y2 D2 k) ^6 {! L 9 I1 g1 h% b: r4 w+ Q
int sdio_register_bus(void) I/ O8 J* V. g. g) X( V. A# r
{
' O* n4 h# `$ P5 K- x' D) a return bus_register(&sdio_bus_type);: v' R# k0 m4 ^0 h' c
}
' b- |. K2 O5 k J, u! m ( k9 u f7 ~( _9 G) I E$ m
static struct class mmc_host_class = {* c3 ~0 q7 z! n" W* x0 i
.name = "mmc_host",6 T: m1 e& V6 ^& Z2 d8 p
.dev_release = mmc_host_classdev_release,
9 I- w0 Z1 A4 M# p# x; o- A% y};: V. e3 a8 f6 P! }/ Y& B0 E* s
5 z, F. C: M F' u4 }
int mmc_register_host_class(void), P: S" f1 R! @
{
u0 ^8 Q2 e2 K7 v% W3 F$ n return class_register(&mmc_host_class);
2 n, p# \; N' J% F& f2 C3 `}9 u6 L9 i# e, X; V
# t( a/ q, @ t2 [3 K* O) P
熟悉Linux的设备驱动模型的同学对这些肯定非常熟悉。总线和类的注册只是调用了相应的接口,这些就不再赘述了。+ ^ @9 \/ j: i" C
5 }4 q* ^7 y" N2 r" Q. B
其次,sdio总线不是我们关心的。我们只关心mmc总线。首先来看看mmc总线的match方法:
/ R/ [2 g; [" k' ^9 x
) O6 U8 ^+ N; l6 R8 t+ _代码位于linux/drivers/mmc/core/bus.c。
/ y3 ~3 c6 a+ |" D4 {1 z! P9 _! ]6 J. O
+ ^# m3 c, t1 v2 I8 ^( T5 I/*# `$ y) m3 z+ M+ ~* x4 x! ^
* This currently matches any MMC driver to any MMC card - drivers5 L5 |7 m4 K5 a/ Z8 X4 I2 C! m
* themselves make the decision whether to drive this card in their# [! y$ \! u/ V# l' K( J& y
* probe method." \1 p @6 d8 p
*/ F7 M1 Q/ k! y# H: p9 U* m
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
$ L% q- B; F P+ U6 S9 g{
% C: I9 s# s% F return 1;
2 I0 I& [% h/ v+ f1 [}4 g; _3 s% a8 [5 [1 b3 R
match返回居然直接返回了1。这表示任意的驱动都能和mmc卡设备成功匹配。
|% @; N2 a* W8 J9 o
( B. E3 f P/ @9 f1 x从注释中我们也能看出,驱动的probe方法将会决定驱动是否能真正的匹配这个mmc卡设备。
, ~# K, X4 x, M- m* R( z: ^3 q9 g' A! z2 c f+ f
熟悉设备驱动模型的可能知道,随着match返回1表示匹配成功后,将会调用总线提供的probe方法。接着我们来看下mmc总线的probe方法。
$ ~1 O: I3 ^" Z9 f7 S k4 `- V& Q$ p! L( T1 T+ r
代码位于linux/drivers/mmc/core/bus.c。
7 O' R& U! G; H# U" q5 G
( h3 u' `( X* ]8 n/ Z r7 ~# D
& u' x# c4 U" v" {8 Zstatic int mmc_bus_probe(struct device *dev)6 n! D; W Q9 @' H9 \8 W& H9 @
{
7 q+ s$ Y! X2 D) a struct mmc_driver *drv = to_mmc_driver(dev->driver);
6 {/ K! Y. K6 D4 K struct mmc_card *card = mmc_dev_to_card(dev);, k0 Z: ~" C/ c+ a
) ~/ \1 ~6 y" d* Z+ e& e- u return drv->probe(card);4 \/ O. Z1 X4 S9 m/ Y
}
6 a/ \4 r* V" f6 G从这里我们可以看到在mmc的probe方法中直接调用了驱动probe方法,这也验证了刚才注释中所说的话。
; P# j# ?8 O K5 C3 i& W. N3 ?7 x从上面分析可以看出,子系统初始化代码仅仅注册了两条总线和一个类,并建立了一个工作队列。
5 S% k2 ?* E' K( l. z) Y) f0 D% c' Y% `
; S, y4 o4 y8 [4 b, B0 p$ T2 ~( |0 c) S ^
四、核心层与控制器层间的接口API* M) l9 E; ?3 ^! Y
MMC核心层要和SD卡设备进行通信,为了完成这一个工作需要将CMD或者ACMD命令通过MMC/SD控制器发送给SD卡。
/ E% t2 l3 K2 ^ D( c0 c" P6 ^7 h. {4 a- \8 G
那么MMC核心层如何将通信的数据包交给MMC/SD控制器,并让后者去发送呢?
) A5 B8 y7 C7 o0 ^' Z- w& b) `+ Z! f- _) H: ?5 c
MMC通过函数mmc_wait_for_req完成这个工作,我们来看下这个函数。2 Q. m7 w+ P2 @9 w2 J/ @
+ R6 K/ U& R* |6 M
4.1 mmc_wait_for_req 函数, g# J( U H, a1 _+ S6 h* G
下列代码位于linux/drivers/mmc/core/core.c。
: {# Z8 R0 W* g
e, i+ u3 n* ?' f' o/**5 |* F8 ?4 y% ?+ ~( x
* mmc_wait_for_req - start a request and wait for completion
4 ?; i/ C6 }) d7 y# P * @host: MMC host to start command- r8 s" J/ A) Y) }' `
* @mrq: MMC request to start. K+ m5 C0 r0 d$ K! y: G5 l
*
: m# a( o' V3 x3 [: p * Start a new MMC custom command request for a host, and wait1 w5 @) D5 s7 j
* for the command to complete. Does not attempt to parse the4 S7 N7 Y$ K/ A' m$ j7 Z' T
* response.6 [+ Y j. n' _
*/
- }0 ?* Q9 [2 K) M9 bvoid mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)8 ^9 s; ?3 Y* p. y: h$ e
{
) V! U0 Y/ L* F __mmc_start_req(host, mrq);
2 [4 D$ m- X2 [# {& F mmc_wait_for_req_done(host, mrq);
. b/ h/ r+ n/ a8 e}
( d: q8 w2 A4 z& R/ J# EEXPORT_SYMBOL(mmc_wait_for_req);& U" q% P5 H1 u1 O U. O
7 s7 V: t( G2 o2 l& E 通过注释可以发现,该函数会阻塞并等待request的完成。
' d9 b. k% s4 }! L1 a& K! m 该函数分两步走,第一步调用__mmc_start_req发送命令,第二部调用 mmc_wait_for_req_done等待命令完成。! [3 H% u. Q* F* t f; T
3 L; r1 a( n% N0 ]+ A! U
分别来看下这两个函数 :
) c4 n. `) t5 F& o
2 q0 K& W6 q- I$ G* {4 D
; [! h: k3 Q$ \8 {
; `9 J. v- I* E% s9 |static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)* n2 m8 \2 N& W! t
{% D& t# K5 d6 E* F% U2 F( I
/* 初始化completion,并设置done方法*/
1 q5 r6 z' p7 ~% f, |1 o init_completion(&mrq->completion);8 V& h5 x. p9 t1 _5 o H
mrq->done = mmc_wait_done;8 O. j) k- p5 G) E# b
/* 如果mmc已经被拔出,设置错误并返回错误*/: ~% Z; ~0 s$ Z; O. k6 v! n7 V
if (mmc_card_removed(host->card)) {
/ ^' b% k3 S1 x2 c9 ?6 s mrq->cmd->error = -ENOMEDIUM;' v7 _/ f) q1 e* U Q
complete(&mrq->completion);' O: k. Q/ q; U+ B* \% e' n
return -ENOMEDIUM;- o! u/ u, }1 k
}
7 f& b& w: t, \8 s9 K- Y/ k /* 发送命令 */4 C8 X' W! C8 `5 ]; V
mmc_start_request(host, mrq); B4 N6 t) k. [9 k: g* B# y
return 0;
4 t- `3 ]' @7 p}
; n) a3 x3 r4 [该函数首先初始化了completion并设置了mrq->done方法为mmc_wait_done函数,该函数如下。. [; x& e& k2 [* L: u0 z
9 X" s: L5 v6 S
static void mmc_wait_done(struct mmc_request *mrq)* u6 r# y* }; T8 C
{
N/ t5 a3 n. [ Y3 X4 \# x complete(&mrq->completion);
# H. q$ ~' M- _1 b- F7 P}
& z6 g. |8 u/ t+ _. t$ }这边使用completion的目的是为了等待request发送的完成。 x" B, z, C) K5 v
在第二步mmc_wait_for_req_done中会使用wait_for_completion函数等待mmc控制器完成request,控制器驱动在完成request的发送后,会调用mrq->done方法来激活处于等待中的wait_for_completion函数。# E! p( [! ?1 ~8 ?( L |
$ C" i# q, `- a. T9 D随后函数会首先检查sd卡是否已被拔出,如果卡都被拔出了则没有必要发送request,可以直接调用copletion函数告之相关的等待函数,并设置error值然后返回错误。
7 ~/ a; g, }$ b4 H6 l9 Q
4 F2 s9 Z5 f$ s( o7 V/ S5 ]4 D7 M#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))0 B+ G! y9 o/ n7 ?! @9 p3 B2 t% \' N
如果sd卡存在,则调用mmc_start_request函数发送request,该函数如下:; j" r4 B) D# R, P; R1 _
" |/ ~) F) x' ]4 |/ x; O! t1 M; c1 W y& y0 B1 N% x
static void, X g/ Z* |& u- _! S7 b
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)+ g3 G" U* J) G, M8 I5 D
{1 p/ Q! P. b: n# S
#ifdef CONFIG_MMC_DEBUG7 y! B5 z8 D+ R- }
unsigned int i, sz;& B/ s7 I$ K' }
struct scatterlist *sg;+ J0 ?: W, h) X% I7 k `
#endif
, k/ t5 ]; N9 X0 @9 C: u 0 u6 ^# i1 a" g
if (mrq->sbc) {% V3 V1 @, F c V2 M) l8 ~
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",+ [! L3 _9 l: K/ v, q
mmc_hostname(host), mrq->sbc->opcode,
5 V! l+ a! q: F mrq->sbc->arg, mrq->sbc->flags);
" [! R( e' f" X0 P# r+ S2 _! V }% w5 @7 m; P7 M/ ~: \& c" u$ ~
9 N1 `- ^- p0 L2 n
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
! y( Q5 D& e Q mmc_hostname(host), mrq->cmd->opcode,
7 t0 u j0 H: P7 H9 O9 C mrq->cmd->arg, mrq->cmd->flags);/ B4 w: Z) q% j+ k# E
6 o7 I% `2 v% \; W6 J if (mrq->data) { _( U8 \* z* _9 T
pr_debug("%s: blksz %d blocks %d flags %08x "
$ O, Y/ W! T8 J+ O j "tsac %d ms nsac %d\n", K2 e- V- ]' B4 n: @
mmc_hostname(host), mrq->data->blksz,
7 b. ` U+ C) ~8 U+ Z mrq->data->blocks, mrq->data->flags,
% ?/ C: q; z6 s0 a' I+ c mrq->data->timeout_ns / 1000000,, I$ s+ |/ C) E
mrq->data->timeout_clks);, f0 M5 p6 k7 ?- y T8 t- J2 b
}/ C& {' j8 ^/ e5 _: [# G
6 T+ U8 s/ t* W& H( d" u) Q5 H if (mrq->stop) {
$ a& U# ^3 H2 g/ E& c1 U pr_debug("%s: CMD%u arg %08x flags %08x\n",( S+ q9 F( s7 m
mmc_hostname(host), mrq->stop->opcode,. G/ W0 n- S% A" O& `
mrq->stop->arg, mrq->stop->flags);
6 d; }) J% _& f! b$ E# p) Y }
' K' Y; G+ O" |
5 A$ A {( @- p8 m WARN_ON(!host->claimed);
8 |: X: A* N- F/ f4 ~, h / D+ X1 D# B" Z1 Q, X
mrq->cmd->error = 0;8 ?* [$ J$ B1 ^* T$ h
mrq->cmd->mrq = mrq;
4 f4 c p0 r* \4 V if (mrq->data) {. b4 O; \) Z/ J& }
BUG_ON(mrq->data->blksz > host->max_blk_size);; v8 e' G6 r+ l+ d' K" v3 j; T' I
BUG_ON(mrq->data->blocks > host->max_blk_count);4 k' B8 Z: R5 n
BUG_ON(mrq->data->blocks * mrq->data->blksz >6 s% c: p9 n9 ?/ \1 l/ |
host->max_req_size);" Y8 l; L. {& `: O: n
% `1 x$ [: h1 B: a#ifdef CONFIG_MMC_DEBUG
4 s2 `7 z) N, J: ?2 \; g5 ~ sz = 0;
9 l( L! I1 G; S( X5 t( w for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i)7 u" D8 i+ F& @7 D9 ]9 d6 ?
sz += sg->length;! y- |8 o$ ^2 |7 p' L& C- S9 `. P7 S# ~
BUG_ON(sz != mrq->data->blocks * mrq->data->blksz);! ^& t; I1 H. T
#endif/ p; [; N, S% g! l/ \4 B2 H9 _/ V
l8 L) D: w c0 z: A mrq->cmd->data = mrq->data;
* k: O4 H8 I4 I. N0 w mrq->data->error = 0;% i- T. R! X# G& ?% |
mrq->data->mrq = mrq;
" \) h9 V+ G. i if (mrq->stop) {
) `5 }3 ?8 g5 w0 {% a( T mrq->data->stop = mrq->stop;- H( I& S8 N! ^- w) N( ] l
mrq->stop->error = 0;: Q3 l+ z3 m, ~, l
mrq->stop->mrq = mrq;
0 }1 X' X+ ?; M& t7 c" O }* _, x8 }4 \& z2 l3 r" W
}, u; E4 B3 R, P [" i9 t' g
mmc_host_clk_hold(host);
; D2 E7 i! Y1 @- {! z9 Q/ ? led_trigger_event(host->led, LED_FULL);
: ]& Z4 h" j( _) J, J1 K% ` /* 发送request*/; I3 f x$ I: T3 H& z& O/ {
host->ops->request(host, mrq);
1 U) V7 _2 |( b0 [: P7 n}9 |5 D* {! F! V5 _% m
该函数会打印一堆信息,然后清除cmd->error,并绑定cmd和mrq,接着如果mrq是请求数据
?2 ]4 m3 A$ ummc_host_clk_hold函数是通过宏CONFIG_MMC_CLKGATE来进行使能的,这个宏默认是不打开的,具体就不分析了,简要说下这个宏的作用。
( J2 A4 `; F3 |. [! z" o5 y. @9 s+ K
这个宏的作用是使能时钟门控功能,这个功能在不需要MMC控制器工作的时候,停止MMC控制器,以节省功耗。
/ t: B5 E8 T; l% t8 Z$ w2 N, X2 V6 i
随后会调用led_trigger_event触发led事件,这个牵涉到Led子系统,就不进行说明了。
. ?1 A" C% R% r- B- T3 \3 M% I4 ]4 F+ X5 M7 j& A4 K M
顺便提一句,s3c2440的mmc控制器驱动并没有使用这个led触发功能,也就是说host->led是为空的。8 b8 O+ _. ]8 h. Z* R7 q
; b, W3 t& X7 u
最后调用了mmc控制器驱动提供的request方法发送request。
w+ j1 H+ M2 j# ?5 i5 D) b9 t, o
. t _! ?9 T, B9 b- T6 r% ^这里需要注意下函数指针的形参:一个为host表示mmc控制器,一个为mrq表示request(请求)。
$ R; V4 l! g2 w0 C5 N5 N0 h
! Q. s. U! @1 J! l1 D很显然,要求host指向的mmc控制器发送mrq指向的请求,同时,也可以看出所有传递到mmc控制器驱动的请求都是使用struct mmc_request结构体进行封装的。$ Y3 Q) L! r; F! {) |) |( z# S! m
/ V: I5 C6 n# ~1 ]: q8 _至此,第一步完成,接着我们来看第二步:
/ T' ?9 @' W0 `. h5 N% n$ N/ w% M4 }6 ?' B( P+ p
' k& E" {/ W9 Z ustatic void mmc_wait_for_req_done(struct mmc_host *host,# H; K0 A0 m. d: D& D; j2 P
struct mmc_request *mrq)9 L% g, E5 }* h) _
{- L. O4 F) y* y8 O3 [
struct mmc_command *cmd;
$ X+ j. q: R& Z% K- F: G) K G/ y6 U 6 r8 o9 Q/ L& \6 @8 p) z) F
while (1) {
, \6 H+ t" p4 s1 U* Z q% s7 v wait_for_completion(&mrq->completion);
- R6 }+ s3 f# T) ?, X) |/ y( S8 w % l" _" g. G0 W/ X7 _! b
cmd = mrq->cmd;
, J4 N& |2 `( ?1 ~ t" b5 R, Q& U6 `
0 g! Y2 U9 } R- U2 ^" ^: F% ?5 v /*7 D2 f+ n( b! o7 C
* If host has timed out waiting for the sanitize
% r- ]. C+ O5 n1 P * to complete, card might be still in programming state( w1 @& i) K6 P, e. n4 {
* so let's try to bring the card out of programming2 l/ _! s: P; J; t2 W$ t+ \( R
* state." }' z0 s, I3 O$ h: G
*/
3 c' K+ o7 l4 l# g4 t. C0 [) K* a, R if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {6 y) k; |5 O7 Y( {# x
if (!mmc_interrupt_hpi(host->card)) {
8 {1 @; Z- e' |( h+ j8 E4 F8 }1 V pr_warning("%s: %s: Interrupted sanitize\n",9 e- J" R& X- q* {
mmc_hostname(host), __func__);
' W- z6 Y6 j5 T" d0 |! \* Y5 ` cmd->error = 0;
/ C: ]2 Z3 A' _3 F break;& P$ y) L4 M4 O; i- M5 x9 F0 ~
} else {
. I3 ]7 F3 j, S3 z* @$ p% u pr_err("%s: %s: Failed to interrupt sanitize\n",
* o6 @4 s7 C: G; W3 | mmc_hostname(host), __func__);
4 Q/ X8 Z0 v3 z$ X% `* [& ^ }
: x: j5 @2 |! h }
R A& F+ K) Z if (!cmd->error || !cmd->retries ||/ K& O! C% D3 s6 q% X) M
mmc_card_removed(host->card))
, m4 w; v8 c( ]9 l break;
. G) Q2 ?. H/ ^7 l) y8 }
2 L8 d- z7 O: g" }. e pr_debug("%s: req failed (CMD%u): %d, retrying...\n",* _* l8 x5 U* V6 A1 ~3 W( s
mmc_hostname(host), cmd->opcode, cmd->error);4 `! W, Y& \! L
cmd->retries--;
. z3 l) k2 Q: h# e% E2 ]* s cmd->error = 0;
. Z; [8 h+ y6 w6 n& P% n) ]9 f7 Y /* 没有成功,尝试再次发送request*/- P/ E0 D! I" a! E
host->ops->request(host, mrq);5 q0 n0 \2 L& E4 e# t! Q
}4 ]/ J) r L( Q$ A7 g% o9 A5 a
}
- d+ J. g- R4 b' G: J这个函数首先调用了wait_for_completion来等待mmc控制器驱动调用mmc_wait_done来唤醒自己。
) a" c) f. O V) B5 `# R被唤醒后会执行一系列检查,如果request成功发送,则会break,并直接返回。
1 d% z- d% L* a
8 w' x. p8 @5 @% ?8 Z如果没有发送成功,只要retries非0,则会尝试再次调用mmc控制器驱动的request方法再次发送。. m& ?; F+ \' D! m: \$ o/ Q2 C
( K7 @* J! s+ G, c1 }- b, |* A
4.2 CMD和ACMD发送函数9 a( O& k( x/ t1 s3 e
通过4.1小结,我们知道MMC核心层如何将request交给MMC控制器驱动,并由后者发送该request给sd卡。
6 n& U# z! a# E0 Z/ P% s6 S P5 u) w: B" P9 J, g
通过SD卡规范,我们知道有两种形式的命令,一种为CMD,而另一种为ACMD。
3 n9 W5 r0 U1 g, C b( X* z2 Y$ Y w3 }1 Z1 |
MMC子系统提供了两个函数来完成这两命令的发送,分别是mmc_wait_for_cmd和mmc_wait_for_app_cmd。, U) ^1 ~9 _- \/ S; Y! W7 z
3 ^) J+ C2 N4 {
先来看下CMD的发送函数:
% s1 r/ x" I+ t* x ~& T. [% }( O( R* @
下列代码位于linux/drivers/mmc/core/core.c。; X8 s; F, O% [" S3 y2 N0 j
" v9 ?* v1 m! u, \' u
/**% F N" i; [- w3 {4 {( W
* mmc_wait_for_cmd - start a command and wait for completion& d5 _# n( W# T2 W2 u/ ^
* @host: MMC host to start command# I3 p+ B7 h. ~- f( W
* @cmd: MMC command to start. Z! j* S) ]" d- `' _* I
* @retries: maximum number of retries7 u( J3 y% X4 R% ^9 Q
*
$ o; R: }: n! V. _9 L( z * Start a new MMC command for a host, and wait for the command; H/ { l7 N n- P: v- Z8 M
* to complete. Return any error that occurred while the command" i! E& J2 v2 N. ?- R" M
* was executing. Do not attempt to parse the response.: t' I! n5 t e6 Y+ O
*/
8 e4 j8 u6 Q7 W: r- aint mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)# F3 _+ A; b! ?: N0 i
{
0 z' o: V ~/ O J struct mmc_request mrq = {NULL};
0 v0 |; g, @+ |) D' U, s 5 t' e4 q7 D1 M3 R9 Q( k
WARN_ON(!host->claimed);1 T, p6 ~' @+ `7 h! J2 j
- A! m$ r4 ?- k2 ~) @
/* 清空应答 */5 b& I( M8 s) _
memset(cmd->resp, 0, sizeof(cmd->resp));- L0 Y$ f. C' F/ D8 P% I
cmd->retries = retries;4 x R; ]- A: \. v: k; N1 S; k% C9 }
+ P: {/ I) y) ^7 ?" A x$ O/ V6 O2 |. e /* 保存命令*/
) d3 P+ j7 A: e, L; F mrq.cmd = cmd;4 o r- k% h6 S& H$ N
cmd->data = NULL;+ l$ l( {; m3 a( q- _2 L
: F" N$ ]3 I# T! \1 H
/* 发送命令并等待response */
+ R% P0 _' Y# O/ I( d0 t6 I( ]0 G mmc_wait_for_req(host, &mrq);9 C0 Y4 A+ W& G& H
% i& X$ M, @/ D1 r4 |( o
return cmd->error;
! a6 @* f; P6 S4 X3 ~" t1 I}9 e0 }& C1 h' M# D
有了4.1小结的分析,这个函数还是比较简单的。
5 k6 Q# W5 w1 A( k$ m) c o& F该函数首先清空命令的应答数据(resp),并保存命令(cmd)到mrq中,随后调用4.1小节中的mmc_wait_for_req函数发送CMD。
+ O: C1 F# e* i9 O
- h5 ]0 L4 Y; m. t% n* F* j从这个函数的形参我们可以看出:所有需要发送的CMD都由mmc_command进行封装,在函数内部被mmc_request ' C q% T3 J* O' u; M
结构体进行再次封装,并将mmc_request交给MMC控制器驱动完成CMD的发送。8 S3 s9 C/ h2 P B& h5 |. v
F. C- t, Y; Y o O* r. Q: Y, B3 i接着看下ACMD命令的发送函数mmc_wait_for_app_cmd:
) V# c- `' G! _6 {/ x/ @' x; o
下列代码位于Linux/drivers/mmc/core/sd_ops.h。
8 b" a+ @" _- n: n' p# Y
; b; i5 a1 c4 M$ G& o" Y9 u/**
+ R! I( e5 |( j1 ] * mmc_wait_for_app_cmd - start an application command and wait for
: q9 |1 _8 K$ e% Y completion
7 f) t! o4 c, z2 B5 Q) l1 f O2 }8 O * @host: MMC host to start command
* a, Y' u, M1 V' Z* H * @card: Card to send MMC_APP_CMD to
5 q( Z7 A2 c1 N2 H * @cmd: MMC command to start
5 d# @8 r9 _# @, q * @retries: maximum number of retries
5 f* @9 X# w5 m$ H; P% ?" z5 d *. |, K; m; V1 U! d/ N
* Sends a MMC_APP_CMD, checks the card response, sends the command% ~# \! L7 L5 x5 P+ |6 Y
* in the parameter and waits for it to complete. Return any error0 a4 \6 K1 _7 h& _! J
* that occurred while the command was executing. Do not attempt to. ?/ F' K4 y% C5 j0 n' _
* parse the response.1 x; c% o' R) h6 r
*/
. e1 h9 P/ `% A$ Y1 o# rint mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,5 N7 `* y" L2 N, h, ]
struct mmc_command *cmd, int retries)6 Q6 J4 ~$ R1 N* t+ u2 d
{0 [/ a# z4 W" p- A5 b$ \" Y
struct mmc_request mrq = {NULL};! Q1 C2 }! X* i& v' p
% m1 o. o2 m; K- y/ @ int i, err;
+ |0 V! E0 \0 q5 f+ C- H( C& m
8 L. ~3 o/ d! Z3 Q BUG_ON(!cmd);6 h. Y4 `1 j4 {
BUG_ON(retries < 0);
/ s: M& T3 @( ^; F
6 |. e) N6 a7 w: ` err = -EIO;
' G1 k$ a' p* ?+ Z* B
6 G6 e1 o6 h: F: I1 d9 ^. y /*) T" `6 F: e: r( K- g9 X) k
* We have to resend MMC_APP_CMD for each attempt so7 X% N' _ k* C9 D& e
* we cannot use the retries field in mmc_command.
$ z- h: V& W P" X9 X */
^. @& Q; c j for (i = 0;i <= retries;i++) {
1 }* _$ Z0 X3 o* |' w& q% g4 a /* 发送CMD55*/
& I9 ?* I. U2 A# u6 a5 h0 Y6 o: g/ { err = mmc_app_cmd(host, card);
+ M0 t8 _) [$ T1 [- q( m if (err) {
. z) D4 [9 E8 y, i' I, ^ /* no point in retrying; no APP commands allowed */ p0 D0 a# r4 T$ r' f9 m
if (mmc_host_is_spi(host)) {8 o. F" S; H/ p* J
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
3 u' `3 o8 U( ]; o/ N3 [/ n break;5 ?; | z& o& j, L/ w
}
( r9 q: W0 }2 v3 B) R( W$ N* N continue;
0 d6 I9 q% }6 g, ] }
' \7 i! _2 X# @: i
0 z# Q) d0 W9 r' R$ O7 o8 I* o memset(&mrq, 0, sizeof(struct mmc_request));: K* \1 }$ F; L3 E, X+ w
; f* o; j2 L+ W# v6 C) k8 a2 S: f memset(cmd->resp, 0, sizeof(cmd->resp));
) w% F( h W% |2 ~2 L- ?" A cmd->retries = 0;
, u2 d, N/ U l+ ^" M6 o1 a 8 v* ?) g' a: Z7 K6 q! L& _
mrq.cmd = cmd;; y# N* t, u: `
cmd->data = NULL;. X, ?9 p4 ~; f3 _' E
) h* a$ V! s T) h
/* 发送ACMDx*/2 |; }( B% x, F, ]! b
mmc_wait_for_req(host, &mrq);2 H: B4 }$ S4 R! b1 w8 m( B
4 F6 x: o y+ r err = cmd->error;
. R& n. n& T l /* 发送成功,直接break并返回*/
1 W: o0 E+ _2 @3 q2 V2 E4 d if (!cmd->error)7 q) q/ ^" M7 Y+ B# I4 c, y+ I& H0 ]
break;! r( B0 o! T9 u, i
- A0 K' K! Y& h' ~5 X5 } L
/* no point in retrying illegal APP commands */
3 `) m0 g5 C0 \% i, w5 b% g if (mmc_host_is_spi(host)) {* A' c+ A" j9 C2 V, Q
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
- y! W! U( |2 l+ b" J4 Q# i break;+ W I' q# R) f; ?2 Y% X, }
}- ~( h2 i' F2 E, E3 Y1 s0 N
}( ^% e' D5 m* v: ?
! `: O% e4 a# K4 h8 R3 {, o% j return err;" J1 i8 H; ?6 X9 ~( q
}
[$ |, K3 v7 ?2 m5 I7 \+ o
8 _6 V' z* v1 G5 E6 E0 _$ uEXPORT_SYMBOL(mmc_wait_for_app_cmd);1 n6 U' p6 T+ }' t
该函数的形参cmd保存了代发送的ACMD命令。1 e& g, {* h3 f* T( G
根据SD卡规范的要求:在发送ACMD命令只前,需要发送CMD55,以表示后面一个命令为AMD命令。1 s: h1 I3 \5 C, }* e
; b X5 j& o' f v* H) \所以,该函数首先调用mmc_app_cmd函数来发送CMD55命令,我们来看下这个函数:0 r3 T9 E6 [% M2 E& b( v3 Y
' ]9 \, l/ C: Q2 ~- K# @
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
. B& p+ r8 \5 k. c7 W- k6 k5 a N{& s5 y6 J8 S; D0 A# g4 u
int err;
7 m9 l* ?( n; f; V5 Q D5 b w struct mmc_command cmd = {0};
& J3 \7 g, [! g" ?0 Z+ k) U- Y ' |& r1 w; s% p" n5 }
BUG_ON(!host);
7 M" s1 U" A( ` BUG_ON(card && (card->host != host)); z; Y9 u" P8 m: Z- |4 J; f) i
F Q; j& r7 B a! ?
cmd.opcode = MMC_APP_CMD; /* CMD55 */
. \0 `4 N8 t. [" S ! r5 {- G% F3 c- _9 N1 ^
if (card) {
% f/ U* ?1 j+ e6 }/ g cmd.arg = card->rca << 16; /* 卡地址*/
" |6 Y! i5 \; N: R4 ~ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;. e p n% Z+ T, H2 y- m- L
} else {( R h) S) M& [% o
cmd.arg = 0; /* 卡地址*/
4 M7 s( x. I2 J& [, W. J' Z cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
9 y, Y% t* g3 ~* x- @4 Q# c. W }& r0 K% I8 J0 O. {" D3 }+ L
* g9 m f9 `5 ^( b/ a6 L# u
/* 发送cmd并等待(阻塞方式)*/
2 B E5 u9 f: |1 A: j7 |* S err = mmc_wait_for_cmd(host, &cmd, 0);
, t6 `3 f( |/ E% v4 G( k' E$ z0 a if (err)
3 @3 Z7 \3 S) p3 k5 R& ? return err;' W& h# p; F+ o8 e/ [% E
; C8 ^& }% n- M ]/ l* W- |& x
/* Check that card supported application commands */
; E8 i% |8 \, v9 c* B /* 检查card status第5位,判断SD卡是否支持ACMD*/2 _; W$ N- }$ Q0 J; E) a
if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
! G2 a( h; h) O) e5 l return -EOPNOTSUPP;6 e6 r& {, k) b" h2 K3 q! n1 f" l
( a2 G4 ^8 c5 a; Y' Y6 C" R return 0;
0 u( U9 p! u! H}* K, Q. b0 V6 H5 q
EXPORT_SYMBOL_GPL(mmc_app_cmd);8 B/ ]% O" b& I6 a" C4 y3 X
先来看下SD规范中关于CMD55的说明:) t0 J, B2 r: o& [( _ I' ?
7 j+ n' Z+ p- c& R
5 m, l- m- \/ ]1 ^9 {9 Q
从上述命令说明中,我们可以看出:1 Y( c4 Q( C0 d" n \- S' c
, d/ {2 ]: \ c/ j6 l* o6 m1)该命令为ac类型命令,也就是点对点命令,并且在DAT信号线上没有数据传输。& _# y* r' u" r# F1 e
/ o+ F2 b0 @6 Y. Z2)其次,该命令的参数(31位至16位)为RCA,也就是卡的地址。# @; l* {& a. G3 l* [" J9 {% A# c
3)最后,命令的应答数据格式为R1。
: |' f# E A# Y2 U3 a$ q2 y
m4 |4 J) l% Y. T! M回到函数中。! n3 D; L6 n7 R
( d/ F. w) z. I
cmd.arg为发送命令的参数,函数首先设置了命令的参数为sd卡地址(RCA),这符合上面的描述。; y/ Z( t- W* h$ X4 Y" c
" S' Y) ]+ p$ Z, H6 D! G) @- B随后调用了之前分析的mmc_wait_for_cmd函数发送CMD55命令。
2 H: \: [1 ?% P8 B6 p! n9 K
- }3 ]+ y( D% f6 g6 h3 x上面提到CMD55命令的响应为R1,其格式如下:
+ l& d# {% J0 V7 C' b8 w$ G1 P4 X& N) n) ]# a# g
- M) ?4 k) S% `' i* [
5 F1 H% t4 m* _/ @4 T其中32bit的card status作为响应数据被保存在resp数组中。
" s" i- R9 D) i6 A4 a# r1 C* A8 E/ W3 A0 W+ k
card status的具体位定义请查看SD规范的4.10.1小结。
; O W- v. @" k: d! x T K# s! M" H! z' A2 v7 W
最后检查CMD55的响应来判断SD卡是否支持ACMD命令。# T* ?# g8 A& a- }4 A' i: Q
2 }2 ]8 b" ~& u: k
CMD55发送成功后,返回到mmc_wait_for_app_cmd函数中。
" i! g( R" Y5 J4 _9 }0 ?6 f4 z1 |3 L+ s- F
接着,cmd被保存到mrq.cmd 中,并调用mmc_wait_for_req中发送ACMD命令。
" u1 c7 P8 X: O3 Z/ J7 E$ e6 t0 J L
, z) V! K( R1 L1 M$ C3 y五、小结+ x4 z$ h) q# v M
本问主要对MMC子系统架构进行了简单的介绍,并给出了一些关键数据结构。同时,对MMC子系统的初始化过程进行了简单分析,最后,重点介绍了CMD和ACMD命令的发送函数。7 @. f9 A! n9 _; ?
) U4 t# M- R. h* t `& `1 W2 M7 D5 N$ d, l
& N" C0 ] `' E! R) z$ t' w7 \: O
9 C+ X2 ]4 y& f+ D8 c6 v3 u
% T( B' t" V9 |# K; a+ P/ L- K p
4 G# P& L0 K$ C' a( d% b% T
|
|