您的位置:首页 > 运维架构 > Linux

linux的mmc_spi学习笔记

2013-07-14 22:24 495 查看
内核为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块设备驱动中被调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux mmc emmc