linux設備驅動-SD卡驅動詳解2core層


core層作為整個MMC 的核心,這部分完成了不同協議和規范的實現,並為HOST 層的驅動提供了接口函數。

CORE 部分: 這是整個MMC 的核心存,這部分完成了不同協議和規范的實現,並為HOST 層的驅動提供了接口函數。

HOST 部分是針對不同主機的驅動程序,這一部是驅動程序工程師需要根據自己的特點平台來完成的。

CARD 部分:因為這些記憶卡都是塊設備,當然需要提供塊設備的驅動程序,這部分就是實現了將你的SD 卡如何實現為塊設備的。

它們分布於下面的文件夾中 Linux/drivers/mmc中:

1 數據結構

1.1 mmc_host

定義位於:linux-3.10.73\include\linux\mmc\host.h

 1 struct mmc_host {
 2     struct device        *parent;
 3     struct device        class_dev;
 4     int            index;
 5     const struct mmc_host_ops *ops;
 6     unsigned int        f_min;
 7     unsigned int        f_max;
 8     unsigned int        f_init;
 9     u32            ocr_avail;
10     u32            ocr_avail_sdio;    /* SDIO-specific OCR */
11     u32            ocr_avail_sd;    /* SD-specific OCR */
12     u32            ocr_avail_mmc;    /* MMC-specific OCR */
13     struct notifier_block    pm_notify;
14     u32            max_current_330;
15     u32            max_current_300;
16     u32            max_current_180;
17 
18 ...
19 
20     unsigned long        private[0] ____cacheline_aligned;
21 }

1.2 總線

platform bus //MMC host controller 作為一種 platform device, 它是需要注冊到 platform bus上 的  。

1 struct bus_type platform_bus_type = {  
2     .name        = "platform",  
3     .dev_attrs    = platform_dev_attrs,  
4     .match        = platform_match,  
5     .uevent        = platform_uevent,  
6     .pm        = &platform_dev_pm_ops,  
7 }; 

mmc bus type  ,mmc_init()中被創建的.通過調用 mmc_register_bus() 來注冊 MMC 總線  。定義位於:drivers\mmc\core\bus.c

1 static struct bus_type mmc_bus_type = {
2     .name        = "mmc",
3     .dev_attrs    = mmc_dev_attrs,
4     .match        = mmc_bus_match,
5     .uevent        = mmc_bus_uevent,
6     .probe        = mmc_bus_probe,
7     .remove        = mmc_bus_remove,
8     .pm        = &mmc_bus_pm_ops,
9 };

sdio bus type,在mmc_init()中被創建的.通過調用sdio_register_bus() 來注冊 SDIO 總線 。定義位於:drivers\mmc\core\sdio_bus.c

1 static struct bus_type sdio_bus_type = {
2     .name        = "sdio",
3     .dev_attrs    = sdio_dev_attrs,
4     .match        = sdio_bus_match,
5     .uevent        = sdio_bus_uevent,
6     .probe        = sdio_bus_probe,
7     .remove        = sdio_bus_remove,
8     .pm        = SDIO_PM_OPS_PTR,
9 };

1.3 操作結構體

其中mmc總線操作相關函數,由於mmc卡支持多種總數據線,如SPI、SDIO、8LineMMC而不同的總線的操作控制方式不盡相同,所以通過此結構與相應的總線回調函數相關聯。

sdio的總線操作 core/sdio.c

1 static const struct mmc_bus_ops mmc_sdio_ops = {
2     .remove = mmc_sdio_remove,
3     .detect = mmc_sdio_detect,
4     .suspend = mmc_sdio_suspend,
5     .resume = mmc_sdio_resume,
6     .power_restore = mmc_sdio_power_restore,
7     .alive = mmc_sdio_alive,
8 };

mmc卡的總線操作 core/mmc.c

 1 static const struct mmc_bus_ops mmc_ops = {
 2     .awake = mmc_awake,
 3     .sleep = mmc_sleep,
 4     .remove = mmc_remove,
 5     .detect = mmc_detect,
 6     .suspend = NULL,
 7     .resume = NULL,
 8     .power_restore = mmc_power_restore,
 9     .alive = mmc_alive,
10 };

sd卡的總線操作 core/sd.c

1 static const struct mmc_bus_ops mmc_sd_ops = {
2     .remove = mmc_sd_remove,
3     .detect = mmc_sd_detect,
4     .suspend = NULL,
5     .resume = NULL,
6     .power_restore = mmc_sd_power_restore,
7     .alive = mmc_sd_alive,
8 };

.detect操作函數:驅動程序經常需要調用此函數去檢測mmc卡的狀態,具體實現是發送CMD13命令,並讀回響應,如果響應錯誤,則依次調用.removedetach_bus來移除卡及釋放總線。

2 core的flow

內核啟動時,首先執行core/core.c的mmc_init,注冊mmc、sd總線,以及一個host class設備。接着執行card/block.c中mmc_blk_init(),申請一個塊設備。

2.1 初始化

2.1.1 mmc_init

定義位於:linux-3.10.73\drivers\mmc\core\core.c

 1 static int __init mmc_init(void)
 2 {
 3     int ret;
 4 
 5     workqueue = alloc_ordered_workqueue("kmmcd", 0);//建立隊列,主要用來支持熱插熱拔
 6     if (!workqueue)
 7         return -ENOMEM;
 8 
 9     ret = mmc_register_bus();//注冊mmc總線
10     if (ret)
11         goto destroy_workqueue;
12 
13     ret = mmc_register_host_class();//注冊mmc host class
14     if (ret)
15         goto unregister_bus;
16 
17     ret = sdio_register_bus();//注冊SDIO總線
18     if (ret)
19         goto unregister_host_class;
20 
21     return 0;
22 
23 unregister_host_class:
24     mmc_unregister_host_class();
25 unregister_bus:
26     mmc_unregister_bus();
27 destroy_workqueue:
28     destroy_workqueue(workqueue);
29 
30     return ret;
31 }

2.1.2 函數mmc_blk_init

定義位於:mmc/card/block.c

 1 static int __init mmc_blk_init(void)
 2 {
 3     int res;
 4 
 5     if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
 6         pr_info("mmcblk: using %d minors per device\n", perdev_minors);
 7 
 8     max_devices = 256 / perdev_minors;
 9 
10     res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");//注冊一個塊設備
11     if (res)
12         goto out;
13 
14     res = mmc_register_driver(&mmc_driver);//注冊一個mmc設備驅動
15     if (res)
16         goto out2;
17 
18     return 0;
19  out2:
20     unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
21  out:
22     return res;
23 }

mmc_driver

1 static struct mmc_driver mmc_driver = {
2     .drv        = {
3         .name    = "mmcblk",
4     },
5     .probe        = mmc_blk_probe,
6     .remove        = mmc_blk_remove,
7     .suspend    = mmc_blk_suspend,
8     .resume        = mmc_blk_resume,
9 };

mmc_driver probe函數

 1 static int mmc_blk_probe(struct mmc_card *card)
 2 {
 3     struct mmc_blk_data *md, *part_md;
 4     char cap_str[10];
 5 
 6     /*
 7      * Check that the card supports the command class(es) we need.
 8      */
 9     if (!(card->csd.cmdclass & CCC_BLOCK_READ))
10         return -ENODEV;
11 
12     md = mmc_blk_alloc(card);
13     if (IS_ERR(md))
14         return PTR_ERR(md);
15 
16     string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
17             cap_str, sizeof(cap_str));
18     pr_info("%s: %s %s %s %s\n",
19         md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
20         cap_str, md->read_only ? "(ro)" : "");
21 
22     if (mmc_blk_alloc_parts(card, md))
23         goto out;
24 
25     mmc_set_drvdata(card, md);
26     mmc_fixup_device(card, blk_fixups);
27 
28     if (mmc_add_disk(md))
29         goto out;
30 
31     list_for_each_entry(part_md, &md->part, part) {
32         if (mmc_add_disk(part_md))
33             goto out;
34     }
35     return 0;
36 
37  out:
38     mmc_blk_remove_parts(card, md);
39     mmc_blk_remove_req(md);
40     return 0;
41 }

2.2 注冊mmc總線

定義位於:drivers\mmc\core\bus.c

 1 int mmc_register_bus(void)
 2 {
 3     return bus_register(&mmc_bus_type);//注冊bus
 4 }
 5 
 6 static struct bus_type mmc_bus_type = {
 7     .name        = "mmc",
 8     .dev_attrs    = mmc_dev_attrs,
 9     .match        = mmc_bus_match,
10     .uevent        = mmc_bus_uevent,
11     .probe        = mmc_bus_probe,
12     .remove        = mmc_bus_remove,
13     .pm        = &mmc_bus_pm_ops,
14 };

2.3 注冊host class

定義位於:drivers\mmc\core\host.c

1 static struct class mmc_host_class = {
2     .name        = "mmc_host",
3     .dev_release    = mmc_host_classdev_release,
4 };
5 
6 int mmc_register_host_class(void)
7 {
8     return class_register(&mmc_host_class);
9 }

2.4 注冊sdio總線

定義位於:drivers\mmc\core\sdio_bus.c

 1 static struct bus_type sdio_bus_type = {
 2     .name        = "sdio",
 3     .dev_attrs    = sdio_dev_attrs,
 4     .match        = sdio_bus_match,
 5     .uevent        = sdio_bus_uevent,
 6     .probe        = sdio_bus_probe,
 7     .remove        = sdio_bus_remove,
 8     .pm        = SDIO_PM_OPS_PTR,
 9 };
10 
11 int sdio_register_bus(void)
12 {
13     return bus_register(&sdio_bus_type);
14 }

2.5 mmc_rescan函數

mmc_rescan 函數是需要重點關注的,因為SD卡協議中的檢測,以及卡識別等都是在此函數中實現。

插入SD卡,主控制器產生中斷,進入中斷處理函數s3cmci_irq_cd,其中調用的函數 mmc_detect_change,它將最終調用queue_delayed_work執行工作隊列里的mmc_rescan函數。

mmc_rescan掃描SD總線上是否存在SD卡,是mmc host的detect work的功能函數,用於探測目標卡的類型並且根據mmc/sd/sdio協議進行comm的初始化。

 1 void mmc_rescan(struct work_struct *work)
 2 {
 3     struct mmc_host *host =
 4         container_of(work, struct mmc_host, detect.work);//
 5     int i;
 6 
 7     if (host->rescan_disable)
 8         return;
 9 
10     /* If there is a non-removable card registered, only scan once */
11     if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
12         return;
13     host->rescan_entered = 1;
14 
15     mmc_bus_get(host);//
16 
17     /*
18      * if there is a _removable_ card registered, check whether it is
19      * still present
20      */
21     if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
22         && !(host->caps & MMC_CAP_NONREMOVABLE))
23         host->bus_ops->detect(host);//
24 
25     host->detect_change = 0;//
26 
27     /*
28      * Let mmc_bus_put() free the bus/bus_ops if we've found that
29      * the card is no longer present.
30      */
31     mmc_bus_put(host);//減少引用計數,即釋放
32     mmc_bus_get(host);//增加bus引用計數
33 
34     /* if there still is a card present, stop here */
35     if (host->bus_ops != NULL) {
36         mmc_bus_put(host);//如果卡仍然存在,減少引用計數,不必探測了
37         goto out;
38     }
39 
40     /*
41      * Only we can add a new handler, so it's safe to
42      * release the lock here.
43      */
44     mmc_bus_put(host);//
45 
46     if (host->ops->get_cd && host->ops->get_cd(host) == 0) {//有卡,退出
47         mmc_claim_host(host);//
48         mmc_power_off(host);//
49         mmc_release_host(host);//
50         goto out;
51     }
52 
53     mmc_claim_host(host);//
54     for (i = 0; i < ARRAY_SIZE(freqs); i++) {
55         if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))//
56             break;
57         if (freqs[i] <= host->f_min)
58             break;
59     }
60     mmc_release_host(host);//
61 
62  out:
63     if (host->caps & MMC_CAP_NEEDS_POLL)
64         mmc_schedule_delayed_work(&host->detect, HZ);//
65 }

初始化卡接以下流程初始化:

發送CMD0使卡進入IDLE狀態
發送CMD8,檢查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出了先發送CMD8,如響應為無效命令,則卡為SD1.1,否則就是SD2.0(請參考SD2.0 Spec)。
發送CMD5讀取OCR寄存器。
發送ACMD55、CMD41,使卡進入工作狀態。MMC卡並不支持ACMD55、CMD41,如果這步通過了,則證明這張卡是SD卡。
如果d步驟錯誤,則發送CMD1判斷卡是否為MMC。SD卡不支持CMD1,而MMC卡支持,這就是SD和MMC類型的判斷依據。
如果ACMD41和CMD1都不能通過,那這張卡恐怕就是無效卡了,初始化失敗。

假如掃描到總線上掛有有效的設備,就調用相對應的函數把設備裝到系統中,mmc_attach_sdio()、mmc_attach_sd()、mmc_attach_mmc()這三個函數分別是裝載sdio設備,sd卡和mmc卡的。

在 sd卡中,驅動循環發送ACMD41、CMD55給卡,讀取OCR寄存器,成功后,依次發送CMD2(讀CID)、CMD3(得到RCA)、CMD9(讀 CSD)、CMD7(選擇卡)。后面還有幾個命令分別是ACMD41&CMD51,使用CMD6切換一些功能,如切換到高速模式。

經過上述步驟,已經確定當前插入的卡是一張有效、可識別的存儲卡。然后調用mmc_add_card()把存儲卡加到系統中。正式與系統驅動連接在一起。

2.6 函數mmc_rescan_try_freq

 1 static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
 2 {
 3     host->f_init = freq; //根據mmc_recan函數傳進來的參數知道,首先傳進來的是400kHZ.一般mmc/sd/sdio的初始化時鍾采用的是400kHZ
 4 
 5 #ifdef CONFIG_MMC_DEBUG
 6     pr_info("%s: %s: trying to init card at %u Hz\n",
 7         mmc_hostname(host), __func__, host->f_init);
 8 #endif
 9     mmc_power_up(host);//上電,我們知道,在mmc_add_host時,會調用mmc_start_host,而那里首先是將host掉電的。這里上電
10 
11     /*
12      * Some eMMCs (with VCCQ always on) may not be reset after power up, so
13      * do a hardware reset if possible.
14      */
15     mmc_hw_reset_for_init(host);//復位硬件,可以選擇性實現。
16 
17     /*
18      * sdio_reset sends CMD52 to reset card.  Since we do not know
19      * if the card is being re-initialized, just send it.  CMD52
20      * should be ignored by SD/eMMC cards.
21      */
22     sdio_reset(host);
23     mmc_go_idle(host);//發送CMD0 復位SD卡
24 
25     mmc_send_if_cond(host, host->ocr_avail);
26     /*Linux 卡的探測順序是:先辨別卡是否是sdio功能卡或者是sdio與sd卡的組合卡,然后辨別是否是SD卡,最后才會辨別是否是mmc卡*/
27     /* Order's important: probe SDIO, then SD, then MMC */
28     if (!mmc_attach_sdio(host))//匹配sdio接口
29         return 0;
30     if (!mmc_attach_sd(host))
31         return 0;
32     if (!mmc_attach_mmc(host))
33         return 0;
34 
35     mmc_power_off(host);
36     return -EIO;
37 }

函數sdio_reset(host);/的作用:

(1)如果目標卡是純SD卡(對MMC卡不了解,所以不加評論),則目標卡不會應答,一般主機host的寄存器會報錯,但是這個無關緊要,可以不理它。

(2)如果目標卡是純SDIO卡,那么這里就是復位SDIO卡,通過命令CMD52來實現的。

(3)如果目標卡是SD卡和SDIO卡的組合卡,則需要先發送CMD52來復位SDIO卡,再復位SD卡,因為CMD52要先於CMD0發送。

函數mmc_send_if_cond(host, host->ocr_avail);的作用 :

為了支持sd version 2.0以上的sd卡,在初始化的過程中必須在發送ACMD41之前,先發送CMD8,CMD8一般是用於檢測SD卡是否能運行在host提供的電壓范圍內。大家可能發現,這個調用過程沒有檢查是否出錯,其實一般CMD8是用來辨別目標卡是否是高容量SD卡,如果是,CMD8 會有R7應答,R7應答中會有目標SD卡支持的電壓范圍以及CMD8中發送過去的“check pattern(一般是0xAA)”,否則,目標卡不會應答,在Linux 內核代碼中,判斷是這樣的,如果應答,目標卡就是SD高容量卡,否則出現應答超時錯誤,就是標准SD卡!這里的調用,主要作用是為了在發送ACMD41之前發送CMD8,這是version 2.0及以上的規定順序,后面還會有發送CMD8的地方,那里才是真正檢測目標卡的類型的地方。

2.6.1 函數mmc_go_idle

 1 int mmc_go_idle(struct mmc_host *host)    
 2     struct mmc_command cmd = {0};  
 3     cmd.opcode = MMC_GO_IDLE_STATE; //即CMD0  
 4     cmd.arg = 0;                    //此命令無參數  
 5     err = mmc_wait_for_cmd(host, &cmd, 0)  
 6       
 7 int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)  
 8     memset(cmd->resp, 0, sizeof(cmd->resp));  //調用了 mmc_start_request,   
 9     cmd->retries = retries;  
10     mrq.cmd = cmd;                                
11     mmc_wait_for_req(host, &mrq);  
12       
13 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)   ----重要函數  
14     __mmc_start_req(host, mrq);  
15   
16 static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)  
17     mmc_start_request(host, mrq);  
18           
19 static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)  
20     host->ops->request(host, mrq);    //即 msmsdcc_request, MMC 核心與核HOST 層握手了 

host即struct mmc_host類型,在drivers\mmc\host\s3cmci.c的probe函數中定義賦值:

 1 static int s3cmci_probe(struct platform_device *pdev)
 2 {
 3     ...
 4     mmc->ops     = &s3cmci_ops;
 5         ...
 6 }
 7 
 8 static struct mmc_host_ops s3cmci_ops = {
 9     .request    = s3cmci_request,
10     .set_ios    = s3cmci_set_ios,
11     .get_ro        = s3cmci_get_ro,
12     .get_cd        = s3cmci_card_present,
13     .enable_sdio_irq = s3cmci_enable_sdio_irq,
14 };

request函數分析見linux設備驅動-SD卡驅動詳解3 host層

2.7 sdio的掛載

2.7.1 函數mmc_attach_sdio

定義位於:drivers\mmc\core\sdio.c

  1 int mmc_attach_sdio(struct mmc_host *host)
  2 {
  3     int err, i, funcs;
  4     u32 ocr;
  5     struct mmc_card *card;
  6 
  7     BUG_ON(!host);
  8     WARN_ON(!host->claimed);
  9 
 10     err = mmc_send_io_op_cond(host, 0, &ocr);//
 11     if (err)
 12         return err;
 13 
 14     mmc_attach_bus(host, &mmc_sdio_ops);//
 15     if (host->ocr_avail_sdio)
 16         host->ocr_avail = host->ocr_avail_sdio;
 17 
 18     /*
 19      * Sanity check the voltages that the card claims to
 20      * support.
 21      */
 22     if (ocr & 0x7F) {
 23         pr_warning("%s: card claims to support voltages "
 24                "below the defined range. These will be ignored.\n",
 25                mmc_hostname(host));
 26         ocr &= ~0x7F;
 27     }
 28 
 29     host->ocr = mmc_select_voltage(host, ocr);//設置時鍾和總線  ---------------- 詳解1
 30 
 31     /*
 32      * Can we support the voltage(s) of the card(s)?
 33      */
 34     if (!host->ocr) {
 35         err = -EINVAL;
 36         goto err;
 37     }
 38 
 39     /*
 40      * Detect and init the card.
 41      */
 42     if (mmc_host_uhs(host))
 43         /* to query card if 1.8V signalling is supported */
 44         host->ocr |= R4_18V_PRESENT;
 45 
 46     err = mmc_sdio_init_card(host, host->ocr, NULL, 0);//
 47     if (err) {
 48         if (err == -EAGAIN) {
 49             /*
 50              * Retry initialization with S18R set to 0.
 51              */
 52             host->ocr &= ~R4_18V_PRESENT;
 53             err = mmc_sdio_init_card(host, host->ocr, NULL, 0);//
 54         }
 55         if (err)
 56             goto err;
 57     }
 58     card = host->card;
 59 
 60     /*
 61      * Enable runtime PM only if supported by host+card+board
 62      */
 63     if (host->caps & MMC_CAP_POWER_OFF_CARD) {
 64         /*
 65          * Let runtime PM core know our card is active
 66          */
 67         err = pm_runtime_set_active(&card->dev);//
 68         if (err)
 69             goto remove;
 70 
 71         /*
 72          * Enable runtime PM for this card
 73          */
 74         pm_runtime_enable(&card->dev);//
 75     }
 76 
 77     /*
 78      * The number of functions on the card is encoded inside
 79      * the ocr.
 80      */
 81     funcs = (ocr & 0x70000000) >> 28;
 82     card->sdio_funcs = 0;
 83 
 84     /*
 85      * Initialize (but don't add) all present functions.
 86      */
 87     for (i = 0; i < funcs; i++, card->sdio_funcs++) {
 88         err = sdio_init_func(host->card, i + 1);
 89         if (err)
 90             goto remove;
 91 
 92         /*
 93          * Enable Runtime PM for this func (if supported)
 94          */
 95         if (host->caps & MMC_CAP_POWER_OFF_CARD)
 96             pm_runtime_enable(&card->sdio_func[i]->dev);
 97     }
 98 
 99     /*
100      * First add the card to the driver model...
101      */
102     mmc_release_host(host);
103     err = mmc_add_card(host->card);//注冊一個mmc card
104     if (err)
105         goto remove_added;
106 
107     /*
108      * ...then the SDIO functions.
109      */
110     for (i = 0;i < funcs;i++) {
111         err = sdio_add_func(host->card->sdio_func[i]);//注冊一個sdio func  
112         if (err)
113             goto remove_added;
114     }
115 
116     mmc_claim_host(host);//
117     return 0;
118 
119 
120 remove_added:
121     /* Remove without lock if the device has been added. */
122     mmc_sdio_remove(host);
123     mmc_claim_host(host);//
124 remove:
125     /* And with lock if it hasn't been added. */
126     mmc_release_host(host);//
127     if (host->card)
128         mmc_sdio_remove(host);
129     mmc_claim_host(host);
130 err:
131     mmc_detach_bus(host);//
132 
133     pr_err("%s: error %d whilst initialising SDIO card\n",
134         mmc_hostname(host), err);
135 
136     return err;
137 }

詳解1:mmc_select_voltage函數設置時鍾和總線,協議層里利用回調函數為所有滿足該協議的設備提供統一的接口,而具體實現由底層不同的設備驅動各自完成。注意到,之所以要定義一些放之四海而皆准的公用的類,比如struct mmc_host,就是需要通過struct mmc_host *host指針作為形參傳到協議層所提供的接口函數中,從而得以調用。

 1 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
 2 {
 3     mmc_set_ios(host);
 4  
 5     ... ...
 6 }
 7  
 8 static inline void mmc_set_ios(struct mmc_host *host)
 9 {
10     struct mmc_ios *ios = &host->ios;
11  
12     host->ops->set_ios(host, ios);  // 設置主控制器時鍾和總線的回調函數,具體實現由主控制器驅動完成
13 }

2.7.2 函數mmc_add_card

定義位於:drivers\mmc\core\bus.c

最終call device_add,就是將card注冊進linux設備模型 ,注冊結果就是可以在/sys/bus/mmc/devices目錄下見到card 的名字,如mmc2:0001

 1 /*
 2  * Register a new MMC card with the driver model.
 3  */
 4 int mmc_add_card(struct mmc_card *card)
 5 {
 6     int ret;
 7     const char *type;
 8     const char *uhs_bus_speed_mode = "";
 9     static const char *const uhs_speeds[] = {
10         [UHS_SDR12_BUS_SPEED] = "SDR12 ",
11         [UHS_SDR25_BUS_SPEED] = "SDR25 ",
12         [UHS_SDR50_BUS_SPEED] = "SDR50 ",
13         [UHS_SDR104_BUS_SPEED] = "SDR104 ",
14         [UHS_DDR50_BUS_SPEED] = "DDR50 ",
15     };
16 
17 
18     dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);//設置device 名字
19 
20     switch (card->type) {
21     case MMC_TYPE_MMC:
22         type = "MMC";
23         break;
24     case MMC_TYPE_SD:
25         type = "SD";
26         if (mmc_card_blockaddr(card)) {
27             if (mmc_card_ext_capacity(card))
28                 type = "SDXC";
29             else
30                 type = "SDHC";
31         }
32         break;
33     case MMC_TYPE_SDIO:
34         type = "SDIO";
35         break;
36     case MMC_TYPE_SD_COMBO:
37         type = "SD-combo";
38         if (mmc_card_blockaddr(card))
39             type = "SDHC-combo";
40         break;
41     default:
42         type = "?";
43         break;
44     }
45 
46     if (mmc_sd_card_uhs(card) &&
47         (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
48         uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
49 
50     if (mmc_host_is_spi(card->host)) {
51         pr_info("%s: new %s%s%s card on SPI\n",
52             mmc_hostname(card->host),
53             mmc_card_highspeed(card) ? "high speed " : "",
54             mmc_card_ddr_mode(card) ? "DDR " : "",
55             type);
56     } else {
57         pr_info("%s: new %s%s%s%s%s card at address %04x\n",
58             mmc_hostname(card->host),
59             mmc_card_uhs(card) ? "ultra high speed " :
60             (mmc_card_highspeed(card) ? "high speed " : ""),
61             (mmc_card_hs200(card) ? "HS200 " : ""),
62             mmc_card_ddr_mode(card) ? "DDR " : "",
63             uhs_bus_speed_mode, type, card->rca);
64     }
65 
66 #ifdef CONFIG_DEBUG_FS
67     mmc_add_card_debugfs(card);
68 #endif
69     mmc_init_context_info(card->host);
70 
71     ret = device_add(&card->dev);//添加設備device
72     if (ret)
73         return ret;
74 
75     mmc_card_set_present(card);
76 
77     return 0;
78 }

2.7.3 sdio總線上driver

以上已向系統添加設備device。以TIdrivers\net\wireless\ti\wlcore\sdio.c的wlan為例:

 1 static struct sdio_driver wl1271_sdio_driver = {
 2     .name        = "wl1271_sdio",
 3     .id_table    = wl1271_devices,
 4     .probe        = wl1271_probe,
 5     .remove        = wl1271_remove,
 6 #ifdef CONFIG_PM
 7     .drv = {
 8         .pm = &wl1271_sdio_pm_ops,
 9     },
10 #endif
11 };
12 
13 static int __init wl1271_init(void)
14 {
15     return sdio_register_driver(&wl1271_sdio_driver);//向sdio總線注冊driver
16 }

函數sdio_register_driver,定義位於:drivers\mmc\core\sdio_bus.c

 1 /**
 2  *    sdio_register_driver - register a function driver
 3  *    @drv: SDIO function driver
 4  */
 5 int sdio_register_driver(struct sdio_driver *drv)
 6 {
 7     drv->drv.name = drv->name;
 8     drv->drv.bus = &sdio_bus_type;
 9     return driver_register(&drv->drv);
10 }

總結:(1)以上device和driver是掛在sdio總線上的,sdio總線在kernel啟動時加載的;

(2)以drivers\mmc\host\s3cmci.c為例作為host,sdio設備插入時產生中斷,調用mmc_resan建立host等工作,最終添加sdio總線上的設備device;

(3)以下的mmc總線和sd卡也是類似。

2.8 sd卡的掛載

2.8.1 函數mmc_attach_sd

完成匹配,和初始化卡的功能 。定義位於:drivers\mmc\core\sd.c

 1 /*
 2  * Starting point for SD card init.
 3  */
 4 int mmc_attach_sd(struct mmc_host *host)
 5 {
 6     int err;
 7     u32 ocr;
 8 
 9     BUG_ON(!host);
10     WARN_ON(!host->claimed);
11 
12     err = mmc_send_app_op_cond(host, 0, &ocr);//檢測是否是支持SD卡
13     if (err)
14         return err;
15 
16     mmc_sd_attach_bus_ops(host);
17     if (host->ocr_avail_sd)
18         host->ocr_avail = host->ocr_avail_sd;
19 
20     /*
21      * We need to get OCR a different way for SPI.
22      */
23     if (mmc_host_is_spi(host)) {
24         mmc_go_idle(host);
25 
26         err = mmc_spi_read_ocr(host, 0, &ocr);
27         if (err)
28             goto err;
29     }
30 
31     /*
32      * Sanity check the voltages that the card claims to
33      * support.
34      */
35     if (ocr & 0x7F) {
36         pr_warning("%s: card claims to support voltages "
37                "below the defined range. These will be ignored.\n",
38                mmc_hostname(host));
39         ocr &= ~0x7F;
40     }
41 
42     if ((ocr & MMC_VDD_165_195) &&
43         !(host->ocr_avail_sd & MMC_VDD_165_195)) {
44         pr_warning("%s: SD card claims to support the "
45                "incompletely defined 'low voltage range'. This "
46                "will be ignored.\n", mmc_hostname(host));
47         ocr &= ~MMC_VDD_165_195;
48     }
49 
50     host->ocr = mmc_select_voltage(host, ocr);//設置MMC電壓
51 
52     /*
53      * Can we support the voltage(s) of the card(s)?
54      */
55     if (!host->ocr) {
56         err = -EINVAL;
57         goto err;
58     }
59 
60     /*
61      * Detect and init the card.
62      */
63     err = mmc_sd_init_card(host, host->ocr, NULL);//對mmc卡進行初始化,主要是讀取mmc卡里的一些寄存器信息,且對這些寄存器的值進行設置
64     if (err)
65         goto err;
66 
67     mmc_release_host(host);
68     err = mmc_add_card(host->card);//調用 mmc_add_card 來把 mmc_card 掛載到 mmc_bus_type 總線去
69     mmc_claim_host(host);
70     if (err)
71         goto remove_card;
72 
73     return 0;
74 
75 remove_card:
76     mmc_release_host(host);
77     mmc_remove_card(host->card);
78     host->card = NULL;
79     mmc_claim_host(host);
80 err:
81     mmc_detach_bus(host);
82 
83     pr_err("%s: error %d whilst initialising SD card\n",
84         mmc_hostname(host), err);
85 
86     return err;
87 }

2.8.2 函數mmc_send_app_op_cond

定義位於:drivers\mmc\core\sd_ops.c

1 int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)  
2 {
3     ...
4     cmd.opcode = SD_APP_OP_COND;    //ACMD41,獲取 SDcard 的允許電壓范圍值,保存在 ocr 中. 所有發送它之前需要發送 CMD_55 命令。執行完后 card 狀態變為 READY
5   ...
6 }

2.8.3 函數mmc_sd_init_card

 1 static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard)  
 2 {
 3    ...
 4     err = mmc_sd_get_cid(host, ocr, cid, &rocr);        //發送 CMD2 ,獲取卡的身份信息,進入到身份狀態  
 5    ...
 6     card = mmc_alloc_card(host, &sd_type);              //分配一張 SD 類型的 card 結構  
 7    ...
 8     err = mmc_send_relative_addr(host, &card->rca);      //獲取卡的相對地址,注意一前卡和主機通信都采用默認地址,現在有了自己的地址了,進入到 stand_by 狀態  
 9    ...
10     err = mmc_sd_get_csd(host, card); ----mmc_send_csd(card, card->raw_csd);//CMD9, 獲取 CSD 寄存器的信息,包括 block 長度,卡容量等信息  
11    ...
12     err = mmc_select_card(card);                        //發送 CMD7, 選中目前 RADD 地址上的卡,任何時候總線上只有一張卡被選中,進入了傳輸狀態   
13    ...
14     err = mmc_sd_setup_card(host, card, oldcard != NULL);     
15     ...
16 }

函數mmc_sd_setup_card

1 int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,bool reinit)  
2 {
3    ...
4     mmc_app_send_scr(card, card->raw_scr);   //發送命令 ACMD51 獲取 SRC 寄存器的內容,進入到 SENDING-DATA 狀態 
5    ... 
6     if (host->ops->get_ro(host) > 0 )      // get_ro(host) 即是 msmsdcc_get_ro   
7         mmc_card_set_readonly(card);        //是否寫保護,如果是的,將 card 狀態設置為只讀狀態 
8    ...
9 }

卡設備加到系統中后,對於塊設備來說,會通知mmc塊設備驅動。塊設備驅動此時調用probe函數,即mmc_blk_probe()函數,mmc_blk_probe() 先分配一個新的mmc_blk_data結構變量,然后調用mmc_init_queue,初始化blk隊列。然后建立一個線程 mmc_queue_thread()。

3 sdio core總結

   

 mmc_rescan函數總結

參考博文:https://blog.csdn.net/zqixiao_09/article/list/5


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM