emmc初始化加载失败--mmc0: error -110 whilst initialising MMC card

1:异常现象

新硬件回来调试,发现 emmc无法挂载,使用"dmesg | grep mmc"查看报错如下
mmc0: error -110 whilst initialising MMC card;

2:问题排查

2.1 超时错误(-110 错误码)

Linux 内核中的 MMC/SD 子系统在等待卡响应时超时(-ETIMEDOUT,即错误码 -110),可能的原因包括:
1:卡未正确插入或未被检测到(比如EMMC接触不良,供电电压异常)。
3:控制器配置错误(如错误的电压模式或总线宽度或时钟频率等)。

2.2 硬件排查

首先根据硬件原理进行排查,确保电源线和数据线等连接正常;不然主机发送的数据,从机没有应答,必然导致初始化失败;
在这里插入图片描述

2.3 软件排查

先定位一下错误代码位置

int mmc_attach_mmc(struct mmc_host *host)
{
...
err:
	mmc_detach_bus(host);

	pr_err("%s: error %d whilst initialising MMC card\n",
		mmc_hostname(host), err);
}

通过增加日志,发现是在 “mmc_init_card” 函数里面报错;
进一步增加日志发现是 “mmc_switch” 函数报错;

__mmc_switch 函数修改,增加日志

int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
		unsigned int timeout_ms, unsigned char timing,
		bool send_status, bool retry_crc_err, unsigned int retries)
{
	struct mmc_host *host = card->host;
	int err;
	struct mmc_command cmd = {};
	bool use_r1b_resp;
	unsigned char old_timing = host->ios.timing;

	mmc_retune_hold(host);

	if (!timeout_ms) {
		pr_warn("%s: unspecified timeout for CMD6 - use generic\n",
			mmc_hostname(host));
		timeout_ms = card->ext_csd.generic_cmd6_time;
	}

	cmd.opcode = MMC_SWITCH;
	cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
		  (index << 16) |
		  (value << 8) |
		  set;
	use_r1b_resp = mmc_prepare_busy_cmd(host, &cmd, timeout_ms);

    /* 调试日志:打印发送的 CMD6 参数 */
	pr_err("%s: CMD6: opcode=%d, arg=0x%08x (set=0x%x, index=0x%x, value=0x%x), timeout=%ums, timing=%u\n",
		mmc_hostname(host), cmd.opcode, cmd.arg, set, index, value, timeout_ms, timing);
	err = mmc_wait_for_cmd(host, &cmd, retries);
	if (err)
    {
        /* 调试日志:命令执行失败 */
		pr_err("%s: CMD6 failed, err=%d, resp=0x%08x\n",
			mmc_hostname(host), err, cmd.resp[0]);
        goto out;
	} else {
		/* 调试日志:命令执行成功 */
		pr_err("%s: CMD6 success, resp=0x%08x\n",
			mmc_hostname(host), cmd.resp[0]);
	}


	/*If SPI or used HW busy detection above, then we don't need to poll. */
	if (((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) ||
		mmc_host_is_spi(host))
		goto out_tim;

	/*
	 * If the host doesn't support HW polling via the ->card_busy() ops and
	 * when it's not allowed to poll by using CMD13, then we need to rely on
	 * waiting the stated timeout to be sufficient.
	 */
	/* 如果不需轮询状态,直接延迟等待 */
	if (!send_status && !host->ops->card_busy) {
		pr_err("%s: CMD6 no polling, delay %ums\n",
			mmc_hostname(host), timeout_ms);
		mmc_delay(timeout_ms);
		goto out_tim;
	}

	/* 轮询卡状态 */
	pr_err("%s: CMD6 polling busy, timeout=%ums, retry_crc_err=%d\n",
		mmc_hostname(host), timeout_ms, retry_crc_err);
	err = mmc_poll_for_busy(card, timeout_ms, retry_crc_err, MMC_BUSY_CMD6);
	if (err) {
		pr_err("%s: CMD6 polling failed, err=%d\n",
			mmc_hostname(host), err);
		goto out;
	}

    out_tim:
    	/* 切换总线时序 */
    	if (timing) {
    		pr_err("%s: CMD6 timing change: %u -> %u\n",
    			mmc_hostname(host), old_timing, timing);
    		mmc_set_timing(host, timing);
    	}

    	/* 检查卡状态 */
    	if (send_status) {
    		pr_err("%s: CMD6 checking switch status\n", mmc_hostname(host));
    		err = mmc_switch_status(card, true);
    		if (err && timing) {
    			pr_err("%s: CMD6 status check failed, revert timing to %u\n",
    			 	mmc_hostname(host), old_timing);
    			mmc_set_timing(host, old_timing);
    		}
    	}
    out:
    	mmc_retune_release(host);
    	return err;
}

dmesg查看 mmc_switch 日志记录如下

/ # dmesg | grep mmc
[    5.546652][   T55] mmc0: SDHCI controller on 8804000.sdhci [8804000.sdhci] using ADMA 64-bit
[    5.731317][  T157] mmc0: CMD6: opcode=6, arg=0x03af0101 (set=0x1, index=0xaf, value=0x1), timeout=500ms, timing=0
[    5.742542][  T157] mmc0: CMD6 success, resp=0x00000800
[    5.754533][  T157] mmc0: CMD6 checking switch status
[    5.772783][  T157] mmc0: CMD6: opcode=6, arg=0x03220101 (set=0x1, index=0x22, value=0x1), timeout=500ms, timing=0
[    5.789514][  T157] mmc0: CMD6 success, resp=0x00000800
[    5.789518][  T157] mmc0: CMD6 checking switch status
[    5.800410][  T157] mmc0: CMD6: opcode=6, arg=0x03b70201 (set=0x1, index=0xb7, value=0x2), timeout=500ms, timing=0
[    5.811825][  T157] mmc0: CMD6 success, resp=0x00000800
[    5.817205][  T157] mmc0: CMD6 checking switch status
[    5.825010][  T157] mmc0: CMD6: opcode=6, arg=0x03b90101 (set=0x1, index=0xb9, value=0x1), timeout=500ms, timing=0
[    5.836650][  T157] mmc0: CMD6 success, resp=0x00000800
[    5.855325][  T157] mmc0: CMD6: opcode=6, arg=0x03b78601 (set=0x1, index=0xb7, value=0x86), timeout=500ms, timing=0
[    5.870566][  T157] mmc0: CMD6 success, resp=0x00000800
[    5.875940][  T157] mmc0: CMD6 checking switch status
[    5.875981][  T157] mmc0: CMD6: opcode=6, arg=0x03b90301 (set=0x1, index=0xb9, value=0x3), timeout=500ms, timing=0
[    5.897900][  T157] mmc0: CMD6 success, resp=0x00000800
[    5.909801][  T157] mmc0: CMD6: opcode=6, arg=0x03a10101 (set=0x1, index=0xa1, value=0x1), timeout=500ms, timing=0
[    5.920631][  T157] mmc0: CMD6 failed, err=-110, resp=0x00000000
[    5.932305][  T157] mmc0: Enabling HPI failed
[    5.936818][  T157] mmc0: CMD6: opcode=6, arg=0x03210101 (set=0x1, index=0x21, value=0x1), timeout=1600ms, timing=0
[    5.947751][  T157] mmc0: CMD6 failed, err=-110, resp=0x00000000
[    5.955402][  T157] mmc0: error -110 whilst initialising MMC card

这里我找deepseek帮我分析了一下CMD6的指令详情; 注意这里一定要结合自己的代码版本(mmc.h)进行核对,不然emmc协议好几个版本,deepseek给出的解析是错误的

1. CMD6: index=0xAF (175), value=0x1
[    5.777543] mmc0: CMD6: opcode=6, arg=0x03af0101 (set=0x1, index=0xaf, value=0x1)
  • EXT_CSD寄存器EXT_CSD_ERASE_GROUP_DEF (175)
  • 作用:定义是否使用预定义的擦除组大小Erase Group Definition
  • value=0x1:启用预定义的擦除组大小(1表示使用设备默认的擦除单位,0表示使用CSD中的值)
  • 响应0x00000800(成功)

2. CMD6: index=0x22 (34), value=0x1
[    5.799086] mmc0: CMD6: opcode=6, arg=0x03220101 (set=0x1, index=0x22, value=0x1)
  • EXT_CSD寄存器EXT_CSD_POWER_OFF_NOTIFICATION (34)
  • 作用:配置电源关闭通知Power Off Notification
  • value=0x1:启用短时电源关闭通知(EXT_CSD_POWER_ON
  • 响应0x00000800(成功)

注意
如果index=0x22实际对应EXT_CSD_HS_TIMING(185),则可能是驱动使用了不同的索引计算方式(如index = EXT_CSD_offset - 0x80)。
但按标准定义,0x22应为EXT_CSD_POWER_OFF_NOTIFICATION


3. CMD6: index=0xB7 (183), value=0x2
[    5.835860] mmc0: CMD6: opcode=6, arg=0x03b70201 (set=0x1, index=0xb7, value=0x2)
  • EXT_CSD寄存器EXT_CSD_BUS_WIDTH (183)
  • 作用:设置总线宽度
  • value=0x2EXT_CSD_BUS_WIDTH_8(8位总线模式)
  • 响应0x00000800(成功)

4. CMD6: index=0xB9 (185), value=0x1
[    5.860607] mmc0: CMD6: opcode=6, arg=0x03b90101 (set=0x1, index=0xb9, value=0x1)
  • EXT_CSD寄存器EXT_CSD_HS_TIMING (185)
  • 作用:设置高速时序模式
  • value=0x1EXT_CSD_TIMING_HS(高速模式,52MHz)
  • 响应0x00000800(成功)

5. CMD6: index=0xB7 (183), value=0x86
[    5.878618] mmc0: CMD6: opcode=6, arg=0x03b78601 (set=0x1, index=0xb7, value=0x86)
  • EXT_CSD寄存器EXT_CSD_BUS_WIDTH (183)
  • 作用:再次配置总线宽度
  • value=0x86
    • 0x80EXT_CSD_BUS_WIDTH_STROBE,增强型Strobe模式)
    • 0x06EXT_CSD_DDR_BUS_WIDTH_8,8位DDR模式)
  • 响应0x00000800(成功)

说明
该命令可能是尝试切换到HS400模式(需要DDR + Strobe支持)。


6. CMD6: index=0xB9 (185), value=0x3
[    5.905665] mmc0: CMD6: opcode=6, arg=0x03b90301 (set=0x1, index=0xb9, value=0x3)
  • EXT_CSD寄存器EXT_CSD_HS_TIMING (185)
  • 作用:切换至更高速度模式
  • value=0x3EXT_CSD_TIMING_HS400(HS400模式,200MHz DDR)
  • 响应0x00000800(成功)

7. CMD6: index=0xA1 (161), value=0x1
[    5.922228] mmc0: CMD6: opcode=6, arg=0x03a10101 (set=0x1, index=0xa1, value=0x1)
  • EXT_CSD寄存器EXT_CSD_HPI_MGMT (161)
  • 作用:配置HPI(Host-Initiated Power Management)
  • value=0x1:启用HPI功能
  • 响应失败(-110,超时)
8. CMD6: index=0x21 (33), value=0x1

自己核对了一下mmc.h,发现这个是和cache相关的
#define EXT_CSD_CACHE_CTRL 33 /* R/W */

2.4 问题解决

通过mmc_switch的日志,我们发现了两个问题;一个是HPI功能,一个是chche功能,主机发送数据的时候EMMC卡应答异常;
正确的做法应该是去修改.dtsi设备树文件,但是很遗憾,我设备树修改没生效;

/*
    // IS21ES16G 发送下面的指令会报错;需要禁用或调试修改,但是配置没生效
    cache-size = <0>;   // 禁用cache
    broken-hpi;         // 不使用hpi
*/

没办法了,一步步跟踪代码;手动在初始化的时候,对如下几个变量赋值了;
HS400CMD6指令正常,实测发现不支持,也手动修改为HS200了;

static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
    // IS21ES16G modify, dtsi no effect;  djq-20250626
    card->mmc_avail_type = (EXT_CSD_CARD_TYPE_DDR_1_8V | EXT_CSD_CARD_TYPE_HS200_1_8V);
    card->ext_csd.hpi = 0;
    card->ext_csd.cache_size = 0;
}

修改之后的日志如下,可以发现EMMC加载和挂载都成功了;

/ # dmesg | grep mmc0
[    5.520834][   T55] mmc0: SDHCI controller on 8804000.sdhci [8804000.sdhci] using ADMA 64-bit
[    5.692539][  T158] mmc0: new HS200 MMC card at address 0001
[    5.773588][  T158] mmcblk0: mmc0:0001 IS016G 14.6 GiB
[    5.792721][  T158] mmcblk0boot0: mmc0:0001 IS016G 4.00 MiB
[    5.799663][  T158] mmcblk0boot1: mmc0:0001 IS016G 4.00 MiB
[    5.806605][  T158] mmcblk0rpmb: mmc0:0001 IS016G 4.00 MiB, chardev (506:0)
/ #
/ #
/ # fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 15 GB, 15675162624 bytes, 30615552 sectors
478368 cylinders, 4 heads, 16 sectors/track
Units: sectors of 1 * 512 = 512 bytes

Device       Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
/dev/mmcblk0p1    0,1,1       1023,3,16           16     131087     131072 64.0M 83 Linux
/dev/mmcblk0p2    1023,3,16   1023,3,16       131088   30615551   30484464 14.5G 83 Linux

EMMC参数初始化流程应该是这样的: 内核默认值->设备树配置值->eMMC设备EXT_CSD寄存器值;没有去深究具体原因,先手动修改用着;

正确的做法应该是根据EMMC芯片手册去适配内核和设备树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值