视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
SD MMC SDIO 驱动分析
2025-09-29 03:03:08 责编:小OO
文档
SD/MMC/SDIO    驱动分析

[来     源]:http://blog168.chinaunix.net/space.php?uid=25678596&do=blog&id=2102933

参考资料: 

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 中填充数据。当  字节的 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 寄存器的内容,进入到 SENDING-DATA 状态 

在函数中还将获得的各个卡寄存器的内容解码,并保存到 cmd 结构的相应成员中。 

( 7 ) if (host->ops->get_ro(host) > 0 ) 

                                     mmc_card_set_readonly(card); 

通过调用 get_ro(host) 函数,实际上就是 s3cmci_get_ro 函数了。 我们判断是否写保护,如果是的,将 card 状态设置为只读状态 

最后再 mmc_attach_sd 里,我们将 card 结构添加进去 

mmc_add_card(host->card); 

dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca); 这里我们以 host 名 +rca 地址来命名卡我们可以看到在 /sys/devices/platform/s3c2440-sdi/mmc_host:mmc0/ 下出现 mmc0 : 0002 的目录,这个 0002 就是 rca 地址 

到这里我们分析完了 MMC 的核心层。 下载本文

显示全文
专题