代码改变世界

SD卡驱动分析(二)

2012-08-20 16:35  至上  阅读(3577)  评论(0编辑  收藏  举报

card是驱动层  core是核心层  host是主控制器层

硬件初始化及注册是从host开始的:

系统启动的时候就会在平台总线上注册设备与驱动,但这不是sd卡的,只是其主控制器的:

static struct resource sep0611_mmc_resources[] = {
        [0] = {
                .start  = SDIO1_BASE_V,
                .end    = SDIO1_BASE_V + 0xFFF,
                .flags  = IORESOURCE_MEM,
        },

        [1] = {
                .start  = INTSRC_SDIO1,
                .end    = INTSRC_SDIO1,
                .flags  = IORESOURCE_IRQ,
        },
};

static u64 sep0611_mmc_dmamask = 0xFFFFFFFFUL;

struct platform_device sep0611_device_mmc = {
        .name   = "sep0611_mmc",
        .id             = -1,
        .dev    = {
                .dma_mask                       = &sep0611_mmc_dmamask,
                .coherent_dma_mask      = 0xFFFFFFFFUL,
        },
        .num_resources  = ARRAY_SIZE(sep0611_mmc_resources),
        .resource               = sep0611_mmc_resources,
};
EXPORT_SYMBOL(sep0611_device_mmc);

上面是关于sd卡控制器的信息,包括iomem,irq,dma,name等

在注册platform_driver后会执行sepmmc_probe函数,通过一系列的调用,最后会运行mmc_rescan函数,如果开机的时候sd卡已经插入在板子上,那么现在就开始从sd卡中取出必要的信息,因为没有这些卡中的参数,是无法搭建整个sd卡运行环境的(比如初始化请求队列等工作)。如果sd卡是后来插入的,就是引起一个检测中断,最后还是运行mmc_rescan函数。所以这个函数是个核心函数。

void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
#ifdef CONFIG_MMC_DEBUG
    unsigned long flags;
    spin_lock_irqsave(&host->lock, flags);
    WARN_ON(host->removed);
    spin_unlock_irqrestore(&host->lock, flags);
#endif

    mmc_schedule_delayed_work(&host->detect, delay);
//对于开机前就插入的sd卡开始delay=0  后来的都是用中断,中断子程序还会调用到这个函数,延迟是为了消除抖动,一般为50ms
}
static int mmc_schedule_delayed_work(struct delayed_work *work,
                     unsigned long delay)
{
    wake_lock(&mmc_delayed_work_wake_lock);
    return queue_delayed_work(workqueue, work, delay);
    //这个工作队列很有意思,属于内核API,最终是申请几个线程(与cpu个数相同),不过这样的线程比较独立,用的也比较少,比如这里的mmc_rescan,初始化的时候就会创建这个线程,不过生命周期
很短,这个运用还不太明显,初始化结束后,插上sd卡,就用到中断了,中断子程序最终还会调用mmc_rescan,但是中断中内容不能太多,执行时间不能太长,所以另外申请了一个线程用于执行后来的工作,
那么为什么不能使用中断下半部呢?因为初始化的时候也要用到这段代码啊。感觉这样的解释还是有点牵强了 。。。。
//工作队列(workqueue)是另外一种将工作推后执行的形式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。
最重要的就是工作队列允许被重新调度甚至是睡眠。
}
void mmc_rescan(struct work_struct *work)
{
    struct mmc_host *host =
        container_of(work, struct mmc_host, detect.work);
    u32 ocr;
    int err;
    int extend_wakelock = 0;

    mmc_bus_get(host);
    //总线操作加1

    /* if there is a card registered, check whether it is still present */
    if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
        host->bus_ops->detect(host);
    //如果一个卡存在,那么对应的总线操作肯定存在

    /* If the card was removed the bus will be marked
     * as dead - extend the wakelock so userspace
     * can respond */
    if (host->bus_dead)
        //卡已经不在了,延长什么呢。。
        extend_wakelock = 1;

    mmc_bus_put(host);


    mmc_bus_get(host);

    /* if there still is a card present, stop here */
    //如果卡一直存在,那么怎么为引起mmc_rescan呢
    if (host->bus_ops != NULL) {
        mmc_bus_put(host);
        goto out;
    }
    //这个只是说原来的卡还存在
       //对于第二个卡,产生中断调用到这儿,这样的处理合适吗
    /* detect a newly inserted card */

    /*
     * Only we can add a new handler, so it's safe to
     * release the lock here.
     */
    mmc_bus_put(host);

    if (host->ops->get_cd && host->ops->get_cd(host) == 0)
        //没有卡退出
        goto out;

    mmc_claim_host(host);
    //驱动中使用 mmc_claim_host(host);来得知,当前mmc控制器是否被占用,
    //    当前mmc控制器如果被占用,那么host->claimed = 1;否则为0,如果为1,
    //那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的
    //操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq
    //中的其他程序获得mmc主控制器的物理使用权 [gliethttp_20080630].
    //第二张卡插入时会等待控制器的使用权,这是很可能的

    mmc_power_up(host);
    mmc_go_idle(host);

    mmc_send_if_cond(host, host->ocr_avail);
    //sd2.0还是sd1.0

    /*
     * First we search for SDIO...
     */
    err = mmc_send_io_op_cond(host, 0, &ocr);
    if (!err) {
        if (mmc_attach_sdio(host, ocr))
            mmc_power_off(host);
        extend_wakelock = 1;
        goto out;
    }

    /*
     * ...then normal SD...
     */
    err = mmc_send_app_op_cond(host, 0, &ocr);
    if (!err) {
        if (mmc_attach_sd(host, ocr))
            //这个是重点,提取sd卡中的信息并且注册到/sys/bus/mmc/device
            //bus_ops也初始化了与卡的类型有关
            mmc_power_off(host);
        extend_wakelock = 1;
        goto out;
    }

    /*
     * ...and finally MMC.
     */
    err = mmc_send_op_cond(host, 0, &ocr);
    if (!err) {
        if (mmc_attach_mmc(host, ocr))
            mmc_power_off(host);
        extend_wakelock = 1;
        goto out;
    }

    mmc_release_host(host);
    mmc_power_off(host);

out:
    if (extend_wakelock)
        wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
    else
        wake_unlock(&mmc_delayed_work_wake_lock);

    if (host->caps & MMC_CAP_NEEDS_POLL)
        mmc_schedule_delayed_work(&host->detect, HZ);
}