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