Chinaunix首页 | 论坛 | 博客
  • 博客访问: 334367
  • 博文数量: 125
  • 博客积分: 30
  • 博客等级: 民兵
  • 技术积分: 160
  • 用 户 组: 普通用户
  • 注册时间: 2012-12-06 15:18
文章分类

全部博文(125)

文章存档

2014年(29)

2013年(93)

2012年(3)

分类: LINUX

2014-02-28 15:53:59




网上已经有很多文章写了Linux SD/MMC的驱动的分析了,尤其是SAMSUNG系列的,估计用汗牛充栋来描写都不过分。俺只能说点我自己写的基于CBP的EVB板子的SD/MMC controller的驱动了,这个驱动没有采用DMA,虽然那是我最擅长的。使用的是pio模式,其实就是CPU读写了,但是竟然也没有使用tasklet,也就没有中断的上下半部的说法了,应该不是一个很好的习惯,虽然tasklet其实就是启动一个内核运行队列,去运行pio的读写等等,本质上没有大的区别,但没有使用就是没有使用,实事求是而已。

那就说说Linux SD/MMC驱动的工作过程吧:

首先按照platform驱动的方式,注册probe函数,然后系统就会调用probe函数,具体方法和原理请参考本人写的以及转载的plateform的文章,就不在赘述了。

在probe里面首先分配一个mmc_host的结构,Linux以后发起各种操作的时候,参数都会通过这个结构传递。

mmc=mmc_alloc_host(sizeof(struct cbpmci_host), &pdev->dev);

mmc_alloc_host这个函数比较有意思,其中传入的第一个参数是一个长度,这个长度是在mmc_hos后面额外连续分配的一块数据,我这句的意思就是紧跟着mmc_host分配了一个cbpmci_host的结构,看mmc_host的定义如下:

struct mmc_host {

       struct device          *parent;

………省略若干行………………

unsigned long        private[0] ____cacheline_aligned;

};


其实cbpmci_host的位置就是private[0]的位置,巧妙吧?经常看看Linux代码能学到不少Linux大牛们的方法。这样就可以通过这个函数来从mmc_host中得到自己带入的私有数据了:

static inline void *mmc_priv(struct mmc_host *host)

{

       return (void *)host->private;

}

按照Linux驱动的惯例,此时应该注册SD/MMC的操作函数了,SD/MMC的操作函数恐怕是Linux驱动里面最少的之一了,只有如下几个:

struct mmc_host_ops {

       int (*enable)(struct mmc_host *host);

       int (*disable)(struct mmc_host *host, int lazy);

       void (*request)(struct mmc_host *host, struct mmc_request *req);

       void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);

       int    (*get_ro)(struct mmc_host *host);

       int    (*get_cd)(struct mmc_host *host);

       void (*enable_sdio_irq)(struct mmc_host *host, int enable);

};

我的定义:

static struct mmc_host_ops cbpmci_ops = {

       .request   = cbpmci_request,

       .set_ios   = cbpmci_set_ios,

       .get_ro           = cbpmci_get_ro,

       .get_cd           = cbpmci_card_present,

       .enable_sdio_irq = cbpmci_enable_sdio_irq,

};

将cbpmci_ops 赋值给mmc->ops即可

mmc->ops     = &cbpmci_ops;

为啥连读写都没有呢,因为他是通过request里面的参数来进行标识的。Request函数主要处理Linux发到驱动的命令请求,包括带数据的和不带数据的,带数据的一般就是read/write, 其实ext CSD之类的东东也是通过读来完成的。

其中的set_ios是设置一些控制参数的,比如时钟频率,电源开/关,总线宽度等等。

get_ro是获取SD/MMC的写保护标志的,就是SD卡上的那个小开关的状态,如果返回0则是可写的,不然为只读。

get_cd是获取卡的插入状态的,返回0则有卡,不然返回1。

接着当然是要申请对应的SD的中断了,在中断函数中将处理命令完成,CRC错误等等中断。

然后再初始化一些mmc_host的参数就OK了,比如最大最小频率,总线宽度,最大的block count等等。然后调用 mmc_add_host将host驱动加入。

ret = mmc_add_host(mmc);

如果驱动加入没有错误,系统将会调用相应的初始化过程!

阅读(918) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~