我的Kernel学习之路2-MMC

发布时间 2023-10-25 11:04:41作者: King_Alex

前言

最近碰到一个问题,希望将主控的MMC的架构从HS模式提高到SDR50的模式,但实际速度就是达不到。那么一般可能是什么原因呢?

MMC子系统的概述

块设备是Linux系统的基础外设之一,而MMC/SD存储设备是一种典型的块设备。Linux内核设计了MMC的子系统用于管理MMC/SD设备。

对于MMC设备来讲主要分为:

  • mmc控制器
  • mmc总线
  • card

而针对卡而言有SD卡、TF卡,这些都是从MMC总线规范演化而来。而基于这种通信方式,又演化出了SDIO,SDIO强调IO,已经不仅仅局限于存储卡与块设备了,还可以连接任何支持SDIO的外设,比如蓝牙设备、WiFi设备等。

一般SOC都会有MMC control。其主要包含电压、时钟、数据、命令三种类型的引脚,当MMC Control检测到数据变化的时候会进行相关的动作,如存储卡一般会有Card Detect脚,如果检测到引脚发生变化,就会进行例如mmc card的注册与注销操作。

而在MMC子系统中,主要包含MMC总线与SDIO总线两个部分,其中MMC总线主要是支持MMC卡、SD卡、TF卡等存储设备,而SDIO则主要实现针对SDIO接口连接的设备。
以下是MMC/SD硬件的连接图。

MMC/SD卡初始化

由于MMC与SD卡的自身实现有所不同,所以在初始化的时候是需要判断出所插入的是MMC卡还是SD卡,然后根据不同的卡执行相应的初始化命令:
初始化过程一般主要有以下几个步骤:

  1. 检测卡是否插入
  2. 重置控制模块及卡
  3. 卡的种类检验
  4. 卡的电压验证
  5. 获取卡的CID
  6. 分配卡的相关地址(RCA,Relatevie Card Address)
  7. 设置读写数据块的大小

MMC/SD卡重置

reset有三种,一种是硬件的reset,mmc/sd卡均被重置了,第二种是软件reset,将控制模块重置,第三种通过CMD0与CMD52来重置mmc/sd卡的状态。

卡的种类检验

判断是sd卡还是mmc卡是通过cmd55来获取的。mmc/sd卡标准定义了两种命令:
  • Application Specific Command(ACMD)
  • General Command(GEN_CMD)

在发送ACMD命令前均需要发送CMD55,以表明后面发送的这条命令是ACMD,而普通的命令则不需要,可以直接发送,通常情况下发送命令后,均会有返回值,根据返回值可以提取相关信息。

mmc卡是不支持CMD55的,所以它没有返回值,这就很好判断是mmc卡还是sd卡了。

mmc/sd的电压检验

所有的卡必须在一定电压下才能被mmc/sd模块正常使用,而卡所能支持的最高电压及最低电压都是存在卡种的Operation Conditions Register(OCR)内。当MMC/SD模块同卡的工作电压范围不匹配时,卡是无法完成识别和CSD数据的发送。所以会有CMD1 、ACMD41、 CMD5对于电压的检测。

获取卡的CID

获取卡的CID需要发送CMD2,每张卡都有自己特有的CID,这是生产商在生产时定义好的,当卡接到该命令后,处于active状态的卡会将CID立即传出,然后进入认证状态

分配卡的相对地址

RCA(Relative Card Address)是卡在本地系统的临时地址,它是由CMD3来动态分配的,虽然有CID这样的唯一标识,但RCA的位数比CID少很多,因而更加方便,类似于ip地址与mac地址的关系

设置读写数据块的大小

通常mmc与sd均属于块设备,都是按块来读的,因而需设定这块数据区的大小。对于sd卡除了设置数据区大小(一般为512字节)还要设置数据宽度。

mmc/sd卡的读写操作

mmc/sd卡都是通过数据块的形式来读写的,每次读写都是block的整数倍。命令CMD17/CMD18和CMD24/CMD25分别是卡读一个或者多个和写一个或者多个数据块的命令,而这些数据在读写时都有CRC校验,当检验失败,所传的数据也会被丢弃,读写操作也会被中止。

SDIO的协议概况

一般来讲SD模式的总线通信协议主要分为三部分,分别是命令(CMD)、响应(resp)、数据(data)。
SD总线上的通信基于以起始位开始、以停止位结束的命令和数据位流。

命令:命令是启动一项操作的令牌。命令可以从主机发送到一张卡(寻址命令)或发送到连接的所有卡(广播命令)。命令在CMD线上串行传输。
响应:响应是从被寻址的卡或(同时)从所有连接的卡发送到主机,作为对接收到的命令的回答的令牌。响应在CMD线上串行传输。
数据:数据可以从卡发送到主机或者相反。数据通过数据线传输。

注:不管块的写操作使用了多少条数据线传输数据,只使用DAT0数据线上的写操作busy信号。

host与sd卡

每一张卡都有一组寄存器:

img

总线速率选择时序:

img

当上电后, 卡为3.3V 信号模式。首先CMD0 选择总线模式: SD 模式或者SPI 模式。1.8V IO 信号模式只能在SD 模式进入。一旦进入了1.8V 信号模式,就不能进入SPI总线模式和3.3V信号模式,除非重新下电上电,如果卡接受CMD0 ,卡返回到Idle状态,但是仍然运行在SDR12 的时序中。
因为更高的速率要求低电压信号,对于SDR50,DDR50和SDR104模式, UHS-I调节成1.8V信号。卡仍然由3.3V 供电,但是SDCLK 、CMD 和Dat[3:0]从3.3V 修改为1.8V 级别。为了避免Host和卡直接电压错误匹配,信号电压级别切换在初始化的电压切换时序中完成。CMD11 发起电压切换时序。卡进入UHS-I 模式,其输入输出时序被改变。UHS-I 只能应用于SD 的4 线制模式,所以Host 应该用ACMD6 将卡设置成4 线制。当卡进入USH-I 模式,不管有无ACMD6 执行,卡都将切换到4 线制模式。
Host 可以通过CMD6 的功能组3 选择卡的合适的输出驱动强度。Host 可以通过CMD6 的功能组1 选择某一UHS-I 模式。每一个UHS-I 模式定义了兼容当前存在的卡的最大频率,采样沿(上升沿或者双沿)和最大电流消耗。Host 选择哪种UHS-I 模式,取决于其能提供的SDCLK 频率和供电能力。
在SDR50 或者SDR104 模式选择后, UHS-I Host 可以用调节命令(CMD19 )调节采样点。DDR50模式并不支持调节命令。

具体的SD卡状态机如下图:

img

下面是卡的初始化流程:

img

HOST的MMC连接

一般soc会接的SOC的接法如下:

img

目前笔者这里的接法如下图:

img

速率切换的流程

在前面已经详细的整个初始化流程里:

img

img

查询相关的cmd:

img

在3.3V的识别成HS模式下,这个是正常的可以被读写的

[ 3022.219016] SDIO0: CMD52, arg 0x00000c00 , R1
[ 3022.219354] SDIO0: SRsp 900 
[ 3022.219382] SDIO0: CMD52, arg 0x80000c08 , R1
[ 3022.219712] SDIO0: SRsp 900 
[ 3022.222010] SDIO0: CMD0, arg 0x00000000 , Rx
[ 3022.222017] SDIO0: unknown resp_type 0000
[ 3022.226016] SDIO0: CMD8, arg 0x000001aa , R1
[ 3022.226330] SDIO0: SRsp 1aa 
[ 3022.226352] SDIO0: CMD5, arg 0x00000000 , R3/R4
[ 3022.226682] SDIO0: SRsp 1aa 
[ 3022.226703] SDIO0: CMD5, arg 0x00000000 , R3/R4
[ 3022.227034] SDIO0: SRsp 1aa 
[ 3022.227070] SDIO0: CMD5, arg 0x00000000 , R3/R4
[ 3022.227408] SDIO0: SRsp 1aa 
[ 3022.227455] SDIO0: CMD5, arg 0x00000000 , R3/R4
[ 3022.227792] SDIO0: SRsp 1aa 
[ 3022.227843] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.228164] SDIO0: SRsp 400120 
[ 3022.228211] SDIO0: CMD41, arg 0x00000000 , R3/R4
[ 3022.228532] SDIO0: SRsp ff8000 
[ 3022.230019] SDIO0: CMD0, arg 0x00000000 , Rx
[ 3022.230026] SDIO0: unknown resp_type 0000
[ 3022.234010] SDIO0: CMD8, arg 0x000001aa , R1
[ 3022.234324] SDIO0: SRsp 1aa 
[ 3022.234348] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.234658] SDIO0: SRsp 120 
[ 3022.234677] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.234997] SDIO0: SRsp ff8000 
[ 3022.248009] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.248326] SDIO0: SRsp 120 
[ 3022.248347] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.248658] SDIO0: SRsp ff8000 
[ 3022.262011] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.262326] SDIO0: SRsp 120 
[ 3022.262347] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.262657] SDIO0: SRsp ff8000 
[ 3022.276007] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.276321] SDIO0: SRsp 120 
[ 3022.276341] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.276651] SDIO0: SRsp ff8000 
[ 3022.290008] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.290322] SDIO0: SRsp 120 
[ 3022.290341] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.290652] SDIO0: SRsp ff8000 
[ 3022.304006] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.304321] SDIO0: SRsp 120 
[ 3022.304342] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.304652] SDIO0: SRsp ff8000 
[ 3022.318009] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.318323] SDIO0: SRsp 120 
[ 3022.318344] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.318654] SDIO0: SRsp ff8000 
[ 3022.332006] SDIO0: CMD55, arg 0x00000000 , R1
[ 3022.332320] SDIO0: SRsp 120 
[ 3022.332341] SDIO0: CMD41, arg 0x40200000 , R3/R4
[ 3022.332651] SDIO0: SRsp c0ff8000 
[ 3022.332671] SDIO0: CMD2, arg 0x00000000 , R2
[ 3022.333207] SDIO0: LRsp ad4c5355 53443030 10bc1536 740154e1
[ 3022.333245] SDIO0: CMD3, arg 0x00000000 , R1
[ 3022.333568] SDIO0: SRsp 59b40520 
[ 3022.333611] SDIO0: CMD9, arg 0x59b40000 , R2
[ 3022.334150] SDIO0: LRsp 400e0032 db790000 ec537f80 a40002d
[ 3022.334195] SDIO0: CMD7, arg 0x59b40000 , R1
[ 3022.334517] SDIO0: SRsp 700 
[ 3022.334563] SDIO0: CMD55, arg 0x59b40000 , R1
[ 3022.334884] SDIO0: SRsp 920 
[ 3022.334938] SDIO0: CMD51, arg 0x00000000 , R1
[ 3022.335416] SDIO0: SRsp 920 
[ 3022.335504] SDIO0: CMD55, arg 0x59b40000 , R1
[ 3022.335817] SDIO0: SRsp 920 
[ 3022.335874] SDIO0: CMD13, arg 0x00000000 , R1
[ 3022.336192] SDIO0: SRsp 920 
[ 3022.337606] SDIO0: CMD6, arg 0x00fffff0 , R1
[ 3022.337921] SDIO0: SRsp 900 
[ 3022.339328] SDIO0: CMD6, arg 0x80fffff1 , R1
[ 3022.339642] SDIO0: SRsp 900 
[ 3022.341088] SDIO0: CMD55, arg 0x59b40000 , R1
[ 3022.341110] SDIO0: SRsp 920 
[ 3022.341131] SDIO0: CMD6, arg 0x00000002 , R1
[ 3022.341146] SDIO0: SRsp 920 
[ 3022.341180] mmc0: new high speed SDHC card at address 59b4
[ 3022.343447] mmcblk0: mmc0:59b4 USD00 29.5 GiB 
[ 3022.345059] SDIO0: CMD18, arg 0x00000000 , R1
[ 3022.345094] SDIO0: SRsp 900 
[ 3022.345665] SDIO0: CMD12, arg 0x00000000 , R1
[ 3022.345697] SDIO0: SRsp b00 
[ 3022.345835]  mmcblk0: p1
[ 3023.409029] SDIO0: CMD13, arg 0x59b40000 , R1
[ 3023.409056] SDIO0: SRsp 900 

分析整个流程:

CMD52触发IO接入,SD卡上电了->CMD0软重启一下->CMD8检查相关的能力集->CMD5类似于ACMD41,询问是否需要切换IO的电压->CMD55/ACMD41->CMD2->CMD3

以下是内核打印的SDIO的协议

<6>[12748.426263] SDIO0: CMD52, arg 0x00000c00 , R1
<6>[12748.426621] SDIO0: SRsp 900 
<6>[12748.426650] SDIO0: CMD52, arg 0x80000c08 , R1
<6>[12748.426991] SDIO0: SRsp 900 
<6>[12748.429262] SDIO0: CMD0, arg 0x00000000 , Rx
<6>[12748.429270] SDIO0: unknown resp_type 0000
<6>[12748.433256] SDIO0: CMD8, arg 0x000001aa , R1
<6>[12748.433579] SDIO0: SRsp 1aa 
<6>[12748.433602] SDIO0: CMD5, arg 0x00000000 , R3/R4
<6>[12748.433942] SDIO0: SRsp 1aa 
<6>[12748.433963] SDIO0: CMD5, arg 0x00000000 , R3/R4
<6>[12748.434303] SDIO0: SRsp 1aa 
<6>[12748.434326] SDIO0: CMD5, arg 0x00000000 , R3/R4
<6>[12748.434668] SDIO0: SRsp 1aa 
<6>[12748.434692] SDIO0: CMD5, arg 0x00000000 , R3/R4
<6>[12748.435031] SDIO0: SRsp 1aa 
<6>[12748.435056] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.435379] SDIO0: SRsp 400120 
<6>[12748.435403] SDIO0: CMD41, arg 0x00000000 , R3/R4
<6>[12748.435723] SDIO0: SRsp ff8000 
<6>[12748.437251] SDIO0: CMD0, arg 0x00000000 , Rx
<6>[12748.437258] SDIO0: unknown resp_type 0000
<6>[12748.441250] SDIO0: CMD8, arg 0x000001aa , R1
<6>[12748.441573] SDIO0: SRsp 1aa 
<6>[12748.441595] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.441915] SDIO0: SRsp 120 
<6>[12748.441936] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.442254] SDIO0: SRsp ff8000 
<6>[12748.455253] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.455575] SDIO0: SRsp 120 
<6>[12748.455597] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.455916] SDIO0: SRsp ff8000 
<6>[12748.469258] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.469580] SDIO0: SRsp 120 
<6>[12748.469604] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.469923] SDIO0: SRsp ff8000 
<6>[12748.483253] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.483575] SDIO0: SRsp 120 
<6>[12748.483596] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.483915] SDIO0: SRsp ff8000 
<6>[12748.497253] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.497574] SDIO0: SRsp 120 
<6>[12748.497595] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.497914] SDIO0: SRsp ff8000 
<6>[12748.511254] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.511576] SDIO0: SRsp 120 
<6>[12748.511599] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.511917] SDIO0: SRsp ff8000 
<6>[12748.525253] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.525575] SDIO0: SRsp 120 
<6>[12748.525597] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.525915] SDIO0: SRsp ff8000 
<6>[12748.539258] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.539581] SDIO0: SRsp 120 
<6>[12748.539605] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.539924] SDIO0: SRsp ff8000 
<6>[12748.553258] SDIO0: CMD55, arg 0x00000000 , R1
<6>[12748.553581] SDIO0: SRsp 120 
<6>[12748.553605] SDIO0: CMD41, arg 0x41200000 , R3/R4
<6>[12748.553923] SDIO0: SRsp c1ff8000 
<6>[12748.553947] SDIO0: CMD11, arg 0x00000000 , R1
<6>[12748.554269] SDIO0: SRsp 320 
<6>[12748.991252] SDIO0: CMD2, arg 0x00000000 , R2
<6>[12748.991797] SDIO0: LRsp ad4c5355 53443030 10bc1536 740154e1
<6>[12748.991823] SDIO0: CMD3, arg 0x00000000 , R1
<6>[12748.992143] SDIO0: SRsp 59b40500 
<6>[12748.992166] SDIO0: CMD9, arg 0x59b40000 , R2
<6>[12748.992713] SDIO0: LRsp 400e0032 db790000 ec537f80 a40002d
<6>[12748.992741] SDIO0: CMD7, arg 0x59b40000 , R1
<6>[12748.993062] SDIO0: SRsp 700 
<6>[12748.993085] SDIO0: CMD55, arg 0x59b40000 , R1
<6>[12748.993409] SDIO0: SRsp 920 
<6>[12748.993444] SDIO0: CMD51, arg 0x00000000 , R1
<6>[12748.994103] SDIO0: SRsp 920 
<6>[12748.994193] SDIO0: CMD55, arg 0x59b40000 , R1
<6>[12748.994517] SDIO0: SRsp 920 
<6>[12748.994568] SDIO0: CMD13, arg 0x00000000 , R1
<6>[12748.994892] SDIO0: SRsp 920 
<6>[12748.996301] SDIO0: CMD6, arg 0x00fffff0 , R1
<6>[12748.996622] SDIO0: SRsp 900 
<6>[12748.997996] SDIO0: CMD55, arg 0x59b40000 , R1
<6>[12748.998321] SDIO0: SRsp 920 
<6>[12748.998345] SDIO0: CMD6, arg 0x00000002 , R1
<6>[12748.998664] SDIO0: SRsp 920 
<6>[12748.998745] SDIO0: CMD6, arg 0x80fffff2 , R1
<6>[12748.999068] SDIO0: SRsp 900 
<6>[12748.999653] mmc0: new ultra high speed SDR50 SDHC card at address 59b4
<6>[12749.001083] mmcblk0: mmc0:59b4 USD00 29.5 GiB 
<6>[12749.002000] SDIO0: CMD18, arg 0x00000000 , R1
<6>[12749.002087] SDIO0: SRsp 900 
<6>[12749.002527] SDIO0: CMD12, arg 0x00000000 , R1
<6>[12749.002557] SDIO0: SRsp b00 
<6>[12749.002699]  mmcblk0: p1
<6>[12749.360275] SDIO1: CMD7, arg 0x00010000 , R1
<6>[12749.360330] SDIO1: SRsp 1e00 
<6>[12750.064282] SDIO0: CMD13, arg 0x59b40000 , R1
<6>[12750.064324] SDIO0: SRsp 900 

可以看到它是这样的一个流程:
CMD52触发IO接入,SD卡上电了->CMD0软重启一下->CMD8检查相关的能力集->CMD5类似于ACMD41,询问是否需要切换IO的电压->CMD55/ACMD41->CMD11->CMD2->CMD3->CMD9->CMD7->....->CMD6

支持高速卡的流程如下:

img

而至少需要满足以下的时序:

img

img

这样就是先以SDR12的时序在进行交互了

img

img

这里翻译一下:就是说在初始化切换的时候是保持在100KHZ~400KHZ之间进行切换

测试信号的确是有这个过程,但最终没有到96MHZ的数据传输,就可以怀疑是不是卡的问题了。

找了市面上的UHS的卡,确实是卡的问题。破案,一般可以达到30MB/s