linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx

上传人:b****3 文档编号:3497522 上传时间:2022-11-23 格式:DOCX 页数:14 大小:26.43KB
下载 相关 举报
linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx_第1页
第1页 / 共14页
linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx_第2页
第2页 / 共14页
linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx_第3页
第3页 / 共14页
linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx_第4页
第4页 / 共14页
linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx

《linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx》由会员分享,可在线阅读,更多相关《linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx(14页珍藏版)》请在冰豆网上搜索。

linux sd卡驱动分析基于mini2440sdio mmc sd卡驱动编写.docx

linuxsd卡驱动分析基于mini2440sdiommcsd卡驱动编写

1.SDMemoryCardSpecifications/Part1.PhysicalLayerSpecification;Version1.0

2.LDD3CHAPTER-16BLOCKDEVICE

3.http:

//www.sdcard.org

引言:

前几天把mini2440的sd卡驱动程序移植到了Android平台,当时对SD卡以及内核的MMC子系统不是很了解,浏览了四天的代码,终于理清了一些头绪,尽管很多细节的实现还不是很清楚,不过先把知道的记录下来,细节部分由时间在慢慢挖掘。

本文先介绍了一下MMC的基本框架结构,然后采用自底向上的方法来分析整个MMC层是如何共同作用的。

阅读时请结合参考资料1和2.

1.硬件基础:

SD/MMC/SDIO概念区分概要

SD(SecureDigital)与MMC(MultimediaCard)

SD是一种flashmemorycard的标准,也就是一般常见的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协会会员也推出很完整的SDIOstack驱动程序,使得SDIO外围(我们称为SDIO卡)的开发与应用变得相当热门。

现在已经有非常多的手机或是手持装置都支持SDIO的功能(SD标准原本就是针对mobiledevice而制定),而且许多SDIO外围也都被开发出来,让手机外接外围更加容易,并且开发上更有弹性(不需要内建外围)。

目前常见的SDIO外围(SDIO卡)有:

·                                Wi-Ficard(无线网络卡)

·                                CMOSsensorcard(照相模块)

·                                GPScard

·                                GSM/GPRSmodemcard

·                                Bluetoothcard

·                                Radio/TVcard(很好玩)

SDIO的应用将是未来嵌入式系统最重要的接口技术之一,并且也会取代目前GPIO式的SPI接口。

SD/SDIO的传输模式

SD传输模式有以下3种:

·                                SPImode(required)

·                                1-bitmode

·                                4-bitmode

SDIO同样也支持以上3种传输模式。

依据SD标准,所有的SD(记忆卡)与SDIO(外围)都必须支持SPImode,因此SPImode是「required」。

此外,早期的MMC卡(使用SPI传输)也能接到SD插糟(SDslot),并且使用SPImode或1-bitmode来读取。

SD的MMCMode

SD也能读取MMC内存,虽然MMC标准上提到,MMC内存不见得要支持SPImode(但是一定要支持1-bitmode),但是市面上能看到的MMC卡其实都有支持SPImode。

因此,我们可以把SD设定成SPImode的传输方式来读取MMC记忆卡。

SD的MMCMode就是用来读取MMC卡的一种传输模式。

不过,SD的MMCMode虽然也是使用SPImode,但其物理特性仍是有差异的:

·                                MMC的SPImode最大传输速率为20Mbit/s;

·                                SD的SPImode最大传输速率为25Mbit/s。

为避免混淆,有时也用SPI/MMCmode与SPI/SDmode的写法来做清楚区别。

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(structs3cmci_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(structs3cmci_host),&pdev->dev);

mmc->ops=&s3cmci_ops;

……………

s3cmci_ops中包含了四个函数:

staticstructmmc_host_opss3cmci_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(structmmc_host*mmc,structmmc_ios*ios)

依据核心层传递过来的ios,来设置硬件IO,包括引脚配置,使能时钟,和配置总线带宽。

s3cmci_request:

这个函数是最主要,也最复杂的函数,实现了命令和数据的发送和接收,

当CORE部分需要发送命令或者传输数据时,都会调用这个函数,并传递mrq请求。

staticvoids3cmci_request(structmmc_host*mmc,structmmc_request*mrq)

{

      structs3cmci_host*host=mmc_priv(mmc);

      host->status="mmcrequest";

      host->cmd_is_stop=0;

      host->mrq=mrq;

      if(s3cmci_card_present(mmc)==0){

             dbg(host,dbg_err,"%s:

nomediumpresent\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)

/     becausehost->dodma          =0,sowedon'tuseit

                    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;

/*withstart2bits*/

        if(cmd->flags&MMC_RSP_PRESENT)

                  ccon|=S3C2410_SDICMDCON_WAITRSP;

/*waitrsp*/

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

             /*DeterminesSDIgenerateaninterruptifTxFIFOfillshalf*/

             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_readFIFO的数据读出。

接下来分析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,

                  /*Ifwehavereachedtheendoftheblock,wehaveto

                    *writeexactlytheremainingnumberofbytes.Ifwe

                    *inthemiddleoftheblock,wehavetowritefull

                    *words,sorounddowntoanevenmultipleof4.*/

                  if(fifo>=host->pio_bytes)//fifo的空间比pio_bytes大,表明这是读这个块的最后一次

                           fifo=host->pio_bytes;

                  /*becausethevolumeofFIFOcancontaintheremaningblock*/

                  else

                           fifo-=fifo&3;/*rounddowntoanevenmultipleof4*/

                  host->pio_bytes-=fifo;//更新还剩余的没有写完的字

                  host->pio_count+=fifo;/*changthevalueofpio_bytes*/

                  fifo=(fifo+3)>>2;//将字节数转化为字数

                  /*howmanywordsfifocontain,everytimewejustwritoneword*/

                  ptr=host->pio_ptr;

                  while(fifo--)

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

                  host->pio_ptr=ptr;

        }

注释一:

注意,MMC核心为mrq->data成员分配了一个structscatterlist的表,用来支持分散聚集,使用这种方法,这样使物理上不一致的内存页,被组装成一个连续的数组,避免了分配大的缓冲区的问题

我们看代码

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

                  dbg(host,dbg_debug,"nomorebuffers(%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引起的吗

接下来我们分析一下中断服务程序:

staticirqreturn_ts3cmci_irq(intirq,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)){

/*ThisbitindicatesthatFIFOdataisavailablefortransmitwhen

DatModeisdatatransmitmode.IfDMAmodeisenable,sd

hostrequestsDMAoperation.*/

                           disable_imask(host,S3C2410_SDIIMSK_TXFIFOHALF);

                           tasklet_schedule(&host->pio_tasklet);

注意我们采用tasklet这种延时机制来减少中断服务的时间,延时函数pio_tasklet中调用了do_pio_write和了do_pio_read

                           host->status="piotx";

                  }

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

                

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1