點擊打開鏈接
內容來自以下博客:
http://blog.csdn.net/qianjin0703/article/details/5918041 Linux設備驅動子系統第二彈 - SD卡 (有介紹SD卡硬件)
http://blog.csdn.net/wavemcu/article/details/7366852???? linux2.6內核SD Card Driver詳細解析之一
http://blog.chinaunix.net/uid-147915-id-3063162.html ?? ? 基于S3C2410的SD卡linux驅動工作原理
http://www.cnblogs.com/autum/archive/2012/08/16/SD.html? SD卡驅動分析(一)(系列關于 SD 卡文章)
http://blog.163.com/fenglang_2006/blog/static/133662318201011183912576/ linux sd卡驅動分析,基于mini2440,sdio mmc sd卡驅動編4
一、先來一些簡單硬件知識:
MMC:MMC就是 MultiMediaCard 的縮寫,即多媒體卡
SD:SD卡為Secure Digital Memory Card, 即安全數碼卡,(另TF卡又稱microSD)
SDIO:SDIO是在SD標準上定義了一種外設接口
MCI:MCI是Multimedia Card Interface的簡稱,即多媒體卡接口。上述的MMC,SD,SDI卡定義的接口都屬于MCI接口
SD卡引腳:
一根指令線CMD,4根數據線DAT0~DAT3,一個 SDCARD_DET_N 檢測引腳
SD卡內部有7個寄存器
OCR,CID,CSD和SCR寄存器保存卡的配置信息
RCA寄存器保存著通信過程中卡當前暫時分配的地址(只適合SD模式)
卡狀態(Card Status)和SD狀態(SD Status)寄存器保存著卡的狀態
OCR寄存器保存著SD/MMC卡的供電電允許范圍
CID為一個16個字節的寄存器,該寄存器包含一個獨特的卡標識號
CSD寄存器(卡特殊數據寄存器)包含訪問卡存儲時需要的相關信息
SD卡命令共分為12類,分別為class0到Class11
CMD0:復位SD 卡
CMD1:讀OCR寄存器
...
二、好,開始軟件部分了。
Linux相關MMC的代碼分布,主要有兩個目錄,一個頭文件目錄和一個源代碼目錄
頭文件目錄:include/linux/mmc
源代碼目錄:drivers/mmc
mmc驅動共分為三個目錄:card/、core/、host/
card:塊設備的驅動程序。這部分就是實現了將SD卡如何實現為塊設備的
core:總線驅動程序。這是整個MMC的核心層,這部分完成了不同協議和規范的實現,并且為HOST層的驅動提供接口函數
host:通訊接口驅動。針對不同主機的驅動程序,這一部分需要根據自己的特定平臺來完成
MMC/SD卡的驅動被分為:卡識別階段和數據傳輸階段
卡識別階段:空閑(idle)、準備(ready)、識別(ident)、等待(stby)、不活動(ina)
數據傳輸階段:發送(data)、傳輸(tran)、接收(rcv)、程序(prg)、斷開連接(dis)
在實際驅動開發中,只需要在host文件夾下實現你具體的MMC/SD設備驅動部分代碼,
也就是控制器(支持對MMC/SD卡的控制,俗稱MMC/SD主機控制器)和SDI控制器與MMC/SD卡的硬件接口電路
其實SD驅動一共就做了兩件事件:
1).卡的檢測。(初始化sd卡)
2).卡數據的讀取:
寫sd卡:POLL、中斷、DMA
讀sd卡:POLL、中斷、DMA
可以從以下幾個方面理解驅動:
1、 msm_sdcc.c代碼初始化過程;
2、 SD卡塊設備注冊過程;
3、 request及數據傳輸的實現
SD 傳輸模式有以下 3 種:
?? ?SPI mode (required )
?? ?1-bit mode
?? ?4-bit mode
開始上代碼
三、重要的結構體
[cpp]?view plaincopy
卡控制器??kernel/include/linux/mmc/host.h??struct?mmc_host?{??????const?struct?mmc_host_ops?*ops;???????????struct?mmc_ios??????ios;??????????????????truct?mmc_card??????*card;????????????????const?struct?mmc_bus_ops?*bus_ops;????????...????????}????卡控制器操作集??用于從主機控制器向?core?層注冊操作函數,從而將core層與具體的主機控制器隔離??也就是說?core?要操作主機控制器,就是這個?ops?當中給的函數指針操作,不能直接調用具體主控制器的函數??struct?mmc_host_ops?{???-----------------非常重要????????????int?(*enable)(struct?mmc_host?*host);?????????int?(*disable)(struct?mmc_host?*host);????????????void????(*request)(struct?mmc_host?*host,?struct?mmc_request?*req);?????????????void????(*set_ios)(struct?mmc_host?*host,?struct?mmc_ios?*ios);???????int?(*get_ro)(struct?mmc_host?*host);?????????int?(*get_cd)(struct?mmc_host?*host);?????????void????(*enable_sdio_irq)(struct?mmc_host?*host,?int?enable);????????...??}????控制器對卡的I/O狀態??struct?mmc_ios?{????????}????描述卡??kernel/include/linux/mmc/card.h??struct?mmc_card?{????????}????讀寫MMC卡的請求??包括命令,數據以及請求完成后的回調函數??struct?mmc_request?{??????struct?mmc_command??*sbc;?????????????struct?mmc_command??*cmd;??????struct?mmc_data?????*data;??????struct?mmc_command??*stop;????????struct?completion???completion;??????void??(*done)(struct?mmc_request?*);??????struct?mmc_host?????*host;??};????MMC卡讀寫的數據相關信息??如:請求,操作命令,數據以及狀態等??struct?mmc_data?{????????}????MMC卡操作相關命令及數據,狀態信息等??一條指令command共48位,其中command?index指代這條具體的指令名稱,argument為該指令的參數??struct?mmc_command?{??????u32?????????opcode;???????????u32?????????arg;??????????????u32?????????resp[4];??????????...??}????描述mmc卡驅動??struct?mmc_driver?{??????struct?device_driver?drv;??????int?(*probe)(struct?mmc_card?*);??????void?(*remove)(struct?mmc_card?*);??????int?(*suspend)(struct?mmc_card?*);??????int?(*resume)(struct?mmc_card?*);??????void?(*shutdown)(struct?mmc_card?*);??};????總線操作結構??由于mmc卡支持多種總數據線,如SPI、SDIO、8LineMMC,而不同的總線的操作控制方式不盡相同,所以通過此結構與相應的總線回調函數相關聯??struct?mmc_bus_ops?{??????void?(*remove)(struct?mmc_host?*);????????void?(*detect)(struct?mmc_host?*);????????...??};??四、涉及到三種總線
[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,??};??對于 platform bus 上的設備,通常初始化的流程是:
a. 在 platform bus上注冊 platform device.
b. 在 platform bus上注冊 platform driver.
c. 如果 platform bus 上的 device 和 driver 相互匹配, 則調用其 probe() 函數進行初始化.
對于SD host controller 設備也是一樣.
[cpp]?view plaincopy
1.?注冊?platform?device??board-9625.c?(arch\arm\mach-msm)??void?__init?msm9625_init(void)??????board_dt_populate(msm9625_auxdata_lookup)??????????of_platform_populate(of_find_node_by_path("/soc"),of_default_bus_match_table,?adata,?NULL);????????????kernel\arch\arm\boot\dts\msm9625.dtsi??????sdcc2:?qcom,sdcc@f98a4000?{??????????cell-index?=?<2>;???????????compatible?=?"qcom,msm-sdcc";??????????...??????}??????2.?注冊?platform?driver:??driver/mmc/host/msm_sdcc.c??static?int?__init?msmsdcc_init(void)??????platform_driver_register(&msmsdcc_driver);??五、總體架構及流程
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);??然后再掛載 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);??}??????此時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;??驅動掛載成功后, 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都不能通過,那這張卡恐怕就是無效卡了,初始化失敗。
[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);???關于命令和數據的發送和接收
[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?卡??六、SD卡熱插拔檢測的兩種方法
1.中斷
在probe 中有三個中斷函數:
[cpp]?view plaincopy
ret?=?request_irq(core_irqres->start,?msmsdcc_irq,?IRQF_SHARED,?DRIVER_NAME?"?(cmd)",?host);??????ret?=?request_irq(core_irqres->start,?msmsdcc_pio_irq,?IRQF_SHARED,?DRIVER_NAME?"?(pio)",?host);??ret?=?request_irq(plat->sdiowakeup_irq,msmsdcc_platform_sdiowakeup_irq,IRQF_SHARED?|?IRQF_TRIGGER_LOW,?DRIVER_NAME?"sdiowakeup",?host);???[cpp]?view plaincopy
??????ret?=?request_threaded_irq(plat->status_irq,?NULL,msmsdcc_platform_status_irq,?plat->irq_flags,?DRIVER_NAME?"?(slot)",?host);??????static?struct?mmc_platform_data?*msmsdcc_populate_pdata(struct?device?*dev)??????msmsdcc_dt_parse_gpio_info(dev,?pdata)??????????static?int?msmsdcc_dt_parse_gpio_info(struct?device?*dev,?struct?mmc_platform_data?*pdata)??????msmsdcc_dt_get_cd_wp_gpio(dev,?pdata);????static?void?msmsdcc_dt_get_cd_wp_gpio(struct?device?*dev,?struct?mmc_platform_data?*pdata)??????pdata->status_gpio?=?of_get_named_gpio_flags(np,"cd-gpios",?0,?&flags);????????pdata->status_irq?=?platform_get_irq_byname(pdev,?"status_irq");???????apps_proc\kernel\arch\arm\boot\dts\msm9625.dtsi??????interrupt-names?=?"core_irq",?"bam_irq",?"status_irq";??????cd-gpios?=?<&msmgpio?66?0>;????????static?irqreturn_t?msmsdcc_platform_status_irq(int?irq,?void?*dev_id)??????msmsdcc_check_status((unsigned?long)?host);????static?void?msmsdcc_check_status(unsigned?long?data)??????if?(host->plat->status?||?gpio_is_valid(host->plat->status_gpio))?????????mmc_detect_change(host->mmc,?0);????void?mmc_detect_change(struct?mmc_host?*host,?unsigned?long?delay)??????mmc_schedule_delayed_work(&host->detect,?delay);??2.輪詢:
[cpp]?view plaincopy
Msm_sdcc.c?(drivers\mmc\host)??static?int?msmsdcc_probe(struct?platform_device?*pdev)??????mmc->caps?|=?MMC_CAP_NEEDS_POLL;?????????????????????core/core.c??void?mmc_rescan(struct?work_struct?*work)??????out:??????????if?(host->caps?&?MMC_CAP_NEEDS_POLL)???????????????????mmc_schedule_delayed_work(&host->detect,?HZ);??以上僅是SD卡驅動部分信息,其他關于塊設備,總線知識,linux驅動中已經很成熟了,暫時也就沒有了解。
?七、開始上圖片了
初始化:
卡識別:
數據傳輸:
request
總結
以上是生活随笔為你收集整理的sd 卡驱动--基于高通平台的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。