已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
SD/MMC/SDIO驱动分析来 源:/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. 引言: 前几天把mini2440 的sd 卡驱动程序移植到了Android 平台,当时对SD 卡以及内核的MMC 子系统不是很了解,浏览了四天的代码,终于理清了一些头绪,尽管很多细节的实现还不是很清楚,不过先把知道的记录下来,细节部分由时间在慢慢挖掘。本文先介绍了一下MMC 的基本框架结构,然后采用自底向上的方法来分析整个MMC 层是如何共同作用的。阅读时请结合参考资料1 和2. 1. 硬件基础: /yelov/198217/message.aspx SD/MMC/SDIO 概念区分概要 SD (Secure Digital )与 MMC (Multimedia Card ) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代。在维基百科上有相当详细的 SD/MMC 规格说明: /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 dont 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-sghost-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 sgi), sg_dma_len(&data-sgi); res = s3c2410_dma_enqueue(host-dma, (void *) host, sg_dma_address(&data-sgi), sg_dma_len(&data-sgi); 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-sgi), 返回的是总线( DMA )地址 sg_dma_len(&data-sgi); 返回的是缓冲区的长度。 最后调用 s3c2410_dma_enqueue(host-dma, (void *) host, sg_dma_address(&data-sgi), sg_dma_len(&data-sgi); 对每个 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_req
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年县直事业单位招聘公共基础知识真题200道附完整答案(典优)
- 成都树德中学怡心湖学校公开招聘历年真题汇编附答案解析
- 2026年设备监理师之设备监理合同考试题库附答案【轻巧夺冠】
- 2025贵州高速公路集团有限公司第二批次招聘6人(第二轮)参考题库附答案解析
- 2025福建厦门市集美区杏苑小学非在编教师招聘1人备考题库带答案解析
- 2025四川天府银行社会招聘(遂宁)笔试模拟试卷带答案解析
- 青川县总工会关于公开招聘工会社会工作者(2人)笔试模拟试卷附答案解析
- 2025年福建莆田市仙游县公安局警务辅助人员招聘50人历年真题汇编附答案解析
- 2025四川南充市房地产管理局遴选参照管理人员2人历年真题汇编带答案解析
- 2025福建宁德市部分市直医院招聘编外人员3人(二)备考公基题库附答案解析
- 公路工程标准施工招标文件(2018年版)解析
- 旅游纪念品培训资料课件
- 事故车出险定损单模板
- 高三化学一轮复习+专题大π键课件
- YY/T 0951-2015干扰电治疗设备
- GB/T 17215.311-2008交流电测量设备特殊要求第11部分:机电式有功电能表(0.5、1和2级)
- 物业管理服务有限公司战略规划(2022-2026)
- 社会主义发展史课件
- 2023年福建高职单招语文真题试卷-(含参考答案)
- 船舶保安职责-保安意识考试题库(含答案)
- 红山窑中学残疾儿童送教上门教案
评论
0/150
提交评论