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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
在阅读本文之前,请先掌握以下基本知识,不然请略过本文。
. K& @0 \4 ^( Y3 S% W/ H$ i' i* ~- R, h( J1 Q
预备知识:
( c  D9 {/ d4 N7 Y  V  l
: [+ _3 ^( x1 P9 p熟读LDD3前十章节的内容。' u, c0 p( g1 I

0 D( F# g$ ]1 H( F0 j7 A5 {) G熟悉内核驱动模型(sysfs)和platform总线。# L7 w+ g" b# _$ L: L
4 L& @* A; y( @; k! o0 j
简要了解过SD卡规范。9 X( g8 F( i1 N# s8 `% w1 ~3 I
, Q( ^6 S. M* `' X
8 D0 x: k( X1 r

+ h1 z( M4 I' P; L# A* x6 |本文的内容基于如下硬件和软件平台:
3 C3 |( D8 F  Y+ f* l% k4 h( l, b9 O! r9 ?- v" \- O9 _- p
目标平台:TQ2440
" y1 }4 o( [5 j9 J* x
3 ~& w5 c/ s% H+ S  t0 S8 XCPU:s3c2440
3 I3 o: ~5 V9 n$ B( q' r& u! X/ t5 \! V' S: |1 _! ^
内核版本:3.12.5
7 @- ?8 m- t# I0 v
& D0 E7 T4 [+ N- o4 a. H2 Q" s# E基于SD规范4.10,即《SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10》。6 B6 M* \) m3 u- s0 t, g5 ^6 x

  Z& m7 z. w" J/ e
9 _) Z5 \2 {  C1 i  @; _; G6 I
# S0 H4 A' L0 |4 \* y在阅读MMC子系统时,一个问题随之就会产生:当我们插入一张SD卡时,系统是如何识别到这张SD卡并将它注册进系统的呢?
: z* `" Z9 i& q! j) }: w; c' g$ L
- a! {/ p" ~/ e' Y; m这一过程,源于MMC控制器驱动的不懈努力。。。。。。下面,我们从控制器驱动开始,来深入挖掘这一过程。7 o+ E2 s3 p# p" K5 j$ O8 H% D
  k$ y" t. Y/ o
1. MMC控制器驱动2 X- h; V) z8 Y
1.1 MMC控制器入口函数及probe方法
" t$ J1 }- u. R4 j- L本文以三星的s3c2440上的MMC控制器驱动为例,来进行简要的说明。/ }2 |& K' S* l! j- g
: M7 y+ C; ^! A3 e. D  t( T+ Q
从MMC控制器驱动的入口函数开始,如下:
7 F2 Z  a/ M8 u* J; \
6 S8 G5 y9 p; I( @- O/ X9 C" b下列代码位于:linux/drivers/mmc/host/s3cmci.c6 P, v) T( o! K, ^- C5 G9 j! ^' i
! l7 N, e9 e, O" K3 `- z
static struct platform_driver s3cmci_driver = {
. x3 ?) ]5 e/ m# V" y! P        .driver        = {
  `% n/ l- g/ K8 g/ m' V) ]                .name        = "s3c-sdi",
1 K8 o' q" t* X% N$ T% T                .owner        = THIS_MODULE,$ l# F( j2 Q8 F* S0 m& W9 u# x, y4 B
                .pm        = s3cmci_pm_ops,
2 q0 N0 @: I7 H8 l        },
* w' e2 w" c0 L  E( q$ [$ k7 B        .id_table        = s3cmci_driver_ids,% M7 y# S8 ~' N1 ^; _( x' `" ^
        .probe                = s3cmci_probe,
; |" Z0 r1 w! E/ f/ C. G        .remove                = s3cmci_remove," a$ `# x2 {' d, B& L- \
        .shutdown        = s3cmci_shutdown,+ I7 V6 I; J; G* m7 u0 s
};
% \- S: a8 m- Z/ L4 {' u6 m3 k1 O. A
module_platform_driver(s3cmci_driver);
# G% t) N+ O9 [, L' z2 x' N
2 C0 o  ^/ `# G. [; y, r5 s+ Y这里直接调用了platform的驱动注册函数来注册一个名为s3c-sdi的驱动,该驱动将绑定mmc主控制器设备。  P5 F# F8 O5 y0 s% c
为了让该驱动成功绑定MMC主控制器设备,需要先进行移植操作,具体可见:S3C2440 Linux驱动移植——SD卡驱动
8 z% i$ z/ ~6 M
0 s% ?% f+ i0 P) w" @* K& `绑定成功后,立即会调用probe方法,也就是s3cmci_probe函数,我们来看下:) R2 Y- F$ Z0 {! [) \- N6 ]5 s

/ f0 m: O& s6 ostatic int s3cmci_probe(struct platform_device *pdev)3 r  P- J( j' Y5 }2 U. i0 e# y
{
* T; h2 O$ D3 _        struct s3cmci_host *host;- s% C; M' e( Z) Q
        struct mmc_host        *mmc;+ z1 Z& _  G3 V; n5 J
        int ret;
+ F/ i3 J; Y9 a& D" Q        int is2440;
' ~1 M6 A* p* C! ]        int i;. R; I8 p7 P) V% v$ F8 ^
) @% B% d1 z1 H" O0 ~& E( \
        /* */9 c8 s* b/ H# B' S
        is2440 = platform_get_device_id(pdev)->driver_data;
: ?  z3 D  Q# ?" e7 V# M# D) t
  p) E1 D3 L2 r. i# B        /* 分配struct mmc_host和struct s3cmci_host */
4 z9 ?" I) Q0 Y- s$ W        mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);  f$ _. v4 x' j, d! y. G6 i0 g
        if (!mmc) {3 y; {, ^& P# R/ P1 n/ ~
                ret = -ENOMEM;) e2 u" V6 ?) _
                goto probe_out;& X' {, u) o8 X8 N5 V1 a
        }
7 h! K+ W1 Y2 \3 \5 w- v& o8 F
- {" x* ~5 ]; Q) O2 F) c        /* 申请IO管脚*/
% K2 ?3 n9 V" i, j5 m1 W0 V4 t2 k        for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {7 O$ p0 |$ b5 t% e  w4 J
                ret = gpio_request(i, dev_name(&pdev->dev));
% }- q# J* h1 n; b9 B- \                if (ret) {
+ C) z/ L: J# N2 `  B                        dev_err(&pdev->dev, "failed to get gpio %d\n", i);
& u0 q2 O! Z; C6 z# x) }5 i4 g8 r  J
                        for (i--; i >= S3C2410_GPE(5); i--)' \: j  D. X0 A+ ?; [
                                gpio_free(i);& S9 H1 d" l6 `! u
- M3 u' H  I5 G- d- t
                        goto probe_free_host;
+ u+ R+ ~. @, ]2 d7 X6 T                }  m0 ~5 Q) R# }, m# g7 j
        }+ p% q) O5 w" s. i9 _9 K
( R* d& Q) t  L
        host = mmc_priv(mmc);        /* 获取struct s3cmci_host指针*/
' Y  O, A* O3 y6 }/ Y# `1 Q4 `3 X        host->mmc         = mmc;        /* 保存mmc指针*/) A5 u3 `: f: `9 Z% a+ g: \6 H/ d
        host->pdev        = pdev;        /* 保存平台设备指针*/
3 {& o5 V. a: ?7 v8 s- J: ]$ F5 T        host->is2440        = is2440; /* 是否为2440*/* J7 h# k  m2 s: T2 q$ L0 T
3 h+ P! d' r" o; P6 e
        host->pdata = pdev->dev.platform_data; /* 保存板级设备信息*/
. D4 }$ W# u9 ]6 d, J/ m: `- l( Y        if (!host->pdata) {: Q  ~+ o0 ~) J8 l9 ?& q
                /* 如果没有板级设备信息,给出默认的*/
0 G$ W$ E& l# x: V* Q# k                pdev->dev.platform_data = &s3cmci_def_pdata;! ?% \+ [* R# e. _1 ^% E
                host->pdata = &s3cmci_def_pdata;% U9 a0 K3 A; U7 J) s) W4 r% d7 T, U
        }6 U* W* I: t; k2 n
4 E$ T  u/ h6 ~% G! [
        /* 初始化自旋锁*/
4 Z/ {) y  r8 l        spin_lock_init(&host->complete_lock);: d4 S( f/ Z, b
        /* 初始化1个tasklet,执行pio_tasklet*/
3 l; C$ L! ~& \9 j! P1 ?        tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);& w6 C0 `4 [" J- C! p

+ J8 K. ?* y5 v' b0 }$ L4 p/ u        if (is2440) {
( b0 K& a* y* A6 o                host->sdiimsk        = S3C2440_SDIIMSK;
, x, K3 E# s& U                host->sdidata        = S3C2440_SDIDATA;3 U  c4 l+ B" K1 h
                host->clk_div        = 1;3 F* R6 E% \& o3 c& c  k
        } else {
  Y- w4 o2 `9 S. q                host->sdiimsk        = S3C2410_SDIIMSK;
3 _+ s  l. @& `, T+ x8 D                host->sdidata        = S3C2410_SDIDATA;& ?1 t3 C8 n  Z/ q! H
                host->clk_div        = 2;' k6 Y: v: ?& [
        }$ ]7 w7 w% W) z, Z- a

- s* s$ G% `# {4 \        host->complete_what         = COMPLETION_NONE;. v7 z: F- c1 |+ I) a5 N
        host->pio_active         = XFER_NONE;
2 L, e2 {. n2 C* d, J- k% w# J5 C  `" ]6 c
/* 板级数据可以决定是否使用DMA,也可以通过配置来决定*/
; x$ e4 A/ e# F/ q#ifdef CONFIG_MMC_S3C_PIODMA
% `- `, I: \* I' B        host->dodma                = host->pdata->use_dma;  
  d2 J, V( X0 }2 L* J#endif8 S, V9 j$ z9 d/ s& H" }
/ Q, N5 d% h8 R$ ~3 b0 j
        /* 获取寄存器资源*/( e7 I$ D) |. B, \% P0 Z
        host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- P9 k0 Y) }$ Y* \        if (!host->mem) {
6 z6 z* J2 h0 S. \1 w                dev_err(&pdev->dev,
1 A# r/ b  u3 y+ }2 a                        "failed to get io memory region resource.\n");
. B% Q0 V7 U+ N& B; d# X. X5 p- S7 W+ ^" J
                ret = -ENOENT;! Y8 d0 I6 F$ q1 Y
                goto probe_free_gpio;
. r7 j! |$ E, c! |( f; @0 i) _' _        }
/ c, {" U6 G) B' w8 V! x: j        /* 申请IO内存空间*/
3 y6 ]9 N& Q; |$ U- M9 X0 T; U        host->mem = request_mem_region(host->mem->start,+ T2 g" L. j' e/ a
                                       resource_size(host->mem), pdev->name);+ r3 X1 S8 L5 `) S9 j* l

7 L( n, R# S8 G0 G5 @% T/ N+ ]        if (!host->mem) {
1 S* v. W- C: X3 ^; X                dev_err(&pdev->dev, "failed to request io memory region.\n");! k" X! T  V# J
                ret = -ENOENT;: i% X& y$ I, S/ w+ `& ]3 @
                goto probe_free_gpio;* I4 u) q1 v1 z( b9 c8 H
        }8 w4 g8 J& Q# K3 b2 H6 ?" j

$ L  b; [& |0 O' K2 `        /* 映射寄存器*/
+ T: B  G  Y6 R% b$ \! _  d        host->base = ioremap(host->mem->start, resource_size(host->mem));
# m5 Y- @0 I% N5 q        if (!host->base) {
" F- A( \* t4 f# ^$ a9 x. Z                dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");* `, r! E! B% u4 w$ p
                ret = -EINVAL;- \/ K  K* D4 M7 h" _4 c. E
                goto probe_free_mem_region;3 }7 Z( j) Y# |
        }" p9 I; L  F3 w" P& E5 A; w

# ~9 j$ E( T1 R+ r        /*获取IRQ        */  e& P3 e, y9 Z0 L
        host->irq = platform_get_irq(pdev, 0);! G8 r5 e/ L( ]( ?
        if (host->irq == 0) {0 ^/ C" e/ h$ o+ z" g
                dev_err(&pdev->dev, "failed to get interrupt resource.\n");
% B2 f" a0 M* A                ret = -EINVAL;7 ]5 g9 ?6 C9 ?8 p! ^; a
                goto probe_iounmap;; D8 A& b; @' Z7 r% w8 h
        }  B- r* f" w3 w1 k' K$ x
$ z% V" g! M9 C. ?1 u
        /* 注册IRQ*/- m) L: Q: Z. c# e4 q
        if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {/ O6 h% h$ C2 j& {6 l: `
                dev_err(&pdev->dev, "failed to request mci interrupt.\n");
$ [% W2 h7 b) p2 \2 H                ret = -ENOENT;
, ~; {+ e7 T% `5 [                goto probe_iounmap;# c( c$ y$ Z: g* u6 H$ Z/ Q& e
        }& n: B# J3 |1 \5 e. V
1 E7 P( C) d) `0 c7 K
        /* We get spurious interrupts even when we have set the IMSK
+ ~( |% Q9 E& `6 _- ?' T2 P         * register to ignore everything, so use disable_irq() to make. l6 U/ e( G1 L5 M' b6 n" [/ t
         * ensure we don't lock the system with un-serviceable requests. */
- C% i: J6 g0 u2 z& d
3 y! e- b& g* B8 t! s        /* 禁止中断并设置中断状态*/
- j$ k/ l- [2 b! P* @        disable_irq(host->irq);
6 R; c8 u, s1 t8 H        host->irq_state = false;# U! \( I' \8 j& x. n

: N6 x* K8 w) A        /* 使用detect功能,则申请gpio */
: |! H6 f: N! w& P: h        if (!host->pdata->no_detect) {% k" W' E; C& K5 l
                ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");( C) l* H6 x* y  p/ q' Z; @$ \
                if (ret) {
4 ]  x: _+ E( }/ b* o                        dev_err(&pdev->dev, "failed to get detect gpio\n");" O4 ]7 c2 L; ]2 V% l4 C( I  D
                        goto probe_free_irq;8 v5 s3 s8 |6 A1 z0 D
                }
1 q. S# C* L$ T: U7 V                /* 根据gpio获取中断号*/
4 d/ d$ N9 h& p, V; H; c2 s                host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);
: N8 D# v* j* Q( J- ~& k( B- a* _
" Z- t0 g# @  f: Y5 Q2 i                /* 中断号有效则注册该中断*/4 `+ s3 U3 g: T: R4 X  r
                if (host->irq_cd >= 0) {
; D; [: _; s4 {3 K) J( T" Y                        if (request_irq(host->irq_cd, s3cmci_irq_cd,
1 f6 U# f" ]. Z% n# N                                        IRQF_TRIGGER_RISING |
; q( b  S0 `8 |  s7 g* V( Q                                        IRQF_TRIGGER_FALLING,
+ M/ ?8 H; Y! Y                                        DRIVER_NAME, host)) {
7 ?& @6 L( X9 [                                dev_err(&pdev->dev,
) l* E4 G/ R) Q- O$ k/ W/ Q                                        "can't get card detect irq.\n");
: t6 Y- R3 P, x0 `& E, _                                ret = -ENOENT;
- B8 E" q" E7 o+ ]9 z0 S                                goto probe_free_gpio_cd;
! E. {( k! e. c                        }
4 x2 K0 |  Q( s                } else {
, h0 L; C  h, N( l: P                        dev_warn(&pdev->dev,( R) j/ x7 @- Q- {) n& r
                                 "host detect has no irq available\n");
4 g$ l' i, }7 L# T! M) N                        gpio_direction_input(host->pdata->gpio_detect);: @- K% t+ Z/ C
                }
5 N' R( W3 V. l8 h* W; G1 m        } else- m2 \2 u8 e5 k% |& ^9 u) S+ B7 d
                host->irq_cd = -1;
9 Y/ O* Y! g" |9 i4 V: Z6 O' m( {; }4 d% o
       
9 f% S. c! i9 j  S        /* 使用wprotect功能,则申请gpio */
/ V! K# L6 \. m/ q- b        if (!host->pdata->no_wprotect) {; [$ Y& R1 S9 o+ X( ^6 p
                ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");; ~0 i$ [0 t4 s6 S, O. x- y
                if (ret) {
7 r# ]* t5 f4 M/ f+ l- H                        dev_err(&pdev->dev, "failed to get writeprotect\n");
7 Q& Q7 Q; i- ~9 U; S  y0 J' a" M/ o                        goto probe_free_irq_cd;
2 q# ?/ E* l4 F  \                }4 i. \2 M; ?* \4 W5 U1 K: x
                2 i; ?. Q! }3 H/ \+ V" K  V# X
                gpio_direction_input(host->pdata->gpio_wprotect);" o2 {* _6 z3 T6 O* g3 m: G/ R
        }
- \3 t, H/ _* Y7 h- K  l" I. X" Y1 m2 S% t
        /* depending on the dma state, get a dma channel to use. */6 l, w. ^+ ?6 @6 i. S2 k% H3 `/ G
/ n. J- c$ P5 P0 l7 p/ |1 t1 w! A
        /* 如果使用DMA则申请相应的物理DMA通道*/9 u* u& [2 [3 Y' \
        if (s3cmci_host_usedma(host)) {
4 \+ X- }! `9 s$ K3 l2 x                /* 返回值为通道号*/3 M( [# E2 i4 g* r9 @2 B
                host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
, r' G1 @: m. X, ?+ i6 n5 H                                                host);! b" _: W5 t" s
                if (host->dma < 0) {
% b  N0 i; b" Q! ]                        /* DMA申请失败,尝试使用IO操作*/* q/ c$ g" \/ X: [: G& j- d7 h
                        dev_err(&pdev->dev, "cannot get DMA channel.\n");
2 y" D8 K0 @. g8 d                        if (!s3cmci_host_canpio()) {
1 W6 ^" f- x( t                                ret = -EBUSY;0 c  F! g) x8 s/ r( f6 E# L
                                goto probe_free_gpio_wp;
, u; F4 A/ @  V3 S9 u8 O% C7 x                        } else {
, j/ j; h  U. {5 E1 x6 p2 I3 M8 o                                dev_warn(&pdev->dev, "falling back to PIO.\n");4 _, T1 J2 ^( `  Y0 I- w) W
                                host->dodma = 0;        /* 表示不使用DMA?/% H4 i( w5 N3 H  ?6 {
                        }2 X) F; [% r5 E5 }' j4 V# h$ P
                }* P' ?! v2 w) |! [7 I9 d" S: l
        }
7 D1 L( e- O, w7 ^- w! T7 P% j, ^        /* 获取clk*/
; G& Z; o+ a1 B" Z( ~        host->clk = clk_get(&pdev->dev, "sdi");
. z8 G& ^. |! h! K        if (IS_ERR(host->clk)) {
6 O  _- b- j' m) f1 y                dev_err(&pdev->dev, "failed to find clock source.\n");5 n5 |0 H) B0 C. \+ H( l# w7 C
                ret = PTR_ERR(host->clk);5 ^2 T, J4 R; F) M; k$ N$ c$ F
                host->clk = NULL;
! y$ |/ x3 ]+ Z1 D                goto probe_free_dma;/ I" y9 g' N2 @* Z7 \- B1 }% Y
        }
# x% J$ @6 c; q, A- _5 O
; \2 C5 K& R! w: [( ]8 J9 r        /*使能clk*/% F+ {0 [+ I$ |, Y
        ret = clk_enable(host->clk);
, k# W9 I5 E  Z' A- O& C0 c1 S        if (ret) {4 ~$ i* Q; j% f! M1 |- `; V, Q6 q# d
                dev_err(&pdev->dev, "failed to enable clock source.\n");
& B1 B' \/ x; X" @& C$ I                goto clk_free;( [" T" X$ E! S, z/ ~4 X: W
        }
3 q7 Z- w" s  z9 f) @" v. k* L# O8 Z5 N( I5 m/ A
        host->clk_rate = clk_get_rate(host->clk);  /* 保存时钟频率*/! r: a8 m& t% Y
$ B: \  }2 W6 C: J
        /*开始初始化mmc当中的字段*// m1 G/ D4 f" t5 T& i& \1 `7 u
        mmc->ops         = &s3cmci_ops;  /* 给出控制器operation函数集*/
! N/ _3 Y1 Y0 L8 z        mmc->ocr_avail        = MMC_VDD_32_33 | MMC_VDD_33_34;
8 _/ L7 q, n9 K4 d#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ) z0 t4 Y& @$ v% _6 k3 p- F2 p
        mmc->caps        = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;6 `& I5 X9 Z8 q1 X* \* |8 j
#else
$ z- Z  _) r4 o" L) ^3 k        mmc->caps        = MMC_CAP_4_BIT_DATA;% r$ [7 O1 G+ x9 x, w
#endif
  L2 v( P( {$ x- I5 Z        /* 计算最大和最小的工作频率*/
3 g  ~! ^0 V$ U/ N        mmc->f_min         = host->clk_rate / (host->clk_div * 256);: N. G! J! K: t, ?8 E: i/ v, V
        mmc->f_max         = host->clk_rate / host->clk_div;
; e8 D: s+ S* K# X: Y8 U* L( G0 r; Z+ B$ t0 D
        if (host->pdata->ocr_avail)
' w% L! `; U5 ~& N/ x  F                mmc->ocr_avail = host->pdata->ocr_avail;
2 t# L. Q! r! @0 ?! d" c
5 a: `( ]2 d# _4 L4 G        mmc->max_blk_count        = 4095;        /* 一个请求的最大block数*/0 V! g- q* N7 `4 l+ B! p% T% S
        mmc->max_blk_size        = 4095;        /* block的最大容量*/; E3 h+ p6 D: q; N
        mmc->max_req_size        = 4095 * 512;  /* 一个请求的最大字节数*/, Y1 q3 }2 Z/ S+ \2 \
        mmc->max_seg_size        = mmc->max_req_size;1 S: @2 H2 N* L7 a0 H

2 e/ x' s) c0 w% x5 `! t0 |        mmc->max_segs                = 128;8 X5 k- |. H0 V

6 |5 l! f1 _& T& Z        dbg(host, dbg_debug,( P  \- b. O9 A; f6 `
            "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
2 }8 f/ @8 U0 T# l            (host->is2440?"2440":""),& i% j/ e$ V1 u# a7 h3 }
            host->base, host->irq, host->irq_cd, host->dma);* p8 r( ]9 v1 v( S- g: s3 v

. f/ Z* }+ ?" ^) ]* N' Z7 M+ }+ M        ret = s3cmci_cpufreq_register(host);" s0 M  v$ `- {/ d
        if (ret) {4 q' |; c' X: p& L
                dev_err(&pdev->dev, "failed to register cpufreq\n");- P' p7 }  i5 F2 u8 D2 O- n6 \
                goto free_dmabuf;
! M. {1 S" }5 ^1 E' U        }4 V' C5 P. f( N5 e2 e9 j

! O/ n/ R# Y! o8 n) G; [7 g7 p. u) x        /* 注册该mmc 控制器到子系统中*/- W- K+ k' i, \
        ret = mmc_add_host(mmc);
7 A9 C8 [7 \. b5 W" _* \        if (ret) {
% t! v2 \2 h8 }& Z: D                dev_err(&pdev->dev, "failed to add mmc host.\n");% {8 r8 \6 B5 \9 t! U$ b/ W2 ?
                goto free_cpufreq;9 D4 {/ o) d0 s' t
        }% w. v. }4 i1 b3 h
# a: i- R0 P6 g
        s3cmci_debugfs_attach(host);
9 w$ I$ ~9 b% W) _' R6 W; ~% Q
" g7 f: i" p& W4 y2 P- l6 n9 Z! r: n        /* 设置驱动数据*/
' s! ]1 E$ s* h* e6 o6 Z2 Z( ~        platform_set_drvdata(pdev, mmc);, z- i- \, K. P/ r
        dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc),! u5 M; m3 M: P
                 s3cmci_host_usedma(host) ? "dma" : "pio",( _. Z$ ^) y* I8 ~
                 mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");' ]& L" L7 G, Q% q- S5 }* J

: c4 x# R1 Y- G5 g2 ]2 p& X        return 0;) C2 Y! h% I. W, _- v; R

% |- z% V8 S9 @% h free_cpufreq:
8 F/ W5 U- r  U        s3cmci_cpufreq_deregister(host);- ?  |! u! M9 P

$ ^3 U& l' e; G0 B3 E! ]- K3 ] free_dmabuf:7 l5 Y9 \& `* z8 T
        clk_disable(host->clk);
3 O2 ?$ g5 V9 D5 {8 j" u: t% u+ g. X
clk_free:$ v' v6 C7 D4 j! Z! R3 t+ i
        clk_put(host->clk);
0 Z4 U+ e, s; I5 P" ?5 y1 X8 R$ m- X6 `3 s- E( U7 c
probe_free_dma:0 ]% x2 V  U: y: x8 L6 d& y
        if (s3cmci_host_usedma(host))5 C0 g5 W% i- Q$ A
                s3c2410_dma_free(host->dma, &s3cmci_dma_client);
" ~  @5 E1 T8 g9 d
7 p7 b& K, t) N probe_free_gpio_wp:
) P6 Z0 j- k* b        if (!host->pdata->no_wprotect)
( J" ~* `# c, l. l                gpio_free(host->pdata->gpio_wprotect);7 t1 l1 e5 A# u# k6 w8 P
7 ~" S* V/ R" y$ p
probe_free_gpio_cd:
9 T3 B/ }; t5 l) b+ P        if (!host->pdata->no_detect)7 A8 U% h) M' ]* f. H4 s! A
                gpio_free(host->pdata->gpio_detect);
. T) u  f& [  n0 K5 }; K% x- c/ @" b6 d9 f
probe_free_irq_cd:
2 X' v5 p+ N  z0 [1 [' ~/ `' G0 P9 P3 ~        if (host->irq_cd >= 0), G& l' o9 p# g% Z# b7 ~+ @8 X
                free_irq(host->irq_cd, host);% }4 W  E) y3 ?1 j" z0 b& x

- M: m% {' _* L1 N( B. s probe_free_irq:
5 K8 {' ?  G/ N1 }) `  h8 |        free_irq(host->irq, host);4 _& Y5 h* g2 ?9 ^7 C8 b* x9 w
+ I& |- ]% g% b
probe_iounmap:) }$ r1 [5 b' p4 l3 s. U+ m9 A9 a, ]% g
        iounmap(host->base);
7 C5 U( ~8 B# O# R8 m) J; B6 @
' q. h% W1 p/ D  D9 x probe_free_mem_region:! t# M3 e7 f/ M. X' E
        release_mem_region(host->mem->start, resource_size(host->mem));5 K: D! W* h5 g7 `6 x! x
/ R  n1 c/ @+ L! w) E  z0 B+ f, j
probe_free_gpio:
- {/ o5 }6 g" e% c7 e        for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
" p1 T# |4 X4 j. c. ^# J% m                gpio_free(i);
! T8 R  z6 B; c8 t2 B- G9 R+ Z3 O- N
) x. b7 _9 t' k0 o; }0 I2 S probe_free_host:
9 u% @, {* \& ~9 z' b        mmc_free_host(mmc);
' _+ i+ K* @& T. R! V2 m/ u9 c: S& u) W5 K5 |; P& I
probe_out:
! ^8 J2 |' g* K' o        return ret;/ d0 W* o# ]+ s$ Y1 W
}& `2 I4 \+ ?0 L

9 N* }* d. G# |  这个函数想当的长,差不多300行,我们来简要分析下:
& U, J6 O3 E# q第一步,该函数首先调用mmc_alloc_host分配了struct mmc_host和truct s3cmci_host和两个结构体的内存空间,其中前者包含后者,而后者有一个指针指向前者。
2 q" l$ Q3 n1 R4 \  A1 B$ O: i% m0 G
该函数的详细的实现如下:; {1 p' f% L) L/ J5 V9 ?
* `6 m$ a4 ]# t4 ~) _3 {
/**
" l8 p5 f4 l; x) r) b2 u *        mmc_alloc_host - initialise the per-host structure.
, I1 a% x3 N3 a/ w2 F+ u6 G *        @extra: sizeof private data structure
6 u  [( A" \, F! v# S$ A: m* T *        @dev: pointer to host device model structure
, D  v* V  Y9 _, V3 K! { *2 C4 Z' |7 L1 }1 y" }& H
*        Initialise the per-host structure.' C+ i0 i) `/ \0 R: M: ?
*/
1 w, B7 F& Y1 d4 F! y- M. bstruct mmc_host *mmc_alloc_host(int extra, struct device *dev)6 g  T9 j6 l) o( {  Q3 ]
{
$ q/ J% l; ~, f" K6 J$ e        int err;1 r7 s( V4 r" M: q, }( U& }2 g
        struct mmc_host *host;8 ~' d& z+ x. J

" D' @: y+ N5 t        host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);$ g' B* r1 f# z# W, k
        if (!host)
" M4 h# B# D% h+ S5 ]& b                return NULL;+ m. L' l1 d) F+ K- V% H
2 n7 n, H  O2 C& v9 Y( ^
        /* scanning will be enabled when we're ready */
4 E3 Q4 u' a0 H        host->rescan_disable = 1;8 g/ |; z& G! ]. Z5 }( v
        idr_preload(GFP_KERNEL);
% R( ]+ \. N. R+ f3 c& w0 k        /* 获取一个idr,通过自旋锁进行互斥保护*/
0 Q1 ?- x2 {8 O, }6 f+ ^' J        spin_lock(&mmc_host_lock);" `) b2 l' o# Z. O7 A
        err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
/ I. [( M/ j4 s' R; H+ t5 r1 h        if (err >= 0)& z$ G) m4 B" C3 R
                host->index = err;5 M* ]* r: j, [! f' g; d1 ^# V3 j$ W
        spin_unlock(&mmc_host_lock);( p  Q8 c- c/ U% b
        idr_preload_end();
6 Q8 H  ^+ G5 r: ?6 v2 p, k        if (err < 0)% R: C/ T. B# d, P' L" m
                goto free;* f, k; K7 u+ q+ j, f

/ ?, W4 l7 s2 L8 }) R% V        /* 设置设备名*/3 i0 E1 X) l9 v( u; H
        dev_set_name(&host->class_dev, "mmc%d", host->index);
/ X# M- G$ P& W3 |+ _% U- c5 ]8 S& d) c0 J1 ^! Z9 o. R* r% N" g$ S
        host->parent = dev;0 x; g6 e9 q' f
        host->class_dev.parent = dev; /* 设置父设备*/" ]0 ^9 S) e: I5 _
        host->class_dev.class = &mmc_host_class;  /* 设置设备所属的类*/
7 I" K( x1 Z& |        device_initialize(&host->class_dev);
7 e1 |7 R8 F2 [% M7 A+ F) B* L" E9 N' R# d- a
        mmc_host_clk_init(host);5 Q& r2 B+ C' [  {

1 U- u. y6 h; `7 M! R* a) c/ Q        mutex_init(&host->slot.lock);# b2 S( K5 M7 c% e$ s
        host->slot.cd_irq = -EINVAL;
" S& a3 _: O; y3 v# n# K9 y7 a( G/ g0 X
        spin_lock_init(&host->lock);
/ C/ ?# e4 L7 v7 C        init_waitqueue_head(&host->wq);( ^; F* t9 b" D# ]! o! f
        INIT_DELAYED_WORK(&host->detect, mmc_rescan);   /* 创建延时工作队列*/
# [& h3 j: _* c7 j#ifdef CONFIG_PM
3 z8 o7 a2 B; p' s# m4 m  Q, k        host->pm_notify.notifier_call = mmc_pm_notify; /* 通知链回调函数*/1 y* N! H% \5 z3 H8 u1 |/ U
#endif; H/ b4 q/ C, s

0 B+ B. P# ~1 t        /*; v; w, P% n; @; S% w
         * By default, hosts do not support SGIO or large requests.0 i, I' _, t" v# {5 F3 n8 m9 d
         * They have to set these according to their abilities.
9 j: E/ c8 p0 s, o- d' X8 m7 o. m! N$ ]6 y         */
& s2 B* s8 z' x3 U- s        host->max_segs = 1;
# S6 |  p& L8 w/ O" z( S" n. W        host->max_seg_size = PAGE_CACHE_SIZE;! A, `6 A7 n5 Q+ z$ b, ]

- ~. h  u) E, n9 M( u% W% a- i4 O$ H        host->max_req_size = PAGE_CACHE_SIZE;( x' B1 ]3 @- ^
        host->max_blk_size = 512;5 A  q0 U# T  L' m8 f4 `' u
        host->max_blk_count = PAGE_CACHE_SIZE / 512;/ |$ Z3 I) d& J/ x" p

2 D$ Q! l8 {$ R( f        return host;
2 C2 T4 N. ]) d
% i" v2 ^& |9 B9 B) _free:. P. m) `9 ]# f  g4 g6 }
        kfree(host);9 L* O" h. G4 A9 Z
        return NULL;
! [$ V2 E6 |- ]( Y. W( @}' H% }" K+ R' \, R
% j5 ~2 H3 |0 U2 W; q
EXPORT_SYMBOL(mmc_alloc_host);
' s& c' o2 r- ], Z: x+ [   其大致过程如下:分配相应的结构体,设置标志位rescan_disable为1,表示禁止扫描SD卡。  i% p) D/ R( j6 V, Y# _
                         随后利用idr分配了一个编号给该MMC控制器,并初始化了MMC控制器设备对象(device),
% o, r2 I& S' n3 ]9 w$ E' B% X- U. @6 ^8 e0 u
                         初始化了控制器时钟信息。6 j! _& H/ N+ V" ]. d
5 F# G3 a) X; H% K4 C' i, V3 D  B
                         很重要的一步,初始化了一个工作(host->detect)为mmc_rescan,该函数将负责执行对SD卡的扫描,该函数将在后面描述。) ?! @: O4 J1 t3 g
: G$ q0 }- ~/ H9 F: q
                         最后,初始化了MMC控制器的访问块大小的能力。! C4 u( Q( ?' e% x6 g0 [0 m' Y& B
+ G* m4 q% A6 E& ~5 [
第二步,根据控制器所使用的引脚,向gpio子系统申请该引脚。& [1 v0 Z- ]0 n, [3 _

! A& k9 N: z: X! V$ P, ^0 R第三步,获取板级设备信息(pdev->dev.platform_data),并初始化struct s3cmci_host中的一些字段。' l7 v" \/ S( N4 [
6 Q0 H* O$ c3 J  I
第四步,控制器驱动决定时候需要使用DMA进行传输(host->dodma)。+ v. |' M+ N7 l0 J- s: ]  I

8 H! P% @$ d( _+ e( u. o  w" z第五步,和其他驱动一样,获得寄存器空间,申请IO内存,并完成映射工作。' S' i4 N% u) p; z% E0 o. g

4 E8 T9 }1 O! A8 }* {第六步,和其他驱动一样,获取IRQ号,并注册该irq,中断服务程序(ISR)为s3cmci_irq函数,并且关闭中断。
) s5 `, g9 d& V7 d$ S5 |1 [% \
4 A* R4 P$ w6 @8 x6 m1 y* q# d第七步,根据板级设备信息,判断是否使用探测(detect)功能,如果使用则向gpio子系统申请该引脚,并为该gpio注册中断服务程序。
/ x! A: |. m! _1 M5 y0 P8 I: M
5 O) g+ n4 ~/ K9 J: w$ k- ]% F, K              中断服务程序为s3cmci_irq_cd函数,上下沿有效,该函数就是用来判断SD卡是否插入卡槽中的。。
, @. b7 h# B+ l4 C6 ?& Q, s0 v8 Z' ~' s1 a3 K4 v, A# O
第八步,根据板级设备信息,判断是否使用写保护(no_wprotect)功能,如果使用则向gpio子系统申请该引脚。
8 d' V: w& x7 \% h7 n: W" |0 S" c) n+ d
第九步,如果使用DMA传输,则对DMA进行相关的初始化工作。; r- T5 _% p  }  \+ c7 a

! c) g+ y6 Q5 @3 G  y# s" T9 V第十步,获取clk,并使能,然后获取时钟速率。8 }* `+ [" w" q6 n- k; x
! @$ `& t/ ?! ]! h
第十一步,设置控制器的访问函数集为s3cmci_ops,该结构体包括了MMC控制器行为的抽象,非常重要,我们来看下:
5 `2 A. G3 @' ?/ e0 ]% x3 X4 G$ [$ c: m  P; t% D
static struct mmc_host_ops s3cmci_ops = {  g+ r% h9 J" j! {  X* z3 f
        .request        = s3cmci_request,, Y( D1 c5 d9 W* E/ A) \
        .set_ios        = s3cmci_set_ios,0 p. W* F4 w  C" a! p
        .get_ro                = s3cmci_get_ro,                /* 判断是否只读*/
# t/ R( b; f5 m) l) e) E        .get_cd                = s3cmci_card_present,    /* 判断card是否存在*/
/ U' |5 h4 K& Y/ X        .enable_sdio_irq = s3cmci_enable_sdio_irq,% M; `! Z+ X8 k1 Y5 g9 P' L  _
};
7 k/ N3 U7 c6 L' h# X' Kstruct mmc_host_ops其中包含了很多MMC控制器行为的抽象,S3C2440的MMC控制器驱动只使用了5个,功能如下:
) t3 [7 M* Q( n' F8 n* P    第一个是请求函数,用于向SD卡发送命令,并获取应答。" l& q2 r4 T8 F4 y9 |

9 I) D6 G9 m9 }( ?- k/ l    第二个用于设置MMC控制器参数。
. G; G/ k& Y2 [: b' G; `& _1 a6 J& {9 V* c6 }7 q# f4 _, s# l( G+ l, ]
    第三个用于判断SD卡是否为只读。
6 J* C0 D: B, d. b/ e5 A0 B0 m# r" Z; o) x5 _  O# f. ?9 T
    第四个用于判断SD卡是否在卡槽内。
0 F0 C. x+ s; d3 Y: ^
  _! U& @# b+ k- Z    第五个用户使能sdio中断。7 b3 `. w: i; K( d- F' ]3 q
9 H" J* ^6 W( Z: v) |, X4 W8 w
接下来开始初始化struct mmc_host 结构体里的字段,这些字段的具体含义就不细说了。
/ Z# R& B+ `( D7 y7 @5 Z
% r; v9 R3 F7 [3 r第十二步,调用s3cmci_cpufreq_register注册一个通知链,有关通知链的东西就不在这里赘述了。
! e5 Y; N3 j+ C5 R% }, p5 ]& M) |7 I+ @, N
第十三步,调用mmc_add_host注册MMC控制器,该函数将在1.2小结叙说。
; x9 z, f  ^" @/ b" |2 X8 d4 T
% w; J  Z9 r( q8 e: f6 E第十三步,调用s3cmci_debugfs_attach向debuf文件系统注册有关访问接口,debugfs有关的我们就略过了。
8 q8 }6 r! `! p3 ~
5 c! b9 ?9 b7 A5 r% k第十四步,保存mmc至驱动的私有平台数据中(dpev->dev->p->driver_data)。
# k' u3 V. N3 ]4 i! ^  x
, j: x! v6 G5 G" b* C2 R( T# {& w2 H: v  h5 V+ m* A9 h

! q4 T8 n4 H' b& x作为MMC控制器的probe方法,自然而然的需要注册MMC控制器,从上面我们可以看到在进行大量的初始化工作后,最终在第十三步注册该控制器驱动。
# f0 v( i) U, t( b8 I' @
1 K. p1 ^: }* |1 W下以小结我们来看看这个函数干了点什么。* L5 b; C9 I$ W; q3 ]4 d$ F) ?% g8 i

2 j/ F4 z& t4 W& q8 L9 H% j& y. U1.2 函数mmc_add_host的使命
: f2 t& Z% Q- }, d下列代码位于:linux/drivers/mmc/core/host.c
! _) G% ~8 @' g1 N3 M( m  O9 z4 A9 j% A  r: \0 \# Q- p7 ~
/**9 H  _2 O/ k/ w! G" e
*        mmc_add_host - initialise host hardware
: Y6 {$ M! p: H3 M *        @host: mmc host$ p  ~: I2 n3 s9 \9 S
*: Z+ b0 q( u2 K% k  _" Z( ^5 n
*        Register the host with the driver model. The host must be
7 m, ~' D5 z% ?( y8 v8 N" q) C) r *        prepared to start servicing requests before this function
, X$ u1 P& U/ d  Z *        completes.0 M7 f' Q3 O# K7 G6 b
*/* e& R; j* N* q% c. u
int mmc_add_host(struct mmc_host *host)" d$ o3 V* r' S& q% [+ }: Q9 g
{6 `/ Y! W" F- t; h0 L: r
        int err;: W" H2 y; M# |

* n6 K8 r1 f  c0 `/ ^- B        WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&3 w0 O+ S- E* J( N" L) M7 ]( \
                !host->ops->enable_sdio_irq);$ }2 I# Z" s7 y! A6 T6 s6 j
% O- U/ D7 A* d1 S) \9 o
        /* 注册控制器设备实例*/
; ]& U' h) }+ n  s3 n        err = device_add(&host->class_dev);
* O, s& W4 R7 P3 q        if (err)% l+ h5 L$ c$ r  [  n7 w
                return err;
- {( y8 z9 k$ n2 `+ S2 D' b
( N# q7 k: H+ O9 C% c  q        /* 注册一个led trigger*/1 v4 O/ h" V! R! D* J
        led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
! J! Y, k) t3 S0 _/ [
- E  D0 @2 i, K! V+ T1 q) l8 K% w#ifdef CONFIG_DEBUG_FS
9 g* T2 @7 c1 b- [2 w% Y        mmc_add_host_debugfs(host);
3 d( K; W% [* ?#endif
: e& s/ L. f3 ?2 F' Y( R        mmc_host_clk_sysfs_init(host);! C/ v- W) R) N$ a

: P( C) }" U0 f0 }& _3 W5 Q$ t; @/ a- X  l
        mmc_start_host(host);% R) \& S/ D7 q7 a+ m
        ' N; u( `- |7 X3 k; m
        /* 注册一个通知链*/
) }$ {) W% \. b' d" W        register_pm_notifier(&host->pm_notify);
* q; j& `. @9 p# D& u; C' l
- U, T+ v% p4 I3 u8 s8 P: |4 }9 {9 r/ P        return 0;3 D/ ~2 m2 y8 K' m
}( L5 F% A8 W% @: J# N

, f  ~/ r4 m# k2 ]1 }& {) }EXPORT_SYMBOL(mmc_add_host);
, z- n- k! f0 [& y2 R, g& C# d4 o( f; K! ?
该函数首先调用device_add注册了一个设备实例,随后注册了一个led trigger。
- R% B7 s% U' {/ H4 }/ j: [+ A+ O并调用mmc_host_clk_sysfs_init,后者利用sysfs向用户提供了门控相关的信息,' L  T; i# A, e$ p  b

  b& Z! ]6 m) b* D3 k接着调用mmc_start_host来启动MMC控制器的时钟,并且判断SD卡是否已在卡槽中。1 U( t' E' l4 x* v4 F

4 e( r& H' q0 q6 t5 H# N% [: t最后,调用register_pm_notifier向注册了一个用于电源管理的通知链。
- S0 |. ^9 q* D! W" F  z7 I8 s7 x6 K2 {$ ?5 f
很明显,这里调用的5个函数,我们需要关心的是mmc_start_host函数。
$ |1 a4 H/ @# \# g! d. G
! ~) U8 z7 `7 ~1 J. t
# C8 G% s# w' J/ Z( W  M; @, @. h. i$ m5 f+ T
void mmc_start_host(struct mmc_host *host)
$ `4 C- b8 [" O7 D{% s( {4 v+ s, O+ B
        host->f_init = max(freqs[0], host->f_min);- w! ~) `( r" w. t( W1 t1 V+ x( Y
        host->rescan_disable = 0;/ y, q& n$ ?& x0 w+ W, [+ ]
        /* 必须重新scan才能上电,则关闭mmc控制器*/
' W, \) x' s- }/ R        if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
6 N7 x, A/ X4 J* Y5 _1 A7 a8 Z                mmc_power_off(host);        /* 关闭MMC控制器*/
4 F; q4 C6 ~/ ~0 P  P- k0 p        else0 K. l4 g1 c- U" f! E
                mmc_power_up(host);        /* 打卡MMC控制器*/
! _0 U4 Q/ c* R: \7 a- ?        mmc_detect_change(host, 0);4 \; |$ a  h6 |
}4 J1 {5 c/ I+ @1 W8 O- p. R6 u
MMC_CAP2_NO_PRESCAN_POWERUP 表示需要在上电前扫面SD卡,
* \- w2 I/ Z' d+ W8 w如果定义了该宏,则需要关闭MMC控制器的电压,否则打开电源,该宏默认是不定义的。/ F3 U$ K# }- o4 }/ G

* ~/ H. g7 J9 V, r5 R+ qmmc_power_up函数还是比较复杂的,不过它不是我们关心的重点,简单说句,该函数最后会调用在1.1小结中MMC控制器的抽象行为函数中的
% E6 Y  E$ [: U; ]" E5 K" m3 |' S
set_ios来使能MMC控制器的电源。9 _* S! z, g2 k* Y: _9 Y0 a
1 A4 O6 R) o/ o; p; M" b2 e# y' _
该函数最后调用mmc_detect_change来探测SD卡是否存在。
' T. f8 v- \8 S
  x' L' F, ^' \( H. m/**- u1 J9 @: v3 M  @" j) h
*    mmc_detect_change - process change of state on a MMC socket( A$ p$ ?- @% Y4 ?9 f8 P$ Y6 G$ Z9 O- C
*    @host: host which changed state.
0 ]: ?2 m# i- z& ]% ?4 P *    @delay: optional delay to wait before detection (jiffies)9 \) U' H4 ^" b
*
) U0 s( v7 Q/ n. R& A, n2 j *    MMC drivers should call this when they detect a card has been
% k; D$ Z: [' x- Q, y4 o3 i *    inserted or removed. The MMC layer will confirm that any
3 ~7 l& _) c8 T( r! P. R7 `- a% T *    present card is still functional, and initialize any newly5 k; ?, q3 ^  I* M5 D
*    inserted.
+ z4 X$ b: _* z+ s0 {7 x */
! I0 f& `: z1 E" Ovoid mmc_detect_change(struct mmc_host *host, unsigned long delay): a* _& ?& j0 n* ]9 t) g
{
: g9 m! F+ X' U8 S#ifdef CONFIG_MMC_DEBUG
" E7 z. J3 t3 W0 d/ X    unsigned long flags;. j# @1 h1 p8 Y
    spin_lock_irqsave(&host->lock, flags);
9 I* }: d5 C9 u/ L% N, F. a    WARN_ON(host->removed);
5 }3 J& `- B2 \( H% E$ @    spin_unlock_irqrestore(&host->lock, flags);$ t1 V' @1 }' b% H) O8 |, S1 j
#endif( o, q# p2 I1 g- y0 S3 r
    host->detect_change = 1;
/ g; s( H) Z4 _+ ^) H5 _6 R' y    mmc_schedule_delayed_work(&host->detect, delay);8 _6 \6 Y( Y( C; K9 u: V! U3 T
}6 Y5 G: |7 b  |* `

1 G* `) y% F$ D+ c, u最开始的宏中的代码可以直接略过了。/ J5 L+ r- N  T! I& Q" M
将标志位detect_change置1后,函数紧接着调用了mmc_schedule_delayed_work函数,该函数如下:8 m  q9 n; t2 B8 Y" z. g
/*
( p  w& _% K5 d5 j' o/ {7 m * Internal function. Schedule delayed work in the MMC work queue.' \1 p, L( B, d7 P
*/1 a- P. V% `1 m0 U& l# O  e
static int mmc_schedule_delayed_work(struct delayed_work *work,
3 P: _  F9 T) u3 K                                     unsigned long delay)4 x1 i  e# v2 M3 X. @8 Z
{+ ]$ j/ U7 w8 `2 y$ p
        return queue_delayed_work(workqueue, work, delay);$ G2 h+ s) C$ _. O$ L0 b
}+ ~! N( d' A! u  T

) W  k! x+ T/ J% K+ s1 z- ?) ]该函数简单调用了queue_delayed_work来添加一个工作到工作队列workqueue中。
) j- @; _: N2 ]4 V$ z9 A1 n, g在基于S3C2440的嵌入式Linux驱动——MMC/SD子系统解读(一)中的第三章,MMC子系统在初始化时就注册了一个工作队列,并保存到全局变量workqueue中,' m: P3 a+ f; m5 g
# Z6 m6 P6 G9 z) g4 M- z  P
同时函数将detect工作添加到了该工作队列中,而detect工作的执行函数即为在1.1中分析时说道的mmc_rescan函数。( B7 p. y( ?  ?9 a; }
/ z4 v2 m9 X& e; {+ m5 G: Z5 M" o% j
因此该函数的作用就是将detec表示的工作函数mmc_rescan添加到了工作队列中。+ |/ Y/ T0 Q1 e# U/ X$ Y9 x

$ T. X) B5 }- _. D创建完工作队列后,mmc_start_host函数的使命也算结束了,它的存在极其短暂,但是它却调用了一个非常关键的函数mmc_detect_change,' m2 Y) [, D+ I7 P$ h  ~/ z

2 h8 L0 h2 h( y& K) {/ z% Y/ u# Y% j后者创建的工作函数mmc_rescan将会扫描是否有SD卡插入。
) O8 I# e5 F0 ?, A* y4 q
% I8 E6 u( S% c( U% @8 r8 n1 q: O$ v; i& b1 x( @! D3 `. f9 e
) J0 q7 U; u! w! }& i7 _0 j1 W. a
2. SD卡扫描函数mmc_rescan
$ }; d' X- U  Q& i整个mmc_rescan函数执行分为两个部分。
6 Y3 u' j" p/ J7 R. J9 {- v  L/ y
第一部分,探测SD卡是否存在。
$ C: [; t% {4 L! l  J' L- ?4 @7 O* o
第二部分,按照SD规范,初始化SD卡。
+ R& v8 k, w+ u1 T+ f( U& ]) k, ?3 n( S0 R
% B6 d! H. m) X+ q2.1 detect SD卡% S$ t2 o, k* _! f, J
下列代码位于:linux/drivers/mmc/core/core.c1 B6 u0 l) G( Z. N6 P5 ?& X

7 \2 F9 C3 R) j/ |0 xvoid mmc_rescan(struct work_struct *work)
: T' N7 Q% O" ^. z- B" k0 t4 @{
$ h) O' I1 `- x' D" X* y( S- t        struct mmc_host *host =, z0 h/ R0 |* w# [0 s9 C3 f
                container_of(work, struct mmc_host, detect.work);" Y( w+ _2 x& l9 H. K2 [
        int i;: A; x9 a, Z; G- f
- P- ]( P( U( Q, F/ T2 h" M
        /* host禁止再次scan,则直接返回*/- I  C# p2 V* i( X# {# x
        if (host->rescan_disable)
  A% f1 M" _& p3 l+ F                return;
- h4 b  V3 A6 J1 M1 U0 [! e" {# i
) t9 g$ o/ V/ I2 s5 M8 P- C        /* If there is a non-removable card registered, only scan once */
$ T$ m) D+ B6 f: z0 y! o# S        /* 是不可插拔的sd卡,则只scan一次*/
  N9 [; [1 R( E% K. d& Z        if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
2 f3 m* B$ w2 g$ {7 O( r3 a3 }) N                return;
" ~/ m" ?0 J1 x/ x* r* h/ h        host->rescan_entered = 1;  /* 表示至少scan一次了*/1 w5 B1 p9 ~+ }% `( w

7 P% O( B4 \+ V5 u        mmc_bus_get(host);        /* 增加总线引用技术*/# Q1 C1 Q4 |1 p( n4 I3 }

; P( p/ o8 u" `% V        /*9 ^3 w# k' R) A$ o
         * if there is a _removable_ card registered, check whether it is. e( V8 Y# J- \* h$ `7 w& j2 s# P
         * still present( a. g  i# v# h2 W5 j" M6 A
         */2 h6 \& w0 o( a" R
         /* 如果总线提供detect方法则调用*/% J& d: Y$ b- M/ q
        if (host->bus_ops && host->bus_ops->detect && !host->bus_dead7 L* ^4 U5 q7 q, `& k: m0 _
            && !(host->caps & MMC_CAP_NONREMOVABLE))
0 l' d/ S1 K4 p4 e3 E3 E. r6 F4 j                host->bus_ops->detect(host);       
" j2 S$ L. F  u. Q4 d) }/ `2 I/ r: E0 ?6 q9 r0 N8 [/ z" i
        host->detect_change = 0;
! r9 I* D3 K5 X1 E5 _% ]9 l( ^6 y9 W7 g6 u( S
        /*% j3 Z! N2 L6 ~: T! n: H
         * Let mmc_bus_put() free the bus/bus_ops if we've found that
& z/ ?3 A. `: m' d, `         * the card is no longer present.
; M* T4 p! v: J' E         */* T% O8 e7 l% F+ j# ~3 S# L% d9 T9 ]
        mmc_bus_put(host);        /* 减少总线引用技术*/( G0 B' K6 x7 p) {9 c6 a
        mmc_bus_get(host);( y0 y( B6 j+ S* c
3 z6 Z5 _& ]: b  D; l0 I$ u
        /* if there still is a card present, stop here */
* |6 k* N3 S% r3 i6 H        /* 有card存在,无需继续了*/
) q, q0 R' N4 s+ x        if (host->bus_ops != NULL) {
8 r2 t3 i' ^4 x7 T                mmc_bus_put(host);
( f' U% G6 c% @# l( J, i                goto out;8 a/ V' @: V& V, }) Y6 u7 q
        }# Y) r" k$ _& q& L& q7 w. \7 c' e

# X1 S6 r3 Y7 g8 ]; G% F        /*
4 t4 h5 w6 _$ H" @: ~& F& [; C         * Only we can add a new handler, so it's safe to, o  H; Q0 m3 d* y4 }0 }2 H! j6 i
         * release the lock here.! N, d& _$ F$ }+ X. w2 l. D' G6 K
         */
( ~" v0 q! W( \+ U        mmc_bus_put(host);
% }. D  B8 q/ W" d  r5 v" v- i: X, r/ ?- a/ \1 @3 H+ l1 e4 C
        /* 调用get_cd方法,判断card是否存在*/
) _5 [) ?' b! o) `        if (host->ops->get_cd && host->ops->get_cd(host) == 0) {& P6 h5 C7 u: g9 F; s+ L3 y. i
                mmc_claim_host(host);9 B) ?: j) D9 I9 r! `) o5 g- k$ F
                mmc_power_off(host);6 y- ^# \% X0 N
                mmc_release_host(host);8 j& ^! j7 j, _6 h
                goto out;8 i# l% U  A% f9 c0 U/ N/ Y0 Q$ Z
        }
/ b; h- }# p* Y* J' Z1 m9 p0 ?7 U" j- t* o- |( e7 K  J
        0 q3 \) h/ R* Z$ W8 R+ M
        mmc_claim_host(host);* F& k% [" ^, M; J# V
        /**/0 C2 o. |1 t  M5 N, [- b
        for (i = 0; i < ARRAY_SIZE(freqs); i++) {
1 e2 _' M2 I. s% k                if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
1 D' u; J8 b& r! d: r                        break;0 O3 z/ D( \8 Y" {
                if (freqs[i] <= host->f_min)
, q3 z" f0 x1 y+ r% ~- l                        break;
8 M! s: C8 I- {7 F$ L# G        }2 M2 j4 q6 X9 @* O6 p
        mmc_release_host(host);
* `9 p/ T* A& u
0 }, {! D7 ^5 ~9 @8 m# H out:1 n& r( W$ V8 K6 [2 ]0 p
        /* 如果需要再次扫描*/2 U" i* l, u/ A( N/ {2 h
        if (host->caps & MMC_CAP_NEEDS_POLL)
1 C/ H# a0 y6 y: [  b5 _, a8 F0 v                mmc_schedule_delayed_work(&host->detect, HZ);: m/ x! X) D1 W% K- Q
}
5 C* ?2 P: l2 h- a1 L( N. L4 p  i, b5 U" t+ Z4 o* M
该函数首先判断rescan_disable是否为真,如果为真则不用扫描SD卡,直接推出了。8 B4 |2 V2 U+ L/ ^5 f! O. ^) q$ Y9 n
在mmc_start_host时,该变量被置为0,因此这里函数不返回,程序继续往下走。
7 W/ _) L5 D$ B/ f3 b; B7 F8 G9 P8 s# F, R" r
" u# G( U% U8 i. W% c( r5 J
( ~" @, ^) t/ X8 y; N2 }% h$ B

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-25 21:15 , Processed in 0.203125 second(s), 24 queries , Gzip On.

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

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

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