參考資料:

 

1.SD Memory Card Specifications / Part 1. Physical Layer Specification; Version 1.0

 

2.LDD3 CHAPTER-16 BLOCK DEVICE

 

3. http://www.sdcard.org

 

引言: 前幾天把mini2440 sd 卡驅動程式移植到了Android 平臺,當時對SD 卡以及內核的MMC 子系統不是很瞭解,流覽了四天的代碼,終於理清了一些頭緒,儘管很多細節的實現還不是很清楚,不過先把知道的記錄下來,細節部分由時間在慢慢挖掘。本文先介紹了一下MMC 的基本框架結構,然後採用自底向上的方法來分析整個MMC 層是如何共同作用的。閱讀時請結合參考資料1 2.

 

1. 硬體基礎:

 

http://blog.ednchina.com/yelov/198217/message.aspx

 

SD/MMC/SDIO 概念區分概要

 

SD Secure Digital )與 MMC Multimedia Card

 

SD 是一種 flash memory card 的標準,也就是一般常見的 SD 記憶卡,而 MMC 則是較早的一種記憶卡標準,目前已經被 SD 標準所取代。在維琪百科上有相當詳細的 SD/MMC 規格說明: [ http://zh.wikipedia.org/wiki/Secure_Digital ]

 

SDIO 是目前我們比較關心的技術,SDIO 故名思義,就是 SD I/O 介面(interface )的意思,不過這樣解釋可能還有點抽像。更具體的說明,SD 本來是記憶卡的標準,但是現在也可以把 SD 拿來插上一些週邊介面使用,這樣的技術便是 SDIO

 

所以 SDIO 本身是一種相當單純的技術,透過 SD I/O 接腳來連接外部週邊,並且透過 SD 上的 I/O 資料接位元與這些週邊傳輸資料,而且 SD 協會會員也推出很完整的 SDIO stack 驅動程式,使得 SDIO 週邊(我們稱為 SDIO 卡)的開發與應用變得相當熱門。

 

現在已經有非常多的手機或是手持裝置都支援 SDIO 的功能(SD 標準原本就是針對 mobile device 而制定),而且許多 SDIO 週邊也都被開發出來,讓手機外接週邊更加容易,並且開發上更有彈性(不需要內建週邊)。目前常見的 SDIO 週邊(SDIO 卡)有:

 

·                                 Wi-Fi card (無線網路卡)

 

·                                 CMOS sensor card (照相模組)

 

·                                 GPS card

 

·                                 GSM/GPRS modem card

 

·                                 Bluetooth card

 

·                                 Radio/TV card (很好玩)

 

SDIO 的應用將是未來嵌入式系統最重要的介面技術之一,並且也會取代目前 GPIO 式的 SPI 介面。

 

SD/SDIO 的傳輸模式

 

SD 傳輸模式有以下 3 種:

 

·              SPI mode (required )

 

·                                 1-bit mode

 

·              4-bit mode

 

SDIO 同樣也支援以上 3 種傳輸模式。依據 SD 標準,所有的 SD (記憶卡)與 SDIO (週邊)都必須支援 SPI mode ,因此 SPI mode 是「required 」。此外,早期的 MMC 卡(使用 SPI 傳輸)也能接到 SD 插糟(SD slot ),並且使用 SPI mode 1-bit mode 來讀取。

 

SD MMC Mode

 

SD 也能讀取 MMC 記憶體,雖然 MMC 標準上提到,MMC 記憶體不見得要支援 SPI mode (但是一定要支持 1-bit mode ),但是市面上能看到的 MMC 卡其實都有支持 SPI mode 。因此,我們可以把 SD 設定成 SPI mode 的傳輸方式來讀取 MMC 記憶卡。

 

SD MMC Mode 就是用來讀取 MMC 卡的一種傳輸模式。不過,SD MMC Mode 雖然也是使用 SPI mode ,但其物理特性仍是有差異的:

 

·                                 MMC SPI mode 最大傳輸速率為 20 Mbit/s

 

·                                 SD SPI mode 最大傳輸速率為 25 Mbit/s

 

為避免混淆,有時也用 SPI/MMC mode SPI/SD mode 的寫法來做清楚區別。

 

2.MMC 子系統的基本框架結構:

 

很遺憾,內核沒有為我們提供關於MMC 子系統的文檔,在穀歌上搜索了很多,也沒有找到相關文章。只能自己看代碼分析了,可能有很多理解不對的地方,希望研究過這方面的朋友多郵件交流一下。

 

MMC 子系統的代碼在kernel/driver/MMC 下,目前的MMC 子系統支持一些形式的記憶卡:SD,SDIO,MMC. 由於筆者對SDIO 的規範不是很清楚,後面的分析中不會涉及。MMC 子系統範圍三個部分:

 

HOST 部分是針對不同主機的驅動程式,這一部是驅動程式工程師需要根據自己的特點平臺來完成的。

 

CORE 部分: 這是整個MMC 的核心存,這部分完成了不同協定和規範的實現,並為HOST 層的驅動提供了介面函數。

 

CARD 部分:因為這些記憶卡都是塊設備,當然需要提供塊設備的驅動程式,這部分就是實現了將你的SD 卡如何實現為塊設備的。

 

3.HOST 層分析:

 

HOST 層實現的就是我們針對特定主機的驅動程式,這裏以mini2440 s3cmci.c 為例子進行分析,我們先採用platform_driver_register(&s3cmci_2440_driver) 註冊了一個平臺設備,接下來重點關注probe 函數。在這個函數總,我們與CORE 的聯繫是通過下面三句實現的。首先分配一個mmc_host 結構體,注意sizeof(struct s3cmci_host) ,這樣就能在mmc_host 中找到了s3cmci_host ,嵌入結構和被嵌入的結構體能夠找到對方在Linux 內核代碼中的常用技術了。接下來為mmc->pos 賦值, s3cmci_ops 結構實現了幾個很重要的函數,待會我一一介紹。中間還對mmc 結構的很多成員進行了賦值,最後將mmc 結構加入到MMC 子系統,mmc_alloc_host ,以及mmc_add_host 的具體做了什麼事情,我們在下節再分析,這三句是些MMC 層驅動必須包含的。

 

mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);

 

mmc->ops = &s3cmci_ops;

 

……………

 

s3cmci_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,

 

};

 

我們從簡單的開始分析 , 這些函數都會在 core 部分被調用:

 

s3cmci_get_ro: 這個函數通過從 GPIO 讀取,來判斷我們的卡是否是防寫的

 

s3cmci_card_present 這個函數通過從 GPIO 讀取來判斷卡是否存在

 

s3cmci_set_ios s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

 

依據核心層傳遞過來的 ios ,來設置硬體 IO, 包括引腳配置,使能時鐘,和配置匯流排帶寬。

 

s3cmci_request 這個 函數是最主要,也最複雜的函數,實現了命令和資料的發送和接收,

 

CORE 部分需要發送命令或者傳輸資料時,都會調用這個函數,並傳遞 mrq 請求。

 

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);

 

}

 

接下來看 s3cmci_send_request(mmc)

 

這個函數先判斷一下請求時傳輸資料還是命令 如果是資料的話

 

先調用 s3cmci_setup_data 來對 S3C2410_SDIDCON 寄存器進行設置然後設置 SDITIMER 寄存器這就設置好了匯流排寬度是否使用 DMA, 並啟動了資料傳輸模式並且使能了下面這些中斷

 

imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |

 

              S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;

 

解析來判斷是否是採用 DMA 進行資料傳輸還是採用 FIFO 進行資料傳輸

 

if (host->dodma)

 

/      because host->dodma           = 0,so we don't use it

 

                     res = s3cmci_prepare_dma(host, cmd->data);// 準備 DMA 傳輸

 

              else

 

                     res = s3cmci_prepare_pio(host, cmd->data);.// 準備 FIFO 傳輸

 

如果是命令的話 則調用 s3cmci_send_command ()這個函數是命令發送的函數 datesheet 上描述的過程差不多 , 關於 SD 規範中命令的格式請參考參考資料 1.

 

writel(cmd->arg, host->base + S3C2410_SDICMDARG);/* 先寫參數寄存器

 

         ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;// 確定命令種類

 

         ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;

 

/*with start 2bits*/

 

         if (cmd->flags & MMC_RSP_PRESENT)

 

                   ccon |= S3C2410_SDICMDCON_WAITRSP;

 

/*wait rsp*/

 

         if (cmd->flags & MMC_RSP_136)

 

                   ccon |= S3C2410_SDICMDCON_LONGRSP;

 

// 確定 respose 的種類

 

         writel(ccon, host->base + S3C2410_SDICMDCON);

 

 

命令通道分析完了我們分析資料通道先分析採用 FIFO 方式傳輸是怎麼樣實現的。

 

先分析 s3cmci_prepare_pio(host, cmd->data)

 

根據 rw 來判斷是讀還是寫

 

if (rw) {

 

              do_pio_write(host);

 

              /* Determines SDI generate an interrupt if Tx FIFO fills half*/

 

              enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);

 

       } else {

 

              enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF

 

                          | S3C2410_SDIIMSK_RXFIFOLAST);

 

       }

 

如果是寫資料到 SD 的話會調用 do_pio_write, FIFO 中填充數據。當 64 位元組的 FIFO 少於 33 位元組時就會產生中斷。如果是從 SD 讀數據,則先使能中斷,當 FIFO 多於 31 位元組時時,則會調用中斷服務程式,中斷服務程式中將會調用 do_pio_read FIFO 的資料讀出。

 

接下來分析 do_pio_write

 

to_ptr = host->base + host->sdidata;

 

fifo_free(host) 用來檢測 fifo 剩餘空間

 

while ((fifo = fifo_free(host)) > 3) {

 

                   if (!host->pio_bytes) {

 

                            res = get_data_buffer(host, &host->pio_bytes,

 

                   /* If we have reached the end of the block, we have to

 

                     * write exactly the remaining number of bytes. If we

 

                     * in the middle of the block, we have to write full

 

                     * words, so round down to an even multiple of 4. */

 

                   if (fifo >= host->pio_bytes)//fifo 的空間比 pio_bytes 表明這是讀這個塊的最後一次

 

                            fifo = host->pio_bytes;

 

                   /* because the volume of FIFO can contain the remaning block*/

 

                   else

 

                            fifo -= fifo & 3;/*round down to an even multiple of 4*/

 

 

                   host->pio_bytes -= fifo;// 更新還剩餘的沒有寫完的字

 

                   host->pio_count += fifo;/*chang the value of pio_bytes*/

 

 

                   fifo = (fifo + 3) >> 2;// 將位元組數轉化為字數

 

                   /*how many words fifo contain,every time we just writ one word*/

 

                   ptr = host->pio_ptr;

 

                   while (fifo--)

 

                            writel(*ptr++, to_ptr);// 寫往 FIFO.

 

                   host->pio_ptr = ptr;

 

         }

 

注釋一注意 MMC 核心為 mrq->data 成員分配了一個 struct scatterlist 的表用來支持分散聚集使用這種方法這樣使物理上不一致的記憶體頁被組裝成一個連續的陣列避免了分配大的緩衝區的問題

 

我們看代碼

 

         if (host->pio_sgptr >= host->mrq->data->sg_len) {

 

                   dbg(host, dbg_debug, "no more buffers (%i/%i)\n",

 

                         host->pio_sgptr, host->mrq->data->sg_len);

 

                   return -EBUSY;

 

         }

 

         sg = &host->mrq->data->sg[host->pio_sgptr];

 

         *bytes = sg->length;// 頁緩衝區中的長度

 

         * pointer = sg_virt(sg); 將頁位址映射為虛擬位址

 

         host->pio_sgptr++; 這裏表明我們的程式又完成了一次映射

 

這樣,每一個 mmc 請求,我們只能處理 scatterlist 表中的一個頁(塊)。因此,完成一次完整的請求需要映射 sg_len

 

再來總結一下一個 mmc 寫設備請求的過程:

 

s3cmci_prepare_pio 中我們第一次先調用 do_pio_write ,如果 FIFO 空間大於 3 ,且能夠獲取到 scatterlist ,則我們就開始往 FIFO 寫資料,當 FIFO 空間小於 3 ,則使能 TXFIFOHALF 中斷,在中斷服務程式中,如果檢測到 TFDET 表明又有 FIFO 空間了,則關閉 TXFIFOHALF 中斷,並調用 do_pio_write 進行寫。

 

資料流程向如下: scatterlist-------->fifo---------->sdcard

 

一個 mmc 讀設備請求的過程 資料流程向如下 sdcard --------> fifo ---------->scatterlist

 

????關於讀數據的過程,中斷的觸發不是很清楚, s3cmci_prepare_pio enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF S3C2410_SDIIMSK_RXFIFOLAST); 但如果沒從 SD 卡中讀數據,怎麼會引發這個中斷呢?是由 S3C2410_SDIIMSK_RXFIFOLAST 引起的嗎

 

接下來我們分析一下中斷服務程式:

 

static irqreturn_t s3cmci_irq(int irq, void *dev_id)

 

該程式先獲取所有的狀態寄存器:

 

mci_csta = readl(host->base + S3C2410_SDICMDSTAT);

 

         mci_dsta = readl(host->base + S3C2410_SDIDSTA);

 

         mci_dcnt = readl(host->base + S3C2410_SDIDCNT);

 

         mci_fsta = readl(host->base + S3C2410_SDIFSTA);

 

         mci_imsk = readl(host->base + host->sdiimsk);

 

這些將作為中斷處理的依據。

 

如果不是 DMA 模式,則處理資料的收發

 

if (!host->dodma) {

 

                   if ((host->pio_active == XFER_WRITE) &&

 

                       (mci_fsta & S3C2410_SDIFSTA_TFDET)) {

 

/*This bit indicates that FIFO data is available for transmit when

 

DatMode is data transmit mode. If DMA mode is enable, sd

 

host requests DMA operation.*/

 

                            disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);

 

                            tasklet_schedule(&host->pio_tasklet);

 

注意我們採用 tasklet 這種延時機制來減少中斷服務的時間,延時函數 pio_tasklet 中調用了 do_pio_write do_pio_read

 

                            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";

 

                   }

 

         接下來的很多代碼是對其他的一些類型中斷的處理。

 

最後來分析 DMA 模式:這種模式下不需要 CPU 的干預。 S3C2440 DMA 4 個通道,我們選擇了通道 0

 

static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)

 

{

 

         int dma_len, i;

 

         int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;

 

 

         BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);

 

 

         s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW); // 注一

 

         s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

 

 

         dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,

 

                                 (rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); // 注二

 

 

         if (dma_len == 0)

 

                   return -ENOMEM;

 

 

         host->dma_complete = 0;

 

         host->dmatogo = dma_len;

 

 

         for (i = 0; i < dma_len; i++) {

 

                   int res;

 

 

                   dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,

 

                            sg_dma_address(&data->sg[i]),

 

                            sg_dma_len(&data->sg[i]));

 

 

                   res = s3c2410_dma_enqueue(host->dma, (void *) host,

 

                                                 sg_dma_address(&data->sg[i]),

 

                                                 sg_dma_len(&data->sg[i]));

 

 

                   if (res) {

 

                            s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

 

                            return -EBUSY;

 

                   }

 

         }

 

 

         s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);

 

 

         return 0;

 

}

 

注一 : 這個函數先調用 s3c2410_dma_devconfig 來配置 DMA / 目的的意見類型和位址,注意我們這裏的設備位址 host->mem->start + host->sdidata 實際上就是 SDIDATA 寄存器的位址值,如果是寫 SD 卡,則為目的地址,否則為源地址。然後調用 s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback);

 

設置 dma 通道 0 的回調函數。

 

注二:

 

dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,

 

                                 (rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

 

這裏進行分散 / 聚集映射( P444,LDD3 , 返回值是傳送的 DMA 緩衝區數,可能會小於 sg_len ,也就是說 sg_len dma_len 可能是不同的。

 

sg_dma_address(&data->sg[i]), 返回的是匯流排( DMA )位址

 

sg_dma_len(&data->sg[i])); 返回的是緩衝區的長度。

 

最後調用 s3c2410_dma_enqueue(host->dma, (void *) host,

 

                                                 sg_dma_address(&data->sg[i]),

 

                                                 sg_dma_len(&data->sg[i]));

 

對每個 DMA 緩衝區進行排隊,等待處理。

 

s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START); 啟動 DMA

 

這樣 DMA 緩衝區就和 scatterlist 聯繫起來,當寫資料時, scatterlist 中的資料由於上面的映射關係會直接拷貝 DMA 緩衝區,當讀數據時則反之。整個過程不需要 CPU 干預,自動完成。

 

以上就是針對 mini2440 HOST 部分的內容。

 

 

 

4 CORE 層分析:

 

CORE 層完成了不同協定和規範的實現,並為 HOST 層的驅動提供了介面函數,在 HOST 層我們曾經調用的兩個函數:

 

mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);

 

mmc_add_host(mmc);

 

我們就從這兩個函數入手,來分析 CORE 層與 HOST 層是如何交互的。

 

先看 mmc_alloc_host 函數:

 

    dev_set_name(&host->class_dev, "mmc%d", host->index);

 

         host->parent = dev;

 

         host->class_dev.parent = dev;

 

         host->class_dev.class = &mmc_host_class;

 

         device_initialize(&host->class_dev);

 

這幾句是將導致在 /SYS/CLASS/mmc_host 下出現 mmc0 目錄,添加類設備,在 2.6.21 後的版本中,類設備的 class_device 已近被 device 所取代, LDD3P387 的內容有點 OUT

 

       INIT_DELAYED_WORK(&host->detect, mmc_rescan);

 

初始化了一個工作隊列,延時函數為 mmc_rescan ,這個延時函數很重要,下午要詳細分析

 

最後對 host 做一些默認配置,不過這些配置在 probe 函數的後面都被重置了。

 

分析 mmc_add_host(mmc);

 

device_add(&host->class_dev); 這裏才真正的添加了類設備。

 

其中調用了 mmc_start_host

 

void mmc_start_host(struct mmc_host *host)

 

{

 

       mmc_power_off(host);

 

       mmc_detect_change(host, 0);

 

}

 

mmc_power_off 中對 ios 進行了設置,然後調用 mmc_set_ios(host);

 

host->ios.power_mode = MMC_POWER_OFF;

 

       host->ios.bus_width = MMC_BUS_WIDTH_1;

 

       host->ios.timing = MMC_TIMING_LEGACY;

 

       mmc_set_ios(host);

 

mmc_set_ios(host) 中的關鍵語句 host->ops->set_ios(host, ios); 這裏的 set_ios 實際上就是我們前面所提到的 .set_ios = s3cmci_set_ios,

 

再看 mmc_detect_change(host, 0); 最後一句是

 

       mmc_schedule_delayed_work(&host->detect, delay);

 

實際上就是調用我們前面說的延時函數 mmc_rescan

 

mmc_power_up(host);// 這個函數實際上與前面的 mmc_power_off 類似,不過設置了啟動時需要的 ios

 

                   mmc_go_idle(host);

 

                   //CMD0 from inactive to idle

 

                   mmc_send_if_cond(host, host->ocr_avail);// 發送 SD_SEND_IF_COND ,是使用 SD2.0 卡才需要設置的命令

 

/*suppot for 2.0 card*/

 

                     * ...then normal SD...

 

                     */

 

                   err = mmc_send_app_op_cond(host, 0, &ocr);

 

                   if (!err) {

 

                            if (mmc_attach_sd(host, ocr))

 

                                     mmc_power_off(host);

 

                            goto out;

 

                   }

 

藍色部分是遵照 SD 卡協定的 SD 卡啟動過程,包括了非啟動模式、卡識別模式和資料傳輸模式三種模式共九種狀態的轉換,你需要參照相關規範來理解。可以先參考下面三章圖對模式和狀態,以及狀態轉換有個初步瞭解。

 

       

 

我們最初的 SD 卡的狀態時 inactive 狀態調用 mmc_go_idle(host) 後,發送命令 CMD0 是其處於 IDLE 狀態。

 

我們詳細分析一下 mmc_go_idle

 

memset(&cmd, 0, sizeof(struct mmc_command));

 

         cmd.opcode = MMC_GO_IDLE_STATE; MMC_GO_IDLE_STATE 就是命令 CMD0

 

         cmd.arg = 0; 此命令無參數

 

         cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;

 

         err = mmc_wait_for_cmd(host, &cmd, 0);// 見注 1

 

         mmc_delay(1);

 

 

 

1 mmc_wait_for_cmd(host, &cmd, 0) 是用來發送命令的,我們揭開它的神秘面紗吧。

 

memset(&mrq, 0, sizeof(struct mmc_request));

 

         memset(cmd->resp, 0, sizeof(cmd->resp));

 

         cmd->retries = retries;

 

         mrq.cmd = cmd; 將命令嵌入到一個 mmc 請求中

 

         cmd->data = NULL;mmc 命令的 data 部分設置為 NULL, 這樣表示我們要傳輸的是命令而不是資料

 

         mmc_wait_for_req(host, &mrq);// 關鍵部分

 

在該函數中調用了mmc_start_request ,而這個函數調用了host->ops->request(host, mrq) ,這個request 函數就是我們在前面分析的s3cmci_request ,這樣MMC 核心第二次核HOST 層握手了

 

 

我們再看看:        err = mmc_send_app_op_cond(host, 0, &ocr);// 注一

 

                   if (!err) {

 

                            if (mmc_attach_sd(host, ocr))// 注二

 

                                     mmc_power_off(host);

 

                            goto out;

 

注一:實際上是要發送 ACMD41 命令,這條命令可以用來獲取 SDcard 的允許電壓範圍值,由於這是一條應用命令,所有發送它之前需要發送 CMD_55 命令。執行完後 card 狀態變為 READY 獲取的電壓範圍保存在 ocr 中,再調用 mmc_attach_sd(host, ocr) 看這個電壓範圍是否滿足主機的要求,不滿足,則 power_off 主機。

 

注二: mmc_attach_sd 完成匹配,和初始化卡的功能

 

host->ocr = mmc_select_voltage(host, ocr); 看是否匹配,如果匹配則做下面初始化工作

 

mmc_sd_init_card(host, host->ocr, NULL); 我們分析該函數

 

1 mmc_all_send_cid ()這個函數發生 CMD2 ,獲取卡的身份資訊,進入到身份狀態

 

(2)card = mmc_alloc_card(host, &sd_type); 分配一張 SD 類型的 card 結構

 

(3) 接著調用 mmc_send_relative_add, 獲取卡的相對位址,注意一前卡和主機通信都採用默認位址,現在有了自己的位址了,進入到 stand_by 狀態

 

4 )通過發送 SEND_CSD (CMD9) 獲取 CSD 寄存器的資訊,包括 block 長度,卡容量等資訊

 

(5) mmc_select_card(card) 發送 CMD7, 選中目前 RADD 位址上的卡,任何時候匯流排上只有一張卡被選中,進入了傳輸狀態

 

6 )調用 mmc_app_send_scr 發送命令 ACMD51 獲取 SRC 寄存器的內容?A

 

 

 

 原文地址 http://hi.baidu.com/jggdqq/blog/item/f2129d2469e5e620d5074269.html 

文章標籤
全站熱搜
創作者介紹
創作者 horace papa 的頭像
horace papa

Horace papa's life

horace papa 發表在 痞客邦 留言(0) 人氣(2,484)