一、开发环境
- 主 机:VMWare--Fedora 9
- 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4
- 编译器:arm-linux-gcc-4.3.2
6. s3cmci_ops SDI主机控制器操作接口函数功能分析:
static struct mmc_host_ops s3cmci_ops =
{
.request = s3cmci_request,//实现host的请求处理(即:命令和数据的发送和接收)
.set_ios = s3cmci_set_ios,//通过核心层传递过来的ios,配置host寄存器(使能时钟、总线带宽等)
.get_ro = s3cmci_get_ro,//通过读取GPIO端口来判断卡是否写有保护
.get_cd = s3cmci_card_present,//通过读取GPIO端口来判断卡是否存在
};
void (*request)(struct mmc_host *host, struct mmc_request *req); |
从各函数原型上看,他们都将mmc_host结构体作为参数,所以我在刚开始的时候就说过mmc_host结构体是MMC/SD卡驱动中比较重要的数据结构。 可以这样说,他是Core层与Host层进行数据交换的载体。那么,这些接口函数何时会被调用呢?答案可以在Core层的core.c和sd.c中找到,我们可以看到如下部分代码:
static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) |
好了,我们开始分析每个接口函数的具体实现吧,从简单的开始吧。 判断卡是否存在,如下代码:
static int s3cmci_card_present(struct mmc_host *mmc) |
获取卡是否写有保护,其实实现跟卡检查类似,代码如下:
static int s3cmci_get_ro(struct mmc_host *mmc) |
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
MMC/SD请求处理,这是Host驱动中比较重要的一部分。请求处理的整个流程请参考(一)中的流程图,他很好的描述了一个请求是怎样从Host层发出,通过Core层提交到Card层被块设备处理的。下面看代码:
static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host *host = mmc_priv(mmc);
//s3cmci_host结构体定义的status主要是记录请求过程所处的阶段及状态,方便调试时使用
host->status = "mmc request";
//请求处理主要包括MMC/SD命令和数据处理,所以定义cmd_is_stop来区分是哪种请求
host->cmd_is_stop = 0;
//将Core层的mmc_request对象保存到Host层中以备使用
host->mrq = mrq;
//在开始发出一个请求前先要检测一下卡是否还存在,否则提交到了块设备层而没有请求处理的对象发生错误
if (s3cmci_card_present(mmc) == 0)
{
dbg(host, dbg_err, "%s: no medium present\n", __func__);
host->mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);//如果卡不存在则马上结束这次请求
}
else
{
s3cmci_send_request(mmc);//如果卡还存在则发出请求
}
}
//发送请求 |
//数据请求处理设置,主要是数据控制寄存器的配置 //复位整个MMC/SD时钟 con |= S3C2440_SDICON_SDRESET; |
//使用DMA传输数据方式,注意:这里就不讲如何使用DMA的具体细节了,以后再讲。 //返回值是传送的DMA缓冲区数,可能会小于sg_len,也就是说sg_len与dma_len可能是不同。 //sg_dma_address返回的是总线(DMA)的地址,sg_dma_len返回的是缓存区的长度
//DMA回调函数, 当一段数据传输完后该函数被调用 |
//使用FIFO传输数据方式。具体操作就是调用do_pio_write往FIFO中填充数据,当64字节的FIFO少于33字节时就会产生中断; |
//以上三段代码是对发送数据请求处理的,下面是发送命令请求
static void s3cmci_send_command(struct s3cmci_host *host, struct mmc_command *cmd)
{
u32 ccon, imsk;
//出现CRC状态错误|命令响应超时|接收命令响应|命令发出|响应CRC校验失败时,将产生SDI中断
imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
S3C2410_SDIIMSK_RESPONSECRC;
//将值写入SDI中断屏蔽寄存器中
enable_imask(host, imsk);
//判断请求所处在何种状态
if (cmd->data)
//如果有数据传输,则设当前任务为完成数据传输且接收命令响应状态
host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
else if (cmd->flags & MMC_RSP_PRESENT)
host->complete_what = COMPLETION_RSPFIN;
else
//命令发送状态
host->complete_what = COMPLETION_CMDSENT;
//设置命令参数寄存器
writel(cmd->arg, host->base + S3C2410_SDICMDARG);
ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;//命令操作开始
if (cmd->flags & MMC_RSP_PRESENT)
ccon |= S3C2410_SDICMDCON_WAITRSP;//主设备等待响应
if (cmd->flags & MMC_RSP_136)
ccon |= S3C2410_SDICMDCON_LONGRSP;//主设备接收一个136位长的响应
//设置命令控制寄存器,开始命令的传输
writel(ccon, host->base + S3C2410_SDICMDCON);
}
7. s3cmci_irq_cd SDI的卡检测中断服务功能
//当MMC/SD卡插入卡槽时引发的中断
static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
{
//这个dev_id参数是申请中断时传递过来的
struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
//调用核心层中的方法将将struct delayed_work detect加入共享工作队列,
//其处理函数为核心层中的mmc_rescan方法,用于卡的识别并初始化。
mmc_detect_change(host->mmc, msecs_to_jiffies(500));
return IRQ_HANDLED;
}
8. s3cmci_irq SDI的中断服务功能。我们从第6小节中对MMC/SD各种请求处理的代码中和(一)中“命令、数据发送流程图”中可以看出,在这个中断服务中将要处理很多请求相关的事情。但对于中断服务来说,这样会严重影响系统的性能,所以这正是为什么要在驱动中实现中断的底半部机制。下面看代码进行分析。
//MMC/SD卡中断服务程序 |
来自