生活随笔
收集整理的這篇文章主要介紹了
Linux SD卡驱动开发(六) —— SD卡启动过程总体分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、工作流程
mmc驅動主要文件包括
drivers/mmc/card/block.c
drivers/mmc/card/queue.c
drivers/mmc/core/core.c
drivers/mmc/core/host.c
drivers/mmc/core/
內核啟動時,首先執行core/core.c的mmc_init,注冊mmc、sd總線,以及一個host class設備。接著執行card/block.c中,申請一個塊設備。
二、數據結構:
這里涉及三種總線
[cpp]?view plaincopy
1.?platform?bus??? driver/base/platform.c???? struct?bus_type?platform_bus_type?=?{???? ????.name????????=?"platform",???? ????.dev_attrs????=?platform_dev_attrs,???? ????.match????????=?platform_match,???? ????.uevent????????=?platform_uevent,???? ????.pm????????=?&platform_dev_pm_ops,???? };???? ???? 2.?mmc?bus?type???? drivers\mmc\core\bus.c???? static?struct?bus_type?mmc_bus_type?=?{???? ????.name????????=?"mmc",???? ????.dev_attrs????=?mmc_dev_attrs,???? ????.match????????=?mmc_bus_match,???? ????.uevent????????=?mmc_bus_uevent,???? ????.probe????????=?mmc_bus_probe,???? ????.remove????????=?mmc_bus_remove,???? ????.shutdown????????=?mmc_bus_shutdown,???? ????.pm????????=?&mmc_bus_pm_ops,???? };???? ???? 3.?sdio?bus?type?????? drivers\mmc\core\sdio_bus.c???? static?struct?bus_type?sdio_bus_type?=?{???? ????.name????????=?"sdio",???? ????.dev_attrs????=?sdio_dev_attrs,???? ????.match????????=?sdio_bus_match,???? ????.uevent????????=?sdio_bus_uevent,???? ????.probe????????=?sdio_bus_probe,???? ????.remove????????=?sdio_bus_remove,???? ????.pm????????=?SDIO_PM_OPS_PTR,???? };????
? ? ?其中mmc總線操作相關函數,由于mmc卡支持多種總數據線,如SPI、SDIO、8LineMMC而不同的總線的操作控制方式不盡相同,所以通過此結構與相應的總線回調函數相關聯。
[cpp]?view plaincopy
?? struct?mmc_bus_ops?{?? ????void?(*remove)(struct?mmc_host?*);?? ????void?(*detect)(struct?mmc_host?*);?? ????int?(*sysfs_add)(struct?mmc_host?*,?struct?mmc_card?*card);?? ????void?(*sysfs_remove)(struct?mmc_host?*,?struct?mmc_card?*card);?? ????void?(*suspend)(struct?mmc_host?*);?? ????void?(*resume)(struct?mmc_host?*);?? };?? ?? static?const?struct?mmc_bus_ops?mmc_ops?=?{?? ????.remove?=?mmc_remove,?? ????.detect?=?mmc_detect,?? ????.sysfs_add?=?mmc_sysfs_add,?? ????.sysfs_remove?=?mmc_sysfs_remove,?? ????.suspend?=?mmc_suspend,?? ????.resume?=?mmc_resume,?? };?? ?? static?const?struct?mmc_bus_ops?mmc_sd_ops?=?{?? ????.remove?=?mmc_sd_remove,?? ????.detect?=?mmc_sd_detect,?? ????.sysfs_add?=?mmc_sd_sysfs_add,?? ????.sysfs_remove?=?mmc_sd_sysfs_remove,?? ????.suspend?=?mmc_sd_suspend,?? ????.resume?=?mmc_sd_resume,?? };?? ?? static?const?struct?mmc_bus_ops?mmc_sdio_ops?=?{?? ????.remove?=?mmc_sdio_remove,?? ????.detect?=?mmc_sdio_detect,?? };??
關于總線操作的函數:
.detect,驅動程序經常需要調用此函數去檢測mmc卡的狀態,具體實現是發送CMD13命令,并讀回響應,如果響應錯誤,則依次調用.remove、detach_bus來移除卡及釋放總線。
三、總體架構
1、kernel啟動時,先后執行mmc_init()及mmc_blk_init(),以對mmc設備及mmc塊模塊進行初始化
[cpp]?view plaincopy
mmc/core/core.c???? static?int?__init?mmc_init(void)???? ????workqueue?=?alloc_ordered_workqueue("kmmcd",?0);?? ????ret?=?mmc_register_bus();?? ????ret?=?mmc_register_host_class();?? ????ret?=?sdio_register_bus();?? ???????? *******????? mmc/card/block.c???? static?int?__init?mmc_blk_init(void)???? ????res?=?register_blkdev(MMC_BLOCK_MAJOR,?"mmc");?? ????res?=?mmc_register_driver(&mmc_driver);?? ???? static?struct?mmc_driver?mmc_driver?=???? ????.probe??????=?mmc_blk_probe,???? ???????? static?int?mmc_blk_probe(struct?mmc_card?*card)???? ????mmc_set_bus_resume_policy(card->host,?1);??
2、core部分會做兩件事
a --?取得總線
b --?檢查總線操作結構指針bus_ops,如果為空,則重新利用各總線對端口進行掃描,檢測順序依次為:SDIO、Normal SD、MMC。當檢測到相應的卡類型后,就使用mmc_attach_bus()把相對應的總線操作與host連接起來
[cpp]?view plaincopy
void?mmc_attach_bus(struct?mmc_host?*host,?const?struct?mmc_bus_ops?*ops)?? {?? ????...?? ????host->bus_ops?=?ops;?? ????...?? }??
3、然后在掛載mmc設備驅動時,執行驅動程序中的xx_mmc_probe(),檢測host設備中掛載的sd設備
[cpp]?view plaincopy
kernel\arch\arm\configs\msm9625_defconfig???? CONFIG_MMC_MSM=y???? ???? kernel\drivers\mmc\host\Makefile???? obj-$(CONFIG_MMC_MSM)????????+=?msm_sdcc.o???????? ???? msm_sdcc.c?(drivers\mmc\host)???? ?? static?int?__init?msmsdcc_init(void)???? ????platform_driver_register(&msmsdcc_driver);?????? ???????? static?struct?platform_driver?msmsdcc_driver?=?{???? ????.probe????????=?msmsdcc_probe,???? ????.remove????????=?msmsdcc_remove,???? ????.driver????????=?{???? ????????.name????=?"msm_sdcc",???? ????????.pm????=?&msmsdcc_dev_pm_ops,???? ????????.of_match_table?=?msmsdcc_dt_match,???? ????},???? };???????? ???????? ?? ?? ?? static?int?msmsdcc_probe(struct?platform_device?*pdev)???????? {???????? ?????? ????if?(pdev->dev.of_node)?{???? ????plat?=?msmsdcc_populate_pdata(&pdev->dev);?????????? ????of_property_read_u32((&pdev->dev)->of_node,"cell-index",?&pdev->id);???? ????}?else?{???? ????????plat?=?pdev->dev.platform_data;???? ????}???? ?????? ????mmc?=?mmc_alloc_host(sizeof(struct?msmsdcc_host),?&pdev->dev);????????????----?1???? ?????? ????mmc->ops?=?&msmsdcc_ops;???? ?????? ????ret?=?request_irq(core_irqres->start,?msmsdcc_irq,?IRQF_SHARED,DRIVER_NAME?"?(cmd)",?host);???? ?????? ????mmc_add_host(mmc);????????????????????????????????????????????????????????----?2???? ?????? ????ret?=?device_create_file(&pdev->dev,?&host->auto_cmd21_attr);???? }????????
4、此時probe函數會創建一個host設備,然后開啟一個延時任務mmc_rescan()。
[cpp]?view plaincopy
1:?????? core/host.c????? ?? struct?mmc_host?*mmc_alloc_host(int?extra,?struct?device?*dev)----創建一個?mmc_host?和?mmc_spi_host?,且mmc_host的最后一個成員指針private指向mmc_spi_host???? ?????? ????struct?mmc_host?*host;?????? ????host?=?kzalloc(sizeof(struct?mmc_host)?+?extra,?GFP_KERNEL);???? ?????? ????host->parent?=?dev;???? ????host->class_dev.parent?=?dev;???? ????host->class_dev.class?=?&mmc_host_class;???? ????device_initialize(&host->class_dev);???? ?????? ????init_waitqueue_head(&host->wq);???? ????INIT_DELAYED_WORK(&host->detect,?mmc_rescan);?????? ?????? ????host->max_segs?=?1;???? ????host->max_seg_size?=?PAGE_CACHE_SIZE;???? ????return?host;????
5、驅動掛載成功后,mmc_rescan()函數被執行,然后對卡進行初始化(步驟后面詳細講述)
[cpp]?view plaincopy
core/core.c???? ?? void?mmc_rescan(struct?work_struct?*work)???? ????if?(host->bus_ops?&&?host->bus_ops->detect?&&?!host->bus_dead?&&?!(host->caps?&?MMC_CAP_NONREMOVABLE))?????? ????host->bus_ops->detect(host);???? ????mmc_bus_put(host);?????? ????mmc_bus_get(host);?????? ????if?(host->bus_ops?!=?NULL)?{???? ????????mmc_bus_put(host);?????? ????????goto?out;???? ????}???? ????if?(host->ops->get_cd?&&?host->ops->get_cd(host)?==?0)???? ????goto?out;???? ????mmc_claim_host(host);????????????????????? ???????? ????if?(!mmc_rescan_try_freq(host,?host->f_min))????
初始化卡接以下流程初始化:
a、發送CMD0使卡進入IDLE狀態
b、發送CMD8,檢查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出了先發送CMD8,如響應為無效命令,則卡為SD1.1,否則就是SD2.0(請參考SD2.0 Spec)。
c、發送CMD5讀取OCR寄存器。
d、發送ACMD55、CMD41,使卡進入工作狀態。MMC卡并不支持ACMD55、CMD41,如果這步通過了,則證明這張卡是SD卡。
e、如果d步驟錯誤,則發送CMD1判斷卡是否為MMC。SD卡不支持CMD1,而MMC卡支持,這就是SD和MMC類型的判斷依據。
f、如果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()把存儲卡加到系統中。正式與系統驅動連接在一起。
[cpp]?view plaincopy
static?int?mmc_rescan_try_freq(struct?mmc_host?*host,?unsigned?freq)???? ????host->f_init?=?freq;??????????????????? ????mmc_power_up(host);??????????????????????? ????mmc_go_idle(host);??????????----1a???????? ????mmc_send_if_cond(host,?host->ocr_avail);?? ????if?(!mmc_attach_sd(host))???----1b???????? ???? 1a:???? int?mmc_go_idle(struct?mmc_host?*host)?????? ????struct?mmc_command?cmd?=?{0};???? ????cmd.opcode?=?MMC_GO_IDLE_STATE;??? ????cmd.arg?=?0;?????????????????????? ????err?=?mmc_wait_for_cmd(host,?&cmd,?0)???? ???????? int?mmc_wait_for_cmd(struct?mmc_host?*host,?struct?mmc_command?*cmd,?int?retries)???? ????memset(cmd->resp,?0,?sizeof(cmd->resp));???? ????cmd->retries?=?retries;???? ????mrq.cmd?=?cmd;?????????????????????????????????? ????mmc_wait_for_req(host,?&mrq);???? ???????? void?mmc_wait_for_req(struct?mmc_host?*host,?struct?mmc_request?*mrq)???----重要函數???? ????__mmc_start_req(host,?mrq);???? ???? static?int?__mmc_start_req(struct?mmc_host?*host,?struct?mmc_request?*mrq)???? ????mmc_start_request(host,?mrq);???? ???????????? static?void?mmc_start_request(struct?mmc_host?*host,?struct?mmc_request?*mrq)???? ????host->ops->request(host,?mrq);?????? ???? ???????? 1b:????? core/mmc.c???? int?mmc_attach_sd(struct?mmc_host?*host)?????????????????????? ????err?=?mmc_send_app_op_cond(host,?0,?&ocr);??????----1b1??? ????host->ocr?=?mmc_select_voltage(host,?ocr);????????????????? ????err?=?mmc_init_card(host,?host->ocr,?NULL);???????????????? ????err?=?mmc_sd_init_card(host,?host->ocr,?NULL);???----1b2???? ????err?=?mmc_add_card(host->card);??????????????????----1b3??? ???????? ???????? 1b1:???? int?mmc_send_app_op_cond(struct?mmc_host?*host,?u32?ocr,?u32?*rocr)???? ????cmd.opcode?=?SD_APP_OP_COND;?????? ???? ???????? ???????? 1b2:???? static?int?mmc_sd_init_card(struct?mmc_host?*host,?u32?ocr,struct?mmc_card?*oldcard)???? ????err?=?mmc_sd_get_cid(host,?ocr,?cid,?&rocr);?????????? ????card?=?mmc_alloc_card(host,?&sd_type);???????????????? ????err?=?mmc_send_relative_addr(host,?&card->rca);???????? ????err?=?mmc_sd_get_csd(host,?card);?----mmc_send_csd(card,?card->raw_csd);?? ????err?=?mmc_select_card(card);?????????????????????????? ????err?=?mmc_sd_setup_card(host,?card,?oldcard?!=?NULL);??????? ???? int?mmc_sd_setup_card(struct?mmc_host?*host,?struct?mmc_card?*card,bool?reinit)???? ????mmc_app_send_scr(card,?card->raw_scr);????? ????if?(host->ops->get_ro(host)?>?0?)???????? ????????mmc_card_set_readonly(card);?????????? ???????? 1b3:???? core/bus.c???? int?mmc_add_card(struct?mmc_card?*card)??????? ????ret?=?device_add(&card->dev);???? ???? drivers/base/core.c???? int?device_add(struct?device?*dev)???? ????dev_set_name(dev,?"%s%u",?dev->bus->dev_name,?dev->id);??? ????bus_probe_device(dev);???? ???? void?bus_probe_device(struct?device?*dev)???? ????????if?(bus->p->drivers_autoprobe)????? ????????ret?=?device_attach(dev);????????????? ???????? ???????? ***********????? 2:???? ?? int?mmc_add_host(struct?mmc_host?*host)???? ????err?=?device_add(&host->class_dev);?? ????mmc_start_host(host);???? ???????? ???????? void?mmc_start_host(struct?mmc_host?*host)?????? ????mmc_power_off(host);????????????????----2a???? ????mmc_detect_change(host,?0);?????????----2b???? ???? 2a:???? void?mmc_power_off(struct?mmc_host?*host)??????? ????host->ios.power_mode?=?MMC_POWER_OFF;?????? ????...???? ????mmc_set_ios(host);???? ???? void?mmc_set_ios(struct?mmc_host?*host)???? ????host->ops->set_ios(host,?ios);?????????????? ???? 2b:???? void?mmc_detect_change(struct?mmc_host?*host,?unsigned?long?delay)???? ????????mmc_schedule_delayed_work(&host->detect,?delay);???
6、卡設備加到系統中后,通知mmc塊設備驅動。塊設備驅動此時調用probe函數,即mmc_blk_probe()函數,mmc_blk_probe()首 先分配一個新的mmc_blk_data結構變量,然后調用mmc_init_queue,初始化blk隊列。然后建立一個線程 mmc_queue_thread()。
7、然后就可以進行傳輸命令和數據了
[cpp]?view plaincopy
struct?mmc_host_ops?{??????????? ?????? ????void????(*request)(struct?mmc_host?*host,?struct?mmc_request?*req);???? ???? }???? ???? static?const?struct?mmc_host_ops?msmsdcc_ops?=?{???? ????.enable?????=?msmsdcc_enable,???? ????.disable????=?msmsdcc_disable,???? ????.pre_req????????=?msmsdcc_pre_req,???? ????.post_req???????=?msmsdcc_post_req,???? ????.request????=?msmsdcc_request,???? ????.set_ios????=?msmsdcc_set_ios,???? ????.get_ro?????=?msmsdcc_get_ro,???? ????.enable_sdio_irq?=?msmsdcc_enable_sdio_irq,???? ????.start_signal_voltage_switch?=?msmsdcc_switch_io_voltage,???? ????.execute_tuning?=?msmsdcc_execute_tuning,???? ????.hw_reset?=?msmsdcc_hw_reset,???? ????.stop_request?=?msmsdcc_stop_request,???? ????.get_xfer_remain?=?msmsdcc_get_xfer_remain,???? ????.notify_load?=?msmsdcc_notify_load,???? };???? ???? ? ???? static?void?msmsdcc_request(struct?mmc_host?*mmc,?struct?mmc_request?*mrq)???? ????mmc_request_done(mmc,?mrq);??????????????? ????msmsdcc_request_start(host,?mrq);??????????? ???? static?void?msmsdcc_request_start?(struct?msmsdcc_host?*host,?struct?mmc_request?*mrq)???? ????if?((mrq->data->flags?&?MMC_DATA_READ)?||host->curr.use_wr_data_pend)???????? ????????msmsdcc_start_data(host,?mrq->data,mrq->sbc???mrq->sbc?:?mrq->cmd,0);????? ????else???? ????????msmsdcc_start_command(host,mrq->sbc???mrq->sbc?:?mrq->cmd,0);???????????? ???? ???? static?void?msmsdcc_start_data(struct?msmsdcc_host?*host,?struct?mmc_data?*data,struct?mmc_command?*cmd,?u32?c)???? ?????? ????...???? ????if?(is_dma_mode(host)?&&?(datactrl?&?MCI_DPSM_DMAENABLE))????? ????????msmsdcc_start_command_deferred(host,?cmd,?&c);???????????? ????else???????? ????????msmsdcc_start_command(host,?cmd,?c)???? ???? static?void?msmsdcc_start_command(struct?msmsdcc_host?*host,?struct?mmc_command?*cmd,?u32?c)???? {???? ????msmsdcc_start_command_deferred(host,?cmd,?&c);???? ????msmsdcc_start_command_exec(host,?cmd->arg,?c);???? }???? ???? static?void?msmsdcc_start_command_deferred(struct?msmsdcc_host?*host,struct?mmc_command?*cmd,?u32?*c)???? ????cmd->opcode?----對應SD卡命令?,如?CMD0:復位SD?卡?
總結
以上是生活随笔為你收集整理的Linux SD卡驱动开发(六) —— SD卡启动过程总体分析的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。