Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3547557
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类: 其他平台

2014-10-15 15:14:33

原文地址:linux的mmc_spi学习笔记 作者:hrnbdldx

内核为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块设备驱动中被调用。


  • 本稿件为独家原创稿件,版权所有,引用或转载请注明出处。
  • 文章出处:
阅读(695) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~