Chinaunix首页 | 论坛 | 博客
  • 博客访问: 130273
  • 博文数量: 22
  • 博客积分: 949
  • 博客等级: 准尉
  • 技术积分: 266
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-10 22:31
文章分类

全部博文(22)

文章存档

2012年(1)

2011年(2)

2010年(19)

分类: 嵌入式

2010-11-15 19:20:20

网上已经有很多文章写了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);
如果驱动加入没有错误,系统将会调用相应的初始化过程,好像写的有点长了,还是下回分解吧!
 
不过瘾?请看下一篇《编写自己的SD/MMC Host驱动(二):工作过程和大结局》http://blog.chinaunix.net/u3/118741/showart_2397279.html
阅读(1872) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~