找回密码
 注册
关于网站域名变更的通知
查看: 308|回复: 1
打印 上一主题 下一主题

基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-6-5 16:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
在阅读本文之前,请先掌握以下基本知识,不然请略过本文。
: V  ], q% \! s0 Y# \% @% c2 a% f1 M8 r9 h8 Z, ?' ?
预备知识:
# d7 z# I7 }/ P
" Z7 k" F# }% |6 z熟读LDD3前十章节的内容。- m. Y/ V! E0 [* e

+ |4 K3 E) z, q, N熟悉内核驱动模型(sysfs)和platform总线。
2 c) f" B: ~4 X# W
. U! F, N) W* s. b& X- v- ?+ N: p简要了解过SD卡规范。
1 E3 e8 k( d3 o, Y: m4 j3 r' D0 O, P; r9 F3 Q; Z: z9 r. J
6 t( A) o  X. y  B: ~4 d

' ?& l, Z9 Q$ s- S本文的内容基于如下硬件和软件平台:
& e8 P9 ~, @5 Y$ \# W2 F: A7 X3 ~: k" ]) U& ^
目标平台:TQ24408 H' L  a' b0 H6 B
9 g4 |, x3 x2 u$ q8 a0 E1 K" t: r
CPU:s3c24405 }2 c: R7 c+ Z  r, X* X

! a" T3 R8 x" R$ j+ i内核版本:3.12.5
/ ]% V; u# M( Z- \% [
  T2 C. m* |1 W# G* n: e基于SD规范4.10,即《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10》。8 N3 _1 }& T0 Z4 i' r& R
! `0 @, R* J: x2 o
5 m  X% m) O% @3 E

. a8 o/ H! G7 F2 r& Y在阅读MMC子系统时,一个问题随之就会产生:当我们插入一张SD卡时,系统是如何识别到这张SD卡并将它注册进系统的呢?) P( W  H  n. A/ t. I
% x& T1 o7 y/ G. h
这一过程,源于MMC控制器驱动的不懈努力。。。。。。下面,我们从控制器驱动开始,来深入挖掘这一过程。# W* K7 i9 _$ |0 n6 q; M) f* G! \
3 }  _( v( ^/ A: w
1. MMC控制器驱动
  \. ]) N) f7 L: m& M9 ~) N3 C1.1 MMC控制器入口函数及probe方法: e$ x2 \( R3 I/ m
本文以三星的s3c2440上的MMC控制器驱动为例,来进行简要的说明。6 f4 T& A! \; C: d
. u- k- S" m/ A" m, h9 _" g
从MMC控制器驱动的入口函数开始,如下:
0 ?! }2 y2 h' \+ e# N; i5 B. b
0 f; s1 J2 k& I5 f5 O& z下列代码位于:linux/drivers/mmc/host/s3cmci.c- }: i6 p) U2 h# l8 r4 I0 H

6 q/ s. A! G+ |( B# B& Q4 r* Gstatic struct platform_driver s3cmci_driver = {
/ \! j7 B/ S* s9 B) Y* @        .driver        = {7 c5 l! |3 M! M1 Y0 U
                .name        = "s3c-sdi",4 [8 p/ g6 B4 G4 n9 I. V& c2 w
                .owner        = THIS_MODULE,
5 ^* k- F$ X7 ?; G* M4 Y                .pm        = s3cmci_pm_ops,' a" i+ t  s8 ]/ L8 b3 Y3 U$ D6 e
        },' \( d4 ?) |9 p) a& u
        .id_table        = s3cmci_driver_ids,
8 s3 z4 j9 j' ~/ f) l0 }- x        .probe                = s3cmci_probe,
, r! D; e3 ~4 Q% @        .remove                = s3cmci_remove," _0 i3 l4 s$ B, ?0 V
        .shutdown        = s3cmci_shutdown,/ a& r: h# ~8 Q$ q. P) U
};1 a6 _. u; U5 m
7 o! {5 w1 K6 [8 U. N* S4 y4 {
module_platform_driver(s3cmci_driver);! z! H$ D1 E6 P) R, N+ e
# K$ z7 x" n, ?$ W; j, q
这里直接调用了platform的驱动注册函数来注册一个名为s3c-sdi的驱动,该驱动将绑定mmc主控制器设备。9 V& ?+ [3 ]- P
为了让该驱动成功绑定MMC主控制器设备,需要先进行移植操作,具体可见:S3C2440 Linux驱动移植——SD卡驱动
% t. Y/ C3 X7 `! a/ m; Q& s0 \
! A, S! L, S9 @; z# [绑定成功后,立即会调用probe方法,也就是s3cmci_probe函数,我们来看下:
' v% m+ }, e' y) e" n: D1 d. o. n; l$ |& H* \3 c! Z. h3 v
static int s3cmci_probe(struct platform_device *pdev)
0 l- H0 u  ^+ L: ?{
* k) ]9 [6 E! r5 K1 z        struct s3cmci_host *host;
% H, g9 M- Y0 q  |( T* U2 m3 S. ?; o        struct mmc_host        *mmc;4 H; V( w3 Y- ~- X
        int ret;5 Q. h# s# T# @2 Z5 j4 I
        int is2440;4 o7 D: t3 ?9 Z# D& e
        int i;3 d4 e  a/ b; q- X

( x8 Q) Y, Y% l  `$ c: z        /* */' u  b2 ?, U) Y9 _- J
        is2440 = platform_get_device_id(pdev)->driver_data;
! F" K9 q6 ?4 a
0 J* @* [3 E0 E% d# r        /* 分配struct mmc_host和struct s3cmci_host */
1 l. g- N% m7 V. `4 m4 I. f        mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);  P% P5 O: V( W* A9 o; e
        if (!mmc) {7 S3 a6 d) {$ ^- P) s* B
                ret = -ENOMEM;% Q4 R+ T$ Z: O1 @. i4 J
                goto probe_out;
2 B& f0 r1 ~5 ^: j        }
/ n0 w9 F5 }$ F' f7 y  Z: u- a, }1 N( P+ M: @7 P
        /* 申请IO管脚*/7 p6 ]1 [% G" z* P# N- `! |- Y
        for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {
1 u) u/ K/ L1 I6 d' o% p! n                ret = gpio_request(i, dev_name(&pdev->dev));4 b" Q8 P0 ~% s2 y7 W( c: v- w
                if (ret) {: r* W) {" o8 D) @2 z
                        dev_err(&pdev->dev, "failed to get gpio %d\n", i);3 z" v, q  [# v- F) j4 L( f( L
2 e' b$ {% P. V$ v
                        for (i--; i >= S3C2410_GPE(5); i--)3 v. i, R* }8 a/ q* M: [6 U8 Q
                                gpio_free(i);  O% M1 S8 B* s7 q6 b

4 G) S* A' u3 |                        goto probe_free_host;
- X$ U7 \9 w! ?% m' E                }! s' F0 H+ G3 w3 x; _. g
        }
, V4 |: Z& v" w+ D3 g- Y
, ^8 H. p; i( M; G; v- G1 h        host = mmc_priv(mmc);        /* 获取struct s3cmci_host指针*/
4 p( V2 y  V+ r) F- F1 E* B- m! h        host->mmc         = mmc;        /* 保存mmc指针*/
: X7 o$ N" `1 U+ b( v+ F5 q        host->pdev        = pdev;        /* 保存平台设备指针*/1 G+ o9 [5 O  K- ]
        host->is2440        = is2440; /* 是否为2440*/; U' d% E. M( Q# P* p  C$ e, }

8 i. S0 `) A  C5 K/ Z5 [  W7 ?  m        host->pdata = pdev->dev.platform_data; /* 保存板级设备信息*/( b0 j+ k% h# [7 e& j
        if (!host->pdata) {
4 m$ L( ], |3 a: U2 b& c+ q% W. c' [                /* 如果没有板级设备信息,给出默认的*/
# I. ^. u& y( g; N                pdev->dev.platform_data = &s3cmci_def_pdata;
% i8 I4 @, w9 @( j8 E4 _& b6 a                host->pdata = &s3cmci_def_pdata;
; \" l9 @8 Y* F% K) j; V        }
) v. Q7 Q8 g% B( V0 s; g0 w2 i# \/ M8 i
! Z) ?$ D5 A+ P2 S0 u! W) I0 T        /* 初始化自旋锁*/
5 @' ~- _' J9 G  R, P0 W5 T        spin_lock_init(&host->complete_lock);4 }& Y* Y" n9 ]! P
        /* 初始化1个tasklet,执行pio_tasklet*/
) V5 Q) t/ R" |9 A8 _        tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);8 w; m0 b6 \+ l; `0 F
6 J  ?. `$ y7 G5 Q8 R0 ~7 N8 L
        if (is2440) {
8 A4 c# W4 i3 l7 z3 v: C2 M                host->sdiimsk        = S3C2440_SDIIMSK;
5 t& o4 F$ t% d8 i$ y/ U% L                host->sdidata        = S3C2440_SDIDATA;; p+ W) B2 b" Y5 d! Q( a) m1 D
                host->clk_div        = 1;
& r1 e! ]7 f) e        } else {
' Q7 a! |5 g5 r3 o5 I- F# ?                host->sdiimsk        = S3C2410_SDIIMSK;+ B# X6 L5 B# a1 Z: s; ]
                host->sdidata        = S3C2410_SDIDATA;; t& f& M* }" Q: ~, t
                host->clk_div        = 2;
6 a; a5 c8 ~/ b; @" {  D        }; A4 p7 Z4 B& p9 E

; W0 A9 V( p/ k& Q. v6 Z3 B' T* c        host->complete_what         = COMPLETION_NONE;
& V# x3 a/ S- h" q        host->pio_active         = XFER_NONE;
7 O' O% r6 B) f8 o' v2 g2 b2 g5 p+ O+ M( T- j
/* 板级数据可以决定是否使用DMA,也可以通过配置来决定*/1 z( {, J3 W4 ~
#ifdef CONFIG_MMC_S3C_PIODMA
" a; z1 ?9 [& o) C% ~7 L4 G0 m; i        host->dodma                = host->pdata->use_dma;  6 ?% e7 M3 x; @$ b% j8 C- P
#endif+ \" e& J$ L; K+ v) n

) t; \* G, R9 @5 m- G9 x        /* 获取寄存器资源*/% T0 A: N1 Y8 B7 l
        host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  x! S9 P* C. d5 h+ v% ^        if (!host->mem) {
$ t4 v- P) ^1 E% |                dev_err(&pdev->dev,% M9 x8 Y: ]/ L) }: H' k) v
                        "failed to get io memory region resource.\n");6 c: ^2 u3 S! ^3 L/ j( G  z

$ C: @+ b- A' w7 z( d/ j$ `                ret = -ENOENT;" e8 G4 r! h- X! C& r
                goto probe_free_gpio;
; P$ c' x. {4 T- z$ t! Q        }% N; a& n; n' I$ n
        /* 申请IO内存空间*/
$ o) x( D5 O# u- J5 _        host->mem = request_mem_region(host->mem->start,' `$ ]0 u: D+ ~% u7 K
                                       resource_size(host->mem), pdev->name);! m; D& Q) g* y3 J" k0 p) j, X

; ?  J3 R4 m- s        if (!host->mem) {: B( V4 D$ S. _
                dev_err(&pdev->dev, "failed to request io memory region.\n");
3 t7 h7 V0 n$ v) L" h- g9 B! v                ret = -ENOENT;
( ^% i+ ?/ h! i8 G$ {                goto probe_free_gpio;
# I6 w5 i& b# W2 |  o1 Z; G        }1 |. k+ |7 I3 L5 H2 I& q
4 O7 }% f) M* s
        /* 映射寄存器*/- n. Q+ R5 T1 G: _" h% p) o
        host->base = ioremap(host->mem->start, resource_size(host->mem));% w+ Z& u  Y/ |7 N) t1 }2 p; p
        if (!host->base) {- ^: z- n7 N. v6 A1 D( V. r: s
                dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
6 Q5 U9 N, r" J$ G  v/ R9 b: }. d0 ~6 Q                ret = -EINVAL;
( L! T4 ], X% G/ J                goto probe_free_mem_region;; ]. _2 K4 V( ^( W( }9 O1 c6 h
        }# \" `0 g$ }" F0 S

, t1 e: n* j. v9 z7 c2 O7 I# g        /*获取IRQ        */
% s$ l) G* D0 W7 p        host->irq = platform_get_irq(pdev, 0);* y* s3 ]) N. b5 U; G  V( K3 i
        if (host->irq == 0) {8 z0 x8 x4 V7 |7 B
                dev_err(&pdev->dev, "failed to get interrupt resource.\n");: d6 k4 S( }. j: j9 C3 O0 _
                ret = -EINVAL;8 U; X8 K2 a0 |5 e* B* _
                goto probe_iounmap;2 a5 n1 O. D. u
        }$ u4 p! D9 L8 K
& P& ?. F" f8 ]2 X
        /* 注册IRQ*/# P6 m4 m# ~$ R- m9 W% M) ?
        if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
  l) F9 v! M9 N# G                dev_err(&pdev->dev, "failed to request mci interrupt.\n");8 p  K7 Q1 _4 l- M
                ret = -ENOENT;! _1 G8 s! q/ |7 T( a: i
                goto probe_iounmap;9 r, ?7 V; ^$ P. h
        }
- n* v! T' l4 e/ E# ]* u. F1 n: K! A
        /* We get spurious interrupts even when we have set the IMSK  b9 p! a- |1 l; c+ H/ y
         * register to ignore everything, so use disable_irq() to make
* t! C( ]% O* }0 n: }         * ensure we don't lock the system with un-serviceable requests. */
, z0 o) M8 j2 ~9 W; t0 \3 E/ @
; Z! f& e- M2 U/ U6 m, w6 x        /* 禁止中断并设置中断状态*/2 j4 U1 ^. A- N
        disable_irq(host->irq);
8 E( j8 A8 r: x% Y4 ?        host->irq_state = false;
7 f( W; g; e2 A* `" r- h6 J6 T: X7 Z, M, [; A" v9 X  y9 |
        /* 使用detect功能,则申请gpio */: ^# r9 b  l1 g
        if (!host->pdata->no_detect) {
% T. V4 R" m" J" e                ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");
( b7 M5 C0 T3 n! M; X# D6 I                if (ret) {$ P" e( A# k+ X
                        dev_err(&pdev->dev, "failed to get detect gpio\n");
9 O% g, S9 ^* r4 T                        goto probe_free_irq;
7 X6 B0 h( U5 ?/ ~6 g. F                }
. H, Q3 }: b8 h) x6 r$ j8 a7 ~' [                /* 根据gpio获取中断号*/
5 q& W" W, T" v; u, r/ q/ E                host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);
  Q) L9 \9 R& e9 g7 e6 C3 k7 y  W) i/ c4 d5 Y7 C
                /* 中断号有效则注册该中断*/. M; `9 J: K0 S( x  A: L$ _
                if (host->irq_cd >= 0) {/ B* N  x  L. p1 b0 G* g: ]
                        if (request_irq(host->irq_cd, s3cmci_irq_cd,- B1 X! v3 S8 o; y% N
                                        IRQF_TRIGGER_RISING |9 j" T4 G) U6 n/ ~% S5 C
                                        IRQF_TRIGGER_FALLING,
! ~; K# r" \8 j                                        DRIVER_NAME, host)) {7 L1 T+ E) W( }" T. x1 Z/ @0 ?1 J2 \
                                dev_err(&pdev->dev,
% W8 A: x8 z4 u5 j                                        "can't get card detect irq.\n");
+ g% B6 c4 |0 e& B                                ret = -ENOENT;
/ t( `+ f% f) p6 C* v                                goto probe_free_gpio_cd;! S0 X) P* }* X% [2 `( ?
                        }
* Y' T: k7 l( D                } else {  ^9 R: B$ o+ W" X
                        dev_warn(&pdev->dev,: @1 v( e6 K# k4 K
                                 "host detect has no irq available\n");  @7 \! U3 U! Q* I* n% h
                        gpio_direction_input(host->pdata->gpio_detect);
& M  Y  O; e* H: a6 N                }/ n9 Z2 e( n2 X: u5 g# O% y
        } else
5 X2 B" Y8 ~& ]- V* o* A                host->irq_cd = -1;$ q, c3 G# y" \! e) m5 k
5 e0 y' B3 l# C# N- O9 c
        - y9 C, N8 F  P1 y4 p
        /* 使用wprotect功能,则申请gpio */' k1 F4 L! j/ K7 Y1 O& n8 M
        if (!host->pdata->no_wprotect) {
* d4 k* u, Q# i: S8 o/ P                ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");6 m6 p" Q( E3 Z; n" E' F
                if (ret) {5 }# b2 M, ?2 t* ~5 ]
                        dev_err(&pdev->dev, "failed to get writeprotect\n");9 g% u+ |' @! j# x5 y+ I, l/ F: [
                        goto probe_free_irq_cd;2 i8 M5 ?! @& a7 _
                }
2 d# [! [# u6 ~" K4 u+ E5 `4 x! H                ! F" F: H; t" b
                gpio_direction_input(host->pdata->gpio_wprotect);  f: G. v2 g$ E- p- f  @! c. t
        }
1 R; G. p8 `# r0 g$ b4 ?# e+ \: U' r5 C) x0 S- Q( h7 |; }7 b
        /* depending on the dma state, get a dma channel to use. */# S+ \4 D2 h( z: z9 b

; r, U8 {" [, W: Y        /* 如果使用DMA则申请相应的物理DMA通道*/
3 y4 K$ w: `6 h/ O0 z* s        if (s3cmci_host_usedma(host)) {
( b4 E# k# g- E$ X- B8 ^                /* 返回值为通道号*/
3 N; r. K; O+ c$ v: f                host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
0 a" u" W" n' Z: \5 j8 S( l                                                host);
; J3 P% ]4 z7 V                if (host->dma < 0) {4 B2 c, `' n5 Q+ K$ p
                        /* DMA申请失败,尝试使用IO操作*/
  P. a5 L6 q! W: K                        dev_err(&pdev->dev, "cannot get DMA channel.\n");' L9 u' Q! K3 c! s( O9 P
                        if (!s3cmci_host_canpio()) {
- U2 a" a) ?! }8 O! l                                ret = -EBUSY;  A$ g2 f1 Q/ |- L+ |3 d; ?
                                goto probe_free_gpio_wp;+ h0 u( o0 C& ?( u  x7 ^/ p  }$ R
                        } else {
/ T5 i, h6 ?; Q# O1 ^                                dev_warn(&pdev->dev, "falling back to PIO.\n");
" @, L; y/ X9 {, K                                host->dodma = 0;        /* 表示不使用DMA?/( N& q8 U6 `3 `8 R# g* D" X! \* Z! L
                        }" s7 X: e. k8 e6 V8 b
                }1 t% O" _8 l1 v. k
        }! z% T3 |4 N# F# s: H- J
        /* 获取clk*/
1 G& _" o$ X$ N, i$ F/ N        host->clk = clk_get(&pdev->dev, "sdi");
+ W( ]- ^( q7 @' f1 y3 `: ?        if (IS_ERR(host->clk)) {, u/ f, ^$ T6 [' @. D0 R( w
                dev_err(&pdev->dev, "failed to find clock source.\n");) Y5 P, p/ j3 R6 `5 s. c  R
                ret = PTR_ERR(host->clk);
: Y8 @5 m( Q* O! J9 [                host->clk = NULL;  c- ~3 v" E8 B5 i
                goto probe_free_dma;, T  u% h/ v+ p* R' }
        }
6 r6 Z, j- J( b5 B+ C5 `5 z. D. {3 x, k
        /*使能clk*/
9 X/ V  ^5 T5 u3 S% _' q+ X! M        ret = clk_enable(host->clk);; N3 i! O! }- F& R; _5 D$ y% j
        if (ret) {
4 _5 q4 X; G+ f3 V                dev_err(&pdev->dev, "failed to enable clock source.\n");
9 B  Y4 ~! z- x7 V! n" p7 @5 h; L                goto clk_free;
8 K2 B: e% C  ^% G9 T) t0 V. |3 w        }/ @' K2 Y9 f& @! j; F' R, F' p
1 H: A/ v0 `7 S, j# ]
        host->clk_rate = clk_get_rate(host->clk);  /* 保存时钟频率*/
0 {. c) x% L, Q3 J4 x: F+ ~- M5 U# O
        /*开始初始化mmc当中的字段*/
) A5 G$ x6 |% V        mmc->ops         = &s3cmci_ops;  /* 给出控制器operation函数集*/
# i' S% V, w3 z7 B! w) S1 N. F        mmc->ocr_avail        = MMC_VDD_32_33 | MMC_VDD_33_34;
# g  V. _) l9 S# {. z  O#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ
( m: L1 U6 p& V        mmc->caps        = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;' w4 t( a) t; J9 P7 ~( y
#else) O: h/ J$ ], ~0 ^# R
        mmc->caps        = MMC_CAP_4_BIT_DATA;
. D7 D) k. H; w* q+ O$ c' J8 O" Q#endif
) w4 I" \& p7 D  J        /* 计算最大和最小的工作频率*/+ ?' E0 m+ F; {/ t( p
        mmc->f_min         = host->clk_rate / (host->clk_div * 256);' o% k3 o, l; f% J
        mmc->f_max         = host->clk_rate / host->clk_div;
7 Y3 q' v$ h# S% E! o
, K; ?0 [7 M3 ]/ ?; W: ?  `        if (host->pdata->ocr_avail)7 B5 D" b. R7 D. V1 M8 A
                mmc->ocr_avail = host->pdata->ocr_avail;
: |" C& o5 N+ |/ B/ W. W4 o% v+ z( v; m# }' z2 Z: L
        mmc->max_blk_count        = 4095;        /* 一个请求的最大block数*/
/ V. }- N. S  @% K% M% r3 @) K        mmc->max_blk_size        = 4095;        /* block的最大容量*/
/ r* S6 ~3 S, ]; A        mmc->max_req_size        = 4095 * 512;  /* 一个请求的最大字节数*/
3 c; |( v  G+ N) K/ y+ f        mmc->max_seg_size        = mmc->max_req_size;
$ M6 S. ?9 Z+ {" W' L0 }1 r: u( M9 ?: e3 |$ q* K& S
        mmc->max_segs                = 128;
$ \: [( x' P/ z  L* G' S2 m# F9 o9 y% w) r( B: U
        dbg(host, dbg_debug,
* _. Y, M8 s6 l1 U' y8 @/ a            "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
4 \, c" f! H+ ^( c7 `3 k# X# R            (host->is2440?"2440":""),
, C* j1 Q6 U: k- v* t            host->base, host->irq, host->irq_cd, host->dma);: Y% F; d6 x5 u, l% k: e0 r" G8 _* Y
8 j, `  F/ O% \. z' e$ b  ^- ?
        ret = s3cmci_cpufreq_register(host);' k" H* [3 ]  Y# |6 M" [' j+ h+ E
        if (ret) {
7 K: a3 _* b& Z9 g- W                dev_err(&pdev->dev, "failed to register cpufreq\n");
( a# V' D7 K' K' u' B, t                goto free_dmabuf;
& P; n+ o% l( W        }% W- O/ c3 R! A7 G) h0 G

1 L9 C0 E" {7 c% J# c        /* 注册该mmc 控制器到子系统中*/
+ k1 O7 e+ ]. F" I: ?" V        ret = mmc_add_host(mmc);
, t- ]& \1 v4 X: Z* Z. s        if (ret) {. n% W0 w2 B: m: z4 k
                dev_err(&pdev->dev, "failed to add mmc host.\n");
, O* h% }: q$ J# J                goto free_cpufreq;& a! H7 H% a3 J( b' y$ {% n  [' }
        }  H* @1 ?5 \: v  d5 P  |1 A  k6 d
/ v4 _; k" x. Z7 k: v9 }7 `4 ?
        s3cmci_debugfs_attach(host);! r$ W$ Z$ O1 R! i0 U

" ]% V, t, g1 m2 q: V. f/ D/ m        /* 设置驱动数据*/
6 C$ \8 `% l: ?! x, ?        platform_set_drvdata(pdev, mmc);+ E/ v, T0 v" a5 d% g0 F$ s
        dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc),) s. i7 f2 B3 a
                 s3cmci_host_usedma(host) ? "dma" : "pio",, M8 q1 G8 T- L" t
                 mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");
5 y# a; Q: @0 C7 p5 t# I
) M3 Z" @, ~! _2 C        return 0;: W  ]1 Q- [7 g1 U

4 W  h3 T( A* W- R' ` free_cpufreq:
; i6 a6 x; i5 L+ |        s3cmci_cpufreq_deregister(host);( p0 o' |' L& a3 T' A

- p4 \& t+ I* e+ x3 E free_dmabuf:
+ D1 o9 [/ S8 x  }' I6 B        clk_disable(host->clk);
) v/ c9 A+ W) Y) X7 p% I: R# o9 ^* ~7 l* L; ?: {: \
clk_free:
$ ^+ D7 z+ k4 X- h+ m8 u        clk_put(host->clk);% W2 M# m: X9 o- e. D1 {; h7 d) E
. D/ n7 f. `3 J: k
probe_free_dma:9 L& v* R9 m/ L, B' W2 b7 X
        if (s3cmci_host_usedma(host))
0 O& o; I2 `& U3 }1 `3 w                s3c2410_dma_free(host->dma, &s3cmci_dma_client);
) F  W- i1 F: t# x8 W6 o; W
/ z# P# E4 `7 n/ }) k probe_free_gpio_wp:
9 I/ {$ n: A" ?/ K# q        if (!host->pdata->no_wprotect)
2 u* \% L4 i9 ~9 o& \( t% M                gpio_free(host->pdata->gpio_wprotect);
8 T) s" K5 P4 e+ @3 G" w' ^# D( Y/ b5 \: d; ^8 n5 S( S
probe_free_gpio_cd:% U' ^! U* c/ j+ y
        if (!host->pdata->no_detect)
& J- T) I3 `+ O2 N( o9 M5 q: Q/ O1 S                gpio_free(host->pdata->gpio_detect);
, r6 p& B/ f) s& t* o8 H& v, v  P: R  v; `
probe_free_irq_cd:
' t, W. N0 n" }( I5 ^6 M        if (host->irq_cd >= 0)
, q! A# V, j, J) C$ n  }& E6 Y                free_irq(host->irq_cd, host);
1 L6 s$ W# {- |1 b  _4 R9 I2 Z2 B7 u/ @' w5 n: T4 W
probe_free_irq:. X; e3 k2 ], b8 m
        free_irq(host->irq, host);
+ y0 g8 x  E0 Y% I
5 l( S; b! w4 i2 Z probe_iounmap:
0 ^9 c5 e# V/ _1 Z        iounmap(host->base);
% Y* F, Z( _. m3 }: b1 r% F* K& Q6 _# `; F* v8 e" @- Z! D; P
probe_free_mem_region:
# w3 z! N- K. _        release_mem_region(host->mem->start, resource_size(host->mem));
" E3 [& Y1 e( u
: d# v$ [0 S8 y6 i4 G probe_free_gpio:
/ \* x: A' x. k2 Q9 ?        for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
% I. Y) _) H) H: }                gpio_free(i);5 v2 n, k! V5 w$ M# t/ B, w6 z
" B# U6 j' J4 \, ?- q0 Q
probe_free_host:2 O, b; W' X! E$ C
        mmc_free_host(mmc);" c& y9 y7 k  T8 R/ _; j  Q

& i0 x" _5 D- N5 S5 k probe_out:8 F( X# J/ }+ G4 S2 @" Y
        return ret;( }- ]+ _+ C1 J+ u; k
}: q" n$ l% N" o4 b

( i# r, l% [5 _0 p- G4 D* r# S  这个函数想当的长,差不多300行,我们来简要分析下:% f1 w, i4 f" ^$ Y
第一步,该函数首先调用mmc_alloc_host分配了struct mmc_host和truct s3cmci_host和两个结构体的内存空间,其中前者包含后者,而后者有一个指针指向前者。
7 y5 W! o0 N# P8 g0 L- ^, K2 S: Y! `" {3 N, D! D" Q$ Z
该函数的详细的实现如下:2 p5 K4 I+ q2 M1 v4 K4 v7 c
7 C" a* G* v  R3 B# [
/**
6 _; s; j. a) j& B# n: u) k *        mmc_alloc_host - initialise the per-host structure.
' {. ~+ o/ x: S; V/ t *        @extra: sizeof private data structure5 p$ w; {" N+ c) E
*        @dev: pointer to host device model structure
8 K$ o' R) D' g/ Q *
$ r( p5 T1 p* R& m7 Y0 p. k1 g' Q *        Initialise the per-host structure.8 D+ }6 ?3 m; `' f
*/
! y$ B* z& B0 S6 Y- p8 [2 H5 \struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
( T9 f3 E7 C# x{
0 h) N* Y) X2 a) g" j$ V        int err;$ N& M+ |4 Q+ b8 ?6 Z
        struct mmc_host *host;$ Y' \% q9 ^! t& ]9 c7 G8 t( r
' m8 m6 A8 N/ N. |: l, x1 N8 ^
        host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
+ G. o; O: v0 }* F        if (!host)
9 G; M% V/ J) ~0 v: Z  h+ C                return NULL;- f' k; {5 ^; W8 s; C; }; q5 n9 _

6 R; l/ L1 P1 W9 m9 c1 \8 M        /* scanning will be enabled when we're ready */
2 p: g4 H* F% Z' A        host->rescan_disable = 1;# N1 S2 |5 q: u1 b0 F( S8 f
        idr_preload(GFP_KERNEL);
2 j5 h1 m5 v3 X- i8 i# Q/ k5 E        /* 获取一个idr,通过自旋锁进行互斥保护*/
, Z, f+ C0 z# a        spin_lock(&mmc_host_lock);, C: }8 x& z$ C8 _9 _! ]5 R
        err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);' ?! g8 P, e: ?# c# f$ U
        if (err >= 0)
+ I! {. s" [% h  K                host->index = err;. r) m6 V2 [/ D" @- t
        spin_unlock(&mmc_host_lock);
0 y/ n+ G% b- V3 q        idr_preload_end();
: L+ K0 \# Z! r! W( e        if (err < 0)
: t% p; X. [: [                goto free;7 ]! D3 u9 O( w0 u; P7 [/ B, m$ U+ \

- |1 ?# [# r2 q1 f        /* 设置设备名*/
5 }" N% t8 g9 L" Q( k. c        dev_set_name(&host->class_dev, "mmc%d", host->index);4 u7 M8 x1 K) a& B# S

0 s5 b, M  N/ i6 ^        host->parent = dev;
! Q/ B6 z. X) h" I' \6 G  R9 `' ^        host->class_dev.parent = dev; /* 设置父设备*/
3 B1 ^( d. j& b3 g        host->class_dev.class = &mmc_host_class;  /* 设置设备所属的类*/
* j+ Z. J7 y9 z: O+ B5 c        device_initialize(&host->class_dev);
* }2 m/ P/ o# e; H8 E$ b) \9 R% s
& J. d# ]7 l2 Y& G        mmc_host_clk_init(host);, g3 i- L* t/ V; E

! k+ P: L! f- n! v9 ?; R/ h        mutex_init(&host->slot.lock);. ~% |$ J" h1 X& n% q) S
        host->slot.cd_irq = -EINVAL;# W/ d" Z" M! Q( z4 y  U- f/ l# ^

5 F- f  T+ \+ r6 q0 S, P! K5 y        spin_lock_init(&host->lock);
! n2 q* e  d7 ?8 a. w7 P0 k$ o) B- f        init_waitqueue_head(&host->wq);
& h" A9 f) X$ Z% x# O6 }4 X. _        INIT_DELAYED_WORK(&host->detect, mmc_rescan);   /* 创建延时工作队列*/2 \8 R# m. ]5 z* K, o. k$ g+ }
#ifdef CONFIG_PM8 G& x# |/ d! x: J) V/ s
        host->pm_notify.notifier_call = mmc_pm_notify; /* 通知链回调函数*/
! O9 x0 B+ q# f: y#endif6 ?9 g4 c- }5 W9 j9 s, F
2 n/ Z! H% x2 z* ]! a0 ~/ s
        /*
8 ^( K$ u1 B7 U+ i0 u+ A2 b4 _         * By default, hosts do not support SGIO or large requests.
1 [! J0 V/ \& U% t# w7 C% i         * They have to set these according to their abilities.
' T* R, x0 _' d3 ^  q$ t/ q         */
  c& M. D, @4 i, |        host->max_segs = 1;
9 |% S! N$ d' C, R1 {        host->max_seg_size = PAGE_CACHE_SIZE;( @6 N4 V$ w7 Y

$ {! i4 A- x) m! O1 P        host->max_req_size = PAGE_CACHE_SIZE;
' p3 K) V6 w* F! j        host->max_blk_size = 512;
1 `( t% _" q# x* T        host->max_blk_count = PAGE_CACHE_SIZE / 512;, B- D8 ~8 C9 n* q9 F* z2 S

9 @5 Q5 A8 h( l' [        return host;/ C* `* C8 z* t5 K2 V
& @+ G8 w2 `7 Y0 N6 d% u0 c5 E
free:: [" r# |1 h$ N$ M
        kfree(host);
9 ^/ T! `" |$ X        return NULL;6 V0 f: [7 ?% |& p3 ]% t0 U* {) [
}" ~0 }1 d+ o+ b& A2 \0 g9 d

0 [5 A4 F& X7 _EXPORT_SYMBOL(mmc_alloc_host);
5 X" ^. n1 g. w) p* w   其大致过程如下:分配相应的结构体,设置标志位rescan_disable为1,表示禁止扫描SD卡。
  }- B/ ]  s6 m9 y  Q                         随后利用idr分配了一个编号给该MMC控制器,并初始化了MMC控制器设备对象(device),
5 h8 L1 D- S: d, t6 B6 k, ^( L$ Q  d3 M' a3 I  k% Z2 m! q7 P; m
                         初始化了控制器时钟信息。- {+ X  D1 x1 ^" ^1 d

/ \6 d- x, q( J7 h- g: j                         很重要的一步,初始化了一个工作(host->detect)为mmc_rescan,该函数将负责执行对SD卡的扫描,该函数将在后面描述。( @0 D4 E! t. ]( ^* B
7 H' \$ K/ t0 q0 b# A! R+ H
                         最后,初始化了MMC控制器的访问块大小的能力。7 R0 W* p8 x6 H. I0 y- Y

4 }: `9 h9 z; ?' F第二步,根据控制器所使用的引脚,向gpio子系统申请该引脚。* x2 I6 A0 L& P4 K, j6 |7 E$ h9 P
9 I! m- [2 R; X! D4 R
第三步,获取板级设备信息(pdev->dev.platform_data),并初始化struct s3cmci_host中的一些字段。. ^+ C+ |0 A: o( y
0 @- k  Q% M# O
第四步,控制器驱动决定时候需要使用DMA进行传输(host->dodma)。/ h/ C, P4 F' m

4 A: Q: T3 l$ f1 k+ E8 n  C第五步,和其他驱动一样,获得寄存器空间,申请IO内存,并完成映射工作。
. m  L5 U" K8 s) ]8 b0 s) a
; ]! o% N" i+ _% u1 |& f* ?  w9 T第六步,和其他驱动一样,获取IRQ号,并注册该irq,中断服务程序(ISR)为s3cmci_irq函数,并且关闭中断。
! R, P. T" n1 G, O7 t" f. t* A/ V7 Z' H6 P9 E8 u
第七步,根据板级设备信息,判断是否使用探测(detect)功能,如果使用则向gpio子系统申请该引脚,并为该gpio注册中断服务程序。' ?0 Y0 O( s& y0 ~' y0 Z" J' O) O. T
/ {% c/ u- }; W; z9 D: J
              中断服务程序为s3cmci_irq_cd函数,上下沿有效,该函数就是用来判断SD卡是否插入卡槽中的。。
3 p% u0 `4 l8 A# q8 ^; q
. \0 H; I) [, U第八步,根据板级设备信息,判断是否使用写保护(no_wprotect)功能,如果使用则向gpio子系统申请该引脚。1 V% @1 `5 Y+ a& _* b; x+ ?1 v, {+ w
& ?  U$ i2 S) r' Z  O8 n
第九步,如果使用DMA传输,则对DMA进行相关的初始化工作。9 q# X; q; U* Y; f0 A& j

" G( G" [, Q$ f) x第十步,获取clk,并使能,然后获取时钟速率。
& a8 N2 `$ S: _$ a( p* v% b) O1 u) e4 I) t, S- g/ ^
第十一步,设置控制器的访问函数集为s3cmci_ops,该结构体包括了MMC控制器行为的抽象,非常重要,我们来看下:& v+ a& f0 c3 _/ F! z% t$ ^2 ?

- r; H& a5 r% c6 r3 vstatic struct mmc_host_ops s3cmci_ops = {
9 x! B5 ]/ w! y( ]9 {9 j, {9 |        .request        = s3cmci_request,& Q4 v6 p' O; w6 G8 C: V
        .set_ios        = s3cmci_set_ios,+ s4 ?% @8 @9 a, M  W& p+ |/ @
        .get_ro                = s3cmci_get_ro,                /* 判断是否只读*/
$ U& m# Q5 s: K. P0 {9 M3 I5 d2 A        .get_cd                = s3cmci_card_present,    /* 判断card是否存在*/1 P% E& d8 c" p& Y
        .enable_sdio_irq = s3cmci_enable_sdio_irq,
2 Y- Q4 {, `3 N1 y};
& M6 t3 ]- G# ]3 l7 d6 ?struct mmc_host_ops其中包含了很多MMC控制器行为的抽象,S3C2440的MMC控制器驱动只使用了5个,功能如下:
4 j# k& d0 f8 U6 {9 A3 V    第一个是请求函数,用于向SD卡发送命令,并获取应答。! ?, t# U. u* W5 ~, B

3 T2 R: m: w  e0 n$ Q1 ~$ k    第二个用于设置MMC控制器参数。
/ v" |/ u8 Q! W5 v) k2 U, N: r2 _$ i' T, ?, Y4 V4 A) k! f4 T
    第三个用于判断SD卡是否为只读。
" I2 j2 Q# m! d4 T
2 e" a5 f6 X# U# o9 V9 Y$ j    第四个用于判断SD卡是否在卡槽内。
" `: s  w2 j: b. |' b% @
& I6 Y* ~. N0 O    第五个用户使能sdio中断。% K* f* T( p# l+ A- F9 K
; l$ A; O! q5 ^# w1 S! ?! A$ _
接下来开始初始化struct mmc_host 结构体里的字段,这些字段的具体含义就不细说了。  I+ z+ e8 ~. H* i. Q% F- E

) q8 R( M' {9 N) M6 G1 O# W8 \4 ?第十二步,调用s3cmci_cpufreq_register注册一个通知链,有关通知链的东西就不在这里赘述了。2 \; {4 F: e4 @& n# A1 r5 s
/ ]" N5 j+ w( e' A+ P* b
第十三步,调用mmc_add_host注册MMC控制器,该函数将在1.2小结叙说。
; t# z8 ]! R. ^5 L! h9 u6 b# r. ?# S+ r2 V4 i4 j
第十三步,调用s3cmci_debugfs_attach向debuf文件系统注册有关访问接口,debugfs有关的我们就略过了。
3 L8 a% ^0 L" J9 y! [" w2 Y, S+ Y7 M3 M7 q; o4 Y" E
第十四步,保存mmc至驱动的私有平台数据中(dpev->dev->p->driver_data)。( J, U# O, o; F2 Z
' x1 ~7 b( h/ A' o0 B" J

; l* @3 k! v7 r5 M9 b. w/ T& H1 _1 O& S: z. L, n% d. \
作为MMC控制器的probe方法,自然而然的需要注册MMC控制器,从上面我们可以看到在进行大量的初始化工作后,最终在第十三步注册该控制器驱动。' c6 f' [: _2 p. e& s& Y2 Z
; z# c0 `6 g; k
下以小结我们来看看这个函数干了点什么。* Y( ]% N! i6 N) f: b
' l0 G4 n) ~. I
1.2 函数mmc_add_host的使命. Q! z0 T  n. w: s: X/ `5 k
下列代码位于:linux/drivers/mmc/core/host.c, ^! C. \  a. T2 ?( g6 |
. `4 J$ R5 H6 w! }( |& T  m
/**
  C9 V  `; a) o% R& X *        mmc_add_host - initialise host hardware( J1 \1 {9 s0 I$ v- X
*        @host: mmc host
+ r& H% }! ]- s3 | *
: N- x* f8 w6 S *        Register the host with the driver model. The host must be
, C9 n2 Z. Z% k *        prepared to start servicing requests before this function
) j4 X; a; U) [, J: m: Z( ^ *        completes.% R: ~$ H2 e4 X8 N/ d" a, I1 i& ~
*/
" W& Q) h  d; u7 b; H" C$ uint mmc_add_host(struct mmc_host *host)
, d; U! V. X2 Y1 q- r{# p1 h5 b5 g& x! n6 a! B
        int err;
  o6 f9 N6 J6 {- n3 K/ n1 U' ]3 D/ B1 x+ |3 b
        WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&: k) `( p9 G* V  o" ~
                !host->ops->enable_sdio_irq);, @/ ^' q/ S; _) }

5 j# N% A+ a" b# R: P        /* 注册控制器设备实例*/7 ^7 \+ `. l  c; @
        err = device_add(&host->class_dev);
& @8 ^2 I2 n7 z+ m' e$ _. M        if (err)
* a3 X1 P$ \) h+ X                return err;
. o- d% ?. m( M
. [  d- \' I7 _9 [( b/ a" i        /* 注册一个led trigger*/
! I! {5 B5 V4 _( x* e" a2 C4 A        led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
) T. P/ c7 L# V" s, U- i* A* h: c& w- H  K
#ifdef CONFIG_DEBUG_FS* W7 x- F0 l/ T6 q" E% W
        mmc_add_host_debugfs(host);
, ]% P/ E( t. [2 o" _4 j4 ~#endif- j3 v% J: {, X: o, C4 o9 s
        mmc_host_clk_sysfs_init(host);  c2 j! r9 z7 e2 h

. W; v6 `! z& U% m' ^$ B/ r, f1 u4 R: H0 Y3 G% D8 U6 R
        mmc_start_host(host);
6 W- s5 w3 `! J       
* o+ ]4 ]$ i9 v/ Z& C* X, E        /* 注册一个通知链*/
% a' R: U0 q# }8 g+ }4 m! y        register_pm_notifier(&host->pm_notify);1 F) R" M$ m# ?" ]

; \/ y: Q; L4 m! z) L+ Z2 [7 u: Z        return 0;
% {- _6 e1 n9 |& \0 k}
) r3 {9 x5 ]  A. }6 H. u3 T% I1 P; u' R5 A
EXPORT_SYMBOL(mmc_add_host);
: g9 d0 b2 P) ~! J( m8 K/ `, R2 U. n1 u. g: u
该函数首先调用device_add注册了一个设备实例,随后注册了一个led trigger。
( w" X- i# o7 |" ?' ]% X9 D" P并调用mmc_host_clk_sysfs_init,后者利用sysfs向用户提供了门控相关的信息,7 h( q$ K( N9 g

5 v, F: ~' I+ J9 B, E6 n接着调用mmc_start_host来启动MMC控制器的时钟,并且判断SD卡是否已在卡槽中。* R0 K( K: `6 Q( n
  H& e+ y$ T& S. U- K2 f/ g
最后,调用register_pm_notifier向注册了一个用于电源管理的通知链。
8 J& D) u6 f) K4 V. S8 K$ f  }, z+ M" B. c" {3 d6 i
很明显,这里调用的5个函数,我们需要关心的是mmc_start_host函数。
9 f; K8 v, R: d! q0 ^- s" i4 _) O

+ ^. @3 Z- }. z* r6 w, L8 Y1 M+ B: d
void mmc_start_host(struct mmc_host *host)5 T/ }; s6 A- M8 n% f, g5 u  N
{
6 F4 Y, y. H2 W( C! N. x        host->f_init = max(freqs[0], host->f_min);2 C5 R/ \, p1 P) F$ I& g7 e
        host->rescan_disable = 0;
4 y: V$ j' H, t3 E' i. ]& S9 q        /* 必须重新scan才能上电,则关闭mmc控制器*/
- i6 D3 }( S, k1 c& Z        if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
) t" E7 L- @7 `1 y2 D                mmc_power_off(host);        /* 关闭MMC控制器*/# J6 `9 v* _% w' r
        else
# s3 _' f4 g9 F7 k                mmc_power_up(host);        /* 打卡MMC控制器*/
" K$ _+ l. n0 F6 z# p! G) n4 k, M        mmc_detect_change(host, 0);3 Z$ G0 y: [' K4 H
}
2 A4 P& J  I/ m) G1 D1 BMMC_CAP2_NO_PRESCAN_POWERUP 表示需要在上电前扫面SD卡,
: u. |* w1 P* f5 A如果定义了该宏,则需要关闭MMC控制器的电压,否则打开电源,该宏默认是不定义的。
- `3 d1 M* R* Z' b/ r+ n
; B6 q, Q& G/ t, `: ummc_power_up函数还是比较复杂的,不过它不是我们关心的重点,简单说句,该函数最后会调用在1.1小结中MMC控制器的抽象行为函数中的# z: r0 ]; [& y" _( Y! O
' Z- B  {3 Q9 ]+ r
set_ios来使能MMC控制器的电源。
6 M6 `7 D: ?4 K% T+ Z
! m4 N' N4 ?+ }* @' I. [该函数最后调用mmc_detect_change来探测SD卡是否存在。0 d0 |5 V( N, q( n6 u
" B* H  o7 D+ U7 j3 I. Z7 C7 z
/**
1 _; R  E$ N1 V& D *    mmc_detect_change - process change of state on a MMC socket
3 c  D. g& v6 N *    @host: host which changed state./ J, i7 L* }- `; j% ?( {7 V
*    @delay: optional delay to wait before detection (jiffies)" K# U# x- H2 i
*
/ w0 r2 c0 ~5 ^& \" I, S/ f, k *    MMC drivers should call this when they detect a card has been
4 \+ R9 D3 Z& A2 N& w *    inserted or removed. The MMC layer will confirm that any  D+ r* c6 a- K- x9 q1 o) u# K7 z
*    present card is still functional, and initialize any newly+ Y3 p8 v! A+ u7 d# u
*    inserted.$ s2 T, K5 N9 V0 M" `/ H8 a& [& U
*/
: n2 B: K- d9 U9 Nvoid mmc_detect_change(struct mmc_host *host, unsigned long delay). h, t0 a0 W5 `% n: Z# g
{
5 C1 q2 J0 ~9 C/ T6 D#ifdef CONFIG_MMC_DEBUG' A$ ~' q# q( W& E% s( p
    unsigned long flags;
" p4 k9 r8 i' T2 c6 U* [    spin_lock_irqsave(&host->lock, flags);+ L/ ^, X3 C6 ?2 g) S& V: C
    WARN_ON(host->removed);0 }/ _! i( T: H! B8 W! `3 u
    spin_unlock_irqrestore(&host->lock, flags);9 n, v( U$ d% ~. T$ g1 `6 v0 O" t
#endif; k2 h6 Q+ R& T! s* n0 {
    host->detect_change = 1;+ e* z3 V) Z3 g$ X4 w0 q
    mmc_schedule_delayed_work(&host->detect, delay);
( s  Z6 {! G/ V! v}( l: d" ^& ]# w

+ x, _8 J0 v$ \" n3 d4 w最开始的宏中的代码可以直接略过了。
% C, B# @4 ]1 J/ B. ~将标志位detect_change置1后,函数紧接着调用了mmc_schedule_delayed_work函数,该函数如下:
( d7 \" B" }0 M3 F; K/*
" I, p7 F0 a$ }( P * Internal function. Schedule delayed work in the MMC work queue.
5 d% o4 ^9 f' ?- x9 U( Y */
. K& D  {3 M/ I$ ~3 c, j* Z4 cstatic int mmc_schedule_delayed_work(struct delayed_work *work,. M& E1 W8 Z) f8 j8 z4 F/ ]1 [
                                     unsigned long delay)& @% S7 y  u! o& d3 B
{
5 `" @" T2 R) B8 g* @( U        return queue_delayed_work(workqueue, work, delay);
9 `" S1 e' K( B0 x' }) J% u) t! W}
0 ~. Y8 J8 D$ S( ~" s% @
  {5 V1 {7 [6 V该函数简单调用了queue_delayed_work来添加一个工作到工作队列workqueue中。9 e( v9 o/ V6 o/ O; d
在基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)中的第三章,MMC子系统在初始化时就注册了一个工作队列,并保存到全局变量workqueue中,* r% {& Q) d2 Z, ?9 k. L

3 }' \8 S; j* P6 c! h' Y* a同时函数将detect工作添加到了该工作队列中,而detect工作的执行函数即为在1.1中分析时说道的mmc_rescan函数。1 @, Y" u$ R6 R  x) K% v
" a1 m. k) g1 d, ~
因此该函数的作用就是将detec表示的工作函数mmc_rescan添加到了工作队列中。  Y$ m6 \. l; E& r  d& D

, q% O: p! e" u& }/ R) E, h& }创建完工作队列后,mmc_start_host函数的使命也算结束了,它的存在极其短暂,但是它却调用了一个非常关键的函数mmc_detect_change,! p. E6 K- d" j$ }1 O) ^' D, N3 ]

' N6 V! }+ i4 c后者创建的工作函数mmc_rescan将会扫描是否有SD卡插入。+ G! ?. h1 N+ l6 t
, V  q4 P- V/ D# y

9 X" p1 Y) n8 L
" t5 Z/ n! a- a# ]4 g2. SD卡扫描函数mmc_rescan! @0 T5 ~5 L+ `2 P- ^% ~% P/ V( L) \
整个mmc_rescan函数执行分为两个部分。6 _+ Z+ o; Y7 q  M' p5 r
3 o5 L* ?2 }7 G
第一部分,探测SD卡是否存在。! L0 f1 m' E; S& E
& `- w* Z$ g( m  \: |' m' x! q
第二部分,按照SD规范,初始化SD卡。2 {2 [3 S+ J/ ^+ k1 u2 @
& G) j2 G, c# [2 K& N, o1 B9 }
2.1 detect SD卡$ l1 d4 G, ]* o/ f
下列代码位于:linux/drivers/mmc/core/core.c
4 B5 D, A" S- Z; |+ S' A% N. D* o+ ]7 [% F1 G+ Q% t1 S
void mmc_rescan(struct work_struct *work)
& Q8 \1 ~, U8 Z  `$ [& Q{
2 z4 j9 [- p& h$ I$ h$ J        struct mmc_host *host =( ~$ ?1 w6 J8 N( `1 ?& ~
                container_of(work, struct mmc_host, detect.work);6 j" `# z5 W6 ]/ n2 ?9 g1 G  [
        int i;
" p6 b/ G. N2 Y5 ]8 m) s& z
7 b/ ]  P- @; c# b/ W        /* host禁止再次scan,则直接返回*/
7 }" l; c! o2 x* r  V8 E& d4 O        if (host->rescan_disable)' F! x& ^% s0 c" g
                return;6 O) q' U( q5 t) z8 t
% t# e8 \; m$ Y- F
        /* If there is a non-removable card registered, only scan once */
5 f: T6 l$ X; `1 }3 J. L        /* 是不可插拔的sd卡,则只scan一次*/$ T2 T) B% _. R3 g7 b
        if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
+ y( i! J3 t$ i1 f" Z                return;4 p, e1 s* t3 l7 `1 f9 Y
        host->rescan_entered = 1;  /* 表示至少scan一次了*/7 X. l# T) t- M/ a2 f! O' M" @
2 k% {8 R4 }$ U" S
        mmc_bus_get(host);        /* 增加总线引用技术*/
+ G6 [9 ]7 R' M& N" `; G1 h- J) h1 ^6 P
        /*
; g- }- P& t0 Y% a: s; [3 ]- \7 q         * if there is a _removable_ card registered, check whether it is
2 K4 h7 z: m3 {. @- g6 o         * still present  D" R; p, N" O/ ~/ w" G/ l( E& z
         */
$ K" t$ M& {' {' J( O, i/ d         /* 如果总线提供detect方法则调用*/  k5 V# x: Y1 C: }$ b
        if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
0 _) K% z' e* ^1 A0 D            && !(host->caps & MMC_CAP_NONREMOVABLE))
* z" E. d' S! D& [0 h# D) W                host->bus_ops->detect(host);          X5 i4 ?! g: a* Y% C; b+ ?5 x/ p

( G4 g( I/ @7 |* [1 K  ?        host->detect_change = 0;0 J0 ?; Q9 x) r$ m
2 y, `4 y$ k7 f- t0 |2 D( k0 \
        /*" d; _' w6 q- o) C7 G
         * Let mmc_bus_put() free the bus/bus_ops if we've found that. k8 n' \% L4 G$ o+ |7 O. z
         * the card is no longer present.- u/ |. E: G7 @8 Q5 F! D4 T# b
         */
, Q6 Y( ?( F+ o  ?, T        mmc_bus_put(host);        /* 减少总线引用技术*/
9 |; }3 y" Y7 M1 m1 F        mmc_bus_get(host);3 T* C  L9 w( U
/ {! m+ D& H, t  Q$ l# Y& Y5 A
        /* if there still is a card present, stop here */. f! z, J6 W& B6 K6 b3 n
        /* 有card存在,无需继续了*/
! V4 z0 F- Z7 K: D4 b" Q1 j        if (host->bus_ops != NULL) {
% R6 |7 t8 N9 Q                mmc_bus_put(host);
) \; f$ Y( ~" S& R& I6 Q" y                goto out;5 A6 |! K: X% U; I
        }; g: R2 i6 o7 |$ Q# s9 C

' y- ~3 D8 b/ d1 j% W" X        /*" X$ c* u0 }& p& {2 b7 t
         * Only we can add a new handler, so it's safe to
4 A+ z7 D/ Q7 [0 e         * release the lock here.
3 f& L+ l6 i& U. X& p2 ^& A  i         */
- |+ }4 ^0 Z5 T  r) X+ H$ X+ K        mmc_bus_put(host);- L  |( M. z- f6 \$ g

# W' _+ G9 f9 B  N: v0 S- Y' j, O        /* 调用get_cd方法,判断card是否存在*/& U, m  L  O; T; h
        if (host->ops->get_cd && host->ops->get_cd(host) == 0) {- b9 |# r+ p4 h$ m
                mmc_claim_host(host);
  N' T) Q) b! z4 y0 F0 o+ |                mmc_power_off(host);- Y0 B3 R% \) D8 q- k: _2 Z; R! n
                mmc_release_host(host);( {8 o1 x  G0 ?" L) A
                goto out;
/ _7 x" H+ a( t" w( C        }
% n0 E0 Q9 ?  B9 w9 o( b
* Y" M. P9 m6 [/ d  f        2 x1 E0 O. ~. ^+ e- U% Y7 E
        mmc_claim_host(host);
+ |2 D6 @7 L, g6 E7 U2 q        /**/) ]" [6 T" G  D: Y
        for (i = 0; i < ARRAY_SIZE(freqs); i++) {5 G6 ^' a. J5 Q5 C) }. _) y
                if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
. g# n; K3 o! z5 }3 G. p                        break;
  M1 b  Q- V8 V1 A- x                if (freqs[i] <= host->f_min)/ \6 y3 S( p+ L' V
                        break;
, I3 o7 p% r0 k6 M5 K6 V) i7 w        }
, v; q' X6 z3 }. j) j! t        mmc_release_host(host);
; s1 ?% f2 n: }2 P8 O6 S2 Q  D$ i) @
out:
* J9 O6 b- y: ]& `4 O9 E  m7 X         /* 如果需要再次扫描*/! X+ G" `/ ]) F6 A
        if (host->caps & MMC_CAP_NEEDS_POLL)
+ R) m, K5 \  N; Z2 R                mmc_schedule_delayed_work(&host->detect, HZ);- z6 J: Z0 z6 Z6 Y$ b8 i* S
}
! d1 s* {( T" ^2 q
& B, B! e; z1 E+ n9 r该函数首先判断rescan_disable是否为真,如果为真则不用扫描SD卡,直接推出了。$ N$ Y* p* i% n+ U
在mmc_start_host时,该变量被置为0,因此这里函数不返回,程序继续往下走。
, u  k/ ~7 h! }7 J" r+ o% t
* n. d( z, [4 ]* K: x0 h: O" G( k( n% }5 a
8 {+ d+ E% L6 ^9 l

该用户从未签到

2#
发表于 2020-6-5 18:04 | 只看该作者
基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(二)
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-25 22:41 , Processed in 0.187500 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表