为了学习SD/SDIO协议,看了一下linux中初始化SD卡的流程,结合代码更容易SD初始化是怎么做的。

下面图截自:"SD Specifications Part 1 Physical Layer Simplified Specification Version 4.10"

SD卡在sd模式下的初始化流程图,sd协议还有spi模式暂不研究。

这个流程图对应于linux 代码就在

/driver/mmc/core/sd.c

static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)

传入参数

truct mmc_host *host mmc/sd/sdio主机控器的结构,成员用到再说明

u32 ocr 这个比较重要,与ACMD41和sd卡中ocr寄存器相关。调用mmc_sd_init_card之前

linux已经做过一些工作,就是发送ACMD41获取SD卡工作电压,与Host支持电压匹配并设置host的电压,然后调用mmc_sd_init_card,并把电压信息通过ocr传递下来。流程图开始时这些工作已经做好。

struct mmc_card *oldcard  新插入的卡初始化时该值为NULL。

linux版本3.7 mmc_sd_init_card函数:

 /*
* Handle the detection and initialisation of a card.
*
* In the case of a resume, "oldcard" will contain the card
* we're trying to reinitialise.
*/
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int err;
u32 cid[];
u32 rocr = ; BUG_ON(!host);
WARN_ON(!host->claimed); err = mmc_sd_get_cid(host, ocr, cid, &rocr);
if (err)
return err; if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != )
return -ENOENT; card = oldcard;
} else {
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host, &sd_type);
if (IS_ERR(card))
return PTR_ERR(card); card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
} /*
* For native busses: get card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
return err;
} if (!oldcard) {
err = mmc_sd_get_csd(host, card);
if (err)
return err; mmc_decode_cid(card);
} /*
* Select card, as all following commands rely on that.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
return err;
} err = mmc_sd_setup_card(host, card, oldcard != NULL);
if (err)
goto free_card; /* Initialization sequence for UHS-I cards */
if (rocr & SD_ROCR_S18A) {
err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card; /* Card is an ultra-high-speed card */
mmc_card_set_uhs(card); /*
* Since initialization is now complete, enable preset
* value registers for UHS-I cards.
*/
if (host->ops->enable_preset_value) {
mmc_host_clk_hold(card->host);
host->ops->enable_preset_value(host, true);
mmc_host_clk_release(card->host);
}
} else {
/*
* Attempt to change to high-speed (if supported)
*/
err = mmc_sd_switch_hs(card);
if (err > )
mmc_sd_go_highspeed(card);
else if (err)
goto free_card; /*
* Set bus speed.
*/
mmc_set_clock(host, mmc_sd_get_max_clock(card)); /*
* Switch to wider bus (if supported).
*/
if ((host->caps & MMC_CAP_4_BIT_DATA) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
if (err)
goto free_card; mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
} host->card = card;
return ; free_card:
if (!oldcard)
mmc_remove_card(card); return err;
}

18行,err = mmc_sd_get_cid(host, ocr, cid, &rocr);字面意思就是获取CID,对照着流程图给该函数注释:

 /*
* Fetch CID from card.
*/
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
{
int err;
u32 max_current; /*
* Since we're changing the OCR value, we seem to
* need to tell some cards to go back to the idle
* state. We wait 1ms to give cards time to
* respond.
*/
mmc_go_idle(host);//发送CMD0 /*
* If SD_SEND_IF_COND indicates an SD 2.0
* compliant card and we should set bit 30
* of the ocr to indicate that we can handle
* block-addressed SDHC cards.
*/
err = mmc_send_if_cond(host, ocr);//发送CMD8,
if (!err)
ocr |= SD_OCR_CCS;//如果返回失败,说明卡不是SD2.0或之后的版本,如果是2.0的SD卡,把ocr的30位置1,即协议里ACMD41命令中的HCS.
表示支持SDHC或SDXC,ACMD41命令见下面图示
/*
* If the host supports one of UHS-I modes, request the card
* to switch to 1.8V signaling level.
*/
if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))
ocr |= SD_OCR_S18R; //判断主机控制器是否支持UHS-I host->caps通常在主机控制器驱动的probe函数里面初始化,值和具体主机控制器
                    有关.如果支持那么就把ocr的24位置1,即ACMD41命令的S18R.表示要求SD卡准备切换到1.8V电压模式 /*
* If the host can supply more than 150mA at current voltage,
* XPC should be set to 1.
*/
max_current = sd_get_host_max_current(host);
if (max_current > )
ocr |= SD_OCR_XPC;//SDXC中Power Control相关的项,ocr第28位,ACMD41中的XPC try_again:
err = mmc_send_app_op_cond(host, ocr, rocr);//发送ACMD41,这个函数你可以进去看一下,会发现循环检验ACMD41的,应答值的31位,与
协议流程图中符合.rocr就是卡对ACMD41的应答值.另外ACMD41属于app cmd需要先发送CMD55,这个linux都封装在了函数里,并且流程图上也简化掉了.
                                
if (err)
return err; /*
* In case CCS and S18A in the response is set, start Signal Voltage
* Switch procedure. SPI mode doesn't support CMD11.
*/
if (!mmc_host_is_spi(host) && rocr &&
((*rocr & 0x41000000) == 0x41000000)) {//这个if语句,先判断是不是spi模式,是spi模式就不继续,我只分析SD模式所以继续
判断rocr即ACMD41的应答值的CSS(30位)和S18A(24位)这两位具体意义见代码下面的图表.只有当sd卡是SDHC或SDXC,并且SD卡准备好切换电压模式,才切换
SDSC不进行切换.
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true);//发送CMD11切换,主机端也做相应的处理.
if (err) {
ocr &= ~SD_OCR_S18R;
goto try_again;
}
} if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);//发送CMD2.获取cid,即Card IDentification register,存放了一些卡的信息,
到这里发现已经到了流程图的底部,只剩CMD3了 return err;
}

ACMD41命令:

图中红线标出的就是代码中rocr的值.关于该命令更多的内容见SD的spec.

回到mmc_sd_init_card函数,

22~37行与协议无关,主要是初始化一个struct mmc_card *card结构体.这个结构体就相当于这张卡的身份证,从卡的CID,CSD..寄存器拿到的值都要填到该结构中备用.

43行 err = mmc_send_relative_addr(host, &card->rca),发送CMD3获取RCA,得到卡的地址.流程图到这里就结束了. mmc_sd_init_card却没有结束.

接下来还要获取SD卡的CSD寄存器的值,来填充struct mmc_card *card结构体.

49行 err = mmc_sd_get_csd(host, card);发送CMD9获取CSD并解析,填充到card.CSD寄存器保存了大量卡的信息.

53行 mmc_decode_cid(card); 解析前面获得的CID并填充到card,这一步为什么不在前面获得cid的时候做? 是因为sd卡协议有不同版本而版本信息放在CSD中,所以需要先得到CSD,获得版本号,在根据版本号解析CID中的数据. 详细内容见spec中寄存器部分.

60行 err = mmc_select_card(card);发送CMD7使用上面得到的地址选择卡

65行 err = mmc_sd_setup_card(host, card, oldcard != NULL);发送ACMD51获取SCR寄存器值,发送ACMD13获取SD卡状态信息,解析并填充card结构,SCR寄存器是对CSD的补充.
最后69~113行 if else语句,判断是否是UHS-I,分别进行处理.先看一下esle中的代码

先调用 err = mmc_sd_switch_hs(card);,支持高速就发命令把卡设置到高速

 int mmc_sd_switch_hs(struct mmc_card *card)
{
int err;
u8 *status; if (card->scr.sda_vsn < SCR_SPEC_VER_1)//判断卡的版本,SD1.1和之后的版本支持高速
return ; if (!(card->csd.cmdclass & CCC_SWITCH))//判断是否支持class10命令
return ; if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))//判断主机控制器是否支持高速卡
return ; if (card->sw_caps.hs_max_dtr == )/*这个值什么情况下会为0? 该值赋值是在mmc_read_switch函数中,发送CMD6命令时赋值的,如果不支持HIGHSPEED就没有赋值.
通常sd卡驱动中使用CMD6命令,都会发送两次,第一次查询卡是否支持接下来要做的切换(在mmc_read_switch中).
第二次执行切换操作.CMD6命令详细内容见spec*/
return ; err = -EIO; status = kmalloc(, GFP_KERNEL);//分配64bytes的空间.用于接收CMD6的应答数据.
if (!status) {
pr_err("%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host));
return -ENOMEM;
} err = mmc_sd_switch(card, , , , status);//发送CMD6切换卡到高速
if (err)
goto out; if ((status[] & 0xF) != ) {//切换结果在第17字节,也是spec中规定的
pr_warning("%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(card->host));
err = ;
} else {
err = ;
} out:
kfree(status); return err;
}

93行,设置主机这边,最终会调用host驱动,与你host硬件有关.

100行,mmc_set_clock(host, mmc_sd_get_max_clock(card));//设置时钟频率,mmc_sd_get_max_clock(card)是用的card->sw_caps.hs_max_dtr或card->csd.max_dtr.如果支持高速linux会设置为card->sw_caps.hs_max_dtr,这个值在之前被赋为

50000000.

105~111 同样需要host和card同时支持4bit宽度,才能设置,并且要同时设置host和card

err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);设置卡的总线宽度,使用的命令是ACMD6

至此,mmc_sd_init_card函数就结束了. linux中的初始化处理过程基本上与spec中的流程图相符. 之后还有设置时钟,设置数据线宽度等操作.

还有一个关于UHS-I的分支没有分析,这个放到下篇分析.并且其中又涉及到CMD6,顺便简单分析学习一下CMD6的使用.

(linux)SD卡初始化-mmc_sd_init_card函数的更多相关文章

  1. (linux)SD卡初始化-mmc_sd_init_card函数(续)

      转自:http://www.cnblogs.com/fengeryi/p/3472728.html   mmc_sd_init_card剩下的关于UHS-I的分支结构. uhs-I的初始化流程图如 ...

  2. (续)linux SD卡初始化---mmc_sd_init_card函数

    mmc_sd_init_card剩下的关于UHS-I的分支结构. uhs-I的初始化流程图如图: 红线标出的部分是已经做了的事,与上一篇那个流程图是一致的,之后就是if分支中做的事. if分支中的函数 ...

  3. Linux SD卡驱动开发(四) —— SD 控制器之真正的硬件操作

    前面对SD卡控制器有了一个主要的介绍.事实上SD控制器层更过的意义是为core层提供一种操作SD卡硬件的一种方法.当然不同的控制器对硬件控制的方法不尽同样,可是他们终于都能像core层提交一个统一的封 ...

  4. SD卡初始化以及命令详解

    SD卡是嵌入式设备中很常用的一种存储设备,体积小,容量大,通讯简单,电路简单所以受到很多设备厂商的欢迎,主要用来记录设备运行过程中的各种信息,以及程序的各种配置信息,很是方便,有这样几点是需要知道的 ...

  5. Linux SD卡建立两个分区

    本文主要介绍Linux 环境下 SD 卡建立两个分区的操作流程: 操作环境:Linux Ubuntu 2016.4 操作目的:将 SD 卡分为两个分区:第一分区格式为 FAT32,大小 500M.第二 ...

  6. SD卡读写一些函数

    /SPI2 读写一个字节 //TxData:要写入的字节 //返回值:读取到的字节 u8 SPI2_ReadWriteByte(u8 TxData) { u16 retry=0;   while((S ...

  7. SD/MMC卡初始化及读写流程

    二.MMC/SD卡的模型和工作原理 PIN脚.SD卡总线.SD卡结构.SD卡寄存器.上电过程 SD卡寄存器:  OCR:操作电压寄存器: 只读,32位 第31位: 表示卡上电的状态位   CID: 卡 ...

  8. SD卡的SPI模式的初始化顺序(转)

    为了使SD卡初始化进入SPI模式,我们需要使用的命令有3个:CMD0,ACMD41,CMD55(使用ACMD类的指令前应先发CMD55,CMD55起到一个切换到ACMD类命令的作用). 为什么在使用C ...

  9. sd 卡驱动--基于高通平台

    点击打开链接 内容来自以下博客: http://blog.csdn.net/qianjin0703/article/details/5918041 Linux设备驱动子系统第二弹 - SD卡 (有介绍 ...

随机推荐

  1. NAND FLASH 物理结构分析

    转自:http://blog.51cto.com/hardywang/2053915 NAND Flash是一种非易失性随机访问存储介质,基于浮栅(Floating Gate)晶体管设计,通过浮栅来锁 ...

  2. Modular Production Line

     Modular Production Line 时空限制: 1000ms /65536K   An automobile factory has a car production line. Now ...

  3. css-包含块

    在CSS中,有事一个元素的位置和尺寸的计算都相对于一个矩形,这个矩形被称作包含块.包含块是一个相对的概念,比如 子元素的初始化布局总是在父元素的左上角,这就是一个相对的概念.其中父元素就是一个参照物, ...

  4. 实现uitable cell中点击button设置当前cell为选中状态

    - (void)buttonClick:(id)senser{    NSInteger tag = [senser tag];    NSLog(@"the button tag is % ...

  5. BUPT复试专题—哈夫曼编码(2009)

    题目描述 哈夫曼编码中  平均码长=码长×码字出现的概率 如:ABCDE 五个字符的出现次数分别为50 20 5 10 15 那么,其哈夫曼编码为A:0   B:10   C:1110   D:111 ...

  6. kibana dev tools快捷键

    kibana dev tools快捷键 ctrl+enter  提交请求 ctrl+i 自动缩进 ctrl+enter 提交请求 down 打开自动补全菜单 enter或tab 选中项自动补全 esc ...

  7. 转: memcached Java客户端spymemcached的一致性Hash算法

    转自:http://colobu.com/2015/04/13/consistent-hash-algorithm-in-java-memcached-client/ memcached Java客户 ...

  8. 转: Code Review 程序员的寄望与哀伤

    转自: http://www.cnblogs.com/mindwind/p/5639008.html 一个程序员,他写完了代码,在测试环境通过了测试,然后他把它发布到了线上生产环境,但很快就发现在生产 ...

  9. 【LeetCode】Generate Parentheses 解题报告

    [题目] Given n pairs of parentheses, write a function to generate all combinations of well-formed pare ...

  10. Spark MLlib Deep Learning Convolution Neural Network (深度学习-卷积神经网络)3.2

    3.Spark MLlib Deep Learning Convolution Neural Network(深度学习-卷积神经网络)3.2 http://blog.csdn.net/sunbow0 ...