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

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

[复制链接]

该用户从未签到

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

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

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

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

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

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

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