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