前面对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;
-
-
-
-
mci_con = readl(host->base + S3C2410_SDICON);
-
-
switch (ios->power_mode) {
-
case MMC_POWER_ON:
-
case MMC_POWER_UP:
-
-
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);
-
-
-
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//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_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);
-
}
第9行判断SD卡是否还在卡槽之中,如果已经拔出,那不客气mmc_request_done将帮您结束这个请求。怎么个解决法还是先看看mmc_request_done的代码:
mmc_request_done
[core/core.c]
-
-
-
-
-
-
-
-
-
void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
-
{
-
struct mmc_command *cmd = mrq->cmd;
-
int err = cmd->error;
-
-
if (err && cmd->retries && mmc_host_is_spi(host)) {
-
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
-
cmd->retries = 0;
-
}
-
-
if (err && cmd->retries && !mmc_card_removed(host->card)) {
-
-
-
-
-
if (mrq->done)
-
mrq->done(mrq);
-
} else {
-
mmc_should_fail_request(host, mrq);
-
-
led_trigger_event(host->led, LED_OFF);
-
-
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
-
mmc_hostname(host), cmd->opcode, err,
-
cmd->resp[0], cmd->resp[1],
-
cmd->resp[2], cmd->resp[3]);
-
-
if (mrq->data) {
-
pr_debug("%s: %d bytes transferred: %d\n",
-
mmc_hostname(host),
-
mrq->data->bytes_xfered, mrq->data->error);
-
}
-
-
if (mrq->stop) {
-
pr_debug("%s: (CMD%u): %d: %08x %08x %08x %08x\n",
-
mmc_hostname(host), mrq->stop->opcode,
-
mrq->stop->error,
-
mrq->stop->resp[0], mrq->stop->resp[1],
-
mrq->stop->resp[2], mrq->stop->resp[3]);
-
}
-
-
if (mrq->done)
-
mrq->done(mrq);
-
-
mmc_host_clk_release(host);
-
}
-
}
14行如果是SPI传输出现错误,而且还有重试的机会,那么只要SPI不忽略这个命令,那么就还是给他重试的机会,也就到了85-91行继续调用host->ops->request(host, mrq);提交请求,否则既然是SPI忽略了这个命令,无论重试多少次都不会有结果,那么就干脆一不做二不休cmd->retries = 0;
14-16行就是只要设备有重生的机会就还是继续拯救...
26-51行如果传输无误或者重试次数到了,就会执行。其中多半是调试信息。
50-51行许下的承诺就好比欠下的债,前面我们讨论mmc_wait_for_req的时候有这么两句:
mrq->done_data = &complete;
mrq->done = mmc_wait_done;
然后我们说N年以后的某一天我们会和mmc_wait_done 再聚首,这里51 行便是调用的mmc_wait_done。内容如下:
mmc_wait_done
[core/core.c]
-
static void mmc_wait_done(struct mmc_request *mrq)
-
{
-
complete(mrq->done_data);
-
}
还记得mmc_wait_for_req中为了你苦苦等待的那个wait_for_completion(&complete),因为等待,所以她进入了睡眠。现在事情做完了,他重新回来调用complete(mrq->done_data)唤醒这个沉睡的内核精灵。说到这好奇的人难免会问,那要是一直出错又该是谁来唤醒他呢?带着疑问我们继续向前....
回到s3cmci_request,如果卡还存在的话s3cmci_send_request将真正开始这个请求的处理。
s3cmci_send_request
[host/s3cmci.c]
-
static void s3cmci_send_request(struct mmc_host *mmc)
-
{
-
struct s3cmci_host *host = mmc_priv(mmc);
-
struct mmc_request *mrq = host->mrq;
-
struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
-
-
host->ccnt++;
-
prepare_dbgmsg(host, cmd, host->cmd_is_stop);
-
-
-
-
-
writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
-
writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
-
writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
-
-
if (cmd->data) {
-
int res = s3cmci_setup_data(host, cmd->data);
-
-
host->dcnt++;
-
-
if (res) {
-
dbg(host, dbg_err, "setup data error %d\n", res);
-
cmd->error = res;
-
cmd->data->error = res;
-
-
mmc_request_done(mmc, mrq);
-
return;
-
}
-
-
if (s3cmci_host_usedma(host))
-
res = s3cmci_prepare_dma(host, cmd->data);
-
else
-
res = s3cmci_prepare_pio(host, cmd->data);
-
-
if (res) {
-
dbg(host, dbg_err, "data prepare error %d\n", res);
-
cmd->error = res;
-
cmd->data->error = res;
-
-
mmc_request_done(mmc, mrq);
-
return;
-
}
-
}
-
-
-
s3cmci_send_command(host, cmd);
-
-
-
s3cmci_enable_irq(host, true);
-
}
13-15行全部写入1,是为了清除之前传输的SDI命令状态寄存器、SDI数据状态寄存器以及SDI FIFO状态寄存器。这是在一次新的传输之前所必须有的初始化工作,否则可能出现未知的状态错误。
17行cmd->data实际上就是mmc_request->data,前面没少对他进行介绍。与之相类似的还有stop->data。这里我们姑且不说带有数据的传输过程,先来看看SD卡命令的实现。也就是1171行s3cmci_send_command(host, cmd);
命令、数据发送流程如下图:
1、发送命令
s3cmci_send_command(host, cmd)
[host/s3cmci.c]
-
static void s3cmci_send_command(struct s3cmci_host *host,
-
struct mmc_command *cmd)
-
{
-
u32 ccon, imsk;
-
-
imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
-
S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
-
S3C2410_SDIIMSK_RESPONSECRC;
-
-
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;
-
-
writel(ccon, host->base + S3C2410_SDICMDCON);
-
}
6-8行是使能相应的中断,其中包括CRC校验错误、命令超时、收到命令响应等等。具体的中断屏蔽寄存器的内容可以参考S3C2440用户手册。
12-17行实际上指当前的这个命令结束时候应该所处的状态,中断处理函数将实际硬件的完成情况与这个状态相比较,最终得到这个命令执行的结果。而cmd->flags正是前面提交命令之前根据不同命令的实际情况来设置的,比如具有应答数据的命令可能需要设置cmd->flags |= MMC_RSP_PRESENT。然后对应的结束状态也就应该是COMPLETION_RSPFIN收到应答。前面说过host->complete_what是个枚举类型的变量包含了整个命令过程的各个阶段,内容如下:
-
enum s3cmci_waitfor {
-
COMPLETION_NONE,
-
COMPLETION_FINALIZE,
-
COMPLETION_CMDSENT,
-
COMPLETION_RSPFIN,
-
COMPLETION_XFERFINISH,
-
COMPLETION_XFERFINISH_RSPFIN,
-
};
一般的命令可能无应答阶段,我们默认数据传输正确完成以后即认为命令执行完成也就是17行对应的host->complete_what = COMPLETION_CMDSENT;
19行是对命令命令参数寄存器的设置,cmd->arg是一个32bit的整数,这里如实填写即可。
21行之后的内容就是对控制寄存器的设置了,由于控制寄存器比较重要,这里列出他寄存器位的信息如下:对照上表应该不难分析函数中所设置的每一位的具体含义,这里就不再一一解释了。SDICmdCon[8]的置位使得SD控制器开始发送命令。回到s3cmci_send_request....
前一段第50行s3cmci_enable_irq(host, true); 就是使能SDI 控制器的中断。然而s3cmci_send_command 中间的944 行设置imr|=S3C2410_SDIIMSK_CMDSENT,命中注定命令发出以后产生一个相应的中断,接下来就进入probe 阶段所注册的那个SDI 中断request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)。
request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)
[host/s3cmci.c]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
static irqreturn_t s3cmci_irq(int irq, void *dev_id)
-
{
-
struct s3cmci_host *host = dev_id;
-
struct mmc_command *cmd;
-
u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
-
u32 mci_cclear = 0, mci_dclear;
-
unsigned long iflags;
-
-
mci_dsta = readl(host->base + S3C2410_SDIDSTA);
-
mci_imsk = readl(host->base + host->sdiimsk);
-
-
if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {
-
if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {
-
mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
-
writel(mci_dclear, host->base + S3C2410_SDIDSTA);
-
-
mmc_signal_sdio_irq(host->mmc);
-
return IRQ_HANDLED;
-
}
-
}
-
-
spin_lock_irqsave(&host->complete_lock, iflags);
-
-
mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
-
mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
-
mci_fsta = readl(host->base + S3C2410_SDIFSTA);
-
mci_dclear = 0;
-
-
if ((host->complete_what == COMPLETION_NONE) ||
-
(host->complete_what == COMPLETION_FINALIZE)) {
-
host->status = "nothing to complete";
-
clear_imask(host);
-
goto irq_out;
-
}
-
-
if (!host->mrq) {
-
host->status = "no active mrq";
-
clear_imask(host);
-
goto irq_out;
-
}
-
-
cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
-
-
if (!cmd) {
-
host->status = "no active cmd";
-
clear_imask(host);
-
goto irq_out;
-
}
-
-
if (!s3cmci_host_usedma(host)) {
-
if ((host->pio_active == XFER_WRITE) &&
-
(mci_fsta & S3C2410_SDIFSTA_TFDET)) {
-
-
disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
-
tasklet_schedule(&host->pio_tasklet);
-
host->status = "pio tx";
-
}
-
-
if ((host->pio_active == XFER_READ) &&
-
(mci_fsta & S3C2410_SDIFSTA_RFDET)) {
-
-
disable_imask(host,
-
S3C2410_SDIIMSK_RXFIFOHALF |
-
S3C2410_SDIIMSK_RXFIFOLAST);
-
-
tasklet_schedule(&host->pio_tasklet);
-
host->status = "pio rx";
-
}
-
}
-
-
if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
-
dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
-
cmd->error = -ETIMEDOUT;
-
host->status = "error: command timeout";
-
goto fail_transfer;
-
}
-
-
if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
-
if (host->complete_what == COMPLETION_CMDSENT) {
-
host->status = "ok: command sent";
-
goto close_transfer;
-
}
-
-
mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
-
}
-
-
if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
-
if (cmd->flags & MMC_RSP_CRC) {
-
if (host->mrq->cmd->flags & MMC_RSP_136) {
-
dbg(host, dbg_irq,
-
"fixup: ignore CRC fail with long rsp\n");
-
} else {
-
-
-
-
-
-
-
-
-
}
-
}
-
-
mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
-
}
-
-
if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {
-
if (host->complete_what == COMPLETION_RSPFIN) {
-
host->status = "ok: command response received";
-
goto close_transfer;
-
}
-
-
if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
-
host->complete_what = COMPLETION_XFERFINISH;
-
-
mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
-
}
-
-
-
-
-
if (!cmd->data)
-
goto clear_status_bits;
-
-
-
if (host->is2440) {
-
if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
-
dbg(host, dbg_err, "FIFO failure\n");
-
host->mrq->data->error = -EILSEQ;
-
host->status = "error: 2440 fifo failure";
-
goto fail_transfer;
-
}
-
} else {
-
if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
-
dbg(host, dbg_err, "FIFO failure\n");
-
cmd->data->error = -EILSEQ;
-
host->status = "error: fifo failure";
-
goto fail_transfer;
-
}
-
}
-
-
if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
-
dbg(host, dbg_err, "bad data crc (outgoing)\n");
-
cmd->data->error = -EILSEQ;
-
host->status = "error: bad data crc (outgoing)";
-
goto fail_transfer;
-
}
-
-
if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
-
dbg(host, dbg_err, "bad data crc (incoming)\n");
-
cmd->data->error = -EILSEQ;
-
host->status = "error: bad data crc (incoming)";
-
goto fail_transfer;
-
}
-
-
if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
-
dbg(host, dbg_err, "data timeout\n");
-
cmd->data->error = -ETIMEDOUT;
-
host->status = "error: data timeout";
-
goto fail_transfer;
-
}
-
-
if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
-
if (host->complete_what == COMPLETION_XFERFINISH) {
-
host->status = "ok: data transfer completed";
-
goto close_transfer;
-
}
-
-
if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
-
host->complete_what = COMPLETION_RSPFIN;
-
-
mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
-
}
-
-
clear_status_bits:
-
writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
-
writel(mci_dclear, host->base + S3C2410_SDIDSTA);
-
-
goto irq_out;
-
-
fail_transfer:
-
host->pio_active = XFER_NONE;
-
-
close_transfer:
-
host->complete_what = COMPLETION_FINALIZE;
-
-
clear_imask(host);
-
tasklet_schedule(&host->pio_tasklet);
-
-
goto irq_out;
-
-
irq_out:
-
dbg(host, dbg_irq,
-
"csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n",
-
mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status);
-
-
spin_unlock_irqrestore(&host->complete_lock, iflags);
-
return IRQ_HANDLED;
-
-
}
36-48行是判断SDIO所触发的中断,与我们说说的无关。飘过....
30-34行分别读取命令状态、尚未完成传输的数据大小以及FIFO的状态的值。
56-61行就是之前所分析的host->complete_what,如果设备无欲无求host->complete_what == COMPLETION_NONE,即使连最基本的命令发送也不要求完成的话,那就没什么意义了,直接清除IMASK,返回。
-
static inline void clear_imask(struct s3cmci_host *host)
-
{
-
u32 mask = readl(host->base + host->sdiimsk);
-
-
-
mask &= S3C2410_SDIIMSK_SDIOIRQ;
-
writel(mask, host->base + host->sdiimsk);
-
}
上面的代码只保留了SDIO IRQ状态,其他的中断都是被屏蔽了的。由此足见其对SDIO设备的偏心程度。
585-589 行尽然玩丢了host->mrq,无论是命令还是数据请求,我们都是递交了struct mmc_request结构的,所以驱动很气愤,直接返回。
591行前面我们看到struct mmc_request中包含了两种类型的struct mmc_cmd一个是所谓的cmd另外一个就是stop了,当然选择哪一个也不是他自己说来算了,当然有主机host-
>cmd_is_stop来决定了。
63-67行是PIO模式下数据传输的,我们姑且先放着,等说完CMD回头再看。
98-103行命令超时以后就会跳转到fail_transfer,至于fail_transfer又干了些啥好事我们走到那里了再说,继续前进...
105-109行命令发送成功以后所产生的中断,如果host->complete_what也正好只要求传输成功即COMPLETION_CMDSENT,那正好完成工作,goto close_transfer。
114-129行是CRC错误,忽略。
134-138行命令相应接收成功,那么依旧goto close_transfer。
140-143行至今尚未发现一个所谓的COMPLETION_XFERFINISH_RSPFIN最多也就数据传输成功那么修改一下这个脑残host->complete_what =COMPLETION_XFERFINISH;
149-150行如果没有数据传输,那么接下来就可以进行状态清理工作了。
153-200行是检查FIFO信息的,回头说到PIO传输的时候在来分析它。
203-204行意图很明确,显然是毁尸灭迹,清除状态。最后可以看到无论是先前的fail_transfer:还是后来的close_transfer,最总都会去调用
215行的tasklet_schedule(&host->pio_tasklet),是什么赋予这个函数如此强大的魅力,且听下回分解...
2、数据传输 s3cmci_setup_data
是时候该看点实际的数据传输了,前面说过s3cmci_send_request中的if (cmd->data)是区分命令是否有数据阶段的关键标志。如果有数据传输的,那么就到了
s3cmci_setup_data。
-
static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
-
{
-
u32 dcon, imsk, stoptries = 3;
-
-
-
-
if (!data) {
-
writel(0, host->base + S3C2410_SDIDCON);
-
return 0;
-
}
-
-
if ((data->blksz & 3) != 0) {
-
-
-
-
if (data->blocks > 1) {
-
pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
-
return -EINVAL;
-
}
-
}
-
-
while (readl(host->base + S3C2410_SDIDSTA) &
-
(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
-
-
dbg(host, dbg_err,
-
"mci_setup_data() transfer stillin progress.\n");
-
-
writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
-
s3cmci_reset(host);
-
-
if ((stoptries--) == 0) {
-
dbg_dumpregs(host, "DRF");
-
return -EINVAL;
-
}
-
}
-
-
dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
-
-
if (s3cmci_host_usedma(host))
-
dcon |= S3C2410_SDIDCON_DMAEN;
-
-
if (host->bus_width == MMC_BUS_WIDTH_4)
-
dcon |= S3C2410_SDIDCON_WIDEBUS;
-
-
if (!(data->flags & MMC_DATA_STREAM))
-
dcon |= S3C2410_SDIDCON_BLOCKMODE;
-
-
if (data->flags & MMC_DATA_WRITE) {
-
dcon |= S3C2410_SDIDCON_TXAFTERRESP;
-
dcon |= S3C2410_SDIDCON_XFER_TXSTART;
-
}
-
-
if (data->flags & MMC_DATA_READ) {
-
dcon |= S3C2410_SDIDCON_RXAFTERCMD;
-
dcon |= S3C2410_SDIDCON_XFER_RXSTART;
-
}
-
-
if (host->is2440) {
-
dcon |= S3C2440_SDIDCON_DS_WORD;
-
dcon |= S3C2440_SDIDCON_DATSTART;
-
}
-
-
writel(dcon, host->base + S3C2410_SDIDCON);
-
-
-
-
writel(data->blksz, host->base + S3C2410_SDIBSIZE);
-
-
-
imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
-
S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
-
-
enable_imask(host, imsk);
-
-
-
-
if (host->is2440) {
-
writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
-
} else {
-
writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
-
-
-
if (data->flags & MMC_DATA_READ)
-
writel(0xFF, host->base + S3C2410_SDIPRE);
-
}
-
-
return 0;
-
}
7-10行如果data不存在,接下来就无事可做了。
12-20行块大小是4字节对齐的,如果data->blksz不满足,那么返回错误。
22-35行读取数据状态寄存器,如果正在发送或接收数据,则s3cmci_reset(host);复位SD控制器。
-
static void s3cmci_reset(struct s3cmci_host *host)
-
{
-
u32 con = readl(host->base + S3C2410_SDICON);
-
-
con |= S3C2440_SDICON_SDRESET;
-
writel(con, host->base + S3C2410_SDICON);
-
}
37-63行根据数据特征、主机总线宽度等信息设置数据控制寄存器。
67行设置SD控制器块大小寄存器。这是上层设置下来的值,一般为512。
70-73行设置中断屏蔽寄存器,使能数据传输完成中断、超时等。
77-84行是关于读写超时的处理。接着返回到s3cmci_send_request....
如果不出什么问题,应该就到了85行。
s3cmci_prepare_dma(host, cmd->data);是DMA传输的处理,
s3cmci_prepare_pio(host, cmd->data);是PIO方式的处理,下面我们先来关注PIO方式的数据传输。