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

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

[复制链接]

该用户从未签到

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

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

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-7-3 05:34 , Processed in 0.093750 second(s), 23 queries , Gzip On.

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

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

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