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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM