日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux sdio驱动

發布時間:2024/3/26 linux 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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。

[ /include/linux/mmc/host.h ]struct mmc_host 用來描述卡控制器struct mmc_card 用來描述卡struct mmc_driver 用來描述 mmc 卡驅動struct sdio_func 用來描述 功能設備struct mmc_host_ops 用來描述卡控制器操作接口函數功能,用于從 主機控制器層向 core 層注冊操作函數,從而將core 層與具體的主機控制器隔離。

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

int dw_mci_probe(struct dw_mci *host) {const struct dw_mci_drv_data *drv_data = host->drv_data;int width, i, ret = 0;u32 fifo_size;int init_slots = 0;u32 msize;......tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);//注冊tasklet中斷上半部,處理各種中斷host->card_workqueue = alloc_workqueue("dw-mci-card",WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);if (!host->card_workqueue)goto err_dmaunmap;INIT_WORK(&host->card_work, dw_mci_work_routine_card); //注冊workqueue中斷上半部,主要處理卡檢測ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, //注冊硬件中斷host->irq_flags, "dw-mci", host); ......../* We need at least one slot to succeed */for (i = 0; i < host->num_slots; i++) {ret = dw_mci_init_slot(host, i); //初始化slotif (ret)dev_dbg(host->dev, "slot %d init failed\n", i);elseinit_slots++;}。。。。 }

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);

mmc_rescan() { ....//以不同頻率掃描卡static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };//MMC標準默認為400KHZfor (i = 0; i < ARRAY_SIZE(freqs); i++) {if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {extend_wakelock = true;break;}if (freqs[i] <= host->f_min)break;} ..... } static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) {host->f_init = freq;//host上電mmc_power_up(host);/** Some eMMCs (with VCCQ always on) may not be reset after power up, so* do a hardware reset if possible.*/mmc_hw_reset_for_init(host); //復位硬件/** sdio_reset sends CMD52 to reset card. Since we do not know* if the card is being re-initialized, just send it. CMD52* should be ignored by SD/eMMC cards.*/ //如果目標卡是純SD卡則目標卡不會應答,一般主機host的寄存器會報錯,但是這個無關緊要,可以不理它。如果目標卡是純SDIO卡,那么這里就是復位SDIO卡,通過命令CMD52來實現的。③如果目標卡是SD卡和SDIO卡的組合卡,則需要先發送CMD52來復位SDIO卡,再復位SD卡,因為CMD52要先于CMD0發送。sdio_reset(host);mmc_go_idle(host);//發送CMD0,讓設備進入IDLE模式mmc_send_if_cond(host, host->ocr_avail);//發送CMD8,獲取該卡所支持的電壓值/* Order's important: probe SDIO, then SD, then MMC */識別卡類型首先發送 CMD5。如果收到一個響應,那么該卡是 SDIO。否則發送 ACMD41;如果收到一個響應,那么該卡是 SD。 否則,該卡是 MMC。if (!mmc_attach_sdio(host))return 0;if (!mmc_attach_sd(host))return 0;if (!mmc_attach_mmc(host))return 0;mmc_power_off(host);return -EIO; }

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驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。