内核为linux3.0
硬件平台为:龙芯1B开发板
1、驱动文件结构:
mmc驱动共分为三个目录:card/、core/、host/
card目录下的驱动文件是卡的设备驱动,也就是针对mmc或者sd卡的块设备驱动
core目录下的驱动文件是mmc总线驱动程序
host目录下的驱动文件是mmc或者sd卡的通讯接口驱动
2、体系结构简述(以mmc为例)
(1)card部分中的block.c文件中的初始化函数:
staticint __init mmc_blk_init(void)
{
…...
res= register_blkdev(MMC_BLOCK_MAJOR, "mmc");
…...
res= mmc_register_driver(&mmc_driver);
}
可以看到,register_blkdev为注册一个块设备,mmc_register_driver为注册一个mmc驱动。
其中mmc_driver就是从mmc总线驱动中注册一个mmc_driver,这样,我们就可以从probe函数中得到mmc_device,这里叫mmc_card。
staticint mmc_blk_probe(struct mmc_card *card);
其中mmc_card结构体有一个成员指针为:
structmmc_host *host;
该指针指向一个mmc主机实例,块设备中的读写操作就是调用这个mmc主机的操作函数host->ops->request来实现对实际硬件的操作。
从上面的分析来看,要找到这mmc_card,就得先把mmc_card这个设备挂载到mmc_bus去。
(2)core部分中的core.c文件中的初始化函数:
staticint __init mmc_init(void)
{
……
workqueue= alloc_ordered_workqueue("kmmcd", 0);
…...
ret= mmc_register_bus();
……
ret= mmc_register_host_class();
……
ret= sdio_register_bus();
…...
}
这个函数一开始建立了一个工作队列workqueue,这个工作队列的作用主要是用来支持热插拔,在后面我们将会看到。然后分别注册一个mmc总线mmc_bus_type,注册了一个mmc_host类,和注册了一个sdio_bus_type。这里我们以mmc为例,主要是知道注册了一个mmc总线。那么怎样生成一个mmc设备,并挂载到该总线上呢?
(3)host部分中的mmc_spi.c文件中的初始化函数:
staticint __init mmc_spi_init(void)
{
returnspi_register_driver(&mmc_spi_driver);
}
该函数只是注册了一个spi驱动,由于我们的mmc是通过spi接口来通讯的,所以这个驱动实则上就是最低层的硬件接口驱动。当然,在这之前,我们得注册一个spi总线,并在该spi总线挂载了一个spi接口设备。这样,在注册spi驱动的时候,就会调用该驱动中的probe函数。Probe函数中调用了函数:
mmc= mmc_alloc_host(sizeof(*host), &spi->dev);
创建一个mmc_host结构,这个结构就是在mmc_card所需要的mmc主机实例。
在mmc_alloc_host中,创建一个mmc_host和mmc_spi_host,且mmc_host的最后一个成员指针private指向mmc_spi_host。
该函数一个重要的初始化操作:
mmc->ops= &mmc_spi_ops;
这个mmc_spi_ops操作函数就是spi接口的操作函数。
除此之外,最重要的是建立了一个工作队列任务structdelayed_work detect。工作队列任务执行的函数为mmc_rescan,在这个函数中以不同的频率扫描:
staticint mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init= freq;
……
mmc_power_up(host);
sdio_reset(host);
mmc_go_idle(host);
mmc_send_if_cond(host,host->ocr_avail);
if(!mmc_attach_sdio(host))
return0;
if(!mmc_attach_sd(host))
return0;
if(!mmc_attach_mmc(host))
return0;
mmc_power_off(host);
}
从这个函数看到,一开始就是设置某一个时钟频率,然后对mmc或者sd发送一些命令进行探测。这里以mmc为例,所以看mmc_attach_mmc这个函数:
intmmc_attach_mmc(struct mmc_host *host)
{
…...
err= mmc_init_card(host, host->ocr, NULL);
……
err= mmc_add_card(host->card);
…...
}
一开始设置mmc的电压,然后就对mmc卡进行初始化,主要是读取mmc卡里的一些寄存器信息,且对这些寄存器的值进行设置。然后,调用mmc_add_card来把mmc_card挂载到mmc_bus_type总线去:
voidbus_probe_device(struct device *dev)
{
if(bus && bus->p->drivers_autoprobe) {
ret = device_attach(dev);
}
}
这样,在总线mmc_bus_type中就有了mmc设备mmc_card了。
不过这里有个问题,就是这个工作队列任务什么时候开始执行呢,又与(1)部分创建的工作队列workqueue有什么关系,跟热插拔有什么关系。
继续把焦点放到probe函数:
if(host->pdata && host->pdata->init) {
status= host->pdata->init(&spi->dev,mmc_spi_detect_irq, mmc);
if(status != 0)
gotofail_glue_init;
}
这里有个mmc_spi_detect_irq的函数,这个函数顾名思义是检测mmc_spi中断。意思大概是当mmc插入时检测到一个外部中断。
staticirqreturn_t
mmc_spi_detect_irq(intirq, void *mmc)
{
structmmc_spi_host *host = mmc_priv(mmc);
u16delay_msec = max(host->pdata->detect_delay, (u16)100);
mmc_detect_change(mmc,msecs_to_jiffies(delay_msec));
returnIRQ_HANDLED;
}
mmc_detect_change调用了:
mmc_schedule_delayed_work(&host->detect,delay);
再调用:
queue_delayed_work(workqueue,work, delay);
这个函数就是把工作队列任务host->detect放到一开始就创建的工作队列workqueue中。也就是这个时候开始执行mmc_rescan来扫描mmc卡。
那么什么时候产生这个中断的呢,至少要申请一个外部中断,这是热插拔的功能,同时是移植驱动的关键。
再来看host->pdata->init(&spi->dev,mmc_spi_detect_irq,mmc);这个函数。先找到host->pdata的由来,还是在probe函数中:
host->pdata= mmc_spi_get_pdata(spi);
staticinline *
mmc_spi_get_pdata(structspi_device *spi)
{
returnspi->dev.platform_data;
}
由此可知,我们要实现一个structmmc_spi_platform_data结构,并将其赋值给spi_device的dev的platform_data成员。
structmmc_spi_platform_data {
/*driver activation and (optional) card detect irq hookup */
int(*init)(struct device *,
irqreturn_t(*)(int, void *),
void*);
void(*exit)(struct device *, void *);
/*sense switch on sd cards */
int(*get_ro)(struct device *);
/*
*If board does not use CD interrupts, driver can optimize polling
*using this function.
*/
int(*get_cd)(struct device *);
/*Capabilities to pass into mmc core (e.g. MMC_CAP_NEEDS_POLL). */
unsignedlong caps;
/*how long to debounce card detect, in msecs */
u16detect_delay;
/*power management */
u16powerup_msecs; /* delay of up to 250 msec */
u32ocr_mask; /* available voltages */
void(*setpower)(struct device *, unsigned int maskval);
};
从这个结构中,可以看到,要实现init这个函数来申请外部IO中断,并把mmc_spi_detect_irq作为中断服务程序。再来看几个成员函数:
get_ro:读取写保护状态(getthe state of read only)。
get_cd:读取卡插入或者拔出状态(getthe state of card detected)。
setpower:设置卡上电或者下电状态。
那么这些函数在哪里被调用呢。看到mmc->ops= &mmc_spi_ops中的mmc_spi_ops结构:
staticconst struct mmc_host_ops mmc_spi_ops = {
.request = mmc_spi_request,
.set_ios = mmc_spi_set_ios,
.get_ro = mmc_spi_get_ro,
.get_cd = mmc_spi_get_cd,
};
对应的操作函数中调用了mmc_spi_platform_data对应的函数。mmc_spi_ops操作函数在card块设备驱动中被调用。