mmc_rescan_try_freq 简析

本文深入分析了mmc_rescan_try_freq函数在MMC卡初始化阶段如何通过不同频率尝试与目标卡建立连接。重点介绍了从上电到复位硬件、初始化I/O和内存控制器的过程,并详细解释了如何根据不同类型的MMC卡(如SD、SDIO、SD卡和SDIO卡组合)进行正确的初始化操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继之前的文章mmc_rescan之后,我们来分析一下mmc_rescan_try_freq,顾名思义,就是用不同的clock去尝试初始化与目标卡的连接。

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
	host->f_init = freq; //根据mmc_recan函数传进来的参数知道,首先传进来的是400kHZ.一般mmc/sd/sdio的初始化时钟采用的是400kHZ.

#ifdef CONFIG_MMC_DEBUG
	pr_info("%s: %s: trying to init card at %u Hz\n",
		mmc_hostname(host), __func__, host->f_init);
#endif
1.	mmc_power_up(host); //上电,我们知道,在mmc_add_host时,会调用mmc_start_host,而那里首先是将host掉电的。这里上电。

	/*
	 * Some eMMCs (with VCCQ always on) may not be reset after power up, so
	 * do a hardware reset if possible.
	 */
	mmc_hw_reset_for_init(host); //复位硬件,可以选择性实现。

	/* Initialization should be done at 3.3 V I/O voltage. */
	mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);//这里其实什么事情都没做,因为一般host控制器并不会实现host->ops->start_signal_voltage_switch

	/*
	 * sdio_reset sends CMD52 to reset card.  Since we do not know
	 * if the card is being re-initialized, just send it.  CMD52
	 * should be ignored by SD/eMMC cards.
	 */
	2. sdio_reset(host);//①如果目标卡是纯SD卡(对MMC卡不了解,所以不加评论),则目标卡不会应答,一般主机host的寄存器会报错,但是这个无关紧要,可以不理它。②如果目标卡是纯SDIO卡,那么这里就是复位SDIO卡,通过命令CMD52来实现的。③如果目标卡是SD卡和SDIO卡的组合卡,则需要先发送CMD52来复位SDIO卡,再复位SD卡,因为CMD52要先于CMD0发送。

When the host re-initializes both I/O and Memory controllers, it is strongly recommended that the host either execute a power reset (power off then on) or issues a reset commands to both controllers prior to any other operation. If the host chooses to use the reset commands, it shall issue CMD52 (I/O Reset) first, because it cannot issue CMD52 after CMD0 (see 4.4). After the reset, the host shall re-initialize both the I/O and Memory controller as defined in Figure 3-2.(协议)
	
	3. mmc_go_idle(host); //发送CMD0 复位SD卡。

	4. mmc_send_if_cond(host, host->ocr_avail); //为了支持sd version 2.0以上的sd卡,在初始化的过程中必须在发送ACMD41之前,先发送CMD8,CMD8一般是用于检测SD卡是否能运行在host提供的电压范围内。大家可能发现,这个调用过程没有检查是否出错,其实一般CMD8是用来辨别目标卡是否是高容量SD卡,如果是,CMD8 会有R7应答,R7应答中会有目标SD卡支持的电压范围以及CMD8中发送过去的“check pattern(一般是0xAA)”,否则,目标卡不会应答,在Linux 内核代码中,判断是这样的,如果应答,目标卡就是SD高容量卡,否则出现应答超时错误,就是标准SD卡!这里的调用,主要作用是为了在发送ACMD41之前发送CMD8,这是version 2.0及以上的规定顺序,后面还会有发送CMD8的地方,那里才是真正检测目标卡的类型的地方。
 

	/* Order's important: probe SDIO, then SD, then MMC */
	5 .if (!mmc_attach_sdio(host))
		return 0;
	6.if (!mmc_attach_sd(host))
		return 0;
	7.if (!mmc_attach_mmc(host))
		return 0;
	Linux 卡的探测顺序是:先辨别卡是否是sdio功能卡或者是sdio与sd卡的组合卡,然后辨别是否是SD卡,最后才会辨别是否是mmc卡。
	mmc_power_off(host); 
	return -EIO;
}


<think>我们正在讨论MMC(多媒体卡)子系统中的mmc_host结构体及其相关技术。根据提供的引用片段,我们可以总结以下关键点:1.mmc_host结构体代表一个MMC主机控制器,负责与MMC/SD/SDIO卡通信。2.主机控制器驱动需要完成的任务包括:-申请mmc_host内存空间-设置mmc_host的成员(包括操作集ops)-初始化主机控制器硬件-注册mmc_host(注册后会触发卡检测)3.当出现错误时(如卡初始化失败),需要执行清理操作:-断开总线(mmc_detach_bus)-释放主机(mmc_release_host)-移除卡(mmc_remove_card)并将host->card置为NULL4.主机锁机制(通过mmc_claim_host和mmc_release_host)确保同一时间只有一个线程访问主机控制器。5.块设备请求队列设置(如blk_queue_max_hw_sectors设置最大扇区数)。下面将详细解释mmc_host相关的技术细节和常见问题解决方案。###1.mmc_host结构体概述在Linux内核中,`mmc_host`是MMC子系统的核心数据结构之一,定义在`include/linux/mmc/host.h`中。它代表一个物理的MMC主机控制器,每个控制器对应一个`mmc_host`实例。主要成员包括:```cstructmmc_host{conststructmmc_host_ops*ops;//主机控制器操作集structdevice*parent;//父设备(通常是平台设备)structdeviceclass_dev;//设备模型中的设备intindex;//主机索引号unsignedintf_min;//最小工作频率unsignedintf_max;//最大工作频率u32ocr_avail;//支持的电压范围unsignedlongcaps;//主机能力标志(如MMC_CAP_4_BIT_DATA)/*...其他成员...*/structmmc_card*card;//当前插入的卡/*...等待队列、锁、线程等...*/};```###2.主机控制器驱动开发步骤开发一个新的MMC主机控制器驱动需要以下步骤:####(1)分配mmc_host使用`mmc_alloc_host`函数分配内存:```cstructmmc_host*mmc_alloc_host(intextra,structdevice*dev);```-`extra`:额外分配的私有数据大小(通常用于存放驱动私有数据)-`dev`:关联的父设备####(2)设置主机操作集(`mmc_host_ops`)操作集定义了主机控制器底层操作,例如:```cstaticconststructmmc_host_opsmy_ops={.request=my_request,.set_ios=my_set_ios,.get_cd=my_get_cd,//检测卡是否存在.get_ro=my_get_ro,//检测写保护.enable_sdio_irq=my_enable_sdio_irq,//使能SDIO中断};```####(3)初始化硬件在驱动探测函数中:-初始化硬件寄存器-申请中断、DMA资源等-设置主机参数(如`host->f_min`,`host->f_max`,`host->ocr_avail`,`host->caps`)####(4)注册主机使用`mmc_add_host(host)`注册主机。注册后,内核会启动卡检测机制(通过`mmc_rescan`工作队列)。###3.主机锁机制由于主机控制器一次只能处理一个命令,因此需要使用锁机制。常用函数:-`mmc_claim_host(host)`:获取主机锁(可能睡眠等待)-`mmc_release_host(host)`:释放主机锁示例(简化自引用[4]):```cvoidsome_function(structmmc_host*host){mmc_claim_host(host);//执行需要独占主机的操作(如发送命令)mmc_release_host(host);}```###4.错误处理流程当卡初始化失败时(如引用[1]所示):```c//1.移除卡(如果已部分初始化)mmc_remove_card(host->card);host->card=NULL;//2.重新获取主机锁(因为此时可能已释放)mmc_claim_host(host);//3.断开总线(清理总线相关资源)mmc_detach_bus(host);//4.释放主机锁mmc_release_host(host);//5.打印错误日志printk(KERN_ERR"%s:error%dwhilstinitialisingMMCcard\n",mmc_hostname(host),err);```###5.块设备请求队列设置对于支持块设备的MMC卡(如SD卡),需要设置请求队列参数(引用[3]):```cstructmmc_queue{structrequest_queue*queue;//...};staticintmmc_init_queue(structmmc_queue*mq,...){//...blk_queue_max_hw_sectors(mq->queue,BLK_DEF_MAX_SECTORS);//设置最大扇区数(1024)//...}```###常见问题解决方案####问题1:卡检测失败-检查硬件连接和电源-确认主机控制器的`get_cd`回调函数正确实现(返回卡存在状态)-检查主机能力标志`caps`是否包含`MMC_CAP_NEEDS_POLL`(如果无法检测卡插入事件,需要轮询)####问题2:数据传输错误-检查DMA配置(如缓冲区地址对齐、长度)-验证时序设置(通过`set_ios`回调设置正确的时钟频率和总线宽度)-检查电压配置(是否在卡的`ocr_avail`范围内)####问题3:性能优化-启用DMA传输(设置`caps`包含`MMC_CAP_DMA`)-使用ADMA2(高级DMA)如果硬件支持(如SDHCI控制器)-调整块设备队列深度(`queue_depth`)和最大扇区数###总结`mmc_host`是LinuxMMC子系统中的关键结构,代表一个MMC主机控制器。开发主机驱动需要实现操作集并正确管理主机锁。错误处理需遵循清理流程以避免资源泄漏。性能优化可考虑DMA、队列参数调整等方法。§§相关问题§§1.如何实现MMC主机控制器驱动的`get_cd`回调函数?2.MMC卡初始化失败的可能原因有哪些?3.如何调试MMC数据传输错误?4.MMC主机控制器支持哪些电源管理模式?</think>### MMC_HOST 技术详解 `mmc_host` 是 Linux 内核中多媒体卡(MMC)子系统的核心数据结构,代表一个物理的 MMC/SD/SDIO 主机控制器。以下是关键技术要点: #### 1. **核心功能** - **硬件抽象**:封装主机控制器的寄存器操作和硬件特性 - **数据传输**:管理命令、数据和中断的传输流程 - **电源管理**:控制卡的供电和时钟频率 - **总线管理**:处理总线初始化、挂起和恢复操作 #### 2. **关键数据结构** ```c struct mmc_host { struct device *parent; // 关联的硬件设备 const struct mmc_host_ops *ops; // 硬件操作函数集 unsigned int f_min, f_max; // 工作频率范围 u32 ocr_avail; // 支持的电压范围 unsigned long caps; // 控制器能力标志 struct mmc_card *card; // 当前插入的卡 struct wait_queue_head wq; // 等待队列 // ... }; ``` #### 3. **核心操作流程** 1. **主机注册**(引用[2]): ```c host = mmc_alloc_host(sizeof_priv, dev); host->ops = &custom_host_ops; mmc_add_host(host); // 触发卡检测 ``` - 分配内存 → 初始化操作集 → 注册到内核 → 启动卡检测线程 2. **卡初始化流程**: - 检测卡插入 → 发送初始化命令序列 → 识别卡类型(MMC/SD/SDIO) - 失败处理(引用[1]): ```c mmc_remove_card(host->card); host->card = NULL; mmc_detach_bus(host); // 断开总线 mmc_release_host(host); // 释放主机锁 ``` 3. **主机锁机制**(引用[4]): - `mmc_claim_host()`:独占访问控制器 - `mmc_release_host()`:释放控制权 - 使用等待队列(`host->wq`)管理访问竞争 #### 4. **关键问题解决方案** **问题1:卡初始化失败** - **原因**:电压不匹配/时序错误/硬件故障 - **解决步骤**: 1. 检查 `ocr_avail` 与卡的需求是否匹配 2. 验证时钟配置(`f_min`/`f_max`) 3. 使用示波器检查物理信号完整性 4. 查看内核日志:`dmesg | grep mmc` **问题2:数据传输超时** - **解决方案**: ```c // 调整块设备参数(引用[3]) blk_queue_max_hw_sectors(queue, BLK_DEF_MAX_SECTORS); ``` 1. 检查 DMA 配置(对齐/缓冲区大小) 2. 优化中断处理延迟 3. 调整 SDMA 缓冲区大小:`host->max_segs` **问题3:并发访问冲突** - **模式**: ```c mmc_claim_host(host); // 获取锁 // 执行关键操作 mmc_release_host(host);// 释放锁 ``` 避免在持有锁时执行阻塞操作 #### 5. **调试技巧** - 动态日志控制: ```bash echo 8 > /proc/sys/kernel/printk # 启用调试日志 ``` - 关键函数追踪: - `mmc_rescan()`:卡检测入口 - `mmc_power_up()`:电源序列 - `mmc_attach_*()`:卡类型识别 ### 典型应用流程 ```mermaid graph TD A[探测硬件] --> B[分配mmc_host] B --> C[配置操作集 ops] C --> D[注册mmc_add_host] D --> E[启动扫描线程] E --> F{检测到卡?} F -->|是| G[初始化卡] F -->|否| H[定时重试] G --> I[创建块设备] ``` ### 总结 `mmc_host` 是 MMC 子系统的硬件抽象层,通过标准化的接口(`mmc_host_ops`)实现: - 主机控制器的统一管理 - 热插拔检测和错误恢复 - 跨平台兼容性支持 开发/调试时需重点关注操作集实现、锁机制和电源时序控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值