linux SD卡初始化---mmc_sd_init_card函數

 

爲了學習SD/SDIO協議,看了一下linux中初始化SD卡的流程,結合代碼更容易SD初始化是怎麼作的。linux

下面圖截自:"SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10"app

SD卡在sd模式下的初始化流程圖,sd協議還有spi模式暫不研究。ide

這個流程圖對應於linux 代碼就在函數

/driver/mmc/core/sd.c學習

static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)ui

傳入參數spa

truct mmc_host *host mmc/sd/sdio主機控器的結構,成員用到再說明code

u32 ocr 這個比較重要,與ACMD41和sd卡中ocr寄存器相關。調用mmc_sd_init_card以前blog

linux已經作過一些工做,就是發送ACMD41獲取SD卡工做電壓,與Host支持電壓匹配並設置host的電壓,而後調用mmc_sd_init_card,並把電壓信息經過ocr傳遞下來。流程圖開始時這些工做已經作好。ci

struct mmc_card *oldcard  新插入的卡初始化時該值爲NULL。

linux版本3.7 mmc_sd_init_card函數:

  1 /*
  2  * Handle the detection and initialisation of a card.
  3  *
  4  * In the case of a resume, "oldcard" will contain the card
  5  * we're trying to reinitialise.
  6  */
  7 static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
  8     struct mmc_card *oldcard)
  9 {
 10     struct mmc_card *card;
 11     int err;
 12     u32 cid[4];
 13     u32 rocr = 0;
 14 
 15     BUG_ON(!host);
 16     WARN_ON(!host->claimed);
 17 
 18     err = mmc_sd_get_cid(host, ocr, cid, &rocr);
 19     if (err)
 20         return err;
 21 
 22     if (oldcard) {
 23         if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
 24             return -ENOENT;
 25 
 26         card = oldcard;
 27     } else {
 28         /*
 29          * Allocate card structure.
 30          */
 31         card = mmc_alloc_card(host, &sd_type);
 32         if (IS_ERR(card))
 33             return PTR_ERR(card);
 34 
 35         card->type = MMC_TYPE_SD;
 36         memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
 37     }
 38 
 39     /*
 40      * For native busses:  get card RCA and quit open drain mode.
 41      */
 42     if (!mmc_host_is_spi(host)) {
 43         err = mmc_send_relative_addr(host, &card->rca);
 44         if (err)
 45             return err;
 46     }
 47 
 48     if (!oldcard) {
 49         err = mmc_sd_get_csd(host, card);
 50         if (err)
 51             return err;
 52 
 53         mmc_decode_cid(card);
 54     }
 55 
 56     /*
 57      * Select card, as all following commands rely on that.
 58      */
 59     if (!mmc_host_is_spi(host)) {
 60         err = mmc_select_card(card);
 61         if (err)
 62             return err;
 63     }
 64 
 65     err = mmc_sd_setup_card(host, card, oldcard != NULL);
 66     if (err)
 67         goto free_card;
 68 
 69     /* Initialization sequence for UHS-I cards */
 70     if (rocr & SD_ROCR_S18A) {
 71         err = mmc_sd_init_uhs_card(card);
 72         if (err)
 73             goto free_card;
 74 
 75         /* Card is an ultra-high-speed card */
 76         mmc_card_set_uhs(card);
 77 
 78         /*
 79          * Since initialization is now complete, enable preset
 80          * value registers for UHS-I cards.
 81          */
 82         if (host->ops->enable_preset_value) {
 83             mmc_host_clk_hold(card->host);
 84             host->ops->enable_preset_value(host, true);
 85             mmc_host_clk_release(card->host);
 86         }
 87     } else {
 88         /*
 89          * Attempt to change to high-speed (if supported)
 90          */
 91         err = mmc_sd_switch_hs(card);
 92         if (err > 0)
 93             mmc_sd_go_highspeed(card);
 94         else if (err)
 95             goto free_card;
 96 
 97         /*
 98          * Set bus speed.
 99          */
100         mmc_set_clock(host, mmc_sd_get_max_clock(card));
101 
102         /*
103          * Switch to wider bus (if supported).
104          */
105         if ((host->caps & MMC_CAP_4_BIT_DATA) &&
106             (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
107             err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
108             if (err)
109                 goto free_card;
110 
111             mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
112         }
113     }
114 
115     host->card = card;
116     return 0;
117 
118 free_card:
119     if (!oldcard)
120         mmc_remove_card(card);
121 
122     return err;
123 }

18行,err = mmc_sd_get_cid(host, ocr, cid, &rocr);字面意思就是獲取CID,對照着流程圖給該函數註釋:

 1 /*
 2  * Fetch CID from card.
 3  */
 4 int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
 5 {
 6     int err;
 7     u32 max_current;
 8 
 9     /*
10      * Since we're changing the OCR value, we seem to
11      * need to tell some cards to go back to the idle
12      * state.  We wait 1ms to give cards time to
13      * respond.
14      */
15     mmc_go_idle(host);//發送CMD0
16 
17     /*
18      * If SD_SEND_IF_COND indicates an SD 2.0
19      * compliant card and we should set bit 30
20      * of the ocr to indicate that we can handle
21      * block-addressed SDHC cards.
22      */
23     err = mmc_send_if_cond(host, ocr);//發送CMD8,
24 if (!err) 25 ocr |= SD_OCR_CCS;//若是返回失敗,說明卡不是SD2.0或以後的版本,若是是2.0的SD卡,把ocr的30位置1,即協議裏ACMD41命令中的HCS.
26 表示支持SDHC或SDXC,ACMD41命令見下面圖示
27 /* 28 * If the host supports one of UHS-I modes, request the card 29 * to switch to 1.8V signaling level. 30 */ 31 if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | 32 MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)) 33 ocr |= SD_OCR_S18R; //判斷主機控制器是否支持UHS-I host->caps一般在主機控制器驅動的probe函數裏面初始化,值和具體主機控制器
                    有關.若是支持那麼就把ocr的24位置1,即ACMD41命令的S18R.表示要求SD卡準備切換到1.8V電壓模式
34 35 /* 36 * If the host can supply more than 150mA at current voltage, 37 * XPC should be set to 1. 38 */ 39 max_current = sd_get_host_max_current(host);
40 if (max_current > 150) 41 ocr |= SD_OCR_XPC;//SDXC中Power Control相關的項,ocr第28位,ACMD41中的XPC
42 43 try_again: 44 err = mmc_send_app_op_cond(host, ocr, rocr);//發送ACMD41,這個函數你能夠進去看一下,會發現循環檢驗ACMD41的,應答值的31位,與
協議流程圖中符合.rocr就是卡對ACMD41的應答值.另外ACMD41屬於app cmd須要先發送CMD55,這個linux都封裝在了函數裏,而且流程圖上也簡化掉了.
                                
45 if (err) 46 return err; 47 48 /* 49 * In case CCS and S18A in the response is set, start Signal Voltage 50 * Switch procedure. SPI mode doesn't support CMD11. 51 */ 52 if (!mmc_host_is_spi(host) && rocr && 53 ((*rocr & 0x41000000) == 0x41000000)) {//這個if語句,先判斷是否是spi模式,是spi模式就不繼續,我只分析SD模式因此繼續
判斷rocr即ACMD41的應答值的CSS(30位)和S18A(24位)這兩位具體意義見代碼下面的圖表.只有當sd卡是SDHC或SDXC,而且SD卡準備好切換電壓模式,才切換
SDSC不進行切換.

54 err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true);//發送CMD11切換,主機端也作相應的處理.
55 if (err) { 56 ocr &= ~SD_OCR_S18R; 57 goto try_again; 58 } 59 } 60 61 if (mmc_host_is_spi(host)) 62 err = mmc_send_cid(host, cid); 63 else 64 err = mmc_all_send_cid(host, cid);//發送CMD2.獲取cid,即Card IDentification register,存放了一些卡的信息,
到這裏發現已經到了流程圖的底部,只剩CMD3了

65 66 return err; 67 }

ACMD41命令:


 

圖中紅線標出的就是代碼中rocr的值.關於該命令更多的內容見SD的spec.

 

回到mmc_sd_init_card函數,

22~37行與協議無關,主要是初始化一個struct mmc_card *card結構體.這個結構體就至關於這張卡的身份證,從卡的CID,CSD..寄存器拿到的值都要填到該結構中備用.

43行 err = mmc_send_relative_addr(host, &card->rca),發送CMD3獲取RCA,獲得卡的地址.流程圖到這裏就結束了. mmc_sd_init_card卻沒有結束.

接下來還要獲取SD卡的CSD寄存器的值,來填充struct mmc_card *card結構體.

49行 err = mmc_sd_get_csd(host, card);發送CMD9獲取CSD並解析,填充到card.CSD寄存器保存了大量卡的信息.

53行 mmc_decode_cid(card); 解析前面得到的CID並填充到card,這一步爲何不在前面得到cid的時候作? 是由於sd卡協議有不一樣版本而版本信息放在CSD中,因此須要先獲得CSD,得到版本號,在根據版本號解析CID中的數據. 詳細內容見spec中寄存器部分.

60行 err = mmc_select_card(card);發送CMD7使用上面獲得的地址選擇卡

65行 err = mmc_sd_setup_card(host, card, oldcard != NULL);發送ACMD51獲取SCR寄存器值,發送ACMD13獲取SD卡狀態信息,解析並填充card結構,SCR寄存器是對CSD的補充.
最後69~113行 if else語句,判斷是不是UHS-I,分別進行處理.先看一下esle中的代碼

先調用 err = mmc_sd_switch_hs(card);,支持高速就發命令把卡設置到高速

 

 1 int mmc_sd_switch_hs(struct mmc_card *card)
 2 {
 3     int err;
 4     u8 *status;
 5 
 6     if (card->scr.sda_vsn < SCR_SPEC_VER_1)//判斷卡的版本,SD1.1和以後的版本支持高速
 7         return 0;
 8 
 9     if (!(card->csd.cmdclass & CCC_SWITCH))//判斷是否支持class10命令
10         return 0;
11 
12     if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))//判斷主機控制器是否支持高速卡
13         return 0;
14 
15     if (card->sw_caps.hs_max_dtr == 0)/*這個值什麼狀況下會爲0? 該值賦值是在mmc_read_switch函數中,發送CMD6命令時賦值的,若是不支持HIGHSPEED就沒有賦值.
16 一般sd卡驅動中使用CMD6命令,都會發送兩次,第一次查詢卡是否支持接下來要作的切換(在mmc_read_switch中).
17 第二次執行切換操做.CMD6命令詳細內容見spec*/
18         return 0;
19 
20     err = -EIO;
21 
22     status = kmalloc(64, GFP_KERNEL);//分配64bytes的空間.用於接收CMD6的應答數據.
23     if (!status) {
24         pr_err("%s: could not allocate a buffer for "
25             "switch capabilities.\n", mmc_hostname(card->host));
26         return -ENOMEM;
27     }
28 
29     err = mmc_sd_switch(card, 1, 0, 1, status);//發送CMD6切換卡到高速
30     if (err)
31         goto out;
32 
33     if ((status[16] & 0xF) != 1) {//切換結果在第17字節,也是spec中規定的
34         pr_warning("%s: Problem switching card "
35             "into high-speed mode!\n",
36             mmc_hostname(card->host));
37         err = 0;
38     } else {
39         err = 1;
40     }
41 
42 out:
43     kfree(status);
44 
45     return err;
46 }

93行,設置主機這邊,最終會調用host驅動,與你host硬件有關.

100行,mmc_set_clock(host, mmc_sd_get_max_clock(card));//設置時鐘頻率,mmc_sd_get_max_clock(card)是用的card->sw_caps.hs_max_dtr或card->csd.max_dtr.若是支持高速linux會設置爲card->sw_caps.hs_max_dtr,這個值在以前被賦爲

50000000.

105~111 一樣須要host和card同時支持4bit寬度,才能設置,而且要同時設置host和card

err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);設置卡的總線寬度,使用的命令是ACMD6

 

至此,mmc_sd_init_card函數就結束了. linux中的初始化處理過程基本上與spec中的流程圖相符. 以後還有設置時鐘,設置數據線寬度等操做.

還有一個關於UHS-I的分支沒有分析,這個放到下篇分析.而且其中又涉及到CMD6,順便簡單分析學習一下CMD6的使用.

相關文章
相關標籤/搜索