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