• SD卡驱动分析(二)


    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);
    }
  • 相关阅读:
    window.open()被浏览器拦截问题汇总
    如何快速判断页面是服务端渲染还是客户端渲染
    前端标注软件-pxcook像素大厨使用心得
    nodejs 实践:express 最佳实践(四) express-session 解析
    webpack 精华文章
    数据库 join
    $.ajax 中的contentType
    nodejs 实践:express 最佳实践(二) 中间件
    漂亮的代码6:增加字符串后的数字
    走马观花看语言总纲
  • 原文地址:https://www.cnblogs.com/autum/p/mmc.html
Copyright © 2020-2023  润新知