生活随笔
收集整理的這篇文章主要介紹了
Linux SD卡驱动开发(六) —— SD卡启动过程总体分析
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一、工作流程
mmc驅(qū)動主要文件包括
drivers/mmc/card/block.c drivers/mmc/card/queue.c drivers/mmc/core/core.c drivers/mmc/core/host.c drivers/mmc/core/
內(nèi)核啟動時,首先執(zhí)行core/core.c的mmc_init,注冊mmc、sd總線,以及一個host class設(shè)備。接著執(zhí)行card/block.c中,申請一個塊設(shè)備 。
二、數(shù)據(jù)結(jié)構(gòu):
這里涉及三種總線
[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總線操作相關(guān)函數(shù),由于mmc卡支持多種總數(shù)據(jù)線,如SPI、SDIO、8LineMMC而不同的總線的操作控制方式不盡相同,所以通過此結(jié)構(gòu)與相應(yīng)的總線回調(diào)函數(shù)相關(guān)聯(lián)。
[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,?? };??
關(guān)于總線操作的函數(shù):
.detect ,驅(qū)動程序經(jīng)常需要調(diào)用此函數(shù)去檢測mmc卡的狀態(tài),具體實現(xiàn)是發(fā)送CMD13命令,并讀回響應(yīng),如果響應(yīng)錯誤,則依次調(diào)用.remove、detach_bus來移除卡及釋放總線。
三、總體架構(gòu)
1、kernel啟動時,先后執(zhí)行mmc_init()及mmc_blk_init(),以對mmc設(shè)備及mmc塊模塊進(jìn)行初始化
[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 --? 檢查總線操作結(jié)構(gòu)指針bus_ops,如果為空,則重新利用各總線對端口進(jìn)行掃描,檢測順序依次為:SDIO、Normal SD、MMC。當(dāng)檢測到相應(yīng)的卡類型后,就使用mmc_attach_bus()把相對應(yīng)的總線操作與host連接起來
[cpp] ?view plaincopy
void ?mmc_attach_bus( struct ?mmc_host?*host,? const ? struct ?mmc_bus_ops?*ops)?? {?? ????...?? ????host->bus_ops?=?ops;?? ????...?? }??
3、然后在掛載mmc設(shè)備驅(qū)動時,執(zhí)行驅(qū)動程序中的xx_mmc_probe(),檢測host設(shè)備中掛載的sd設(shè)備
[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函數(shù)會創(chuàng)建一個host設(shè)備,然后開啟一個延時任務(wù)mmc_rescan() 。
[cpp] ?view plaincopy
1:?????? core/host.c????? ?? struct ?mmc_host?*mmc_alloc_host( int ?extra,? struct ?device?*dev)----創(chuàng)建一個?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、驅(qū)動掛載成功后,mmc_rescan()函數(shù)被執(zhí)行,然后對卡進(jìn)行初始化 (步驟后面詳細(xì)講述)
[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、發(fā)送CMD0使卡進(jìn)入IDLE狀態(tài) b、發(fā)送CMD8,檢查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出了先發(fā)送CMD8,如響應(yīng)為無效命令,則卡為SD1.1,否則就是SD2.0(請參考SD2.0 Spec)。 c、發(fā)送CMD5讀取OCR寄存器。 d、發(fā)送ACMD55、CMD41,使卡進(jìn)入工作狀態(tài)。MMC卡并不支持ACMD55、CMD41,如果這步通過了,則證明這張卡是SD卡。 e、如果d步驟錯誤,則發(fā)送CMD1判斷卡是否為MMC。SD卡不支持CMD1,而MMC卡支持,這就是SD和MMC類型的判斷依據(jù)。 f、如果ACMD41和CMD1都不能通過,那這張卡恐怕就是無效卡了,初始化失敗。 ? ? ??假如掃描到總線上掛有有效的設(shè)備 ,就調(diào)用相對應(yīng)的函數(shù)把設(shè)備裝到系統(tǒng)中,mmc_attach_sdio()、mmc_attach_sd()、mmc_attach_mmc()這三個函數(shù)分別是裝載sdio設(shè)備,sd卡和mmc卡的。
? ? ?在 sd卡中,驅(qū)動循環(huán)發(fā)送ACMD41、CMD55給卡,讀取OCR寄存器,成功后,依次發(fā)送CMD2(讀CID)、CMD3(得到RCA)、CMD9(讀 CSD)、CMD7(選擇卡)。后面還有幾個命令分別是ACMD41&CMD51,使用CMD6切換一些功能,如切換到高速模式。
? ? ?經(jīng)過上述步驟,已經(jīng)確定當(dāng)前插入的卡是一張有效、可識別的存儲卡。然后調(diào)用mmc_add_card()把存儲卡加到系統(tǒng)中。正式與系統(tǒng)驅(qū)動連接在一起 。
[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)???----重要函數(shù)???? ????__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、卡設(shè)備加到系統(tǒng)中后,通知mmc塊設(shè)備驅(qū)動。塊設(shè)備驅(qū)動此時調(diào)用probe函數(shù),即mmc_blk_probe()函數(shù),mmc_blk_probe()首 先分配一個新的mmc_blk_data結(jié)構(gòu)變量,然后調(diào)用mmc_init_queue,初始化blk隊列。然后建立一個線程 mmc_queue_thread() 。
7、然后就可以進(jìn)行傳輸命令和數(shù)據(jù)了
[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?----對應(yīng)SD卡命令?,如?CMD0:復(fù)位SD?卡?
總結(jié)
以上是生活随笔 為你收集整理的Linux SD卡驱动开发(六) —— SD卡启动过程总体分析 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。