歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> Linux SD卡驅動開發(四) —— SD 控制器之真正的硬件操作

Linux SD卡驅動開發(四) —— SD 控制器之真正的硬件操作

日期:2017/3/3 11:47:16   编辑:Linux技術
前面對SD卡控制器有了一個基本的介紹。其實SD控制器層更過的意義是為core層提供一種操作SD卡硬件的一種方法,當然不同的控制器對硬件控制的方法不盡相同,但是他們最終都能像core層提交一個統一的封裝有操作方法的數據結構,那便是即將閃亮登場的struct
mmc_host_ops
....對應的host文件為s3cmci.c。
接下來就來揭開與之對應的struct mmc_host_ops結構的神秘面紗....
[cpp] view
plain copy





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寫保護引腳的狀態就能判定卡寫保護的情況。實現的代碼如下:
[cpp] view
plain copy





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可能會相對煩鎖一些,具體的代碼如下:
[cpp] view
plain copy





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卡間通信所需要的指令和數據
[cpp] view
plain copy





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]
[cpp] view
plain copy





/**
* mmc_request_done - finish processing an MMC request
* @host: MMC host which completed request
* @mrq: MMC request which request
*
* MMC drivers should call this function when they have completed
* their processing of a request.
*/
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)) {
/*
* Request starter must handle retries - see
* mmc_wait_for_req_done().
*/
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]
[cpp] view
plain copy





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]
[cpp] view
plain copy





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);
/* Clear command, data and fifo status registers
Fifo clear only necessary on 2440, but doesn't hurt on 2410
*/
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;
}
}
/* Send command */
s3cmci_send_command(host, cmd);
/* Enable Interrupt */
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]
[cpp] view
plain copy





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是個枚舉類型的變量包含了整個命令過程的各個階段,內容如下:
[cpp] view
plain copy





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]
[cpp] view
plain copy





/*
* ISR for SDI Interface IRQ
* Communication between driver and ISR works as follows:
* host->mrq points to current request
* host->complete_what Indicates when the request is considered done
* COMPLETION_CMDSENT when the command was sent
* COMPLETION_RSPFIN when a response was received
* COMPLETION_XFERFINISH when the data transfer is finished
* COMPLETION_XFERFINISH_RSPFIN both of the above.
* host->complete_request is the completion-object the driver waits for
*
* 1) Driver sets up host->mrq and host->complete_what
* 2) Driver prepares the transfer
* 3) Driver enables interrupts
* 4) Driver starts transfer
* 5) Driver waits for host->complete_rquest
* 6) ISR checks for request status (errors and success)
* 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
* 7) ISR completes host->complete_request
* 8) ISR disables interrupts
* 9) Driver wakes up and takes care of the request
*
* Note: "->error"-fields are expected to be set to 0 before the request
* was issued by mmc.c - therefore they are only set, when an error
* contition comes up
*/
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 {
/* note, we used to fail the transfer
* here, but it seems that this is just
* the hardware getting it wrong.
*
* cmd->error = -EILSEQ;
* host->status = "error: bad command crc";
* goto fail_transfer;
*/
}
}
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;
}
/* errors handled after this point are only relevant
when a data transfer is in progress */
if (!cmd->data)
goto clear_status_bits;
/* Check for FIFO failure */
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,返回。
[cpp] view
plain copy





static inline void clear_imask(struct s3cmci_host *host)
{
u32 mask = readl(host->base + host->sdiimsk);
/* preserve the SDIO IRQ mask state */
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
[cpp] view
plain copy





static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
{
u32 dcon, imsk, stoptries = 3;
/* write DCON register */
if (!data) {
writel(0, host->base + S3C2410_SDIDCON);
return 0;
}
if ((data->blksz & 3) != 0) {
/* We cannot deal with unaligned blocks with more than
* one block being transferred. */
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);
/* write BSIZE register */
writel(data->blksz, host->base + S3C2410_SDIBSIZE);
/* add to IMASK register */
imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
enable_imask(host, imsk);
/* write TIMER register */
if (host->is2440) {
writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
} else {
writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
/* FIX: set slow clock to prevent timeouts on read */
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控制器。
[cpp] view
plain copy





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方式的數據傳輸。
Copyright © Linux教程網 All Rights Reserved