MMC/SD设备驱动分为三个文件夹:host、card、core,这三个文件夹联系的非常紧密,初始化也好、扫描也好、读写也好,总是跳来跳去。
一 host的分配和添加
s3cmci_probe()中的重要函数
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
1 host->rescan_disable = 1;失能card检测。
2 给host分配index,类似ID;
(1) 分配idr的后备资源,预备役。idr_pre_get(&mmc_host_idr, GFP_KERNEL);
(2) get一个与host相关联的id到&host->index。err = idr_get_new(&mmc_host_idr, host, &host->index);
3 初始化等待队列头。init_waitqueue_head(&host->wq);
4 初始化延时工作队列工作。INIT_DELAYED_WORK(&host->detect, mmc_rescan);
(1) (_work)->func = (_func);
(2) 初始化&(_work)->timer定时器,到期的回调函数为delayed_work_timer_fn。
void delayed_work_timer_fn(unsigned long __data)
{
struct delayed_work *dwork = (struct delayed_work *)__data;
struct cpu_workqueue_struct *cwq = get_work_cwq(&dwork->work);
/* should have been called from irqsafe timer with irq already off */
__queue_work(dwork->cpu, cwq->wq, &dwork->work);
}
ret = mmc_add_host(mmc);
ret = mmc_add_host(mmc);mmc_start_host(host);
mmc_detect_change(host, 0);
mmc_schedule_delayed_work(&host->detect, delay);
static struct workqueue_struct *workqueue;
static int mmc_schedule_delayed_work(struct delayed_work *work,
unsigned long delay)
{
return queue_delayed_work(workqueue, work, delay);
}
调度一个延时工作项,就是加入到workqueue,最后会insert到线程池worker_pool的worklist上;delay == 0马上加入,否则,add timer,等到定时到期的回调函数中会执行相同的功能。
mmc_power_off(host);名为断电;包括一些io设置等。
这些工作做完,mmc_rescan()就开始运行了,它是用来扫描card的。
二 card的分配和添加
void mmc_rescan(struct work_struct *work)
1 if (host->rescan_disable)就直接return了,所以一定要使能card检测。
2 host->bus_ops是个什么东西?第一次rescan时,它是NULL的。
3 请求一个host控制器。
mmc_claim_host(host);
__mmc_claim_host(host, NULL);
(1) 初始化话了一个等待队列wait_queue_t,添加到host->wq的等待队列头上。
(2) 设置当前进程是不可中断的。
(3) 查看是否满足终止、host可用、拥有host的是当前线程,满足则break;否则,没有获得host控制器的使用权,就schedule()调度出去。
(4) 设置当前进程为运行状态。
(5) 请求到控制器后,如果只有一张卡,stop会一直为0,没有冲突;那么设置一些请求标记;否则,stop为1,就wake up hots->wq上的等待队列;马上把这个等待队列wait从wq中remove。看来是要wake up其他的wait;为什么不先remove本wait,再wake up其他的?
(6) 如果没有停止,enble host。
(7) stop代表终止host的等待队列并唤醒。
4 mmc_rescan_try_freq(host, max(freqs[i], host->f_min)。
(1) power up
(2) 硬件reset,一是host->ops->hw_reset(host);2是发送CMD52 reset sdio,sd/mmc忽略此命令。
(3) CMD0,设置卡为idle state,注意,如果DAT3/nCS是低电平,就进入SPI模式了。
(4) CMD8,ACM41之前一定会发送这个cmd,SD2.0会响应;SD1.0会fail,区分出来后为什么没有保存?
CMD8的参数:cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;spec2.0: