前面对SD卡控制器有了一个基本的介绍。其实SD控制器层更过的意义是为core层提供一种操作SD卡硬件的一种方法,当然不同的控制器对硬件控制的方法不尽相同,但是他们最终都能像core层提交一个统一的封装有操作方法的数据结构,那便是即将闪亮登场的struct mmc_host_ops....对应的host文件为s3cmci.c。
接下来就来揭开与之对应的struct mmc_host_ops结构的神秘面纱....
static struct mmc_host_ops s3cmci_ops = {
.request = s3cmci_request,
.set_ios = s3cmci_set_ios,
.get_ro = s3cmci_get_ro,
.get_cd = s3cmci_card_present,
.enable_sdio_irq = s3cmci_enable_sdio_irq,
};
在讲述每个方法具体的实现之前,先来对struct mmc_host_ops结构中的各个成员有个简单的认识。
request方法:无论是前面所说的单纯的命令传输,还是带有数据的传输过程,无一例外最终都是调用request来实现的,那么如您所想,他也将成为这个舞台万众瞩目的焦点。
set_ios方法:用于设置SD卡控制器,前面我们所见到的设置控制器时钟,数据线宽度等等一系列操作最终就是通过他来实现的。
get_ro方法:获取卡的写保护状态,前面所过,SD卡初始化完成以后,我们进行的一个最后的工作便是检测卡的写保护状态,其实就是调用get_ro方法。
get_cd方法:检测卡是否在卡槽之中,它所对应的函数前面已经在初始化中分析过了,这里不再单独列来。
enable_sdio_irq方法:就是使能SDIO卡的中断,这个是对sdio卡而言的,这里将不做重点分析。
有了一个初步的了解之后,接下来的时间就来各个击破了,本着由浅入深的原则我们先来看看s3cmci_get_ro。
一、s3cmci_get_ro
从SD卡结构上来说有个写保护的开关,这就使得判断SD卡是否写保护可以从其机械特征上入手,从而特殊设计的SD卡槽为我们提供了方便。在这里采用的方法正是利用了这种特殊设计的SD卡槽带来的优势,因此只需要读取SD卡槽的SD写保护引脚的状态就能判定卡写保护的情况。实现的代码如下:
static int s3cmci_get_ro(struct mmc_host *mmc)
{
struct s3cmci_host *host = mmc_priv(mmc);
struct s3c24xx_mci_pdata *pdata = host->pdata;
int ret;
if (pdata->no_wprotect)
return 0;
ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;
ret ^= pdata->wprotect_invert;
return ret;
}
第10行正是获取SD写保护引脚的值,当然由于硬件设计上的不同可能带来状态上的取反,所以这里有个pdata->wprotect_invert标记决定是否应该反相。对于只读来说应该返回1,否则该方法的返回值为0。
二、s3cmci_set_ios
根据我们前面所见到的种种设置,这里的ioset可能会相对烦锁一些,具体的代码如下:
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct s3cmci_host *host = mmc_priv(mmc);
u32 mci_con;
/* Set the power state */
mci_con = readl(host->base + S3C2410_SDICON);
switch (ios->power_mode) {
case MMC_POWER_ON:
case MMC_POWER_UP:
/* Configure GPE5...GPE10 pins in SD mode */
s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2),
S3C_GPIO_PULL_NONE);
if (host->pdata->set_power)
host->pdata->set_power(ios->power_mode, ios->vdd);
if (!host->is2440)
mci_con |= S3C2410_SDICON_FIFORESET;
break;
case MMC_POWER_OFF:
default:
gpio_direction_output(S3C2410_GPE(5), 0);
if (host->is2440)
mci_con |= S3C2440_SDICON_SDRESET;
if (host->pdata->set_power)
host->pdata->set_power(ios->power_mode, ios->vdd);
break;
}
s3cmci_set_clk(host, ios);
/* Set CLOCK_ENABLE */
if (ios->clock)
mci_con |= S3C2410_SDICON_CLOCKTYPE;
else
mci_con &= ~S3C2410_SDICON_CLOCKTYPE;
writel(mci_con, host->base + S3C2410_SDICON);
if ((ios->power_mode == MMC_POWER_ON) ||
(ios->power_mode == MMC_POWER_UP)) {
dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
host->real_rate/1000, ios->clock/1000);
} else {
dbg(host, dbg_conf, "powered down.\n");
}
host->bus_width = ios->bus_width;
}
8行对SD卡控制器的设置最直接的莫过于对寄存器S3C2410_SDICON的访问了,为了保证后面不改变其他无关位的值,这里先读取S3C2410_SDICON中的当前值保存。
10-39行是SD控制器工作状态的设定,对ioset来说,swith无疑是他最好的朋友,当MMC_POWER_UP时,SD控制器的相应管脚会得到正确的初始化。其他的如fifo也将被正确复位。
41-47行就是对sd控制器时钟的设置,最终一切ioset的成功归功于49行将重新设置的S3C2410_SDICON状态写入寄存器,从此新的控制器状态生效。
那么在主机驱动层中的一个请求处理是怎么通过核心层提交到块设备请求层的呢?
在网上找到一副图来说明他们之间的关联和处理流程,如下图:
三、s3cmci_request
结构体mmc_request定义于/include/linux/mmc/core.h,它主要存放两大数据结构的指针,分别是cmd和data,顾名思意,一个为指令,一个为数据
也就是说,mmc_request结构体存放了进行主控制器与sd卡间通信所需要的指令和数据
static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct s3cmci_host *host = mmc_priv(mmc);
host->status = "mmc request";
host->cmd_is_stop = 0;
host->mrq = mrq;
if (s3cmci_card