mmc驱动

来源:互联网 发布:centos退出root 编辑:程序博客网 时间:2024/05/29 10:01

MMC子系统的代码在kernel/driver/MMC下.MMC子系统范围三个部分:
HOST部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的。
CORE部分:这是整个MMC的核心存,这部分完成了不同协议和规范的实现,并为HOST层的驱动提供了接口函数。
CARD部分:因为这些记忆卡都是块设备,当然需要提供块设备的驱动程序,这部分就是实现了将你的SD卡如何实现为块设备的。

1.HOST层分析:
HOST层实现的就是我们针对特定主机的驱动程序,我们先采用platform_driver_register(&&atxx_sdhc_drv)注册了一个平台设备,接下来重点关注probe函数。在这个函数中,我们与CORE的联系是通过下面三句实现的。

 

首先分配一个mmc_host结构体,

mmc = mmc_alloc_host(sizeof(atxx_host_t), &dev->dev);注意sizeof(atxx_host_t),这样就能在mmc_host中找到了atxx_host。

 

接下来为mmc->ops赋值,mmc->ops = &atxx_ops;atxx_ops结构实现了几个很重要的函数。中间还对mmc结构的很多成员进行了赋值,最后将mmc结构加入到MMC子系统,mmc_alloc_host,以及mmc_add_host的具体做了什么事情,我们在下节再分析,这三句是些MMC层驱动必须包含的。



static struct mmc_host_ops atxx_ops =

{

    .enable            = atxx_enable,

    .disable       = atxx_disable,

    .request       = atxx_request,

    .set_ios       = atxx_set_ios,

    .get_ro            = atxx_get_ro,

    .get_cd            = atxx_get_cd,

    .enable_sdio_irq  = atxx_enable_sdio_irq,

};

atxx_get_ro:判断我们的卡是否是写保护的
atxx_get_cd:判断卡是否存在
atxx_set_ios:atxx_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
依据核心层传递过来的ios,来设置硬件IO,包括引脚配置,使能时钟,和配置总线带宽。
atxx_request:这个函数是最主要,也最复杂的函数,实现了命令和数据的发送和接收,
当CORE部分需要发送命令或者传输数据时,都会调用这个函数,并传递mrq请求。

static void atxx_request(struct mmc_host *mmc, struct mmc_request *req)

{

    atxx_host_t *host;

    host = mmc_priv(mmc);

    host->mrq = req;

 

    PDEBUG("atxx_request:cmd %d,arg 0x%x,resp 0x%x.\n",req->cmd->opcode,

            req->cmd->arg,req->cmd->resp[0]);

 

    if (card_present(host) == 0) {

        PDEBUG("card is out\n");

        req->cmd->error = -ENOMEDIUM;

        host->mrq = NULL;

        mmc_request_done(host->mmc,req); //如果卡不存在就终止请求

        return;

    }

    if (atxx_setup_cmd(host,req->cmd) != 0) {

        PERROR("atxx_setup_cmd failed\n");

        req->cmd->error = -EIO;

        host->mrq = NULL;

        mmc_request_done(host->mmc,req);

        return;

    }

 

    return;

}

 

Atxx_host.c分析:

static int __init atxx_sdhc_init(void)

{

    return platform_driver_register(&atxx_sdhc_drv);

}

注册平台驱动

static struct platform_driver atxx_sdhc_drv =

{

    .driver = {

        .name  = "sdhc",

        .owner = THIS_MODULE,

        .pm = &atxx_sdhc_pm_ops,

    },

    .probe     = atxx_sdhc_probe,

    .remove        = atxx_sdhc_remove,

    .shutdown  = atxx_sdhc_shutdown,

};

.probe     = atxx_sdhc_probe,是核心,由于host与硬件是直接相关的,probe接下来将做部分关于硬件初始化的工作

进入函数atxx_sdhc_probe

    r = platform_get_resource(dev, IORESOURCE_MEM, 0);

    irq = platform_get_resource(dev, IORESOURCE_IRQ, 0)->start;

获取平台资源

mmc = mmc_alloc_host(sizeof(atxx_host_t), &dev->dev);分配一个mmc的控制器

host = mmc_priv(mmc);

host->sdhc.controller_id = dev->id;

host->pdev = dev;

 

ret=request_threaded_irq(irq,atxx_sdhc_irq_handle,atxx_sdhc_irq_thread,     IRQF_DISABLED, host_names[dev->id], host);//中断申请

 

host->mmc = mmc;

host->irq = irq;

host->sdhc.ctx = host

对atxx_host_t这个私有结构的配置

 

if (host->sdhc.controller_id == SYNOP_MEM_CARD) {

        host->clk_gate = clk_get(&dev->dev,"a_sd");

    } else {

        host->clk_gate = clk_get(&dev->dev,"a_sdio");

    }

clk_enable(host->clk_gate);获取时钟并使能        ????时钟从哪里获取,怎么获取的

 

mmc->ops = &atxx_ops;向core层提供的接口函数集

 

mmc->ocr_avail = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_30_31

        | MMC_VDD_32_33 | MMC_VDD_33_34;

#ifdef CONFIG_ATXX_SDHC_4BITS

    PINFO("SD bus width is set to 4bits\n");

    mmc->caps = MMC_CAP_SDIO_IRQ | MMC_CAP_4_BIT_DATA;

#else

    mmc->caps = MMC_CAP_SDIO_IRQ;

#endif

    mmc->f_min = 125000;

    mmc->f_max = 26000000;

 

#if defined(CONFIG_MACH_T3C)

        mmc->caps |= MMC_CAP_SD_HIGHSPEED;

        mmc->f_max = 52000000;

#endif

 

    mmc->max_hw_segs = MAX_DESCRIPTOR_NUM;

    mmc->max_phys_segs = MAX_DESCRIPTOR_NUM;

   

 

    mmc->max_req_size = MAX_DESCRIPTOR_NUM * PAGE_SIZE;

   

 

    mmc->max_seg_size = PAGE_SIZE;

 

    mmc->max_blk_size = 65535;

    mmc->max_blk_count = 65535;

    PDEBUG("max_segs %d, max_size %d,max_blk_size %d\n",mmc->max_hw_segs,mmc->max_seg_size,mmc->max_blk_size);

向mmc_host结构体赋值

 

host->dma_desc = dma_alloc_coherent(NULL,

            sizeof(struct DES) * MAX_DESCRIPTOR_NUM,

            &host->dma_desc_phy, GFP_KERNEL);

Dma映射

 

    platform_set_drvdata(dev, mmc);

 

    mmc_add_host(mmc);增加设备类并启动host

 

mmc_add_host分析:

int mmc_add_host(struct mmc_host *host)

{

    int err;

 

    WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&

        !host->ops->enable_sdio_irq);

 

    led_trigger_register_simple(dev_name(&host->class_dev), &host->led);

 

    err = device_add(&host->class_dev);

    if (err)

        return err;

 

#ifdef CONFIG_DEBUG_FS

    mmc_add_host_debugfs(host);

#endif

 

    mmc_start_host(host);  //开启主机控制器

 

    return 0;

}

 

void mmc_start_host(struct mmc_host *host)

{

    mmc_power_off(host);

    mmc_detect_change(host, 0);

}

Mmc_delect_change函数中调用mmc_schedule_delayed_work(&host->detect, delay);作用是延时delay后就会调用mmc_rescan函数,由于delay=0,即相当于直接调用mmc_rescan函数

 

进入mmc_rescan函数

 

    if (host->ops->get_cd && host->ops->get_cd(host) == 0)

        goto out;

检测是否有sd卡存在

host->ops->get_cd(host)

Get_cd对应atxx_ops结构中的.get_cd         = atxx_get_cd,

 

mmc_claim_host(host);获取主机控制权

mmc_power_up(host);设置并开启主机控制器     

mmc_go_idle(host);是sd卡进入idle状态

err = mmc_send_app_op_cond(host, 0, &ocr);对sd卡的设置

 

if (mmc_attach_sd(host, ocr))

函数mmc_attach_sd开始对sd的初始化

其中mmc_sd_attach_bus_ops(host);完成bus_ops与sd的绑定

host->ocr = mmc_select_voltage(host, ocr);这是根据host的电压范围和读取到的SD卡工作电压,来判断工作电压是否匹配。

err = mmc_sd_init_card(host, host->ocr, NULL);这个函数完成了整个SD卡初始化的全动作

函数mmc_sd_init_card中在调用mmc_all_send_cid函数

      memcpy(cid, cmd.resp, sizeof(u32) * 4);拷贝身份信息

函数mmc_sd_init_card继续调用card = mmc_alloc_card(host, &sd_type);

card->dev.parent = mmc_classdev(host);

card->dev.bus = &mmc_bus_type;

card->dev.release = mmc_release_card;

card->dev.type = type;

sd卡devices信息与core层的driver相匹配

原创粉丝点击