Linux驱动——mmc sd card初始化流程(十一)

备注:
  1. Kernel版本:5.4
  2. 使用工具:Source Insight 4.0
  3. 参考博客:
  (1)[sd card] sd card初始化流程

概述

  本章节基于 《SD Specifications Part 1 Physical Layer Simplified Specification Version 3.01 May 18, 2010》 协议的 “4. SD Memory Card Functional Description” 进行说明。

sd card简介

主机和卡之间的交互都是由主机控制的。
**主机发送两种命令:**广播命令,寻址(点对点)命令。

  • 广播命令
    广播命令的目的是所有的卡。部分命令需要响应。
      
  • 寻址(点对点)命令
    寻址命令是发送给对应地址的卡的,并且会引起这张卡的响应。

SD 卡系统(host &card)定义了两种操作模式:

  • 卡识别模式
    在复位后,查找总线上的新卡的时候,主机会处于“卡识别模式”。卡在复位后会处于
    识别模式,直到收到 SEND_RCA(CMD3)命令。
      
  • 数据传输模式
    当 RCA 第一次发布后,卡会处于“数据传输模式”。主机会在总线上所有的卡都被识别
    后进入这个模式。

sd card操作模式和卡状态

  下图展示了操作模式以及卡状态之间的关系。每一个 SD 卡状态都会关联一个操作模式:
请添加图片描述

inactive mode(无效模式)

  非激活模式。当host提供的电压不在card的电压的可用范围之内时,会进入这种状态。
  这种状态下,card不会响应任何命令。

Card identification mode(卡识别模式)

  在卡识别模式下,主机会复位所有处于“卡识别模式”的卡,确认工作电压范围,识别
卡,并且要求他们发布相对卡地址(Relative Card Address)。这个操作是通过卡各自的 CMD
线完成的。卡识别模式下,所有数据通信都只通过数据线完成。

Data transfer mode(数据传输模式)

  数据传输模式,在这种模式下,host和card可以根据data线来传输数据

card identification mode流程说明

请添加图片描述

  在卡识别模式下,主机会复位所有处于“卡识别模式”的卡,确认工作电压范围,识别卡,并且要求他们发布相对卡地址(Relative Card Address)。这个操作是通过卡各自的 CMD线完成的。
  卡识别模式下,所有数据通信都只通过数据线完成。

请添加图片描述

  通过上图可以看出Card Identification Mode下,时钟频率不能超过400kHz。

卡识别模式

card identification mode下有如下几种操作

  • 卡复位(card reset)
  • 卡操作条件确认(Operating Condition Validation)
卡复位(card reset)

  GO_IDLE_STATE(CMD0)是软复位命令,设置每张卡进入“Idle”状态,不管当前是什么
状态。“Inactive”状态的卡不受这个命令的影响。
  主机上电后,所有的卡进入“Idle”状态,包括“Inactive”状态的卡在“上电”或者“CMD0”后,所有卡的 CMD 线都是输入模式,等待下个命令的起始位(Start bit)。
  卡初始化的时候,会有一个默认的相对地址(RCA=0x000),和一个默认的驱动级寄存
器设置(最低速度,最高驱动电流能力)。

卡操作条件确认(Operating Condition Validation)

  在主机和卡交互之初,主机可能不知道卡支持的电压,卡也可能不知道是否支持当前的电压。主机会先假设 SD卡支持某个电压,并以这个电压发送一个复位命令 CMD0。

  为了验证host假定的电压是否被card支持,SD2.0协议中定义了一个新的命令 CMD8。

  SEND_IF_COND(CMD8)用于验证 SD 卡接口操作条件。卡会通过分析 CMD8 的参数来检测操作条件的正确性。而主机会通过分析 CMD8 的响应来检查正确性)。支持的电压是由参数里的 VHS 区指定的。卡会假设 VHS 里面指定的电压是当前支持的电压。每一次命令VHS 里面只有 1 位能被设置为 1。主机会通过 CRC 和检查模式来确认通信的有效性。

  如果卡能够在支持电压下操作,响应会传回命令参数里设置的支持的电压和检测模式。
如果卡不能在支持电压下操作,就不会发送响应,并保持在“idle”状态。强制要求:在发送第一个 ACMD41 之前要先发送 CMD8,以便初始化高容量 SD 卡。SD 卡如果收到 CMD8,就会知道主机支持 V2.0,就可以使能新的功能。

  
  同样强制的,低电主机在发送ACMD41之前,也要发送CMD8。如果双电压卡没有收到CMD8,那么卡就会作为单独的高电压卡来工作,并且如果低电主机不发送 CMD8 的话,卡在收到ACMD41 后会进入“inactive”状态。

  
  SD_SEND_OP_COND(ACMD41)是用来提供给主机一种机制来识别和拒绝那些不匹配它期望的 VDD 范围的卡。主机通过发送需求的 VDD 电压范围来完成这个命令,这个范围作为命令的参数。不能支持指定电压范围的卡应该自动放弃后续的总线操作,并且进入“inactive”状态。OCR 寄存器里面的标准应该响应的定义。注意:ACMD41 是应用特定命令,因此 APP_CMD(CMD55)应该永远在 ACMD41 之前发送。“idle”状态下用于 CMD55的 RCA 寄存器应该是默认的 RCA=0x0000。

  
  主机发送复位命令(CMD0)复位卡,主机应该在 ACMD41 之前发送 CMD8 重新初始化 SD 卡。

卡初始化(card initialization)

  总线激活后,主机启动卡的初始化和识别进程。初始化进程以命令SD_SEND_OP_COND(ACMD41)作为开始,通过设置操作条件和 OCR 的 HCS 位来进行。HCS(High Capacity Support)位为 1,表示主机支持高容量 SD 卡。为 0 表示不支持。

  
  CMD8 扩展了 ACMD41 的功能;参数里的 HCS 位以及响应里的 CCS(Card Capacity Status)位。HCS 会被不回应 CMD8 的卡忽视掉。然而,如果卡不回应 CMD8,主机应该设置 HCS 为 0。标准容量卡会忽略 HCS。如果 HCS 设置为 0,那么高容量 SD 卡永远都不会返回 ready 状态(保持 busy 位为 0)。卡通过 OCR 的 busy 位来通知主机 ACMD41 的初始化完成了。设置 busy 位为 0 表示卡仍然在初始化。设置 busy 位为 1,表示已经完成初始化。主机会重复发送 ACMD41,直到 busy 为被设置为 1,此时的CCS和S18A才是可靠的。随后,card进入了ready state。

  
备注ACMD41使用方法:

  • ACMD41命令格式
ACMD INDEXtypeargumentrespabbreviationcommand description
ACMD41bcr[31]reserved bit [30]HCS(OCR[30]) [29]reserved for eSD [28]XPC [27:25]reserved bits [24]S18R [23:0] VDD Voltage Window(OCR[23:0])R3SD_SEND_OP_CONDSends host capacity support information (HCS) and asks the accessed card to send its operating condition register (OCR) content in the response on the CMD line. HCS is effective when card receives SEND_IF_COND command. Sends request to switch to 1.8V signaling (S18R). Reserved bit shall be set to ‘0’. CCS bit is assigned to OCR[30]. XPC controls the maximum current in the default speed mode of SDXC card. XPC=0 means 100mA (max.) but speed class is not supported. XPC=1 means 150mA (max.) and speed class is supported.
  • ACMD40 argument格式
    请添加图片描述

  • ACMD41 response格式
    请添加图片描述

卡电压切换(card bus signal voltage switch)

  通常card刚上电的情况下,其信号电压一般都是处于3.3V的模式。当card进入ready状态后,为了节省功耗,首先需要考虑是是否需要切换信号电压到1.8V。

  
  前面说过,如果host支持输出1.8V的信号电压的话,会将ACMD41的参数的S18R(bit24)设置为1来告诉card。当card收到这个ACMD41时,如果自己允许切换到1.8V的信号电压模式,那么就设置response的S18A(bit24)设置为1,否则设置为0。

  
  当host从response的S18A(bit24)解析出1的时候,可以向card发送CMD11命令,来通知card准备切换到1.8V的信号电压模式了。随后,host就可以将自己的输出的信号电压切换到1.8V了。
此时,card还是处于ready state。

  
电压切换流程:

请添加图片描述
请添加图片描述

卡识别流程(card identification process)

  在系统中,主机遵照相同的初始化顺序来初始化所有的新卡。不兼容的卡会进入“Inactive”状态。

  主机接着就会发送命令 ALL_SEND_CID(CMD2)给每一个卡,来得到他们的 CID 号。未识别的卡(处于 Ready 状态的)发送自己的 CID 作为响应。当卡发送了 CID 之后,它就进入“Identification”状态。

  之后主机发送 SEND_RELATIVE_ADDR(CMD3)命令,通知卡发布一个新的相对地址(RCA),这个地址比 CID 短,用于作为将来数据传输模式的地址。一旦收到 RCA,卡就会变为“Stand-by”状态。这时,如果主机想要分配另一个 RCA 号,它可以再发送一个 CMD3,通知卡重新发布一个 RCA 号。最后一个产生的 RCA 才是有效的。主机会重复识别进程,为系统中的每个卡循环发送“CMD2”和“CMD3”。

卡数据传输模式(data transfer mode)

在卡识别模式期间,主机应该保持在 Fod 频率(不超过400K),工作电压和信号电压都已经设置完成。因为某些卡可能在卡识别模式中有频率限制,总线宽度和总线速度模式并没有设置。

总线速度模式的设置:

总线速度模式的设置主要以依赖于CMD6。

  • 说明
    switch function command(CMD6)是用来切换或者扩展card的function。当前有四个function组定义如下:
    (1)Access mode:访问模式,用于选择SD总线接口的速度模式(也就是我们这里的目标)
    (2)Command system:命令系统,可以通过共享命令集来扩展和控制一个特殊的功能
    (3)Driver strength:驱动强度,在UHS-I模式下用于选择一个合适的驱动信号强度,取决于host的环境
    (4)Current limit:电流限制,在UHS-I模式下设置card的最大电流,由host的供电属性决定

  CMD6只有在transfer state下才是可用的。一旦card复位之后,所有group默认都选中function0.

  
  card会返回R1 response(CMD线)以及512bit的状态数据(DAT线)作为对host的CMD6的响应。从sd传输标准上看,CMD6相当于一个单块读命令、超时时间是100ms。

  
  card对于对于CMD6的切换动作会在状态数据传输完之后的8个时钟之内完成。当CMD6导致总线行为(例如总线速度模式)发生变化后,host要求至少要等CMD6传输完成之后的8个时钟之后才允许使用新的总线行为进行通讯。

  • CMD6的模式
    CMD6有两种模式,分别是check function模式和set function模式。
    (1)check function模式用来查询card所支持的function
    (2)set function模式用来切换card的functionality

总线宽度的设置:

  上电之后或者执行CMD0命令之后,card的总线宽度模式总是默认设置为1bit模式。可以通过ACMD6来设置card的总线宽度模式。
  如果想要改变总线宽度,需要具备下面两个条件:

  • a) 卡处于 transfer 状态
  • b) 卡没有被锁定 (锁定的卡会认为 ACMD6 是无效命令)

数据传输模式状态切换过程:
请添加图片描述

  所有数据传输模式下的数据通信都是主机和被选择卡之间通过寻址命令点对点进行的。
寻址命令以命令线上的响应作为应答信号。

  
  各种数据传输模式的关系总结如下:

  • 所有数据读命令可以在任何情况下通过停止命令(CMD12)来中止。数据传输会中止,卡会回到传输状态。读命令有:块读(CMD17),多块读(CMD18),发送写保护(CMD30),发送SCR(ACMD51)以及读模式的通用命令(CMD56)。

  

  • 所有数据写命令同样也可以通过 CMD12 来中止。在发送 CMD7 取消选定卡之前,应该先停止写命令。写命令有:块写(CMD24 和 CMD25),编程 CSD(CMD27),锁定/解锁命令(CMD42)以及写模式的通用通用命令(CMD56)。

  

  • 一旦数据传输完成,卡会退出写状态,并且进入编程状态(传输成功)或者传输状态(传输失败)。

  

  • 如果“块写操作”停止,并且块长度和最后一个块的 CRC 是有效的,那么数据会被处理。

  

  • 卡可能会提供缓存给“块写”。这就意味着当前一个块正在处理的时候,就可以发送后一个块了。如果缓存都慢了,那么卡就会处于编程状态(见图上图),DAT0 会被拉低(busy)。

  

  • “写 CSD”,“写保护”和“擦除”的时候没有缓存操作,这就意味着当这些命令被处理的时候,其他传输命令都不接受。DAT0 线也会保持低电平,保持编程状态。实际上,如果 CMD 和 DAT0 线都是分开的,那么在一个卡的 DAT0 是低电平的时候,主机可以访问其他的卡。

  

  • 当卡正在处理命令/数据的时候,是不允许发送参数设置的命令的。参数设置命令有:设置块长度(CMD16),擦除块开始(CMD32)和擦除块结束(CMD33)

  

  • 当卡正在处理命令/数据的时候,读命令也是不允许的。

  

  • 将另一张卡从 Stand-by 模式转换到 Transfer 模式(CMD7)不会终止擦除和编程操作。卡会切换到 Disconnect 状态并且释放 DAT 线。

  

  • 处于 Disconnect 状态的卡可以通过 CMD7 的命令重新被选定。这时,卡会进入Programming 模式,并且重新使能 busy 指示。

  

  • 复位卡(CMD0 或者 CMD15)会中止任何等候或者执行的编程操作。这可能会损坏卡的内容。主机应该尽量预防这种事情。

  

  • CMD34-37,CMD50 和 CMD57 是 SD 命令系统的保留命令。这些命令的状态转换是单独定义的。

host流程说明

根据card的外部初始化流程,可以简单整理出host在sd各个状态下需要做的操作流程如下(黑体部分是我们这里重点关心的部分):

未上电状态

(1)准备好工作电压、信号电压,上电
(2)准备好时钟(400kHz)

idle state

(1)尝试获取一个合适的工作电压
  host发送CMD0命令进行复位
  host发送CMD8命令,告诉card,host可以支持SD2.0。card收到CMD8命令之后会使能自己符合SD2.0的一些新功能
  host发送参数为0的ACMD41命令,提取response中的VHS,得到card支持的工作电压范围
  host选择一个card和host都支持的最低的工作电压,并将host提供给card的工作电压设置为这个值。
这个值就是合法的,后续就以这个ocr作为工作电压重新复位开始对sd card真正的初始化过程.

2)重新复位,完成card的内部初始化
  host发送CMD0命令进行复位
  host发送CMD8命令,告诉card,host可以支持SD2.0。card收到CMD8命令之后会使能自己符合SD2.0的一些新功能。同时,获取到ocr寄存器的值。
  host根据host是否支持SDHC来设置ocr的HCS、是否支持1.8V来设置ocr的S18R,将设置好的ocr作为ACMD41的参数,发送给card。
  host读取ACMD41的busy位来判断card的内部初始化是否完成,如果没有完成继续发送ACMD41
一旦card的内部初始化完成,则card进入ready state。

ready state

(1)设置信号电压
  host根据ACMD41的response提取对应的S18A,如果为1,说明card支持切换到信号电压为1.8V的模式。
  host发送CMD11命令,要求card切换到1.8V的信号电压模式。
  host切换提供给card的信号电压为1.8V。
(2)获取card的CID值
  host发送CMD2命令,要求card回复其CID寄存器的值。
  一旦card返回response之后,进入identification state。

identification state

(1)获取card的RCA值
  host发送CMD3命令,要求card回复其RCA值。
  一旦card通过response返回这个RCA之后,进入stand-by state。
  identification mode也就完成了。

stand-by state——>transfer state

(1)获取sd card的特殊数据寄存器
  csd寄存器中存储了sd card的一些信息。
  host发送CMD9命令,要求card回去其CSD寄存器(card specific data)的值

(2)切换到transfer state模式
  后续的初始化操作需要在transfer state下进行,所以需要发送CMD7命令选中对应的card,将card切换到transfer state

(3)获取sd card的配置寄存器和状态寄存器
  host发送ACMD51命令,要求card回复其SCR寄存器(SD configuration register)的值
  host发送ACMD13命令,要求card回复其SSR寄存器(SD status regiter)的值

(4)读取card 的switch状态,也就是其支持的function
  host发送CMD6命令来读取card switch status。
  通过card switch status可以得到card支持的总线速度模式以及驱动强度类型。

(5)切换总线宽度
  host发送ACMD11命令,要求card将总线宽度切换到4bit模式
  设置host自身的总线宽度为4bit模式

(6)选择合适的总线速度模式、驱动强度、以及限流并进行设置
  host从host和card都支持的总线速度模式中选择一个最优的模式。
  host根据选择的总线速度模式,来选择对应的驱动类型以及限流值,通过CMD6命令让card进行相应值的切换。
  host发送CMD6命令、并且mode=1、group=0、function=总线速度模式码,card收到命令之后会切换到相应的总线速度模式上。
  设置host自身的总线速度模式(时序,timing)。

(7)执行tuning操作
  对于UHS-I的card来说,如果处于uhs的速度模式,host需要发送CMD19执行tuning操作以获取一个最佳的采样点。

到此,host对于sd card的初始化就完成了。

host初始化sd card代码

整个代码设计是围绕着“在sd card初始化过程中,host要做的事情”的思想来设计的。
  因此,可以看代码的过程中,回头看看前面的设计思想。了解了上述的初始化流程之后再来看代码会感觉比较容易理解。
  对应代码drivers/mmc/core/sd.c、drivers/mmc/driver/core/sd-ops.c。

mmc_rescan_try_freq
        |--->mmc_go_idle
        |--->mmc_send_if_cond
        |--->mmc_attach_sdio
        |--->mmc_attach_sd
        |--->mmc_attach_mmc

  mmc_rescan_try_freq() 函数源码详解,请参考《Linux驱动——mmc core浅析(三)》。


mmc_attach_sd
        |--->mmc_send_app_op_cond
        |--->mmc_attach_bus
        |--->mmc_select_voltage
        |--->mmc_sd_init_card
        |              |--->mmc_sd_get_cid
        |              |--->mmc_alloc_card
        |              |--->mmc_sd_get_csd
        |              |--->mmc_select_card
        |              |--->mmc_sd_setup_card
        |              |--->mmc_read_switch
        |              |--->mmc_sd_init_uhs_card
        |              |                |--->mmc_set_bus_width
        |              |                |--->sd_update_bus_speed_mode
        |              |                |--->sd_select_driver_type
        |              |                |--->sd_set_current_limit
        |              |                |--->sd_set_bus_speed_mode
        |              |                |--->mmc_execute_tuning
        |              |--->mmc_sd_switch_hs
        |              |               |--->mmc_set_clock
        |              |               |--->mmc_set_bus_width
        |--->mmc_add_card

  mmc_attach_sd() 函数源码详解,请参考《Linux驱动——mmc sd card初始化流程(十)》。

Logo

更多推荐