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