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命令,並讀回響應,如果響應錯誤,則依次調用.remove、detach_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函數總結