IMX6Q SD-HOST分析
超安全数字主机控制器(Ultra secure Digital Host Controller, uSDHC)提供了与主机系统和SD/SDIO/MMC卡的交互接口
硬件:IMX6Q
系统:Linux 4.1.15
一.初始化代码分析
Sdhci-esdhc-imx.c (drivers\mmc\host)
static struct platform_driver sdhci_esdhc_imx_driver = { //平台驱动 .driver = { .name = "sdhci-esdhc-imx", .of_match_table = imx_esdhc_dt_ids, //匹配id table .pm = &sdhci_esdhc_pmops, }, .id_table = imx_esdhc_devtype, .probe = sdhci_esdhc_imx_probe, //匹配后执行 .remove = sdhci_esdhc_imx_remove, }; module_platform_driver(sdhci_esdhc_imx_driver);
与dts中的devices匹配后,进入sdhci_esdhc_imx_probe
static int sdhci_esdhc_imx_probe(struct platform_device *pdev) { host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0); //单独分析1 ......获取dts里面的数据,并进行一些初始化..... if (of_id) err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data); //单独分析2 else err = sdhci_esdhc_imx_probe_nondt(pdev, host, imx_data); if (err) goto disable_clk; //设置/重置设备唤醒能力标志。 device_set_wakeup_capable(&pdev->dev, 1); //添加进Secure Digital Host Controller Interface err = sdhci_add_host(host); //单独分析3 if (err) goto disable_clk; return 0; }
单独分析1
sdhci_pltfm_init里面进行平台相关的一些初始化,分配mmc_alloc_host,获取中断和IO资源
/* sdhci ops */ static const struct sdhci_ops sdhci_pltfm_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata, size_t priv_size) { iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (resource_size(iomem) < 0x100) dev_err(&pdev->dev, "Invalid iomem size!\n"); /*里面调用mmc_alloc_host分配host,然后mmc_alloc_host里面 INIT_DELAYED_WORK(&host->detect, mmc_rescan),mmc_rescan扫描设备 */ host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pltfm_host) + priv_size); //设置操作函数 if (pdata && pdata->ops) host->ops = pdata->ops; else host->ops = &sdhci_pltfm_ops; //获取中断,,, host->irq = platform_get_irq(pdev, 0); ......其他操作...... }
单独分析2
获取dts里面的一些值,并进行解析,比如wp,sd等
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, struct sdhci_host *host, struct pltfm_imx_data *imx_data) { if (of_get_property(np, "fsl,wp-controller", NULL)) boarddata->wp_type = ESDHC_WP_CONTROLLER; boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); if (gpio_is_valid(boarddata->wp_gpio)) boarddata->wp_type = ESDHC_WP_GPIO; of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step); of_property_read_u32(np, "fsl,tuning-start-tap", &boarddata->tuning_start_tap); if (of_find_property(np, "no-1-8-v", NULL)) //注意这里需要查看芯片手册,card是1.8V还是3.2V boarddata->support_vsel = false; else boarddata->support_vsel = true; if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) boarddata->delay_line = 0; mmc_of_parse_voltage(np, &host->ocr_mask); /* call to generic mmc_of_parse to support additional capabilities */ //调用通用mmc_of_parse以支持其他功能,比如是否支持热拔插,总线宽度,等等 ret = mmc_of_parse(host->mmc); if (ret) return ret; if (of_get_property(np, "wifi-host", NULL)) { wifi_mmc_host = host->mmc; host->quirks2 |= SDHCI_QUIRK2_SDIO_IRQ_THREAD; dev_info(mmc_dev(host->mmc), "assigned as wifi host\n"); } return 0; }
单独分析3
MMC的操作函数,读写等
static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .post_req = sdhci_post_req, .pre_req = sdhci_pre_req, .set_ios = sdhci_set_ios, .get_cd = sdhci_get_cd, .get_ro = sdhci_get_ro, .hw_reset = sdhci_hw_reset, .enable_sdio_irq = sdhci_enable_sdio_irq, .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .prepare_hs400_tuning = sdhci_prepare_hs400_tuning, .execute_tuning = sdhci_execute_tuning, .select_drive_strength = sdhci_select_drive_strength, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, };
设置mmc的一些操作函数,还有中断处理函数,把mmc host加入到系统里面
int sdhci_add_host(struct sdhci_host *host) { mmc = host->mmc; ...host的一些寄存器设置..... ...DMA的一些设置..... /* * Set host parameters.sdhci的操作函数,核心操作都在这里 */ mmc->ops = &sdhci_ops; ...根据DTS里面读取的信息设置一些能力到寄存器... //SDHCI的初始化 sdhci_init(host, 0); //中断函数sdhci_irq和中断下半部处理程序sdhci_thread_irq ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, IRQF_SHARED, mmc_hostname(mmc), host); /*把MMC host加入到系统,然后调用mmc_start_host->_mmc_detect_change->mmc_schedule_delayed_work(&host->detect, delay) 调用mmc_rescan扫描设备 */ mmc_add_host(mmc); //开机log会出现 pr_info("%s: SDHCI controller on %s [%s] using %s\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), (host->flags & SDHCI_USE_ADMA) ? (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" : (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); //使能card检测,注意是寄存器方面的 sdhci_enable_card_detection(host); return 0; }
二.mmc_rescan分析
检测MMC,sdio等卡
void mmc_rescan(struct work_struct *work) { //申请一个host用于一组操作 mmc_claim_host(host); for (i = 0; i < ARRAY_SIZE(freqs); i++) { //用不同的频率去扫描设备 if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) break; if (freqs[i] <= host->f_min) break; } mmc_release_host(host); }
mmc_rescan_try_freq里面是直接的工作函数
以 SDIO为例其会采用mmc_attach_sdio来实现驱动和设备的匹配,其本质还是根据sdio_bus的匹配规则来实现匹配。
在 mmc_attach_sdio中首先是mmc匹配一个bus,即采用何种bus来进行mmc bus来处理host。在这里需要理解一点就是在SDIO中,对于SD卡存储器mmc为实体设备,而对于非SD卡存储器,如SDIO接口的设备,则mmc 则表征为bus,这个比较重要。除了mmc bus外还存在SDIO_BUS。*/
int mmc_attach_sdio(struct mmc_host *host) { //host匹配一条mmc bus mmc_attach_bus(host, &mmc_sdio_ops); if (host->ocr_avail_sdio) host->ocr_avail = host->ocr_avail_sdio; /* * Detect and init the card.里面会调用mmc_alloc_card申请一个card实体, * 类似于总线设备。然后进行一些初始化操作。 */ err = mmc_sdio_init_card(host, rocr, NULL, 0); if (err) goto err; card = host->card; /* * The number of functions on the card is encoded inside * the ocr. */ funcs = (ocr & 0x70000000) >> 28; card->sdio_funcs = 0; /* * Initialize (but don't add) all present functions. */ for (i = 0; i < funcs; i++, card->sdio_funcs++) { /* 里面会调用sdio_alloc_func分配func,然后设置verdor,device和max_blksize值, * 这些值是从卡里面读取的,用于匹配驱动 * func->vendor = func->card->cis.vendor; * func->device = func->card->cis.device; * func->max_blksize = func->card->cis.blksize; */ err = sdio_init_func(host->card, i + 1); if (err) goto remove; /* * Enable Runtime PM for this func (if supported) */ if (host->caps & MMC_CAP_POWER_OFF_CARD) pm_runtime_enable(&card->sdio_func[i]->dev); } /* * First add the card to the driver model... */ mmc_release_host(host); //用驱动模型注册一个新的MMC卡 err = mmc_add_card(host->card); if (err) goto remove_added; /* * ...then the SDIO functions. */ for (i = 0;i < funcs;i++) { //用驱动模型注册一个新的func err = sdio_add_func(host->card->sdio_func[i]); if (err) goto remove_added; } mmc_claim_host(host); return 0; }
比较难以理解的是func,这个东东其实是一个实体设备的封装,可以认为其是一个设备,一个card下面可以有多个func.
sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */