linux sdio驱动
文章目錄
- 架構
- sdio控制器驅動
架構
MMC/SD設備驅動在Linux中的結構層次
在Linux中MMC/SD卡的記憶體都當作塊設備。MMC/SD設備驅動代碼在drivers\mmc 分別有card、core和host三個文件夾,
card層 存放閃存卡(塊設備)的相關驅動,如MMC/SD卡設備驅動
core層 MMC的核心層,抽象了mmc驅動的公共部分,完成不同協議和規范的實現,為host層和設備驅動層提供接口函數
host層 mmc/sd/sdio主機控制器代碼
sdio接口用來連接主機和設備,如wifi,gps等。主機中的wifi,gps驅動通過sdio接口和wifi芯片,gps芯片通信。
wilc1000wifi芯片,提供sdio slave接口與 sdio host 接口相連。
mmc core為host控制器驅動提供注冊接口,抽象同一API,提供給功能層驅動使用。
先看sdio控制器層驅動
sdio控制器驅動
samsung,exynos5250-dw-mshc驅動為例
host控制器設備是以platform_device類型注冊到platform總線,同時host控制器驅動以platform_driver注冊到到platform總線,無論是platform_device或platform_driver注冊到platform總線都進行probe。
dts中device描述:
dwmmc_3: dwmmc3@12230000 {compatible = "samsung,exynos5250-dw-mshc";reg = <0x12230000 0x1000>;interrupts = <0 78 0>;#address-cells = <1>;#size-cells = <0>;clocks = <&clock 283>, <&clock 142>;clock-names = "biu", "ciu"; };/** On Snow we've got SIP WiFi and so can keep drive strengths low to* reduce EMI.*/ dwmmc3@12230000 {slot@0 {pinctrl-names = "default";pinctrl-0 = <&sd3_clk &sd3_cmd &sd3_bus4>;}; };驅動注冊
static const struct dw_mci_drv_data exynos_drv_data = {.caps = exynos_dwmmc_caps,.init = dw_mci_exynos_priv_init,.setup_clock = dw_mci_exynos_setup_clock,.prepare_command = dw_mci_exynos_prepare_command,.set_ios = dw_mci_exynos_set_ios,.parse_dt = dw_mci_exynos_parse_dt, };static const struct of_device_id dw_mci_exynos_match[] = {{ .compatible = "samsung,exynos4412-dw-mshc",.data = &exynos_drv_data, },{ .compatible = "samsung,exynos5250-dw-mshc",.data = &exynos_drv_data, },{}, }; MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); static struct platform_driver dw_mci_exynos_pltfm_driver = {.probe = dw_mci_exynos_probe,.remove = __exit_p(dw_mci_pltfm_remove),.driver = {.name = "dwmmc_exynos",.of_match_table = dw_mci_exynos_match,.pm = &dw_mci_pltfm_pmops,}, };module_platform_driver(dw_mci_exynos_pltfm_driver); //驅動注冊通過compatible = "samsung,exynos4412-dw-mshc ,在platform bus上匹配成功后,調用dw_mci_exynos_probe;
static int dw_mci_exynos_probe(struct platform_device *pdev) {const struct dw_mci_drv_data *drv_data;const struct of_device_id *match;match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);drv_data = match->data; //driver data:exynos_drv_datareturn dw_mci_pltfm_register(pdev, drv_data); }struct dw_mci *host; 主機控制器驅動層定義的管理結構;而struct mmc_host 是core層抽象的主機控制器通用管理結構。
dw_mci_pltfm_register中先分配struct dw_mci *host; 獲取dts中定義各種控制器設備資源:io地址,中斷號等。
然后調用dw_mci_probe,dw_mci_probe先完成控制器初始化配置,時鐘,電壓。dw_mci_init_slot初始化slot
dw_mci_init_slot:
主要 mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);分配 struct mmc_host *mmc;
mmc_add_host-》mmc_start_host 控制器初始完畢,開始工作;
其中,mmc_alloc_host中會 INIT_DELAYED_WORK(&host->detect, mmc_rescan); 創建卡檢測work;
void mmc_start_host(struct mmc_host *host) {host->f_init = max(freqs[0], host->f_min);host->rescan_disable = 0;if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)mmc_power_off(host);elsemmc_power_up(host);mmc_detect_change(host, 0); //控制器開始工作后,主動探測卡是否存在 } mmc_detect_change--》mmc_schedule_delayed_work(&host->detect, delay); 喚醒mmc_rescan卡掃描,后面再描述中斷處理
發生中斷后,會回調dw_mci_interrupt,讀取中斷狀態寄存器,查看中斷源,進行不同處理。例如:命令發送完成,數據發送完成,卡連接,錯誤處理等。
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) {struct dw_mci *host = dev_id;u32 pending;int i;pending = mci_readl(host, MINTSTS); /* read-only mask reg */if (pending) {/** DTO fix - version 2.10a and below, and only if internal DMA* is configured.*/if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {if (!pending &&((mci_readl(host, STATUS) >> 17) & 0x1fff))pending |= SDMMC_INT_DATA_OVER;}if (pending & SDMMC_INT_CMD_DONE) {u32 cmd = mci_readl(host, CMD) & 0x3f;if (cmd == SD_SWITCH_VOLTAGE &&!(mci_readl(host, STATUS) & SDMMC_DATA_BUSY)) {pending |= SDMMC_INT_RTO;}}if (pending & SDMMC_INT_HLE) {mci_writel(host, RINTSTS, SDMMC_INT_HLE);host->cmd_status = pending;tasklet_schedule(&host->tasklet);}if (pending & DW_MCI_CMD_ERROR_FLAGS) {mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);host->cmd_status = pending;smp_wmb();set_bit(EVENT_CMD_COMPLETE, &host->pending_events);}if (pending & SDMMC_INT_VOLT_SW) {u32 cmd = mci_readl(host, CMD) & 0x3f;if (cmd == SD_SWITCH_VOLTAGE) {mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SW);dw_mci_cmd_interrupt(host, pending);}}if (pending & DW_MCI_DATA_ERROR_FLAGS) {/* if there is an error report DATA_ERROR */mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);host->data_status = pending;smp_wmb();set_bit(EVENT_DATA_ERROR, &host->pending_events);tasklet_schedule(&host->tasklet);}if (pending & SDMMC_INT_DATA_OVER) {if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)del_timer(&host->dto_timer);mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);if (!host->data_status)host->data_status = pending;smp_wmb();if (host->dir_status == DW_MCI_RECV_STATUS) {if (host->sg != NULL)dw_mci_read_data_pio(host, true);}set_bit(EVENT_DATA_COMPLETE, &host->pending_events);tasklet_schedule(&host->tasklet);}if (pending & SDMMC_INT_RXDR) {mci_writel(host, RINTSTS, SDMMC_INT_RXDR);if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) {dw_mci_read_data_pio(host, false);} else {if (host->hw_mmc_id == DWMMC_SD_ID && !host->sg) {printk(KERN_DEBUG"mmc%d:debug error:host.sg=%p.cmd%d\n",host->hw_mmc_id, host->sg,mci_readl(host, CMD) & 0x3F);dw_mci_fifo_reset(host->dev, host);dw_mci_ciu_reset(host->dev, host);}}}if (pending & SDMMC_INT_TXDR) {mci_writel(host, RINTSTS, SDMMC_INT_TXDR);if (host->dir_status == DW_MCI_SEND_STATUS && host->sg)dw_mci_write_data_pio(host);}if (pending & SDMMC_INT_CMD_DONE) {mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);dw_mci_cmd_interrupt(host, pending);}if (pending & SDMMC_INT_CD) { //卡檢測中mci_writel(host, RINTSTS, SDMMC_INT_CD);queue_work(host->card_workqueue, &host->card_work);//喚醒card_workqueue隊列工作}/* Handle SDIO Interrupts */for (i = 0; i < host->num_slots; i++) {struct dw_mci_slot *slot = host->slot[i];if (pending & SDMMC_INT_SDIO(i)) {mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));mmc_signal_sdio_irq(slot->mmc);}}}#ifdef CONFIG_MMC_DW_IDMAC/* Handle DMA interrupts */pending = mci_readl(host, IDSTS);if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);host->dma_ops->complete(host);} #endifreturn IRQ_HANDLED; }bit 15:結束位錯誤/CRC 錯誤
bit 14:自動命令完成(ACD)
bit 13:起始位錯誤(SBE) /忙退出
中斷 0(BCI0)。
bit 12:硬件鎖定寫錯誤(HLE)
bit 11: FIFO 下溢出/上溢出錯誤
(FRUN)
bit 10:主機超時引起的數據缺乏
(HTO) /電壓切換中斷
bit 9:數據讀超時(DRTO) /Boot 數
據開始(BDS)
bit 8:響應超時(RTO) /Boot 接收
響應(BAR)
bit 7:數據 CRC 錯誤(DCRC)
bit 6:響應 CRC 錯誤(RCRC)
bit 5:接收 FIFO 數據請求(RXDR)
bit 4:發送 FIFO 數據請求(TXDR)
bit 3:數據傳輸結束(DTO)
bit 2:命令完成(CD)
bit 1:響應錯誤(RE)
bit 0:卡檢測(CDT)
卡檢測
host->card_workqueue–》
dw_mci_work_routine_card:讀取控制器寄存器 present = dw_mci_get_cd(mmc);獲取卡狀態,插入或拔出;
卡存在:
mmc_detect_change–>mmc_schedule_delayed_work(&host->detect, delay); 喚醒mmc_alloc_host中創建的 INIT_DELAYED_WORK(&host->detect, mmc_rescan);
SDIO類型初始化
/** Starting point for SDIO card init.*/ int mmc_attach_sdio(struct mmc_host *host) {int err, i, funcs;u32 ocr;struct mmc_card *card;//發送cmd5,如果收到響應就是sdio卡,否則不是直接返回; 收到響應為R4指示sdio卡使用的電壓err = mmc_send_io_op_cond(host, 0, &ocr);if (err)return err;//設置mmc host中bus_ops為sdio opsmmc_attach_bus(host, &mmc_sdio_ops);if (host->ocr_avail_sdio)host->ocr_avail = host->ocr_avail_sdio;/** Sanity check the voltages that the card claims to* support.*/cmd5時返回電壓if (ocr & 0x7F) {pr_warning("%s: card claims to support voltages ""below the defined range. These will be ignored.\n",mmc_hostname(host));ocr &= ~0x7F;}//設置電壓值,需要sdio控制器支持的電壓和sdio卡匹配host->ocr = mmc_select_voltage(host, ocr);//初始化卡,里面先分配card類型結構,struct mmc_card card = mmc_alloc_card(host, NULL);并判斷sdio對應device是否為存儲還是其它功能(wifi,gps等),設置card->type = MMC_TYPE_SD_COMBO;或card->type = MMC_TYPE_SDIO;并且獲取mmc_send_relative_addr(host, &card->rca);設備地址保存到card->rcaerr = mmc_sdio_init_card(host, host->ocr, NULL, 0);card = host->card;/** The number of functions on the card is encoded inside* the ocr.*/funcs = (ocr & 0x70000000) >> 28; //sdio device上具備的功能個數,例如wifi設備card->sdio_funcs = 0;/** Initialize (but don't add) all present functions.*/for (i = 0; i < funcs; i++, card->sdio_funcs++) {//初始化化sdio 對應卡設備上功能,分配了struct sdio_func,sdio_alloc_func(card);err = sdio_init_func(host->card, i + 1);}/** First add the card to the driver model...*/mmc_release_host(host);err = mmc_add_card(host->card); //把card設備注冊到系統中/** ...then the SDIO functions.*/for (i = 0;i < funcs;i++) {err = sdio_add_func(host->card->sdio_func[i]); //把func功能注冊到設備上if (err)goto remove_added;}mmc_claim_host(host);return 0; }sdio_init_func(host->card, i + 1);
初始化功能并注冊到系統中,在/sys/bus/sdio/devices,
mmc1:0001:1 表示host控制器1:連接的設備1:設備1上功能1
mmc1:0001:2 表示host控制器1:連接的設備1:設備1上功能2
分配struct mmc_card
/** Allocate and initialise a new MMC card structure.*/ struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type) {struct mmc_card *card;card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);card->host = host;device_initialize(&card->dev);card->dev.parent = mmc_classdev(host);card->dev.bus = &mmc_bus_type;//設置卡設備bus為mmc_bus_typecard->dev.release = mmc_release_card;card->dev.type = type;return card; }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,.pm = &mmc_bus_pm_ops, }; /** This currently matches any MMC driver to any MMC card - drivers* themselves make the decision whether to drive this card in their* probe method.*/ static int mmc_bus_match(struct device *dev, struct device_driver *drv) {return 1; //默認返回1,任何mmc card和mmc驅動都能夠匹配 }mmc_blk_init–>mmc_register_driver(&mmc_driver);–>drv->drv.bus = &mmc_bus_type;–>driver_register(&drv->drv);
注冊了mmc_bus_type 類型mmc card驅動會和 sdio card設備 匹配到。
func分配struct sdio_func
/** Allocate and initialise a new SDIO function structure.*/ struct sdio_func *sdio_alloc_func(struct mmc_card *card) {struct sdio_func *func;func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);func->card = card;device_initialize(&func->dev);func->dev.parent = &card->dev;//父節點為card設備func->dev.bus = &sdio_bus_type; //bus類型為sdio_bus_typefunc->dev.release = sdio_release_func;return func; }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, };static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,struct sdio_driver *sdrv) {const struct sdio_device_id *ids;ids = sdrv->id_table;if (ids) {while (ids->class || ids->vendor || ids->device) { 讀取card func中id和driver中id匹配if (sdio_match_one(func, ids))return ids;ids++;}}return NULL; }func為sdio_bus_type 注冊到sdio bus中,和系統中注冊的driver進行匹配。然后調用驅動的probe。
總結
以上是生活随笔為你收集整理的linux sdio驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux CentOS 8(磁盘设备与
- 下一篇: Maven的下载与使用