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) */

posted @ 2022-04-04 19:31  luoyuna  阅读(293)  评论(0编辑  收藏  举报