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