|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在阅读本文之前,请先掌握以下基本知识,不然请略过本文。 u* j) z2 P1 t
' M: U% \" C2 }; G l& o0 t
预备知识:
7 q j8 b: |0 P; ]& F, @4 X) ~( O5 `# v$ _# l9 ]- n. L; k; I
熟读LDD3前十章节的内容。
5 u" A4 x, E( g8 N4 F' o* p
. n8 _' E: C: ^1 E) _熟悉内核驱动模型(sysfs)和platform总线。
0 D) f7 f7 q0 \+ L) L& Q F' J1 j0 E- S& E ?
简要了解过SD卡规范。
* X6 Q# y* R: T& H4 T i
8 ?. m" N& e( O( Y3 L. {8 N3 H# U4 a/ t2 ?! q
1 G6 r/ U3 E- ]6 M* k$ C本文的内容基于如下硬件和软件平台:
+ N; J# x, H% I Q% i3 F0 v( }$ a2 A2 Y/ T( |& ^% B' q
目标平台:TQ2440
) o# h4 }8 N! Q. R
- N' `% }- X1 Z7 g- E" g) b: bCPU:s3c2440
5 I( ?4 w$ M" a, s; t) g) r$ g2 W/ X! x; l6 A/ y1 c
内核版本:3.12.5
( v* ^& t8 ?. e9 u( f
' T6 ]! b% r% J& X& X4 ^基于SD规范4.10,即《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10》。 L. h* a4 f, U- c% \: m% Z& W1 V
+ L" t1 Y A- l, A7 d" f+ |- o
* D3 z# x4 G( s3 ^' ?9 g0 I2 N$ H5 [: g' d
在阅读MMC子系统时,一个问题随之就会产生:当我们插入一张SD卡时,系统是如何识别到这张SD卡并将它注册进系统的呢?* M. }4 a+ u4 x* l7 a
5 b' q$ z( Z3 N: }* T# q# d) J
这一过程,源于MMC控制器驱动的不懈努力。。。。。。下面,我们从控制器驱动开始,来深入挖掘这一过程。
- c0 V a% x* T. Z
* B. n$ k. m' W! ^3 T1. MMC控制器驱动
3 l' K9 l& v. N0 [0 \; E5 R1.1 MMC控制器入口函数及probe方法# C- p% l- [7 E a" p
本文以三星的s3c2440上的MMC控制器驱动为例,来进行简要的说明。1 N8 P* z) y* d$ D# j5 t5 o
" ?, w( L3 z9 j! F
从MMC控制器驱动的入口函数开始,如下:& C E9 F3 g- j$ `& k; \8 l
& g4 D" m0 Q. r5 f* M, E0 R
下列代码位于:linux/drivers/mmc/host/s3cmci.c
& M% S F$ v7 @" b3 o
; ]( x1 q8 e, m K# Zstatic struct platform_driver s3cmci_driver = {. J. F9 O& i2 c
.driver = {6 ~4 K- O# L, s0 U1 D5 l3 m' v
.name = "s3c-sdi",0 c5 }7 G. Y' L/ }1 g, g' p
.owner = THIS_MODULE,
1 Y" e' h, y2 j .pm = s3cmci_pm_ops,9 I5 Z; D! |7 Q' g5 z) ?# O" R
},
) N) _8 w8 H$ k: {7 \6 Y& e' J; `6 ~# n .id_table = s3cmci_driver_ids,
( n6 L. Q$ b g .probe = s3cmci_probe,4 k: g6 A+ h/ y3 i+ H
.remove = s3cmci_remove,
# @9 F. n2 X4 [+ L. N. w3 r1 \( D .shutdown = s3cmci_shutdown,# ]0 L, ~* s. ]3 F5 K; V+ T$ w- l
};( V+ x [; ^; e+ m9 {6 _; ]( J( L" T
' L7 p7 T* k e. a
module_platform_driver(s3cmci_driver);
; @+ @2 x. ?6 y$ G) v9 M8 C( t. g1 y2 }- L5 o* m- H" r( V
这里直接调用了platform的驱动注册函数来注册一个名为s3c-sdi的驱动,该驱动将绑定mmc主控制器设备。
8 H4 {& s( Z% v" |3 ]" J- H为了让该驱动成功绑定MMC主控制器设备,需要先进行移植操作,具体可见:S3C2440 Linux驱动移植——SD卡驱动
5 R! K& k, [8 R# b
& b" i# ~9 I- l5 j* t绑定成功后,立即会调用probe方法,也就是s3cmci_probe函数,我们来看下:
3 @. J, l0 \: h! H2 A$ Y/ W
* o) S, m7 N- xstatic int s3cmci_probe(struct platform_device *pdev)6 a3 H$ j3 E7 E3 r+ q. K6 K z
{
$ i% P6 d, q$ c4 a/ @ struct s3cmci_host *host;
' d3 {: P- F- r( V2 f struct mmc_host *mmc;
! p# j/ ^& |" e: O! o int ret;
: @( g+ W: t z+ r9 D5 a+ ` int is2440;
2 P" B# ~4 k! ?6 |$ U int i;$ ]& F4 u& y) |0 n! F' U }( _
5 q5 {2 G" [- ?$ ~1 H! p0 L
/* */
9 x/ U2 \9 _) ] D is2440 = platform_get_device_id(pdev)->driver_data;: e) I" K+ x2 e0 Z: X c# N
4 H5 m" q( J# Z: L/ H" g /* 分配struct mmc_host和struct s3cmci_host */! u+ @, c1 m; [$ O5 u/ ^( `
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);, E3 P! `( J, Y- x# D
if (!mmc) {4 m! m0 k+ o' E) r) D+ m5 h _
ret = -ENOMEM;
- Z* {( p. d9 y& f% l p/ I3 z7 l goto probe_out;' G2 |0 r6 C" x- M7 u
}
' [; T4 {2 G5 X4 Y0 g, u8 D2 w' h0 a4 r9 g ^4 z
/* 申请IO管脚*/( z% A9 W7 T3 `' y% h7 m- a3 e1 O
for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {2 c7 J. X" T$ c; o! e1 G& C
ret = gpio_request(i, dev_name(&pdev->dev));
6 R8 Z* c: c: W if (ret) {& s' {& R0 l( M+ Z) ]9 }: | r) x
dev_err(&pdev->dev, "failed to get gpio %d\n", i);5 p2 H( w( f e6 a. \" Y7 B* M& b
. |8 R" N' k4 S8 L9 E# H _# p for (i--; i >= S3C2410_GPE(5); i--)3 `' N; H- i9 U5 P& ~# A2 Y
gpio_free(i);
4 H: |) a$ f7 @( S) ]$ T5 a2 t4 T( R* y4 U
goto probe_free_host;
z" s: r2 \7 M+ M }+ I: ~4 a4 W( n/ ~# x" a0 J- D6 {
}
# p& V8 g! i2 t8 ^' Y! l+ w# N3 [
host = mmc_priv(mmc); /* 获取struct s3cmci_host指针*/9 |4 ~% n% J D @3 G. @; M
host->mmc = mmc; /* 保存mmc指针*/$ x: y1 c" M2 m R/ i& M
host->pdev = pdev; /* 保存平台设备指针*/4 n- L$ Y1 c9 P0 p/ F
host->is2440 = is2440; /* 是否为2440*/
' r' Z' c2 s5 k- x8 Y( Y; j
6 y7 f) _* m% u" x. ~7 V host->pdata = pdev->dev.platform_data; /* 保存板级设备信息*/- P1 [, ?: T- h- ^
if (!host->pdata) {" \. E4 H o% Y% x
/* 如果没有板级设备信息,给出默认的*/
$ N% r: d( ]) [3 K8 u0 T9 i5 \ pdev->dev.platform_data = &s3cmci_def_pdata;
4 ^5 j2 c7 p1 ]* j' W N host->pdata = &s3cmci_def_pdata;( I* c3 [3 e' N Z
}/ W% V9 a# g0 H
$ K, f5 _: P/ Z/ k /* 初始化自旋锁*/
) @1 A5 Q3 a: t$ [ spin_lock_init(&host->complete_lock);
! T# W+ b% b: C. [3 k /* 初始化1个tasklet,执行pio_tasklet*/! C1 {' P, |* }, Y4 J, _$ S1 J
tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
$ Q# O1 F7 Z2 A8 I1 u- `& V: }& Q& i5 g
if (is2440) {' u6 @: L7 X" U
host->sdiimsk = S3C2440_SDIIMSK;$ F1 I2 ^+ _4 p; Q" x
host->sdidata = S3C2440_SDIDATA;$ N6 ~ ?" @3 p4 c1 }
host->clk_div = 1;2 l' e4 [2 v' E9 ^6 _8 M% K4 x
} else {
5 v& q. F. u! C( o host->sdiimsk = S3C2410_SDIIMSK;' p, r$ r. ~+ a7 F) F6 s
host->sdidata = S3C2410_SDIDATA;
8 \6 C7 D4 x9 `. ?" i9 U& R# ?! k host->clk_div = 2;# w! w1 N' \* E% K3 O. {* Q# ]+ h
}+ ?5 V6 q. X% k+ a
8 f M6 C2 l- j8 a- e; F
host->complete_what = COMPLETION_NONE;4 C, c; W# H) Q. {/ s2 }
host->pio_active = XFER_NONE;, R/ p ]0 {/ M
/ v: i( Q8 Q) O( S/ x8 I/* 板级数据可以决定是否使用DMA,也可以通过配置来决定*/6 p5 N3 X0 [; a
#ifdef CONFIG_MMC_S3C_PIODMA
6 i1 X \* B$ h! N) S0 ] host->dodma = host->pdata->use_dma;
2 C, e {. ^; f J8 V* H$ u' Y1 v#endif: Z" Q5 x6 |7 t6 P4 g) v3 H
h* e/ C9 |" a$ ?% }3 H' m
/* 获取寄存器资源*/! j+ N3 W& }' M& o' K6 q! ]9 g
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);- |" }5 s& T9 b+ {, ~
if (!host->mem) {6 P; P* n' X0 N9 I5 O9 H
dev_err(&pdev->dev,
' Q' g* c* }+ D. I9 ?4 i "failed to get io memory region resource.\n");
% K% X! n0 Y# ^7 j8 Y- j0 B0 {1 z. B$ `; B+ L5 p* p
ret = -ENOENT;) ^8 u, O/ S" ~; v" T
goto probe_free_gpio;
4 {: ?" i9 E% G) C0 n% c5 E }+ a( A$ F7 b6 S1 v8 j' y5 P E, G
/* 申请IO内存空间*/
8 i! c; ^( b3 K$ N5 T7 [% { host->mem = request_mem_region(host->mem->start,9 f; G) {: M. L( \
resource_size(host->mem), pdev->name);4 D( I9 e/ _/ D* E. c& \2 e* J0 G
! Y+ V& F4 {/ L" `$ P if (!host->mem) {
( T$ D2 j" S/ u% {: ~5 f1 }5 y dev_err(&pdev->dev, "failed to request io memory region.\n");
# M; s, }! w/ J$ S! T ret = -ENOENT;
+ `) f% [& C. G0 ` goto probe_free_gpio;
$ g3 [: k1 v6 @9 d4 K }- Y% {% K* w! L C
. D6 k) ~% `+ X8 o" H /* 映射寄存器*/
) D1 x# b+ b; \ host->base = ioremap(host->mem->start, resource_size(host->mem));
, d6 T1 S8 O% c6 j7 }3 M if (!host->base) {0 b+ P3 \& t7 {+ x
dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
1 b* m6 E& h5 F+ I ret = -EINVAL;
9 \+ R8 H" k8 V4 a3 x# r' @0 O7 f2 T goto probe_free_mem_region;1 t1 t' P6 f5 R! {8 _
}- R" }4 y5 U/ N. C0 ]. |' k
9 `# E1 k2 p4 Q( ]2 g# V' u /*获取IRQ */- q5 g2 l7 D7 Q2 g
host->irq = platform_get_irq(pdev, 0);$ P& T8 {, [) R/ v" z
if (host->irq == 0) {7 d6 R; L4 _8 T/ v" ^9 I
dev_err(&pdev->dev, "failed to get interrupt resource.\n");) ~3 j) b! ]$ `% s$ b0 E5 z2 `
ret = -EINVAL;
/ v$ R# r5 Z0 s* q. C7 b* L goto probe_iounmap;8 H O; u9 P- S9 Q; e' H6 m3 V; |
}* p) N( j1 Q2 ]0 H6 t
/ y" B. U, N2 t- Y# ?! L0 R3 N1 P /* 注册IRQ*/, b V. c9 F- S- U: F3 d% s/ l( [
if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {5 H2 X, M. S- m+ G. Q0 X& {4 i
dev_err(&pdev->dev, "failed to request mci interrupt.\n");
0 A3 F5 G/ ~2 f1 Q0 | ret = -ENOENT;
F7 \2 ?( w& H9 b; g goto probe_iounmap;
) \! Y* s- z1 G1 P }
( X# T7 F7 u& e4 @1 p; z6 t; C* T1 _: B. k
/* We get spurious interrupts even when we have set the IMSK9 w/ j+ x2 A3 @/ p
* register to ignore everything, so use disable_irq() to make' N+ [) |* u) P8 ~1 V
* ensure we don't lock the system with un-serviceable requests. */6 b! W* x7 W- I' w* }
& q7 w! I; Q) v" B7 d) v' `
/* 禁止中断并设置中断状态*/
+ @3 \1 j$ [' p# k6 W disable_irq(host->irq);* o ]9 K, e, W% D& I7 ?; K9 F
host->irq_state = false;
3 {. V8 u( l; n5 N
+ U7 E$ Q6 z- H' ` /* 使用detect功能,则申请gpio */
' X7 f; v7 U* T L+ B# E6 B; y if (!host->pdata->no_detect) {
& I6 p- w& ]$ `& T ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");5 L$ B; o3 V9 I* W) \" u% G) ~
if (ret) {8 h! W* c6 Q y- ~) N: d6 I- l
dev_err(&pdev->dev, "failed to get detect gpio\n");
. V* X; b+ X/ }% L4 w5 ~# Y goto probe_free_irq;
( i' u7 @9 `% e: v" o# D( F } \/ G }
3 b/ }% v" {+ }4 O, P /* 根据gpio获取中断号*/
5 h" s/ C( m$ ?, x0 v7 k3 W- i host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);. G* G4 z8 p$ Y* Q0 ^
: b w! F/ Z; N# V) p
/* 中断号有效则注册该中断*/
3 @# b6 I. ?4 O( i3 d% g if (host->irq_cd >= 0) {5 V! r; v2 \2 s! ^5 w3 g' B8 J4 j: {
if (request_irq(host->irq_cd, s3cmci_irq_cd, R" d; C7 l$ k$ u& p. s
IRQF_TRIGGER_RISING |0 g& ]! w4 q+ c- f/ y2 U. y
IRQF_TRIGGER_FALLING,
0 s$ f$ v; x- d& F" X DRIVER_NAME, host)) {: v7 x3 {( v- W5 q1 F
dev_err(&pdev->dev,( }& @1 q( ~$ V' V% K
"can't get card detect irq.\n");
) e; ^5 ^0 s! @6 t) [$ ~ ret = -ENOENT;
9 y3 b: F7 \: F9 T5 E& M goto probe_free_gpio_cd;4 r) |. z" v2 i
}
! Z5 \% ~$ ]$ p& p* S( y } else {! E( ^( M6 w& w1 z }
dev_warn(&pdev->dev,
1 F' T- C; N2 } "host detect has no irq available\n");
* i$ o8 ^' ?3 e6 T# |- q; O# Z gpio_direction_input(host->pdata->gpio_detect);
$ o% Z. l; X$ m }
& `' L, D$ [% W( e( [ } else( k- M3 S' [- v
host->irq_cd = -1;! m/ y5 Y! i- r
4 a5 n6 D, V2 b: ]& A% r 5 M! J1 D3 z8 j1 x( E# P
/* 使用wprotect功能,则申请gpio */( C6 f# _) H- g$ }6 H; ?0 D# V$ k( i
if (!host->pdata->no_wprotect) {% ~) N" S! b" {( X) y- K6 m- |3 U
ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");1 h; }1 L4 @$ ^4 K
if (ret) {/ l9 c5 N. \. A9 ?; Z. Q8 O
dev_err(&pdev->dev, "failed to get writeprotect\n");
4 G% l+ _7 F2 f/ ~2 W goto probe_free_irq_cd;7 z( x9 G3 l' ^; f6 g
}1 o3 |. ^/ N6 i/ ] C I
1 Q/ F4 e" s b$ \% ?/ `3 L3 u gpio_direction_input(host->pdata->gpio_wprotect);/ F! y6 i9 V1 p0 O/ p3 r3 n9 W
}
1 n. d: `; Q% t6 f8 Z! x7 F6 A8 n$ m# E
/* depending on the dma state, get a dma channel to use. */- ~8 k9 g2 c/ l7 [
- x1 M" H6 I# C% a- d; [* L. r! E /* 如果使用DMA则申请相应的物理DMA通道*/$ R0 Y4 {; ?6 x3 p2 |# x# T
if (s3cmci_host_usedma(host)) {5 g% b5 T/ [ C) }7 h) e
/* 返回值为通道号*/
% A# T. R7 P: e, ?; ]5 b# q host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
2 G, i0 J* f' V* i' d host);
9 n; C+ y! Z8 f. p$ J( [ if (host->dma < 0) {
0 Z& ~; {; B* i1 J. y2 S. E8 e v /* DMA申请失败,尝试使用IO操作*/. k& j3 `3 a2 N6 Q
dev_err(&pdev->dev, "cannot get DMA channel.\n");
0 F6 N0 j% b1 ~: l0 g$ N+ e if (!s3cmci_host_canpio()) {# S& Q+ `. {; d+ ^: c
ret = -EBUSY;+ v+ h% ]6 ~: x* n) F4 c
goto probe_free_gpio_wp;: N! T* \; C1 x7 O L; V
} else {
6 ?9 |# l& k, f$ D dev_warn(&pdev->dev, "falling back to PIO.\n");7 `1 |! U0 ]; }( i1 [4 S B& y% U
host->dodma = 0; /* 表示不使用DMA?/
- W% C, R& m3 V7 ~: I- ~7 D }: \9 o+ f" B8 R) G: B2 |
}
( T! B+ S! J9 q# T. k }4 F! t* v4 t: v' l1 E
/* 获取clk*/& ]3 L2 \2 p+ l/ g
host->clk = clk_get(&pdev->dev, "sdi");. M, i+ _3 U+ |# X$ V/ z2 T
if (IS_ERR(host->clk)) {+ q! x Q, W% [
dev_err(&pdev->dev, "failed to find clock source.\n");
7 z7 X! O/ B) C0 ]/ S ret = PTR_ERR(host->clk);
+ ?$ }0 u5 e! e& K; F& V9 m host->clk = NULL;- t* U- M: e6 v* x! ?9 x+ ~
goto probe_free_dma;+ s$ y' D& c: ] R5 W7 @1 T& {# O
}& X( e9 Q8 I6 v
5 P2 `# R) g9 ~ /*使能clk*/
6 n7 y; U7 L/ P0 W1 O ret = clk_enable(host->clk);& H3 S# _4 H0 {% I; [1 |2 }
if (ret) {
0 h: S8 W0 t$ O7 P# V0 e& y dev_err(&pdev->dev, "failed to enable clock source.\n");; d$ T9 k/ W- ~/ l+ Y: G7 a
goto clk_free;
5 X: s; \. z T5 ~' ~0 v }) v1 E- N& J; c
9 p% a# ~, \& T/ R
host->clk_rate = clk_get_rate(host->clk); /* 保存时钟频率*/
; Z# G0 J0 y _0 }1 v9 ?; `
- |/ ~1 D, [ a! G: j /*开始初始化mmc当中的字段*/
; e5 V! G2 }/ l$ X! K9 f8 t1 y+ A2 k mmc->ops = &s3cmci_ops; /* 给出控制器operation函数集*/
7 ] i6 Q) L0 |7 @3 S mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;" J% K2 v$ Z6 b& I% B
#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ
) s1 y1 _0 M6 p( ~( H mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
* \, K" m$ J4 u/ T, c5 K+ _#else
) v+ r! o- ^2 ?4 i8 o; r1 w mmc->caps = MMC_CAP_4_BIT_DATA;
: d6 g: i) R x9 r- `#endif( m% a# @6 I( I/ [' I* b3 b( p
/* 计算最大和最小的工作频率*/
% M9 r9 B2 B; E1 Q( C" v mmc->f_min = host->clk_rate / (host->clk_div * 256);3 o" X3 f9 c$ o) k& M
mmc->f_max = host->clk_rate / host->clk_div;
- \+ @% U3 S" g$ O% m' V8 x2 l& z, I5 N' G- h# s, C
if (host->pdata->ocr_avail). |7 O9 m$ a! A* s2 I" d# m
mmc->ocr_avail = host->pdata->ocr_avail;" ?! E+ i# S; ~, y2 C7 t% }; ^9 J
( K' |' s. g* \3 N mmc->max_blk_count = 4095; /* 一个请求的最大block数*/9 `0 u9 G: C j
mmc->max_blk_size = 4095; /* block的最大容量*// q9 i- n9 E( T M! B
mmc->max_req_size = 4095 * 512; /* 一个请求的最大字节数*/* c& T. v4 P7 T1 I( P
mmc->max_seg_size = mmc->max_req_size;
! |9 g: [# P" L; @0 `1 n5 ?* V7 W. B1 o
mmc->max_segs = 128;
8 k( K# w8 i; c, q8 U7 s5 R+ m/ G3 e# C7 X% ~
dbg(host, dbg_debug,
- R) u( {8 R6 `, ^ ? "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",$ t+ s3 p$ O6 x; {2 H0 g& k
(host->is2440?"2440":""),
4 C" L- V+ l3 v O7 F host->base, host->irq, host->irq_cd, host->dma);
/ l5 [0 ~% H3 ~2 D! p8 K8 t. s3 r. P; P
ret = s3cmci_cpufreq_register(host);
5 X9 {+ `; R: T if (ret) {
" w$ _7 F8 n7 N' Q" c+ F4 P dev_err(&pdev->dev, "failed to register cpufreq\n");
1 q1 [- Q) B4 ?: d S! f goto free_dmabuf;: v; `0 K/ U: w7 N3 y
}
7 G1 @' i* H: r: ^0 b' {3 t
, X" G+ U( O# Z5 P1 s /* 注册该mmc 控制器到子系统中*/5 J* g" _& I Z8 l
ret = mmc_add_host(mmc);9 E/ r/ i2 m1 d
if (ret) {
/ C" G o t& B- H dev_err(&pdev->dev, "failed to add mmc host.\n");9 L0 X, x$ P2 s2 `2 X. K8 O8 B
goto free_cpufreq;. Y. x3 C$ M) c+ Y" u
}
$ m7 `% G; U0 p: N2 y
$ {* W! ^1 j- u v( f a s3cmci_debugfs_attach(host);
0 Q- A7 A2 ~% A5 a1 m; t! ~( d/ A
; i# o4 T% \6 T9 x( ] /* 设置驱动数据*/ X% O, d- J" I
platform_set_drvdata(pdev, mmc);
8 m5 L5 f6 {5 d/ ` dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc),; e& a* }. A+ v) Z+ {! b: _
s3cmci_host_usedma(host) ? "dma" : "pio",
! I9 h/ {8 U! p* g mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");# x; E, r! f: P! z' |
$ w4 n; S4 A4 }" W- o return 0;% t% p5 m! e& m" K7 Y
! I3 I4 q2 A0 U
free_cpufreq:) L/ H+ N2 S9 X c- l ~ g
s3cmci_cpufreq_deregister(host);
; q# ?( d5 D: V! `& g- q
# g" X6 L9 _2 U% l4 o1 W free_dmabuf:
, X8 n- Z5 |+ M( d% C' d6 a clk_disable(host->clk);2 i2 D0 ?/ G0 \8 O9 [
& s" }6 D+ `% z1 }/ {; t clk_free:
: O" N |- K0 c& T clk_put(host->clk);
. P8 m5 H: S r" M+ q! c5 F" }# V# L; D5 Q q
probe_free_dma:
9 C9 |6 m# [) d( s) E if (s3cmci_host_usedma(host))
1 e. t' K- P3 |: s s3c2410_dma_free(host->dma, &s3cmci_dma_client);4 [: r% i! x7 R+ o5 M2 w
, l0 [) p/ t. a probe_free_gpio_wp:
) N& O% X% W$ n2 y5 j* u if (!host->pdata->no_wprotect)' s( Z, V6 ~7 {" R8 M6 a3 Q* {/ ~
gpio_free(host->pdata->gpio_wprotect);& H; C$ l+ O4 A3 l% O' `* d; t# ]
! m& O( P6 ~2 Q d* f, `1 R0 m
probe_free_gpio_cd:. t- U8 g+ R+ {, h I U X
if (!host->pdata->no_detect)
6 M( }* O+ ^( D. `* _+ x gpio_free(host->pdata->gpio_detect);% F8 g7 S$ I0 K! h5 {3 t& C% t
6 t; N4 ~$ L% N$ N2 C' Z6 Q probe_free_irq_cd:
" o/ Q6 ], y# y4 V N3 ] if (host->irq_cd >= 0)
`% N7 _6 ]3 J' g( V" w, f free_irq(host->irq_cd, host);
4 c; X+ p+ @. f# z# I' N, B; E
2 ^: v. C& N4 i8 ^( j6 l. W probe_free_irq:
$ r$ B2 R: @+ D% b: ~! V$ b free_irq(host->irq, host);7 G- F m; X9 I7 b1 _6 w
) W9 C0 d" v, }, [0 y6 d/ _ probe_iounmap:6 i" T& z% v/ K7 s [3 G
iounmap(host->base); n6 b6 x- G# f; s
- Q2 { b# V& r, C2 ?9 f& A- R7 ~
probe_free_mem_region:
' s& T, c" {4 b: M6 m9 D release_mem_region(host->mem->start, resource_size(host->mem));4 m( L# k% v+ q( n
. l$ e/ X1 a* p$ u probe_free_gpio:
/ w& `' }# v B! }& ? for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
/ V4 l0 |% a1 i2 M gpio_free(i);
Y& N/ q: T5 Y- C+ X
. Z; \% i* M: V6 V C' h! A probe_free_host:
1 j" [, b6 [3 v9 { mmc_free_host(mmc);
# S) s+ D$ |! o7 u
" y5 n" J9 F. }/ I! p( Y% V8 Z5 y probe_out:
& k5 z$ r4 I8 {( c3 V, e/ s return ret; p$ r$ a# p$ k+ f6 Q) h" r) x
}
! D4 J+ J, N4 Y% z1 k+ @; T* @( H. h6 d7 Q8 `& t- k
这个函数想当的长,差不多300行,我们来简要分析下:, \! K# C6 P; s4 p. b; W) l/ L
第一步,该函数首先调用mmc_alloc_host分配了struct mmc_host和truct s3cmci_host和两个结构体的内存空间,其中前者包含后者,而后者有一个指针指向前者。, [4 j) B/ N9 z9 _/ ]) q) }
( ~$ h Q4 z" B. I; k. {
该函数的详细的实现如下:
- N( t' g/ f/ x5 }" y5 M8 M# x7 Z
: |0 |8 q7 H! r5 n, v. S1 M/**
7 C% s, M0 ^1 W) ~ * mmc_alloc_host - initialise the per-host structure.
8 R4 Z& d8 ^8 G * @extra: sizeof private data structure; n( e2 u6 G0 L+ s$ R8 V- S
* @dev: pointer to host device model structure
2 O2 T- }7 F. \. q* s0 A1 i7 S: j) ?, Z *( `- n# V. _1 |. c% i
* Initialise the per-host structure.
- \* a/ ~+ w0 ~& g */
: n2 F& _+ b" Q8 `struct mmc_host *mmc_alloc_host(int extra, struct device *dev); t' e2 y4 f2 d& z
{9 t/ b7 l: {) `9 Z. M& ^# d
int err;
X6 l' ]! ]/ i+ V5 A struct mmc_host *host;3 Y; Y: ~* A- o# ]. ]3 ?
+ c8 O4 _. u$ L4 h3 O( y
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);) V; [# ?+ p* ]7 T
if (!host) i* j$ P' v$ N4 S1 @: K4 S: z
return NULL;
! I! q. @& y2 i2 {
' O; V5 R; @( q- f4 r( } /* scanning will be enabled when we're ready */
' C0 Q) E: g5 H% n; v$ A1 n host->rescan_disable = 1;
3 Z1 p2 @4 d1 Q2 o" Q& L idr_preload(GFP_KERNEL);
% e* @% O. Z; ~0 ]5 j b2 L h /* 获取一个idr,通过自旋锁进行互斥保护*/
/ r' y* A; J1 j% F spin_lock(&mmc_host_lock);. e1 }4 V% |5 }! n' z. [
err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
0 D" s! F) W, k8 O8 m) t if (err >= 0)
9 q. V& |! \9 J+ l. c; { host->index = err;) e: k& ~: S' f- s5 l) l" F! p& U
spin_unlock(&mmc_host_lock);
/ @$ n, }; @5 v, a) D' r8 d idr_preload_end();- n2 |4 U1 d, P; O2 J9 ^
if (err < 0)
" R6 X) B" C L+ y$ Z. W5 F) Z/ S goto free;; x, U( W( |* q+ x8 A3 Y$ s3 V
$ k. \/ ~2 \& w- U /* 设置设备名*/4 y% e$ I& ~. L( E, _$ l4 F" G
dev_set_name(&host->class_dev, "mmc%d", host->index);
7 z @6 w, t5 Y" a3 g9 O3 s9 N$ M
host->parent = dev;# o8 W' S6 F P' j: L, a7 |6 t
host->class_dev.parent = dev; /* 设置父设备*/
, ?3 A2 u B2 J6 z host->class_dev.class = &mmc_host_class; /* 设置设备所属的类*/. [; c9 N7 u! ~* k5 C9 S' D
device_initialize(&host->class_dev);+ B) W3 W" z( L( X( b+ d W
4 T; l" E$ e: N& U mmc_host_clk_init(host);& v! G! D7 m3 {$ N
5 w" ~7 D Z+ B' R
mutex_init(&host->slot.lock);( H+ f$ |3 K; Z7 J5 l
host->slot.cd_irq = -EINVAL;5 I- H' w3 X2 v1 \
: I4 X; {: h3 \7 y. W. q
spin_lock_init(&host->lock);
4 I7 S0 v, y4 ^; v% V init_waitqueue_head(&host->wq);# Y! n4 S6 ^8 l
INIT_DELAYED_WORK(&host->detect, mmc_rescan); /* 创建延时工作队列*// _, U1 [- H; \; _1 D
#ifdef CONFIG_PM
- z/ y" x9 a2 S, l host->pm_notify.notifier_call = mmc_pm_notify; /* 通知链回调函数*/# O6 g- \" v+ u2 n5 b1 D
#endif
7 p! t( S$ A1 Q8 G! b0 C7 u/ |! a% N* Y, E P
/*' X1 D, {, } J* F- d( _, @- ?' D
* By default, hosts do not support SGIO or large requests.. `3 E5 q" A7 d& m
* They have to set these according to their abilities.
* u; r/ c" L( h */
( j, U0 V% d' t- G. y& p' S0 F host->max_segs = 1;! D" f' w0 o7 H; q( ?
host->max_seg_size = PAGE_CACHE_SIZE;* z$ o$ z/ F- g( }) j
) H" e g( @6 d. ~# T$ X! Z host->max_req_size = PAGE_CACHE_SIZE;) M z; L# L1 N& C
host->max_blk_size = 512;
' c" ~8 @: l7 E. _! P8 X2 y) m host->max_blk_count = PAGE_CACHE_SIZE / 512;9 r9 e+ h) F9 F. c5 f7 z
" W# g' z1 t* ]! B return host;$ x) i* x# O! c" ?. i
! ?# J; _1 B' S+ g( \% jfree:1 Z% T2 ~" d, d) }" d! i
kfree(host);
1 h, z! W( ^: T$ {" i return NULL;
$ Z* {: H; F8 o# Z6 u& d4 x}
1 _# {0 O' X; r/ ]6 w. a
4 } x- D3 k) } W" S$ XEXPORT_SYMBOL(mmc_alloc_host);
% @" j3 j1 [& `, }& g' N0 |9 K5 W 其大致过程如下:分配相应的结构体,设置标志位rescan_disable为1,表示禁止扫描SD卡。8 n2 X% |0 S5 r# u1 Y# f
随后利用idr分配了一个编号给该MMC控制器,并初始化了MMC控制器设备对象(device),
% z( m# c$ J2 W' ~# U- ~' ]# S" _" O5 E4 S/ Q
初始化了控制器时钟信息。3 G' X; a, t4 m
8 X1 }, g6 Q; @/ `. j
很重要的一步,初始化了一个工作(host->detect)为mmc_rescan,该函数将负责执行对SD卡的扫描,该函数将在后面描述。
" W( H# s$ i4 w, N( A+ u
& ]+ b+ e* H+ k u 最后,初始化了MMC控制器的访问块大小的能力。
; t* b) j2 ^! \' I- J y' L5 E0 s: g3 u+ q+ w8 b- t
第二步,根据控制器所使用的引脚,向gpio子系统申请该引脚。
9 D: }3 z7 }6 ]1 ^1 B3 g2 E2 K5 t! u; z6 I X
第三步,获取板级设备信息(pdev->dev.platform_data),并初始化struct s3cmci_host中的一些字段。
' O/ K2 ~' m% g# ] @, ^
' {; K$ j) l% r* K+ Q o第四步,控制器驱动决定时候需要使用DMA进行传输(host->dodma)。) X) g% J) Q; x$ X% S
8 S+ O, R! g2 d' G5 _) V' B
第五步,和其他驱动一样,获得寄存器空间,申请IO内存,并完成映射工作。
: b7 P- L- d' B7 B5 V+ U& [* Y+ v
0 [- U9 w" [' _2 S& a$ h; q% B第六步,和其他驱动一样,获取IRQ号,并注册该irq,中断服务程序(ISR)为s3cmci_irq函数,并且关闭中断。
) U7 X/ ]( E; x' g7 u/ d( H# x$ d- ?! D1 a0 W
第七步,根据板级设备信息,判断是否使用探测(detect)功能,如果使用则向gpio子系统申请该引脚,并为该gpio注册中断服务程序。 H% t2 E, }' i1 V& i
# N1 f' a# ^, g+ r0 r8 ~8 L- f
中断服务程序为s3cmci_irq_cd函数,上下沿有效,该函数就是用来判断SD卡是否插入卡槽中的。。
+ Q3 p l& z6 L! ?: j8 y1 I: w9 z4 K/ C% V, z, ?
第八步,根据板级设备信息,判断是否使用写保护(no_wprotect)功能,如果使用则向gpio子系统申请该引脚。
3 M; E8 Y: J; s4 w
$ V: Y! Q* l8 V ^) e, L" K第九步,如果使用DMA传输,则对DMA进行相关的初始化工作。4 x" }) \( g( H) z$ q" t, t
# y) e% P/ ~4 o6 u4 O: y- R
第十步,获取clk,并使能,然后获取时钟速率。
" p" N7 a) h6 W
# M' s! D) U) b' w; \第十一步,设置控制器的访问函数集为s3cmci_ops,该结构体包括了MMC控制器行为的抽象,非常重要,我们来看下:
! d* P9 e- z8 E% { `4 ?4 g+ M( R0 h5 J' B2 Z8 ~$ _
static struct mmc_host_ops s3cmci_ops = {
% P L' I5 D( o% m' F% ?4 @ .request = s3cmci_request,& O- P+ L+ u% T4 Z
.set_ios = s3cmci_set_ios, h! Z0 g8 {- d, a. Z: ]: f8 O
.get_ro = s3cmci_get_ro, /* 判断是否只读*/5 }, F7 S) C n: F1 n# u+ t2 F
.get_cd = s3cmci_card_present, /* 判断card是否存在*/% @4 |' |7 F) B- _7 D
.enable_sdio_irq = s3cmci_enable_sdio_irq,0 r* U g) ~3 D/ j4 T
};
* Y2 i. ]- c0 p$ A( ]struct mmc_host_ops其中包含了很多MMC控制器行为的抽象,S3C2440的MMC控制器驱动只使用了5个,功能如下:
- w4 L2 e/ O( E h6 G8 w 第一个是请求函数,用于向SD卡发送命令,并获取应答。1 m4 ]% R( R, ^6 E# k" P9 l% f
+ T, z) \: q; j8 e# c1 C
第二个用于设置MMC控制器参数。2 ? G1 B& ]" p* B% N
& H# m) s a# L6 l
第三个用于判断SD卡是否为只读。: a9 Y* ?/ ], \8 U! ?9 t; v) {
) @# ~: t! G2 T9 s 第四个用于判断SD卡是否在卡槽内。' n! M; _2 o4 G" D. d
0 Z; P. w$ p! p: n M; W 第五个用户使能sdio中断。
9 Y" Y( H. X1 k5 g9 p+ l, m, \' U' u3 J* k* x9 `- g9 d
接下来开始初始化struct mmc_host 结构体里的字段,这些字段的具体含义就不细说了。( x: I+ f* e7 R6 ^
) s; B2 D, q+ y3 B! a) O) h8 q
第十二步,调用s3cmci_cpufreq_register注册一个通知链,有关通知链的东西就不在这里赘述了。$ q( g$ ]7 \6 Y+ F1 R
1 y( Y' u( B# Y0 P/ W第十三步,调用mmc_add_host注册MMC控制器,该函数将在1.2小结叙说。
( U& d0 }4 z( ^, s, H' o
' l" C/ b( m, D- z! I第十三步,调用s3cmci_debugfs_attach向debuf文件系统注册有关访问接口,debugfs有关的我们就略过了。6 t( B; K. S w0 N, d7 s
8 _. x+ V0 `# w( b4 o7 @/ G a6 I
第十四步,保存mmc至驱动的私有平台数据中(dpev->dev->p->driver_data)。7 i; N; r3 a' x; D. Q% h! Z
) b# R7 j; t3 T9 Y. ~7 I, p5 ^
( b2 J2 x% u ]8 m9 I
( C% }0 D# L3 ?/ P作为MMC控制器的probe方法,自然而然的需要注册MMC控制器,从上面我们可以看到在进行大量的初始化工作后,最终在第十三步注册该控制器驱动。
% i a. x, J5 W. T5 Y( ~$ ], u8 t7 K4 p, k
下以小结我们来看看这个函数干了点什么。
5 x0 H6 l9 k" ?0 | A
! v% } f! [* B& D1.2 函数mmc_add_host的使命8 K2 ?' u! [& b7 @4 L$ s4 H( j
下列代码位于:linux/drivers/mmc/core/host.c2 b) c1 c8 {! f( |: Z
1 A" x; [, U( i! W: D1 I
/**- L' j0 T6 n. w6 G! ]8 |7 V$ _/ v
* mmc_add_host - initialise host hardware1 Q! T; u- m$ N' p( i, K
* @host: mmc host
: I' j9 ]3 Z; b, e6 K8 o */ y; d6 t; @& s2 ~0 v
* Register the host with the driver model. The host must be9 [3 X' i. |3 O% h- t5 [
* prepared to start servicing requests before this function
4 p& W0 k1 t# r; q7 `/ F) V * completes.- y( }) }3 X) Q
*/
) o8 v x! |! F0 Bint mmc_add_host(struct mmc_host *host)
2 G- ?2 S( k* N$ r1 A{
7 C, J! F7 G& @ F H; j int err;
+ @9 i$ R( E% D- u
/ |) F: L" p$ Z( R3 N WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
: p* ^( ?. U/ N9 x- E% d2 ?' { !host->ops->enable_sdio_irq);
9 P% V) x G5 P; ~0 e
$ p& s8 T5 S, n /* 注册控制器设备实例*/+ Q% g9 A' c* U/ g% L1 l
err = device_add(&host->class_dev);
' f) H1 q3 l" g0 d if (err)
7 A3 A3 R, l1 v+ i# D* l0 w return err;
, E7 | m1 F l' F, f% ~% w5 B" u$ Z) M: E' @5 d! Y/ C
/* 注册一个led trigger*/
% h1 W- [" c: l: L9 { led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
+ `, d3 e! c0 Q7 |* e
! D4 m' g' N$ x* o5 B#ifdef CONFIG_DEBUG_FS5 r' n% P1 U+ [% I7 X
mmc_add_host_debugfs(host);* p8 V7 Q3 d! w* v: a
#endif
n- q9 ~9 |7 e1 V/ c4 i mmc_host_clk_sysfs_init(host);( h$ B) v- g9 p; y ^) j. g: }
H: G+ U4 y3 ]/ Q! [2 ]0 X( n# K
( O* p' F. o" z% f6 {* }1 q
mmc_start_host(host);
, w$ o: S+ I& L. b/ C0 L 6 b& i% X7 S1 c9 X
/* 注册一个通知链*/* [) O& G9 l8 Z3 |
register_pm_notifier(&host->pm_notify);) n# L' K# z! k/ {5 H
( G% g, G3 T- m/ m) s( e
return 0;6 R8 C/ [4 ?# F) S
}
7 [ j1 ~) L9 e- S/ l3 F) c# `
/ t0 E+ N8 ]. v% D+ OEXPORT_SYMBOL(mmc_add_host);
8 ?; p% V( V: K% d0 o; R5 ?* b
6 X+ A% E( Q7 Y, P该函数首先调用device_add注册了一个设备实例,随后注册了一个led trigger。
/ K; G- j( A! R! i1 K并调用mmc_host_clk_sysfs_init,后者利用sysfs向用户提供了门控相关的信息,
3 ^( @* I9 ], Z* u! S c; G8 Z* F2 b9 v
接着调用mmc_start_host来启动MMC控制器的时钟,并且判断SD卡是否已在卡槽中。" s$ B3 g- o. H" ?& ]
% Q$ s: V# |' T% s; p6 M1 Q- z
最后,调用register_pm_notifier向注册了一个用于电源管理的通知链。% d% E; ?$ K: x8 e7 d3 e+ n" s
" o" ^ a" Q- _! i很明显,这里调用的5个函数,我们需要关心的是mmc_start_host函数。
0 q+ U' p8 k+ L4 k2 l% j0 Y+ x7 Q' A+ {: c' W& b( p
% O2 c/ {& x& B( L* d
8 D1 H+ P: r% @& N, o
void mmc_start_host(struct mmc_host *host)+ y6 @7 ^5 _# F4 r% r: A" U8 }
{
( B3 [8 |: f, b) t% v# }0 M host->f_init = max(freqs[0], host->f_min);
$ H0 ?. w% F7 z/ H/ t4 G& h& E; J" Y( A host->rescan_disable = 0;+ m. f& n" R& k1 a
/* 必须重新scan才能上电,则关闭mmc控制器*/( b* n( S O: F7 M/ Q) F# e1 E
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)* R: d( Y% H: n
mmc_power_off(host); /* 关闭MMC控制器*/
9 |+ d4 G; d5 D& s0 o; {" u else8 R9 I X9 K. e- f, k. Y( }1 o* |. R
mmc_power_up(host); /* 打卡MMC控制器*/
; t0 H4 k1 f. b9 e3 V- E5 t mmc_detect_change(host, 0);! M6 Q8 l, G( U) W
}
. i; _$ a' ?0 M3 |% ]& Z& a- RMMC_CAP2_NO_PRESCAN_POWERUP 表示需要在上电前扫面SD卡,) {$ Y- w- f: [. ?- {
如果定义了该宏,则需要关闭MMC控制器的电压,否则打开电源,该宏默认是不定义的。
% M. h$ y: e2 U0 N7 r- _$ ]. E$ Y8 J1 g Y' b8 E
mmc_power_up函数还是比较复杂的,不过它不是我们关心的重点,简单说句,该函数最后会调用在1.1小结中MMC控制器的抽象行为函数中的+ V" P: M9 K( j1 q; p% X
4 D+ t' ] I& h) ~1 Gset_ios来使能MMC控制器的电源。/ c2 p5 R0 E* [2 E0 P9 N1 r
& h1 T. s0 p% b7 J该函数最后调用mmc_detect_change来探测SD卡是否存在。
5 I: I$ z, w9 J) a3 D- n) \8 g$ P( s! P/ I
/**0 t% O v# u3 L& e, J8 [2 N
* mmc_detect_change - process change of state on a MMC socket. |$ U( A# X$ J0 e+ v" S9 f7 Q8 e) }
* @host: host which changed state.
( k* R* r# k% v7 X5 a6 C * @delay: optional delay to wait before detection (jiffies)
$ u0 N" W1 x1 H2 A* ? */ [" R% |3 a0 J/ O. u: I
* MMC drivers should call this when they detect a card has been" j$ a$ M& E' I
* inserted or removed. The MMC layer will confirm that any. D3 ~# Z, G6 I( O
* present card is still functional, and initialize any newly
3 o" z. A: z! b * inserted.; J% m9 s; _2 W# P7 n4 V
*/
6 d" O$ O! h4 n4 j( dvoid mmc_detect_change(struct mmc_host *host, unsigned long delay)) ~# y H" L9 P
{% Z( u, M' q3 M* p3 R( Q
#ifdef CONFIG_MMC_DEBUG
* a% c$ t+ V% ]* H5 l: D4 e unsigned long flags;
, H6 @- P9 C! J6 ]! H spin_lock_irqsave(&host->lock, flags);( Z6 \+ i% j/ Z8 X
WARN_ON(host->removed);
7 G# Q3 f7 ^! S- w7 r spin_unlock_irqrestore(&host->lock, flags);. W( ?; b* j& B( o& o7 V
#endif; \! `+ e+ [: S6 P- j) _
host->detect_change = 1;/ w5 H# x# y+ n. v6 s5 e. w& E& v! G
mmc_schedule_delayed_work(&host->detect, delay);
# K& J0 e. m7 W7 x4 q, ?$ c, w}
, D( p: B4 S! k' y. J+ H/ ~" Z& k5 f! r
最开始的宏中的代码可以直接略过了。, W) }7 B+ j* q; j- A# p9 e" \' N
将标志位detect_change置1后,函数紧接着调用了mmc_schedule_delayed_work函数,该函数如下:, F( Z$ f4 N5 `% H4 w* X+ I
/*
: U' H; T- o# A1 e * Internal function. Schedule delayed work in the MMC work queue.
6 \& w8 d+ V1 P7 _1 ~ */+ \& u1 C0 a0 ^! h" D% x$ ?2 G, }
static int mmc_schedule_delayed_work(struct delayed_work *work,: C/ h) D6 c& I' D# M
unsigned long delay)
8 F4 Z, u5 R, W' a. Y4 B{* i& S7 H* ?3 r8 b7 l$ R
return queue_delayed_work(workqueue, work, delay);- |9 }, x! n8 k& i7 Z5 e& V, S; ?' f
} L7 g( U$ Z% ^/ M9 Q% K" e
7 d# |4 z) A8 j' G, d
该函数简单调用了queue_delayed_work来添加一个工作到工作队列workqueue中。
4 X# z- @1 _2 c: u4 Y, ]$ h* P4 c在基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)中的第三章,MMC子系统在初始化时就注册了一个工作队列,并保存到全局变量workqueue中,
0 o" q2 ^( S8 B0 a& I+ k- o9 w8 Q2 B, s k/ g, r3 B
同时函数将detect工作添加到了该工作队列中,而detect工作的执行函数即为在1.1中分析时说道的mmc_rescan函数。# ?. L! D$ q' m
* u* c3 X: d4 c3 y4 z因此该函数的作用就是将detec表示的工作函数mmc_rescan添加到了工作队列中。
. l; T; o4 [* i y
1 e0 B" |9 O% i& O: l6 S创建完工作队列后,mmc_start_host函数的使命也算结束了,它的存在极其短暂,但是它却调用了一个非常关键的函数mmc_detect_change,8 p& e! a5 R; |- v X: g
: i% t6 u. U$ T; m9 s. _' o
后者创建的工作函数mmc_rescan将会扫描是否有SD卡插入。
0 E3 q$ K. P9 u+ ^& w( a- C
+ r$ M0 N$ y* c# e! |, q, K
" p9 p J% A% J# ~% r! i, L- k( ^/ `' \: A. a* W
2. SD卡扫描函数mmc_rescan8 z+ F; R1 x7 |, F
整个mmc_rescan函数执行分为两个部分。
0 c/ Q3 y* s/ I$ b0 g9 F0 U
- F( b4 ?/ y' O, U! ?第一部分,探测SD卡是否存在。
5 H4 N1 U9 \1 e& M- }) U* u7 Y, G: F( _" z# y1 M
第二部分,按照SD规范,初始化SD卡。! I% C$ g- B) e& j( J- h$ X5 T0 c
& ?# [ e# Q- b& t" g; K/ D
2.1 detect SD卡* H6 W" z1 a. u7 |1 h6 y
下列代码位于:linux/drivers/mmc/core/core.c
* g. w j* ?2 ]; L. l3 ^& ]% |* N J) V2 z
void mmc_rescan(struct work_struct *work)5 S: D; D/ A4 I
{+ h# W9 N5 p2 P! M$ x
struct mmc_host *host =
, W7 b1 N2 G. L+ K container_of(work, struct mmc_host, detect.work);! v" Q9 o* L* U( J$ Q" E
int i;) w7 \* a) W$ k$ O6 j- ?, B
, [4 m+ X; v9 h) } /* host禁止再次scan,则直接返回*/
, Q# ^% `4 r6 a if (host->rescan_disable)9 ~# C, L0 x) T e, ]3 y& R
return;& L, p) n7 ?( L
1 `9 G7 q& ^: _ /* If there is a non-removable card registered, only scan once */1 j. {7 {( o' E8 |. S' {* R
/* 是不可插拔的sd卡,则只scan一次*/1 w3 z: \9 T) W1 W9 ]6 h/ R% P
if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
6 X8 M) r$ W# ^& O return;; J! ?+ \/ F& A
host->rescan_entered = 1; /* 表示至少scan一次了*/
' B/ U2 ?5 W# q1 i4 V
* g5 ~/ A2 |# Q, a/ ~ mmc_bus_get(host); /* 增加总线引用技术*/
u9 j( X3 [( D- g' Y9 t
! R) S8 [ p2 X6 _ /*
, S. z' c$ X I' h; K * if there is a _removable_ card registered, check whether it is
. P# x& f7 p+ d$ ]% D1 C$ Q/ N * still present% c, I1 ^2 l4 \9 I
*/# y" m) f2 i( r
/* 如果总线提供detect方法则调用*/- E4 c* y5 w9 H
if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
- d5 b; i: }& M) d && !(host->caps & MMC_CAP_NONREMOVABLE))
! g0 z. e/ l a6 T0 J host->bus_ops->detect(host); + q5 a7 \( m0 ~( X) F0 |, Z
; e" C" S5 n6 @5 C; s host->detect_change = 0;
% V( B6 D- y* Z) D! I5 f7 ~
" W& {( J/ @8 J0 [( J, | /*
6 a! ]# L- j" ~ * Let mmc_bus_put() free the bus/bus_ops if we've found that
5 g2 K. H8 N$ X: V5 e5 w+ P/ X * the card is no longer present.
/ m. d9 r- g" W/ [$ m: m */) n: e- A4 ~9 X+ A
mmc_bus_put(host); /* 减少总线引用技术*/" v$ c- I3 Z9 W: y2 t P, o- t
mmc_bus_get(host);# `3 r! `; p- G
# h" }: r- [; ]% L. S /* if there still is a card present, stop here */
: K$ e1 E- w( q b7 V! _( z5 y /* 有card存在,无需继续了*/
# ]4 C% c- J4 s0 l+ }. J3 m: g if (host->bus_ops != NULL) {
' C3 ]# ?; d% Y7 b( B, S mmc_bus_put(host);3 w. `. _9 ~) V% n
goto out;
, m7 C1 r& i7 W- d0 W5 } }' w5 ~) m3 I# f4 g' c. S
# Z& o4 _) q% C; ~" D' h7 a
/*
( v5 x8 D% m. f5 Y+ w8 G" X * Only we can add a new handler, so it's safe to
6 B$ \. V& E7 m * release the lock here.+ r5 V1 m2 b5 n& D6 s3 D5 ~2 d
*/* Z. G3 i/ l3 F9 u4 P9 t0 b
mmc_bus_put(host);
' o/ {/ e2 b9 z$ ]
: g& l- a7 W3 a5 v /* 调用get_cd方法,判断card是否存在*/. v% S" M4 |0 N1 E" T! j2 O
if (host->ops->get_cd && host->ops->get_cd(host) == 0) {' @$ D) @3 I" |# H" k' Z
mmc_claim_host(host);, @2 W" k2 H( b- B r
mmc_power_off(host);0 y* k( m2 `; ` R& s/ H& g2 F
mmc_release_host(host);
3 ]( r$ ?' {- ]4 f5 l5 u% g goto out;; k" e; ~" p t ?+ [& g# G7 Y
}' v, W) }# b4 n9 |3 R6 X
7 m# l9 u; g. w, B
" q' w, A7 e/ y- A/ q& u( `, t$ y' Y mmc_claim_host(host);
, S& s6 g; z( t9 j! V9 M% M /**/; ?% M* b4 H8 c2 g- x8 ~& Z" H
for (i = 0; i < ARRAY_SIZE(freqs); i++) {& ^3 o' C% V- p6 }. d, u
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))( l; K, f% }' v# R7 r0 T
break;! X/ F6 _1 Q: J) f) r# d& `
if (freqs[i] <= host->f_min)! M+ f9 ]: l+ L/ n2 s
break;
6 \& a6 P2 Z! P' v9 O }
* q; k' t6 X( N7 s& Z+ m4 I5 T mmc_release_host(host);5 J6 L1 i, B) ^
/ \8 W l7 F$ |4 B1 b; l
out:2 y: G: ~# O' k7 t4 S& @6 p+ W
/* 如果需要再次扫描*/
3 c7 g$ B- G% _; J" i7 t if (host->caps & MMC_CAP_NEEDS_POLL); L+ ^$ `; k+ ]. |! k
mmc_schedule_delayed_work(&host->detect, HZ);. i, C! d ~, X! ~7 G+ I
}
& z( O! v: z, H5 S4 x+ d
8 ~' S5 }6 z4 h, [; T该函数首先判断rescan_disable是否为真,如果为真则不用扫描SD卡,直接推出了。
/ U/ k k$ ]& b8 s+ y在mmc_start_host时,该变量被置为0,因此这里函数不返回,程序继续往下走。3 {5 K' T5 H+ `$ q1 `1 a* o
! X, e4 y/ Z. ~5 A/ s
+ `# P& c3 q& x/ I, S. @9 c9 h( [" M5 w( ~7 ~8 R- ]7 F
|
|